X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=blobdiff_plain;f=src%2Flib%2Fffmpeg_decoder.cc;h=83bce18308ae3b42de919ad6ffc26fe356ee3d61;hp=f8152b56e7629021d1a197b633cbe3d1559eaaff;hb=aeb835a18c8df347e0ed68fb24631b320abeb611;hpb=2651ef8c2fd486332457630d459c1e620e626c1b diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index f8152b56e..83bce1830 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -34,104 +34,56 @@ #include "ffmpeg_content.h" #include "raw_image_proxy.h" #include "film.h" -#include "timer.h" +#include "compose.hpp" extern "C" { #include #include } #include -#include #include #include #include #include -#include #include "i18n.h" -#define LOG_GENERAL(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL); -#define LOG_ERROR(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR); -#define LOG_WARNING_NC(...) _video_content->film()->log()->log (__VA_ARGS__, Log::TYPE_WARNING); -#define LOG_WARNING(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_WARNING); +#define LOG_GENERAL(...) _log->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL); +#define LOG_ERROR(...) _log->log (String::compose (__VA_ARGS__), LogEntry::TYPE_ERROR); +#define LOG_WARNING_NC(...) _log->log (__VA_ARGS__, LogEntry::TYPE_WARNING); +#define LOG_WARNING(...) _log->log (String::compose (__VA_ARGS__), LogEntry::TYPE_WARNING); using std::cout; -using std::string; using std::vector; using std::list; using std::min; using std::pair; -using std::make_pair; using std::max; using boost::shared_ptr; -using boost::optional; -using boost::dynamic_pointer_cast; using dcp::Size; -FFmpegDecoder::FFmpegDecoder (shared_ptr c, shared_ptr log) +FFmpegDecoder::FFmpegDecoder (shared_ptr c, shared_ptr log, bool fast) : VideoDecoder (c) - , AudioDecoder (c) + , AudioDecoder (c, fast) , SubtitleDecoder (c) , FFmpeg (c) , _log (log) + , _pts_offset (pts_offset (c->ffmpeg_audio_streams(), c->first_video(), c->video_frame_rate())) { - /* Audio and video frame PTS values may not start with 0. We want - to fiddle them so that: - 1. One of them starts at time 0. - 2. The first video PTS value ends up on a frame boundary. - - Then we remove big initial gaps in PTS and we allow our - insertion of black frames to work. - - We will do: - audio_pts_to_use = audio_pts_from_ffmpeg + pts_offset; - video_pts_to_use = video_pts_from_ffmpeg + pts_offset; - */ - - /* First, make one of them start at 0 */ - - vector > streams = c->ffmpeg_audio_streams (); - - _pts_offset = ContentTime::min (); - - if (c->first_video ()) { - _pts_offset = - c->first_video().get (); - } - - BOOST_FOREACH (shared_ptr i, streams) { - if (i->first_audio) { - _pts_offset = max (_pts_offset, - i->first_audio.get ()); - } - } - - /* If _pts_offset is positive we would be pushing things from a -ve PTS to be played. - I don't think we ever want to do that, as it seems things at -ve PTS are not meant - to be seen (use for alignment bars etc.); see mantis #418. - */ - if (_pts_offset > ContentTime ()) { - _pts_offset = ContentTime (); - } - - /* Now adjust so that the video pts starts on a frame */ - if (c->first_video ()) { - ContentTime first_video = c->first_video().get() + _pts_offset; - ContentTime const old_first_video = first_video; - _pts_offset += first_video.round_up (c->video_frame_rate ()) - old_first_video; - } } void FFmpegDecoder::flush () { /* Get any remaining frames */ - + _packet.data = 0; _packet.size = 0; - + /* XXX: should we reset _packet.data and size after each *_decode_* call? */ - + while (decode_video_packet ()) {} - + decode_audio_packet (); AudioDecoder::flush (); } @@ -152,7 +104,7 @@ FFmpegDecoder::pass () av_strerror (r, buf, sizeof(buf)); LOG_ERROR (N_("error on av_read_frame (%1) (%2)"), buf, r); } - + flush (); return true; } @@ -206,7 +158,7 @@ FFmpegDecoder::deinterleave_audio (shared_ptr stream, uint8_t } } break; - + case AV_SAMPLE_FMT_S16: { int16_t* p = reinterpret_cast (data[0]); @@ -234,7 +186,7 @@ FFmpegDecoder::deinterleave_audio (shared_ptr stream, uint8_t } } break; - + case AV_SAMPLE_FMT_S32: { int32_t* p = reinterpret_cast (data[0]); @@ -268,7 +220,7 @@ FFmpegDecoder::deinterleave_audio (shared_ptr stream, uint8_t } } break; - + case AV_SAMPLE_FMT_FLTP: { float** p = reinterpret_cast (data); @@ -314,14 +266,17 @@ FFmpegDecoder::seek (ContentTime time, bool accurate) /* XXX: it seems debatable whether PTS should be used here... http://www.mjbshaw.com/2012/04/seeking-in-ffmpeg-know-your-timestamp.html */ - - ContentTime const u = time - _pts_offset; + + ContentTime u = time - _pts_offset; + if (u < ContentTime ()) { + u = ContentTime (); + } av_seek_frame (_format_context, _video_stream, u.seconds() / av_q2d (_format_context->streams[_video_stream]->time_base), AVSEEK_FLAG_BACKWARD); avcodec_flush_buffers (video_codec_context()); /* XXX: should be flushing audio buffers? */ - + if (subtitle_codec_context ()) { avcodec_flush_buffers (subtitle_codec_context ()); } @@ -333,7 +288,7 @@ FFmpegDecoder::decode_audio_packet () /* Audio packets can contain multiple frames, so we may have to call avcodec_decode_audio4 several times. */ - + AVPacket copy_packet = _packet; /* XXX: inefficient */ @@ -347,7 +302,7 @@ FFmpegDecoder::decode_audio_packet () /* The packet's stream may not be an audio one; just ignore it in this method if so */ return; } - + while (copy_packet.size > 0) { int frame_finished; @@ -369,18 +324,30 @@ FFmpegDecoder::decode_audio_packet () } if (frame_finished) { - ContentTime const ct = ContentTime::from_seconds ( + ContentTime ct = ContentTime::from_seconds ( av_frame_get_best_effort_timestamp (_frame) * av_q2d ((*stream)->stream (_format_context)->time_base)) + _pts_offset; - + int const data_size = av_samples_get_buffer_size ( 0, (*stream)->stream(_format_context)->codec->channels, _frame->nb_samples, audio_sample_format (*stream), 1 ); - audio (*stream, deinterleave_audio (*stream, _frame->data, data_size), ct); + shared_ptr data = deinterleave_audio (*stream, _frame->data, data_size); + + 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 ()))); + data->move (remove, 0, data->frames() - remove); + data->set_frames (data->frames() - remove); + ct += ContentTime::from_frames (remove, (*stream)->frame_rate ()); + } + + if (data->frames() > 0) { + audio (*stream, data, ct); + } } - + copy_packet.data += decode_result; copy_packet.size -= decode_result; } @@ -397,7 +364,7 @@ FFmpegDecoder::decode_video_packet () boost::mutex::scoped_lock lm (_filter_graphs_mutex); shared_ptr graph; - + list >::iterator i = _filter_graphs.begin(); while (i != _filter_graphs.end() && !(*i)->can_process (dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) { ++i; @@ -416,12 +383,12 @@ FFmpegDecoder::decode_video_packet () for (list, int64_t> >::iterator i = images.begin(); i != images.end(); ++i) { shared_ptr image = i->first; - + if (i->second != AV_NOPTS_VALUE) { double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset.seconds (); video ( shared_ptr (new RawImageProxy (image)), - rint (pts * _ffmpeg_content->video_frame_rate ()) + llrint (pts * _ffmpeg_content->video_frame_rate ()) ); } else { LOG_WARNING_NC ("Dropping frame without PTS"); @@ -430,7 +397,7 @@ FFmpegDecoder::decode_video_packet () return true; } - + void FFmpegDecoder::decode_subtitle_packet () { @@ -439,14 +406,12 @@ FFmpegDecoder::decode_subtitle_packet () if (avcodec_decode_subtitle2 (subtitle_codec_context(), &sub, &got_subtitle, &_packet) < 0 || !got_subtitle) { return; } - + if (sub.num_rects <= 0) { /* Sometimes we get an empty AVSubtitle, which is used by some codecs to indicate that the previous subtitle should stop. We can ignore it here. */ return; - } else if (sub.num_rects > 1) { - throw DecodeError (_("multi-part subtitles not yet supported")); } /* Subtitle PTS (within the source, not taking into account any of the @@ -460,25 +425,27 @@ FFmpegDecoder::decode_subtitle_packet () period.to = sub_period.to.get() + _pts_offset; } else { /* We have to look up the `to' time in the stream's records */ - period.to = ffmpeg_content()->subtitle_stream()->find_subtitle_to (sub_period.from); + period.to = ffmpeg_content()->subtitle_stream()->find_subtitle_to (period.from); } - - AVSubtitleRect const * rect = sub.rects[0]; - - switch (rect->type) { - case SUBTITLE_NONE: - break; - case SUBTITLE_BITMAP: - decode_bitmap_subtitle (rect, period); - break; - case SUBTITLE_TEXT: - cout << "XXX: SUBTITLE_TEXT " << rect->text << "\n"; - break; - case SUBTITLE_ASS: - cout << "XXX: SUBTITLE_ASS " << rect->ass << "\n"; - break; + + for (unsigned int i = 0; i < sub.num_rects; ++i) { + AVSubtitleRect const * rect = sub.rects[i]; + + switch (rect->type) { + case SUBTITLE_NONE: + break; + case SUBTITLE_BITMAP: + decode_bitmap_subtitle (rect, period); + break; + case SUBTITLE_TEXT: + cout << "XXX: SUBTITLE_TEXT " << rect->text << "\n"; + break; + case SUBTITLE_ASS: + cout << "XXX: SUBTITLE_ASS " << rect->ass << "\n"; + break; + } } - + avsubtitle_free (&sub); } @@ -501,7 +468,7 @@ FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimeP G, third B, fourth A. */ shared_ptr image (new Image (PIX_FMT_RGBA, dcp::Size (rect->w, rect->h), true)); - + /* Start of the first line in the subtitle */ uint8_t* sub_p = rect->pict.data[0]; /* sub_p looks up into a BGRA palette which is here @@ -510,7 +477,7 @@ FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimeP uint32_t const * palette = (uint32_t *) rect->pict.data[1]; /* Start of the output data */ uint32_t* out_p = (uint32_t *) image->data()[0]; - + for (int y = 0; y < rect->h; ++y) { uint8_t* sub_line_p = sub_p; uint32_t* out_line_p = out_p; @@ -521,7 +488,7 @@ FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimeP sub_p += rect->pict.linesize[0]; out_p += image->stride()[0] / sizeof (uint32_t); } - + dcp::Size const vs = _ffmpeg_content->video_size (); dcpomatic::Rect const scaled_rect ( static_cast (rect->x) / vs.width, @@ -529,7 +496,6 @@ FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimeP static_cast (rect->w) / vs.width, static_cast (rect->h) / vs.height ); - + image_subtitle (period, image, scaled_rect); } -