From 89aa9d4ba69e471949f791cdafe4ae20cea554d2 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 21 Feb 2017 21:42:44 +0000 Subject: [PATCH] Various fixes to push audio vaguely in the right direction. --- run/tests | 2 +- src/lib/audio_decoder.cc | 65 ++++------- src/lib/audio_decoder.h | 14 +-- src/lib/audio_decoder_stream.cc | 178 ------------------------------- src/lib/audio_decoder_stream.h | 66 ------------ src/lib/audio_merger.cc | 1 + src/lib/audio_merger.h | 3 + src/lib/content_audio.h | 4 +- src/lib/dcp_decoder.cc | 4 +- src/lib/dcp_subtitle_decoder.cc | 4 +- src/lib/decoder.cc | 29 +++-- src/lib/decoder.h | 6 +- src/lib/decoder_part.h | 2 +- src/lib/ffmpeg_decoder.cc | 3 +- src/lib/image_decoder.cc | 3 +- src/lib/piece.h | 2 + src/lib/player.cc | 102 ++++++++++++++---- src/lib/player.h | 4 + src/lib/resampler.cc | 21 +++- src/lib/resampler.h | 4 +- src/lib/subtitle_decoder.h | 4 +- src/lib/text_subtitle_decoder.cc | 4 +- src/lib/video_decoder.h | 4 +- src/lib/video_mxf_decoder.cc | 3 +- src/lib/wscript | 1 - test/resampler_test.cc | 2 +- 26 files changed, 184 insertions(+), 351 deletions(-) delete mode 100644 src/lib/audio_decoder_stream.cc delete mode 100644 src/lib/audio_decoder_stream.h diff --git a/run/tests b/run/tests index 77db5d52b..d9815a449 100755 --- a/run/tests +++ b/run/tests @@ -2,7 +2,7 @@ # # e.g. --run_tests=foo -export LD_LIBRARY_PATH=build/src/lib:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=build/src/lib:/home/c.hetherington/lib:$LD_LIBRARY_PATH export DCPOMATIC_LINUX_SHARE_PREFIX=`pwd` if [ "$1" == "--debug" ]; then shift; diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc index 90f731108..5a33716f4 100644 --- a/src/lib/audio_decoder.cc +++ b/src/lib/audio_decoder.cc @@ -20,7 +20,6 @@ #include "audio_decoder.h" #include "audio_buffers.h" -#include "audio_decoder_stream.h" #include "audio_content.h" #include "log.h" #include "compose.hpp" @@ -34,11 +33,11 @@ using std::map; using boost::shared_ptr; using boost::optional; -AudioDecoder::AudioDecoder (Decoder* parent, shared_ptr content, shared_ptr log) +AudioDecoder::AudioDecoder (Decoder* parent, shared_ptr content, shared_ptr log) : DecoderPart (parent, log) { BOOST_FOREACH (AudioStreamPtr i, content->streams ()) { - _streams[i] = shared_ptr (new AudioDecoderStream (content, i, parent, this, log)); + _positions[i] = 0; } } @@ -49,58 +48,32 @@ AudioDecoder::emit (AudioStreamPtr stream, shared_ptr data, return; } - if (_streams.find (stream) == _streams.end ()) { - - /* This method can be called with an unknown stream during the following sequence: - - Add KDM to some DCP content. - - Content gets re-examined. - - SingleStreamAudioContent::take_from_audio_examiner creates a new stream. - - Some content property change signal is delivered so Player::Changed is emitted. - - Film viewer to re-gets the frame. - - Player calls DCPDecoder pass which calls this method on the new stream. - - At this point the AudioDecoder does not know about the new stream. - - Then - - Some other property change signal is delivered which marks the player's pieces invalid. - - Film viewer re-gets again. - - Everything is OK. - - In this situation it is fine for us to silently drop the audio. - */ - - return; + if (_positions[stream] == 0) { + _positions[stream] = time.frames_round (stream->frame_rate ()); } - _streams[stream]->audio (data, time); - _positions[stream] = time; + Data (stream, ContentAudio (data, _positions[stream])); + _positions[stream] += data->frames(); } -void -AudioDecoder::flush () +ContentTime +AudioDecoder::position () const { - for (StreamMap::const_iterator i = _streams.begin(); i != _streams.end(); ++i) { - i->second->flush (); + optional p; + for (map::const_iterator i = _positions.begin(); i != _positions.end(); ++i) { + ContentTime const ct = ContentTime::from_frames (i->second, i->first->frame_rate ()); + if (!p || ct < *p) { + p = ct; + } } -} -void -AudioDecoder::set_fast () -{ - for (StreamMap::const_iterator i = _streams.begin(); i != _streams.end(); ++i) { - i->second->set_fast (); - } + return p.get_value_or(ContentTime()); } -optional -AudioDecoder::position () const +void +AudioDecoder::seek () { - optional p; - for (map::const_iterator i = _positions.begin(); i != _positions.end(); ++i) { - if (!p || i->second < *p) { - p = i->second; - } + for (map::iterator i = _positions.begin(); i != _positions.end(); ++i) { + i->second = 0; } - - return p; } diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h index 10d88d1ec..a4b74e2ea 100644 --- a/src/lib/audio_decoder.h +++ b/src/lib/audio_decoder.h @@ -43,22 +43,16 @@ class Log; class AudioDecoder : public boost::enable_shared_from_this, public DecoderPart { public: - AudioDecoder (Decoder* parent, boost::shared_ptr, boost::shared_ptr log); - - boost::optional position () const; - - void set_fast (); - void flush (); + AudioDecoder (Decoder* parent, boost::shared_ptr content, boost::shared_ptr log); + ContentTime position () const; void emit (AudioStreamPtr stream, boost::shared_ptr, ContentTime); + void seek (); boost::signals2::signal Data; private: - /** An AudioDecoderStream object to manage each stream in _audio_content */ - typedef std::map > StreamMap; - StreamMap _streams; - std::map _positions; + std::map _positions; }; #endif diff --git a/src/lib/audio_decoder_stream.cc b/src/lib/audio_decoder_stream.cc deleted file mode 100644 index 8f0905e0d..000000000 --- a/src/lib/audio_decoder_stream.cc +++ /dev/null @@ -1,178 +0,0 @@ -/* - Copyright (C) 2012-2016 Carl Hetherington - - This file is part of DCP-o-matic. - - DCP-o-matic is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - DCP-o-matic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with DCP-o-matic. If not, see . - -*/ - -#include "audio_decoder_stream.h" -#include "audio_buffers.h" -#include "audio_processor.h" -#include "audio_decoder.h" -#include "resampler.h" -#include "util.h" -#include "film.h" -#include "log.h" -#include "audio_content.h" -#include "compose.hpp" -#include - -#include "i18n.h" - -using std::list; -using std::pair; -using std::cout; -using std::min; -using std::max; -using boost::optional; -using boost::shared_ptr; - -AudioDecoderStream::AudioDecoderStream ( - shared_ptr content, AudioStreamPtr stream, Decoder* decoder, AudioDecoder* audio_decoder, shared_ptr log - ) - : _content (content) - , _stream (stream) - , _decoder (decoder) - , _audio_decoder (audio_decoder) - , _log (log) - /* We effectively start having done a seek to zero; this allows silence-padding of the first - data that comes out of our decoder. - */ - , _seek_reference (ContentTime ()) -{ - if (content->resampled_frame_rate() != _stream->frame_rate() && _stream->channels() > 0) { - _resampler.reset (new Resampler (_stream->frame_rate(), content->resampled_frame_rate(), _stream->channels ())); - } - - reset_decoded (); -} - -void -AudioDecoderStream::reset_decoded () -{ - _decoded = ContentAudio (shared_ptr (new AudioBuffers (_stream->channels(), 0)), 0); -} - -/** Audio timestamping is made hard by many factors, but perhaps the most entertaining 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. - * - * The time is passed in here so that after a seek we can set up our _position. The - * time is ignored once this has been done. - */ -void -AudioDecoderStream::audio (shared_ptr data, ContentTime time) -{ - _log->log (String::compose ("ADS receives %1 %2", to_string(time), data->frames ()), LogEntry::TYPE_DEBUG_DECODE); - - if (_resampler) { - data = _resampler->run (data); - } - - Frame const frame_rate = _content->resampled_frame_rate (); - - if (_seek_reference) { - /* We've had an accurate seek and now we're seeing some data */ - ContentTime const delta = time - _seek_reference.get (); - Frame const delta_frames = delta.frames_round (frame_rate); - if (delta_frames > 0) { - /* This data comes after the seek time. Pad the data with some silence. */ - shared_ptr padded (new AudioBuffers (data->channels(), data->frames() + delta_frames)); - padded->make_silent (); - padded->copy_from (data.get(), data->frames(), 0, delta_frames); - data = padded; - time -= delta; - } - _seek_reference = optional (); - } - - if (!_position) { - _position = time.frames_round (frame_rate); - } - - DCPOMATIC_ASSERT (_position.get() >= (_decoded.frame + _decoded.audio->frames())); - - add (data); -} - -void -AudioDecoderStream::add (shared_ptr data) -{ - if (!_position) { - /* This should only happen when there is a seek followed by a flush, but - we need to cope with it. - */ - return; - } - - /* Resize _decoded to fit the new data */ - int new_size = 0; - if (_decoded.audio->frames() == 0) { - /* There's nothing in there, so just store the new data */ - new_size = data->frames (); - _decoded.frame = _position.get (); - } else { - /* Otherwise we need to extend _decoded to include the new stuff */ - new_size = _position.get() + data->frames() - _decoded.frame; - } - - _decoded.audio->ensure_size (new_size); - _decoded.audio->set_frames (new_size); - - /* Copy new data in */ - _decoded.audio->copy_from (data.get(), data->frames(), 0, _position.get() - _decoded.frame); - _position = _position.get() + data->frames (); - - /* Limit the amount of data we keep in case nobody is asking for it */ - int const max_frames = _content->resampled_frame_rate () * 10; - if (_decoded.audio->frames() > max_frames) { - int const to_remove = _decoded.audio->frames() - max_frames; - _decoded.frame += to_remove; - _decoded.audio->move (to_remove, 0, max_frames); - _decoded.audio->set_frames (max_frames); - } -} - -void -AudioDecoderStream::flush () -{ - if (!_resampler) { - return; - } - - shared_ptr b = _resampler->flush (); - if (b) { - add (b); - } -} - -void -AudioDecoderStream::set_fast () -{ - if (_resampler) { - _resampler->set_fast (); - } -} - -optional -AudioDecoderStream::position () const -{ - if (!_position) { - return optional (); - } - - return ContentTime::from_frames (_position.get(), _content->resampled_frame_rate()); -} diff --git a/src/lib/audio_decoder_stream.h b/src/lib/audio_decoder_stream.h deleted file mode 100644 index b2ab65ac0..000000000 --- a/src/lib/audio_decoder_stream.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright (C) 2012-2015 Carl Hetherington - - This file is part of DCP-o-matic. - - DCP-o-matic is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - DCP-o-matic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with DCP-o-matic. If not, see . - -*/ - -#ifndef DCPOMATIC_AUDIO_DECODER_STREAM_H -#define DCPOMATIC_AUDIO_DECODER_STREAM_H - -#include "audio_stream.h" -#include "content_audio.h" -#include "dcpomatic_time.h" -#include - -class AudioContent; -class AudioDecoder; -class Resampler; -class Log; -class Decoder; - -class AudioDecoderStream -{ -public: - AudioDecoderStream (boost::shared_ptr, AudioStreamPtr, Decoder* decoder, AudioDecoder* audio_decoder, boost::shared_ptr log); - - void audio (boost::shared_ptr, ContentTime); - void flush (); - void set_fast (); - - boost::optional position () const; - -private: - - void reset_decoded (); - void add (boost::shared_ptr); - - boost::shared_ptr _content; - AudioStreamPtr _stream; - Decoder* _decoder; - AudioDecoder* _audio_decoder; - boost::shared_ptr _log; - boost::shared_ptr _resampler; - boost::optional _position; - /** Currently-available decoded audio data */ - ContentAudio _decoded; - /** The time of an accurate seek after which we have not yet received any actual - data at the seek time. - */ - boost::optional _seek_reference; -}; - -#endif diff --git a/src/lib/audio_merger.cc b/src/lib/audio_merger.cc index a0d300b7f..49cdea6a3 100644 --- a/src/lib/audio_merger.cc +++ b/src/lib/audio_merger.cc @@ -19,6 +19,7 @@ #include "audio_merger.h" #include "dcpomatic_time.h" +#include using std::pair; using std::min; diff --git a/src/lib/audio_merger.h b/src/lib/audio_merger.h index c9026b93e..6db28b6c3 100644 --- a/src/lib/audio_merger.h +++ b/src/lib/audio_merger.h @@ -31,6 +31,9 @@ public: */ std::pair, DCPTime> pull (DCPTime time); void push (boost::shared_ptr audio, DCPTime time); + DCPTime last_pull () const { + return _last_pull; + } private: boost::shared_ptr _buffers; diff --git a/src/lib/content_audio.h b/src/lib/content_audio.h index 4bfbf0cc7..509e7e4a6 100644 --- a/src/lib/content_audio.h +++ b/src/lib/content_audio.h @@ -39,12 +39,12 @@ public: , frame (0) {} - ContentAudio (boost::shared_ptr a, Frame f) + ContentAudio (boost::shared_ptr a, Frame f) : audio (a) , frame (f) {} - boost::shared_ptr audio; + boost::shared_ptr audio; Frame frame; }; diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc index e7f04a061..0b99e8dd0 100644 --- a/src/lib/dcp_decoder.cc +++ b/src/lib/dcp_decoder.cc @@ -210,8 +210,10 @@ DCPDecoder::get_readers () } void -DCPDecoder::seek (ContentTime t, bool) +DCPDecoder::seek (ContentTime t, bool accurate) { + Decoder::seek (t, accurate); + _reel = _reels.begin (); _offset = 0; get_readers (); diff --git a/src/lib/dcp_subtitle_decoder.cc b/src/lib/dcp_subtitle_decoder.cc index 120836643..04b264192 100644 --- a/src/lib/dcp_subtitle_decoder.cc +++ b/src/lib/dcp_subtitle_decoder.cc @@ -38,8 +38,10 @@ DCPSubtitleDecoder::DCPSubtitleDecoder (shared_ptr con } void -DCPSubtitleDecoder::seek (ContentTime time, bool) +DCPSubtitleDecoder::seek (ContentTime time, bool accurate) { + Decoder::seek (time, accurate); + _next = _subtitles.begin (); list::const_iterator i = _subtitles.begin (); while (i != _subtitles.end() && ContentTime::from_seconds (_next->in().as_seconds()) < time) { diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 785fb96f0..ee03a1579 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -22,23 +22,36 @@ #include "video_decoder.h" #include "audio_decoder.h" #include "subtitle_decoder.h" +#include +#include + +using std::cout; +using boost::optional; ContentTime Decoder::position () const { - ContentTime pos; + optional pos; - if (video && video->position()) { - pos = min (pos, video->position().get()); + if (video && (!pos || video->position() < *pos)) { + pos = video->position(); } - if (audio && audio->position()) { - pos = min (pos, audio->position().get()); + if (audio && (!pos || audio->position() < *pos)) { + pos = audio->position(); } - if (subtitle && subtitle->position()) { - pos = min (pos, subtitle->position().get()); + if (subtitle && (!pos || subtitle->position() < *pos)) { + pos = subtitle->position(); } - return pos; + return pos.get_value_or(ContentTime()); +} + +void +Decoder::seek (ContentTime time, bool accurate) +{ + if (audio) { + audio->seek (); + } } diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 26035d221..d87ff610a 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -47,9 +47,11 @@ public: boost::shared_ptr audio; boost::shared_ptr subtitle; - /** @return true if there is no more data to come from this decoder */ + /** Do some decoding and perhaps emit video, audio or subtitle data. + * @return true if this decoder will emit no more data unless a seek() happens. + */ virtual bool pass () = 0; - virtual void seek (ContentTime time, bool accurate) = 0; + virtual void seek (ContentTime time, bool accurate); ContentTime position () const; }; diff --git a/src/lib/decoder_part.h b/src/lib/decoder_part.h index 39f77e6e6..a9568be8a 100644 --- a/src/lib/decoder_part.h +++ b/src/lib/decoder_part.h @@ -33,7 +33,7 @@ public: DecoderPart (Decoder* parent, boost::shared_ptr log); virtual ~DecoderPart () {} - virtual boost::optional position () const = 0; + virtual ContentTime position () const = 0; void set_ignore () { _ignore = true; diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index b7b70e061..1bae99d63 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -112,7 +112,6 @@ FFmpegDecoder::flush () if (audio) { decode_audio_packet (); - audio->flush (); } } @@ -299,6 +298,8 @@ FFmpegDecoder::bytes_per_audio_sample (shared_ptr stream) con void FFmpegDecoder::seek (ContentTime time, bool accurate) { + Decoder::seek (time, accurate); + /* If we are doing an `accurate' seek, we need to use pre-roll, as we don't really know what the seek will give us. */ diff --git a/src/lib/image_decoder.cc b/src/lib/image_decoder.cc index 5a637f4df..fd51c1ba3 100644 --- a/src/lib/image_decoder.cc +++ b/src/lib/image_decoder.cc @@ -78,7 +78,8 @@ ImageDecoder::pass () } void -ImageDecoder::seek (ContentTime time, bool) +ImageDecoder::seek (ContentTime time, bool accurate) { + Decoder::seek (time, accurate); _frame_video_position = time.frames_round (_image_content->active_video_frame_rate ()); } diff --git a/src/lib/piece.h b/src/lib/piece.h index b5ab2d233..711e292ee 100644 --- a/src/lib/piece.h +++ b/src/lib/piece.h @@ -33,11 +33,13 @@ public: : content (c) , decoder (d) , frc (f) + , done (false) {} boost::shared_ptr content; boost::shared_ptr decoder; FrameRateChange frc; + bool done; }; #endif diff --git a/src/lib/player.cc b/src/lib/player.cc index 611e9c900..7c2048489 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -47,6 +47,8 @@ #include "content_subtitle.h" #include "dcp_decoder.h" #include "image_decoder.h" +#include "resampler.h" +#include "compose.hpp" #include #include #include @@ -122,10 +124,6 @@ Player::setup_pieces () decoder->audio->set_ignore (); } - if (decoder->audio && _fast) { - decoder->audio->set_fast (); - } - shared_ptr dcp = dynamic_pointer_cast (decoder); if (dcp && _play_referenced) { dcp->set_decode_referenced (); @@ -508,12 +506,15 @@ Player::pass () } shared_ptr earliest; - DCPTime earliest_position; + DCPTime earliest_content; + BOOST_FOREACH (shared_ptr i, _pieces) { - DCPTime const t = i->content->position() + DCPTime (i->decoder->position(), i->frc); - if (!earliest || t < earliest_position) { - earliest_position = t; - earliest = i; + if (!i->done) { + DCPTime const t = i->content->position() + DCPTime (i->decoder->position(), i->frc); + if (!earliest || t < earliest_content) { + earliest_content = t; + earliest = i; + } } } @@ -521,11 +522,21 @@ Player::pass () return true; } - earliest->decoder->pass (); + earliest->done = earliest->decoder->pass (); /* Emit any audio that is ready */ - pair, DCPTime> audio = _audio_merger.pull (earliest_position); + optional earliest_audio; + BOOST_FOREACH (shared_ptr i, _pieces) { + if (i->decoder->audio) { + DCPTime const t = i->content->position() + DCPTime (i->decoder->audio->position(), i->frc); + if (!earliest_audio || t < *earliest_audio) { + earliest_audio = t; + } + } + } + + pair, DCPTime> audio = _audio_merger.pull (earliest_audio.get_value_or(DCPTime())); if (audio.first->frames() > 0) { DCPOMATIC_ASSERT (audio.second >= _last_audio_time); DCPTime t = _last_audio_time; @@ -557,6 +568,11 @@ Player::video (weak_ptr wp, ContentVideo video) DCPTime const time = content_video_to_dcp (piece, video.frame.index()); DCPTimePeriod const period (time, time + DCPTime::from_frames (1, _film->video_frame_rate())); + /* Discard if it's outside the content's period */ + if (time < piece->content->position() || time >= piece->content->end()) { + return; + } + /* Get any subtitles */ optional subtitles; @@ -646,22 +662,28 @@ Player::audio (weak_ptr wp, AudioStreamPtr stream, ContentAudio content_a shared_ptr content = piece->content->audio; DCPOMATIC_ASSERT (content); - shared_ptr audio = content_audio.audio; - /* Gain */ if (content->gain() != 0) { - shared_ptr gain (new AudioBuffers (audio)); + shared_ptr gain (new AudioBuffers (content_audio.audio)); gain->apply_gain (content->gain ()); - audio = gain; + content_audio.audio = gain; + } + + /* Resample */ + if (stream->frame_rate() != content->resampled_frame_rate()) { + shared_ptr r = resampler (content, stream, true); + pair, Frame> ro = r->run (content_audio.audio, content_audio.frame); + content_audio.audio = ro.first; + content_audio.frame = ro.second; } /* XXX: end-trimming used to be checked here */ /* Compute time in the DCP */ - DCPTime const time = resampled_audio_to_dcp (piece, content_audio.frame) + DCPTime::from_seconds (content->delay() / 1000); + DCPTime time = resampled_audio_to_dcp (piece, content_audio.frame) + DCPTime::from_seconds (content->delay() / 1000); /* Remap channels */ - shared_ptr dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames())); + shared_ptr dcp_mapped (new AudioBuffers (_film->audio_channels(), content_audio.audio->frames())); dcp_mapped->make_silent (); AudioMapping map = stream->mapping (); @@ -669,7 +691,7 @@ Player::audio (weak_ptr wp, AudioStreamPtr stream, ContentAudio content_a for (int j = 0; j < dcp_mapped->channels(); ++j) { if (map.get (i, static_cast (j)) > 0) { dcp_mapped->accumulate_channel ( - audio.get(), + content_audio.audio.get(), i, static_cast (j), map.get (i, static_cast (j)) @@ -678,13 +700,23 @@ Player::audio (weak_ptr wp, AudioStreamPtr stream, ContentAudio content_a } } - audio = dcp_mapped; + content_audio.audio = dcp_mapped; if (_audio_processor) { - audio = _audio_processor->run (audio, _film->audio_channels ()); + content_audio.audio = _audio_processor->run (content_audio.audio, _film->audio_channels ()); } - _audio_merger.push (audio, time); + /* XXX: this may be nonsense */ + if (time < _audio_merger.last_pull()) { + DCPTime const discard_time = _audio_merger.last_pull() - time; + Frame discard_frames = discard_time.frames_round(_film->audio_frame_rate()); + content_audio.audio.reset (new AudioBuffers (_film->audio_channels(), content_audio.audio->frames() - discard_frames)); + time += discard_time; + } + + if (content_audio.audio->frames() > 0) { + _audio_merger.push (content_audio.audio, time); + } } void @@ -768,6 +800,7 @@ Player::seek (DCPTime time, bool accurate) BOOST_FOREACH (shared_ptr i, _pieces) { if (i->content->position() <= time && time < i->content->end()) { i->decoder->seek (dcp_to_content_time (i, time), accurate); + i->done = false; } } @@ -777,3 +810,30 @@ Player::seek (DCPTime time, bool accurate) _last_time = optional (); } } + +shared_ptr +Player::resampler (shared_ptr content, AudioStreamPtr stream, bool create) +{ + ResamplerMap::const_iterator i = _resamplers.find (make_pair (content, stream)); + if (i != _resamplers.end ()) { + return i->second; + } + + if (!create) { + return shared_ptr (); + } + + LOG_GENERAL ( + "Creating new resampler from %1 to %2 with %3 channels", + stream->frame_rate(), + content->resampled_frame_rate(), + stream->channels() + ); + + shared_ptr r ( + new Resampler (stream->frame_rate(), content->resampled_frame_rate(), stream->channels()) + ); + + _resamplers[make_pair(content, stream)] = r; + return r; +} diff --git a/src/lib/player.h b/src/lib/player.h index ba7845b54..c891ee85c 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -44,6 +44,7 @@ class Playlist; class Font; class AudioBuffers; class ReferencedReelAsset; +class Resampler; /** @class Player * @brief A class which can `play' a Playlist. @@ -105,6 +106,7 @@ private: void audio (boost::weak_ptr, AudioStreamPtr, ContentAudio); void image_subtitle (boost::weak_ptr, ContentImageSubtitle); void text_subtitle (boost::weak_ptr, ContentTextSubtitle); + boost::shared_ptr resampler (boost::shared_ptr content, AudioStreamPtr stream, bool create); boost::shared_ptr _film; boost::shared_ptr _playlist; @@ -141,6 +143,8 @@ private: std::list > _subtitles; boost::shared_ptr _audio_processor; + typedef std::map, AudioStreamPtr>, boost::shared_ptr > ResamplerMap; + ResamplerMap _resamplers; boost::signals2::scoped_connection _film_changed_connection; boost::signals2::scoped_connection _playlist_changed_connection; diff --git a/src/lib/resampler.cc b/src/lib/resampler.cc index 01d71c79b..d08e7bc38 100644 --- a/src/lib/resampler.cc +++ b/src/lib/resampler.cc @@ -67,9 +67,19 @@ Resampler::set_fast () } } -shared_ptr -Resampler::run (shared_ptr in) +pair, Frame> +Resampler::run (shared_ptr in, Frame frame) { + if (!_next_in || !_next_out || _next_in.get() != frame) { + /* Either there was a discontinuity in the input or this is the first input; + reset _next_out. + */ + _next_out = lrintf (frame * _out_rate / _in_rate); + } + + /* Expected next input frame */ + _next_in = frame + in->frames (); + int in_frames = in->frames (); int in_offset = 0; int out_offset = 0; @@ -142,7 +152,12 @@ Resampler::run (shared_ptr in) delete[] data.data_out; } - return resampled; + Frame out_frame = _next_out.get (); + + /* Expected next output frame */ + _next_out = _next_out.get() + resampled->frames(); + + return make_pair (resampled, out_frame); } shared_ptr diff --git a/src/lib/resampler.h b/src/lib/resampler.h index afc28aefd..359598b12 100644 --- a/src/lib/resampler.h +++ b/src/lib/resampler.h @@ -31,7 +31,7 @@ public: Resampler (int, int, int); ~Resampler (); - boost::shared_ptr run (boost::shared_ptr); + std::pair, Frame> run (boost::shared_ptr, Frame); boost::shared_ptr flush (); void set_fast (); @@ -40,4 +40,6 @@ private: int _in_rate; int _out_rate; int _channels; + boost::optional _next_in; + boost::optional _next_out; }; diff --git a/src/lib/subtitle_decoder.h b/src/lib/subtitle_decoder.h index f44766393..2b38153bf 100644 --- a/src/lib/subtitle_decoder.h +++ b/src/lib/subtitle_decoder.h @@ -48,7 +48,7 @@ public: boost::shared_ptr log ); - boost::optional position () const { + ContentTime position () const { return _position; } @@ -65,7 +65,7 @@ public: private: boost::shared_ptr _content; - boost::optional _position; + ContentTime _position; }; #endif diff --git a/src/lib/text_subtitle_decoder.cc b/src/lib/text_subtitle_decoder.cc index 1b8ee1310..bdec17a8d 100644 --- a/src/lib/text_subtitle_decoder.cc +++ b/src/lib/text_subtitle_decoder.cc @@ -42,8 +42,10 @@ TextSubtitleDecoder::TextSubtitleDecoder (shared_ptr } void -TextSubtitleDecoder::seek (ContentTime time, bool) +TextSubtitleDecoder::seek (ContentTime time, bool accurate) { + Decoder::seek (time, accurate); + _next = 0; while (_next < _subtitles.size() && ContentTime::from_seconds (_subtitles[_next].from.all_as_seconds ()) < time) { ++_next; diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index 6609811e4..e16884568 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -51,7 +51,7 @@ public: friend struct ffmpeg_pts_offset_test; friend void ffmpeg_decoder_sequential_test_one (boost::filesystem::path file, float fps, int gaps, int video_length); - boost::optional position () const { + ContentTime position () const { return _position; } @@ -62,7 +62,7 @@ public: private: boost::shared_ptr _content; boost::optional _last_emitted; - boost::optional _position; + ContentTime _position; }; #endif diff --git a/src/lib/video_mxf_decoder.cc b/src/lib/video_mxf_decoder.cc index 7b4066f20..216721375 100644 --- a/src/lib/video_mxf_decoder.cc +++ b/src/lib/video_mxf_decoder.cc @@ -94,7 +94,8 @@ VideoMXFDecoder::pass () } void -VideoMXFDecoder::seek (ContentTime t, bool) +VideoMXFDecoder::seek (ContentTime t, bool accurate) { + Decoder::seek (t, accurate); _next = t; } diff --git a/src/lib/wscript b/src/lib/wscript index 0e4e5bcfc..efcb2bc1e 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -27,7 +27,6 @@ sources = """ audio_buffers.cc audio_content.cc audio_decoder.cc - audio_decoder_stream.cc audio_delay.cc audio_filter.cc audio_filter_graph.cc diff --git a/test/resampler_test.cc b/test/resampler_test.cc index 6f102dd4c..6f3e3fef1 100644 --- a/test/resampler_test.cc +++ b/test/resampler_test.cc @@ -44,7 +44,7 @@ resampler_test_one (int from, int to) for (int64_t i = 0; i < N; i += 1000) { shared_ptr a (new AudioBuffers (1, 1000)); a->make_silent (); - shared_ptr r = resamp.run (a); + pair, Frame> r = resamp.run (a, 0); } } -- 2.30.2