summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/analyse_audio_job.cc2
-rw-r--r--src/lib/analyse_audio_job.h2
-rw-r--r--src/lib/audio_content.cc26
-rw-r--r--src/lib/audio_content.h4
-rw-r--r--src/lib/audio_decoder.cc33
-rw-r--r--src/lib/audio_decoder.h19
-rw-r--r--src/lib/audio_merger.h11
-rw-r--r--src/lib/content.cc18
-rw-r--r--src/lib/content.h30
-rw-r--r--src/lib/decoded.h116
-rw-r--r--src/lib/decoder.cc32
-rw-r--r--src/lib/decoder.h24
-rw-r--r--src/lib/encoder.cc2
-rw-r--r--src/lib/ffmpeg.cc4
-rw-r--r--src/lib/ffmpeg_content.cc9
-rw-r--r--src/lib/ffmpeg_content.h2
-rw-r--r--src/lib/ffmpeg_decoder.cc246
-rw-r--r--src/lib/ffmpeg_decoder.h13
-rw-r--r--src/lib/film.cc16
-rw-r--r--src/lib/film.h11
-rw-r--r--src/lib/image.cc27
-rw-r--r--src/lib/image_content.cc4
-rw-r--r--src/lib/image_content.h2
-rw-r--r--src/lib/image_decoder.cc27
-rw-r--r--src/lib/image_decoder.h9
-rw-r--r--src/lib/job.cc2
-rw-r--r--src/lib/player.cc517
-rw-r--r--src/lib/player.h70
-rw-r--r--src/lib/playlist.cc35
-rw-r--r--src/lib/playlist.h6
-rw-r--r--src/lib/resampler.cc8
-rw-r--r--src/lib/resampler.h2
-rw-r--r--src/lib/sndfile_content.cc2
-rw-r--r--src/lib/sndfile_content.h2
-rw-r--r--src/lib/sndfile_decoder.cc19
-rw-r--r--src/lib/sndfile_decoder.h8
-rw-r--r--src/lib/subtitle_decoder.cc5
-rw-r--r--src/lib/subtitle_decoder.h7
-rw-r--r--src/lib/types.h5
-rw-r--r--src/lib/util.cc12
-rw-r--r--src/lib/util.h10
-rw-r--r--src/lib/video_content.cc16
-rw-r--r--src/lib/video_content.h4
-rw-r--r--src/lib/video_decoder.cc18
-rw-r--r--src/lib/video_decoder.h21
-rw-r--r--src/tools/server_test.cc11
-rw-r--r--src/wx/audio_plot.cc2
-rw-r--r--src/wx/film_editor.cc2
-rw-r--r--src/wx/film_editor.h6
-rw-r--r--src/wx/film_viewer.cc18
-rw-r--r--src/wx/film_viewer.h4
-rw-r--r--src/wx/timecode.cc26
-rw-r--r--src/wx/timecode.h8
-rw-r--r--src/wx/timeline.cc86
-rw-r--r--src/wx/timeline.h10
-rw-r--r--src/wx/timeline_dialog.cc8
-rw-r--r--src/wx/timeline_dialog.h6
-rw-r--r--src/wx/timing_panel.cc10
-rw-r--r--src/wx/timing_panel.h12
-rw-r--r--src/wx/video_panel.cc2
60 files changed, 951 insertions, 718 deletions
diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc
index 8186f9de4..3f84bf16d 100644
--- a/src/lib/analyse_audio_job.cc
+++ b/src/lib/analyse_audio_job.cc
@@ -81,7 +81,7 @@ AnalyseAudioJob::run ()
}
void
-AnalyseAudioJob::audio (shared_ptr<const AudioBuffers> b, Time)
+AnalyseAudioJob::audio (shared_ptr<const AudioBuffers> b, DCPTime)
{
for (int i = 0; i < b->frames(); ++i) {
for (int j = 0; j < b->channels(); ++j) {
diff --git a/src/lib/analyse_audio_job.h b/src/lib/analyse_audio_job.h
index 3d4881983..2e93ef500 100644
--- a/src/lib/analyse_audio_job.h
+++ b/src/lib/analyse_audio_job.h
@@ -33,7 +33,7 @@ public:
void run ();
private:
- void audio (boost::shared_ptr<const AudioBuffers>, Time);
+ void audio (boost::shared_ptr<const AudioBuffers>, DCPTime);
boost::weak_ptr<AudioContent> _content;
OutputAudioFrame _done;
diff --git a/src/lib/audio_content.cc b/src/lib/audio_content.cc
index b4c4f34b6..83a43f3bd 100644
--- a/src/lib/audio_content.cc
+++ b/src/lib/audio_content.cc
@@ -40,7 +40,7 @@ int const AudioContentProperty::AUDIO_GAIN = 203;
int const AudioContentProperty::AUDIO_DELAY = 204;
int const AudioContentProperty::AUDIO_MAPPING = 205;
-AudioContent::AudioContent (shared_ptr<const Film> f, Time s)
+AudioContent::AudioContent (shared_ptr<const Film> f, DCPTime s)
: Content (f, s)
, _audio_gain (0)
, _audio_delay (Config::instance()->default_audio_delay ())
@@ -149,3 +149,27 @@ AudioContent::technical_summary () const
{
return String::compose ("audio: channels %1, length %2, raw rate %3, out rate %4", audio_channels(), audio_length(), content_audio_frame_rate(), output_audio_frame_rate());
}
+
+/** Note: this is not particularly fast, as the FrameRateChange lookup
+ * is not very intelligent.
+ *
+ * @param t Some duration to convert.
+ * @param at The time within the DCP to get the active frame rate change from; i.e. a point at which
+ * the `controlling' video content is active.
+ */
+AudioContent::Frame
+AudioContent::time_to_content_audio_frames (DCPTime t, DCPTime at) const
+{
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+
+ /* Consider the case where we're running a 25fps video at 24fps (i.e. slow)
+ Our audio is at 44.1kHz. We will resample it to 48000 * 25 / 24 and then
+ run it at 48kHz (i.e. slow, to match).
+
+ After 1 second, we'll have run the equivalent of 44.1kHz * 24 / 25 samples
+ in the source.
+ */
+
+ return rint (t * content_audio_frame_rate() * film->active_frame_rate_change(at).speed_up / TIME_HZ);
+}
diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h
index ca4a1f234..e1b38bd97 100644
--- a/src/lib/audio_content.h
+++ b/src/lib/audio_content.h
@@ -43,7 +43,7 @@ class AudioContent : public virtual Content
public:
typedef int64_t Frame;
- AudioContent (boost::shared_ptr<const Film>, Time);
+ AudioContent (boost::shared_ptr<const Film>, DCPTime);
AudioContent (boost::shared_ptr<const Film>, boost::filesystem::path);
AudioContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
AudioContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
@@ -74,6 +74,8 @@ public:
return _audio_delay;
}
+ Frame time_to_content_audio_frames (DCPTime, DCPTime) const;
+
private:
/** Gain to apply to audio in dB */
float _audio_gain;
diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc
index c0ef02f65..aecf39644 100644
--- a/src/lib/audio_decoder.cc
+++ b/src/lib/audio_decoder.cc
@@ -35,24 +35,33 @@ using boost::shared_ptr;
AudioDecoder::AudioDecoder (shared_ptr<const Film> film, shared_ptr<const AudioContent> content)
: Decoder (film)
, _audio_content (content)
- , _audio_position (0)
+ , _last_audio (0)
{
-
+ if (content->output_audio_frame_rate() != content->content_audio_frame_rate() && content->audio_channels ()) {
+ _resampler.reset (new Resampler (content->content_audio_frame_rate(), content->output_audio_frame_rate(), content->audio_channels ()));
+ }
}
void
-AudioDecoder::audio (shared_ptr<const AudioBuffers> data, AudioContent::Frame frame)
+AudioDecoder::audio (shared_ptr<const AudioBuffers> data, ContentTime time)
{
- Audio (data, frame);
- _audio_position = frame + data->frames ();
+ if (_resampler) {
+ data = _resampler->run (data);
+ }
+
+ _pending.push_back (shared_ptr<DecodedAudio> (new DecodedAudio (data, time)));
+ _last_audio = time + (data->frames() * TIME_HZ / _audio_content->output_audio_frame_rate());
}
-/** This is a bit odd, but necessary when we have (e.g.) FFmpegDecoders with no audio.
- * The player needs to know that there is no audio otherwise it will keep trying to
- * pass() the decoder to get it to emit audio.
- */
-bool
-AudioDecoder::has_audio () const
+void
+AudioDecoder::flush ()
{
- return _audio_content->audio_channels () > 0;
+ if (!_resampler) {
+ return;
+ }
+
+ shared_ptr<const AudioBuffers> b = _resampler->flush ();
+ if (b) {
+ audio (b, _last_audio);
+ }
}
diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h
index ab6c4b8a9..a295df0cc 100644
--- a/src/lib/audio_decoder.h
+++ b/src/lib/audio_decoder.h
@@ -27,8 +27,10 @@
#include "decoder.h"
#include "content.h"
#include "audio_content.h"
+#include "decoded.h"
class AudioBuffers;
+class Resampler;
/** @class AudioDecoder.
* @brief Parent class for audio decoders.
@@ -37,17 +39,20 @@ class AudioDecoder : public virtual Decoder
{
public:
AudioDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const AudioContent>);
-
- bool has_audio () const;
-
- /** Emitted when some audio data is ready */
- boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame)> Audio;
+
+ boost::shared_ptr<const AudioContent> audio_content () const {
+ return _audio_content;
+ }
protected:
- void audio (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
+ void audio (boost::shared_ptr<const AudioBuffers>, ContentTime);
+ void flush ();
+
boost::shared_ptr<const AudioContent> _audio_content;
- AudioContent::Frame _audio_position;
+ boost::shared_ptr<Resampler> _resampler;
+ /* End time of last audio that we wrote to _pending; only used for flushing the resampler */
+ ContentTime _last_audio;
};
#endif
diff --git a/src/lib/audio_merger.h b/src/lib/audio_merger.h
index 226601e0e..f068b504e 100644
--- a/src/lib/audio_merger.h
+++ b/src/lib/audio_merger.h
@@ -37,6 +37,8 @@ public:
TimedAudioBuffers<T>
pull (T time)
{
+ assert (time >= _last_pull);
+
TimedAudioBuffers<T> out;
F const to_return = _t_to_f (time - _last_pull);
@@ -97,9 +99,16 @@ public:
if (_buffers->frames() == 0) {
return TimedAudioBuffers<T> ();
}
-
+
return TimedAudioBuffers<T> (_buffers, _last_pull);
}
+
+ void
+ clear (DCPTime t)
+ {
+ _last_pull = t;
+ _buffers.reset (new AudioBuffers (_buffers->channels(), 0));
+ }
private:
boost::shared_ptr<AudioBuffers> _buffers;
diff --git a/src/lib/content.cc b/src/lib/content.cc
index d835a5b05..fa0031abf 100644
--- a/src/lib/content.cc
+++ b/src/lib/content.cc
@@ -54,7 +54,7 @@ Content::Content (shared_ptr<const Film> f)
}
-Content::Content (shared_ptr<const Film> f, Time p)
+Content::Content (shared_ptr<const Film> f, DCPTime p)
: _film (f)
, _position (p)
, _trim_start (0)
@@ -83,9 +83,9 @@ Content::Content (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
_paths.push_back ((*i)->content ());
}
_digest = node->string_child ("Digest");
- _position = node->number_child<Time> ("Position");
- _trim_start = node->number_child<Time> ("TrimStart");
- _trim_end = node->number_child<Time> ("TrimEnd");
+ _position = node->number_child<DCPTime> ("Position");
+ _trim_start = node->number_child<DCPTime> ("TrimStart");
+ _trim_end = node->number_child<DCPTime> ("TrimEnd");
}
Content::Content (shared_ptr<const Film> f, vector<shared_ptr<Content> > c)
@@ -146,7 +146,7 @@ Content::signal_changed (int p)
}
void
-Content::set_position (Time p)
+Content::set_position (DCPTime p)
{
{
boost::mutex::scoped_lock lm (_mutex);
@@ -157,7 +157,7 @@ Content::set_position (Time p)
}
void
-Content::set_trim_start (Time t)
+Content::set_trim_start (DCPTime t)
{
{
boost::mutex::scoped_lock lm (_mutex);
@@ -168,7 +168,7 @@ Content::set_trim_start (Time t)
}
void
-Content::set_trim_end (Time t)
+Content::set_trim_end (DCPTime t)
{
{
boost::mutex::scoped_lock lm (_mutex);
@@ -200,7 +200,7 @@ Content::technical_summary () const
return String::compose ("%1 %2 %3", path_summary(), digest(), position());
}
-Time
+DCPTime
Content::length_after_trim () const
{
return full_length() - trim_start() - trim_end();
@@ -210,7 +210,7 @@ Content::length_after_trim () const
* @return true if this time is trimmed by our trim settings.
*/
bool
-Content::trimmed (Time t) const
+Content::trimmed (DCPTime t) const
{
return (t < trim_start() || t > (full_length() - trim_end ()));
}
diff --git a/src/lib/content.h b/src/lib/content.h
index 4ee7c267f..965b33b22 100644
--- a/src/lib/content.h
+++ b/src/lib/content.h
@@ -49,7 +49,7 @@ class Content : public boost::enable_shared_from_this<Content>, public boost::no
{
public:
Content (boost::shared_ptr<const Film>);
- Content (boost::shared_ptr<const Film>, Time);
+ Content (boost::shared_ptr<const Film>, DCPTime);
Content (boost::shared_ptr<const Film>, boost::filesystem::path);
Content (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
Content (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
@@ -60,7 +60,7 @@ public:
virtual std::string technical_summary () const;
virtual std::string information () const = 0;
virtual void as_xml (xmlpp::Node *) const;
- virtual Time full_length () const = 0;
+ virtual DCPTime full_length () const = 0;
virtual std::string identifier () const;
boost::shared_ptr<Content> clone () const;
@@ -92,41 +92,41 @@ public:
return _digest;
}
- void set_position (Time);
+ void set_position (DCPTime);
- /** Time that this content starts; i.e. the time that the first
+ /** DCPTime that this content starts; i.e. the time that the first
* bit of the content (trimmed or not) will happen.
*/
- Time position () const {
+ DCPTime position () const {
boost::mutex::scoped_lock lm (_mutex);
return _position;
}
- void set_trim_start (Time);
+ void set_trim_start (DCPTime);
- Time trim_start () const {
+ DCPTime trim_start () const {
boost::mutex::scoped_lock lm (_mutex);
return _trim_start;
}
- void set_trim_end (Time);
+ void set_trim_end (DCPTime);
- Time trim_end () const {
+ DCPTime trim_end () const {
boost::mutex::scoped_lock lm (_mutex);
return _trim_end;
}
- Time end () const {
+ DCPTime end () const {
return position() + length_after_trim() - 1;
}
- Time length_after_trim () const;
+ DCPTime length_after_trim () const;
void set_change_signals_frequent (bool f) {
_change_signals_frequent = f;
}
- bool trimmed (Time) const;
+ bool trimmed (DCPTime) const;
boost::signals2::signal<void (boost::weak_ptr<Content>, int, bool)> Changed;
@@ -145,9 +145,9 @@ protected:
private:
std::string _digest;
- Time _position;
- Time _trim_start;
- Time _trim_end;
+ DCPTime _position;
+ DCPTime _trim_start;
+ DCPTime _trim_end;
bool _change_signals_frequent;
};
diff --git a/src/lib/decoded.h b/src/lib/decoded.h
new file mode 100644
index 000000000..1de2ff79e
--- /dev/null
+++ b/src/lib/decoded.h
@@ -0,0 +1,116 @@
+/*
+ Copyright (C) 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.
+
+*/
+
+#ifndef DCPOMATIC_LIB_DECODED_H
+#define DCPOMATIC_LIB_DECODED_H
+
+#include "types.h"
+#include "rect.h"
+
+class Image;
+
+class Decoded
+{
+public:
+ Decoded ()
+ : content_time (0)
+ , dcp_time (0)
+ {}
+
+ Decoded (DCPTime ct)
+ : content_time (ct)
+ , dcp_time (0)
+ {}
+
+ virtual ~Decoded () {}
+
+ virtual void set_dcp_times (float speed_up, DCPTime offset) = 0;
+
+ ContentTime content_time;
+ DCPTime dcp_time;
+};
+
+/** One frame of video from a VideoDecoder */
+class DecodedVideo : public Decoded
+{
+public:
+ DecodedVideo ()
+ : eyes (EYES_BOTH)
+ , same (false)
+ {}
+
+ DecodedVideo (boost::shared_ptr<const Image> im, Eyes e, bool s, ContentTime ct)
+ : Decoded (ct)
+ , image (im)
+ , eyes (e)
+ , same (s)
+ {}
+
+ void set_dcp_times (float speed_up, DCPTime offset) {
+ dcp_time = rint (content_time / speed_up) + offset;
+ }
+
+ boost::shared_ptr<const Image> image;
+ Eyes eyes;
+ bool same;
+};
+
+class DecodedAudio : public Decoded
+{
+public:
+ DecodedAudio (boost::shared_ptr<const AudioBuffers> d, ContentTime ct)
+ : Decoded (ct)
+ , data (d)
+ {}
+
+ void set_dcp_times (float speed_up, DCPTime offset) {
+ dcp_time = rint (content_time / speed_up) + offset;
+ }
+
+ boost::shared_ptr<const AudioBuffers> data;
+};
+
+class DecodedSubtitle : public Decoded
+{
+public:
+ DecodedSubtitle ()
+ : content_time_to (0)
+ , dcp_time_to (0)
+ {}
+
+ DecodedSubtitle (boost::shared_ptr<Image> im, dcpomatic::Rect<double> r, ContentTime f, ContentTime t)
+ : Decoded (f)
+ , image (im)
+ , rect (r)
+ , content_time_to (t)
+ , dcp_time_to (0)
+ {}
+
+ void set_dcp_times (float speed_up, DCPTime offset) {
+ dcp_time = rint (content_time / speed_up) + offset;
+ dcp_time_to = rint (content_time_to / speed_up) + offset;
+ }
+
+ boost::shared_ptr<Image> image;
+ dcpomatic::Rect<double> rect;
+ ContentTime content_time_to;
+ DCPTime dcp_time_to;
+};
+
+#endif
diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc
index 3f4cda6eb..4e136d619 100644
--- a/src/lib/decoder.cc
+++ b/src/lib/decoder.cc
@@ -23,9 +23,11 @@
#include "film.h"
#include "decoder.h"
+#include "decoded.h"
#include "i18n.h"
+using std::cout;
using boost::shared_ptr;
/** @param f Film.
@@ -33,6 +35,36 @@ using boost::shared_ptr;
*/
Decoder::Decoder (shared_ptr<const Film> f)
: _film (f)
+ , _done (false)
{
}
+
+shared_ptr<Decoded>
+Decoder::peek ()
+{
+ while (!_done && _pending.empty ()) {
+ _done = pass ();
+ }
+
+ if (_done) {
+ return shared_ptr<Decoded> ();
+ }
+
+ return _pending.front ();
+}
+
+void
+Decoder::consume ()
+{
+ if (!_pending.empty ()) {
+ _pending.pop_front ();
+ }
+}
+
+void
+Decoder::seek (ContentTime, bool)
+{
+ _pending.clear ();
+ _done = false;
+}
diff --git a/src/lib/decoder.h b/src/lib/decoder.h
index d67592ed8..6646b0e76 100644
--- a/src/lib/decoder.h
+++ b/src/lib/decoder.h
@@ -27,8 +27,10 @@
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/utility.hpp>
+#include "types.h"
class Film;
+class Decoded;
/** @class Decoder.
* @brief Parent class for decoders of content.
@@ -39,18 +41,32 @@ public:
Decoder (boost::shared_ptr<const Film>);
virtual ~Decoder () {}
- /** Perform one decode pass of the content, which may or may not
- * cause the object to emit some data.
+ /** Seek so that the next get_*() will yield the next thing
+ * (video/sound frame, subtitle etc.) at or after the requested
+ * time. Pass accurate = true to try harder to get close to
+ * the request.
*/
- virtual void pass () = 0;
- virtual bool done () const = 0;
+ virtual void seek (ContentTime time, bool accurate);
+
+ boost::shared_ptr<Decoded> peek ();
+ void consume ();
protected:
+ /** Perform one decode pass of the content, which may or may not
+ * result in a complete quantum (Decoded object) of decoded stuff
+ * being made ready.
+ * @return true if the decoder is done (i.e. no more data will be
+ * produced by any future calls to pass() without a seek() first).
+ */
+ virtual bool pass () = 0;
virtual void flush () {};
/** The Film that we are decoding in */
boost::weak_ptr<const Film> _film;
+
+ std::list<boost::shared_ptr<Decoded> > _pending;
+ bool _done;
};
#endif
diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc
index eff38b6a5..475c230da 100644
--- a/src/lib/encoder.cc
+++ b/src/lib/encoder.cc
@@ -216,7 +216,7 @@ Encoder::process_video (shared_ptr<PlayerImage> image, Eyes eyes, ColourConversi
TIMING ("adding to queue of %1", _queue.size ());
_queue.push_back (shared_ptr<DCPVideoFrame> (
new DCPVideoFrame (
- image->image(), _video_frames_out, eyes, conversion, _film->video_frame_rate(),
+ image->image(PIX_FMT_RGB24, false), _video_frames_out, eyes, conversion, _film->video_frame_rate(),
_film->j2k_bandwidth(), _film->resolution(), _film->log()
)
));
diff --git a/src/lib/ffmpeg.cc b/src/lib/ffmpeg.cc
index e5e5f317a..53d12419a 100644
--- a/src/lib/ffmpeg.cc
+++ b/src/lib/ffmpeg.cc
@@ -191,6 +191,10 @@ FFmpeg::video_codec_context () const
AVCodecContext *
FFmpeg::audio_codec_context () const
{
+ if (!_ffmpeg_content->audio_stream ()) {
+ return 0;
+ }
+
return _ffmpeg_content->audio_stream()->stream(_format_context)->codec;
}
diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc
index 9533315a5..65a8d24f1 100644
--- a/src/lib/ffmpeg_content.cc
+++ b/src/lib/ffmpeg_content.cc
@@ -310,16 +310,15 @@ FFmpegContent::output_audio_frame_rate () const
/* Resample to a DCI-approved sample rate */
double t = dcp_audio_frame_rate (content_audio_frame_rate ());
- FrameRateConversion frc (video_frame_rate(), film->video_frame_rate());
+ FrameRateChange frc (video_frame_rate(), film->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->video_frame_rate();
+ t /= frc.speed_up;
}
return rint (t);
@@ -446,13 +445,13 @@ FFmpegSubtitleStream::as_xml (xmlpp::Node* root) const
FFmpegStream::as_xml (root);
}
-Time
+DCPTime
FFmpegContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
- FrameRateConversion frc (video_frame_rate (), film->video_frame_rate ());
+ FrameRateChange frc (video_frame_rate (), film->video_frame_rate ());
return video_length() * frc.factor() * TIME_HZ / film->video_frame_rate ();
}
diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h
index 7ff159b85..ba73c0f9b 100644
--- a/src/lib/ffmpeg_content.h
+++ b/src/lib/ffmpeg_content.h
@@ -134,7 +134,7 @@ public:
std::string technical_summary () const;
std::string information () const;
void as_xml (xmlpp::Node *) const;
- Time full_length () const;
+ DCPTime full_length () const;
std::string identifier () const;
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index 5a1b78762..298284512 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -70,7 +70,6 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC
, _decode_audio (audio)
, _video_pts_offset (0)
, _audio_pts_offset (0)
- , _just_sought (false)
{
setup_subtitle ();
@@ -89,7 +88,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC
*/
bool const have_video = video && c->first_video();
- bool const have_audio = audio && c->audio_stream() && c->audio_stream()->first_audio;
+ bool const have_audio = _decode_audio && c->audio_stream () && c->audio_stream()->first_audio;
/* First, make one of them start at 0 */
@@ -143,12 +142,10 @@ FFmpegDecoder::flush ()
decode_audio_packet ();
}
- /* Stop us being asked for any more data */
- _video_position = _ffmpeg_content->video_length ();
- _audio_position = _ffmpeg_content->audio_length ();
+ AudioDecoder::flush ();
}
-void
+bool
FFmpegDecoder::pass ()
{
int r = av_read_frame (_format_context, &_packet);
@@ -164,7 +161,7 @@ FFmpegDecoder::pass ()
}
flush ();
- return;
+ return true;
}
avcodec_get_frame_defaults (_frame);
@@ -183,6 +180,7 @@ FFmpegDecoder::pass ()
}
av_free_packet (&_packet);
+ return false;
}
/** @param data pointer to array of pointers to buffers.
@@ -297,70 +295,131 @@ FFmpegDecoder::bytes_per_audio_sample () const
return av_get_bytes_per_sample (audio_sample_format ());
}
-void
-FFmpegDecoder::seek (VideoContent::Frame frame, bool accurate)
+int
+FFmpegDecoder::minimal_run (boost::function<bool (optional<ContentTime>, optional<ContentTime>, int)> finished)
{
- double const time_base = av_q2d (_format_context->streams[_video_stream]->time_base);
+ int frames_read = 0;
+ optional<ContentTime> last_video;
+ optional<ContentTime> last_audio;
- /* If we are doing an accurate seek, our initial shot will be 5 frames (5 being
- a number plucked from the air) earlier than we want to end up. The loop below
- will hopefully then step through to where we want to be.
- */
- int initial = frame;
+ while (!finished (last_video, last_audio, frames_read)) {
+ int r = av_read_frame (_format_context, &_packet);
+ if (r < 0) {
+ /* We should flush our decoders here, possibly yielding a few more frames,
+ but the consequence of having to do that is too hideous to contemplate.
+ Instead we give up and say that you can't seek too close to the end
+ of a file.
+ */
+ return frames_read;
+ }
- if (accurate) {
- initial -= 5;
- }
+ ++frames_read;
+
+ double const time_base = av_q2d (_format_context->streams[_packet.stream_index]->time_base);
+
+ if (_packet.stream_index == _video_stream) {
- if (initial < 0) {
- initial = 0;
+ avcodec_get_frame_defaults (_frame);
+
+ int finished = 0;
+ r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet);
+ if (r >= 0 && finished) {
+ last_video = rint (
+ (av_frame_get_best_effort_timestamp (_frame) * time_base + _video_pts_offset) * TIME_HZ
+ );
+ }
+
+ } else if (_ffmpeg_content->audio_stream() && _packet.stream_index == _ffmpeg_content->audio_stream()->index (_format_context)) {
+ AVPacket copy_packet = _packet;
+ while (copy_packet.size > 0) {
+
+ int finished;
+ r = avcodec_decode_audio4 (audio_codec_context(), _frame, &finished, &_packet);
+ if (r >= 0 && finished) {
+ last_audio = rint (
+ (av_frame_get_best_effort_timestamp (_frame) * time_base + _audio_pts_offset) * TIME_HZ
+ );
+ }
+
+ copy_packet.data += r;
+ copy_packet.size -= r;
+ }
+ }
+
+ av_free_packet (&_packet);
}
- /* Initial seek time in the stream's timebase */
- int64_t const initial_vt = ((initial / _ffmpeg_content->video_frame_rate()) - _video_pts_offset) / time_base;
+ return frames_read;
+}
+
+bool
+FFmpegDecoder::seek_overrun_finished (ContentTime seek, optional<ContentTime> last_video, optional<ContentTime> last_audio) const
+{
+ return (last_video && last_video.get() >= seek) || (last_audio && last_audio.get() >= seek);
+}
+
+bool
+FFmpegDecoder::seek_final_finished (int n, int done) const
+{
+ return n == done;
+}
+
+void
+FFmpegDecoder::seek_and_flush (ContentTime t)
+{
+ int64_t s = ((double (t) / TIME_HZ) - _video_pts_offset) /
+ av_q2d (_format_context->streams[_video_stream]->time_base);
+
+ if (_ffmpeg_content->audio_stream ()) {
+ s = min (
+ s, int64_t (
+ ((double (t) / TIME_HZ) - _audio_pts_offset) /
+ av_q2d (_ffmpeg_content->audio_stream()->stream(_format_context)->time_base)
+ )
+ );
+ }
- av_seek_frame (_format_context, _video_stream, initial_vt, AVSEEK_FLAG_BACKWARD);
+ av_seek_frame (_format_context, _video_stream, s, AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers (video_codec_context());
+ if (audio_codec_context ()) {
+ avcodec_flush_buffers (audio_codec_context ());
+ }
if (_subtitle_codec_context) {
avcodec_flush_buffers (_subtitle_codec_context);
}
+}
- _just_sought = true;
- _video_position = frame;
+void
+FFmpegDecoder::seek (ContentTime time, bool accurate)
+{
+ Decoder::seek (time, accurate);
- if (frame == 0 || !accurate) {
- /* We're already there, or we're as close as we need to be */
- return;
+ /* If we are doing an accurate seek, our initial shot will be 200ms (200 being
+ a number plucked from the air) earlier than we want to end up. The loop below
+ will hopefully then step through to where we want to be.
+ */
+
+ ContentTime pre_roll = accurate ? (0.2 * TIME_HZ) : 0;
+ ContentTime initial_seek = time - pre_roll;
+ if (initial_seek < 0) {
+ initial_seek = 0;
}
+
+ /* Initial seek time in the video stream's timebase */
- while (1) {
- int r = av_read_frame (_format_context, &_packet);
- if (r < 0) {
- return;
- }
+ seek_and_flush (initial_seek);
- if (_packet.stream_index != _video_stream) {
- av_free_packet (&_packet);
- continue;
- }
-
- avcodec_get_frame_defaults (_frame);
-
- int finished = 0;
- r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet);
- if (r >= 0 && finished) {
- _video_position = rint (
- (av_frame_get_best_effort_timestamp (_frame) * time_base + _video_pts_offset) * _ffmpeg_content->video_frame_rate()
- );
+ if (!accurate) {
+ /* That'll do */
+ return;
+ }
- if (_video_position >= (frame - 1)) {
- av_free_packet (&_packet);
- break;
- }
- }
-
- av_free_packet (&_packet);
+ int const N = minimal_run (boost::bind (&FFmpegDecoder::seek_overrun_finished, this, time, _1, _2));
+
+ seek_and_flush (initial_seek);
+ if (N > 0) {
+ minimal_run (boost::bind (&FFmpegDecoder::seek_final_finished, this, N - 1, _3));
}
}
@@ -377,6 +436,7 @@ FFmpegDecoder::decode_audio_packet ()
int frame_finished;
int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, &copy_packet);
+
if (decode_result < 0) {
shared_ptr<const Film> film = _film.lock ();
assert (film);
@@ -385,31 +445,16 @@ FFmpegDecoder::decode_audio_packet ()
}
if (frame_finished) {
-
- if (_audio_position == 0) {
- /* Where we are in the source, in seconds */
- double const pts = av_q2d (_format_context->streams[copy_packet.stream_index]->time_base)
- * av_frame_get_best_effort_timestamp(_frame) + _audio_pts_offset;
-
- if (pts > 0) {
- /* Emit some silence */
- shared_ptr<AudioBuffers> silence (
- new AudioBuffers (
- _ffmpeg_content->audio_channels(),
- pts * _ffmpeg_content->content_audio_frame_rate()
- )
- );
-
- silence->make_silent ();
- audio (silence, _audio_position);
- }
- }
-
+ ContentTime const t = rint (
+ (av_q2d (_format_context->streams[copy_packet.stream_index]->time_base)
+ * av_frame_get_best_effort_timestamp(_frame) + _audio_pts_offset) * TIME_HZ
+ );
+
int const data_size = av_samples_get_buffer_size (
0, audio_codec_context()->channels, _frame->nb_samples, audio_sample_format (), 1
);
- audio (deinterleave_audio (_frame->data, data_size), _audio_position);
+ audio (deinterleave_audio (_frame->data, data_size), t);
}
copy_packet.data += decode_result;
@@ -458,45 +503,8 @@ FFmpegDecoder::decode_video_packet ()
}
if (i->second != AV_NOPTS_VALUE) {
-
- double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _video_pts_offset;
-
- if (_just_sought) {
- /* We just did a seek, so disable any attempts to correct for where we
- are / should be.
- */
- _video_position = rint (pts * _ffmpeg_content->video_frame_rate ());
- _just_sought = false;
- }
-
- double const next = _video_position / _ffmpeg_content->video_frame_rate();
- double const one_frame = 1 / _ffmpeg_content->video_frame_rate ();
- double delta = pts - next;
-
- while (delta > one_frame) {
- /* This PTS is more than one frame forward in time of where we think we should be; emit
- a black frame.
- */
-
- /* XXX: I think this should be a copy of the last frame... */
- boost::shared_ptr<Image> black (
- new Image (
- static_cast<AVPixelFormat> (_frame->format),
- libdcp::Size (video_codec_context()->width, video_codec_context()->height),
- true
- )
- );
-
- black->make_black ();
- video (image, false, _video_position);
- delta -= one_frame;
- }
-
- if (delta > -one_frame) {
- /* This PTS is within a frame of being right; emit this (otherwise it will be dropped) */
- video (image, false, _video_position);
- }
-
+ ContentTime const t = rint ((i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _video_pts_offset) * TIME_HZ);
+ video (image, false, t);
} else {
shared_ptr<const Film> film = _film.lock ();
assert (film);
@@ -529,14 +537,6 @@ FFmpegDecoder::setup_subtitle ()
}
}
-bool
-FFmpegDecoder::done () const
-{
- bool const vd = !_decode_video || (_video_position >= _ffmpeg_content->video_length());
- bool const ad = !_decode_audio || !_ffmpeg_content->audio_stream() || (_audio_position >= _ffmpeg_content->audio_length());
- return vd && ad;
-}
-
void
FFmpegDecoder::decode_subtitle_packet ()
{
@@ -562,8 +562,8 @@ FFmpegDecoder::decode_subtitle_packet ()
double const packet_time = static_cast<double> (sub.pts) / AV_TIME_BASE;
/* hence start time for this sub */
- Time const from = (packet_time + (double (sub.start_display_time) / 1e3)) * TIME_HZ;
- Time const to = (packet_time + (double (sub.end_display_time) / 1e3)) * TIME_HZ;
+ ContentTime const from = (packet_time + (double (sub.start_display_time) / 1e3)) * TIME_HZ;
+ ContentTime const to = (packet_time + (double (sub.end_display_time) / 1e3)) * TIME_HZ;
AVSubtitleRect const * rect = sub.rects[0];
diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h
index 11f83ed97..55b624bd6 100644
--- a/src/lib/ffmpeg_decoder.h
+++ b/src/lib/ffmpeg_decoder.h
@@ -51,15 +51,12 @@ public:
FFmpegDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const FFmpegContent>, bool video, bool audio);
~FFmpegDecoder ();
- void pass ();
- void seek (VideoContent::Frame, bool);
- bool done () const;
+ void seek (ContentTime time, bool);
private:
friend class ::ffmpeg_pts_offset_test;
- static double compute_pts_offset (double, double, float);
-
+ bool pass ();
void flush ();
void setup_subtitle ();
@@ -74,6 +71,11 @@ private:
void maybe_add_subtitle ();
boost::shared_ptr<AudioBuffers> deinterleave_audio (uint8_t** data, int size);
+ bool seek_overrun_finished (ContentTime, boost::optional<ContentTime>, boost::optional<ContentTime>) const;
+ bool seek_final_finished (int, int) const;
+ int minimal_run (boost::function<bool (boost::optional<ContentTime>, boost::optional<ContentTime>, int)>);
+ void seek_and_flush (int64_t);
+
AVCodecContext* _subtitle_codec_context; ///< may be 0 if there is no subtitle
AVCodec* _subtitle_codec; ///< may be 0 if there is no subtitle
@@ -85,5 +87,4 @@ private:
double _video_pts_offset;
double _audio_pts_offset;
- bool _just_sought;
};
diff --git a/src/lib/film.cc b/src/lib/film.cc
index 1bf35cc5f..98b7bf6b9 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -842,7 +842,7 @@ Film::move_content_later (shared_ptr<Content> c)
_playlist->move_later (c);
}
-Time
+DCPTime
Film::length () const
{
return _playlist->length ();
@@ -860,6 +860,12 @@ Film::best_video_frame_rate () const
return _playlist->best_dcp_frame_rate ();
}
+FrameRateChange
+Film::active_frame_rate_change (DCPTime t) const
+{
+ return _playlist->active_frame_rate_change (t, video_frame_rate ());
+}
+
void
Film::playlist_content_changed (boost::weak_ptr<Content> c, int p)
{
@@ -879,24 +885,24 @@ Film::playlist_changed ()
}
OutputAudioFrame
-Film::time_to_audio_frames (Time t) const
+Film::time_to_audio_frames (DCPTime t) const
{
return t * audio_frame_rate () / TIME_HZ;
}
OutputVideoFrame
-Film::time_to_video_frames (Time t) const
+Film::time_to_video_frames (DCPTime t) const
{
return t * video_frame_rate () / TIME_HZ;
}
-Time
+DCPTime
Film::audio_frames_to_time (OutputAudioFrame f) const
{
return f * TIME_HZ / audio_frame_rate ();
}
-Time
+DCPTime
Film::video_frames_to_time (OutputVideoFrame f) const
{
return f * TIME_HZ / video_frame_rate ();
diff --git a/src/lib/film.h b/src/lib/film.h
index e318f7724..f1564e83b 100644
--- a/src/lib/film.h
+++ b/src/lib/film.h
@@ -103,17 +103,18 @@ public:
OutputAudioFrame audio_frame_rate () 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;
+ OutputAudioFrame time_to_audio_frames (DCPTime) const;
+ OutputVideoFrame time_to_video_frames (DCPTime) const;
+ DCPTime video_frames_to_time (OutputVideoFrame) const;
+ DCPTime audio_frames_to_time (OutputAudioFrame) const;
/* Proxies for some Playlist methods */
ContentList content () const;
- Time length () const;
+ DCPTime length () const;
bool has_subtitles () const;
OutputVideoFrame best_video_frame_rate () const;
+ FrameRateChange active_frame_rate_change (DCPTime) const;
libdcp::KDM
make_kdm (
diff --git a/src/lib/image.cc b/src/lib/image.cc
index 95bf2b04d..e5f626c24 100644
--- a/src/lib/image.cc
+++ b/src/lib/image.cc
@@ -31,6 +31,7 @@ extern "C" {
#include "image.h"
#include "exceptions.h"
#include "scaler.h"
+#include "timer.h"
using std::string;
using std::min;
@@ -94,9 +95,9 @@ Image::crop_scale_window (Crop crop, libdcp::Size inter_size, libdcp::Size out_s
libdcp::Size cropped_size = crop.apply (size ());
struct SwsContext* scale_context = sws_getContext (
- cropped_size.width, cropped_size.height, pixel_format(),
- inter_size.width, inter_size.height, out_format,
- scaler->ffmpeg_id (), 0, 0, 0
+ cropped_size.width, cropped_size.height, pixel_format(),
+ inter_size.width, inter_size.height, out_format,
+ scaler->ffmpeg_id (), 0, 0, 0
);
uint8_t* scale_in_data[components()];
@@ -375,8 +376,18 @@ Image::make_black ()
void
Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
{
- /* Only implemented for RGBA onto RGB24 so far */
- assert (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA);
+ int this_bpp = 0;
+ int other_bpp = 0;
+
+ if (_pixel_format == PIX_FMT_BGRA && other->pixel_format() == PIX_FMT_RGBA) {
+ this_bpp = 4;
+ other_bpp = 4;
+ } else if (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA) {
+ this_bpp = 3;
+ other_bpp = 4;
+ } else {
+ assert (false);
+ }
int start_tx = position.x;
int start_ox = 0;
@@ -395,15 +406,15 @@ Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
}
for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) {
- uint8_t* tp = data()[0] + ty * stride()[0] + position.x * 3;
+ uint8_t* tp = data()[0] + ty * stride()[0] + position.x * this_bpp;
uint8_t* op = other->data()[0] + oy * other->stride()[0];
for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) {
float const alpha = float (op[3]) / 255;
tp[0] = (tp[0] * (1 - alpha)) + op[0] * alpha;
tp[1] = (tp[1] * (1 - alpha)) + op[1] * alpha;
tp[2] = (tp[2] * (1 - alpha)) + op[2] * alpha;
- tp += 3;
- op += 4;
+ tp += this_bpp;
+ op += other_bpp;
}
}
}
diff --git a/src/lib/image_content.cc b/src/lib/image_content.cc
index b05fa6b8d..0f9526071 100644
--- a/src/lib/image_content.cc
+++ b/src/lib/image_content.cc
@@ -120,13 +120,13 @@ ImageContent::set_video_length (VideoContent::Frame len)
signal_changed (ContentProperty::LENGTH);
}
-Time
+DCPTime
ImageContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
- FrameRateConversion frc (video_frame_rate(), film->video_frame_rate ());
+ FrameRateChange frc (video_frame_rate(), film->video_frame_rate ());
return video_length() * frc.factor() * TIME_HZ / video_frame_rate();
}
diff --git a/src/lib/image_content.h b/src/lib/image_content.h
index 47c5a20e3..88c178faa 100644
--- a/src/lib/image_content.h
+++ b/src/lib/image_content.h
@@ -41,7 +41,7 @@ public:
std::string summary () const;
std::string technical_summary () const;
void as_xml (xmlpp::Node *) const;
- Time full_length () const;
+ DCPTime full_length () const;
std::string identifier () const;
diff --git a/src/lib/image_decoder.cc b/src/lib/image_decoder.cc
index a7999c02a..a5ca67e0d 100644
--- a/src/lib/image_decoder.cc
+++ b/src/lib/image_decoder.cc
@@ -36,20 +36,22 @@ ImageDecoder::ImageDecoder (shared_ptr<const Film> f, shared_ptr<const ImageCont
: Decoder (f)
, VideoDecoder (f, c)
, _image_content (c)
+ , _video_position (0)
{
}
-void
+bool
ImageDecoder::pass ()
{
if (_video_position >= _image_content->video_length ()) {
- return;
+ return true;
}
if (_image && _image_content->still ()) {
- video (_image, true, _video_position);
- return;
+ video (_image, true, _video_position * TIME_HZ / _video_content->video_frame_rate ());
+ ++_video_position;
+ return false;
}
Magick::Image* magick_image = 0;
@@ -80,17 +82,16 @@ ImageDecoder::pass ()
delete magick_image;
- video (_image, false, _video_position);
-}
+ video (_image, false, _video_position * TIME_HZ / _video_content->video_frame_rate ());
+ ++_video_position;
-void
-ImageDecoder::seek (VideoContent::Frame frame, bool)
-{
- _video_position = frame;
+ return false;
}
-bool
-ImageDecoder::done () const
+void
+ImageDecoder::seek (ContentTime time, bool accurate)
{
- return _video_position >= _image_content->video_length ();
+ Decoder::seek (time, accurate);
+
+ _video_position = rint (time * _video_content->video_frame_rate() / TIME_HZ);
}
diff --git a/src/lib/image_decoder.h b/src/lib/image_decoder.h
index c7500243e..f4d81dfb5 100644
--- a/src/lib/image_decoder.h
+++ b/src/lib/image_decoder.h
@@ -34,14 +34,13 @@ public:
return _image_content;
}
- /* Decoder */
-
- void pass ();
- void seek (VideoContent::Frame, bool);
- bool done () const;
+ void seek (ContentTime, bool);
private:
+ bool pass ();
+
boost::shared_ptr<const ImageContent> _image_content;
boost::shared_ptr<Image> _image;
+ ContentTime _video_position;
};
diff --git a/src/lib/job.cc b/src/lib/job.cc
index 9981934ec..05a90524c 100644
--- a/src/lib/job.cc
+++ b/src/lib/job.cc
@@ -198,7 +198,7 @@ Job::set_state (State s)
}
}
-/** @return Time (in seconds) that this sub-job has been running */
+/** @return DCPTime (in seconds) that this sub-job has been running */
int
Job::elapsed_time () const
{
diff --git a/src/lib/player.cc b/src/lib/player.cc
index 09a437494..e3d88a54c 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -31,7 +31,6 @@
#include "job.h"
#include "image.h"
#include "ratio.h"
-#include "resampler.h"
#include "log.h"
#include "scaler.h"
@@ -45,69 +44,20 @@ using std::map;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::dynamic_pointer_cast;
+using boost::optional;
class Piece
{
public:
- Piece (shared_ptr<Content> c)
- : content (c)
- , video_position (c->position ())
- , audio_position (c->position ())
- , repeat_to_do (0)
- , repeat_done (0)
- {}
-
- Piece (shared_ptr<Content> c, shared_ptr<Decoder> d)
+ Piece (shared_ptr<Content> c, shared_ptr<Decoder> d, FrameRateChange f)
: content (c)
, decoder (d)
- , video_position (c->position ())
- , audio_position (c->position ())
+ , frc (f)
{}
- /** Set this piece to repeat a video frame a given number of times */
- void set_repeat (IncomingVideo video, int num)
- {
- repeat_video = video;
- repeat_to_do = num;
- repeat_done = 0;
- }
-
- void reset_repeat ()
- {
- repeat_video.image.reset ();
- repeat_to_do = 0;
- repeat_done = 0;
- }
-
- bool repeating () const
- {
- return repeat_done != repeat_to_do;
- }
-
- void repeat (Player* player)
- {
- player->process_video (
- repeat_video.weak_piece,
- repeat_video.image,
- repeat_video.eyes,
- repeat_done > 0,
- repeat_video.frame,
- (repeat_done + 1) * (TIME_HZ / player->_film->video_frame_rate ())
- );
-
- ++repeat_done;
- }
-
shared_ptr<Content> content;
shared_ptr<Decoder> decoder;
- /** Time of the last video we emitted relative to the start of the DCP */
- Time video_position;
- /** Time of the last audio we emitted relative to the start of the DCP */
- Time audio_position;
-
- IncomingVideo repeat_video;
- int repeat_to_do;
- int repeat_done;
+ FrameRateChange frc;
};
Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
@@ -120,6 +70,8 @@ Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
, _audio_position (0)
, _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1))
, _last_emit_was_black (false)
+ , _just_did_inaccurate_seek (false)
+ , _approximate_size (false)
{
_playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
_playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
@@ -146,110 +98,131 @@ Player::pass ()
setup_pieces ();
}
- Time earliest_t = TIME_MAX;
- shared_ptr<Piece> earliest;
- enum {
- VIDEO,
- AUDIO
- } type = VIDEO;
+ /* Interrogate all our pieces to find the one with the earliest decoded data */
+
+ shared_ptr<Piece> earliest_piece;
+ shared_ptr<Decoded> earliest_decoded;
+ DCPTime earliest_time = TIME_MAX;
+ DCPTime earliest_audio = TIME_MAX;
for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
- if ((*i)->decoder->done ()) {
- continue;
- }
- shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> ((*i)->decoder);
- shared_ptr<AudioDecoder> ad = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
+ shared_ptr<Decoded> dec = (*i)->decoder->peek ();
- if (_video && vd) {
- if ((*i)->video_position < earliest_t) {
- earliest_t = (*i)->video_position;
- earliest = *i;
- type = VIDEO;
- }
+ if (dec) {
+ dec->set_dcp_times ((*i)->frc.speed_up, (*i)->content->position());
}
- if (_audio && ad && ad->has_audio ()) {
- if ((*i)->audio_position < earliest_t) {
- earliest_t = (*i)->audio_position;
- earliest = *i;
- type = AUDIO;
- }
+ if (dec && dec->dcp_time < earliest_time) {
+ earliest_piece = *i;
+ earliest_decoded = dec;
+ earliest_time = dec->dcp_time;
}
- }
- if (!earliest) {
+ if (dynamic_pointer_cast<DecodedAudio> (dec) && dec->dcp_time < earliest_audio) {
+ earliest_audio = dec->dcp_time;
+ }
+ }
+
+ if (!earliest_piece) {
flush ();
return true;
}
- switch (type) {
- case VIDEO:
- if (earliest_t > _video_position) {
- emit_black ();
- } else {
- if (earliest->repeating ()) {
- earliest->repeat (this);
+ if (earliest_audio != TIME_MAX) {
+ TimedAudioBuffers<DCPTime> tb = _audio_merger.pull (earliest_audio);
+ Audio (tb.audio, tb.time);
+ /* This assumes that the audio_frames_to_time conversion is exact
+ so that there are no accumulated errors caused by rounding.
+ */
+ _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
+ }
+
+ /* Emit the earliest thing */
+
+ shared_ptr<DecodedVideo> dv = dynamic_pointer_cast<DecodedVideo> (earliest_decoded);
+ shared_ptr<DecodedAudio> da = dynamic_pointer_cast<DecodedAudio> (earliest_decoded);
+ shared_ptr<DecodedSubtitle> ds = dynamic_pointer_cast<DecodedSubtitle> (earliest_decoded);
+
+ /* Will be set to false if we shouldn't consume the peeked DecodedThing */
+ bool consume = true;
+
+ /* This is the margin either side of _{video,audio}_position that we will accept
+ as a starting point for a frame consecutive to the previous.
+ */
+ DCPTime const margin = TIME_HZ / (2 * _film->video_frame_rate ());
+
+ if (dv && _video) {
+
+ if (_just_did_inaccurate_seek) {
+
+ /* Just emit; no subtlety */
+ emit_video (earliest_piece, dv);
+ step_video_position (dv);
+
+ } else if (dv->dcp_time - _video_position > margin) {
+
+ /* Too far ahead */
+
+ list<shared_ptr<Piece> >::iterator i = _pieces.begin();
+ while (i != _pieces.end() && ((*i)->content->position() >= _video_position || _video_position >= (*i)->content->end())) {
+ ++i;
+ }
+
+ if (i == _pieces.end() || !_last_incoming_video.video || !_have_valid_pieces) {
+ /* We're outside all video content */
+ emit_black ();
} else {
- earliest->decoder->pass ();
+ /* We're inside some video; repeat the frame */
+ _last_incoming_video.video->dcp_time = _video_position;
+ emit_video (_last_incoming_video.weak_piece, _last_incoming_video.video);
+ step_video_position (_last_incoming_video.video);
}
- }
- break;
- case AUDIO:
- if (earliest_t > _audio_position) {
- emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position));
+ consume = false;
+
+ } else if (abs (dv->dcp_time - _video_position) < margin) {
+ /* We're ok */
+ emit_video (earliest_piece, dv);
+ step_video_position (dv);
} else {
- earliest->decoder->pass ();
-
- if (earliest->decoder->done()) {
- shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (earliest->content);
- assert (ac);
- shared_ptr<Resampler> re = resampler (ac, false);
- if (re) {
- shared_ptr<const AudioBuffers> b = re->flush ();
- if (b->frames ()) {
- process_audio (earliest, b, ac->audio_length ());
- }
- }
- }
+ /* Too far behind: skip */
}
- break;
- }
- if (_audio) {
- boost::optional<Time> audio_done_up_to;
- for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
- if ((*i)->decoder->done ()) {
- continue;
- }
+ _just_did_inaccurate_seek = false;
- if (dynamic_pointer_cast<AudioDecoder> ((*i)->decoder)) {
- audio_done_up_to = min (audio_done_up_to.get_value_or (TIME_MAX), (*i)->audio_position);
- }
- }
+ } else if (da && _audio) {
- if (audio_done_up_to) {
- TimedAudioBuffers<Time> tb = _audio_merger.pull (audio_done_up_to.get ());
- Audio (tb.audio, tb.time);
- _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
+ if (da->dcp_time - _audio_position > margin) {
+ /* Too far ahead */
+ emit_silence (da->dcp_time - _audio_position);
+ consume = false;
+ } else if (abs (da->dcp_time - _audio_position) < margin) {
+ /* We're ok */
+ emit_audio (earliest_piece, da);
+ } else {
+ /* Too far behind: skip */
}
- }
+ } else if (ds && _video) {
+ _in_subtitle.piece = earliest_piece;
+ _in_subtitle.subtitle = ds;
+ update_subtitle ();
+ }
+
+ if (consume) {
+ earliest_piece->decoder->consume ();
+ }
+
return false;
}
-/** @param extra Amount of extra time to add to the content frame's time (for repeat) */
void
-Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, Eyes eyes, bool same, VideoContent::Frame frame, Time extra)
+Player::emit_video (weak_ptr<Piece> weak_piece, shared_ptr<DecodedVideo> video)
{
/* Keep a note of what came in so that we can repeat it if required */
_last_incoming_video.weak_piece = weak_piece;
- _last_incoming_video.image = image;
- _last_incoming_video.eyes = eyes;
- _last_incoming_video.same = same;
- _last_incoming_video.frame = frame;
- _last_incoming_video.extra = extra;
+ _last_incoming_video.video = video;
shared_ptr<Piece> piece = weak_piece.lock ();
if (!piece) {
@@ -259,23 +232,18 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image
shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
assert (content);
- FrameRateConversion frc (content->video_frame_rate(), _film->video_frame_rate());
- if (frc.skip && (frame % 2) == 1) {
- return;
- }
+ FrameRateChange frc (content->video_frame_rate(), _film->video_frame_rate());
- Time const relative_time = (frame * frc.factor() * TIME_HZ / _film->video_frame_rate());
- if (content->trimmed (relative_time)) {
- return;
- }
-
- Time const time = content->position() + relative_time + extra - content->trim_start ();
float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio();
- libdcp::Size const image_size = fit_ratio_within (ratio, _video_container_size);
+ libdcp::Size image_size = fit_ratio_within (ratio, _video_container_size);
+ if (_approximate_size) {
+ image_size.width &= ~3;
+ image_size.height &= ~3;
+ }
shared_ptr<PlayerImage> pi (
new PlayerImage (
- image,
+ video->image,
content->crop(),
image_size,
_video_container_size,
@@ -283,11 +251,15 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image
)
);
- if (_film->with_subtitles () && _out_subtitle.image && time >= _out_subtitle.from && time <= _out_subtitle.to) {
+ if (
+ _film->with_subtitles () &&
+ _out_subtitle.image &&
+ video->dcp_time >= _out_subtitle.from && video->dcp_time <= _out_subtitle.to
+ ) {
Position<int> const container_offset (
(_video_container_size.width - image_size.width) / 2,
- (_video_container_size.height - image_size.width) / 2
+ (_video_container_size.height - image_size.height) / 2
);
pi->set_subtitle (_out_subtitle.image, _out_subtitle.position + container_offset);
@@ -297,18 +269,25 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image
_last_video = piece->content;
#endif
- Video (pi, eyes, content->colour_conversion(), same, time);
-
+ Video (pi, video->eyes, content->colour_conversion(), video->same, video->dcp_time);
+
_last_emit_was_black = false;
- _video_position = piece->video_position = (time + TIME_HZ / _film->video_frame_rate());
+}
- if (frc.repeat > 1 && !piece->repeating ()) {
- piece->set_repeat (_last_incoming_video, frc.repeat - 1);
+void
+Player::step_video_position (shared_ptr<DecodedVideo> video)
+{
+ /* This is a bit of a hack; don't update _video_position if EYES_RIGHT is on its way */
+ if (video->eyes != EYES_LEFT) {
+ /* This assumes that the video_frames_to_time conversion is exact
+ so that there are no accumulated errors caused by rounding.
+ */
+ _video_position += _film->video_frames_to_time (1);
}
}
void
-Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame)
+Player::emit_audio (weak_ptr<Piece> weak_piece, shared_ptr<DecodedAudio> audio)
{
shared_ptr<Piece> piece = weak_piece.lock ();
if (!piece) {
@@ -320,61 +299,49 @@ Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers
/* Gain */
if (content->audio_gain() != 0) {
- shared_ptr<AudioBuffers> gain (new AudioBuffers (audio));
+ shared_ptr<AudioBuffers> gain (new AudioBuffers (audio->data));
gain->apply_gain (content->audio_gain ());
- audio = gain;
+ audio->data = gain;
}
- /* Resample */
- if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
- shared_ptr<Resampler> r = resampler (content, true);
- pair<shared_ptr<const AudioBuffers>, AudioContent::Frame> ro = r->run (audio, frame);
- audio = ro.first;
- frame = ro.second;
- }
-
- Time const relative_time = _film->audio_frames_to_time (frame);
-
- if (content->trimmed (relative_time)) {
+ if (content->trimmed (audio->dcp_time - content->position ())) {
return;
}
- Time time = content->position() + (content->audio_delay() * TIME_HZ / 1000) + relative_time - content->trim_start ();
-
/* Remap channels */
- shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames()));
+ shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->data->frames()));
dcp_mapped->make_silent ();
list<pair<int, libdcp::Channel> > map = content->audio_mapping().content_to_dcp ();
for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) {
- if (i->first < audio->channels() && i->second < dcp_mapped->channels()) {
- dcp_mapped->accumulate_channel (audio.get(), i->first, i->second);
+ if (i->first < audio->data->channels() && i->second < dcp_mapped->channels()) {
+ dcp_mapped->accumulate_channel (audio->data.get(), i->first, i->second);
}
}
- audio = dcp_mapped;
+ audio->data = dcp_mapped;
- /* We must cut off anything that comes before the start of all time */
- if (time < 0) {
- int const frames = - time * _film->audio_frame_rate() / TIME_HZ;
- if (frames >= audio->frames ()) {
+ /* Delay */
+ audio->dcp_time += content->audio_delay() * TIME_HZ / 1000;
+ if (audio->dcp_time < 0) {
+ int const frames = - audio->dcp_time * _film->audio_frame_rate() / TIME_HZ;
+ if (frames >= audio->data->frames ()) {
return;
}
- shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->channels(), audio->frames() - frames));
- trimmed->copy_from (audio.get(), audio->frames() - frames, frames, 0);
+ shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->data->channels(), audio->data->frames() - frames));
+ trimmed->copy_from (audio->data.get(), audio->data->frames() - frames, frames, 0);
- audio = trimmed;
- time = 0;
+ audio->data = trimmed;
+ audio->dcp_time = 0;
}
- _audio_merger.push (audio, time);
- piece->audio_position += _film->audio_frames_to_time (audio->frames ());
+ _audio_merger.push (audio->data, audio->dcp_time);
}
void
Player::flush ()
{
- TimedAudioBuffers<Time> tb = _audio_merger.flush ();
+ TimedAudioBuffers<DCPTime> tb = _audio_merger.flush ();
if (tb.audio) {
Audio (tb.audio, tb.time);
_audio_position += _film->audio_frames_to_time (tb.audio->frames ());
@@ -385,7 +352,7 @@ Player::flush ()
}
while (_audio_position < _video_position) {
- emit_silence (_film->time_to_audio_frames (_video_position - _audio_position));
+ emit_silence (_video_position - _audio_position);
}
}
@@ -395,7 +362,7 @@ Player::flush ()
* @return true on error
*/
void
-Player::seek (Time t, bool accurate)
+Player::seek (DCPTime t, bool accurate)
{
if (!_have_valid_pieces) {
setup_pieces ();
@@ -406,92 +373,113 @@ Player::seek (Time t, bool accurate)
}
for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
- shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content);
- if (!vc) {
- continue;
- }
-
/* s is the offset of t from the start position of this content */
- Time s = t - vc->position ();
- s = max (static_cast<Time> (0), s);
- s = min (vc->length_after_trim(), s);
+ DCPTime s = t - (*i)->content->position ();
+ s = max (static_cast<DCPTime> (0), s);
+ s = min ((*i)->content->length_after_trim(), s);
- /* Hence set the piece positions to the `global' time */
- (*i)->video_position = (*i)->audio_position = vc->position() + s;
+ /* Convert this to the content time */
+ ContentTime ct = (s * (*i)->frc.speed_up) + (*i)->content->trim_start ();
/* And seek the decoder */
- dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (
- vc->time_to_content_video_frames (s + vc->trim_start ()), accurate
- );
-
- (*i)->reset_repeat ();
+ (*i)->decoder->seek (ct, accurate);
}
- _video_position = _audio_position = t;
+ _video_position = time_round_up (t, TIME_HZ / _film->video_frame_rate());
+ _audio_position = time_round_up (t, TIME_HZ / _film->audio_frame_rate());
- /* XXX: don't seek audio because we don't need to... */
+ _audio_merger.clear (_audio_position);
+
+ if (!accurate) {
+ /* We just did an inaccurate seek, so it's likely that the next thing seen
+ out of pass() will be a fair distance from _{video,audio}_position. Setting
+ this flag stops pass() from trying to fix that: we assume that if it
+ was an inaccurate seek then the caller does not care too much about
+ inserting black/silence to keep the time tidy.
+ */
+ _just_did_inaccurate_seek = true;
+ }
}
void
Player::setup_pieces ()
{
list<shared_ptr<Piece> > old_pieces = _pieces;
-
_pieces.clear ();
ContentList content = _playlist->content ();
- sort (content.begin(), content.end(), ContentSorter ());
for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
- shared_ptr<Piece> piece (new Piece (*i));
-
- /* XXX: into content? */
+ shared_ptr<Decoder> decoder;
+ optional<FrameRateChange> frc;
shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
if (fc) {
- shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
-
- fd->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
- fd->Audio.connect (bind (&Player::process_audio, this, weak_ptr<Piece> (piece), _1, _2));
- fd->Subtitle.connect (bind (&Player::process_subtitle, this, weak_ptr<Piece> (piece), _1, _2, _3, _4));
-
- fd->seek (fc->time_to_content_video_frames (fc->trim_start ()), true);
- piece->decoder = fd;
+ decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
+ frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
}
shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
if (ic) {
- bool reusing = false;
-
/* See if we can re-use an old ImageDecoder */
for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
if (imd && imd->content() == ic) {
- piece = *j;
- reusing = true;
+ decoder = imd;
}
}
- if (!reusing) {
- shared_ptr<ImageDecoder> id (new ImageDecoder (_film, ic));
- id->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
- piece->decoder = id;
+ if (!decoder) {
+ decoder.reset (new ImageDecoder (_film, ic));
}
+
+ frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
}
shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
if (sc) {
- shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
- sd->Audio.connect (bind (&Player::process_audio, this, weak_ptr<Piece> (piece), _1, _2));
+ decoder.reset (new SndfileDecoder (_film, sc));
+
+ /* Working out the frc for this content is a bit tricky: what if it overlaps
+ two pieces of video content with different frame rates? For now, use
+ the one with the best overlap.
+ */
+
+ DCPTime best_overlap_t = 0;
+ shared_ptr<VideoContent> best_overlap;
+ for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
+ shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
+ if (!vc) {
+ continue;
+ }
- piece->decoder = sd;
+ DCPTime const overlap = max (vc->position(), sc->position()) - min (vc->end(), sc->end());
+ if (overlap > best_overlap_t) {
+ best_overlap = vc;
+ best_overlap_t = overlap;
+ }
+ }
+
+ if (best_overlap) {
+ frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
+ } else {
+ /* No video overlap; e.g. if the DCP is just audio */
+ frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
+ }
}
- _pieces.push_back (piece);
+ decoder->seek ((*i)->trim_start (), true);
+
+ _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
}
_have_valid_pieces = true;
+
+ /* The Piece for the _last_incoming_video will no longer be valid */
+ _last_incoming_video.video.reset ();
+
+ _video_position = _audio_position = 0;
}
void
@@ -552,29 +540,6 @@ Player::set_video_container_size (libdcp::Size s)
);
}
-shared_ptr<Resampler>
-Player::resampler (shared_ptr<AudioContent> c, bool create)
-{
- map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
- if (i != _resamplers.end ()) {
- return i->second;
- }
-
- if (!create) {
- return shared_ptr<Resampler> ();
- }
-
- _film->log()->log (
- String::compose (
- "Creating new resampler for %1 to %2 with %3 channels", c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()
- )
- );
-
- shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
- _resamplers[c] = r;
- return r;
-}
-
void
Player::emit_black ()
{
@@ -588,17 +553,18 @@ Player::emit_black ()
}
void
-Player::emit_silence (OutputAudioFrame most)
+Player::emit_silence (DCPTime most)
{
if (most == 0) {
return;
}
- OutputAudioFrame N = min (most, _film->audio_frame_rate() / 2);
- shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), N));
+ DCPTime t = min (most, TIME_HZ / 2);
+ shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ));
silence->make_silent ();
Audio (silence, _audio_position);
- _audio_position += _film->audio_frames_to_time (N);
+
+ _audio_position += t;
}
void
@@ -615,18 +581,6 @@ Player::film_changed (Film::Property p)
}
void
-Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to)
-{
- _in_subtitle.piece = weak_piece;
- _in_subtitle.image = image;
- _in_subtitle.rect = rect;
- _in_subtitle.from = from;
- _in_subtitle.to = to;
-
- update_subtitle ();
-}
-
-void
Player::update_subtitle ()
{
shared_ptr<Piece> piece = _in_subtitle.piece.lock ();
@@ -634,7 +588,7 @@ Player::update_subtitle ()
return;
}
- if (!_in_subtitle.image) {
+ if (!_in_subtitle.subtitle->image) {
_out_subtitle.image.reset ();
return;
}
@@ -642,7 +596,7 @@ Player::update_subtitle ()
shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
assert (sc);
- dcpomatic::Rect<double> in_rect = _in_subtitle.rect;
+ dcpomatic::Rect<double> in_rect = _in_subtitle.subtitle->rect;
libdcp::Size scaled_size;
in_rect.y += sc->subtitle_offset ();
@@ -665,15 +619,16 @@ Player::update_subtitle ()
_out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
_out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
-
- _out_subtitle.image = _in_subtitle.image->scale (
+
+ _out_subtitle.image = _in_subtitle.subtitle->image->scale (
scaled_size,
Scaler::from_id ("bicubic"),
- _in_subtitle.image->pixel_format (),
+ PIX_FMT_RGBA,
true
);
- _out_subtitle.from = _in_subtitle.from + piece->content->position ();
- _out_subtitle.to = _in_subtitle.to + piece->content->position ();
+
+ _out_subtitle.from = _in_subtitle.subtitle->dcp_time;
+ _out_subtitle.to = _in_subtitle.subtitle->dcp_time_to;
}
/** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
@@ -682,22 +637,25 @@ Player::update_subtitle ()
bool
Player::repeat_last_video ()
{
- if (!_last_incoming_video.image || !_have_valid_pieces) {
+ if (!_last_incoming_video.video || !_have_valid_pieces) {
return false;
}
- process_video (
+ emit_video (
_last_incoming_video.weak_piece,
- _last_incoming_video.image,
- _last_incoming_video.eyes,
- _last_incoming_video.same,
- _last_incoming_video.frame,
- _last_incoming_video.extra
+ _last_incoming_video.video
);
return true;
}
+void
+Player::set_approximate_size ()
+{
+ _approximate_size = true;
+}
+
+
PlayerImage::PlayerImage (
shared_ptr<const Image> in,
Crop crop,
@@ -722,10 +680,10 @@ PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
}
shared_ptr<Image>
-PlayerImage::image ()
+PlayerImage::image (AVPixelFormat format, bool aligned)
{
- shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false);
-
+ shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
+
Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
if (_subtitle_image) {
@@ -734,3 +692,4 @@ PlayerImage::image ()
return out;
}
+
diff --git a/src/lib/player.h b/src/lib/player.h
index 11cc99e77..b932f4168 100644
--- a/src/lib/player.h
+++ b/src/lib/player.h
@@ -29,6 +29,7 @@
#include "rect.h"
#include "audio_merger.h"
#include "audio_content.h"
+#include "decoded.h"
class Job;
class Film;
@@ -36,24 +37,9 @@ class Playlist;
class AudioContent;
class Piece;
class Image;
-class Resampler;
-/** @class Player
- * @brief A class which can `play' a Playlist; emitting its audio and video.
- */
-
-struct IncomingVideo
-{
-public:
- boost::weak_ptr<Piece> weak_piece;
- boost::shared_ptr<const Image> image;
- Eyes eyes;
- bool same;
- VideoContent::Frame frame;
- Time extra;
-};
-
-/** A wrapper for an Image which contains some pending operations; these may
+/** @class PlayerImage
+ * @brief A wrapper for an Image which contains some pending operations; these may
* not be necessary if the receiver of the PlayerImage throws it away.
*/
class PlayerImage
@@ -63,7 +49,7 @@ public:
void set_subtitle (boost::shared_ptr<const Image>, Position<int>);
- boost::shared_ptr<Image> image ();
+ boost::shared_ptr<Image> image (AVPixelFormat, bool);
private:
boost::shared_ptr<const Image> _in;
@@ -75,6 +61,10 @@ private:
Position<int> _subtitle_position;
};
+/** @class Player
+ * @brief A class which can `play' a Playlist; emitting its audio and video.
+ */
+
class Player : public boost::enable_shared_from_this<Player>, public boost::noncopyable
{
public:
@@ -84,13 +74,14 @@ public:
void disable_audio ();
bool pass ();
- void seek (Time, bool);
+ void seek (DCPTime, bool);
- Time video_position () const {
+ DCPTime video_position () const {
return _video_position;
}
void set_video_container_size (libdcp::Size);
+ void set_approximate_size ();
bool repeat_last_video ();
@@ -101,10 +92,10 @@ public:
* Fourth parameter is true if the image is the same as the last one that was emitted.
* Fifth parameter is the time.
*/
- boost::signals2::signal<void (boost::shared_ptr<PlayerImage>, Eyes, ColourConversion, bool, Time)> Video;
+ boost::signals2::signal<void (boost::shared_ptr<PlayerImage>, Eyes, ColourConversion, bool, DCPTime)> Video;
/** Emitted when some audio data is ready */
- boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, Time)> Audio;
+ boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, DCPTime)> Audio;
/** Emitted when something has changed such that if we went back and emitted
* the last frame again it would look different. This is not emitted after
@@ -118,19 +109,18 @@ private:
friend class PlayerWrapper;
friend class Piece;
- void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const Image>, Eyes, bool, VideoContent::Frame, Time);
- void process_audio (boost::weak_ptr<Piece>, boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
- void process_subtitle (boost::weak_ptr<Piece>, boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time);
void setup_pieces ();
void playlist_changed ();
void content_changed (boost::weak_ptr<Content>, int, bool);
- void do_seek (Time, bool);
+ void do_seek (DCPTime, bool);
void flush ();
void emit_black ();
void emit_silence (OutputAudioFrame);
- boost::shared_ptr<Resampler> resampler (boost::shared_ptr<AudioContent>, bool);
void film_changed (Film::Property);
void update_subtitle ();
+ void emit_video (boost::weak_ptr<Piece>, boost::shared_ptr<DecodedVideo>);
+ void emit_audio (boost::weak_ptr<Piece>, boost::shared_ptr<DecodedAudio>);
+ void step_video_position (boost::shared_ptr<DecodedVideo>);
boost::shared_ptr<const Film> _film;
boost::shared_ptr<const Playlist> _playlist;
@@ -143,29 +133,25 @@ private:
std::list<boost::shared_ptr<Piece> > _pieces;
/** The time after the last video that we emitted */
- Time _video_position;
+ DCPTime _video_position;
/** The time after the last audio that we emitted */
- Time _audio_position;
+ DCPTime _audio_position;
- AudioMerger<Time, AudioContent::Frame> _audio_merger;
+ AudioMerger<DCPTime, AudioContent::Frame> _audio_merger;
libdcp::Size _video_container_size;
boost::shared_ptr<PlayerImage> _black_frame;
- std::map<boost::shared_ptr<AudioContent>, boost::shared_ptr<Resampler> > _resamplers;
struct {
boost::weak_ptr<Piece> piece;
- boost::shared_ptr<Image> image;
- dcpomatic::Rect<double> rect;
- Time from;
- Time to;
+ boost::shared_ptr<DecodedSubtitle> subtitle;
} _in_subtitle;
struct {
- boost::shared_ptr<Image> image;
Position<int> position;
- Time from;
- Time to;
+ boost::shared_ptr<Image> image;
+ DCPTime from;
+ DCPTime to;
} _out_subtitle;
#ifdef DCPOMATIC_DEBUG
@@ -174,7 +160,13 @@ private:
bool _last_emit_was_black;
- IncomingVideo _last_incoming_video;
+ struct {
+ boost::weak_ptr<Piece> weak_piece;
+ boost::shared_ptr<DecodedVideo> video;
+ } _last_incoming_video;
+
+ bool _just_did_inaccurate_seek;
+ bool _approximate_size;
boost::signals2::scoped_connection _playlist_changed_connection;
boost::signals2::scoped_connection _playlist_content_changed_connection;
diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc
index daa82cb94..4175de4c9 100644
--- a/src/lib/playlist.cc
+++ b/src/lib/playlist.cc
@@ -81,7 +81,7 @@ Playlist::maybe_sequence_video ()
_sequencing_video = true;
ContentList cl = _content;
- Time next = 0;
+ DCPTime next = 0;
for (ContentList::iterator i = _content.begin(); i != _content.end(); ++i) {
if (!dynamic_pointer_cast<VideoContent> (*i)) {
continue;
@@ -254,10 +254,10 @@ Playlist::best_dcp_frame_rate () const
return best->dcp;
}
-Time
+DCPTime
Playlist::length () const
{
- Time len = 0;
+ DCPTime len = 0;
for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
len = max (len, (*i)->end() + 1);
}
@@ -279,10 +279,10 @@ Playlist::reconnect ()
}
}
-Time
+DCPTime
Playlist::video_end () const
{
- Time end = 0;
+ DCPTime end = 0;
for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
if (dynamic_pointer_cast<const VideoContent> (*i)) {
end = max (end, (*i)->end ());
@@ -292,6 +292,23 @@ Playlist::video_end () const
return end;
}
+FrameRateChange
+Playlist::active_frame_rate_change (DCPTime t, int dcp_video_frame_rate) const
+{
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+ shared_ptr<const VideoContent> vc = dynamic_pointer_cast<const VideoContent> (*i);
+ if (!vc) {
+ break;
+ }
+
+ if (vc->position() >= t && t < vc->end()) {
+ return FrameRateChange (vc->video_frame_rate(), dcp_video_frame_rate);
+ }
+ }
+
+ return FrameRateChange (dcp_video_frame_rate, dcp_video_frame_rate);
+}
+
void
Playlist::set_sequence_video (bool s)
{
@@ -314,7 +331,7 @@ Playlist::content () const
void
Playlist::repeat (ContentList c, int n)
{
- pair<Time, Time> range (TIME_MAX, 0);
+ pair<DCPTime, DCPTime> range (TIME_MAX, 0);
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
range.first = min (range.first, (*i)->position ());
range.second = max (range.second, (*i)->position ());
@@ -322,7 +339,7 @@ Playlist::repeat (ContentList c, int n)
range.second = max (range.second, (*i)->end ());
}
- Time pos = range.second;
+ DCPTime pos = range.second;
for (int i = 0; i < n; ++i) {
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
shared_ptr<Content> copy = (*i)->clone ();
@@ -355,7 +372,7 @@ Playlist::move_earlier (shared_ptr<Content> c)
return;
}
- Time const p = (*previous)->position ();
+ DCPTime const p = (*previous)->position ();
(*previous)->set_position (p + c->length_after_trim ());
c->set_position (p);
sort (_content.begin(), _content.end(), ContentSorter ());
@@ -382,7 +399,7 @@ Playlist::move_later (shared_ptr<Content> c)
return;
}
- Time const p = (*next)->position ();
+ DCPTime const p = (*next)->position ();
(*next)->set_position (c->position ());
c->set_position (p + c->length_after_trim ());
sort (_content.begin(), _content.end(), ContentSorter ());
diff --git a/src/lib/playlist.h b/src/lib/playlist.h
index 1915e3d04..35709f109 100644
--- a/src/lib/playlist.h
+++ b/src/lib/playlist.h
@@ -25,6 +25,7 @@
#include <boost/enable_shared_from_this.hpp>
#include "ffmpeg_content.h"
#include "audio_mapping.h"
+#include "util.h"
class Content;
class FFmpegContent;
@@ -70,10 +71,11 @@ public:
std::string video_identifier () const;
- Time length () const;
+ DCPTime length () const;
int best_dcp_frame_rate () const;
- Time video_end () const;
+ DCPTime video_end () const;
+ FrameRateChange active_frame_rate_change (DCPTime, int dcp_frame_rate) const;
void set_sequence_video (bool);
void maybe_sequence_video ();
diff --git a/src/lib/resampler.cc b/src/lib/resampler.cc
index d897bf562..00121384d 100644
--- a/src/lib/resampler.cc
+++ b/src/lib/resampler.cc
@@ -64,11 +64,9 @@ Resampler::~Resampler ()
swr_free (&_swr_context);
}
-pair<shared_ptr<const AudioBuffers>, AudioContent::Frame>
-Resampler::run (shared_ptr<const AudioBuffers> in, AudioContent::Frame frame)
+shared_ptr<const AudioBuffers>
+Resampler::run (shared_ptr<const AudioBuffers> in)
{
- AudioContent::Frame const resamp_time = swr_next_pts (_swr_context, frame * _out_rate) / _in_rate;
-
/* Compute the resampled frames count and add 32 for luck */
int const max_resampled_frames = ceil ((double) in->frames() * _out_rate / _in_rate) + 32;
shared_ptr<AudioBuffers> resampled (new AudioBuffers (_channels, max_resampled_frames));
@@ -84,7 +82,7 @@ Resampler::run (shared_ptr<const AudioBuffers> in, AudioContent::Frame frame)
}
resampled->set_frames (resampled_frames);
- return make_pair (resampled, resamp_time);
+ return resampled;
}
shared_ptr<const AudioBuffers>
diff --git a/src/lib/resampler.h b/src/lib/resampler.h
index 69ec83ba9..4ee11a7f0 100644
--- a/src/lib/resampler.h
+++ b/src/lib/resampler.h
@@ -33,7 +33,7 @@ public:
Resampler (int, int, int);
~Resampler ();
- std::pair<boost::shared_ptr<const AudioBuffers>, AudioContent::Frame> run (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
+ boost::shared_ptr<const AudioBuffers> run (boost::shared_ptr<const AudioBuffers>);
boost::shared_ptr<const AudioBuffers> flush ();
private:
diff --git a/src/lib/sndfile_content.cc b/src/lib/sndfile_content.cc
index 89db865d5..1c872cf96 100644
--- a/src/lib/sndfile_content.cc
+++ b/src/lib/sndfile_content.cc
@@ -141,7 +141,7 @@ SndfileContent::as_xml (xmlpp::Node* node) const
_audio_mapping.as_xml (node->add_child("AudioMapping"));
}
-Time
+DCPTime
SndfileContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
diff --git a/src/lib/sndfile_content.h b/src/lib/sndfile_content.h
index 701ff16b2..c88764c1b 100644
--- a/src/lib/sndfile_content.h
+++ b/src/lib/sndfile_content.h
@@ -44,7 +44,7 @@ public:
std::string technical_summary () const;
std::string information () const;
void as_xml (xmlpp::Node *) const;
- Time full_length () const;
+ DCPTime full_length () const;
/* AudioContent */
int audio_channels () const {
diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc
index e10f4f568..432f73f0d 100644
--- a/src/lib/sndfile_decoder.cc
+++ b/src/lib/sndfile_decoder.cc
@@ -55,9 +55,13 @@ SndfileDecoder::~SndfileDecoder ()
delete[] _deinterleave_buffer;
}
-void
+bool
SndfileDecoder::pass ()
{
+ if (_remaining == 0) {
+ return true;
+ }
+
/* Do things in half second blocks as I think there may be limits
to what FFmpeg (and in particular the resampler) can cope with.
*/
@@ -90,9 +94,11 @@ SndfileDecoder::pass ()
}
data->set_frames (this_time);
- audio (data, _done);
+ audio (data, _done * TIME_HZ / audio_frame_rate ());
_done += this_time;
_remaining -= this_time;
+
+ return true;
}
int
@@ -113,8 +119,11 @@ SndfileDecoder::audio_frame_rate () const
return _info.samplerate;
}
-bool
-SndfileDecoder::done () const
+void
+SndfileDecoder::seek (ContentTime t, bool accurate)
{
- return _audio_position >= _sndfile_content->audio_length ();
+ Decoder::seek (t, accurate);
+
+ _done = t * audio_frame_rate() / TIME_HZ;
+ _remaining = _info.frames - _done;
}
diff --git a/src/lib/sndfile_decoder.h b/src/lib/sndfile_decoder.h
index 77fa6d177..63f2f7dc4 100644
--- a/src/lib/sndfile_decoder.h
+++ b/src/lib/sndfile_decoder.h
@@ -29,14 +29,18 @@ public:
SndfileDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const SndfileContent>);
~SndfileDecoder ();
- void pass ();
- bool done () const;
+ void seek (ContentTime, bool);
int audio_channels () const;
AudioContent::Frame audio_length () const;
int audio_frame_rate () const;
private:
+ bool has_audio () const {
+ return true;
+ }
+ bool pass ();
+
boost::shared_ptr<const SndfileContent> _sndfile_content;
SNDFILE* _sndfile;
SF_INFO _info;
diff --git a/src/lib/subtitle_decoder.cc b/src/lib/subtitle_decoder.cc
index c06f3d718..7ba969933 100644
--- a/src/lib/subtitle_decoder.cc
+++ b/src/lib/subtitle_decoder.cc
@@ -21,6 +21,7 @@
#include "subtitle_decoder.h"
using boost::shared_ptr;
+using boost::optional;
SubtitleDecoder::SubtitleDecoder (shared_ptr<const Film> f)
: Decoder (f)
@@ -33,7 +34,7 @@ SubtitleDecoder::SubtitleDecoder (shared_ptr<const Film> f)
* Image may be 0 to say that there is no current subtitle.
*/
void
-SubtitleDecoder::subtitle (shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to)
+SubtitleDecoder::subtitle (shared_ptr<Image> image, dcpomatic::Rect<double> rect, ContentTime from, ContentTime to)
{
- Subtitle (image, rect, from, to);
+ _pending.push_back (shared_ptr<DecodedSubtitle> (new DecodedSubtitle (image, rect, from, to)));
}
diff --git a/src/lib/subtitle_decoder.h b/src/lib/subtitle_decoder.h
index eeeadbd3f..fd1d71f33 100644
--- a/src/lib/subtitle_decoder.h
+++ b/src/lib/subtitle_decoder.h
@@ -21,9 +21,10 @@
#include "decoder.h"
#include "rect.h"
#include "types.h"
+#include "decoded.h"
class Film;
-class TimedSubtitle;
+class DCPTimedSubtitle;
class Image;
class SubtitleDecoder : public virtual Decoder
@@ -31,8 +32,6 @@ class SubtitleDecoder : public virtual Decoder
public:
SubtitleDecoder (boost::shared_ptr<const Film>);
- boost::signals2::signal<void (boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time)> Subtitle;
-
protected:
- void subtitle (boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time);
+ void subtitle (boost::shared_ptr<Image>, dcpomatic::Rect<double>, ContentTime, ContentTime);
};
diff --git a/src/lib/types.h b/src/lib/types.h
index 96b993a8e..33c0c171f 100644
--- a/src/lib/types.h
+++ b/src/lib/types.h
@@ -38,9 +38,10 @@ class AudioBuffers;
*/
#define SERVER_LINK_VERSION 1
-typedef int64_t Time;
+typedef int64_t DCPTime;
#define TIME_MAX INT64_MAX
-#define TIME_HZ ((Time) 96000)
+#define TIME_HZ ((DCPTime) 96000)
+typedef int64_t ContentTime;
typedef int64_t OutputAudioFrame;
typedef int OutputVideoFrame;
typedef std::vector<boost::shared_ptr<Content> > ContentList;
diff --git a/src/lib/util.cc b/src/lib/util.cc
index ddc0a2974..381c47a9a 100644
--- a/src/lib/util.cc
+++ b/src/lib/util.cc
@@ -772,7 +772,7 @@ audio_channel_name (int c)
return channels[c];
}
-FrameRateConversion::FrameRateConversion (float source, int dcp)
+FrameRateChange::FrameRateChange (float source, int dcp)
: skip (false)
, repeat (1)
, change_speed (false)
@@ -790,7 +790,8 @@ FrameRateConversion::FrameRateConversion (float source, int dcp)
repeat = round (dcp / source);
}
- change_speed = !about_equal (source * factor(), dcp);
+ speed_up = dcp / (source * factor());
+ change_speed = !about_equal (speed_up, 1.0);
if (!skip && repeat == 1 && !change_speed) {
description = _("Content and DCP have the same rate.\n");
@@ -914,3 +915,10 @@ fit_ratio_within (float ratio, libdcp::Size full_frame)
return libdcp::Size (full_frame.width, rint (full_frame.width / ratio));
}
+
+DCPTime
+time_round_up (DCPTime t, DCPTime nearest)
+{
+ DCPTime const a = t + nearest - 1;
+ return a - (a % nearest);
+}
diff --git a/src/lib/util.h b/src/lib/util.h
index 7dcd920b7..892b473f7 100644
--- a/src/lib/util.h
+++ b/src/lib/util.h
@@ -79,9 +79,9 @@ extern std::string tidy_for_filename (std::string);
extern boost::shared_ptr<const libdcp::Signer> make_signer ();
extern libdcp::Size fit_ratio_within (float ratio, libdcp::Size);
-struct FrameRateConversion
+struct FrameRateChange
{
- FrameRateConversion (float, int);
+ FrameRateChange (float, int);
/** @return factor by which to multiply a source frame rate
to get the effective rate after any skip or repeat has happened.
@@ -109,11 +109,17 @@ struct FrameRateConversion
*/
bool change_speed;
+ /** Amount by which the video is being sped-up in the DCP; e.g. for a
+ * 24fps source in a 25fps DCP this would be 25/24.
+ */
+ float speed_up;
+
std::string description;
};
extern int dcp_audio_frame_rate (int);
extern int stride_round_up (int, int const *, int);
+extern DCPTime time_round_up (DCPTime, DCPTime);
extern std::multimap<std::string, std::string> read_key_value (std::istream& s);
extern int get_required_int (std::multimap<std::string, std::string> const & kv, std::string k);
extern float get_required_float (std::multimap<std::string, std::string> const & kv, std::string k);
diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc
index cc075a34c..404549532 100644
--- a/src/lib/video_content.cc
+++ b/src/lib/video_content.cc
@@ -59,7 +59,7 @@ VideoContent::VideoContent (shared_ptr<const Film> f)
setup_default_colour_conversion ();
}
-VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len)
+VideoContent::VideoContent (shared_ptr<const Film> f, DCPTime s, VideoContent::Frame len)
: Content (f, s)
, _video_length (len)
, _video_frame_rate (0)
@@ -353,19 +353,21 @@ VideoContent::video_size_after_crop () const
}
/** @param t A time offset from the start of this piece of content.
- * @return Corresponding frame index.
+ * @return Corresponding frame index, rounded up so that the frame index
+ * is that of the next complete frame which starts after `t'.
*/
VideoContent::Frame
-VideoContent::time_to_content_video_frames (Time t) const
+VideoContent::time_to_content_video_frames (DCPTime t) const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
- FrameRateConversion frc (video_frame_rate(), film->video_frame_rate());
-
/* Here we are converting from time (in the DCP) to a frame number in the content.
Hence we need to use the DCP's frame rate and the double/skip correction, not
- the source's rate.
+ the source's rate; source rate will be equal to DCP rate if we ignore
+ double/skip. There's no need to call Film::active_frame_rate_change() here
+ as we know that we are it (since we're video).
*/
- return t * film->video_frame_rate() / (frc.factor() * TIME_HZ);
+ FrameRateChange frc (video_frame_rate(), film->video_frame_rate());
+ return ceil (t * film->video_frame_rate() / (frc.factor() * TIME_HZ));
}
diff --git a/src/lib/video_content.h b/src/lib/video_content.h
index effca5c61..f008143fa 100644
--- a/src/lib/video_content.h
+++ b/src/lib/video_content.h
@@ -43,7 +43,7 @@ public:
typedef int Frame;
VideoContent (boost::shared_ptr<const Film>);
- VideoContent (boost::shared_ptr<const Film>, Time, VideoContent::Frame);
+ VideoContent (boost::shared_ptr<const Film>, DCPTime, VideoContent::Frame);
VideoContent (boost::shared_ptr<const Film>, boost::filesystem::path);
VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
VideoContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
@@ -123,7 +123,7 @@ public:
libdcp::Size video_size_after_3d_split () const;
libdcp::Size video_size_after_crop () const;
- VideoContent::Frame time_to_content_video_frames (Time) const;
+ VideoContent::Frame time_to_content_video_frames (DCPTime) const;
protected:
void take_from_video_examiner (boost::shared_ptr<VideoExaminer>);
diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc
index e7ddec5e6..6a16557cd 100644
--- a/src/lib/video_decoder.cc
+++ b/src/lib/video_decoder.cc
@@ -24,38 +24,36 @@
using std::cout;
using boost::shared_ptr;
+using boost::optional;
VideoDecoder::VideoDecoder (shared_ptr<const Film> f, shared_ptr<const VideoContent> c)
: Decoder (f)
, _video_content (c)
- , _video_position (0)
{
}
+/** Called by subclasses when they have a video frame ready */
void
-VideoDecoder::video (shared_ptr<const Image> image, bool same, VideoContent::Frame frame)
+VideoDecoder::video (shared_ptr<const Image> image, bool same, ContentTime time)
{
switch (_video_content->video_frame_type ()) {
case VIDEO_FRAME_TYPE_2D:
- Video (image, EYES_BOTH, same, frame);
+ _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image, EYES_BOTH, same, time)));
break;
case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
{
int const half = image->size().width / 2;
- Video (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, same, frame);
- Video (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, same, frame);
+ _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, same, time)));
+ _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, same, time)));
break;
}
case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
{
int const half = image->size().height / 2;
- Video (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, same, frame);
- Video (image->crop (Crop (0, 0, half, 0), true), EYES_RIGHT, same, frame);
+ _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, same, time)));
+ _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, 0, half, 0), true), EYES_RIGHT, same, time)));
break;
}
}
-
- _video_position = frame + 1;
}
-
diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h
index 142320a04..d8c362354 100644
--- a/src/lib/video_decoder.h
+++ b/src/lib/video_decoder.h
@@ -25,6 +25,7 @@
#include "decoder.h"
#include "video_content.h"
#include "util.h"
+#include "decoded.h"
class VideoContent;
class Image;
@@ -34,24 +35,14 @@ class VideoDecoder : public virtual Decoder
public:
VideoDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const VideoContent>);
- /** Seek so that the next pass() will yield (approximately) the requested frame.
- * Pass accurate = true to try harder to get close to the request.
- */
- virtual void seek (VideoContent::Frame frame, bool accurate) = 0;
-
- /** Emitted when a video frame is ready.
- * First parameter is the video image.
- * Second parameter is the eye(s) which should see this image.
- * Third parameter is true if the image is the same as the last one that was emitted for this Eyes value.
- * Fourth parameter is the frame within our source.
- */
- boost::signals2::signal<void (boost::shared_ptr<const Image>, Eyes, bool, VideoContent::Frame)> Video;
-
+ boost::shared_ptr<const VideoContent> video_content () const {
+ return _video_content;
+ }
+
protected:
- void video (boost::shared_ptr<const Image>, bool, VideoContent::Frame);
+ void video (boost::shared_ptr<const Image>, bool, ContentTime);
boost::shared_ptr<const VideoContent> _video_content;
- VideoContent::Frame _video_position;
};
#endif
diff --git a/src/tools/server_test.cc b/src/tools/server_test.cc
index b2c5e784b..38e4704b7 100644
--- a/src/tools/server_test.cc
+++ b/src/tools/server_test.cc
@@ -47,10 +47,15 @@ static shared_ptr<FileLog> log_ (new FileLog ("servomatictest.log"));
static int frame = 0;
void
-process_video (shared_ptr<PlayerImage> image, Eyes eyes, ColourConversion conversion, Time)
+process_video (shared_ptr<PlayerImage> image, Eyes eyes, ColourConversion conversion, DCPTime)
{
- shared_ptr<DCPVideoFrame> local (new DCPVideoFrame (image->image(), frame, eyes, conversion, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
- shared_ptr<DCPVideoFrame> remote (new DCPVideoFrame (image->image(), frame, eyes, conversion, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
+ shared_ptr<DCPVideoFrame> local (
+ new DCPVideoFrame (image->image (PIX_FMT_RGB24, false), frame, eyes, conversion, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_)
+ );
+
+ shared_ptr<DCPVideoFrame> remote (
+ new DCPVideoFrame (image->image (PIX_FMT_RGB24, false), frame, eyes, conversion, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_)
+ );
cout << "Frame " << frame << ": ";
cout.flush ();
diff --git a/src/wx/audio_plot.cc b/src/wx/audio_plot.cc
index f78885772..96de34d40 100644
--- a/src/wx/audio_plot.cc
+++ b/src/wx/audio_plot.cc
@@ -145,7 +145,7 @@ AudioPlot::paint ()
gc->SetPen (*wxLIGHT_GREY_PEN);
gc->StrokePath (grid);
- gc->DrawText (_("Time"), data_width, _height - _y_origin + db_label_height / 2);
+ gc->DrawText (_("DCPTime"), data_width, _height - _y_origin + db_label_height / 2);
if (_type_visible[AudioPoint::PEAK]) {
for (int c = 0; c < MAX_AUDIO_CHANNELS; ++c) {
diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc
index 04bf6d2a8..1818fb451 100644
--- a/src/wx/film_editor.cc
+++ b/src/wx/film_editor.cc
@@ -936,7 +936,7 @@ FilmEditor::content_timeline_clicked ()
_timeline_dialog = 0;
}
- _timeline_dialog = new TimelineDialog (this, _film);
+ _timeline_dialog = new DCPTimelineDialog (this, _film);
_timeline_dialog->Show ();
}
diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h
index 23c87e678..dadb583ae 100644
--- a/src/wx/film_editor.h
+++ b/src/wx/film_editor.h
@@ -33,9 +33,9 @@ class wxNotebook;
class wxListCtrl;
class wxListEvent;
class Film;
-class TimelineDialog;
+class DCPTimelineDialog;
class Ratio;
-class Timecode;
+class DCPTimecode;
class FilmEditorPanel;
class SubtitleContent;
@@ -156,5 +156,5 @@ private:
std::vector<Ratio const *> _ratios;
bool _generally_sensitive;
- TimelineDialog* _timeline_dialog;
+ DCPTimelineDialog* _timeline_dialog;
};
diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc
index fbca835c2..e6b8bf8dd 100644
--- a/src/wx/film_viewer.cc
+++ b/src/wx/film_viewer.cc
@@ -36,6 +36,7 @@
#include "lib/player.h"
#include "lib/video_content.h"
#include "lib/video_decoder.h"
+#include "lib/timer.h"
#include "film_viewer.h"
#include "wx_util.h"
@@ -128,6 +129,7 @@ FilmViewer::set_film (shared_ptr<Film> f)
_player = f->make_player ();
_player->disable_audio ();
+ _player->set_approximate_size ();
_player->Video.connect (boost::bind (&FilmViewer::process_video, this, _1, _2, _5));
_player->Changed.connect (boost::bind (&FilmViewer::player_changed, this, _1));
@@ -164,7 +166,7 @@ FilmViewer::timer ()
fetch_next_frame ();
- Time const len = _film->length ();
+ DCPTime const len = _film->length ();
if (len) {
int const new_slider_position = 4096 * _player->video_position() / len;
@@ -275,20 +277,24 @@ FilmViewer::check_play_state ()
}
void
-FilmViewer::process_video (shared_ptr<PlayerImage> image, Eyes eyes, Time t)
+FilmViewer::process_video (shared_ptr<PlayerImage> image, Eyes eyes, DCPTime t)
{
if (eyes == EYES_RIGHT) {
return;
}
-
- _frame = image->image ();
+
+ /* Going via BGRA here makes the scaler faster then using RGB24 directly (about
+ twice on x86 Linux).
+ */
+ shared_ptr<Image> im = image->image (PIX_FMT_BGRA, true);
+ _frame = im->scale (im->size(), Scaler::from_id ("fastbilinear"), PIX_FMT_RGB24, false);
_got_frame = true;
set_position_text (t);
}
void
-FilmViewer::set_position_text (Time t)
+FilmViewer::set_position_text (DCPTime t)
{
if (!_film) {
_frame_number->SetLabel ("0");
@@ -371,7 +377,7 @@ FilmViewer::back_clicked ()
We want to see the one before it, so we need to go back 2.
*/
- Time p = _player->video_position() - _film->video_frames_to_time (2);
+ DCPTime p = _player->video_position() - _film->video_frames_to_time (2);
if (p < 0) {
p = 0;
}
diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h
index c99c73440..0e5c49c6d 100644
--- a/src/wx/film_viewer.h
+++ b/src/wx/film_viewer.h
@@ -59,7 +59,7 @@ private:
void slider_moved ();
void play_clicked ();
void timer ();
- void process_video (boost::shared_ptr<PlayerImage>, Eyes, Time);
+ void process_video (boost::shared_ptr<PlayerImage>, Eyes, DCPTime);
void calculate_sizes ();
void check_play_state ();
void fetch_current_frame_again ();
@@ -68,7 +68,7 @@ private:
void back_clicked ();
void forward_clicked ();
void player_changed (bool);
- void set_position_text (Time);
+ void set_position_text (DCPTime);
boost::shared_ptr<Film> _film;
boost::shared_ptr<Player> _player;
diff --git a/src/wx/timecode.cc b/src/wx/timecode.cc
index 033bd2bd0..b23ff2a39 100644
--- a/src/wx/timecode.cc
+++ b/src/wx/timecode.cc
@@ -26,7 +26,7 @@ using std::string;
using std::cout;
using boost::lexical_cast;
-Timecode::Timecode (wxWindow* parent)
+DCPTimecode::DCPTimecode (wxWindow* parent)
: wxPanel (parent)
{
wxClientDC dc (parent);
@@ -69,11 +69,11 @@ Timecode::Timecode (wxWindow* parent)
_fixed = add_label_to_sizer (_sizer, this, wxT ("42"), false);
- _hours->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&Timecode::changed, this));
- _minutes->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&Timecode::changed, this));
- _seconds->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&Timecode::changed, this));
- _frames->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&Timecode::changed, this));
- _set_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&Timecode::set_clicked, this));
+ _hours->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DCPTimecode::changed, this));
+ _minutes->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DCPTimecode::changed, this));
+ _seconds->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DCPTimecode::changed, this));
+ _frames->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DCPTimecode::changed, this));
+ _set_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DCPTimecode::set_clicked, this));
_set_button->Enable (false);
@@ -83,7 +83,7 @@ Timecode::Timecode (wxWindow* parent)
}
void
-Timecode::set (Time t, int fps)
+DCPTimecode::set (DCPTime t, int fps)
{
int const h = t / (3600 * TIME_HZ);
t -= h * 3600 * TIME_HZ;
@@ -101,10 +101,10 @@ Timecode::set (Time t, int fps)
_fixed->SetLabel (wxString::Format ("%02d:%02d:%02d.%02d", h, m, s, f));
}
-Time
-Timecode::get (int fps) const
+DCPTime
+DCPTimecode::get (int fps) const
{
- Time t = 0;
+ DCPTime t = 0;
string const h = wx_to_std (_hours->GetValue ());
t += lexical_cast<int> (h.empty() ? "0" : h) * 3600 * TIME_HZ;
string const m = wx_to_std (_minutes->GetValue());
@@ -118,20 +118,20 @@ Timecode::get (int fps) const
}
void
-Timecode::changed ()
+DCPTimecode::changed ()
{
_set_button->Enable (true);
}
void
-Timecode::set_clicked ()
+DCPTimecode::set_clicked ()
{
Changed ();
_set_button->Enable (false);
}
void
-Timecode::set_editable (bool e)
+DCPTimecode::set_editable (bool e)
{
_editable->Show (e);
_fixed->Show (!e);
diff --git a/src/wx/timecode.h b/src/wx/timecode.h
index 880b44a31..f95740255 100644
--- a/src/wx/timecode.h
+++ b/src/wx/timecode.h
@@ -21,13 +21,13 @@
#include <wx/wx.h>
#include "lib/types.h"
-class Timecode : public wxPanel
+class DCPTimecode : public wxPanel
{
public:
- Timecode (wxWindow *);
+ DCPTimecode (wxWindow *);
- void set (Time, int);
- Time get (int) const;
+ void set (DCPTime, int);
+ DCPTime get (int) const;
void set_editable (bool);
diff --git a/src/wx/timeline.cc b/src/wx/timeline.cc
index 0ac9a1d4b..9d2aee76c 100644
--- a/src/wx/timeline.cc
+++ b/src/wx/timeline.cc
@@ -39,7 +39,7 @@ using boost::optional;
class View : public boost::noncopyable
{
public:
- View (Timeline& t)
+ View (DCPTimeline& t)
: _timeline (t)
{
@@ -64,12 +64,12 @@ public:
protected:
virtual void do_paint (wxGraphicsContext *) = 0;
- int time_x (Time t) const
+ int time_x (DCPTime t) const
{
return _timeline.tracks_position().x + t * _timeline.pixels_per_time_unit();
}
- Timeline& _timeline;
+ DCPTimeline& _timeline;
private:
dcpomatic::Rect<int> _last_paint_bbox;
@@ -80,7 +80,7 @@ private:
class ContentView : public View
{
public:
- ContentView (Timeline& tl, shared_ptr<Content> c)
+ ContentView (DCPTimeline& tl, shared_ptr<Content> c)
: View (tl)
, _content (c)
, _track (0)
@@ -139,8 +139,8 @@ private:
return;
}
- Time const position = cont->position ();
- Time const len = cont->length_after_trim ();
+ DCPTime const position = cont->position ();
+ DCPTime const len = cont->length_after_trim ();
wxColour selected (colour().Red() / 2, colour().Green() / 2, colour().Blue() / 2);
@@ -203,7 +203,7 @@ private:
class AudioContentView : public ContentView
{
public:
- AudioContentView (Timeline& tl, shared_ptr<Content> c)
+ AudioContentView (DCPTimeline& tl, shared_ptr<Content> c)
: ContentView (tl, c)
{}
@@ -222,7 +222,7 @@ private:
class VideoContentView : public ContentView
{
public:
- VideoContentView (Timeline& tl, shared_ptr<Content> c)
+ VideoContentView (DCPTimeline& tl, shared_ptr<Content> c)
: ContentView (tl, c)
{}
@@ -243,10 +243,10 @@ private:
}
};
-class TimeAxisView : public View
+class DCPTimeAxisView : public View
{
public:
- TimeAxisView (Timeline& tl, int y)
+ DCPTimeAxisView (DCPTimeline& tl, int y)
: View (tl)
, _y (y)
{}
@@ -291,7 +291,7 @@ private:
path.AddLineToPoint (_timeline.width(), _y);
gc->StrokePath (path);
- Time t = 0;
+ DCPTime t = 0;
while ((t * _timeline.pixels_per_time_unit()) < _timeline.width()) {
wxGraphicsPath path = gc->CreatePath ();
path.MoveToPoint (time_x (t), _y - 4);
@@ -326,11 +326,11 @@ private:
};
-Timeline::Timeline (wxWindow* parent, FilmEditor* ed, shared_ptr<Film> film)
+DCPTimeline::DCPTimeline (wxWindow* parent, FilmEditor* ed, shared_ptr<Film> film)
: wxPanel (parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
, _film_editor (ed)
, _film (film)
- , _time_axis_view (new TimeAxisView (*this, 32))
+ , _time_axis_view (new DCPTimeAxisView (*this, 32))
, _tracks (0)
, _pixels_per_time_unit (0)
, _left_down (false)
@@ -343,22 +343,22 @@ Timeline::Timeline (wxWindow* parent, FilmEditor* ed, shared_ptr<Film> film)
SetDoubleBuffered (true);
#endif
- Bind (wxEVT_PAINT, boost::bind (&Timeline::paint, this));
- Bind (wxEVT_LEFT_DOWN, boost::bind (&Timeline::left_down, this, _1));
- Bind (wxEVT_LEFT_UP, boost::bind (&Timeline::left_up, this, _1));
- Bind (wxEVT_RIGHT_DOWN, boost::bind (&Timeline::right_down, this, _1));
- Bind (wxEVT_MOTION, boost::bind (&Timeline::mouse_moved, this, _1));
- Bind (wxEVT_SIZE, boost::bind (&Timeline::resized, this));
+ Bind (wxEVT_PAINT, boost::bind (&DCPTimeline::paint, this));
+ Bind (wxEVT_LEFT_DOWN, boost::bind (&DCPTimeline::left_down, this, _1));
+ Bind (wxEVT_LEFT_UP, boost::bind (&DCPTimeline::left_up, this, _1));
+ Bind (wxEVT_RIGHT_DOWN, boost::bind (&DCPTimeline::right_down, this, _1));
+ Bind (wxEVT_MOTION, boost::bind (&DCPTimeline::mouse_moved, this, _1));
+ Bind (wxEVT_SIZE, boost::bind (&DCPTimeline::resized, this));
playlist_changed ();
SetMinSize (wxSize (640, tracks() * track_height() + 96));
- _playlist_connection = film->playlist()->Changed.connect (bind (&Timeline::playlist_changed, this));
+ _playlist_connection = film->playlist()->Changed.connect (bind (&DCPTimeline::playlist_changed, this));
}
void
-Timeline::paint ()
+DCPTimeline::paint ()
{
wxPaintDC dc (this);
@@ -377,7 +377,7 @@ Timeline::paint ()
}
void
-Timeline::playlist_changed ()
+DCPTimeline::playlist_changed ()
{
ensure_ui_thread ();
@@ -406,7 +406,7 @@ Timeline::playlist_changed ()
}
void
-Timeline::assign_tracks ()
+DCPTimeline::assign_tracks ()
{
for (ViewList::iterator i = _views.begin(); i != _views.end(); ++i) {
shared_ptr<ContentView> cv = dynamic_pointer_cast<ContentView> (*i);
@@ -465,13 +465,13 @@ Timeline::assign_tracks ()
}
int
-Timeline::tracks () const
+DCPTimeline::tracks () const
{
return _tracks;
}
void
-Timeline::setup_pixels_per_time_unit ()
+DCPTimeline::setup_pixels_per_time_unit ()
{
shared_ptr<const Film> film = _film.lock ();
if (!film) {
@@ -482,7 +482,7 @@ Timeline::setup_pixels_per_time_unit ()
}
shared_ptr<View>
-Timeline::event_to_view (wxMouseEvent& ev)
+DCPTimeline::event_to_view (wxMouseEvent& ev)
{
ViewList::iterator i = _views.begin();
Position<int> const p (ev.GetX(), ev.GetY());
@@ -498,7 +498,7 @@ Timeline::event_to_view (wxMouseEvent& ev)
}
void
-Timeline::left_down (wxMouseEvent& ev)
+DCPTimeline::left_down (wxMouseEvent& ev)
{
shared_ptr<View> view = event_to_view (ev);
shared_ptr<ContentView> content_view = dynamic_pointer_cast<ContentView> (view);
@@ -539,7 +539,7 @@ Timeline::left_down (wxMouseEvent& ev)
}
void
-Timeline::left_up (wxMouseEvent& ev)
+DCPTimeline::left_up (wxMouseEvent& ev)
{
_left_down = false;
@@ -551,7 +551,7 @@ Timeline::left_up (wxMouseEvent& ev)
}
void
-Timeline::mouse_moved (wxMouseEvent& ev)
+DCPTimeline::mouse_moved (wxMouseEvent& ev)
{
if (!_left_down) {
return;
@@ -561,7 +561,7 @@ Timeline::mouse_moved (wxMouseEvent& ev)
}
void
-Timeline::right_down (wxMouseEvent& ev)
+DCPTimeline::right_down (wxMouseEvent& ev)
{
shared_ptr<View> view = event_to_view (ev);
shared_ptr<ContentView> cv = dynamic_pointer_cast<ContentView> (view);
@@ -578,7 +578,7 @@ Timeline::right_down (wxMouseEvent& ev)
}
void
-Timeline::set_position_from_event (wxMouseEvent& ev)
+DCPTimeline::set_position_from_event (wxMouseEvent& ev)
{
wxPoint const p = ev.GetPosition();
@@ -597,13 +597,13 @@ Timeline::set_position_from_event (wxMouseEvent& ev)
return;
}
- Time new_position = _down_view_position + (p.x - _down_point.x) / _pixels_per_time_unit;
+ DCPTime new_position = _down_view_position + (p.x - _down_point.x) / _pixels_per_time_unit;
if (_snap) {
bool first = true;
- Time nearest_distance = TIME_MAX;
- Time nearest_new_position = TIME_MAX;
+ DCPTime nearest_distance = TIME_MAX;
+ DCPTime nearest_new_position = TIME_MAX;
/* Find the nearest content edge; this is inefficient */
for (ViewList::iterator i = _views.begin(); i != _views.end(); ++i) {
@@ -614,7 +614,7 @@ Timeline::set_position_from_event (wxMouseEvent& ev)
{
/* Snap starts to ends */
- Time const d = abs (cv->content()->end() - new_position);
+ DCPTime const d = abs (cv->content()->end() - new_position);
if (first || d < nearest_distance) {
nearest_distance = d;
nearest_new_position = cv->content()->end();
@@ -623,7 +623,7 @@ Timeline::set_position_from_event (wxMouseEvent& ev)
{
/* Snap ends to starts */
- Time const d = abs (cv->content()->position() - (new_position + _down_view->content()->length_after_trim()));
+ DCPTime const d = abs (cv->content()->position() - (new_position + _down_view->content()->length_after_trim()));
if (d < nearest_distance) {
nearest_distance = d;
nearest_new_position = cv->content()->position() - _down_view->content()->length_after_trim ();
@@ -653,25 +653,25 @@ Timeline::set_position_from_event (wxMouseEvent& ev)
}
void
-Timeline::force_redraw (dcpomatic::Rect<int> const & r)
+DCPTimeline::force_redraw (dcpomatic::Rect<int> const & r)
{
RefreshRect (wxRect (r.x, r.y, r.width, r.height), false);
}
shared_ptr<const Film>
-Timeline::film () const
+DCPTimeline::film () const
{
return _film.lock ();
}
void
-Timeline::resized ()
+DCPTimeline::resized ()
{
setup_pixels_per_time_unit ();
}
void
-Timeline::clear_selection ()
+DCPTimeline::clear_selection ()
{
for (ViewList::iterator i = _views.begin(); i != _views.end(); ++i) {
shared_ptr<ContentView> cv = dynamic_pointer_cast<ContentView> (*i);
@@ -681,8 +681,8 @@ Timeline::clear_selection ()
}
}
-Timeline::ContentViewList
-Timeline::selected_views () const
+DCPTimeline::ContentViewList
+DCPTimeline::selected_views () const
{
ContentViewList sel;
@@ -697,7 +697,7 @@ Timeline::selected_views () const
}
ContentList
-Timeline::selected_content () const
+DCPTimeline::selected_content () const
{
ContentList sel;
ContentViewList views = selected_views ();
diff --git a/src/wx/timeline.h b/src/wx/timeline.h
index ef1d10797..b3ee77fff 100644
--- a/src/wx/timeline.h
+++ b/src/wx/timeline.h
@@ -29,12 +29,12 @@ class Film;
class View;
class ContentView;
class FilmEditor;
-class TimeAxisView;
+class DCPTimeAxisView;
-class Timeline : public wxPanel
+class DCPTimeline : public wxPanel
{
public:
- Timeline (wxWindow *, FilmEditor *, boost::shared_ptr<Film>);
+ DCPTimeline (wxWindow *, FilmEditor *, boost::shared_ptr<Film>);
boost::shared_ptr<const Film> film () const;
@@ -94,13 +94,13 @@ private:
FilmEditor* _film_editor;
boost::weak_ptr<Film> _film;
ViewList _views;
- boost::shared_ptr<TimeAxisView> _time_axis_view;
+ boost::shared_ptr<DCPTimeAxisView> _time_axis_view;
int _tracks;
double _pixels_per_time_unit;
bool _left_down;
wxPoint _down_point;
boost::shared_ptr<ContentView> _down_view;
- Time _down_view_position;
+ DCPTime _down_view_position;
bool _first_move;
ContentMenu _menu;
bool _snap;
diff --git a/src/wx/timeline_dialog.cc b/src/wx/timeline_dialog.cc
index dbf7ae232..a63c219db 100644
--- a/src/wx/timeline_dialog.cc
+++ b/src/wx/timeline_dialog.cc
@@ -28,8 +28,8 @@ using std::list;
using std::cout;
using boost::shared_ptr;
-TimelineDialog::TimelineDialog (FilmEditor* ed, shared_ptr<Film> film)
- : wxDialog (ed, wxID_ANY, _("Timeline"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE)
+DCPTimelineDialog::DCPTimelineDialog (FilmEditor* ed, shared_ptr<Film> film)
+ : wxDialog (ed, wxID_ANY, _("DCPTimeline"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE)
, _timeline (this, ed, film)
{
wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
@@ -46,11 +46,11 @@ TimelineDialog::TimelineDialog (FilmEditor* ed, shared_ptr<Film> film)
sizer->SetSizeHints (this);
_snap->SetValue (_timeline.snap ());
- _snap->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&TimelineDialog::snap_toggled, this));
+ _snap->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&DCPTimelineDialog::snap_toggled, this));
}
void
-TimelineDialog::snap_toggled ()
+DCPTimelineDialog::snap_toggled ()
{
_timeline.set_snap (_snap->GetValue ());
}
diff --git a/src/wx/timeline_dialog.h b/src/wx/timeline_dialog.h
index 1e5955003..b64445d9c 100644
--- a/src/wx/timeline_dialog.h
+++ b/src/wx/timeline_dialog.h
@@ -24,14 +24,14 @@
class Playlist;
-class TimelineDialog : public wxDialog
+class DCPTimelineDialog : public wxDialog
{
public:
- TimelineDialog (FilmEditor *, boost::shared_ptr<Film>);
+ DCPTimelineDialog (FilmEditor *, boost::shared_ptr<Film>);
private:
void snap_toggled ();
- Timeline _timeline;
+ DCPTimeline _timeline;
wxCheckBox* _snap;
};
diff --git a/src/wx/timing_panel.cc b/src/wx/timing_panel.cc
index 79099b168..08334da4b 100644
--- a/src/wx/timing_panel.cc
+++ b/src/wx/timing_panel.cc
@@ -35,19 +35,19 @@ TimingPanel::TimingPanel (FilmEditor* e)
_sizer->Add (grid, 0, wxALL, 8);
add_label_to_sizer (grid, this, _("Position"), true);
- _position = new Timecode (this);
+ _position = new DCPTimecode (this);
grid->Add (_position);
add_label_to_sizer (grid, this, _("Full length"), true);
- _full_length = new Timecode (this);
+ _full_length = new DCPTimecode (this);
grid->Add (_full_length);
add_label_to_sizer (grid, this, _("Trim from start"), true);
- _trim_start = new Timecode (this);
+ _trim_start = new DCPTimecode (this);
grid->Add (_trim_start);
add_label_to_sizer (grid, this, _("Trim from end"), true);
- _trim_end = new Timecode (this);
+ _trim_end = new DCPTimecode (this);
grid->Add (_trim_end);
add_label_to_sizer (grid, this, _("Play length"), true);
- _play_length = new Timecode (this);
+ _play_length = new DCPTimecode (this);
grid->Add (_play_length);
_position->Changed.connect (boost::bind (&TimingPanel::position_changed, this));
diff --git a/src/wx/timing_panel.h b/src/wx/timing_panel.h
index ab859a1be..7fea45eb5 100644
--- a/src/wx/timing_panel.h
+++ b/src/wx/timing_panel.h
@@ -19,7 +19,7 @@
#include "film_editor_panel.h"
-class Timecode;
+class DCPTimecode;
class TimingPanel : public FilmEditorPanel
{
@@ -36,9 +36,9 @@ private:
void trim_end_changed ();
void play_length_changed ();
- Timecode* _position;
- Timecode* _full_length;
- Timecode* _trim_start;
- Timecode* _trim_end;
- Timecode* _play_length;
+ DCPTimecode* _position;
+ DCPTimecode* _full_length;
+ DCPTimecode* _trim_start;
+ DCPTimecode* _trim_end;
+ DCPTimecode* _play_length;
};
diff --git a/src/wx/video_panel.cc b/src/wx/video_panel.cc
index 90b4adfe3..a777b3e88 100644
--- a/src/wx/video_panel.cc
+++ b/src/wx/video_panel.cc
@@ -334,7 +334,7 @@ VideoPanel::setup_description ()
d << wxString::Format (_("Content frame rate %.4f\n"), vcs->video_frame_rate ());
++lines;
- FrameRateConversion frc (vcs->video_frame_rate(), _editor->film()->video_frame_rate ());
+ FrameRateChange frc (vcs->video_frame_rate(), _editor->film()->video_frame_rate ());
d << frc.description << "\n";
++lines;