From 9c399a21b37d83ceb2c81706975e2c46d1a3f673 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 18 Dec 2013 09:39:36 +0000 Subject: Considerable rework of decoder timing; tests pass, at least. --- src/lib/decoder.cc | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'src/lib/decoder.cc') diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 3f4cda6eb..18c5acd35 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -36,3 +36,32 @@ Decoder::Decoder (shared_ptr f) { } + +shared_ptr +Decoder::peek () +{ + while (_pending.empty () && !pass ()) {} + + if (_pending.empty ()) { + return shared_ptr (); + } + + return _pending.front (); +} + +shared_ptr +Decoder::get () +{ + shared_ptr d = peek (); + if (d) { + _pending.pop_front (); + } + + return d; +} + +void +Decoder::seek (ContentTime, bool) +{ + _pending.clear (); +} -- cgit v1.2.3 From 64dc289b343641fc37db51fcd6b3069078cb5edf Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 24 Dec 2013 02:52:35 +0000 Subject: Some fixes to playback of drop-frame content. --- src/lib/decoder.cc | 21 ++++++++++++++------- src/lib/decoder.h | 2 +- src/lib/player.cc | 10 +++++----- test/seek_zero_test.cc | 4 ++-- 4 files changed, 22 insertions(+), 15 deletions(-) (limited to 'src/lib/decoder.cc') diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 18c5acd35..7f4973737 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -23,6 +23,7 @@ #include "film.h" #include "decoder.h" +#include "decoded.h" #include "i18n.h" @@ -37,10 +38,19 @@ Decoder::Decoder (shared_ptr f) } +class DecodedSorter +{ +public: + bool operator() (shared_ptr a, shared_ptr b) + { + return a->dcp_time < b->dcp_time; + } +}; + shared_ptr Decoder::peek () { - while (_pending.empty () && !pass ()) {} + while (_pending.empty() && !pass ()) {} if (_pending.empty ()) { return shared_ptr (); @@ -49,15 +59,12 @@ Decoder::peek () return _pending.front (); } -shared_ptr -Decoder::get () +void +Decoder::consume () { - shared_ptr d = peek (); - if (d) { + if (!_pending.empty ()) { _pending.pop_front (); } - - return d; } void diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 654cacad4..1c6aff359 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -49,7 +49,7 @@ public: virtual void seek (ContentTime time, bool accurate); boost::shared_ptr peek (); - boost::shared_ptr get (); + void consume (); protected: diff --git a/src/lib/player.cc b/src/lib/player.cc index 0f2cc0365..3e282136f 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -147,7 +147,7 @@ Player::pass () if (_just_did_inaccurate_seek) { /* Just emit; no subtlety */ emit_video (earliest_piece, dv); - earliest_piece->decoder->get (); + earliest_piece->decoder->consume (); } else if (earliest_time > (_video_position + half_frame)) { /* See if we're inside some video content */ @@ -167,14 +167,14 @@ Player::pass () } else { if ( - dv->dcp_time >= _video_position && + abs (dv->dcp_time - _video_position) < half_frame && !earliest_piece->content->trimmed (dv->dcp_time - earliest_piece->content->position ()) ) { emit_video (earliest_piece, dv); } - earliest_piece->decoder->get (); + earliest_piece->decoder->consume (); } } else if (da && _audio) { @@ -182,13 +182,13 @@ Player::pass () emit_silence (earliest_time - _audio_position); } else { emit_audio (earliest_piece, da); - earliest_piece->decoder->get (); + earliest_piece->decoder->consume (); } } else if (ds && _video) { _in_subtitle.piece = earliest_piece; _in_subtitle.subtitle = ds; update_subtitle (); - earliest_piece->decoder->get (); + earliest_piece->decoder->consume (); } _just_did_inaccurate_seek = false; diff --git a/test/seek_zero_test.cc b/test/seek_zero_test.cc index d298a772b..0987f6b9b 100644 --- a/test/seek_zero_test.cc +++ b/test/seek_zero_test.cc @@ -45,10 +45,10 @@ BOOST_AUTO_TEST_CASE (seek_zero_test) wait_for_jobs (); FFmpegDecoder decoder (film, content, true, false); - shared_ptr a = decoder.get (); + shared_ptr a = decoder.peek (); cout << a->content_time << "\n"; decoder.seek (0, true); - shared_ptr b = decoder.get (); + shared_ptr b = decoder.peek (); cout << b->content_time << "\n"; /* a will be after no seek, and b after a seek to zero, which should -- cgit v1.2.3 From abc30110ea12e3e60f45f13556e8eda36a350458 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 24 Dec 2013 16:27:55 +0000 Subject: Tidy up playback slightly. --- src/lib/decoder.cc | 9 --------- src/lib/player.cc | 22 +++++++++++----------- 2 files changed, 11 insertions(+), 20 deletions(-) (limited to 'src/lib/decoder.cc') diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 7f4973737..7102f2aa4 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -38,15 +38,6 @@ Decoder::Decoder (shared_ptr f) } -class DecodedSorter -{ -public: - bool operator() (shared_ptr a, shared_ptr b) - { - return a->dcp_time < b->dcp_time; - } -}; - shared_ptr Decoder::peek () { diff --git a/src/lib/player.cc b/src/lib/player.cc index 3e282136f..d08b7491d 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -144,11 +144,14 @@ Player::pass () if (dv && _video) { DCPTime const half_frame = TIME_HZ / (2 * _film->video_frame_rate ()); + bool consume = true; + if (_just_did_inaccurate_seek) { + /* Just emit; no subtlety */ emit_video (earliest_piece, dv); - earliest_piece->decoder->consume (); - } else if (earliest_time > (_video_position + half_frame)) { + + } else if (abs (dv->dcp_time - _video_position) > half_frame) { /* See if we're inside some video content */ list >::iterator i = _pieces.begin(); @@ -164,18 +167,15 @@ Player::pass () emit_video (_last_incoming_video.weak_piece, _last_incoming_video.video); } - } else { + consume = false; - if ( - abs (dv->dcp_time - _video_position) < half_frame && - !earliest_piece->content->trimmed (dv->dcp_time - earliest_piece->content->position ()) - ) { + } else if (abs (dv->dcp_time - _video_position) < half_frame) { + emit_video (earliest_piece, dv); + } - emit_video (earliest_piece, dv); - } - + if (consume) { earliest_piece->decoder->consume (); - } + } } else if (da && _audio) { if (!_just_did_inaccurate_seek && earliest_time > _audio_position) { -- cgit v1.2.3 From 0987c41ac468fd93bbb444093e66d5d6753cb927 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 28 Dec 2013 00:12:32 +0000 Subject: Fix termination of decodes. --- src/lib/decoder.cc | 9 +++++++-- src/lib/decoder.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src/lib/decoder.cc') diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 7102f2aa4..4e136d619 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -27,6 +27,7 @@ #include "i18n.h" +using std::cout; using boost::shared_ptr; /** @param f Film. @@ -34,6 +35,7 @@ using boost::shared_ptr; */ Decoder::Decoder (shared_ptr f) : _film (f) + , _done (false) { } @@ -41,9 +43,11 @@ Decoder::Decoder (shared_ptr f) shared_ptr Decoder::peek () { - while (_pending.empty() && !pass ()) {} + while (!_done && _pending.empty ()) { + _done = pass (); + } - if (_pending.empty ()) { + if (_done) { return shared_ptr (); } @@ -62,4 +66,5 @@ void Decoder::seek (ContentTime, bool) { _pending.clear (); + _done = false; } diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 1c6aff359..6646b0e76 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -66,6 +66,7 @@ protected: boost::weak_ptr _film; std::list > _pending; + bool _done; }; #endif -- cgit v1.2.3 From 821b556975c44bcb2c8607fc26462b7b79db2fe6 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 3 Jan 2014 00:47:32 +0000 Subject: Various fix-ups. --- src/lib/audio_decoder.cc | 27 +++++++++++++++++---- src/lib/audio_decoder.h | 8 ++++--- src/lib/decoded.h | 52 +++++++++++++++++++++------------------- src/lib/decoder.cc | 2 +- src/lib/ffmpeg_decoder.cc | 60 +++++++++++++++++++++++++--------------------- src/lib/ffmpeg_decoder.h | 4 ++-- src/lib/image_decoder.cc | 4 ++-- src/lib/image_decoder.h | 2 +- src/lib/player.cc | 4 ++-- src/lib/sndfile_decoder.cc | 6 ++--- src/lib/sndfile_decoder.h | 6 ++--- src/lib/video_decoder.cc | 14 ++++++----- src/lib/video_decoder.h | 2 +- test/ffmpeg_pts_offset.cc | 15 ++++-------- test/seek_zero_test.cc | 9 ++++--- 15 files changed, 118 insertions(+), 97 deletions(-) (limited to 'src/lib/decoder.cc') 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 film, shared_ptr 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 data, ContentTime time) +AudioDecoder::audio (shared_ptr data) { if (_resampler) { data = _resampler->run (data); } - _pending.push_back (shared_ptr (new DecodedAudio (data, time))); - _last_audio = time + (data->frames() * TIME_HZ / _audio_content->output_audio_frame_rate()); + _pending.push_back (shared_ptr (new DecodedAudio (data, _audio_position))); + _audio_position += data->frames (); } void @@ -62,6 +68,17 @@ AudioDecoder::flush () shared_ptr b = _resampler->flush (); if (b) { - audio (b, _last_audio); + _pending.push_back (shared_ptr (new DecodedAudio (b, _audio_position))); + _audio_position += b->frames (); } } + +void +AudioDecoder::seek (ContentTime t, bool) +{ + shared_ptr 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, ContentTime); + virtual ContentTime first_audio () const = 0; + void audio (boost::shared_ptr); void flush (); boost::shared_ptr _audio_content; boost::shared_ptr _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 im, Eyes e, bool s, ContentTime ct) - : Decoded (ct) - , image (im) + DecodedVideo (boost::shared_ptr 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 image; Eyes eyes; bool same; + VideoFrame frame; }; class DecodedAudio : public Decoded { public: - DecodedAudio (boost::shared_ptr d, ContentTime ct) - : Decoded (ct) - , data (d) + DecodedAudio (boost::shared_ptr 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 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 im, dcpomatic::Rect 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; dcpomatic::Rect 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 (); } 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 f, shared_ptr f, shared_ptrfirst_video(); @@ -93,16 +90,16 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr f, shared_ptrfirst_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 f, shared_ptrvideo_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, 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, 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 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 _image_content; boost::shared_ptr _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 f, shared_ptrset_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 _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 f, shared_ptr image, bool same, ContentTime time) +VideoDecoder::video (shared_ptr image, bool same, VideoFrame frame) { switch (_video_content->video_frame_type ()) { case VIDEO_FRAME_TYPE_2D: - _pending.push_back (shared_ptr (new DecodedVideo (image, EYES_BOTH, same, time))); + _pending.push_back (shared_ptr (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 (new DecodedVideo (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, same, time))); - _pending.push_back (shared_ptr (new DecodedVideo (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, same, time))); + _pending.push_back (shared_ptr (new DecodedVideo (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, same, frame))); + _pending.push_back (shared_ptr (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 (new DecodedVideo (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, same, time))); - _pending.push_back (shared_ptr (new DecodedVideo (image->crop (Crop (0, 0, half, 0), true), EYES_RIGHT, same, time))); + _pending.push_back (shared_ptr (new DecodedVideo (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, same, frame))); + _pending.push_back (shared_ptr (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, bool, ContentTime); + void video (boost::shared_ptr, bool, VideoFrame); boost::shared_ptr _video_content; }; diff --git a/test/ffmpeg_pts_offset.cc b/test/ffmpeg_pts_offset.cc index 2090e517b..53303f154 100644 --- a/test/ffmpeg_pts_offset.cc +++ b/test/ffmpeg_pts_offset.cc @@ -37,8 +37,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_pts_offset_test) content->_first_video = 0; content->_audio_stream->first_audio = 0; FFmpegDecoder decoder (film, content, true, true); - BOOST_CHECK_EQUAL (decoder._video_pts_offset, 0); - BOOST_CHECK_EQUAL (decoder._audio_pts_offset, 0); + BOOST_CHECK_EQUAL (decoder._pts_offset, 0); } { @@ -46,8 +45,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_pts_offset_test) content->_first_video = 600; content->_audio_stream->first_audio = 600; FFmpegDecoder decoder (film, content, true, true); - BOOST_CHECK_EQUAL (decoder._video_pts_offset, -600); - BOOST_CHECK_EQUAL (decoder._audio_pts_offset, -600); + BOOST_CHECK_EQUAL (decoder._pts_offset, -600); } { @@ -55,8 +53,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_pts_offset_test) content->_first_video = 1.0 / 24.0; content->_audio_stream->first_audio = 0; FFmpegDecoder decoder (film, content, true, true); - BOOST_CHECK_EQUAL (decoder._video_pts_offset, 0); - BOOST_CHECK_EQUAL (decoder._audio_pts_offset, 0); + BOOST_CHECK_EQUAL (decoder._pts_offset, 0); } { @@ -65,8 +62,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_pts_offset_test) content->_first_video = frame + 0.0215; content->_audio_stream->first_audio = 0; FFmpegDecoder decoder (film, content, true, true); - BOOST_CHECK_CLOSE (decoder._video_pts_offset, (frame - 0.0215), 0.00001); - BOOST_CHECK_CLOSE (decoder._audio_pts_offset, (frame - 0.0215), 0.00001); + BOOST_CHECK_CLOSE (decoder._pts_offset, (frame - 0.0215), 0.00001); } { @@ -75,7 +71,6 @@ BOOST_AUTO_TEST_CASE (ffmpeg_pts_offset_test) content->_first_video = frame + 0.0215 + 4.1; content->_audio_stream->first_audio = 4.1; FFmpegDecoder decoder (film, content, true, true); - BOOST_CHECK_EQUAL (decoder._video_pts_offset, (frame - 0.0215) - 4.1); - BOOST_CHECK_EQUAL (decoder._audio_pts_offset, (frame - 0.0215) - 4.1); + BOOST_CHECK_EQUAL (decoder._pts_offset, (frame - 0.0215) - 4.1); } } diff --git a/test/seek_zero_test.cc b/test/seek_zero_test.cc index 0987f6b9b..c20c99ee7 100644 --- a/test/seek_zero_test.cc +++ b/test/seek_zero_test.cc @@ -32,6 +32,7 @@ using std::cout; using boost::shared_ptr; +using boost::dynamic_pointer_cast; BOOST_AUTO_TEST_CASE (seek_zero_test) { @@ -45,14 +46,12 @@ BOOST_AUTO_TEST_CASE (seek_zero_test) wait_for_jobs (); FFmpegDecoder decoder (film, content, true, false); - shared_ptr a = decoder.peek (); - cout << a->content_time << "\n"; + shared_ptr a = dynamic_pointer_cast (decoder.peek ()); decoder.seek (0, true); - shared_ptr b = decoder.peek (); - cout << b->content_time << "\n"; + shared_ptr b = dynamic_pointer_cast (decoder.peek ()); /* a will be after no seek, and b after a seek to zero, which should have the same effect. */ - BOOST_CHECK_EQUAL (a->content_time, b->content_time); + BOOST_CHECK_EQUAL (a->frame, b->frame); } -- cgit v1.2.3 From d7b23d44dec9d6357619e8e009e564e475215470 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 3 Jan 2014 23:18:47 +0000 Subject: Various attempted fixes to audio sync. --- src/lib/audio_decoder.cc | 29 +++++++++++++++++------------ src/lib/audio_decoder.h | 5 ++--- src/lib/decoder.cc | 9 +++++++++ src/lib/ffmpeg_decoder.cc | 20 ++++++++------------ src/lib/ffmpeg_decoder.h | 1 - src/lib/player.cc | 13 ++++--------- src/lib/sndfile_decoder.cc | 5 ++++- src/lib/sndfile_decoder.h | 5 +---- 8 files changed, 45 insertions(+), 42 deletions(-) (limited to 'src/lib/decoder.cc') diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc index adabd5d99..8d3b0e128 100644 --- a/src/lib/audio_decoder.cc +++ b/src/lib/audio_decoder.cc @@ -37,7 +37,6 @@ using boost::shared_ptr; AudioDecoder::AudioDecoder (shared_ptr film, shared_ptr content) : Decoder (film) , _audio_content (content) - , _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 ())); @@ -47,16 +46,26 @@ AudioDecoder::AudioDecoder (shared_ptr film, shared_ptr data) +AudioDecoder::audio (shared_ptr data, ContentTime time) { if (_resampler) { data = _resampler->run (data); } - _pending.push_back (shared_ptr (new DecodedAudio (data, _audio_position))); - _audio_position += data->frames (); + if (!_audio_position) { + shared_ptr film = _film.lock (); + assert (film); + FrameRateChange frc = film->active_frame_rate_change (_audio_content->position ()); + _audio_position = (double (time) / frc.speed_up) * film->audio_frame_rate() / TIME_HZ; + } + + _pending.push_back (shared_ptr (new DecodedAudio (data, _audio_position.get ()))); + _audio_position = _audio_position.get() + data->frames (); } void @@ -68,17 +77,13 @@ AudioDecoder::flush () shared_ptr b = _resampler->flush (); if (b) { - _pending.push_back (shared_ptr (new DecodedAudio (b, _audio_position))); - _audio_position += b->frames (); + _pending.push_back (shared_ptr (new DecodedAudio (b, _audio_position.get ()))); + _audio_position = _audio_position.get() + b->frames (); } } void -AudioDecoder::seek (ContentTime t, bool) +AudioDecoder::seek (ContentTime, bool) { - shared_ptr film = _film.lock (); - assert (film); - - FrameRateChange frc = film->active_frame_rate_change (_audio_content->position ()); - _audio_position = ((t + first_audio()) / frc.speed_up) * film->audio_frame_rate() / TIME_HZ; + _audio_position.reset (); } diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h index 12f8505f6..bb3aafccd 100644 --- a/src/lib/audio_decoder.h +++ b/src/lib/audio_decoder.h @@ -48,13 +48,12 @@ public: protected: - virtual ContentTime first_audio () const = 0; - void audio (boost::shared_ptr); + void audio (boost::shared_ptr, ContentTime); void flush (); boost::shared_ptr _audio_content; boost::shared_ptr _resampler; - AudioFrame _audio_position; + boost::optional _audio_position; }; #endif diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 30244b40b..53a0c31e1 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -40,6 +40,14 @@ Decoder::Decoder (shared_ptr f) } +struct DecodedSorter +{ + bool operator() (shared_ptr a, shared_ptr b) + { + return a->dcp_time < b->dcp_time; + } +}; + shared_ptr Decoder::peek () { @@ -51,6 +59,7 @@ Decoder::peek () return shared_ptr (); } + _pending.sort (DecodedSorter ()); return _pending.front (); } diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 25fe655be..dae0ddbe8 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -445,11 +445,17 @@ FFmpegDecoder::decode_audio_packet () } if (frame_finished) { + ContentTime const ct = ( + av_frame_get_best_effort_timestamp (_frame) * + av_q2d (_ffmpeg_content->audio_stream()->stream (_format_context)->time_base) + + _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)); + + audio (deinterleave_audio (_frame->data, data_size), ct); } copy_packet.data += decode_result; @@ -603,13 +609,3 @@ 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 680e39c39..ee725b20c 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -58,7 +58,6 @@ private: bool pass (); void flush (); - ContentTime first_audio () const; void setup_subtitle (); diff --git a/src/lib/player.cc b/src/lib/player.cc index eb7b177ec..3db2fe6c9 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -172,11 +172,6 @@ Player::pass () /* Will be set to false if we shouldn't consume the peeked DecodedThing */ bool consume = true; - /* This is the margin either side of _{video,audio}_position that we will accept - as a starting point for a frame consecutive to the previous. - */ - DCPTime const margin = TIME_HZ / (2 * _film->video_frame_rate ()); - if (dv && _video) { if (_just_did_inaccurate_seek) { @@ -185,7 +180,7 @@ Player::pass () emit_video (earliest_piece, dv); step_video_position (dv); - } else if (dv->dcp_time - _video_position > margin) { + } else if (dv->dcp_time > _video_position) { /* Too far ahead */ @@ -208,7 +203,7 @@ Player::pass () consume = false; - } else if (abs (dv->dcp_time - _video_position) < margin) { + } else if (dv->dcp_time == _video_position) { /* We're ok */ emit_video (earliest_piece, dv); step_video_position (dv); @@ -222,12 +217,12 @@ Player::pass () } else if (da && _audio) { - if (da->dcp_time - _audio_position > margin) { + if (da->dcp_time > _audio_position) { /* Too far ahead */ emit_silence (da->dcp_time - _audio_position); consume = false; _statistics.audio.silence += (da->dcp_time - _audio_position); - } else if (abs (da->dcp_time - _audio_position) < margin) { + } else if (da->dcp_time == _audio_position) { /* We're ok */ emit_audio (earliest_piece, da); _statistics.audio.good += da->data->frames(); diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc index 1c651e614..d6537843e 100644 --- a/src/lib/sndfile_decoder.cc +++ b/src/lib/sndfile_decoder.cc @@ -45,6 +45,7 @@ SndfileDecoder::SndfileDecoder (shared_ptr f, shared_ptrset_frames (this_time); - audio (data); + audio (data, _done * TIME_HZ / audio_frame_rate ()); + _done += this_time; _remaining -= this_time; return _remaining == 0; @@ -123,5 +125,6 @@ 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 4ecea0846..46d9c5e5c 100644 --- a/src/lib/sndfile_decoder.h +++ b/src/lib/sndfile_decoder.h @@ -36,15 +36,12 @@ public: int audio_frame_rate () const; private: - ContentTime first_audio () const { - return 0; - } - bool pass (); boost::shared_ptr _sndfile_content; SNDFILE* _sndfile; SF_INFO _info; + AudioFrame _done; AudioFrame _remaining; float* _deinterleave_buffer; }; -- cgit v1.2.3