summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2017-02-21 21:42:44 +0000
committerCarl Hetherington <cth@carlh.net>2017-04-19 23:04:32 +0100
commit89aa9d4ba69e471949f791cdafe4ae20cea554d2 (patch)
treea8260555268d392292775a2851d8780e5612091b /src/lib
parent7db99ef207c68910ee96a3e806c9832e8f90b219 (diff)
Various fixes to push audio vaguely in the right direction.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/audio_decoder.cc65
-rw-r--r--src/lib/audio_decoder.h14
-rw-r--r--src/lib/audio_decoder_stream.cc178
-rw-r--r--src/lib/audio_decoder_stream.h66
-rw-r--r--src/lib/audio_merger.cc1
-rw-r--r--src/lib/audio_merger.h3
-rw-r--r--src/lib/content_audio.h4
-rw-r--r--src/lib/dcp_decoder.cc4
-rw-r--r--src/lib/dcp_subtitle_decoder.cc4
-rw-r--r--src/lib/decoder.cc29
-rw-r--r--src/lib/decoder.h6
-rw-r--r--src/lib/decoder_part.h2
-rw-r--r--src/lib/ffmpeg_decoder.cc3
-rw-r--r--src/lib/image_decoder.cc3
-rw-r--r--src/lib/piece.h2
-rw-r--r--src/lib/player.cc102
-rw-r--r--src/lib/player.h4
-rw-r--r--src/lib/resampler.cc21
-rw-r--r--src/lib/resampler.h4
-rw-r--r--src/lib/subtitle_decoder.h4
-rw-r--r--src/lib/text_subtitle_decoder.cc4
-rw-r--r--src/lib/video_decoder.h4
-rw-r--r--src/lib/video_mxf_decoder.cc3
-rw-r--r--src/lib/wscript1
24 files changed, 182 insertions, 349 deletions
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<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;
}
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<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
index 8f0905e0d..000000000
--- a/src/lib/audio_decoder_stream.cc
+++ /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
index b2ab65ac0..000000000
--- a/src/lib/audio_decoder_stream.h
+++ /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
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 <iostream>
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<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;
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<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;
};
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<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) {
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 <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 ();
+ }
}
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<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;
};
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> log);
virtual ~DecoderPart () {}
- virtual boost::optional<ContentTime> 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<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.
*/
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> content;
boost::shared_ptr<Decoder> 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 <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;
+}
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<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;
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<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>
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<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;
};
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> 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
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<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;
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<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
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