summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2022-02-15 22:47:48 +0100
committerCarl Hetherington <cth@carlh.net>2022-02-17 09:59:26 +0100
commit80430058f5eefb55147218a85225adeb6b616f4d (patch)
tree7566946921785c596c0b4be66fbd0e9adb449ec7
parentc3ce1bf707fb5308f4b96cb96ed6e8d29335a319 (diff)
Try to handle EAGAIN from avcodec_send_packet() properly.
The docs say on EAGAIN we should call avcodec_receive_frame() and then re-send the same packet again. This should do that. This is a fix for errors trigged by the accompanying test.
-rw-r--r--src/lib/ffmpeg_decoder.cc38
-rw-r--r--src/lib/ffmpeg_examiner.cc33
-rw-r--r--test/ffmpeg_examiner_test.cc7
3 files changed, 47 insertions, 31 deletions
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index b2a1bbbe0..d49878217 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -533,24 +533,30 @@ FFmpegDecoder::decode_and_process_video_packet (AVPacket* packet)
auto context = video_codec_context();
- int r = avcodec_send_packet (context, packet);
- if (r < 0) {
- LOG_WARNING("avcodec_send_packet returned %1 for a video packet", r);
- }
-
- while (true) {
- r = avcodec_receive_frame (context, _video_frame);
- if (r == AVERROR(EAGAIN) || r == AVERROR_EOF || (r < 0 && !packet)) {
- /* More input is required, no more frames are coming, or we are flushing and there was
- * some error which we just want to ignore.
- */
- return false;
- } else if (r < 0) {
- throw DecodeError (N_("avcodec_receive_frame"), N_("FFmpeg::decode_and_process_video_packet"), r);
+ bool pending = false;
+ do {
+ int r = avcodec_send_packet (context, packet);
+ if (r < 0) {
+ LOG_WARNING("avcodec_send_packet returned %1 for a video packet", r);
}
- process_video_frame ();
- }
+ /* EAGAIN means we should call avcodec_receive_frame and then re-send the same packet */
+ pending = r == AVERROR(EAGAIN);
+
+ while (true) {
+ r = avcodec_receive_frame (context, _video_frame);
+ if (r == AVERROR(EAGAIN) || r == AVERROR_EOF || (r < 0 && !packet)) {
+ /* More input is required, no more frames are coming, or we are flushing and there was
+ * some error which we just want to ignore.
+ */
+ return false;
+ } else if (r < 0) {
+ throw DecodeError (N_("avcodec_receive_frame"), N_("FFmpeg::decode_and_process_video_packet"), r);
+ }
+
+ process_video_frame ();
+ }
+ } while (pending);
return true;
}
diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc
index 3c6d185f4..fab999006 100644
--- a/src/lib/ffmpeg_examiner.cc
+++ b/src/lib/ffmpeg_examiner.cc
@@ -221,22 +221,25 @@ FFmpegExaminer::video_packet (AVCodecContext* context, string& temporal_referenc
return false;
}
- int r = avcodec_send_packet (context, packet);
- if (r < 0 && !(r == AVERROR_EOF && !packet)) {
- /* We could cope with AVERROR(EAGAIN) and re-send the packet but I think it should never happen.
- * AVERROR_EOF can happen during flush if we've already sent a flush packet.
- */
- throw DecodeError (N_("avcodec_send_packet"), N_("FFmpegExaminer::video_packet"), r);
- }
+ bool pending = false;
+ do {
+ int r = avcodec_send_packet (context, packet);
+ if (r < 0) {
+ LOG_WARNING("avcodec_send_packet returned %1 for a video packet", r);
+ }
- r = avcodec_receive_frame (context, _video_frame);
- if (r == AVERROR(EAGAIN)) {
- /* More input is required */
- return true;
- } else if (r == AVERROR_EOF) {
- /* No more output is coming */
- return false;
- }
+ /* EAGAIN means we should call avcodec_receive_frame and then re-send the same packet */
+ pending = r == AVERROR(EAGAIN);
+
+ r = avcodec_receive_frame (context, _video_frame);
+ if (r == AVERROR(EAGAIN)) {
+ /* More input is required */
+ return true;
+ } else if (r == AVERROR_EOF) {
+ /* No more output is coming */
+ return false;
+ }
+ } while (pending);
if (!_first_video) {
_first_video = frame_time (_video_frame, _format_context->streams[_video_stream.get()]);
diff --git a/test/ffmpeg_examiner_test.cc b/test/ffmpeg_examiner_test.cc
index c460830fb..2c244f541 100644
--- a/test/ffmpeg_examiner_test.cc
+++ b/test/ffmpeg_examiner_test.cc
@@ -76,3 +76,10 @@ BOOST_AUTO_TEST_CASE (ffmpeg_examiner_vob_test)
auto examiner = make_shared<FFmpegExaminer>(content);
}
+
+/** Check that another file can be examined without error */
+BOOST_AUTO_TEST_CASE (ffmpeg_examiner_mkv_test)
+{
+ auto content = make_shared<FFmpegContent>(TestPaths::private_data() / "sample.mkv");
+ auto examiner = make_shared<FFmpegExaminer>(content);
+}