X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fffmpeg_decoder.cc;h=8b07658ca427bd5e794bb4716b0b1e7f8ffcba01;hb=5e09964bf1801b76970ed960122249e1d273abea;hp=2baa99876b173adbf58e52d4774f80c5a2c393cd;hpb=3799e91d126d243d41c44dcb0ca1bfa66b53a57e;p=dcpomatic.git diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 2baa99876..8b07658ca 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()) { @@ -122,7 +125,7 @@ FFmpegDecoder::flush () /* EOF can happen if we've already sent a flush packet */ throw DecodeError (N_("avcodec_send_packet"), N_("FFmpegDecoder::flush"), r); } - r = avcodec_receive_frame (context, _frame); + r = avcodec_receive_frame (context, audio_frame(i)); if (r >= 0) { process_audio_frame (i); did_something = true; @@ -144,7 +147,7 @@ FFmpegDecoder::flush () 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); + video->emit (film(), make_shared(_black_image), v); ++v; } } @@ -187,6 +190,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]; @@ -217,8 +221,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(shared_ptr stream, AVFrame* frame) { auto format = static_cast(frame->format); @@ -324,12 +329,12 @@ FFmpegDecoder::deinterleave_audio (AVFrame* frame) case AV_SAMPLE_FMT_FLTP: { auto p = reinterpret_cast (frame->data); - DCPOMATIC_ASSERT (frame->channels <= channels); + DCPOMATIC_ASSERT(channels <= stream->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) { + for (int i = channels; i < stream->channels(); ++i) { audio->make_silent (i); } } @@ -398,13 +403,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()); @@ -423,12 +425,6 @@ FFmpegDecoder::seek (ContentTime time, bool accurate) for (auto& i: _next_time) { i.second = boost::optional(); } - - /* We find that we get some errors from av_send_packet after a seek. Perhaps we should ignore - * all of them (which seems risky), or perhaps we should have some proper fix. But instead - * let's ignore the next 2 errors. - */ - _errors_to_ignore = 2; } @@ -453,10 +449,13 @@ FFmpegDecoder::audio_stream_from_index (int index) const void FFmpegDecoder::process_audio_frame (shared_ptr stream) { - auto data = deinterleave_audio (_frame); + auto frame = audio_frame (stream); + auto data = deinterleave_audio(stream, frame); + + auto const time_base = stream->stream(_format_context)->time_base; ContentTime ct; - if (_frame->pts == AV_NOPTS_VALUE) { + if (frame->pts == AV_NOPTS_VALUE) { /* In some streams we see not every frame coming through with a timestamp; for those that have AV_NOPTS_VALUE we need to work out the timestamp ourselves. This is particularly noticeable with TrueHD streams (see #1111). @@ -466,9 +465,17 @@ FFmpegDecoder::process_audio_frame (shared_ptr stream) } } else { ct = ContentTime::from_seconds ( - _frame->best_effort_timestamp * - av_q2d (stream->stream(_format_context)->time_base)) + frame->best_effort_timestamp * + 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()); @@ -487,8 +494,8 @@ FFmpegDecoder::process_audio_frame (shared_ptr stream) to_string(ct), data->frames(), stream->id(), - _frame->best_effort_timestamp, - av_q2d(stream->stream(_format_context)->time_base), + frame->best_effort_timestamp, + av_q2d(time_base), to_string(_pts_offset) ); } @@ -509,25 +516,18 @@ 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) { - /* We could cope with AVERROR(EAGAIN) and re-send the packet but I think it should never happen. - * Likewise I think AVERROR_EOF should not happen. - */ - if (_errors_to_ignore > 0) { - /* We see errors here after a seek, which is hopefully to be nothing to worry about */ - --_errors_to_ignore; - LOG_GENERAL("Ignoring error %1 avcodec_send_packet after seek; will ignore %2 more", r, _errors_to_ignore); - return; - } - throw DecodeError (N_("avcodec_send_packet"), N_("FFmpegDecoder::decode_and_process_audio_packet"), r); + LOG_WARNING("avcodec_send_packet returned %1 for an audio packet", r); } - while (r >= 0) { - r = avcodec_receive_frame (context, _frame); + 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; } @@ -547,46 +547,40 @@ FFmpegDecoder::decode_and_process_video_packet (AVPacket* packet) auto context = video_codec_context(); - 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_("FFmpegDecoder::decode_and_process_video_packet"), r); - } - - r = avcodec_receive_frame (context, _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); + } - /* We assume we'll only get one frame here, which I think is safe */ + /* 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); + } - boost::mutex::scoped_lock lm (_filter_graphs_mutex); + process_video_frame (); + } + } while (pending); - shared_ptr graph; + return true; +} - auto i = _filter_graphs.begin(); - while (i != _filter_graphs.end() && !(*i)->can_process (dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) { - ++i; - } - if (i == _filter_graphs.end ()) { - dcp::Fraction vfr (lrint(_ffmpeg_content->video_frame_rate().get() * 1000), 1000); - graph = make_shared(dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format, vfr); - graph->setup (_ffmpeg_content->filters ()); - _filter_graphs.push_back (graph); - LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format); - } else { - graph = *i; - } - - auto images = graph->process (_frame); +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) { @@ -604,8 +598,6 @@ FFmpegDecoder::decode_and_process_video_packet (AVPacket* packet) LOG_WARNING_NC ("Dropping frame without PTS"); } } - - return true; } @@ -648,6 +640,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]; @@ -655,7 +648,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"; @@ -666,6 +659,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); } @@ -674,8 +671,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. @@ -758,7 +755,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 }; } @@ -788,7 +785,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)) {