summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2013-05-16 08:36:47 +0100
committerCarl Hetherington <cth@carlh.net>2013-05-16 08:36:47 +0100
commit21ce34c2cd04a2e7e133ff693b84c054182f4f91 (patch)
tree5bda50a34b2fa7526dcd682578247f75a85d26b1 /src
parent0db016f90ae722fc8b72d465e21d9f153f72b340 (diff)
Compiles; strange hang on adding content to a film.
Diffstat (limited to 'src')
-rw-r--r--src/lib/analyse_audio_job.cc4
-rw-r--r--src/lib/audio_buffers.cc222
-rw-r--r--src/lib/audio_buffers.h67
-rw-r--r--src/lib/audio_content.cc6
-rw-r--r--src/lib/audio_content.h5
-rw-r--r--src/lib/audio_decoder.cc14
-rw-r--r--src/lib/audio_decoder.h1
-rw-r--r--src/lib/audio_mapping.cc51
-rw-r--r--src/lib/audio_mapping.h28
-rw-r--r--src/lib/audio_sink.h2
-rw-r--r--src/lib/combiner.cc4
-rw-r--r--src/lib/content.h2
-rw-r--r--src/lib/decoder.h1
-rw-r--r--src/lib/encoder.cc17
-rw-r--r--src/lib/encoder.h2
-rw-r--r--src/lib/ffmpeg_content.cc33
-rw-r--r--src/lib/ffmpeg_content.h5
-rw-r--r--src/lib/ffmpeg_decoder.cc5
-rw-r--r--src/lib/film.cc188
-rw-r--r--src/lib/film.h48
-rw-r--r--src/lib/format.cc3
-rw-r--r--src/lib/format.h2
-rw-r--r--src/lib/imagemagick_content.cc7
-rw-r--r--src/lib/imagemagick_content.h1
-rw-r--r--src/lib/player.cc338
-rw-r--r--src/lib/player.h53
-rw-r--r--src/lib/playlist.cc419
-rw-r--r--src/lib/playlist.h77
-rw-r--r--src/lib/sndfile_content.cc14
-rw-r--r--src/lib/sndfile_content.h5
-rw-r--r--src/lib/sndfile_decoder.cc3
-rw-r--r--src/lib/subtitle.h2
-rw-r--r--src/lib/transcode_job.cc13
-rw-r--r--src/lib/types.h10
-rw-r--r--src/lib/util.cc227
-rw-r--r--src/lib/util.h50
-rw-r--r--src/lib/video_content.cc6
-rw-r--r--src/lib/video_content.h1
-rw-r--r--src/lib/video_decoder.cc4
-rw-r--r--src/lib/writer.cc47
-rw-r--r--src/lib/wscript1
-rw-r--r--src/wx/audio_dialog.cc4
-rw-r--r--src/wx/audio_mapping_view.cc8
-rw-r--r--src/wx/ffmpeg_content_dialog.cc2
-rw-r--r--src/wx/film_editor.cc149
-rw-r--r--src/wx/film_editor.h6
-rw-r--r--src/wx/film_viewer.cc18
-rw-r--r--src/wx/properties_dialog.cc10
-rw-r--r--src/wx/timeline.cc102
-rw-r--r--src/wx/timeline.h16
-rw-r--r--src/wx/timeline_dialog.cc4
-rw-r--r--src/wx/timeline_dialog.h2
52 files changed, 972 insertions, 1337 deletions
diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc
index f3c55b208..0ed33827c 100644
--- a/src/lib/analyse_audio_job.cc
+++ b/src/lib/analyse_audio_job.cc
@@ -55,13 +55,13 @@ AnalyseAudioJob::run ()
player->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1));
- _samples_per_point = max (int64_t (1), _film->audio_length() / _num_points);
+ _samples_per_point = max (int64_t (1), _film->time_to_audio_frames (_film->length()) / _num_points);
_current.resize (MAX_AUDIO_CHANNELS);
_analysis.reset (new AudioAnalysis (MAX_AUDIO_CHANNELS));
while (!player->pass()) {
- set_progress (float (_done) / _film->audio_length ());
+ set_progress (float (_done) / _film->time_to_audio_frames (_film->length ()));
}
_analysis->write (_film->audio_analysis_path ());
diff --git a/src/lib/audio_buffers.cc b/src/lib/audio_buffers.cc
new file mode 100644
index 000000000..cd8fcd35b
--- /dev/null
+++ b/src/lib/audio_buffers.cc
@@ -0,0 +1,222 @@
+/*
+ Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+
+ This program 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.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cassert>
+#include <cstring>
+#include <stdexcept>
+#include "audio_buffers.h"
+
+using std::bad_alloc;
+using boost::shared_ptr;
+
+/** Construct an AudioBuffers. Audio data is undefined after this constructor.
+ * @param channels Number of channels.
+ * @param frames Number of frames to reserve space for.
+ */
+AudioBuffers::AudioBuffers (int channels, int frames)
+ : _channels (channels)
+ , _frames (frames)
+ , _allocated_frames (frames)
+{
+ _data = static_cast<float**> (malloc (_channels * sizeof (float *)));
+ if (!_data) {
+ throw bad_alloc ();
+ }
+
+ for (int i = 0; i < _channels; ++i) {
+ _data[i] = static_cast<float*> (malloc (frames * sizeof (float)));
+ if (!_data[i]) {
+ throw bad_alloc ();
+ }
+ }
+}
+
+/** Copy constructor.
+ * @param other Other AudioBuffers; data is copied.
+ */
+AudioBuffers::AudioBuffers (AudioBuffers const & other)
+ : _channels (other._channels)
+ , _frames (other._frames)
+ , _allocated_frames (other._frames)
+{
+ _data = static_cast<float**> (malloc (_channels * sizeof (float *)));
+ if (!_data) {
+ throw bad_alloc ();
+ }
+
+ for (int i = 0; i < _channels; ++i) {
+ _data[i] = static_cast<float*> (malloc (_frames * sizeof (float)));
+ if (!_data[i]) {
+ throw bad_alloc ();
+ }
+ memcpy (_data[i], other._data[i], _frames * sizeof (float));
+ }
+}
+
+/* XXX: it's a shame that this is a copy-and-paste of the above;
+ probably fixable with c++0x.
+*/
+AudioBuffers::AudioBuffers (boost::shared_ptr<const AudioBuffers> other)
+ : _channels (other->_channels)
+ , _frames (other->_frames)
+ , _allocated_frames (other->_frames)
+{
+ _data = static_cast<float**> (malloc (_channels * sizeof (float *)));
+ if (!_data) {
+ throw bad_alloc ();
+ }
+
+ for (int i = 0; i < _channels; ++i) {
+ _data[i] = static_cast<float*> (malloc (_frames * sizeof (float)));
+ if (!_data[i]) {
+ throw bad_alloc ();
+ }
+ memcpy (_data[i], other->_data[i], _frames * sizeof (float));
+ }
+}
+
+/** AudioBuffers destructor */
+AudioBuffers::~AudioBuffers ()
+{
+ for (int i = 0; i < _channels; ++i) {
+ free (_data[i]);
+ }
+
+ free (_data);
+}
+
+/** @param c Channel index.
+ * @return Buffer for this channel.
+ */
+float*
+AudioBuffers::data (int c) const
+{
+ assert (c >= 0 && c < _channels);
+ return _data[c];
+}
+
+/** Set the number of frames that these AudioBuffers will report themselves
+ * as having.
+ * @param f Frames; must be less than or equal to the number of allocated frames.
+ */
+void
+AudioBuffers::set_frames (int f)
+{
+ assert (f <= _allocated_frames);
+ _frames = f;
+}
+
+/** Make all samples on all channels silent */
+void
+AudioBuffers::make_silent ()
+{
+ for (int i = 0; i < _channels; ++i) {
+ make_silent (i);
+ }
+}
+
+/** Make all samples on a given channel silent.
+ * @param c Channel.
+ */
+void
+AudioBuffers::make_silent (int c)
+{
+ assert (c >= 0 && c < _channels);
+
+ for (int i = 0; i < _frames; ++i) {
+ _data[c][i] = 0;
+ }
+}
+
+/** Copy data from another AudioBuffers to this one. All channels are copied.
+ * @param from AudioBuffers to copy from; must have the same number of channels as this.
+ * @param frames_to_copy Number of frames to copy.
+ * @param read_offset Offset to read from in `from'.
+ * @param write_offset Offset to write to in `to'.
+ */
+void
+AudioBuffers::copy_from (AudioBuffers const * from, int frames_to_copy, int read_offset, int write_offset)
+{
+ assert (from->channels() == channels());
+
+ assert (from);
+ assert (read_offset >= 0 && (read_offset + frames_to_copy) <= from->_allocated_frames);
+ assert (write_offset >= 0 && (write_offset + frames_to_copy) <= _allocated_frames);
+
+ for (int i = 0; i < _channels; ++i) {
+ memcpy (_data[i] + write_offset, from->_data[i] + read_offset, frames_to_copy * sizeof(float));
+ }
+}
+
+/** Move audio data around.
+ * @param from Offset to move from.
+ * @param to Offset to move to.
+ * @param frames Number of frames to move.
+ */
+
+void
+AudioBuffers::move (int from, int to, int frames)
+{
+ if (frames == 0) {
+ return;
+ }
+
+ assert (from >= 0);
+ assert (from < _frames);
+ assert (to >= 0);
+ assert (to < _frames);
+ assert (frames > 0);
+ assert (frames <= _frames);
+ assert ((from + frames) <= _frames);
+ assert ((to + frames) <= _frames);
+
+ for (int i = 0; i < _channels; ++i) {
+ memmove (_data[i] + to, _data[i] + from, frames * sizeof(float));
+ }
+}
+
+/** Add data from from `from', `from_channel' to our channel `to_channel' */
+void
+AudioBuffers::accumulate (AudioBuffers const * from, int from_channel, int to_channel)
+{
+ int const N = frames ();
+ assert (from->frames() == N);
+
+ float* s = from->data (from_channel);
+ float* d = _data[to_channel];
+
+ for (int i = 0; i < N; ++i) {
+ *d++ += *s++;
+ }
+}
+
+void
+AudioBuffers::ensure_size (int frames)
+{
+ if (_allocated_frames >= frames) {
+ return;
+ }
+
+ for (int i = 0; i < _channels; ++i) {
+ _data[i] = static_cast<float*> (realloc (_data[i], _frames * sizeof (float)));
+ if (!_data[i]) {
+ throw bad_alloc ();
+ }
+ }
+}
diff --git a/src/lib/audio_buffers.h b/src/lib/audio_buffers.h
new file mode 100644
index 000000000..5e7b9fda4
--- /dev/null
+++ b/src/lib/audio_buffers.h
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+
+ This program 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.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/shared_ptr.hpp>
+
+/** @class AudioBuffers
+ * @brief A class to hold multi-channel audio data in float format.
+ */
+class AudioBuffers
+{
+public:
+ AudioBuffers (int channels, int frames);
+ AudioBuffers (AudioBuffers const &);
+ AudioBuffers (boost::shared_ptr<const AudioBuffers>);
+ ~AudioBuffers ();
+
+ void ensure_size (int);
+
+ float** data () const {
+ return _data;
+ }
+
+ float* data (int) const;
+
+ int channels () const {
+ return _channels;
+ }
+
+ int frames () const {
+ return _frames;
+ }
+
+ void set_frames (int f);
+
+ void make_silent ();
+ void make_silent (int c);
+
+ void copy_from (AudioBuffers const * from, int frames_to_copy, int read_offset, int write_offset);
+ void move (int from, int to, int frames);
+ void accumulate (AudioBuffers const *, int, int);
+
+private:
+ /** Number of channels */
+ int _channels;
+ /** Number of frames (where a frame is one sample across all channels) */
+ int _frames;
+ /** Number of frames that _data can hold */
+ int _allocated_frames;
+ /** Audio data (so that, e.g. _data[2][6] is channel 2, sample 6) */
+ float** _data;
+};
diff --git a/src/lib/audio_content.cc b/src/lib/audio_content.cc
index dfa48d97e..9968f4725 100644
--- a/src/lib/audio_content.cc
+++ b/src/lib/audio_content.cc
@@ -43,9 +43,3 @@ AudioContent::AudioContent (AudioContent const & o)
{
}
-
-Time
-AudioContent::temporal_length () const
-{
- return audio_length() / audio_frame_rate();
-}
diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h
index 18107843c..87858488c 100644
--- a/src/lib/audio_content.h
+++ b/src/lib/audio_content.h
@@ -44,9 +44,8 @@ public:
virtual int audio_channels () const = 0;
virtual ContentAudioFrame audio_length () const = 0;
- virtual int audio_frame_rate () const = 0;
-
- Time temporal_length () const;
+ virtual int content_audio_frame_rate () const = 0;
+ virtual int output_audio_frame_rate (boost::shared_ptr<const Film>) const = 0;
};
#endif
diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc
index 68554daf9..e1c93ac77 100644
--- a/src/lib/audio_decoder.cc
+++ b/src/lib/audio_decoder.cc
@@ -18,6 +18,7 @@
*/
#include "audio_decoder.h"
+#include "audio_buffers.h"
#include "exceptions.h"
#include "log.h"
@@ -30,11 +31,12 @@ using boost::shared_ptr;
AudioDecoder::AudioDecoder (shared_ptr<const Film> f, shared_ptr<const AudioContent> c)
: Decoder (f)
, _audio_content (c)
+ , _output_audio_frame_rate (_audio_content->output_audio_frame_rate (f))
{
- if (_audio_content->audio_frame_rate() != _film->target_audio_sample_rate()) {
+ if (_audio_content->content_audio_frame_rate() != _output_audio_frame_rate) {
stringstream s;
- s << String::compose ("Will resample audio from %1 to %2", _audio_content->audio_frame_rate(), _film->target_audio_sample_rate());
+ s << String::compose ("Will resample audio from %1 to %2", _audio_content->content_audio_frame_rate(), _output_audio_frame_rate);
_film->log()->log (s.str ());
/* We will be using planar float data when we call the
@@ -49,10 +51,10 @@ AudioDecoder::AudioDecoder (shared_ptr<const Film> f, shared_ptr<const AudioCont
0,
av_get_default_channel_layout (MAX_AUDIO_CHANNELS),
AV_SAMPLE_FMT_FLTP,
- _film->target_audio_sample_rate(),
+ _output_audio_frame_rate,
av_get_default_channel_layout (MAX_AUDIO_CHANNELS),
AV_SAMPLE_FMT_FLTP,
- _audio_content->audio_frame_rate(),
+ _audio_content->content_audio_frame_rate(),
0, 0
);
@@ -74,7 +76,7 @@ AudioDecoder::~AudioDecoder ()
void
AudioDecoder::process_end ()
{
- if (_film->has_audio() && _swr_context) {
+ if (_swr_context) {
shared_ptr<AudioBuffers> out (new AudioBuffers (_film->audio_mapping().dcp_channels(), 256));
@@ -106,7 +108,7 @@ AudioDecoder::emit_audio (shared_ptr<const AudioBuffers> data, Time time)
if (_swr_context) {
/* Compute the resampled frames count and add 32 for luck */
- int const max_resampled_frames = ceil ((int64_t) data->frames() * _film->target_audio_sample_rate() / _audio_content->audio_frame_rate()) + 32;
+ int const max_resampled_frames = ceil ((int64_t) data->frames() * _output_audio_frame_rate / _audio_content->content_audio_frame_rate()) + 32;
shared_ptr<AudioBuffers> resampled (new AudioBuffers (MAX_AUDIO_CHANNELS, max_resampled_frames));
diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h
index 1c7287a55..94845ec23 100644
--- a/src/lib/audio_decoder.h
+++ b/src/lib/audio_decoder.h
@@ -46,6 +46,7 @@ public:
private:
boost::shared_ptr<const AudioContent> _audio_content;
SwrContext* _swr_context;
+ int _output_audio_frame_rate;
};
#endif
diff --git a/src/lib/audio_mapping.cc b/src/lib/audio_mapping.cc
index 7e28aa5c4..e1fa0c220 100644
--- a/src/lib/audio_mapping.cc
+++ b/src/lib/audio_mapping.cc
@@ -31,7 +31,7 @@ using boost::lexical_cast;
using boost::dynamic_pointer_cast;
void
-AudioMapping::add (Channel c, libdcp::Channel d)
+AudioMapping::add (int c, libdcp::Channel d)
{
_content_to_dcp.push_back (make_pair (c, d));
}
@@ -40,7 +40,7 @@ AudioMapping::add (Channel c, libdcp::Channel d)
int
AudioMapping::dcp_channels () const
{
- for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+ for (list<pair<int, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
if (((int) i->second) >= 2) {
return 6;
}
@@ -49,11 +49,11 @@ AudioMapping::dcp_channels () const
return 2;
}
-list<AudioMapping::Channel>
+list<int>
AudioMapping::dcp_to_content (libdcp::Channel d) const
{
- list<AudioMapping::Channel> c;
- for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+ list<int> c;
+ for (list<pair<int, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
if (i->second == d) {
c.push_back (i->first);
}
@@ -62,11 +62,11 @@ AudioMapping::dcp_to_content (libdcp::Channel d) const
return c;
}
-list<AudioMapping::Channel>
+list<int>
AudioMapping::content_channels () const
{
- list<AudioMapping::Channel> c;
- for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+ list<int> c;
+ for (list<pair<int, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
if (find (c.begin(), c.end(), i->first) == c.end ()) {
c.push_back (i->first);
}
@@ -76,10 +76,10 @@ AudioMapping::content_channels () const
}
list<libdcp::Channel>
-AudioMapping::content_to_dcp (Channel c) const
+AudioMapping::content_to_dcp (int c) const
{
list<libdcp::Channel> d;
- for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+ for (list<pair<int, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
if (i->first == c) {
d.push_back (i->second);
}
@@ -91,41 +91,18 @@ AudioMapping::content_to_dcp (Channel c) const
void
AudioMapping::as_xml (xmlpp::Node* node) const
{
- for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+ for (list<pair<int, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
xmlpp::Node* t = node->add_child ("Map");
- shared_ptr<const AudioContent> c = i->first.content.lock ();
- t->add_child ("Content")->add_child_text (c->digest ());
- t->add_child ("ContentIndex")->add_child_text (lexical_cast<string> (i->first.index));
+ t->add_child ("ContentIndex")->add_child_text (lexical_cast<string> (i->first));
t->add_child ("DCP")->add_child_text (lexical_cast<string> (i->second));
}
}
void
-AudioMapping::set_from_xml (ContentList const & content, shared_ptr<const cxml::Node> node)
+AudioMapping::set_from_xml (shared_ptr<const cxml::Node> node)
{
list<shared_ptr<cxml::Node> > const c = node->node_children ("Map");
for (list<shared_ptr<cxml::Node> >::const_iterator i = c.begin(); i != c.end(); ++i) {
- string const c = (*i)->string_child ("Content");
- ContentList::const_iterator j = content.begin ();
- while (j != content.end() && (*j)->digest() != c) {
- ++j;
- }
-
- if (j == content.end ()) {
- continue;
- }
-
- shared_ptr<const AudioContent> ac = dynamic_pointer_cast<const AudioContent> (*j);
- assert (ac);
-
- add (AudioMapping::Channel (ac, (*i)->number_child<int> ("ContentIndex")), static_cast<libdcp::Channel> ((*i)->number_child<int> ("DCP")));
+ add ((*i)->number_child<int> ("ContentIndex"), static_cast<libdcp::Channel> ((*i)->number_child<int> ("DCP")));
}
}
-
-bool
-operator== (AudioMapping::Channel const & a, AudioMapping::Channel const & b)
-{
- shared_ptr<const AudioContent> sa = a.content.lock ();
- shared_ptr<const AudioContent> sb = b.content.lock ();
- return sa == sb && a.index == b.index;
-}
diff --git a/src/lib/audio_mapping.h b/src/lib/audio_mapping.h
index 248d2570e..3c471d3f4 100644
--- a/src/lib/audio_mapping.h
+++ b/src/lib/audio_mapping.h
@@ -30,33 +30,21 @@ class AudioMapping
{
public:
void as_xml (xmlpp::Node *) const;
- void set_from_xml (ContentList const &, boost::shared_ptr<const cxml::Node>);
-
- struct Channel {
- Channel (boost::weak_ptr<const AudioContent> c, int i)
- : content (c)
- , index (i)
- {}
-
- boost::weak_ptr<const AudioContent> content;
- int index;
- };
-
- void add (Channel, libdcp::Channel);
+ void set_from_xml (boost::shared_ptr<const cxml::Node>);
+
+ void add (int, libdcp::Channel);
int dcp_channels () const;
- std::list<Channel> dcp_to_content (libdcp::Channel) const;
- std::list<std::pair<Channel, libdcp::Channel> > content_to_dcp () const {
+ std::list<int> dcp_to_content (libdcp::Channel) const;
+ std::list<std::pair<int, libdcp::Channel> > content_to_dcp () const {
return _content_to_dcp;
}
- std::list<Channel> content_channels () const;
- std::list<libdcp::Channel> content_to_dcp (Channel) const;
+ std::list<int> content_channels () const;
+ std::list<libdcp::Channel> content_to_dcp (int) const;
private:
- std::list<std::pair<Channel, libdcp::Channel> > _content_to_dcp;
+ std::list<std::pair<int, libdcp::Channel> > _content_to_dcp;
};
-extern bool operator== (AudioMapping::Channel const &, AudioMapping::Channel const &);
-
#endif
diff --git a/src/lib/audio_sink.h b/src/lib/audio_sink.h
index 2e3ead005..1aad5edf9 100644
--- a/src/lib/audio_sink.h
+++ b/src/lib/audio_sink.h
@@ -20,6 +20,8 @@
#ifndef DCPOMATIC_AUDIO_SINK_H
#define DCPOMATIC_AUDIO_SINK_H
+class AudioBuffers;
+
class AudioSink
{
public:
diff --git a/src/lib/combiner.cc b/src/lib/combiner.cc
index 9f9461ec2..0afb9807a 100644
--- a/src/lib/combiner.cc
+++ b/src/lib/combiner.cc
@@ -33,7 +33,7 @@ Combiner::Combiner (shared_ptr<Log> log)
* @param image Frame image.
*/
void
-Combiner::process_video (shared_ptr<const Image> image, bool, shared_ptr<Subtitle>, double)
+Combiner::process_video (shared_ptr<const Image> image, bool, shared_ptr<Subtitle>, Time)
{
_image.reset (new SimpleImage (image));
}
@@ -43,7 +43,7 @@ Combiner::process_video (shared_ptr<const Image> image, bool, shared_ptr<Subtitl
* @param sub Subtitle (which will be put onto the whole frame)
*/
void
-Combiner::process_video_b (shared_ptr<const Image> image, bool, shared_ptr<Subtitle> sub, double t)
+Combiner::process_video_b (shared_ptr<const Image> image, bool, shared_ptr<Subtitle> sub, Time t)
{
/* Copy the right half of this image into our _image */
/* XXX: this should probably be in the Image class */
diff --git a/src/lib/content.h b/src/lib/content.h
index e1cf41df0..9f465570b 100644
--- a/src/lib/content.h
+++ b/src/lib/content.h
@@ -46,7 +46,7 @@ public:
virtual std::string information () const = 0;
virtual void as_xml (xmlpp::Node *) const;
virtual boost::shared_ptr<Content> clone () const = 0;
- virtual Time temporal_length () const = 0;
+ virtual Time length (boost::shared_ptr<const Film>) const = 0;
boost::filesystem::path file () const {
boost::mutex::scoped_lock lm (_mutex);
diff --git a/src/lib/decoder.h b/src/lib/decoder.h
index 02ccaa42b..ae0d0c671 100644
--- a/src/lib/decoder.h
+++ b/src/lib/decoder.h
@@ -29,7 +29,6 @@
#include <stdint.h>
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
-#include "util.h"
#include "video_source.h"
#include "audio_source.h"
#include "film.h"
diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc
index 6cb384e20..95e98ab76 100644
--- a/src/lib/encoder.cc
+++ b/src/lib/encoder.cc
@@ -58,7 +58,6 @@ int const Encoder::_history_size = 25;
Encoder::Encoder (shared_ptr<Film> f, shared_ptr<Job> j)
: _film (f)
, _job (j)
- , _video_frames_in (0)
, _video_frames_out (0)
, _have_a_real_frame (false)
, _terminate (false)
@@ -180,13 +179,6 @@ Encoder::frame_done ()
void
Encoder::process_video (shared_ptr<const Image> image, bool same, shared_ptr<Subtitle> sub, Time)
{
- FrameRateConversion frc (_film->video_frame_rate(), _film->dcp_frame_rate());
-
- if (frc.skip && (_video_frames_in % 2)) {
- ++_video_frames_in;
- return;
- }
-
boost::mutex::scoped_lock lock (_mutex);
/* Wait until the queue has gone down a bit */
@@ -220,7 +212,7 @@ Encoder::process_video (shared_ptr<const Image> image, bool same, shared_ptr<Sub
new DCPVideoFrame (
image, sub, _film->format()->dcp_size(), _film->format()->dcp_padding (_film),
_film->subtitle_offset(), _film->subtitle_scale(),
- _film->scaler(), _video_frames_out, _film->dcp_frame_rate(), s.second,
+ _film->scaler(), _video_frames_out, _film->dcp_video_frame_rate(), s.second,
_film->colour_lut(), _film->j2k_bandwidth(),
_film->log()
)
@@ -230,14 +222,7 @@ Encoder::process_video (shared_ptr<const Image> image, bool same, shared_ptr<Sub
_have_a_real_frame = true;
}
- ++_video_frames_in;
++_video_frames_out;
-
- if (frc.repeat) {
- _writer->repeat (_video_frames_out);
- ++_video_frames_out;
- frame_done ();
- }
}
void
diff --git a/src/lib/encoder.h b/src/lib/encoder.h
index b6d3663fd..6815fa6f6 100644
--- a/src/lib/encoder.h
+++ b/src/lib/encoder.h
@@ -101,8 +101,6 @@ private:
/** Number of frames that we should keep history for */
static int const _history_size;
- /** Number of video frames received so far */
- ContentVideoFrame _video_frames_in;
/** Number of video frames written for the DCP so far */
int _video_frames_out;
diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc
index a61f777c8..55281ff9b 100644
--- a/src/lib/ffmpeg_content.cc
+++ b/src/lib/ffmpeg_content.cc
@@ -206,7 +206,7 @@ FFmpegContent::audio_length () const
return 0;
}
- return video_frames_to_audio_frames (_video_length, audio_frame_rate(), video_frame_rate());
+ return video_frames_to_audio_frames (_video_length, content_audio_frame_rate(), video_frame_rate());
}
int
@@ -220,7 +220,7 @@ FFmpegContent::audio_channels () const
}
int
-FFmpegContent::audio_frame_rate () const
+FFmpegContent::content_audio_frame_rate () const
{
if (!_audio_stream) {
return 0;
@@ -229,6 +229,28 @@ FFmpegContent::audio_frame_rate () const
return _audio_stream->frame_rate;
}
+int
+FFmpegContent::output_audio_frame_rate (shared_ptr<const Film> film) const
+{
+ /* Resample to a DCI-approved sample rate */
+ double t = dcp_audio_frame_rate (content_audio_frame_rate ());
+
+ FrameRateConversion frc (video_frame_rate(), film->dcp_video_frame_rate());
+
+ /* Compensate if the DCP is being run at a different frame rate
+ to the source; that is, if the video is run such that it will
+ look different in the DCP compared to the source (slower or faster).
+ skip/repeat doesn't come into effect here.
+ */
+
+ if (frc.change_speed) {
+ t *= video_frame_rate() * frc.factor() / film->dcp_video_frame_rate();
+ cout << "-> " << t << "\n";
+ }
+
+ return rint (t);
+}
+
bool
operator== (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b)
{
@@ -281,8 +303,9 @@ FFmpegContent::clone () const
return shared_ptr<Content> (new FFmpegContent (*this));
}
-double
-FFmpegContent::temporal_length () const
+Time
+FFmpegContent::length (shared_ptr<const Film> film) const
{
- return video_length() / video_frame_rate();
+ FrameRateConversion frc (video_frame_rate (), film->dcp_video_frame_rate ());
+ return video_length() * frc.factor() * TIME_HZ / film->dcp_video_frame_rate ();
}
diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h
index 6d7151498..540df041f 100644
--- a/src/lib/ffmpeg_content.h
+++ b/src/lib/ffmpeg_content.h
@@ -89,12 +89,13 @@ public:
std::string information () const;
void as_xml (xmlpp::Node *) const;
boost::shared_ptr<Content> clone () const;
- double temporal_length () const;
+ Time length (boost::shared_ptr<const Film>) const;
/* AudioContent */
int audio_channels () const;
ContentAudioFrame audio_length () const;
- int audio_frame_rate () const;
+ int content_audio_frame_rate () const;
+ int output_audio_frame_rate (boost::shared_ptr<const Film>) const;
std::vector<FFmpegSubtitleStream> subtitle_streams () const {
boost::mutex::scoped_lock lm (_mutex);
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index 0e704bb14..d21a93e29 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -48,6 +48,7 @@ extern "C" {
#include "ffmpeg_decoder.h"
#include "filter_graph.h"
#include "subtitle.h"
+#include "audio_buffers.h"
#include "i18n.h"
@@ -219,8 +220,10 @@ FFmpegDecoder::setup_subtitle ()
bool
FFmpegDecoder::pass ()
{
- int r = av_read_frame (_format_context, &_packet);
+ cout << "FFmpeg::pass\n";
+ int r = av_read_frame (_format_context, &_packet);
+
if (r < 0) {
if (r != AVERROR_EOF) {
/* Maybe we should fail here, but for now we'll just finish off instead */
diff --git a/src/lib/film.cc b/src/lib/film.cc
index b8102d315..646b114da 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -108,7 +108,7 @@ Film::Film (string d, bool must_exist)
, _colour_lut (0)
, _j2k_bandwidth (200000000)
, _dci_metadata (Config::instance()->default_dci_metadata ())
- , _dcp_frame_rate (0)
+ , _dcp_video_frame_rate (0)
, _dirty (false)
{
set_dci_date_today ();
@@ -179,7 +179,7 @@ Film::Film (Film const & o)
, _colour_lut (o._colour_lut)
, _j2k_bandwidth (o._j2k_bandwidth)
, _dci_metadata (o._dci_metadata)
- , _dcp_frame_rate (o._dcp_frame_rate)
+ , _dcp_video_frame_rate (o._dcp_video_frame_rate)
, _dci_date (o._dci_date)
, _dirty (o._dirty)
{
@@ -198,7 +198,7 @@ Film::video_state_identifier () const
s << format()->id()
<< "_" << _playlist->video_digest()
<< "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom
- << "_" << _dcp_frame_rate
+ << "_" << _dcp_video_frame_rate
<< "_" << f.first << "_" << f.second
<< "_" << scaler()->id()
<< "_" << j2k_bandwidth()
@@ -315,8 +315,8 @@ Film::make_dcp ()
throw MissingSettingError (_("format"));
}
- if (_playlist->content().empty ()) {
- throw MissingSettingError (_("content"));
+ if (_playlist->regions().empty ()) {
+ throw StringError (_("You must add some content to the DCP before creating it"));
}
if (dcp_content_type() == 0) {
@@ -450,9 +450,8 @@ Film::write_metadata () const
root->add_child("ColourLUT")->add_child_text (boost::lexical_cast<string> (_colour_lut));
root->add_child("J2KBandwidth")->add_child_text (boost::lexical_cast<string> (_j2k_bandwidth));
_dci_metadata.as_xml (root->add_child ("DCIMetadata"));
- root->add_child("DCPFrameRate")->add_child_text (boost::lexical_cast<string> (_dcp_frame_rate));
+ root->add_child("DCPVideoFrameRate")->add_child_text (boost::lexical_cast<string> (_dcp_video_frame_rate));
root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date));
- _audio_mapping.as_xml (root->add_child("AudioMapping"));
_playlist->as_xml (root->add_child ("Playlist"));
doc.write_to_file_formatted (file ("metadata.xml"));
@@ -524,11 +523,10 @@ Film::read_metadata ()
_colour_lut = f.number_child<int> ("ColourLUT");
_j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
_dci_metadata = DCIMetadata (f.node_child ("DCIMetadata"));
- _dcp_frame_rate = f.number_child<int> ("DCPFrameRate");
+ _dcp_video_frame_rate = f.number_child<int> ("DCPVideoFrameRate");
_dci_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
_playlist->set_from_xml (f.node_child ("Playlist"));
- _audio_mapping.set_from_xml (_playlist->content(), f.node_child ("AudioMapping"));
_dirty = false;
}
@@ -577,32 +575,6 @@ Film::file (string f) const
return p.string ();
}
-/** @return The sampling rate that we will resample the audio to */
-int
-Film::target_audio_sample_rate () const
-{
- if (!has_audio ()) {
- return 0;
- }
-
- /* Resample to a DCI-approved sample rate */
- double t = dcp_audio_sample_rate (audio_frame_rate());
-
- FrameRateConversion frc (video_frame_rate(), dcp_frame_rate());
-
- /* Compensate if the DCP is being run at a different frame rate
- to the source; that is, if the video is run such that it will
- look different in the DCP compared to the source (slower or faster).
- skip/repeat doesn't come into effect here.
- */
-
- if (frc.change_speed) {
- t *= video_frame_rate() * frc.factor() / dcp_frame_rate();
- }
-
- return rint (t);
-}
-
/** @return a DCI-compliant name for a DCP of this film */
string
Film::dci_name (bool if_created_now) const
@@ -649,22 +621,7 @@ Film::dci_name (bool if_created_now) const
}
}
- switch (audio_channels ()) {
- case 1:
- d << "_10";
- break;
- case 2:
- d << "_20";
- break;
- case 6:
- d << "_51";
- break;
- case 8:
- d << "_71";
- break;
- }
-
- d << "_2K";
+ d << "_51_2K";
if (!dm.studio.empty ()) {
d << "_" << dm.studio;
@@ -738,11 +695,11 @@ Film::set_trust_content_headers (bool t)
signal_changed (TRUST_CONTENT_HEADERS);
- ContentList content = _playlist->content ();
- if (!_trust_content_headers && !content.empty()) {
+ Playlist::RegionList regions = _playlist->regions ();
+ if (!_trust_content_headers && !regions.empty()) {
/* We just said that we don't trust the content's header */
- for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
- examine_content (*i);
+ for (Playlist::RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ examine_content (i->content);
}
}
}
@@ -976,13 +933,13 @@ Film::set_dci_metadata (DCIMetadata m)
void
-Film::set_dcp_frame_rate (int f)
+Film::set_dcp_video_frame_rate (int f)
{
{
boost::mutex::scoped_lock lm (_state_mutex);
- _dcp_frame_rate = f;
+ _dcp_video_frame_rate = f;
}
- signal_changed (DCP_FRAME_RATE);
+ signal_changed (DCP_VIDEO_FRAME_RATE);
}
void
@@ -995,8 +952,7 @@ Film::signal_changed (Property p)
switch (p) {
case Film::CONTENT:
- set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ()));
- set_audio_mapping (_playlist->default_audio_mapping ());
+ set_dcp_video_frame_rate (_playlist->best_dcp_frame_rate ());
break;
default:
break;
@@ -1082,10 +1038,10 @@ Film::playlist () const
return _playlist;
}
-ContentList
-Film::content () const
+Playlist::RegionList
+Film::regions () const
{
- return _playlist->content ();
+ return _playlist->regions ();
}
void
@@ -1101,64 +1057,10 @@ Film::remove_content (shared_ptr<Content> c)
_playlist->remove (c);
}
-void
-Film::move_content_earlier (shared_ptr<Content> c)
-{
- _playlist->move_earlier (c);
-}
-
-void
-Film::move_content_later (shared_ptr<Content> c)
-{
- _playlist->move_later (c);
-}
-
-ContentAudioFrame
-Film::audio_length () const
-{
- return _playlist->audio_length ();
-}
-
-int
-Film::audio_channels () const
-{
- return _playlist->audio_channels ();
-}
-
-int
-Film::audio_frame_rate () const
-{
- return _playlist->audio_frame_rate ();
-}
-
-bool
-Film::has_audio () const
-{
- return _playlist->has_audio ();
-}
-
-float
-Film::video_frame_rate () const
-{
- return _playlist->video_frame_rate ();
-}
-
-libdcp::Size
-Film::video_size () const
+Time
+Film::length () const
{
- return _playlist->video_size ();
-}
-
-ContentVideoFrame
-Film::video_length () const
-{
- return _playlist->video_length ();
-}
-
-ContentVideoFrame
-Film::content_length () const
-{
- return _playlist->content_length ();
+ return _playlist->length (shared_from_this ());
}
bool
@@ -1167,24 +1069,17 @@ Film::has_subtitles () const
return _playlist->has_subtitles ();
}
-void
-Film::set_audio_mapping (AudioMapping m)
+OutputVideoFrame
+Film::best_dcp_video_frame_rate () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _audio_mapping = m;
- }
-
- signal_changed (AUDIO_MAPPING);
+ return _playlist->best_dcp_frame_rate ();
}
void
Film::playlist_content_changed (boost::weak_ptr<Content> c, int p)
{
if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
- set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ()));
- } else if (p == AudioContentProperty::AUDIO_CHANNELS) {
- set_audio_mapping (_playlist->default_audio_mapping ());
+ set_dcp_video_frame_rate (_playlist->best_dcp_frame_rate ());
}
if (ui_signaller) {
@@ -1209,3 +1104,34 @@ Film::set_loop (int c)
{
_playlist->set_loop (c);
}
+
+OutputAudioFrame
+Film::time_to_audio_frames (Time t) const
+{
+ return t * dcp_audio_frame_rate () / TIME_HZ;
+}
+
+OutputVideoFrame
+Film::time_to_video_frames (Time t) const
+{
+ return t * dcp_video_frame_rate () / TIME_HZ;
+}
+
+Time
+Film::audio_frames_to_time (OutputAudioFrame f) const
+{
+ return f * TIME_HZ / dcp_audio_frame_rate ();
+}
+
+Time
+Film::video_frames_to_time (OutputVideoFrame f) const
+{
+ return f * TIME_HZ / dcp_video_frame_rate ();
+}
+
+OutputAudioFrame
+Film::dcp_audio_frame_rate () const
+{
+ /* XXX */
+ return 48000;
+}
diff --git a/src/lib/film.h b/src/lib/film.h
index 18255a15e..cfc55c0ac 100644
--- a/src/lib/film.h
+++ b/src/lib/film.h
@@ -36,7 +36,7 @@
#include "dci_metadata.h"
#include "types.h"
#include "ffmpeg_content.h"
-#include "audio_mapping.h"
+#include "playlist.h"
class DCPContentType;
class Format;
@@ -48,7 +48,6 @@ class AnalyseAudioJob;
class ExternalAudioStream;
class Content;
class Player;
-class Playlist;
/** @class Film
* @brief A representation of some audio and video content, and details of
@@ -87,8 +86,6 @@ public:
std::string file (std::string f) const;
std::string dir (std::string d) const;
- int target_audio_sample_rate () const;
-
void write_metadata () const;
libdcp::Size cropped_size (libdcp::Size) const;
@@ -105,27 +102,24 @@ public:
boost::shared_ptr<Player> player () const;
boost::shared_ptr<Playlist> playlist () const;
- /* Proxies for some Playlist methods */
+ OutputAudioFrame dcp_audio_frame_rate () const;
- ContentList content () const;
+ OutputAudioFrame time_to_audio_frames (Time) const;
+ OutputVideoFrame time_to_video_frames (Time) const;
+ Time video_frames_to_time (OutputVideoFrame) const;
+ Time audio_frames_to_time (OutputAudioFrame) const;
- ContentAudioFrame audio_length () const;
- int audio_channels () const;
- int audio_frame_rate () const;
- bool has_audio () const;
+ /* Proxies for some Playlist methods */
- bool has_subtitles () const;
-
- float video_frame_rate () const;
- libdcp::Size video_size () const;
- ContentVideoFrame video_length () const;
+ Playlist::RegionList regions () const;
- ContentVideoFrame content_length () const;
+ Time length () const;
+ bool has_subtitles () const;
+ OutputVideoFrame best_dcp_video_frame_rate () const;
void set_loop (int);
int loop () const;
-
enum TrimType {
CPL,
ENCODE
@@ -159,7 +153,7 @@ public:
COLOUR_LUT,
J2K_BANDWIDTH,
DCI_METADATA,
- DCP_FRAME_RATE,
+ DCP_VIDEO_FRAME_RATE,
AUDIO_MAPPING
};
@@ -271,14 +265,10 @@ public:
return _dci_metadata;
}
- int dcp_frame_rate () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _dcp_frame_rate;
- }
-
- AudioMapping audio_mapping () const {
+ /* XXX: -> "video_frame_rate" */
+ int dcp_video_frame_rate () const {
boost::mutex::scoped_lock lm (_state_mutex);
- return _audio_mapping;
+ return _dcp_video_frame_rate;
}
/* SET */
@@ -289,8 +279,6 @@ public:
void set_trust_content_headers (bool);
void add_content (boost::shared_ptr<Content>);
void remove_content (boost::shared_ptr<Content>);
- void move_content_earlier (boost::shared_ptr<Content>);
- void move_content_later (boost::shared_ptr<Content>);
void set_dcp_content_type (DCPContentType const *);
void set_format (Format const *);
void set_crop (Crop);
@@ -312,9 +300,8 @@ public:
void set_colour_lut (int);
void set_j2k_bandwidth (int);
void set_dci_metadata (DCIMetadata);
- void set_dcp_frame_rate (int);
+ void set_dcp_video_frame_rate (int);
void set_dci_date_today ();
- void set_audio_mapping (AudioMapping);
/** Emitted when some property has of the Film has changed */
mutable boost::signals2::signal<void (Property)> Changed;
@@ -398,10 +385,9 @@ private:
/** DCI naming stuff */
DCIMetadata _dci_metadata;
/** Frames per second to run our DCP at */
- int _dcp_frame_rate;
+ int _dcp_video_frame_rate;
/** The date that we should use in a DCI name */
boost::gregorian::date _dci_date;
- AudioMapping _audio_mapping;
/** true if our state has changed since we last saved it */
mutable bool _dirty;
diff --git a/src/lib/format.cc b/src/lib/format.cc
index f5026c0da..688b22f16 100644
--- a/src/lib/format.cc
+++ b/src/lib/format.cc
@@ -208,7 +208,8 @@ VariableFormat::VariableFormat (libdcp::Size dcp, string id, string n, string d)
float
VariableFormat::ratio (shared_ptr<const Film> f) const
{
- libdcp::Size const c = f->cropped_size (f->video_size ());
+ /* XXX */
+ libdcp::Size const c;// = f->cropped_size (f->video_size ());
return float (c.width) / c.height;
}
diff --git a/src/lib/format.h b/src/lib/format.h
index d45a3a10a..29347a3fd 100644
--- a/src/lib/format.h
+++ b/src/lib/format.h
@@ -24,7 +24,7 @@
#include <string>
#include <vector>
-#include "util.h"
+#include <libdcp/util.h>
class Film;
diff --git a/src/lib/imagemagick_content.cc b/src/lib/imagemagick_content.cc
index 9e5f00ba0..2e42e25f3 100644
--- a/src/lib/imagemagick_content.cc
+++ b/src/lib/imagemagick_content.cc
@@ -98,3 +98,10 @@ ImageMagickContent::set_video_length (ContentVideoFrame len)
signal_changed (VideoContentProperty::VIDEO_LENGTH);
}
+
+Time
+ImageMagickContent::length (shared_ptr<const Film> film) const
+{
+ FrameRateConversion frc (24, film->dcp_video_frame_rate ());
+ return video_length() * frc.factor() * TIME_HZ / film->dcp_video_frame_rate ();
+}
diff --git a/src/lib/imagemagick_content.h b/src/lib/imagemagick_content.h
index b1e7f9495..366049002 100644
--- a/src/lib/imagemagick_content.h
+++ b/src/lib/imagemagick_content.h
@@ -38,6 +38,7 @@ public:
std::string summary () const;
void as_xml (xmlpp::Node *) const;
boost::shared_ptr<Content> clone () const;
+ Time length (boost::shared_ptr<const Film>) const;
void set_video_length (ContentVideoFrame);
diff --git a/src/lib/player.cc b/src/lib/player.cc
index 95036cfe0..9cc166204 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -27,9 +27,11 @@
#include "sndfile_content.h"
#include "playlist.h"
#include "job.h"
+#include "image.h"
using std::list;
using std::cout;
+using std::min;
using std::vector;
using boost::shared_ptr;
using boost::weak_ptr;
@@ -42,6 +44,11 @@ Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
, _audio (true)
, _subtitles (true)
, _have_valid_decoders (false)
+ , _position (0)
+ , _audio_buffers (MAX_AUDIO_CHANNELS, 0)
+ , _last_video (0)
+ , _last_was_black (false)
+ , _last_audio (0)
{
_playlist->Changed.connect (bind (&Player::playlist_changed, this));
_playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2));
@@ -72,118 +79,118 @@ Player::pass ()
setup_decoders ();
_have_valid_decoders = true;
}
-
- bool done = true;
-
- if (_video && _video_decoder < _video_decoders.size ()) {
-
- /* Run video decoder; this may also produce audio */
-
- if (_video_decoders[_video_decoder]->pass ()) {
- _video_decoder++;
- }
-
- if (_video_decoder < _video_decoders.size ()) {
- done = false;
- }
-
- }
-
- if (!_video && _audio && _playlist->audio_from() == Playlist::AUDIO_FFMPEG && _sequential_audio_decoder < _audio_decoders.size ()) {
-
- /* We're not producing video, so we may need to run FFmpeg content to get the audio */
-
- if (_audio_decoders[_sequential_audio_decoder]->pass ()) {
- _sequential_audio_decoder++;
- }
-
- if (_sequential_audio_decoder < _audio_decoders.size ()) {
- done = false;
- }
-
- }
-
- if (_audio && _playlist->audio_from() == Playlist::AUDIO_SNDFILE) {
-
- /* We're getting audio from SndfileContent */
-
- for (vector<shared_ptr<AudioDecoder> >::iterator i = _audio_decoders.begin(); i != _audio_decoders.end(); ++i) {
- if (!(*i)->pass ()) {
- done = false;
- }
- }
- Audio (_audio_buffers, _audio_time.get());
- _audio_buffers.reset ();
- _audio_time = boost::none;
- }
-
- return done;
+ cout << "-> Player::pass\n";
+
+ /* Here we are just finding the active decoder with the earliest last emission time, then
+ calling pass on it. If there is no decoder, we skip our position on until there is.
+ Hence this method will cause video and audio to be emitted, and it is up to the
+ process_{video,audio} methods to tidy it up.
+ */
+
+ Time earliest_pos = TIME_MAX;
+ shared_ptr<RegionDecoder> earliest;
+ Time next_wait = TIME_MAX;
+
+ for (list<shared_ptr<RegionDecoder> >::iterator i = _decoders.begin(); i != _decoders.end(); ++i) {
+ Time const ts = (*i)->region.time;
+ Time const te = (*i)->region.time + (*i)->region.content->length (_film);
+ if (ts <= _position && te > _position) {
+ Time const pos = ts + (*i)->last;
+ if (pos < earliest_pos) {
+ earliest_pos = pos;
+ earliest = *i;
+ }
+ }
+
+ if (ts > _position) {
+ next_wait = min (next_wait, ts - _position);
+ }
+ }
+
+ if (earliest) {
+ earliest->decoder->pass ();
+ _position = earliest->last;
+ } else if (next_wait < TIME_MAX) {
+ _position += next_wait;
+ } else {
+ cout << "<- Player::pass\n";
+ return true;
+ }
+
+ cout << "<- Player::pass\n";
+ return false;
}
void
-Player::process_video (shared_ptr<const Image> i, bool same, shared_ptr<Subtitle> s, double t)
+Player::process_video (shared_ptr<RegionDecoder> rd, shared_ptr<const Image> image, bool same, shared_ptr<Subtitle> sub, Time time)
{
- Video (i, same, s, _video_start[_video_decoder] + t);
+ shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> (rd->decoder);
+
+ Time const global_time = rd->region.time + time;
+ while ((global_time - _last_video) > 1) {
+ /* Fill in with black */
+ emit_black_frame ();
+ }
+
+ Video (image, same, sub, global_time);
+ rd->last = time;
+ _last_video = global_time;
+ _last_was_black = false;
}
void
-Player::process_audio (weak_ptr<const AudioContent> c, shared_ptr<const AudioBuffers> b, double t)
+Player::process_audio (shared_ptr<RegionDecoder> rd, shared_ptr<const AudioBuffers> audio, Time time)
{
- AudioMapping mapping = _film->audio_mapping ();
- if (!_audio_buffers) {
- _audio_buffers.reset (new AudioBuffers (mapping.dcp_channels(), b->frames ()));
- _audio_buffers->make_silent ();
- _audio_time = t;
- if (_playlist->audio_from() == Playlist::AUDIO_FFMPEG) {
- _audio_time = _audio_time.get() + _audio_start[_sequential_audio_decoder];
- }
- }
-
- for (int i = 0; i < b->channels(); ++i) {
- list<libdcp::Channel> dcp = mapping.content_to_dcp (AudioMapping::Channel (c, i));
- for (list<libdcp::Channel>::iterator j = dcp.begin(); j != dcp.end(); ++j) {
- _audio_buffers->accumulate (b, i, static_cast<int> (*j));
- }
- }
-
- if (_playlist->audio_from() == Playlist::AUDIO_FFMPEG) {
- /* We can just emit this audio now as it will all be here */
- Audio (_audio_buffers, t);
- _audio_buffers.reset ();
- _audio_time = boost::none;
- }
+ /* XXX: mapping */
+
+ /* The time of this audio may indicate that some of our buffered audio is not going to
+ be added to any more, so it can be emitted.
+ */
+
+ if (time > _last_audio) {
+ /* We can emit some audio from our buffers */
+ OutputAudioFrame const N = min (_film->time_to_audio_frames (time - _last_audio), static_cast<OutputAudioFrame> (_audio_buffers.frames()));
+ shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), N));
+ emit->copy_from (&_audio_buffers, N, 0, 0);
+ Audio (emit, _last_audio);
+ _last_audio += _film->audio_frames_to_time (N);
+
+ /* And remove it from our buffers */
+ if (_audio_buffers.frames() > N) {
+ _audio_buffers.move (N, 0, _audio_buffers.frames() - N);
+ }
+ _audio_buffers.set_frames (_audio_buffers.frames() - N);
+ }
+
+ /* Now accumulate the new audio into our buffers */
+
+ if (_audio_buffers.frames() == 0) {
+ /* We have no remaining data. Emit silence up to the start of this new data */
+ if ((time - _last_audio) > 0) {
+ emit_silence (time - _last_audio);
+ }
+ }
+
+ _audio_buffers.ensure_size (time - _last_audio + audio->frames());
+ _audio_buffers.accumulate (audio.get(), 0, _film->time_to_audio_frames (time - _last_audio));
+ rd->last = time + _film->audio_frames_to_time (audio->frames ());
}
/** @return true on error */
bool
-Player::seek (double t)
+Player::seek (Time t)
{
if (!_have_valid_decoders) {
setup_decoders ();
_have_valid_decoders = true;
}
- if (_video_decoders.empty ()) {
+ if (_decoders.empty ()) {
return true;
}
- /* Find the decoder that contains this position */
- _video_decoder = 0;
- while (1) {
- ++_video_decoder;
- if (_video_decoder >= _video_decoders.size () || t < _video_start[_video_decoder]) {
- --_video_decoder;
- t -= _video_start[_video_decoder];
- break;
- }
- }
-
- if (_video_decoder < _video_decoders.size()) {
- _video_decoders[_video_decoder]->seek (t);
- } else {
- return true;
- }
+ /* XXX */
/* XXX: don't seek audio because we don't need to... */
@@ -207,106 +214,60 @@ Player::seek_forward ()
void
Player::setup_decoders ()
{
- vector<shared_ptr<VideoDecoder> > old_video_decoders = _video_decoders;
+ list<shared_ptr<RegionDecoder> > old_decoders = _decoders;
- _video_decoders.clear ();
- _video_decoder = 0;
- _audio_decoders.clear ();
- _sequential_audio_decoder = 0;
+ _decoders.clear ();
- _video_start.clear();
- _audio_start.clear();
+ Playlist::RegionList regions = _playlist->regions ();
+ for (Playlist::RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
- double video_so_far = 0;
- double audio_so_far = 0;
+ shared_ptr<RegionDecoder> rd (new RegionDecoder);
+ rd->region = *i;
+
+ /* XXX: into content? */
- for (int l = 0; l < _playlist->loop(); ++l) {
- list<shared_ptr<const VideoContent> > vc = _playlist->video ();
- for (list<shared_ptr<const VideoContent> >::iterator i = vc.begin(); i != vc.end(); ++i) {
-
- shared_ptr<const VideoContent> video_content;
- shared_ptr<const AudioContent> audio_content;
- shared_ptr<VideoDecoder> video_decoder;
- shared_ptr<AudioDecoder> audio_decoder;
-
- /* XXX: into content? */
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (i->content);
+ if (fc) {
+ shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio, _subtitles));
- shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
- if (fc) {
- shared_ptr<FFmpegDecoder> fd (
- new FFmpegDecoder (
- _film, fc, _video,
- _audio && _playlist->audio_from() == Playlist::AUDIO_FFMPEG,
- _subtitles
- )
- );
-
- video_content = fc;
- audio_content = fc;
- video_decoder = fd;
- audio_decoder = fd;
-
- video_decoder->connect_video (shared_from_this ());
- }
-
- shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i);
- if (ic) {
- video_content = ic;
-
- /* See if we can re-use an old ImageMagickDecoder */
- for (vector<shared_ptr<VideoDecoder> >::const_iterator i = old_video_decoders.begin(); i != old_video_decoders.end(); ++i) {
- shared_ptr<ImageMagickDecoder> imd = dynamic_pointer_cast<ImageMagickDecoder> (*i);
- if (imd && imd->content() == ic) {
- video_decoder = *i;
- }
- }
+ fd->Video.connect (bind (&Player::process_video, this, rd, _1, _2, _3, _4));
+ fd->Audio.connect (bind (&Player::process_audio, this, rd, _1, _2));
- if (!video_decoder) {
- video_decoder.reset (new ImageMagickDecoder (_film, ic));
- video_decoder->connect_video (shared_from_this ());
- }
- }
-
- _video_decoders.push_back (video_decoder);
- _video_start.push_back (video_so_far);
- video_so_far += video_content->video_length() / video_content->video_frame_rate();
-
- if (audio_decoder && _playlist->audio_from() == Playlist::AUDIO_FFMPEG) {
- audio_decoder->Audio.connect (bind (&Player::process_audio, this, audio_content, _1, _2));
- _audio_decoders.push_back (audio_decoder);
- _audio_start.push_back (audio_so_far);
- audio_so_far += double(audio_content->audio_length()) / audio_content->audio_frame_rate();
- }
+ rd->decoder = fd;
}
- _video_decoder = 0;
- _sequential_audio_decoder = 0;
-
- if (_playlist->audio_from() == Playlist::AUDIO_SNDFILE) {
+ shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (i->content);
+ if (ic) {
+ shared_ptr<ImageMagickDecoder> id;
- list<shared_ptr<const AudioContent> > ac = _playlist->audio ();
- for (list<shared_ptr<const AudioContent> >::iterator i = ac.begin(); i != ac.end(); ++i) {
-
- shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
- assert (sc);
-
- shared_ptr<AudioDecoder> d (new SndfileDecoder (_film, sc));
- d->Audio.connect (bind (&Player::process_audio, this, sc, _1, _2));
- _audio_decoders.push_back (d);
- _audio_start.push_back (audio_so_far);
+ /* See if we can re-use an old ImageMagickDecoder */
+ for (list<shared_ptr<RegionDecoder> >::const_iterator i = old_decoders.begin(); i != old_decoders.end(); ++i) {
+ shared_ptr<ImageMagickDecoder> imd = dynamic_pointer_cast<ImageMagickDecoder> ((*i)->decoder);
+ if (imd && imd->content() == ic) {
+ id = imd;
+ }
+ }
+
+ if (!id) {
+ id.reset (new ImageMagickDecoder (_film, ic));
+ id->Video.connect (bind (&Player::process_video, this, rd, _1, _2, _3, _4));
}
+
+ rd->decoder = id;
}
- }
-}
-double
-Player::last_video_time () const
-{
- if (_video_decoder >= _video_decoders.size ()) {
- return 0;
+ shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (i->content);
+ if (sc) {
+ shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
+ sd->Audio.connect (bind (&Player::process_audio, this, rd, _1, _2));
+
+ rd->decoder = sd;
+ }
+
+ _decoders.push_back (rd);
}
-
- return _video_start[_video_decoder] + _video_decoders[_video_decoder]->last_content_time ();
+
+ _position = 0;
}
void
@@ -327,3 +288,26 @@ Player::playlist_changed ()
{
_have_valid_decoders = false;
}
+
+void
+Player::emit_black_frame ()
+{
+ shared_ptr<SimpleImage> image (new SimpleImage (AV_PIX_FMT_RGB24, libdcp::Size (128, 128), true));
+ Video (image, _last_was_black, shared_ptr<Subtitle> (), _last_video);
+ _last_video += _film->video_frames_to_time (1);
+}
+
+void
+Player::emit_silence (Time t)
+{
+ OutputAudioFrame frames = _film->time_to_audio_frames (t);
+ while (frames) {
+ /* Do this in half-second chunks so we don't overwhelm anybody */
+ OutputAudioFrame this_time = min (_film->dcp_audio_frame_rate() / 2, frames);
+ shared_ptr<AudioBuffers> silence (new AudioBuffers (MAX_AUDIO_CHANNELS, this_time));
+ silence->make_silent ();
+ Audio (silence, _last_audio);
+ _last_audio += _film->audio_frames_to_time (this_time);
+ frames -= this_time;
+ }
+}
diff --git a/src/lib/player.h b/src/lib/player.h
index b1be2f456..c9bf2a00b 100644
--- a/src/lib/player.h
+++ b/src/lib/player.h
@@ -27,19 +27,20 @@
#include "audio_source.h"
#include "video_sink.h"
#include "audio_sink.h"
+#include "playlist.h"
+#include "audio_buffers.h"
-class VideoDecoder;
-class AudioDecoder;
class Job;
class Film;
class Playlist;
class AudioContent;
+class Decoder;
/** @class Player
* @brief A class which can `play' a Playlist; emitting its audio and video.
*/
-class Player : public VideoSource, public AudioSource, public VideoSink, public boost::enable_shared_from_this<Player>
+class Player : public VideoSource, public AudioSource, public boost::enable_shared_from_this<Player>
{
public:
Player (boost::shared_ptr<const Film>, boost::shared_ptr<const Playlist>);
@@ -49,18 +50,34 @@ public:
void disable_subtitles ();
bool pass ();
- bool seek (double);
+ bool seek (Time);
void seek_back ();
void seek_forward ();
- double last_video_time () const;
+ Time last_video () const {
+ return _last_video;
+ }
private:
- void process_video (boost::shared_ptr<const Image> i, bool same, boost::shared_ptr<Subtitle> s, double);
- void process_audio (boost::weak_ptr<const AudioContent>, boost::shared_ptr<const AudioBuffers>, double);
+
+ struct RegionDecoder
+ {
+ RegionDecoder ()
+ : last (0)
+ {}
+
+ Playlist::Region region;
+ boost::shared_ptr<Decoder> decoder;
+ Time last;
+ };
+
+ void process_video (boost::shared_ptr<RegionDecoder>, boost::shared_ptr<const Image>, bool, boost::shared_ptr<Subtitle>, Time);
+ void process_audio (boost::shared_ptr<RegionDecoder>, boost::shared_ptr<const AudioBuffers>, Time);
void setup_decoders ();
void playlist_changed ();
void content_changed (boost::weak_ptr<Content>, int);
+ void emit_black_frame ();
+ void emit_silence (Time);
boost::shared_ptr<const Film> _film;
boost::shared_ptr<const Playlist> _playlist;
@@ -71,21 +88,13 @@ private:
/** Our decoders are ready to go; if this is false the decoders must be (re-)created before they are used */
bool _have_valid_decoders;
- /** Video decoders in order of presentation */
- std::vector<boost::shared_ptr<VideoDecoder> > _video_decoders;
- /** Start positions of each video decoder in seconds*/
- std::vector<double> _video_start;
- /** Index of current video decoder */
- size_t _video_decoder;
- /** Audio decoders in order of presentation (if they are from FFmpeg) */
- std::vector<boost::shared_ptr<AudioDecoder> > _audio_decoders;
- /** Start positions of each audio decoder (if they are from FFmpeg) in seconds */
- std::vector<double> _audio_start;
- /** Current audio decoder index if we are running them sequentially; otherwise undefined */
- size_t _sequential_audio_decoder;
-
- boost::shared_ptr<AudioBuffers> _audio_buffers;
- boost::optional<double> _audio_time;
+ std::list<boost::shared_ptr<RegionDecoder> > _decoders;
+
+ Time _position;
+ AudioBuffers _audio_buffers;
+ Time _last_video;
+ bool _last_was_black;
+ Time _last_audio;
};
#endif
diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc
index f1dd881b3..8f4a35ac2 100644
--- a/src/lib/playlist.cc
+++ b/src/lib/playlist.cc
@@ -29,6 +29,8 @@
#include "imagemagick_decoder.h"
#include "imagemagick_content.h"
#include "job.h"
+#include "config.h"
+#include "util.h"
#include "i18n.h"
@@ -39,171 +41,24 @@ using std::min;
using std::max;
using std::string;
using std::stringstream;
+using boost::optional;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::dynamic_pointer_cast;
using boost::lexical_cast;
Playlist::Playlist ()
- : _audio_from (AUDIO_FFMPEG)
- , _loop (1)
+ : _loop (1)
{
}
Playlist::Playlist (shared_ptr<const Playlist> other)
- : _audio_from (other->_audio_from)
- , _loop (other->_loop)
+ : _loop (other->_loop)
{
- for (ContentList::const_iterator i = other->_content.begin(); i != other->_content.end(); ++i) {
- _content.push_back ((*i)->clone ());
+ for (RegionList::const_iterator i = other->_regions.begin(); i != other->_regions.end(); ++i) {
+ _regions.push_back (Region (i->content->clone(), i->time, this));
}
-
- setup ();
-}
-
-void
-Playlist::setup ()
-{
- _audio_from = AUDIO_FFMPEG;
-
- _video.clear ();
- _audio.clear ();
-
- for (list<boost::signals2::connection>::iterator i = _content_connections.begin(); i != _content_connections.end(); ++i) {
- i->disconnect ();
- }
-
- _content_connections.clear ();
-
- for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
-
- /* Video is video */
- shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i);
- if (vc) {
- _video.push_back (vc);
- }
-
- /* FFmpegContent is audio if we are doing AUDIO_FFMPEG */
- shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (*i);
- if (fc && _audio_from == AUDIO_FFMPEG) {
- _audio.push_back (fc);
- }
-
- /* SndfileContent trumps FFmpegContent for audio */
- shared_ptr<SndfileContent> sc = dynamic_pointer_cast<SndfileContent> (*i);
- if (sc) {
- if (_audio_from == AUDIO_FFMPEG) {
- /* This is our fist SndfileContent; clear any FFmpegContent and
- say that we are using Sndfile.
- */
- _audio.clear ();
- _audio_from = AUDIO_SNDFILE;
- }
-
- _audio.push_back (sc);
- }
-
- _content_connections.push_back ((*i)->Changed.connect (bind (&Playlist::content_changed, this, _1, _2)));
- }
-}
-
-/** @return Length of our audio */
-ContentAudioFrame
-Playlist::audio_length () const
-{
- ContentAudioFrame len = 0;
-
- switch (_audio_from) {
- case AUDIO_FFMPEG:
- /* FFmpeg content is sequential */
- for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
- len += (*i)->audio_length ();
- }
- break;
- case AUDIO_SNDFILE:
- /* Sndfile content is simultaneous */
- for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
- len = max (len, (*i)->audio_length ());
- }
- break;
- }
-
- return len * _loop;
-}
-
-/** @return number of audio channels */
-int
-Playlist::audio_channels () const
-{
- int channels = 0;
-
- switch (_audio_from) {
- case AUDIO_FFMPEG:
- /* FFmpeg audio is sequential, so use the maximum channel count */
- for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
- channels = max (channels, (*i)->audio_channels ());
- }
- break;
- case AUDIO_SNDFILE:
- /* Sndfile audio is simultaneous, so it's the sum of the channel counts */
- for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
- channels += (*i)->audio_channels ();
- }
- break;
- }
-
- return channels;
-}
-
-int
-Playlist::audio_frame_rate () const
-{
- if (_audio.empty ()) {
- return 0;
- }
-
- /* XXX: assuming that all content has the same rate */
- return _audio.front()->audio_frame_rate ();
-}
-
-float
-Playlist::video_frame_rate () const
-{
- if (_video.empty ()) {
- return 0;
- }
-
- /* XXX: assuming all the same */
- return _video.front()->video_frame_rate ();
-}
-
-libdcp::Size
-Playlist::video_size () const
-{
- if (_video.empty ()) {
- return libdcp::Size ();
- }
-
- /* XXX: assuming all the same */
- return _video.front()->video_size ();
-}
-
-ContentVideoFrame
-Playlist::video_length () const
-{
- ContentVideoFrame len = 0;
- for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
- len += (*i)->video_length ();
- }
-
- return len * _loop;
-}
-
-bool
-Playlist::has_audio () const
-{
- return !_audio.empty ();
}
void
@@ -212,62 +67,19 @@ Playlist::content_changed (weak_ptr<Content> c, int p)
ContentChanged (c, p);
}
-AudioMapping
-Playlist::default_audio_mapping () const
-{
- AudioMapping m;
- if (_audio.empty ()) {
- return m;
- }
-
- switch (_audio_from) {
- case AUDIO_FFMPEG:
- {
- /* XXX: assumes all the same */
- if (_audio.front()->audio_channels() == 1) {
- /* Map mono sources to centre */
- m.add (AudioMapping::Channel (_audio.front(), 0), libdcp::CENTRE);
- } else {
- int const N = min (_audio.front()->audio_channels (), MAX_AUDIO_CHANNELS);
- /* Otherwise just start with a 1:1 mapping */
- for (int i = 0; i < N; ++i) {
- m.add (AudioMapping::Channel (_audio.front(), i), (libdcp::Channel) i);
- }
- }
- break;
- }
-
- case AUDIO_SNDFILE:
- {
- int n = 0;
- for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
- for (int j = 0; j < (*i)->audio_channels(); ++j) {
- m.add (AudioMapping::Channel (*i, j), (libdcp::Channel) n);
- ++n;
- if (n >= MAX_AUDIO_CHANNELS) {
- break;
- }
- }
- if (n >= MAX_AUDIO_CHANNELS) {
- break;
- }
- }
- break;
- }
- }
-
- return m;
-}
-
string
Playlist::audio_digest () const
{
string t;
- for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
- t += (*i)->digest ();
+ for (RegionList::const_iterator i = _regions.begin(); i != _regions.end(); ++i) {
+ if (!dynamic_pointer_cast<const AudioContent> (i->content)) {
+ continue;
+ }
+
+ t += i->content->digest ();
- shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (i->content);
if (fc) {
t += lexical_cast<string> (fc->audio_stream()->id);
}
@@ -283,9 +95,13 @@ Playlist::video_digest () const
{
string t;
- for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
- t += (*i)->digest ();
- shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
+ for (RegionList::const_iterator i = _regions.begin(); i != _regions.end(); ++i) {
+ if (!dynamic_pointer_cast<const VideoContent> (i->content)) {
+ continue;
+ }
+
+ t += i->content->digest ();
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (i->content);
if (fc && fc->subtitle_stream()) {
t += fc->subtitle_stream()->id;
}
@@ -296,48 +112,22 @@ Playlist::video_digest () const
return md5_digest (t.c_str(), t.length());
}
-ContentVideoFrame
-Playlist::content_length () const
-{
- float const vfr = video_frame_rate() > 0 ? video_frame_rate() : 24;
- int const afr = audio_frame_rate() > 0 ? audio_frame_rate() : 48000;
-
- return max (
- video_length(),
- ContentVideoFrame (audio_length() * vfr / afr)
- );
-}
-
void
Playlist::set_from_xml (shared_ptr<const cxml::Node> node)
{
- list<shared_ptr<cxml::Node> > c = node->node_children ("Content");
+ list<shared_ptr<cxml::Node> > c = node->node_children ("Region");
for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) {
-
- string const type = (*i)->string_child ("Type");
- boost::shared_ptr<Content> c;
-
- if (type == "FFmpeg") {
- c.reset (new FFmpegContent (*i));
- } else if (type == "ImageMagick") {
- c.reset (new ImageMagickContent (*i));
- } else if (type == "Sndfile") {
- c.reset (new SndfileContent (*i));
- }
-
- _content.push_back (c);
+ _regions.push_back (Region (*i, this));
}
_loop = node->number_child<int> ("Loop");
-
- setup ();
}
void
Playlist::as_xml (xmlpp::Node* node)
{
- for (ContentList::iterator i = _content.begin(); i != _content.end(); ++i) {
- (*i)->as_xml (node->add_child ("Content"));
+ for (RegionList::iterator i = _regions.begin(); i != _regions.end(); ++i) {
+ i->as_xml (node->add_child ("Region"));
}
node->add_child("Loop")->add_child_text(lexical_cast<string> (_loop));
@@ -346,87 +136,146 @@ Playlist::as_xml (xmlpp::Node* node)
void
Playlist::add (shared_ptr<Content> c)
{
- _content.push_back (c);
- setup ();
+ _regions.push_back (Region (c, 0, this));
Changed ();
}
void
Playlist::remove (shared_ptr<Content> c)
{
- ContentList::iterator i = find (_content.begin(), _content.end(), c);
- if (i != _content.end ()) {
- _content.erase (i);
+ RegionList::iterator i = _regions.begin ();
+ while (i != _regions.end() && i->content != c) {
+ ++i;
}
+
+ if (i != _regions.end ()) {
+ _regions.erase (i);
+ Changed ();
+ }
+}
- setup ();
+void
+Playlist::set_loop (int l)
+{
+ _loop = l;
Changed ();
}
-void
-Playlist::move_earlier (shared_ptr<Content> c)
+bool
+Playlist::has_subtitles () const
{
- ContentList::iterator i = find (_content.begin(), _content.end(), c);
- if (i == _content.begin () || i == _content.end()) {
- return;
+ for (RegionList::const_iterator i = _regions.begin(); i != _regions.end(); ++i) {
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (i->content);
+ if (fc && !fc->subtitle_streams().empty()) {
+ return true;
+ }
}
- ContentList::iterator j = i;
- --j;
-
- swap (*i, *j);
-
- setup ();
- Changed ();
+ return false;
}
-void
-Playlist::move_later (shared_ptr<Content> c)
+Playlist::Region::Region (shared_ptr<Content> c, Time t, Playlist* p)
+ : content (c)
+ , time (t)
{
- ContentList::iterator i = find (_content.begin(), _content.end(), c);
- if (i == _content.end()) {
- return;
- }
+ connection = c->Changed.connect (bind (&Playlist::content_changed, p, _1, _2));
+}
- ContentList::iterator j = i;
- ++j;
- if (j == _content.end ()) {
- return;
+Playlist::Region::Region (shared_ptr<const cxml::Node> node, Playlist* p)
+{
+ shared_ptr<const cxml::Node> content_node = node->node_child ("Content");
+ string const type = content_node->string_child ("Type");
+
+ if (type == "FFmpeg") {
+ content.reset (new FFmpegContent (content_node));
+ } else if (type == "ImageMagick") {
+ content.reset (new ImageMagickContent (content_node));
+ } else if (type == "Sndfile") {
+ content.reset (new SndfileContent (content_node));
}
- swap (*i, *j);
-
- setup ();
- Changed ();
+ time = node->number_child<Time> ("Time");
+ connection = content->Changed.connect (bind (&Playlist::content_changed, p, _1, _2));
}
void
-Playlist::set_loop (int l)
+Playlist::Region::as_xml (xmlpp::Node* node) const
{
- _loop = l;
- Changed ();
+ xmlpp::Node* sub = node->add_child ("Content");
+ content->as_xml (sub);
+ sub->add_child ("Time")->add_child_text (lexical_cast<string> (time));
}
-
-shared_ptr<FFmpegContent>
-Playlist::ffmpeg () const
+
+class FrameRateCandidate
{
- for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
- shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (*i);
- if (fc) {
- return fc;
+public:
+ FrameRateCandidate (float source_, int dcp_)
+ : source (source_)
+ , dcp (dcp_)
+ {}
+
+ float source;
+ int dcp;
+};
+
+int
+Playlist::best_dcp_frame_rate () const
+{
+ list<int> const allowed_dcp_frame_rates = Config::instance()->allowed_dcp_frame_rates ();
+
+ /* Work out what rates we could manage, including those achieved by using skip / repeat. */
+ list<FrameRateCandidate> candidates;
+
+ /* Start with the ones without skip / repeat so they will get matched in preference to skipped/repeated ones */
+ for (list<int>::const_iterator i = allowed_dcp_frame_rates.begin(); i != allowed_dcp_frame_rates.end(); ++i) {
+ candidates.push_back (FrameRateCandidate (*i, *i));
+ }
+
+ /* Then the skip/repeat ones */
+ for (list<int>::const_iterator i = allowed_dcp_frame_rates.begin(); i != allowed_dcp_frame_rates.end(); ++i) {
+ candidates.push_back (FrameRateCandidate (float (*i) / 2, *i));
+ candidates.push_back (FrameRateCandidate (float (*i) * 2, *i));
+ }
+
+ /* Pick the best one, bailing early if we hit an exact match */
+ float error = std::numeric_limits<float>::max ();
+ optional<FrameRateCandidate> best;
+ list<FrameRateCandidate>::iterator i = candidates.begin();
+ while (i != candidates.end()) {
+
+ float this_error = std::numeric_limits<float>::max ();
+ for (RegionList::const_iterator j = _regions.begin(); j != _regions.end(); ++j) {
+ shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (j->content);
+ if (!vc) {
+ continue;
+ }
+
+ this_error += fabs (i->source - vc->video_frame_rate ());
}
+
+ if (this_error < error) {
+ error = this_error;
+ best = *i;
+ }
+
+ ++i;
}
- return shared_ptr<FFmpegContent> ();
+ if (!best) {
+ return 24;
+ }
+
+ return best->dcp;
}
-bool
-Playlist::has_subtitles () const
+Time
+Playlist::length (shared_ptr<const Film> film) const
{
- shared_ptr<FFmpegContent> fc = ffmpeg ();
- if (!fc) {
- return false;
+ Time len = 0;
+ for (RegionList::const_iterator i = _regions.begin(); i != _regions.end(); ++i) {
+ Time const t = i->time + i->content->length (film);
+ len = max (len, t);
}
-
- return !fc->subtitle_streams().empty();
+
+ return len;
}
diff --git a/src/lib/playlist.h b/src/lib/playlist.h
index e6acff694..5b9299795 100644
--- a/src/lib/playlist.h
+++ b/src/lib/playlist.h
@@ -17,6 +17,9 @@
*/
+#ifndef DCPOMATIC_PLAYLIST_H
+#define DCPOMATIC_PLAYLIST_H
+
#include <list>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
@@ -46,7 +49,7 @@ class Film;
* from the video unless any sound-only files are present. If sound-only files exist, they
* are played simultaneously (i.e. they can be split up into multiple files for different channels)
*/
-
+
class Playlist
{
public:
@@ -58,44 +61,29 @@ public:
void add (boost::shared_ptr<Content>);
void remove (boost::shared_ptr<Content>);
- void move_earlier (boost::shared_ptr<Content>);
- void move_later (boost::shared_ptr<Content>);
- ContentAudioFrame audio_length () const;
- int audio_channels () const;
- int audio_frame_rate () const;
- bool has_audio () const;
-
- float video_frame_rate () const;
- libdcp::Size video_size () const;
- ContentVideoFrame video_length () const;
-
- AudioMapping default_audio_mapping () const;
- ContentVideoFrame content_length () const;
+ bool has_subtitles () const;
- enum AudioFrom {
- AUDIO_FFMPEG,
- AUDIO_SNDFILE
+ struct Region
+ {
+ Region ()
+ : time (0)
+ {}
+
+ Region (boost::shared_ptr<Content> c, Time t, Playlist* p);
+ Region (boost::shared_ptr<const cxml::Node>, Playlist* p);
+
+ void as_xml (xmlpp::Node *) const;
+
+ boost::shared_ptr<Content> content;
+ Time time;
+ boost::signals2::connection connection;
};
- AudioFrom audio_from () const {
- return _audio_from;
- }
-
- bool has_subtitles () const;
+ typedef std::vector<Region> RegionList;
- ContentList content () const {
- return _content;
- }
-
- boost::shared_ptr<FFmpegContent> ffmpeg () const;
-
- std::list<boost::shared_ptr<const VideoContent> > video () const {
- return _video;
- }
-
- std::list<boost::shared_ptr<const AudioContent> > audio () const {
- return _audio;
+ RegionList regions () const {
+ return _regions;
}
std::string audio_digest () const;
@@ -107,26 +95,17 @@ public:
void set_loop (int l);
+ Time length (boost::shared_ptr<const Film>) const;
+ int best_dcp_frame_rate () const;
+
mutable boost::signals2::signal<void ()> Changed;
mutable boost::signals2::signal<void (boost::weak_ptr<Content>, int)> ContentChanged;
private:
- void setup ();
void content_changed (boost::weak_ptr<Content>, int);
- /** where we should get our audio from */
- AudioFrom _audio_from;
-
- /** all our content */
- ContentList _content;
- /** all our content which contains video */
- std::list<boost::shared_ptr<const VideoContent> > _video;
- /** all our content which contains audio. This may contain the same objects
- * as _video for FFmpegContent.
- */
- std::list<boost::shared_ptr<const AudioContent> > _audio;
-
+ RegionList _regions;
int _loop;
-
- std::list<boost::signals2::connection> _content_connections;
};
+
+#endif
diff --git a/src/lib/sndfile_content.cc b/src/lib/sndfile_content.cc
index 539b0dfb5..210ca1577 100644
--- a/src/lib/sndfile_content.cc
+++ b/src/lib/sndfile_content.cc
@@ -68,7 +68,7 @@ SndfileContent::information () const
s << String::compose (
_("%1 channels, %2kHz, %3 samples"),
audio_channels(),
- audio_frame_rate() / 1000.0,
+ content_audio_frame_rate() / 1000.0,
audio_length()
);
@@ -120,3 +120,15 @@ SndfileContent::as_xml (xmlpp::Node* node) const
node->add_child("AudioFrameRate")->add_child_text (lexical_cast<string> (_audio_frame_rate));
}
+int
+SndfileContent::output_audio_frame_rate (shared_ptr<const Film>) const
+{
+ /* Resample to a DCI-approved sample rate */
+ return dcp_audio_frame_rate (content_audio_frame_rate ());
+}
+
+Time
+SndfileContent::length (shared_ptr<const Film> film) const
+{
+ return film->audio_frames_to_time (audio_length ());
+}
diff --git a/src/lib/sndfile_content.h b/src/lib/sndfile_content.h
index e8e86b603..0623aa6f0 100644
--- a/src/lib/sndfile_content.h
+++ b/src/lib/sndfile_content.h
@@ -41,6 +41,7 @@ public:
std::string information () const;
void as_xml (xmlpp::Node *) const;
boost::shared_ptr<Content> clone () const;
+ Time length (boost::shared_ptr<const Film>) const;
/* AudioContent */
int audio_channels () const {
@@ -53,10 +54,12 @@ public:
return _audio_length;
}
- int audio_frame_rate () const {
+ int content_audio_frame_rate () const {
boost::mutex::scoped_lock lm (_mutex);
return _audio_frame_rate;
}
+
+ int output_audio_frame_rate (boost::shared_ptr<const Film>) const;
static bool valid_file (boost::filesystem::path);
diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc
index dc22475cd..f114979de 100644
--- a/src/lib/sndfile_decoder.cc
+++ b/src/lib/sndfile_decoder.cc
@@ -23,6 +23,7 @@
#include "sndfile_decoder.h"
#include "film.h"
#include "exceptions.h"
+#include "audio_buffers.h"
#include "i18n.h"
@@ -59,7 +60,7 @@ SndfileDecoder::pass ()
/* Do things in half second blocks as I think there may be limits
to what FFmpeg (and in particular the resampler) can cope with.
*/
- sf_count_t const block = _sndfile_content->audio_frame_rate() / 2;
+ sf_count_t const block = _sndfile_content->content_audio_frame_rate() / 2;
sf_count_t const this_time = min (block, _remaining);
int const channels = _sndfile_content->audio_channels ();
diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h
index 38ba4e70e..2b77eb4cb 100644
--- a/src/lib/subtitle.h
+++ b/src/lib/subtitle.h
@@ -23,7 +23,7 @@
#include <list>
#include <boost/shared_ptr.hpp>
-#include "util.h"
+#include "types.h"
struct AVSubtitle;
class Image;
diff --git a/src/lib/transcode_job.cc b/src/lib/transcode_job.cc
index 0c3b8c37b..a0a9454b7 100644
--- a/src/lib/transcode_job.cc
+++ b/src/lib/transcode_job.cc
@@ -113,18 +113,7 @@ TranscodeJob::remaining_time () const
return 0;
}
- if (!_film->video_length()) {
- return 0;
- }
-
/* Compute approximate proposed length here, as it's only here that we need it */
- int length = _film->video_length();
- FrameRateConversion const frc (_film->video_frame_rate(), _film->dcp_frame_rate());
- if (frc.skip) {
- length /= 2;
- }
- /* If we are repeating it shouldn't affect transcode time, so don't take it into account */
-
- int const left = length - _transcoder->video_frames_out();
+ OutputVideoFrame const left = _film->time_to_video_frames (_film->length ()) - _transcoder->video_frames_out();
return left / fps;
}
diff --git a/src/lib/types.h b/src/lib/types.h
index 5e4826918..4b8b8072d 100644
--- a/src/lib/types.h
+++ b/src/lib/types.h
@@ -21,15 +21,19 @@
#define DCPOMATIC_TYPES_H
#include <vector>
+#include <stdint.h>
#include <boost/shared_ptr.hpp>
#include <libdcp/util.h>
class Content;
-typedef std::vector<boost::shared_ptr<Content> > ContentList;
typedef int64_t ContentAudioFrame;
-typedef int ContentVideoFrame;
-typedef double Time;
+typedef int ContentVideoFrame;
+typedef int64_t Time;
+#define TIME_MAX INT64_MAX
+#define TIME_HZ 96000
+typedef int64_t OutputAudioFrame;
+typedef int OutputVideoFrame;
/** @struct Crop
* @brief A description of the crop of an image or video.
diff --git a/src/lib/util.cc b/src/lib/util.cc
index 5e957f923..9063b46d4 100644
--- a/src/lib/util.cc
+++ b/src/lib/util.cc
@@ -113,6 +113,12 @@ seconds_to_hms (int s)
return hms.str ();
}
+string
+time_to_hms (Time t)
+{
+ return seconds_to_hms (t / TIME_HZ);
+}
+
/** @param s Number of seconds.
* @return String containing an approximate description of s (e.g. "about 2 hours")
*/
@@ -428,66 +434,11 @@ about_equal (float a, float b)
return (fabs (a - b) < 1e-4);
}
-class FrameRateCandidate
-{
-public:
- FrameRateCandidate (float source_, int dcp_)
- : source (source_)
- , dcp (dcp_)
- {}
-
- float source;
- int dcp;
-};
-
-int
-best_dcp_frame_rate (float source_fps)
-{
- list<int> const allowed_dcp_frame_rates = Config::instance()->allowed_dcp_frame_rates ();
-
- /* Work out what rates we could manage, including those achieved by using skip / repeat. */
- list<FrameRateCandidate> candidates;
-
- /* Start with the ones without skip / repeat so they will get matched in preference to skipped/repeated ones */
- for (list<int>::const_iterator i = allowed_dcp_frame_rates.begin(); i != allowed_dcp_frame_rates.end(); ++i) {
- candidates.push_back (FrameRateCandidate (*i, *i));
- }
-
- /* Then the skip/repeat ones */
- for (list<int>::const_iterator i = allowed_dcp_frame_rates.begin(); i != allowed_dcp_frame_rates.end(); ++i) {
- candidates.push_back (FrameRateCandidate (float (*i) / 2, *i));
- candidates.push_back (FrameRateCandidate (float (*i) * 2, *i));
- }
-
- /* Pick the best one, bailing early if we hit an exact match */
- float error = std::numeric_limits<float>::max ();
- optional<FrameRateCandidate> best;
- list<FrameRateCandidate>::iterator i = candidates.begin();
- while (i != candidates.end()) {
-
- if (about_equal (i->source, source_fps)) {
- best = *i;
- break;
- }
-
- float const e = fabs (i->source - source_fps);
- if (e < error) {
- error = e;
- best = *i;
- }
-
- ++i;
- }
-
- assert (best);
- return best->dcp;
-}
-
-/** @param An arbitrary sampling rate.
- * @return The appropriate DCP-approved sampling rate (48kHz or 96kHz).
+/** @param An arbitrary audio frame rate.
+ * @return The appropriate DCP-approved frame rate (48kHz or 96kHz).
*/
int
-dcp_audio_sample_rate (int fs)
+dcp_audio_frame_rate (int fs)
{
if (fs <= 48000) {
return 48000;
@@ -724,166 +675,6 @@ get_optional_int (multimap<string, string> const & kv, string k)
return lexical_cast<int> (i->second);
}
-/** Construct an AudioBuffers. Audio data is undefined after this constructor.
- * @param channels Number of channels.
- * @param frames Number of frames to reserve space for.
- */
-AudioBuffers::AudioBuffers (int channels, int frames)
- : _channels (channels)
- , _frames (frames)
- , _allocated_frames (frames)
-{
- _data = new float*[_channels];
- for (int i = 0; i < _channels; ++i) {
- _data[i] = new float[frames];
- }
-}
-
-/** Copy constructor.
- * @param other Other AudioBuffers; data is copied.
- */
-AudioBuffers::AudioBuffers (AudioBuffers const & other)
- : _channels (other._channels)
- , _frames (other._frames)
- , _allocated_frames (other._frames)
-{
- _data = new float*[_channels];
- for (int i = 0; i < _channels; ++i) {
- _data[i] = new float[_frames];
- memcpy (_data[i], other._data[i], _frames * sizeof (float));
- }
-}
-
-/* XXX: it's a shame that this is a copy-and-paste of the above;
- probably fixable with c++0x.
-*/
-AudioBuffers::AudioBuffers (boost::shared_ptr<const AudioBuffers> other)
- : _channels (other->_channels)
- , _frames (other->_frames)
- , _allocated_frames (other->_frames)
-{
- _data = new float*[_channels];
- for (int i = 0; i < _channels; ++i) {
- _data[i] = new float[_frames];
- memcpy (_data[i], other->_data[i], _frames * sizeof (float));
- }
-}
-
-/** AudioBuffers destructor */
-AudioBuffers::~AudioBuffers ()
-{
- for (int i = 0; i < _channels; ++i) {
- delete[] _data[i];
- }
-
- delete[] _data;
-}
-
-/** @param c Channel index.
- * @return Buffer for this channel.
- */
-float*
-AudioBuffers::data (int c) const
-{
- assert (c >= 0 && c < _channels);
- return _data[c];
-}
-
-/** Set the number of frames that these AudioBuffers will report themselves
- * as having.
- * @param f Frames; must be less than or equal to the number of allocated frames.
- */
-void
-AudioBuffers::set_frames (int f)
-{
- assert (f <= _allocated_frames);
- _frames = f;
-}
-
-/** Make all samples on all channels silent */
-void
-AudioBuffers::make_silent ()
-{
- for (int i = 0; i < _channels; ++i) {
- make_silent (i);
- }
-}
-
-/** Make all samples on a given channel silent.
- * @param c Channel.
- */
-void
-AudioBuffers::make_silent (int c)
-{
- assert (c >= 0 && c < _channels);
-
- for (int i = 0; i < _frames; ++i) {
- _data[c][i] = 0;
- }
-}
-
-/** Copy data from another AudioBuffers to this one. All channels are copied.
- * @param from AudioBuffers to copy from; must have the same number of channels as this.
- * @param frames_to_copy Number of frames to copy.
- * @param read_offset Offset to read from in `from'.
- * @param write_offset Offset to write to in `to'.
- */
-void
-AudioBuffers::copy_from (AudioBuffers* from, int frames_to_copy, int read_offset, int write_offset)
-{
- assert (from->channels() == channels());
-
- assert (from);
- assert (read_offset >= 0 && (read_offset + frames_to_copy) <= from->_allocated_frames);
- assert (write_offset >= 0 && (write_offset + frames_to_copy) <= _allocated_frames);
-
- for (int i = 0; i < _channels; ++i) {
- memcpy (_data[i] + write_offset, from->_data[i] + read_offset, frames_to_copy * sizeof(float));
- }
-}
-
-/** Move audio data around.
- * @param from Offset to move from.
- * @param to Offset to move to.
- * @param frames Number of frames to move.
- */
-
-void
-AudioBuffers::move (int from, int to, int frames)
-{
- if (frames == 0) {
- return;
- }
-
- assert (from >= 0);
- assert (from < _frames);
- assert (to >= 0);
- assert (to < _frames);
- assert (frames > 0);
- assert (frames <= _frames);
- assert ((from + frames) <= _frames);
- assert ((to + frames) <= _frames);
-
- for (int i = 0; i < _channels; ++i) {
- memmove (_data[i] + to, _data[i] + from, frames * sizeof(float));
- }
-}
-
-/** Add data from from `from', `from_channel' to our channel `to_channel' */
-void
-AudioBuffers::accumulate (shared_ptr<const AudioBuffers> from, int from_channel, int to_channel)
-{
- int const N = frames ();
- assert (from->frames() == N);
-
- float* s = from->data (from_channel);
- float* d = _data[to_channel];
-
- for (int i = 0; i < N; ++i) {
- *d++ += *s++;
- }
-}
-
/** Trip an assert if the caller is not in the UI thread */
void
ensure_ui_thread ()
diff --git a/src/lib/util.h b/src/lib/util.h
index 51ccbba99..65859309d 100644
--- a/src/lib/util.h
+++ b/src/lib/util.h
@@ -51,6 +51,7 @@ extern "C" {
class Scaler;
extern std::string seconds_to_hms (int);
+extern std::string time_to_hms (Time);
extern std::string seconds_to_approximate_hms (int);
extern void stacktrace (std::ostream &, int);
extern std::string dependency_version_summary ();
@@ -101,10 +102,8 @@ struct FrameRateConversion
std::string description;
};
-int best_dcp_frame_rate (float);
-
extern std::string crop_string (Position, libdcp::Size);
-extern int dcp_audio_sample_rate (int);
+extern int dcp_audio_frame_rate (int);
extern std::string colour_lut_index_to_name (int index);
extern int stride_round_up (int, int const *, int);
extern int stride_lookup (int c, int const * stride);
@@ -151,51 +150,6 @@ private:
int _timeout;
};
-/** @class AudioBuffers
- * @brief A class to hold multi-channel audio data in float format.
- */
-class AudioBuffers
-{
-public:
- AudioBuffers (int channels, int frames);
- AudioBuffers (AudioBuffers const &);
- AudioBuffers (boost::shared_ptr<const AudioBuffers>);
- ~AudioBuffers ();
-
- float** data () const {
- return _data;
- }
-
- float* data (int) const;
-
- int channels () const {
- return _channels;
- }
-
- int frames () const {
- return _frames;
- }
-
- void set_frames (int f);
-
- void make_silent ();
- void make_silent (int c);
-
- void copy_from (AudioBuffers* from, int frames_to_copy, int read_offset, int write_offset);
- void move (int from, int to, int frames);
- void accumulate (boost::shared_ptr<const AudioBuffers>, int, int);
-
-private:
- /** Number of channels */
- int _channels;
- /** Number of frames (where a frame is one sample across all channels) */
- int _frames;
- /** Number of frames that _data can hold */
- int _allocated_frames;
- /** Audio data (so that, e.g. _data[2][6] is channel 2, sample 6) */
- float** _data;
-};
-
extern int64_t video_frames_to_audio_frames (ContentVideoFrame v, float audio_sample_rate, float frames_per_second);
extern std::pair<std::string, int> cpu_info ();
diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc
index 2af6ba908..9fb2b9bce 100644
--- a/src/lib/video_content.cc
+++ b/src/lib/video_content.cc
@@ -104,9 +104,3 @@ VideoContent::information () const
return s.str ();
}
-
-Time
-VideoContent::temporal_length () const
-{
- return video_length() / video_frame_rate();
-}
diff --git a/src/lib/video_content.h b/src/lib/video_content.h
index b2ec87e2b..75e507d4d 100644
--- a/src/lib/video_content.h
+++ b/src/lib/video_content.h
@@ -42,7 +42,6 @@ public:
void as_xml (xmlpp::Node *) const;
virtual std::string information () const;
- Time temporal_length () const;
ContentVideoFrame video_length () const {
boost::mutex::scoped_lock lm (_mutex);
diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc
index fd8238441..a24059da2 100644
--- a/src/lib/video_decoder.cc
+++ b/src/lib/video_decoder.cc
@@ -79,7 +79,7 @@ VideoDecoder::set_progress (Job* j) const
{
assert (j);
- if (_film->video_length()) {
- j->set_progress (float (_video_frame) / _film->video_length());
+ if (_film->length()) {
+ j->set_progress (float (_video_frame) / _film->time_to_video_frames (_film->length()));
}
}
diff --git a/src/lib/writer.cc b/src/lib/writer.cc
index c7d2cf8b4..f1451763e 100644
--- a/src/lib/writer.cc
+++ b/src/lib/writer.cc
@@ -75,26 +75,24 @@ Writer::Writer (shared_ptr<Film> f, shared_ptr<Job> j)
new libdcp::MonoPictureAsset (
_film->internal_video_mxf_dir (),
_film->internal_video_mxf_filename (),
- _film->dcp_frame_rate (),
+ _film->dcp_video_frame_rate (),
_film->format()->dcp_size ()
)
);
_picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0);
- if (_film->audio_channels() > 0) {
- _sound_asset.reset (
- new libdcp::SoundAsset (
- _film->dir (_film->dcp_name()),
- _film->dcp_audio_mxf_filename (),
- _film->dcp_frame_rate (),
- _film->audio_mapping().dcp_channels (),
- dcp_audio_sample_rate (_film->audio_frame_rate())
- )
- );
-
- _sound_asset_writer = _sound_asset->start_write ();
- }
+ _sound_asset.reset (
+ new libdcp::SoundAsset (
+ _film->dir (_film->dcp_name()),
+ _film->dcp_audio_mxf_filename (),
+ _film->dcp_video_frame_rate (),
+ MAX_AUDIO_CHANNELS,
+ _film->dcp_audio_frame_rate()
+ )
+ );
+
+ _sound_asset_writer = _sound_asset->start_write ();
_thread = new boost::thread (boost::bind (&Writer::thread, this));
}
@@ -205,8 +203,8 @@ try
}
lock.lock ();
- if (_film->video_length ()) {
- _job->set_progress (float(_full_written + _fake_written + _repeat_written) / _film->video_length());
+ if (_film->length ()) {
+ _job->set_progress (float(_full_written + _fake_written + _repeat_written) / _film->time_to_video_frames (_film->length()));
}
++_last_written_frame;
@@ -263,11 +261,8 @@ Writer::finish ()
_thread = 0;
_picture_asset_writer->finalize ();
-
- if (_sound_asset_writer) {
- _sound_asset_writer->finalize ();
- }
-
+ _sound_asset_writer->finalize ();
+
int const frames = _last_written_frame + 1;
int duration = 0;
if (_film->trim_type() == Film::CPL) {
@@ -302,12 +297,10 @@ Writer::finish ()
_picture_asset->set_directory (_film->dir (_film->dcp_name ()));
_picture_asset->set_file_name (_film->dcp_video_mxf_filename ());
- if (_sound_asset) {
- if (_film->trim_type() == Film::CPL) {
- _sound_asset->set_entry_point (_film->trim_start ());
- }
- _sound_asset->set_duration (duration);
+ if (_film->trim_type() == Film::CPL) {
+ _sound_asset->set_entry_point (_film->trim_start ());
}
+ _sound_asset->set_duration (duration);
libdcp::DCP dcp (_film->dir (_film->dcp_name()));
@@ -317,7 +310,7 @@ Writer::finish ()
_film->dcp_name(),
_film->dcp_content_type()->libdcp_kind (),
frames,
- _film->dcp_frame_rate ()
+ _film->dcp_video_frame_rate ()
)
);
diff --git a/src/lib/wscript b/src/lib/wscript
index bce6c644c..0d9da3a8f 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -6,6 +6,7 @@ sources = """
ab_transcoder.cc
analyse_audio_job.cc
audio_analysis.cc
+ audio_buffers.cc
audio_content.cc
audio_decoder.cc
audio_mapping.cc
diff --git a/src/wx/audio_dialog.cc b/src/wx/audio_dialog.cc
index d1d13ab78..f508b8943 100644
--- a/src/wx/audio_dialog.cc
+++ b/src/wx/audio_dialog.cc
@@ -90,10 +90,6 @@ AudioDialog::set_film (shared_ptr<Film> f)
_film = f;
- for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
- _channel_checkbox[i]->Show (!_film->audio_mapping().dcp_to_content (static_cast<libdcp::Channel> (i)).empty());
- }
-
try_to_load_analysis ();
_plot->set_gain (_film->audio_gain ());
diff --git a/src/wx/audio_mapping_view.cc b/src/wx/audio_mapping_view.cc
index 093d25b7c..10157eb2c 100644
--- a/src/wx/audio_mapping_view.cc
+++ b/src/wx/audio_mapping_view.cc
@@ -140,7 +140,7 @@ AudioMappingView::set_mapping (AudioMapping map)
_grid->DeleteRows (0, _grid->GetNumberRows ());
}
- list<AudioMapping::Channel> content_channels = map.content_channels ();
+ list<int> content_channels = map.content_channels ();
_grid->InsertRows (0, content_channels.size ());
for (size_t r = 0; r < content_channels.size(); ++r) {
@@ -150,10 +150,8 @@ AudioMappingView::set_mapping (AudioMapping map)
}
int n = 0;
- for (list<AudioMapping::Channel>::iterator i = content_channels.begin(); i != content_channels.end(); ++i) {
- shared_ptr<const AudioContent> ac = i->content.lock ();
- assert (ac);
- _grid->SetCellValue (n, 0, wxString::Format (wxT("%s %d"), std_to_wx (ac->file().filename().string()).data(), i->index + 1));
+ for (list<int>::iterator i = content_channels.begin(); i != content_channels.end(); ++i) {
+ _grid->SetCellValue (n, 0, wxString::Format (wxT("%d"), *i + 1));
list<libdcp::Channel> const d = map.content_to_dcp (*i);
for (list<libdcp::Channel>::const_iterator j = d.begin(); j != d.end(); ++j) {
diff --git a/src/wx/ffmpeg_content_dialog.cc b/src/wx/ffmpeg_content_dialog.cc
index 8adff59f4..0949d02a6 100644
--- a/src/wx/ffmpeg_content_dialog.cc
+++ b/src/wx/ffmpeg_content_dialog.cc
@@ -113,7 +113,7 @@ FFmpegContentDialog::audio_stream_changed (wxCommandEvent &)
} else {
s << c->audio_channels() << wxT (" ") << _("channels");
}
- s << wxT (", ") << c->audio_frame_rate() << _("Hz");
+ s << wxT (", ") << c->content_audio_frame_rate() << _("Hz");
_audio_description->SetLabel (s);
}
}
diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc
index 528e3840f..32def4641 100644
--- a/src/wx/film_editor.cc
+++ b/src/wx/film_editor.cc
@@ -163,21 +163,6 @@ FilmEditor::make_film_panel ()
grid->Add (_length, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
++r;
-
- {
- add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim frames"), wxGBPosition (r, 0));
- wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- add_label_to_sizer (s, _film_panel, _("Start"));
- _trim_start = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (_trim_start);
- add_label_to_sizer (s, _film_panel, _("End"));
- _trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (_trim_end);
-
- grid->Add (s, wxGBPosition (r, 1));
- }
- ++r;
-
add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim method"), wxGBPosition (r, 0));
_trim_type = new wxChoice (_film_panel, wxID_ANY);
grid->Add (_trim_type, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
@@ -215,8 +200,6 @@ FilmEditor::connect_to_widgets ()
_content_add->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_add_clicked), 0, this);
_content_remove->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_remove_clicked), 0, this);
_content_properties->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_properties_clicked), 0, this);
- _content_earlier->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_earlier_clicked), 0, this);
- _content_later->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_later_clicked), 0, this);
_content_timeline->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_timeline_clicked), 0, this);
_loop_content->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::loop_content_toggled), 0, this);
_loop_count->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::loop_count_changed), 0, this);
@@ -230,8 +213,6 @@ FilmEditor::connect_to_widgets ()
_dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_frame_rate_changed), 0, this);
_best_dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::best_dcp_frame_rate_clicked), 0, this);
_ab->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::ab_toggled), 0, this);
- _trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::trim_start_changed), 0, this);
- _trim_end->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::trim_end_changed), 0, this);
_trim_type->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::trim_type_changed), 0, this);
_with_subtitles->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this);
_subtitle_offset->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_offset_changed), 0, this);
@@ -335,8 +316,6 @@ FilmEditor::make_video_panel ()
_top_crop->SetRange (0, 1024);
_right_crop->SetRange (0, 1024);
_bottom_crop->SetRange (0, 1024);
- _trim_start->SetRange (0, 100);
- _trim_end->SetRange (0, 100);
_j2k_bandwidth->SetRange (50, 250);
}
@@ -363,10 +342,6 @@ FilmEditor::make_content_panel ()
b->Add (_content_remove);
_content_properties = new wxButton (_content_panel, wxID_ANY, _("Properties..."));
b->Add (_content_properties);
- _content_earlier = new wxButton (_content_panel, wxID_ANY, _("Earlier"));
- b->Add (_content_earlier);
- _content_later = new wxButton (_content_panel, wxID_ANY, _("Later"));
- b->Add (_content_later);
_content_timeline = new wxButton (_content_panel, wxID_ANY, _("Timeline..."));
b->Add (_content_timeline);
@@ -589,7 +564,7 @@ FilmEditor::dcp_frame_rate_changed (wxCommandEvent &)
return;
}
- _film->set_dcp_frame_rate (
+ _film->set_dcp_video_frame_rate (
boost::lexical_cast<int> (
wx_to_std (_dcp_frame_rate->GetString (_dcp_frame_rate->GetSelection ()))
)
@@ -667,12 +642,6 @@ FilmEditor::film_changed (Film::Property p)
case Film::SCALER:
checked_set (_scaler, Scaler::as_index (_film->scaler ()));
break;
- case Film::TRIM_START:
- checked_set (_trim_start, _film->trim_start());
- break;
- case Film::TRIM_END:
- checked_set (_trim_end, _film->trim_end());
- break;
case Film::TRIM_TYPE:
checked_set (_trim_type, _film->trim_type() == Film::CPL ? 0 : 1);
break;
@@ -706,11 +675,11 @@ FilmEditor::film_changed (Film::Property p)
case Film::DCI_METADATA:
setup_dcp_name ();
break;
- case Film::DCP_FRAME_RATE:
+ case Film::DCP_VIDEO_FRAME_RATE:
{
bool done = false;
for (unsigned int i = 0; i < _dcp_frame_rate->GetCount(); ++i) {
- if (wx_to_std (_dcp_frame_rate->GetString(i)) == boost::lexical_cast<string> (_film->dcp_frame_rate())) {
+ if (wx_to_std (_dcp_frame_rate->GetString(i)) == boost::lexical_cast<string> (_film->dcp_video_frame_rate())) {
checked_set (_dcp_frame_rate, i);
done = true;
break;
@@ -721,17 +690,10 @@ FilmEditor::film_changed (Film::Property p)
checked_set (_dcp_frame_rate, -1);
}
- if (_film->video_frame_rate()) {
- _best_dcp_frame_rate->Enable (best_dcp_frame_rate (_film->video_frame_rate ()) != _film->dcp_frame_rate ());
- } else {
- _best_dcp_frame_rate->Disable ();
- }
+ _best_dcp_frame_rate->Enable (_film->best_dcp_video_frame_rate () != _film->dcp_video_frame_rate ());
setup_frame_rate_description ();
break;
}
- case Film::AUDIO_MAPPING:
- _audio_mapping->set_mapping (_film->audio_mapping ());
- break;
}
}
@@ -785,20 +747,10 @@ void
FilmEditor::setup_length ()
{
stringstream s;
- ContentVideoFrame const frames = _film->content_length ();
+ Time const length = _film->length ();
- if (frames && _film->video_frame_rate()) {
- s << frames << " " << wx_to_std (_("frames")) << "; " << seconds_to_hms (frames / _film->video_frame_rate());
- } else if (frames) {
- s << frames << " " << wx_to_std (_("frames"));
- }
-
+ s << time_to_hms (length);
_length->SetLabel (std_to_wx (s.str ()));
-
- if (frames) {
- _trim_start->SetRange (0, frames);
- _trim_end->SetRange (0, frames);
- }
}
void
@@ -806,7 +758,10 @@ FilmEditor::setup_frame_rate_description ()
{
wxString d;
int lines = 0;
-
+
+#if 0
+ XXX
+
if (_film->video_frame_rate()) {
d << std_to_wx (FrameRateConversion (_film->video_frame_rate(), _film->dcp_frame_rate()).description);
++lines;
@@ -819,6 +774,7 @@ FilmEditor::setup_frame_rate_description ()
++lines;
}
}
+#endif
for (int i = lines; i < 2; ++i) {
d << wxT ("\n ");
@@ -905,7 +861,7 @@ FilmEditor::set_film (shared_ptr<Film> f)
film_changed (Film::COLOUR_LUT);
film_changed (Film::J2K_BANDWIDTH);
film_changed (Film::DCI_METADATA);
- film_changed (Film::DCP_FRAME_RATE);
+ film_changed (Film::DCP_VIDEO_FRAME_RATE);
film_changed (Film::AUDIO_MAPPING);
film_content_changed (boost::shared_ptr<Content> (), FFmpegContentProperty::SUBTITLE_STREAMS);
@@ -940,8 +896,6 @@ FilmEditor::set_things_sensitive (bool s)
_dcp_content_type->Enable (s);
_best_dcp_frame_rate->Enable (s);
_dcp_frame_rate->Enable (s);
- _trim_start->Enable (s);
- _trim_end->Enable (s);
_ab->Enable (s);
_trim_type->Enable (s);
_colour_lut->Enable (s);
@@ -1019,26 +973,6 @@ FilmEditor::setup_notebook_size ()
}
void
-FilmEditor::trim_start_changed (wxCommandEvent &)
-{
- if (!_film) {
- return;
- }
-
- _film->set_trim_start (_trim_start->GetValue ());
-}
-
-void
-FilmEditor::trim_end_changed (wxCommandEvent &)
-{
- if (!_film) {
- return;
- }
-
- _film->set_trim_end (_trim_end->GetValue ());
-}
-
-void
FilmEditor::audio_gain_calculate_button_clicked (wxCommandEvent &)
{
GainCalculatorDialog* d = new GainCalculatorDialog (this);
@@ -1168,13 +1102,13 @@ FilmEditor::best_dcp_frame_rate_clicked (wxCommandEvent &)
return;
}
- _film->set_dcp_frame_rate (best_dcp_frame_rate (_film->video_frame_rate ()));
+ _film->set_dcp_video_frame_rate (_film->best_dcp_video_frame_rate ());
}
void
FilmEditor::setup_show_audio_sensitivity ()
{
- _show_audio->Enable (_film && _film->has_audio ());
+ _show_audio->Enable (_film);
}
void
@@ -1188,17 +1122,17 @@ FilmEditor::setup_content ()
_content->DeleteAllItems ();
- ContentList content = _film->content ();
- for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
+ Playlist::RegionList regions = _film->regions ();
+ for (Playlist::RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
int const t = _content->GetItemCount ();
- _content->InsertItem (t, std_to_wx ((*i)->summary ()));
- if ((*i)->summary() == selected_summary) {
+ _content->InsertItem (t, std_to_wx (i->content->summary ()));
+ if (i->content->summary() == selected_summary) {
_content->SetItemState (t, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
}
- if (selected_summary.empty () && !content.empty ()) {
- /* Select the first item of content if non was selected before */
+ if (selected_summary.empty () && !regions.empty ()) {
+ /* Select the item of content if non was selected before */
_content->SetItemState (0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
@@ -1244,10 +1178,10 @@ FilmEditor::content_remove_clicked (wxCommandEvent &)
void
FilmEditor::content_activated (wxListEvent& ev)
{
- ContentList c = _film->content ();
- assert (ev.GetIndex() >= 0 && size_t (ev.GetIndex()) < c.size ());
+ Playlist::RegionList r = _film->regions ();
+ assert (ev.GetIndex() >= 0 && size_t (ev.GetIndex()) < r.size ());
- content_properties (c[ev.GetIndex()]);
+ content_properties (r[ev.GetIndex()].content);
}
void
@@ -1262,16 +1196,16 @@ FilmEditor::content_properties_clicked (wxCommandEvent &)
}
void
-FilmEditor::content_properties (shared_ptr<Content> c)
+FilmEditor::content_properties (shared_ptr<Content> content)
{
- shared_ptr<ImageMagickContent> im = dynamic_pointer_cast<ImageMagickContent> (c);
+ shared_ptr<ImageMagickContent> im = dynamic_pointer_cast<ImageMagickContent> (content);
if (im) {
ImageMagickContentDialog* d = new ImageMagickContentDialog (this, im);
d->ShowModal ();
d->Destroy ();
}
- shared_ptr<FFmpegContent> ff = dynamic_pointer_cast<FFmpegContent> (c);
+ shared_ptr<FFmpegContent> ff = dynamic_pointer_cast<FFmpegContent> (content);
if (ff) {
FFmpegContentDialog* d = new FFmpegContentDialog (this, ff);
d->ShowModal ();
@@ -1280,24 +1214,6 @@ FilmEditor::content_properties (shared_ptr<Content> c)
}
void
-FilmEditor::content_earlier_clicked (wxCommandEvent &)
-{
- shared_ptr<Content> c = selected_content ();
- if (c) {
- _film->move_content_earlier (c);
- }
-}
-
-void
-FilmEditor::content_later_clicked (wxCommandEvent &)
-{
- shared_ptr<Content> c = selected_content ();
- if (c) {
- _film->move_content_later (c);
- }
-}
-
-void
FilmEditor::content_selection_changed (wxListEvent &)
{
setup_content_button_sensitivity ();
@@ -1329,8 +1245,6 @@ FilmEditor::setup_content_button_sensitivity ()
);
_content_remove->Enable (selection && _generally_sensitive);
- _content_earlier->Enable (selection && _generally_sensitive);
- _content_later->Enable (selection && _generally_sensitive);
_content_timeline->Enable (_generally_sensitive);
}
@@ -1342,12 +1256,12 @@ FilmEditor::selected_content ()
return shared_ptr<Content> ();
}
- ContentList c = _film->content ();
- if (s < 0 || size_t (s) >= c.size ()) {
+ Playlist::RegionList r = _film->regions ();
+ if (s < 0 || size_t (s) >= r.size ()) {
return shared_ptr<Content> ();
}
- return c[s];
+ return r[s].content;
}
void
@@ -1355,6 +1269,8 @@ FilmEditor::setup_scaling_description ()
{
wxString d;
+#if 0
+XXX
int lines = 0;
if (_film->video_size().width && _film->video_size().height) {
@@ -1403,6 +1319,7 @@ FilmEditor::setup_scaling_description ()
d << wxT ("\n ");
}
+#endif
_scaling_description->SetLabel (d);
}
@@ -1444,6 +1361,6 @@ FilmEditor::content_timeline_clicked (wxCommandEvent &)
_timeline_dialog = 0;
}
- _timeline_dialog = new TimelineDialog (this, _film->playlist ());
+ _timeline_dialog = new TimelineDialog (this, _film);
_timeline_dialog->Show ();
}
diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h
index 41f7bfd1b..c791ad064 100644
--- a/src/wx/film_editor.h
+++ b/src/wx/film_editor.h
@@ -70,12 +70,8 @@ private:
void content_add_clicked (wxCommandEvent &);
void content_remove_clicked (wxCommandEvent &);
void content_properties_clicked (wxCommandEvent &);
- void content_earlier_clicked (wxCommandEvent &);
- void content_later_clicked (wxCommandEvent &);
void imagemagick_video_length_changed (wxCommandEvent &);
void format_changed (wxCommandEvent &);
- void trim_start_changed (wxCommandEvent &);
- void trim_end_changed (wxCommandEvent &);
void trim_type_changed (wxCommandEvent &);
void dcp_content_type_changed (wxCommandEvent &);
void ab_toggled (wxCommandEvent &);
@@ -177,8 +173,6 @@ private:
/** The Film's audio details */
wxStaticText* _audio;
- wxSpinCtrl* _trim_start;
- wxSpinCtrl* _trim_end;
wxChoice* _trim_type;
/** Selector to generate an A/B comparison DCP */
wxCheckBox* _ab;
diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc
index 03f0ca646..11c826a26 100644
--- a/src/wx/film_viewer.cc
+++ b/src/wx/film_viewer.cc
@@ -180,7 +180,7 @@ FilmViewer::set_film (shared_ptr<Film> f)
void
FilmViewer::update_from_decoder ()
{
- if (!_player || _player->seek (_player->last_video_time ())) {
+ if (!_player || _player->seek (_player->last_video ())) {
return;
}
@@ -201,8 +201,8 @@ FilmViewer::timer (wxTimerEvent &)
get_frame ();
- if (_film->video_length()) {
- int const new_slider_position = 4096 * _player->last_video_time() / (_film->video_length() / _film->video_frame_rate());
+ if (_film->length()) {
+ int const new_slider_position = 4096 * _player->last_video() / _film->length();
if (new_slider_position != _slider->GetValue()) {
_slider->SetValue (new_slider_position);
}
@@ -259,7 +259,7 @@ void
FilmViewer::slider_moved (wxScrollEvent &)
{
if (_film && _player) {
- _player->seek (_slider->GetValue() * _film->video_length() / (4096 * _film->video_frame_rate()));
+ _player->seek (_slider->GetValue() * _film->length() / 4096);
}
get_frame ();
@@ -312,7 +312,7 @@ FilmViewer::raw_to_display ()
when working out the scale that we are applying.
*/
- Size const cropped_size = _film->cropped_size (_film->video_size ());
+ Size const cropped_size = _film->cropped_size (_raw_frame->size ());
Rect tx = subtitle_transformed_area (
float (_film_size.width) / cropped_size.width,
@@ -381,7 +381,7 @@ FilmViewer::check_play_state ()
}
if (_play_button->GetValue()) {
- _timer.Start (1000 / _film->video_frame_rate());
+ _timer.Start (1000 / _film->dcp_video_frame_rate());
} else {
_timer.Stop ();
}
@@ -397,7 +397,7 @@ FilmViewer::process_video (shared_ptr<const Image> image, bool, shared_ptr<Subti
_got_frame = true;
- double const fps = _film->video_frame_rate ();
+ double const fps = _film->dcp_video_frame_rate ();
_frame->SetLabel (wxString::Format (wxT("%d"), int (rint (t * fps))));
double w = t;
@@ -425,6 +425,8 @@ FilmViewer::get_frame ()
return;
}
+ cout << "-> FilmViewer::get_frame()\n";
+
try {
_got_frame = false;
while (!_got_frame) {
@@ -441,6 +443,8 @@ FilmViewer::get_frame ()
check_play_state ();
error_dialog (this, wxString::Format (_("Could not decode video for view (%s)"), std_to_wx(e.what()).data()));
}
+
+ cout << "<- FilmViewer::get_frame()\n";
}
void
diff --git a/src/wx/properties_dialog.cc b/src/wx/properties_dialog.cc
index d20a58ca8..40527ded7 100644
--- a/src/wx/properties_dialog.cc
+++ b/src/wx/properties_dialog.cc
@@ -50,10 +50,8 @@ PropertiesDialog::PropertiesDialog (wxWindow* parent, shared_ptr<Film> film)
_encoded = new ThreadedStaticText (this, _("counting..."), boost::bind (&PropertiesDialog::frames_already_encoded, this));
table->Add (_encoded, 1, wxALIGN_CENTER_VERTICAL);
- _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->video_length())));
- FrameRateConversion frc (_film->video_frame_rate(), _film->dcp_frame_rate());
- int const dcp_length = _film->video_length() * frc.factor();
- double const disk = ((double) _film->j2k_bandwidth() / 8) * dcp_length / (_film->dcp_frame_rate() * 1073741824.0f);
+ _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->time_to_video_frames (_film->length()))));
+ double const disk = ((double) _film->j2k_bandwidth() / 8) * _film->length() / (TIME_HZ * 1073741824.0f);
stringstream s;
s << fixed << setprecision (1) << disk << wx_to_std (_("Gb"));
_disk->SetLabel (std_to_wx (s.str ()));
@@ -80,9 +78,9 @@ PropertiesDialog::frames_already_encoded () const
return "";
}
- if (_film->video_length()) {
+ if (_film->length()) {
/* XXX: encoded_frames() should check which frames have been encoded */
- u << " (" << (_film->encoded_frames() * 100 / _film->video_length()) << "%)";
+ u << " (" << (_film->encoded_frames() * 100 / _film->time_to_video_frames (_film->length())) << "%)";
}
return u.str ();
}
diff --git a/src/wx/timeline.cc b/src/wx/timeline.cc
index 731e72168..f6d41dcb9 100644
--- a/src/wx/timeline.cc
+++ b/src/wx/timeline.cc
@@ -19,6 +19,7 @@
#include <list>
#include <wx/graphics.h>
+#include "film.h"
#include "timeline.h"
#include "wx_util.h"
#include "playlist.h"
@@ -45,7 +46,7 @@ public:
protected:
int time_x (Time t) const
{
- return _timeline.tracks_position().x + t * _timeline.pixels_per_second();
+ return _timeline.tracks_position().x + t * _timeline.pixels_per_time_unit();
}
Timeline& _timeline;
@@ -54,7 +55,7 @@ protected:
class ContentView : public View
{
public:
- ContentView (Timeline& tl, boost::shared_ptr<const Content> c, Time s, int t)
+ ContentView (Timeline& tl, shared_ptr<const Content> c, Time s, int t)
: View (tl)
, _content (c)
, _start (s)
@@ -66,12 +67,13 @@ public:
void paint (wxGraphicsContext* gc)
{
+ shared_ptr<const Film> film = _timeline.film ();
shared_ptr<const Content> content = _content.lock ();
- if (!content) {
+ if (!film || !content) {
return;
}
-
- Time const len = content->temporal_length ();
+
+ Time const len = content->length (film);
gc->SetPen (*wxBLACK_PEN);
@@ -107,19 +109,20 @@ public:
wxDouble name_leading;
gc->GetTextExtent (name, &name_width, &name_height, &name_descent, &name_leading);
- gc->Clip (wxRegion (time_x (_start), y_pos (_track), len * _timeline.pixels_per_second(), _timeline.track_height()));
+ gc->Clip (wxRegion (time_x (_start), y_pos (_track), len * _timeline.pixels_per_time_unit(), _timeline.track_height()));
gc->DrawText (name, time_x (_start) + 12, y_pos (_track + 1) - name_height - 4);
gc->ResetClip ();
}
Rect bbox () const
{
+ shared_ptr<const Film> film = _timeline.film ();
shared_ptr<const Content> content = _content.lock ();
- if (!content) {
+ if (!film || !content) {
return Rect ();
}
- return Rect (time_x (_start), y_pos (_track), content->temporal_length() * _timeline.pixels_per_second(), _timeline.track_height());
+ return Rect (time_x (_start), y_pos (_track), content->length (film) * _timeline.pixels_per_time_unit(), _timeline.track_height());
}
void set_selected (bool s) {
@@ -150,7 +153,7 @@ private:
class AudioContentView : public ContentView
{
public:
- AudioContentView (Timeline& tl, boost::shared_ptr<const Content> c, Time s, int t)
+ AudioContentView (Timeline& tl, shared_ptr<const Content> c, Time s, int t)
: ContentView (tl, c, s, t)
{}
@@ -169,7 +172,7 @@ private:
class VideoContentView : public ContentView
{
public:
- VideoContentView (Timeline& tl, boost::shared_ptr<const Content> c, Time s, int t)
+ VideoContentView (Timeline& tl, shared_ptr<const Content> c, Time s, int t)
: ContentView (tl, c, s, t)
{}
@@ -202,7 +205,7 @@ public:
gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, wxSOLID));
#endif
- int mark_interval = rint (128 / _timeline.pixels_per_second ());
+ int mark_interval = rint (128 * TIME_HZ / _timeline.pixels_per_time_unit ());
if (mark_interval > 5) {
mark_interval -= mark_interval % 5;
}
@@ -226,7 +229,7 @@ public:
gc->StrokePath (path);
Time t = 0;
- while ((t * _timeline.pixels_per_second()) < _timeline.width()) {
+ while ((t * _timeline.pixels_per_time_unit()) < _timeline.width()) {
wxGraphicsPath path = gc->CreatePath ();
path.MoveToPoint (time_x (t), _y - 4);
path.AddLineToPoint (time_x (t), _y + 4);
@@ -246,7 +249,7 @@ public:
wxDouble str_leading;
gc->GetTextExtent (str, &str_width, &str_height, &str_descent, &str_leading);
- int const tx = _timeline.x_offset() + t * _timeline.pixels_per_second();
+ int const tx = _timeline.x_offset() + t * _timeline.pixels_per_time_unit();
if ((tx + str_width) < _timeline.width()) {
gc->DrawText (str, time_x (t), _y + 16);
}
@@ -263,14 +266,14 @@ private:
int _y;
};
-Timeline::Timeline (wxWindow* parent, shared_ptr<Playlist> pl)
+Timeline::Timeline (wxWindow* parent, shared_ptr<const Film> film)
: wxPanel (parent)
- , _playlist (pl)
- , _pixels_per_second (0)
+ , _film (film)
+ , _pixels_per_time_unit (0)
{
SetDoubleBuffered (true);
- setup_pixels_per_second ();
+ setup_pixels_per_time_unit ();
Connect (wxID_ANY, wxEVT_PAINT, wxPaintEventHandler (Timeline::paint), 0, this);
Connect (wxID_ANY, wxEVT_LEFT_DOWN, wxMouseEventHandler (Timeline::left_down), 0, this);
@@ -279,8 +282,8 @@ Timeline::Timeline (wxWindow* parent, shared_ptr<Playlist> pl)
playlist_changed ();
- pl->Changed.connect (bind (&Timeline::playlist_changed, this));
- pl->ContentChanged.connect (bind (&Timeline::playlist_changed, this));
+ film->playlist()->Changed.connect (bind (&Timeline::playlist_changed, this));
+ film->playlist()->ContentChanged.connect (bind (&Timeline::playlist_changed, this));
}
void
@@ -288,11 +291,6 @@ Timeline::paint (wxPaintEvent &)
{
wxPaintDC dc (this);
- shared_ptr<Playlist> pl = _playlist.lock ();
- if (!pl) {
- return;
- }
-
wxGraphicsContext* gc = wxGraphicsContext::Create (dc);
if (!gc) {
return;
@@ -300,9 +298,6 @@ Timeline::paint (wxPaintEvent &)
gc->SetFont (gc->CreateFont (*wxNORMAL_FONT));
- /* XXX */
- _pixels_per_second = (width() - x_offset() * 2) / (pl->content_length() / pl->video_frame_rate());
-
for (list<shared_ptr<View> >::iterator i = _views.begin(); i != _views.end(); ++i) {
(*i)->paint (gc);
}
@@ -313,30 +308,20 @@ Timeline::paint (wxPaintEvent &)
void
Timeline::playlist_changed ()
{
- shared_ptr<Playlist> pl = _playlist.lock ();
- if (!pl) {
+ shared_ptr<const Film> fl = _film.lock ();
+ if (!fl) {
return;
}
_views.clear ();
-
- int track = 0;
- Time time = 0;
- list<shared_ptr<const VideoContent> > vc = pl->video ();
- for (list<shared_ptr<const VideoContent> >::const_iterator i = vc.begin(); i != vc.end(); ++i) {
- _views.push_back (shared_ptr<View> (new VideoContentView (*this, *i, time, track)));
- time += (*i)->temporal_length ();
- }
- ++track;
- time = 0;
- list<shared_ptr<const AudioContent> > ac = pl->audio ();
- for (list<shared_ptr<const AudioContent> >::const_iterator i = ac.begin(); i != ac.end(); ++i) {
- _views.push_back (shared_ptr<View> (new AudioContentView (*this, *i, time, track)));
- if (pl->audio_from() != Playlist::AUDIO_FFMPEG) {
- ++track;
+ Playlist::RegionList regions = fl->playlist()->regions ();
+
+ for (Playlist::RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if (dynamic_pointer_cast<VideoContent> (i->content)) {
+ _views.push_back (shared_ptr<View> (new VideoContentView (*this, i->content, i->time, 0)));
} else {
- time += (*i)->temporal_length ();
+ _views.push_back (shared_ptr<View> (new AudioContentView (*this, i->content, i->time, 1)));
}
}
@@ -348,28 +333,19 @@ Timeline::playlist_changed ()
int
Timeline::tracks () const
{
- shared_ptr<Playlist> pl = _playlist.lock ();
- if (!pl) {
- return 0;
- }
-
- if (pl->audio_from() == Playlist::AUDIO_FFMPEG) {
- return 2;
- }
-
- return 1 + max (size_t (1), pl->audio().size());
+ /* XXX */
+ return 2;
}
void
-Timeline::setup_pixels_per_second ()
+Timeline::setup_pixels_per_time_unit ()
{
- shared_ptr<Playlist> pl = _playlist.lock ();
- if (!pl) {
+ shared_ptr<const Film> film = _film.lock ();
+ if (!film) {
return;
}
- /* XXX */
- _pixels_per_second = (width() - x_offset() * 2) / (pl->content_length() / pl->video_frame_rate());
+ _pixels_per_time_unit = (width() - x_offset() * 2) / film->length();
}
void
@@ -394,3 +370,9 @@ Timeline::force_redraw (Rect const & r)
{
RefreshRect (wxRect (r.x, r.y, r.width, r.height), false);
}
+
+shared_ptr<const Film>
+Timeline::film () const
+{
+ return _film.lock ();
+}
diff --git a/src/wx/timeline.h b/src/wx/timeline.h
index 59800e7ad..4214ee3a8 100644
--- a/src/wx/timeline.h
+++ b/src/wx/timeline.h
@@ -22,13 +22,15 @@
#include <wx/wx.h>
#include "util.h"
-class Playlist;
+class Film;
class View;
class Timeline : public wxPanel
{
public:
- Timeline (wxWindow *, boost::shared_ptr<Playlist>);
+ Timeline (wxWindow *, boost::shared_ptr<const Film>);
+
+ boost::shared_ptr<const Film> film () const;
void force_redraw (Rect const &);
@@ -44,8 +46,8 @@ public:
return 64;
}
- double pixels_per_second () const {
- return _pixels_per_second;
+ double pixels_per_time_unit () const {
+ return _pixels_per_time_unit;
}
Position tracks_position () const {
@@ -58,9 +60,9 @@ private:
void paint (wxPaintEvent &);
void left_down (wxMouseEvent &);
void playlist_changed ();
- void setup_pixels_per_second ();
+ void setup_pixels_per_time_unit ();
- boost::weak_ptr<Playlist> _playlist;
+ boost::weak_ptr<const Film> _film;
std::list<boost::shared_ptr<View> > _views;
- double _pixels_per_second;
+ double _pixels_per_time_unit;
};
diff --git a/src/wx/timeline_dialog.cc b/src/wx/timeline_dialog.cc
index 7a75044c9..91d1f7b07 100644
--- a/src/wx/timeline_dialog.cc
+++ b/src/wx/timeline_dialog.cc
@@ -27,9 +27,9 @@ using std::list;
using std::cout;
using boost::shared_ptr;
-TimelineDialog::TimelineDialog (wxWindow* parent, shared_ptr<Playlist> pl)
+TimelineDialog::TimelineDialog (wxWindow* parent, shared_ptr<const Film> film)
: wxDialog (parent, wxID_ANY, _("Timeline"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE)
- , _timeline (this, pl)
+ , _timeline (this, film)
{
wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
diff --git a/src/wx/timeline_dialog.h b/src/wx/timeline_dialog.h
index e58de5540..bc6b83eb5 100644
--- a/src/wx/timeline_dialog.h
+++ b/src/wx/timeline_dialog.h
@@ -27,7 +27,7 @@ class Playlist;
class TimelineDialog : public wxDialog
{
public:
- TimelineDialog (wxWindow *, boost::shared_ptr<Playlist>);
+ TimelineDialog (wxWindow *, boost::shared_ptr<const Film>);
private:
Timeline _timeline;