X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fffmpeg_decoder.cc;h=5e2cb8638804b328e36e1c89cd127e3e449d2535;hb=a2debda6473273338fd6f2c7936295eb387f9e72;hp=baec57f3d24e19473df2dd74788caa7a411e1841;hpb=fa4d2529d63bcfa7cf7c516b7227e20a1b6dec7e;p=dcpomatic.git diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index baec57f3d..5e2cb8638 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -86,6 +86,9 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr c, shared_ptr if (c->video) { video.reset (new VideoDecoder (this, c, log)); _pts_offset = pts_offset (c->ffmpeg_audio_streams(), c->first_video(), c->active_video_frame_rate()); + /* It doesn't matter what size or pixel format this is, it just needs to be black */ + _black_image.reset (new Image (AV_PIX_FMT_RGB24, dcp::Size (128, 128), true)); + _black_image->make_black (); } else { _pts_offset = ContentTime (); } @@ -97,6 +100,8 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr c, shared_ptr if (c->subtitle) { subtitle.reset (new SubtitleDecoder (this, c->subtitle, log)); } + + _next_time.resize (_format_context->nb_streams); } void @@ -113,6 +118,35 @@ FFmpegDecoder::flush () if (audio) { decode_audio_packet (); + } + + /* Make sure all streams are the same length and round up to the next video frame */ + + FrameRateChange const frc = _ffmpeg_content->film()->active_frame_rate_change(_ffmpeg_content->position()); + ContentTime full_length (_ffmpeg_content->full_length(), frc); + full_length = full_length.ceil (frc.source); + if (video) { + double const vfr = _ffmpeg_content->video_frame_rate().get(); + Frame const f = full_length.frames_round (vfr); + Frame v = video->position().frames_round (vfr); + while (v < f) { + video->emit (shared_ptr (new RawImageProxy (_black_image)), v); + ++v; + } + } + + BOOST_FOREACH (shared_ptr i, _ffmpeg_content->ffmpeg_audio_streams ()) { + ContentTime a = audio->stream_position(i); + while (a < full_length) { + ContentTime to_do = min (full_length - a, ContentTime::from_seconds (0.1)); + shared_ptr silence (new AudioBuffers (i->channels(), to_do.frames_ceil (i->frame_rate()))); + silence->make_silent (); + audio->emit (i, silence, a); + a += to_do; + } + } + + if (audio) { audio->flush (); } } @@ -143,7 +177,7 @@ FFmpegDecoder::pass () if (_video_stream && si == _video_stream.get() && !video->ignore()) { decode_video_packet (); - } else if (fc->subtitle_stream() && fc->subtitle_stream()->uses_index (_format_context, si)) { + } else if (fc->subtitle_stream() && fc->subtitle_stream()->uses_index(_format_context, si) && !subtitle->ignore()) { decode_subtitle_packet (); } else { decode_audio_packet (); @@ -171,8 +205,10 @@ FFmpegDecoder::deinterleave_audio (shared_ptr stream) const of the block that do not form a complete sample or frame they will be dropped. */ int const total_samples = size / bytes_per_audio_sample (stream); - int const frames = total_samples / stream->channels(); - shared_ptr audio (new AudioBuffers (stream->channels(), frames)); + int const channels = stream->channels(); + int const frames = total_samples / channels; + shared_ptr audio (new AudioBuffers (channels, frames)); + float** data = audio->data(); switch (audio_sample_format (stream)) { case AV_SAMPLE_FMT_U8: @@ -181,10 +217,10 @@ FFmpegDecoder::deinterleave_audio (shared_ptr stream) const int sample = 0; int channel = 0; for (int i = 0; i < total_samples; ++i) { - audio->data(channel)[sample] = float(*p++) / (1 << 23); + data[channel][sample] = float(*p++) / (1 << 23); ++channel; - if (channel == stream->channels()) { + if (channel == channels) { channel = 0; ++sample; } @@ -198,10 +234,10 @@ FFmpegDecoder::deinterleave_audio (shared_ptr stream) const int sample = 0; int channel = 0; for (int i = 0; i < total_samples; ++i) { - audio->data(channel)[sample] = float(*p++) / (1 << 15); + data[channel][sample] = float(*p++) / (1 << 15); ++channel; - if (channel == stream->channels()) { + if (channel == channels) { channel = 0; ++sample; } @@ -212,9 +248,9 @@ FFmpegDecoder::deinterleave_audio (shared_ptr stream) const case AV_SAMPLE_FMT_S16P: { int16_t** p = reinterpret_cast (_frame->data); - for (int i = 0; i < stream->channels(); ++i) { + for (int i = 0; i < channels; ++i) { for (int j = 0; j < frames; ++j) { - audio->data(i)[j] = static_cast(p[i][j]) / (1 << 15); + data[i][j] = static_cast(p[i][j]) / (1 << 15); } } } @@ -226,10 +262,10 @@ FFmpegDecoder::deinterleave_audio (shared_ptr stream) const int sample = 0; int channel = 0; for (int i = 0; i < total_samples; ++i) { - audio->data(channel)[sample] = static_cast(*p++) / 2147483648; + data[channel][sample] = static_cast(*p++) / 2147483648; ++channel; - if (channel == stream->channels()) { + if (channel == channels) { channel = 0; ++sample; } @@ -240,9 +276,9 @@ FFmpegDecoder::deinterleave_audio (shared_ptr stream) const case AV_SAMPLE_FMT_S32P: { int32_t** p = reinterpret_cast (_frame->data); - for (int i = 0; i < stream->channels(); ++i) { + for (int i = 0; i < channels; ++i) { for (int j = 0; j < frames; ++j) { - audio->data(i)[j] = static_cast(p[i][j]) / 2147483648; + data[i][j] = static_cast(p[i][j]) / 2147483648; } } } @@ -254,10 +290,10 @@ FFmpegDecoder::deinterleave_audio (shared_ptr stream) const int sample = 0; int channel = 0; for (int i = 0; i < total_samples; ++i) { - audio->data(channel)[sample] = *p++; + data[channel][sample] = *p++; ++channel; - if (channel == stream->channels()) { + if (channel == channels) { channel = 0; ++sample; } @@ -270,9 +306,9 @@ FFmpegDecoder::deinterleave_audio (shared_ptr stream) const float** p = reinterpret_cast (_frame->data); /* Sometimes there aren't as many channels in the _frame as in the stream */ for (int i = 0; i < _frame->channels; ++i) { - memcpy (audio->data(i), p[i], frames * sizeof(float)); + memcpy (data[i], p[i], frames * sizeof(float)); } - for (int i = _frame->channels; i < stream->channels(); ++i) { + for (int i = _frame->channels; i < channels; ++i) { audio->make_silent (i); } } @@ -360,11 +396,12 @@ FFmpegDecoder::decode_audio_packet () */ AVPacket copy_packet = _packet; + int const stream_index = copy_packet.stream_index; /* XXX: inefficient */ vector > streams = ffmpeg_content()->ffmpeg_audio_streams (); vector >::const_iterator stream = streams.begin (); - while (stream != streams.end () && !(*stream)->uses_index (_format_context, copy_packet.stream_index)) { + while (stream != streams.end () && !(*stream)->uses_index (_format_context, stream_index)) { ++stream; } @@ -394,13 +431,24 @@ FFmpegDecoder::decode_audio_packet () } if (frame_finished) { - ContentTime ct = ContentTime::from_seconds ( - av_frame_get_best_effort_timestamp (_frame) * - av_q2d ((*stream)->stream (_format_context)->time_base)) - + _pts_offset; - shared_ptr data = deinterleave_audio (*stream); + ContentTime ct; + if (_frame->pts == AV_NOPTS_VALUE && _next_time[stream_index]) { + /* 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). + */ + ct = *_next_time[stream_index]; + } else { + ct = ContentTime::from_seconds ( + av_frame_get_best_effort_timestamp (_frame) * + av_q2d ((*stream)->stream (_format_context)->time_base)) + + _pts_offset; + } + + _next_time[stream_index] = ct + ContentTime::from_frames(data->frames(), (*stream)->frame_rate()); + if (ct < ContentTime ()) { /* Discard audio data that comes before time 0 */ Frame const remove = min (int64_t (data->frames()), (-ct).frames_ceil(double((*stream)->frame_rate ()))); @@ -410,7 +458,16 @@ FFmpegDecoder::decode_audio_packet () } if (ct < ContentTime()) { - LOG_WARNING ("Crazy timestamp %1", to_string (ct)); + LOG_WARNING ( + "Crazy timestamp %1 for %2 samples in stream %3 packet pts %4 (ts=%5 tb=%6, off=%7)", + to_string(ct), + data->frames(), + copy_packet.stream_index, + copy_packet.pts, + av_frame_get_best_effort_timestamp(_frame), + av_q2d((*stream)->stream(_format_context)->time_base), + to_string(_pts_offset) + ); } /* Give this data provided there is some, and its time is sane */ @@ -615,7 +672,12 @@ FFmpegDecoder::decode_ass_subtitle (string ass, ContentTime from) } sub::RawSubtitle base; - list raw = sub::SSAReader::parse_line (base, bits[9]); + list raw = sub::SSAReader::parse_line ( + base, + bits[9], + _ffmpeg_content->video->size().width, + _ffmpeg_content->video->size().height + ); BOOST_FOREACH (sub::Subtitle const & i, sub::collect > (raw)) { subtitle->emit_text_start (from, i);