X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fffmpeg_decoder.cc;h=c3d32bc752306abd1beb81f6042a80a9bc9acc9c;hb=e67ce8ae4b9121bbcef2c1dcb61bdb5b9330ad78;hp=e5685f6611d60665367b46b97e5f66a06634d113;hpb=b703142e8750c509174b4d964009aecf93f3d834;p=dcpomatic.git diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index e5685f661..c3d32bc75 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -77,6 +77,7 @@ using namespace dcpomatic; FFmpegDecoder::FFmpegDecoder (shared_ptr film, shared_ptr c, bool fast) : FFmpeg (c) , Decoder (film) + , _filter_graphs(c->filters(), dcp::Fraction(lrint(_ffmpeg_content->video_frame_rate().get_value_or(24) * 1000), 1000)) { if (c->video && c->video->use()) { video = make_shared(this, c); @@ -93,8 +94,10 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr film, shared_ptronly_text()) { - /* XXX: this time here should be the time of the first subtitle, not 0 */ - text.push_back (make_shared(this, c->only_text(), ContentTime())); + text.push_back (make_shared(this, c->only_text())); + /* XXX: we should be calling maybe_set_position() on this TextDecoder, but we can't easily find + * the time of the first subtitle at this point. + */ } for (auto i: c->ffmpeg_audio_streams()) { @@ -103,11 +106,41 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr film, shared_ptr(_flush_state)); + + switch (_flush_state) { + case FlushState::CODECS: + if (flush_codecs() == FlushResult::DONE) { + LOG_DEBUG_PLAYER_NC("Finished flushing codecs"); + _flush_state = FlushState::AUDIO_DECODER; + } + break; + case FlushState::AUDIO_DECODER: + if (audio) { + audio->flush(); + } + LOG_DEBUG_PLAYER_NC("Finished flushing audio decoder"); + _flush_state = FlushState::FILL; + break; + case FlushState::FILL: + if (flush_fill() == FlushResult::DONE) { + LOG_DEBUG_PLAYER_NC("Finished flushing fills"); + return FlushResult::DONE; + } + break; + } + return FlushResult::AGAIN; +} + + +/** @return true if we have finished flushing the codecs */ +FFmpegDecoder::FlushResult +FFmpegDecoder::flush_codecs() +{ bool did_something = false; if (video) { if (decode_and_process_video_packet(nullptr)) { @@ -129,48 +162,49 @@ FFmpegDecoder::flush () } } - if (did_something) { - /* We want to be called again */ - return false; - } + return did_something ? FlushResult::AGAIN : FlushResult::DONE; +} + +FFmpegDecoder::FlushResult +FFmpegDecoder::flush_fill() +{ /* Make sure all streams are the same length and round up to the next video frame */ + bool did_something = false; + auto const frc = film()->active_frame_rate_change(_ffmpeg_content->position()); ContentTime full_length (_ffmpeg_content->full_length(film()), frc); full_length = full_length.ceil (frc.source); - if (video) { + if (video && !video->ignore()) { double const vfr = _ffmpeg_content->video_frame_rate().get(); auto const f = full_length.frames_round (vfr); - auto v = video->position(film()).get_value_or(ContentTime()).frames_round(vfr) + 1; - while (v < f) { - video->emit (film(), shared_ptr (new RawImageProxy (_black_image)), v); - ++v; + auto const v = video->position(film()).get_value_or(ContentTime()).frames_round(vfr) + 1; + if (v < f) { + video->emit(film(), make_shared(_black_image), v); + did_something = true; } } - for (auto i: _ffmpeg_content->ffmpeg_audio_streams ()) { - auto a = audio->stream_position(film(), i); - /* Unfortunately if a is 0 that really means that we don't know the stream position since - there has been no data on it since the last seek. In this case we'll just do nothing - here. I'm not sure if that's the right idea. - */ - if (a > ContentTime()) { - while (a < full_length) { + if (audio && !audio->ignore()) { + for (auto i: _ffmpeg_content->ffmpeg_audio_streams ()) { + auto const a = audio->stream_position(film(), i); + /* Unfortunately if a is 0 that really means that we don't know the stream position since + there has been no data on it since the last seek. In this case we'll just do nothing + here. I'm not sure if that's the right idea. + */ + if (a > ContentTime() && a < full_length) { + LOG_DEBUG_PLAYER("Flush inserts silence at %1", to_string(a)); auto to_do = min (full_length - a, ContentTime::from_seconds (0.1)); auto silence = make_shared(i->channels(), to_do.frames_ceil (i->frame_rate())); silence->make_silent (); audio->emit (film(), i, silence, a, true); - a += to_do; + did_something = true; } } } - if (audio) { - audio->flush (); - } - - return true; + return did_something ? FlushResult::AGAIN : FlushResult::DONE; } @@ -187,6 +221,7 @@ FFmpegDecoder::pass () Hence it makes sense to continue here in that case. */ if (r < 0 && r != AVERROR_INVALIDDATA) { + LOG_DEBUG_PLAYER("FFpmegDecoder::pass flushes because av_read_frame returned %1", r); if (r != AVERROR_EOF) { /* Maybe we should fail here, but for now we'll just finish off instead */ char buf[256]; @@ -195,7 +230,7 @@ FFmpegDecoder::pass () } av_packet_free (&packet); - return flush (); + return flush() == FlushResult::DONE; } int const si = packet->stream_index; @@ -217,8 +252,9 @@ FFmpegDecoder::pass () /** @param data pointer to array of pointers to buffers. * Only the first buffer will be used for non-planar data, otherwise there will be one per channel. */ +static shared_ptr -FFmpegDecoder::deinterleave_audio (AVFrame* frame) +deinterleave_audio(AVFrame* frame) { auto format = static_cast(frame->format); @@ -230,6 +266,10 @@ FFmpegDecoder::deinterleave_audio (AVFrame* frame) auto audio = make_shared(channels, frames); auto data = audio->data(); + if (frames == 0) { + return audio; + } + switch (format) { case AV_SAMPLE_FMT_U8: { @@ -324,14 +364,9 @@ FFmpegDecoder::deinterleave_audio (AVFrame* frame) case AV_SAMPLE_FMT_FLTP: { auto p = reinterpret_cast (frame->data); - DCPOMATIC_ASSERT (frame->channels <= channels); - /* Sometimes there aren't as many channels in the frame as in the stream */ - for (int i = 0; i < frame->channels; ++i) { + for (int i = 0; i < channels; ++i) { memcpy (data[i], p[i], frames * sizeof(float)); } - for (int i = frame->channels; i < channels; ++i) { - audio->make_silent (i); - } } break; @@ -398,13 +433,10 @@ FFmpegDecoder::seek (ContentTime time, bool accurate) AVSEEK_FLAG_BACKWARD ); - { - /* Force re-creation of filter graphs to reset them and hence to make sure - they don't have any pre-seek frames knocking about. - */ - boost::mutex::scoped_lock lm (_filter_graphs_mutex); - _filter_graphs.clear (); - } + /* Force re-creation of filter graphs to reset them and hence to make sure + they don't have any pre-seek frames knocking about. + */ + _filter_graphs.clear(); if (video_codec_context ()) { avcodec_flush_buffers (video_codec_context()); @@ -448,7 +480,9 @@ void FFmpegDecoder::process_audio_frame (shared_ptr stream) { auto frame = audio_frame (stream); - auto data = deinterleave_audio (frame); + auto data = deinterleave_audio(frame); + + auto const time_base = stream->stream(_format_context)->time_base; ContentTime ct; if (frame->pts == AV_NOPTS_VALUE) { @@ -462,8 +496,16 @@ FFmpegDecoder::process_audio_frame (shared_ptr stream) } else { ct = ContentTime::from_seconds ( frame->best_effort_timestamp * - av_q2d (stream->stream(_format_context)->time_base)) + av_q2d(time_base)) + _pts_offset; + LOG_DEBUG_PLAYER( + "Process audio with timestamp %1 (BET %2, timebase %3/%4, (PTS offset %5)", + to_string(ct), + frame->best_effort_timestamp, + time_base.num, + time_base.den, + to_string(_pts_offset) + ); } _next_time[stream] = ct + ContentTime::from_frames(data->frames(), stream->frame_rate()); @@ -483,7 +525,7 @@ FFmpegDecoder::process_audio_frame (shared_ptr stream) data->frames(), stream->id(), frame->best_effort_timestamp, - av_q2d(stream->stream(_format_context)->time_base), + av_q2d(time_base), to_string(_pts_offset) ); } @@ -506,6 +548,7 @@ FFmpegDecoder::decode_and_process_audio_packet (AVPacket* packet) auto context = _codec_context[stream->index(_format_context)]; auto frame = audio_frame (stream); + LOG_DEBUG_PLAYER("Send audio packet on stream %1", stream->index(_format_context)); int r = avcodec_send_packet (context, packet); if (r < 0) { LOG_WARNING("avcodec_send_packet returned %1 for an audio packet", r); @@ -514,6 +557,7 @@ FFmpegDecoder::decode_and_process_audio_packet (AVPacket* packet) r = avcodec_receive_frame (context, frame); if (r == AVERROR(EAGAIN)) { /* More input is required */ + LOG_DEBUG_PLAYER_NC("EAGAIN after trying to receive audio frame"); return; } @@ -533,42 +577,39 @@ 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); - } - - 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); - } - - /* We assume we'll only get one frame here, which I think is safe */ + 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); + } - boost::mutex::scoped_lock lm (_filter_graphs_mutex); + /* 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); + } - shared_ptr graph; + process_video_frame (); + } + } while (pending); - auto i = _filter_graphs.begin(); - while (i != _filter_graphs.end() && !(*i)->can_process(dcp::Size(_video_frame->width, _video_frame->height), (AVPixelFormat) _video_frame->format)) { - ++i; - } + return true; +} - if (i == _filter_graphs.end ()) { - dcp::Fraction vfr (lrint(_ffmpeg_content->video_frame_rate().get() * 1000), 1000); - graph = make_shared(dcp::Size(_video_frame->width, _video_frame->height), (AVPixelFormat) _video_frame->format, vfr); - graph->setup (_ffmpeg_content->filters ()); - _filter_graphs.push_back (graph); - LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _video_frame->width, _video_frame->height, _video_frame->format); - } else { - graph = *i; - } +void +FFmpegDecoder::process_video_frame () +{ + auto graph = _filter_graphs.get(dcp::Size(_video_frame->width, _video_frame->height), static_cast(_video_frame->format)); auto images = graph->process (_video_frame); for (auto const& i: images) { @@ -587,8 +628,6 @@ FFmpegDecoder::decode_and_process_video_packet (AVPacket* packet) LOG_WARNING_NC ("Dropping frame without PTS"); } } - - return true; } @@ -631,6 +670,7 @@ FFmpegDecoder::decode_and_process_subtitle_packet (AVPacket* packet) _have_current_subtitle = true; } + ContentBitmapText bitmap_text(from); for (unsigned int i = 0; i < sub.num_rects; ++i) { auto const rect = sub.rects[i]; @@ -638,7 +678,7 @@ FFmpegDecoder::decode_and_process_subtitle_packet (AVPacket* packet) case SUBTITLE_NONE: break; case SUBTITLE_BITMAP: - process_bitmap_subtitle (rect, from); + bitmap_text.subs.push_back(process_bitmap_subtitle(rect)); break; case SUBTITLE_TEXT: cout << "XXX: SUBTITLE_TEXT " << rect->text << "\n"; @@ -649,6 +689,10 @@ FFmpegDecoder::decode_and_process_subtitle_packet (AVPacket* packet) } } + if (!bitmap_text.subs.empty()) { + only_text()->emit_bitmap_start(bitmap_text); + } + if (_current_subtitle_to) { only_text()->emit_stop (*_current_subtitle_to); } @@ -657,8 +701,8 @@ FFmpegDecoder::decode_and_process_subtitle_packet (AVPacket* packet) } -void -FFmpegDecoder::process_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime from) +BitmapText +FFmpegDecoder::process_bitmap_subtitle (AVSubtitleRect const * rect) { /* Note BGRA is expressed little-endian, so the first byte in the word is B, second G, third R, fourth A. @@ -741,7 +785,7 @@ FFmpegDecoder::process_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime static_cast(rect->h) / target_height ); - only_text()->emit_bitmap_start (from, image, scaled_rect); + return { image, scaled_rect }; } @@ -771,7 +815,8 @@ FFmpegDecoder::process_ass_subtitle (string ass, ContentTime from) base, text, _ffmpeg_content->video->size().width, - _ffmpeg_content->video->size().height + _ffmpeg_content->video->size().height, + sub::Colour(1, 1, 1) ); for (auto const& i: sub::collect>(raw)) {