Various fixes to push audio vaguely in the right direction.
authorCarl Hetherington <cth@carlh.net>
Tue, 21 Feb 2017 21:42:44 +0000 (21:42 +0000)
committerCarl Hetherington <cth@carlh.net>
Wed, 19 Apr 2017 22:04:32 +0000 (23:04 +0100)
26 files changed:
run/tests
src/lib/audio_decoder.cc
src/lib/audio_decoder.h
src/lib/audio_decoder_stream.cc [deleted file]
src/lib/audio_decoder_stream.h [deleted file]
src/lib/audio_merger.cc
src/lib/audio_merger.h
src/lib/content_audio.h
src/lib/dcp_decoder.cc
src/lib/dcp_subtitle_decoder.cc
src/lib/decoder.cc
src/lib/decoder.h
src/lib/decoder_part.h
src/lib/ffmpeg_decoder.cc
src/lib/image_decoder.cc
src/lib/piece.h
src/lib/player.cc
src/lib/player.h
src/lib/resampler.cc
src/lib/resampler.h
src/lib/subtitle_decoder.h
src/lib/text_subtitle_decoder.cc
src/lib/video_decoder.h
src/lib/video_mxf_decoder.cc
src/lib/wscript
test/resampler_test.cc

index 77db5d52bbfd15662596a2364fdd00e18fe6c38b..d9815a449ae619ba6a55678584599908b2202ac3 100755 (executable)
--- 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;
index 90f731108cbd66a940b64efe57eaf4409ce9b924..5a33716f4fe775622e2864a7ce97192c281ed84a 100644 (file)
@@ -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<const AudioContent> content, shared_ptr<Log> log)
+AudioDecoder::AudioDecoder (Decoder* parent, shared_ptr<AudioContent> content, shared_ptr<Log> log)
        : DecoderPart (parent, log)
 {
        BOOST_FOREACH (AudioStreamPtr i, content->streams ()) {
-               _streams[i] = shared_ptr<AudioDecoderStream> (new AudioDecoderStream (content, i, parent, this, log));
+               _positions[i] = 0;
        }
 }
 
@@ -49,58 +48,32 @@ AudioDecoder::emit (AudioStreamPtr stream, shared_ptr<const AudioBuffers> 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<ContentTime> p;
+       for (map<AudioStreamPtr, Frame>::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<ContentTime>
-AudioDecoder::position () const
+void
+AudioDecoder::seek ()
 {
-       optional<ContentTime> p;
-       for (map<AudioStreamPtr, ContentTime>::const_iterator i = _positions.begin(); i != _positions.end(); ++i) {
-               if (!p || i->second < *p) {
-                       p = i->second;
-               }
+       for (map<AudioStreamPtr, Frame>::iterator i = _positions.begin(); i != _positions.end(); ++i) {
+               i->second = 0;
        }
-
-       return p;
 }
index 10d88d1ec5f7f76c516881d93394b6659a9666c0..a4b74e2eae011cec3379e4520137504f381fdb6d 100644 (file)
@@ -43,22 +43,16 @@ class Log;
 class AudioDecoder : public boost::enable_shared_from_this<AudioDecoder>, public DecoderPart
 {
 public:
-       AudioDecoder (Decoder* parent, boost::shared_ptr<const AudioContent>, boost::shared_ptr<Log> log);
-
-       boost::optional<ContentTime> position () const;
-
-       void set_fast ();
-       void flush ();
+       AudioDecoder (Decoder* parent, boost::shared_ptr<AudioContent> content, boost::shared_ptr<Log> log);
 
+       ContentTime position () const;
        void emit (AudioStreamPtr stream, boost::shared_ptr<const AudioBuffers>, ContentTime);
+       void seek ();
 
        boost::signals2::signal<void (AudioStreamPtr, ContentAudio)> Data;
 
 private:
-       /** An AudioDecoderStream object to manage each stream in _audio_content */
-       typedef std::map<AudioStreamPtr, boost::shared_ptr<AudioDecoderStream> > StreamMap;
-       StreamMap _streams;
-       std::map<AudioStreamPtr, ContentTime> _positions;
+       std::map<AudioStreamPtr, Frame> _positions;
 };
 
 #endif
diff --git a/src/lib/audio_decoder_stream.cc b/src/lib/audio_decoder_stream.cc
deleted file mode 100644 (file)
index 8f0905e..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
-    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
-
-    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 <http://www.gnu.org/licenses/>.
-
-*/
-
-#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 <iostream>
-
-#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<const AudioContent> content, AudioStreamPtr stream, Decoder* decoder, AudioDecoder* audio_decoder, shared_ptr<Log> 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<AudioBuffers> (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<const AudioBuffers> 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<AudioBuffers> 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<ContentTime> ();
-       }
-
-       if (!_position) {
-               _position = time.frames_round (frame_rate);
-       }
-
-       DCPOMATIC_ASSERT (_position.get() >= (_decoded.frame + _decoded.audio->frames()));
-
-       add (data);
-}
-
-void
-AudioDecoderStream::add (shared_ptr<const AudioBuffers> 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<const AudioBuffers> b = _resampler->flush ();
-       if (b) {
-               add (b);
-       }
-}
-
-void
-AudioDecoderStream::set_fast ()
-{
-       if (_resampler) {
-               _resampler->set_fast ();
-       }
-}
-
-optional<ContentTime>
-AudioDecoderStream::position () const
-{
-       if (!_position) {
-               return optional<ContentTime> ();
-       }
-
-       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 (file)
index b2ab65a..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-    Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
-
-    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 <http://www.gnu.org/licenses/>.
-
-*/
-
-#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 <boost/shared_ptr.hpp>
-
-class AudioContent;
-class AudioDecoder;
-class Resampler;
-class Log;
-class Decoder;
-
-class AudioDecoderStream
-{
-public:
-       AudioDecoderStream (boost::shared_ptr<const AudioContent>, AudioStreamPtr, Decoder* decoder, AudioDecoder* audio_decoder, boost::shared_ptr<Log> log);
-
-       void audio (boost::shared_ptr<const AudioBuffers>, ContentTime);
-       void flush ();
-       void set_fast ();
-
-       boost::optional<ContentTime> position () const;
-
-private:
-
-       void reset_decoded ();
-       void add (boost::shared_ptr<const AudioBuffers>);
-
-       boost::shared_ptr<const AudioContent> _content;
-       AudioStreamPtr _stream;
-       Decoder* _decoder;
-       AudioDecoder* _audio_decoder;
-       boost::shared_ptr<Log> _log;
-       boost::shared_ptr<Resampler> _resampler;
-       boost::optional<Frame> _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<ContentTime> _seek_reference;
-};
-
-#endif
index a0d300b7f8b52ae6550313ec7d1d34e5f3942912..49cdea6a354ebe6c4c4b45503c3122795d2e1198 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "audio_merger.h"
 #include "dcpomatic_time.h"
+#include <iostream>
 
 using std::pair;
 using std::min;
index c9026b93eb99aedf131f0e68db54b148c839ee9f..6db28b6c3cb2e841fa4b8996ff3c383075dda5f8 100644 (file)
@@ -31,6 +31,9 @@ public:
         */
        std::pair<boost::shared_ptr<AudioBuffers>, DCPTime> pull (DCPTime time);
        void push (boost::shared_ptr<const AudioBuffers> audio, DCPTime time);
+       DCPTime last_pull () const {
+               return _last_pull;
+       }
 
 private:
        boost::shared_ptr<AudioBuffers> _buffers;
index 4bfbf0cc708d859cb92b8ea5a65c9c59c837777c..509e7e4a6c92f326536637238db75bebb2081f14 100644 (file)
@@ -39,12 +39,12 @@ public:
                , frame (0)
        {}
 
-       ContentAudio (boost::shared_ptr<AudioBuffers> a, Frame f)
+       ContentAudio (boost::shared_ptr<const AudioBuffers> a, Frame f)
                : audio (a)
                , frame (f)
        {}
 
-       boost::shared_ptr<AudioBuffers> audio;
+       boost::shared_ptr<const AudioBuffers> audio;
        Frame frame;
 };
 
index e7f04a0619ac57028b0e375b2f2af05eec1e1c54..0b99e8dd08f36f42d60da9e6559241db00b32411 100644 (file)
@@ -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 ();
index 120836643dfcd8019581cf512c6462bd39be3a71..04b264192cd9d43c184449820b9c16213118ed28 100644 (file)
@@ -38,8 +38,10 @@ DCPSubtitleDecoder::DCPSubtitleDecoder (shared_ptr<const DCPSubtitleContent> con
 }
 
 void
-DCPSubtitleDecoder::seek (ContentTime time, bool)
+DCPSubtitleDecoder::seek (ContentTime time, bool accurate)
 {
+       Decoder::seek (time, accurate);
+
        _next = _subtitles.begin ();
        list<dcp::SubtitleString>::const_iterator i = _subtitles.begin ();
        while (i != _subtitles.end() && ContentTime::from_seconds (_next->in().as_seconds()) < time) {
index 785fb96f0c2ed4b0b7637abba296bcbb61ca40b1..ee03a1579c59bfe399927fb76a85e316fba21edb 100644 (file)
 #include "video_decoder.h"
 #include "audio_decoder.h"
 #include "subtitle_decoder.h"
+#include <boost/optional.hpp>
+#include <iostream>
+
+using std::cout;
+using boost::optional;
 
 ContentTime
 Decoder::position () const
 {
-       ContentTime pos;
+       optional<ContentTime> 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 ();
+       }
 }
index 26035d221080a5304cbccdde0bcf4490c78550bf..d87ff610ac47d3a9eae2eb795ac072a397c26b3f 100644 (file)
@@ -47,9 +47,11 @@ public:
        boost::shared_ptr<AudioDecoder> audio;
        boost::shared_ptr<SubtitleDecoder> 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;
 };
index 39f77e6e6cbabedffc1e6bd538135a1db7c73019..a9568be8a6586a84b8d596c3f46795f2e89b0de5 100644 (file)
@@ -33,7 +33,7 @@ public:
        DecoderPart (Decoder* parent, boost::shared_ptr<Log> log);
        virtual ~DecoderPart () {}
 
-       virtual boost::optional<ContentTime> position () const = 0;
+       virtual ContentTime position () const = 0;
 
        void set_ignore () {
                _ignore = true;
index b7b70e0618ad129137b6e773e388d0ed59d05e35..1bae99d6368b5b3f3031ba1450e40d2144a7eda5 100644 (file)
@@ -112,7 +112,6 @@ FFmpegDecoder::flush ()
 
        if (audio) {
                decode_audio_packet ();
-               audio->flush ();
        }
 }
 
@@ -299,6 +298,8 @@ FFmpegDecoder::bytes_per_audio_sample (shared_ptr<FFmpegAudioStream> 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.
        */
index 5a637f4df98d4be1bbe4da7c2753adfcc7a4bfb6..fd51c1ba3963888ec891fc44e19349407a29c507 100644 (file)
@@ -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 ());
 }
index b5ab2d233918ecbe542c83564bfc747d3cd7c963..711e292eed451bd7961b8d11d3fa5111a83bdb40 100644 (file)
@@ -33,11 +33,13 @@ public:
                : content (c)
                , decoder (d)
                , frc (f)
+               , done (false)
        {}
 
        boost::shared_ptr<Content> content;
        boost::shared_ptr<Decoder> decoder;
        FrameRateChange frc;
+       bool done;
 };
 
 #endif
index 611e9c9002344b032485dff3febf094a922dac56..7c2048489db81f5c9950aee6004430ebebde2f49 100644 (file)
@@ -47,6 +47,8 @@
 #include "content_subtitle.h"
 #include "dcp_decoder.h"
 #include "image_decoder.h"
+#include "resampler.h"
+#include "compose.hpp"
 #include <dcp/reel.h>
 #include <dcp/reel_sound_asset.h>
 #include <dcp/reel_subtitle_asset.h>
@@ -122,10 +124,6 @@ Player::setup_pieces ()
                        decoder->audio->set_ignore ();
                }
 
-               if (decoder->audio && _fast) {
-                       decoder->audio->set_fast ();
-               }
-
                shared_ptr<DCPDecoder> dcp = dynamic_pointer_cast<DCPDecoder> (decoder);
                if (dcp && _play_referenced) {
                        dcp->set_decode_referenced ();
@@ -508,12 +506,15 @@ Player::pass ()
        }
 
        shared_ptr<Piece> earliest;
-       DCPTime earliest_position;
+       DCPTime earliest_content;
+
        BOOST_FOREACH (shared_ptr<Piece> 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<shared_ptr<AudioBuffers>, DCPTime> audio = _audio_merger.pull (earliest_position);
+       optional<DCPTime> earliest_audio;
+       BOOST_FOREACH (shared_ptr<Piece> 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<shared_ptr<AudioBuffers>, 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<Piece> 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<PositionImage> subtitles;
@@ -646,22 +662,28 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
        shared_ptr<AudioContent> content = piece->content->audio;
        DCPOMATIC_ASSERT (content);
 
-       shared_ptr<AudioBuffers> audio = content_audio.audio;
-
        /* Gain */
        if (content->gain() != 0) {
-               shared_ptr<AudioBuffers> gain (new AudioBuffers (audio));
+               shared_ptr<AudioBuffers> 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<Resampler> r = resampler (content, stream, true);
+               pair<shared_ptr<const AudioBuffers>, 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<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames()));
+       shared_ptr<AudioBuffers> 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<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
                for (int j = 0; j < dcp_mapped->channels(); ++j) {
                        if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
                                dcp_mapped->accumulate_channel (
-                                       audio.get(),
+                                       content_audio.audio.get(),
                                        i,
                                        static_cast<dcp::Channel> (j),
                                        map.get (i, static_cast<dcp::Channel> (j))
@@ -678,13 +700,23 @@ Player::audio (weak_ptr<Piece> 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<Piece> 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<DCPTime> ();
        }
 }
+
+shared_ptr<Resampler>
+Player::resampler (shared_ptr<const AudioContent> 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<Resampler> ();
+       }
+
+       LOG_GENERAL (
+               "Creating new resampler from %1 to %2 with %3 channels",
+               stream->frame_rate(),
+               content->resampled_frame_rate(),
+               stream->channels()
+               );
+
+       shared_ptr<Resampler> r (
+               new Resampler (stream->frame_rate(), content->resampled_frame_rate(), stream->channels())
+               );
+
+       _resamplers[make_pair(content, stream)] = r;
+       return r;
+}
index ba7845b541d935e905663eca9a91abdad9dafe17..c891ee85c0371f01dd97b8b72cb4bc474f2336ce 100644 (file)
@@ -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<Piece>, AudioStreamPtr, ContentAudio);
        void image_subtitle (boost::weak_ptr<Piece>, ContentImageSubtitle);
        void text_subtitle (boost::weak_ptr<Piece>, ContentTextSubtitle);
+       boost::shared_ptr<Resampler> resampler (boost::shared_ptr<const AudioContent> content, AudioStreamPtr stream, bool create);
 
        boost::shared_ptr<const Film> _film;
        boost::shared_ptr<const Playlist> _playlist;
@@ -141,6 +143,8 @@ private:
        std::list<std::pair<PlayerSubtitles, DCPTimePeriod> > _subtitles;
 
        boost::shared_ptr<AudioProcessor> _audio_processor;
+       typedef std::map<std::pair<boost::shared_ptr<const AudioContent>, AudioStreamPtr>, boost::shared_ptr<Resampler> > ResamplerMap;
+       ResamplerMap _resamplers;
 
        boost::signals2::scoped_connection _film_changed_connection;
        boost::signals2::scoped_connection _playlist_changed_connection;
index 01d71c79b68219caefff036363336eb5e23a55f6..d08e7bc38e22f60eab6486d4c9626dc5562837f7 100644 (file)
@@ -67,9 +67,19 @@ Resampler::set_fast ()
        }
 }
 
-shared_ptr<const AudioBuffers>
-Resampler::run (shared_ptr<const AudioBuffers> in)
+pair<shared_ptr<const AudioBuffers>, Frame>
+Resampler::run (shared_ptr<const AudioBuffers> 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<const AudioBuffers> 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<const AudioBuffers>
index afc28aefd01ce58a7ef2a1a431d29fff02ae4311..359598b122b17b4f1dbe30139eaba8ce91409bbe 100644 (file)
@@ -31,7 +31,7 @@ public:
        Resampler (int, int, int);
        ~Resampler ();
 
-       boost::shared_ptr<const AudioBuffers> run (boost::shared_ptr<const AudioBuffers>);
+       std::pair<boost::shared_ptr<const AudioBuffers>, Frame> run (boost::shared_ptr<const AudioBuffers>, Frame);
        boost::shared_ptr<const AudioBuffers> flush ();
        void set_fast ();
 
@@ -40,4 +40,6 @@ private:
        int _in_rate;
        int _out_rate;
        int _channels;
+       boost::optional<Frame> _next_in;
+       boost::optional<Frame> _next_out;
 };
index f44766393c4d889d8f4604a656a8905ac4aacd38..2b38153bf514a63cd129e16e92e17427fe46bcc5 100644 (file)
@@ -48,7 +48,7 @@ public:
                boost::shared_ptr<Log> log
                );
 
-       boost::optional<ContentTime> position () const {
+       ContentTime position () const {
                return _position;
        }
 
@@ -65,7 +65,7 @@ public:
 
 private:
        boost::shared_ptr<const SubtitleContent> _content;
-       boost::optional<ContentTime> _position;
+       ContentTime _position;
 };
 
 #endif
index 1b8ee131004bd4fdb8f50e8399960c9ae45f7372..bdec17a8dba0a7ad386ba9f3b2e676ca881d7885 100644 (file)
@@ -42,8 +42,10 @@ TextSubtitleDecoder::TextSubtitleDecoder (shared_ptr<const TextSubtitleContent>
 }
 
 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;
index 6609811e4339f804288feda8b68ed8068329dcda..e16884568555a72b7c384ffa3a29376ed9681910 100644 (file)
@@ -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<ContentTime> position () const {
+       ContentTime position () const {
                return _position;
        }
 
@@ -62,7 +62,7 @@ public:
 private:
        boost::shared_ptr<const Content> _content;
        boost::optional<Frame> _last_emitted;
-       boost::optional<ContentTime> _position;
+       ContentTime _position;
 };
 
 #endif
index 7b4066f20eaaef204b48518aacbf7fba356d8c88..216721375fac0f832bf06c17dc5c117f890e50d1 100644 (file)
@@ -94,7 +94,8 @@ VideoMXFDecoder::pass ()
 }
 
 void
-VideoMXFDecoder::seek (ContentTime t, bool)
+VideoMXFDecoder::seek (ContentTime t, bool accurate)
 {
+       Decoder::seek (t, accurate);
        _next = t;
 }
index 0e4e5bcfc34371cfa278af370c356af71e9ea435..efcb2bc1efef6fe30bb178778169715a30be74a2 100644 (file)
@@ -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
index 6f102dd4c452d15dbd2645e580ae281070de0d87..6f3e3fef1e424226fc6949339f00cb2a2f92c26a 100644 (file)
@@ -44,7 +44,7 @@ resampler_test_one (int from, int to)
        for (int64_t i = 0; i < N; i += 1000) {
                shared_ptr<AudioBuffers> a (new AudioBuffers (1, 1000));
                a->make_silent ();
-               shared_ptr<const AudioBuffers> r = resamp.run (a);
+               pair<shared_ptr<const AudioBuffers>, Frame> r = resamp.run (a, 0);
        }
 }