From 001a2c51958ebe332562df13e9630768b66def57 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Tue, 16 May 2023 14:31:04 +0300 Subject: [PATCH] Merge pull request #23606 from mshabunin:fix-ffmpeg-packet-limit videoio/FFmpeg: increased packet read attempt limit, allow configuring it resolves #9455 related #3225 * Use different counters for wrong packets recieved by demuxer and errors from decoder * Allow modifying these counters via environment variables `OPENCV_FFMPEG_READ_ATTEMPTS`/`OPENCV_FFMPEG_DECODE_ATTEMPTS` * Added logging when reading breaks at one of error limits Notes: * I've been able to reproduce original issue with a video file with 14 total streams (video + audio + subtitles), at some point in the video only packets from the last stream are being sent by the demuxer, thus exceeding our limit. For my specific video total number of packets from wrong stream was about 2700. I've chosen 4096 as default value. * Default limit of decoding attempts is quite low, because I'm not sure in which cases it can be exceeded (network stream?). I tried to read 8k video from the disk, but it did not cause break at decode point. --- modules/videoio/src/cap_ffmpeg_impl.hpp | 26 +++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index f8a0c78bce..66d7cfaa8f 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -40,6 +40,7 @@ // //M*/ +#include #include "cap_ffmpeg_legacy_api.hpp" #include "opencv2/core/utils/logger.hpp" #include "cap_interface.hpp" @@ -1428,8 +1429,10 @@ bool CvCapture_FFMPEG::grabFrame() { bool valid = false; - int count_errs = 0; - const int max_number_of_attempts = 1 << 9; + static const size_t max_read_attempts = cv::utils::getConfigurationParameterSizeT("OPENCV_FFMPEG_READ_ATTEMPTS", 4096); + static const size_t max_decode_attempts = cv::utils::getConfigurationParameterSizeT("OPENCV_FFMPEG_DECODE_ATTEMPTS", 64); + size_t cur_read_attempts = 0; + size_t cur_decode_attempts = 0; if( !ic || !video_st || !context ) return false; @@ -1484,9 +1487,15 @@ bool CvCapture_FFMPEG::grabFrame() if( packet.stream_index != video_stream ) { _opencv_ffmpeg_av_packet_unref (&packet); - count_errs++; - if (count_errs > max_number_of_attempts) + if (++cur_read_attempts > max_read_attempts) + { + CV_LOG_WARNING(NULL, + "packet read max attempts exceeded, if your video have " + "multiple streams (video, audio) try to increase attempt " + "limit by setting environment variable OPENCV_FFMPEG_READ_ATTEMPTS " + "(current value is " << max_read_attempts << ")"); break; + } continue; } @@ -1514,9 +1523,14 @@ bool CvCapture_FFMPEG::grabFrame() } else { - count_errs++; - if (count_errs > max_number_of_attempts) + if (++cur_decode_attempts > max_decode_attempts) + { + CV_LOG_WARNING(NULL, + "frame decode max attempts exceeded, try to increase attempt " + "limit by setting environment variable OPENCV_FFMPEG_DECODE_ATTEMPTS " + "(current value is " << max_decode_attempts << ")"); break; + } } }