diff options
| author | Carl Hetherington <cth@carlh.net> | 2014-01-03 00:47:32 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2014-01-03 00:47:32 +0000 |
| commit | 821b556975c44bcb2c8607fc26462b7b79db2fe6 (patch) | |
| tree | 14a2ce9227c8ab385ee2b86cc57e54ab08a9a8cd /src/lib | |
| parent | 3dfdd5795cf6514e15fdbece54c28f3bddc2aadc (diff) | |
Various fix-ups.
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/audio_decoder.cc | 27 | ||||
| -rw-r--r-- | src/lib/audio_decoder.h | 8 | ||||
| -rw-r--r-- | src/lib/decoded.h | 52 | ||||
| -rw-r--r-- | src/lib/decoder.cc | 2 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 60 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.h | 4 | ||||
| -rw-r--r-- | src/lib/image_decoder.cc | 4 | ||||
| -rw-r--r-- | src/lib/image_decoder.h | 2 | ||||
| -rw-r--r-- | src/lib/player.cc | 4 | ||||
| -rw-r--r-- | src/lib/sndfile_decoder.cc | 6 | ||||
| -rw-r--r-- | src/lib/sndfile_decoder.h | 6 | ||||
| -rw-r--r-- | src/lib/video_decoder.cc | 14 | ||||
| -rw-r--r-- | src/lib/video_decoder.h | 2 |
13 files changed, 109 insertions, 82 deletions
diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc index aecf39644..d85bfaee2 100644 --- a/src/lib/audio_decoder.cc +++ b/src/lib/audio_decoder.cc @@ -22,6 +22,8 @@ #include "exceptions.h" #include "log.h" #include "resampler.h" +#include "util.h" +#include "film.h" #include "i18n.h" @@ -35,22 +37,26 @@ using boost::shared_ptr; AudioDecoder::AudioDecoder (shared_ptr<const Film> film, shared_ptr<const AudioContent> content) : Decoder (film) , _audio_content (content) - , _last_audio (0) + , _audio_position (0) { if (content->output_audio_frame_rate() != content->content_audio_frame_rate() && content->audio_channels ()) { _resampler.reset (new Resampler (content->content_audio_frame_rate(), content->output_audio_frame_rate(), content->audio_channels ())); } } +/** Audio timestamping is made hard by many factors, but the final nail in the coffin is resampling. + * We have to assume that we are feeding continuous data into the resampler, and so we get continuous + * data out. Hence we do the timestamping here, post-resampler, just by counting samples. + */ void -AudioDecoder::audio (shared_ptr<const AudioBuffers> data, ContentTime time) +AudioDecoder::audio (shared_ptr<const AudioBuffers> data) { if (_resampler) { data = _resampler->run (data); } - _pending.push_back (shared_ptr<DecodedAudio> (new DecodedAudio (data, time))); - _last_audio = time + (data->frames() * TIME_HZ / _audio_content->output_audio_frame_rate()); + _pending.push_back (shared_ptr<DecodedAudio> (new DecodedAudio (data, _audio_position))); + _audio_position += data->frames (); } void @@ -62,6 +68,17 @@ AudioDecoder::flush () shared_ptr<const AudioBuffers> b = _resampler->flush (); if (b) { - audio (b, _last_audio); + _pending.push_back (shared_ptr<DecodedAudio> (new DecodedAudio (b, _audio_position))); + _audio_position += b->frames (); } } + +void +AudioDecoder::seek (ContentTime t, bool) +{ + shared_ptr<const Film> film = _film.lock (); + assert (film); + + FrameRateChange frc = film->active_frame_rate_change (_audio_content->position ()); + _audio_position = (t + first_audio()) / frc.speed_up; +} diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h index a295df0cc..12f8505f6 100644 --- a/src/lib/audio_decoder.h +++ b/src/lib/audio_decoder.h @@ -44,15 +44,17 @@ public: return _audio_content; } + void seek (ContentTime time, bool accurate); + protected: - void audio (boost::shared_ptr<const AudioBuffers>, ContentTime); + virtual ContentTime first_audio () const = 0; + void audio (boost::shared_ptr<const AudioBuffers>); void flush (); boost::shared_ptr<const AudioContent> _audio_content; boost::shared_ptr<Resampler> _resampler; - /* End time of last audio that we wrote to _pending; only used for flushing the resampler */ - ContentTime _last_audio; + AudioFrame _audio_position; }; #endif diff --git a/src/lib/decoded.h b/src/lib/decoded.h index 1de2ff79e..ff32e43f2 100644 --- a/src/lib/decoded.h +++ b/src/lib/decoded.h @@ -22,6 +22,7 @@ #include "types.h" #include "rect.h" +#include "util.h" class Image; @@ -29,20 +30,13 @@ class Decoded { public: Decoded () - : content_time (0) - , dcp_time (0) - {} - - Decoded (DCPTime ct) - : content_time (ct) - , dcp_time (0) + : dcp_time (0) {} virtual ~Decoded () {} - virtual void set_dcp_times (float speed_up, DCPTime offset) = 0; + virtual void set_dcp_times (VideoFrame, AudioFrame, FrameRateChange, DCPTime) = 0; - ContentTime content_time; DCPTime dcp_time; }; @@ -53,62 +47,70 @@ public: DecodedVideo () : eyes (EYES_BOTH) , same (false) + , frame (0) {} - DecodedVideo (boost::shared_ptr<const Image> im, Eyes e, bool s, ContentTime ct) - : Decoded (ct) - , image (im) + DecodedVideo (boost::shared_ptr<const Image> im, Eyes e, bool s, VideoFrame f) + : image (im) , eyes (e) , same (s) + , frame (f) {} - void set_dcp_times (float speed_up, DCPTime offset) { - dcp_time = rint (content_time / speed_up) + offset; + void set_dcp_times (VideoFrame video_frame_rate, AudioFrame, FrameRateChange frc, DCPTime offset) + { + dcp_time = frame * TIME_HZ * frc.factor() / video_frame_rate + offset; } boost::shared_ptr<const Image> image; Eyes eyes; bool same; + VideoFrame frame; }; class DecodedAudio : public Decoded { public: - DecodedAudio (boost::shared_ptr<const AudioBuffers> d, ContentTime ct) - : Decoded (ct) - , data (d) + DecodedAudio (boost::shared_ptr<const AudioBuffers> d, AudioFrame f) + : data (d) + , frame (f) {} - void set_dcp_times (float speed_up, DCPTime offset) { - dcp_time = rint (content_time / speed_up) + offset; + void set_dcp_times (VideoFrame, AudioFrame audio_frame_rate, FrameRateChange, DCPTime offset) + { + dcp_time = frame * TIME_HZ / audio_frame_rate + offset; } boost::shared_ptr<const AudioBuffers> data; + AudioFrame frame; }; class DecodedSubtitle : public Decoded { public: DecodedSubtitle () - : content_time_to (0) + : content_time (0) + , content_time_to (0) , dcp_time_to (0) {} DecodedSubtitle (boost::shared_ptr<Image> im, dcpomatic::Rect<double> r, ContentTime f, ContentTime t) - : Decoded (f) - , image (im) + : image (im) , rect (r) + , content_time (f) , content_time_to (t) , dcp_time_to (0) {} - void set_dcp_times (float speed_up, DCPTime offset) { - dcp_time = rint (content_time / speed_up) + offset; - dcp_time_to = rint (content_time_to / speed_up) + offset; + void set_dcp_times (VideoFrame, AudioFrame, FrameRateChange frc, DCPTime offset) + { + dcp_time = rint (content_time / frc.speed_up) + offset; + dcp_time_to = rint (content_time_to / frc.speed_up) + offset; } boost::shared_ptr<Image> image; dcpomatic::Rect<double> rect; + ContentTime content_time; ContentTime content_time_to; DCPTime dcp_time_to; }; diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 4e136d619..30244b40b 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -47,7 +47,7 @@ Decoder::peek () _done = pass (); } - if (_done) { + if (_done && _pending.empty ()) { return shared_ptr<Decoded> (); } diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 298284512..25fe655be 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -68,8 +68,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC , _subtitle_codec (0) , _decode_video (video) , _decode_audio (audio) - , _video_pts_offset (0) - , _audio_pts_offset (0) + , _pts_offset (0) { setup_subtitle (); @@ -82,9 +81,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC 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 + audio_pts_offset; - video_pts_to_use = video_pts_from_ffmpeg + video_pts_offset; + We will do pts_to_use = pts_from_ffmpeg + pts_offset; */ bool const have_video = video && c->first_video(); @@ -93,16 +90,16 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC /* First, make one of them start at 0 */ if (have_audio && have_video) { - _video_pts_offset = _audio_pts_offset = - min (c->first_video().get(), c->audio_stream()->first_audio.get()); + _pts_offset = - min (c->first_video().get(), c->audio_stream()->first_audio.get()); } else if (have_video) { - _video_pts_offset = - c->first_video().get(); + _pts_offset = - c->first_video().get(); } else if (have_audio) { - _audio_pts_offset = - c->audio_stream()->first_audio.get(); + _pts_offset = - c->audio_stream()->first_audio.get(); } /* Now adjust both so that the video pts starts on a frame */ if (have_video && have_audio) { - double first_video = c->first_video().get() + _video_pts_offset; + double first_video = c->first_video().get() + _pts_offset; double const old_first_video = first_video; /* Round the first video up to a frame boundary */ @@ -110,8 +107,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC first_video = ceil (first_video * c->video_frame_rate()) / c->video_frame_rate (); } - _video_pts_offset += first_video - old_first_video; - _audio_pts_offset += first_video - old_first_video; + _pts_offset += first_video - old_first_video; } } @@ -325,10 +321,10 @@ FFmpegDecoder::minimal_run (boost::function<bool (optional<ContentTime>, optiona r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet); if (r >= 0 && finished) { last_video = rint ( - (av_frame_get_best_effort_timestamp (_frame) * time_base + _video_pts_offset) * TIME_HZ + (av_frame_get_best_effort_timestamp (_frame) * time_base + _pts_offset) * TIME_HZ ); } - + } else if (_ffmpeg_content->audio_stream() && _packet.stream_index == _ffmpeg_content->audio_stream()->index (_format_context)) { AVPacket copy_packet = _packet; while (copy_packet.size > 0) { @@ -337,7 +333,7 @@ FFmpegDecoder::minimal_run (boost::function<bool (optional<ContentTime>, optiona r = avcodec_decode_audio4 (audio_codec_context(), _frame, &finished, &_packet); if (r >= 0 && finished) { last_audio = rint ( - (av_frame_get_best_effort_timestamp (_frame) * time_base + _audio_pts_offset) * TIME_HZ + (av_frame_get_best_effort_timestamp (_frame) * time_base + _pts_offset) * TIME_HZ ); } @@ -367,18 +363,21 @@ FFmpegDecoder::seek_final_finished (int n, int done) const void FFmpegDecoder::seek_and_flush (ContentTime t) { - int64_t s = ((double (t) / TIME_HZ) - _video_pts_offset) / + int64_t s = ((double (t) / TIME_HZ) - _pts_offset) / av_q2d (_format_context->streams[_video_stream]->time_base); - + if (_ffmpeg_content->audio_stream ()) { s = min ( s, int64_t ( - ((double (t) / TIME_HZ) - _audio_pts_offset) / + ((double (t) / TIME_HZ) - _pts_offset) / av_q2d (_ffmpeg_content->audio_stream()->stream(_format_context)->time_base) ) ); } + /* Ridiculous empirical hack */ + s--; + av_seek_frame (_format_context, _video_stream, s, AVSEEK_FLAG_BACKWARD); avcodec_flush_buffers (video_codec_context()); @@ -394,6 +393,7 @@ void FFmpegDecoder::seek (ContentTime time, bool accurate) { Decoder::seek (time, accurate); + AudioDecoder::seek (time, accurate); /* If we are doing an accurate seek, our initial shot will be 200ms (200 being a number plucked from the air) earlier than we want to end up. The loop below @@ -405,7 +405,7 @@ FFmpegDecoder::seek (ContentTime time, bool accurate) if (initial_seek < 0) { initial_seek = 0; } - + /* Initial seek time in the video stream's timebase */ seek_and_flush (initial_seek); @@ -416,7 +416,7 @@ FFmpegDecoder::seek (ContentTime time, bool accurate) } int const N = minimal_run (boost::bind (&FFmpegDecoder::seek_overrun_finished, this, time, _1, _2)); - + seek_and_flush (initial_seek); if (N > 0) { minimal_run (boost::bind (&FFmpegDecoder::seek_final_finished, this, N - 1, _3)); @@ -445,16 +445,11 @@ FFmpegDecoder::decode_audio_packet () } if (frame_finished) { - ContentTime const t = rint ( - (av_q2d (_format_context->streams[copy_packet.stream_index]->time_base) - * av_frame_get_best_effort_timestamp(_frame) + _audio_pts_offset) * TIME_HZ - ); - int const data_size = av_samples_get_buffer_size ( 0, audio_codec_context()->channels, _frame->nb_samples, audio_sample_format (), 1 ); - audio (deinterleave_audio (_frame->data, data_size), t); + audio (deinterleave_audio (_frame->data, data_size)); } copy_packet.data += decode_result; @@ -503,8 +498,9 @@ FFmpegDecoder::decode_video_packet () } if (i->second != AV_NOPTS_VALUE) { - ContentTime const t = rint ((i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _video_pts_offset) * TIME_HZ); - video (image, false, t); + double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset; + VideoFrame const f = rint (pts * _ffmpeg_content->video_frame_rate ()); + video (image, false, f); } else { shared_ptr<const Film> film = _film.lock (); assert (film); @@ -607,3 +603,13 @@ FFmpegDecoder::decode_subtitle_packet () avsubtitle_free (&sub); } + +ContentTime +FFmpegDecoder::first_audio () const +{ + if (!_ffmpeg_content->audio_stream ()) { + return 0; + } + + return _ffmpeg_content->audio_stream()->first_audio.get_value_or(0) + _pts_offset; +} diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 55b624bd6..680e39c39 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -58,6 +58,7 @@ private: bool pass (); void flush (); + ContentTime first_audio () const; void setup_subtitle (); @@ -85,6 +86,5 @@ private: bool _decode_video; bool _decode_audio; - double _video_pts_offset; - double _audio_pts_offset; + double _pts_offset; }; diff --git a/src/lib/image_decoder.cc b/src/lib/image_decoder.cc index a5ca67e0d..204849ecf 100644 --- a/src/lib/image_decoder.cc +++ b/src/lib/image_decoder.cc @@ -49,7 +49,7 @@ ImageDecoder::pass () } if (_image && _image_content->still ()) { - video (_image, true, _video_position * TIME_HZ / _video_content->video_frame_rate ()); + video (_image, true, _video_position); ++_video_position; return false; } @@ -82,7 +82,7 @@ ImageDecoder::pass () delete magick_image; - video (_image, false, _video_position * TIME_HZ / _video_content->video_frame_rate ()); + video (_image, false, _video_position); ++_video_position; return false; diff --git a/src/lib/image_decoder.h b/src/lib/image_decoder.h index f4d81dfb5..63b4c58e3 100644 --- a/src/lib/image_decoder.h +++ b/src/lib/image_decoder.h @@ -41,6 +41,6 @@ private: boost::shared_ptr<const ImageContent> _image_content; boost::shared_ptr<Image> _image; - ContentTime _video_position; + VideoFrame _video_position; }; diff --git a/src/lib/player.cc b/src/lib/player.cc index 4fe81d4b2..da7e7c147 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -119,10 +119,10 @@ Player::pass () break; } - dec->set_dcp_times ((*i)->frc.speed_up, offset); + dec->set_dcp_times (_film->video_frame_rate(), _film->audio_frame_rate(), (*i)->frc, offset); DCPTime const t = dec->dcp_time - offset; if (t >= (*i)->content->full_length() - (*i)->content->trim_end ()) { - /* In the end-trimmed part; decoder has nothing eles to give us */ + /* In the end-trimmed part; decoder has nothing else to give us */ dec.reset (); done = true; } else if (t >= (*i)->content->trim_start ()) { diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc index 5e3f3313b..1c651e614 100644 --- a/src/lib/sndfile_decoder.cc +++ b/src/lib/sndfile_decoder.cc @@ -45,7 +45,6 @@ SndfileDecoder::SndfileDecoder (shared_ptr<const Film> f, shared_ptr<const Sndfi throw DecodeError (_("could not open audio file for reading")); } - _done = 0; _remaining = _info.frames; } @@ -94,8 +93,7 @@ SndfileDecoder::pass () } data->set_frames (this_time); - audio (data, _done * TIME_HZ / audio_frame_rate ()); - _done += this_time; + audio (data); _remaining -= this_time; return _remaining == 0; @@ -123,7 +121,7 @@ void SndfileDecoder::seek (ContentTime t, bool accurate) { Decoder::seek (t, accurate); + AudioDecoder::seek (t, accurate); - _done = t * audio_frame_rate() / TIME_HZ; _remaining = _info.frames - _done; } diff --git a/src/lib/sndfile_decoder.h b/src/lib/sndfile_decoder.h index 9cbddc9f6..4ecea0846 100644 --- a/src/lib/sndfile_decoder.h +++ b/src/lib/sndfile_decoder.h @@ -36,15 +36,15 @@ public: int audio_frame_rate () const; private: - bool has_audio () const { - return true; + ContentTime first_audio () const { + return 0; } + bool pass (); boost::shared_ptr<const SndfileContent> _sndfile_content; SNDFILE* _sndfile; SF_INFO _info; - AudioFrame _done; AudioFrame _remaining; float* _deinterleave_buffer; }; diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index 6a16557cd..a3b45716c 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -35,25 +35,27 @@ VideoDecoder::VideoDecoder (shared_ptr<const Film> f, shared_ptr<const VideoCont /** Called by subclasses when they have a video frame ready */ void -VideoDecoder::video (shared_ptr<const Image> image, bool same, ContentTime time) +VideoDecoder::video (shared_ptr<const Image> image, bool same, VideoFrame frame) { switch (_video_content->video_frame_type ()) { case VIDEO_FRAME_TYPE_2D: - _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image, EYES_BOTH, same, time))); + _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image, EYES_BOTH, same, frame))); break; case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT: { int const half = image->size().width / 2; - _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, same, time))); - _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, same, time))); + _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, same, frame))); + _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, same, frame))); break; } case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM: { int const half = image->size().height / 2; - _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, same, time))); - _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, 0, half, 0), true), EYES_RIGHT, same, time))); + _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, same, frame))); + _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, 0, half, 0), true), EYES_RIGHT, same, frame))); break; } + default: + assert (false); } } diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index d8c362354..8947c2708 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -41,7 +41,7 @@ public: protected: - void video (boost::shared_ptr<const Image>, bool, ContentTime); + void video (boost::shared_ptr<const Image>, bool, VideoFrame); boost::shared_ptr<const VideoContent> _video_content; }; |
