X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fffmpeg_decoder.cc;h=147bb384f3eb1bad5a811adf9d624501b69eedf4;hb=a7a8cd74f2f32de8b708c78cc8bc9c0cf17d60f5;hp=30993f88279bc037bc351f927c5ec6dc7c716eb5;hpb=04e94f01295463f2c910ba2b4306c1b76e340a45;p=dcpomatic.git diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 30993f882..147bb384f 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -69,6 +69,7 @@ using std::max; using std::map; using std::shared_ptr; using std::make_shared; +using std::make_pair; using boost::is_any_of; using boost::split; using boost::optional; @@ -102,25 +103,42 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr film, shared_ptrffmpeg_audio_streams()) { - _next_time[i] = {}; + _next_time[i] = boost::optional(); } } -void +bool FFmpegDecoder::flush () { - /* Get any remaining frames */ + /* Flush video and audio once */ - AVPacket packet; - packet.data = nullptr; - packet.size = 0; - while (video && decode_video_packet(&packet)) {} + bool did_something = false; + if (video) { + AVPacket packet; + av_init_packet (&packet); + packet.data = nullptr; + packet.size = 0; + if (decode_and_process_video_packet(&packet)) { + did_something = true; + } + } - if (audio) { + for (auto i: ffmpeg_content()->ffmpeg_audio_streams()) { + AVPacket packet; + av_init_packet (&packet); packet.data = nullptr; packet.size = 0; - decode_audio_packet (&packet); + auto result = decode_audio_packet (i, &packet); + if (result.second) { + process_audio_frame (i); + did_something = true; + } + } + + if (did_something) { + /* We want to be called again */ + return false; } /* Make sure all streams are the same length and round up to the next video frame */ @@ -158,6 +176,8 @@ FFmpegDecoder::flush () if (audio) { audio->flush (); } + + return true; } @@ -182,19 +202,18 @@ FFmpegDecoder::pass () } av_packet_free (&packet); - flush (); - return true; + return flush (); } int const si = packet->stream_index; auto fc = _ffmpeg_content; if (_video_stream && si == _video_stream.get() && video && !video->ignore()) { - decode_video_packet (packet); + decode_and_process_video_packet (packet); } else if (fc->subtitle_stream() && fc->subtitle_stream()->uses_index(_format_context, si) && !only_text()->ignore()) { decode_and_process_subtitle_packet (packet); } else { - decode_audio_packet (packet); + decode_and_process_audio_packet (packet); } av_packet_free (&packet); @@ -215,6 +234,7 @@ DCPOMATIC_DISABLE_WARNINGS 0, stream->stream(_format_context)->codec->channels, _frame->nb_samples, audio_sample_format (stream), 1 ); DCPOMATIC_ENABLE_WARNINGS + DCPOMATIC_ASSERT (size >= 0); /* XXX: can't we just use _frame->nb_samples directly here? */ /* XXX: can't we use swr_convert() to do the format conversion? */ @@ -425,7 +445,7 @@ DCPOMATIC_ENABLE_WARNINGS _have_current_subtitle = false; for (auto& i: _next_time) { - i.second = {}; + i.second = boost::optional(); } } @@ -449,7 +469,7 @@ FFmpegDecoder::audio_stream_from_index (int index) const void -FFmpegDecoder::process_audio_frame (shared_ptr stream, int64_t packet_pts) +FFmpegDecoder::process_audio_frame (shared_ptr stream) { auto data = deinterleave_audio (stream); @@ -481,11 +501,10 @@ FFmpegDecoder::process_audio_frame (shared_ptr stream, int64_ if (ct < ContentTime()) { LOG_WARNING ( - "Crazy timestamp %1 for %2 samples in stream %3 packet pts %4 (ts=%5 tb=%6, off=%7)", + "Crazy timestamp %1 for %2 samples in stream %3 (ts=%4 tb=%5, off=%6)", to_string(ct), data->frames(), stream->id(), - packet_pts, _frame->best_effort_timestamp, av_q2d(stream->stream(_format_context)->time_base), to_string(_pts_offset) @@ -499,8 +518,29 @@ FFmpegDecoder::process_audio_frame (shared_ptr stream, int64_ } +pair +FFmpegDecoder::decode_audio_packet (shared_ptr stream, AVPacket* packet) +{ + int frame_finished; + DCPOMATIC_DISABLE_WARNINGS + int decode_result = avcodec_decode_audio4 (stream->stream(_format_context)->codec, _frame, &frame_finished, packet); + DCPOMATIC_ENABLE_WARNINGS + if (decode_result < 0) { + /* avcodec_decode_audio4 can sometimes return an error even though it has decoded + some valid data; for example dca_subframe_footer can return AVERROR_INVALIDDATA + if it overreads the auxiliary data. ffplay carries on if frame_finished is true, + even in the face of such an error, so I think we should too. + + Returning from the method here caused mantis #352. + */ + LOG_WARNING ("avcodec_decode_audio4 failed (%1)", decode_result); + } + return make_pair(decode_result, frame_finished); +} + + void -FFmpegDecoder::decode_audio_packet (AVPacket* packet) +FFmpegDecoder::decode_and_process_audio_packet (AVPacket* packet) { auto stream = audio_stream_from_index (packet->stream_index); if (!stream) { @@ -513,11 +553,8 @@ FFmpegDecoder::decode_audio_packet (AVPacket* packet) AVPacket copy_packet = *packet; while (copy_packet.size > 0) { - int frame_finished; - DCPOMATIC_DISABLE_WARNINGS - int decode_result = avcodec_decode_audio4 (stream->stream(_format_context)->codec, _frame, &frame_finished, ©_packet); - DCPOMATIC_ENABLE_WARNINGS - if (decode_result < 0) { + auto result = decode_audio_packet (stream, ©_packet); + if (result.first < 0) { /* avcodec_decode_audio4 can sometimes return an error even though it has decoded some valid data; for example dca_subframe_footer can return AVERROR_INVALIDDATA if it overreads the auxiliary data. ffplay carries on if frame_finished is true, @@ -525,26 +562,24 @@ FFmpegDecoder::decode_audio_packet (AVPacket* packet) Returning from the method here caused mantis #352. */ - LOG_WARNING ("avcodec_decode_audio4 failed (%1)", decode_result); + } - /* Fudge decode_result so that we come out of the while loop when - we've processed this data. - */ - decode_result = copy_packet.size; + if (result.second) { + process_audio_frame (stream); } - if (frame_finished) { - process_audio_frame (stream, copy_packet.pts); + if (result.first) { + break; } - copy_packet.data += decode_result; - copy_packet.size -= decode_result; + copy_packet.data += result.first; + copy_packet.size -= result.first; } } bool -FFmpegDecoder::decode_video_packet (AVPacket* packet) +FFmpegDecoder::decode_and_process_video_packet (AVPacket* packet) { DCPOMATIC_ASSERT (_video_stream);