summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/audio_decoder.cc12
-rw-r--r--src/lib/audio_decoder.h5
-rw-r--r--src/lib/ffmpeg_decoder.cc2
-rw-r--r--src/lib/player.cc108
-rw-r--r--src/lib/player.h21
-rw-r--r--src/lib/sndfile_decoder.cc2
-rw-r--r--src/lib/util.cc18
-rw-r--r--src/lib/util.h8
8 files changed, 127 insertions, 49 deletions
diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc
index 1f5868583..c9fbddda1 100644
--- a/src/lib/audio_decoder.cc
+++ b/src/lib/audio_decoder.cc
@@ -32,8 +32,9 @@ using std::cout;
using boost::optional;
using boost::shared_ptr;
-AudioDecoder::AudioDecoder (shared_ptr<const Film> film)
+AudioDecoder::AudioDecoder (shared_ptr<const Film> film, shared_ptr<const AudioContent> content)
: Decoder (film)
+ , _audio_content (content)
, _audio_position (0)
{
@@ -45,3 +46,12 @@ AudioDecoder::audio (shared_ptr<const AudioBuffers> data, AudioContent::Frame fr
Audio (data, frame);
_audio_position = frame + data->frames ();
}
+
+/** 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 prompting the XXX
+ */
+bool
+AudioDecoder::has_audio () const
+{
+ return _audio_content->channels () > 0;
+}
diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h
index 2ad53da8b..ab6c4b8a9 100644
--- a/src/lib/audio_decoder.h
+++ b/src/lib/audio_decoder.h
@@ -36,7 +36,9 @@ class AudioBuffers;
class AudioDecoder : public virtual Decoder
{
public:
- AudioDecoder (boost::shared_ptr<const Film>);
+ 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;
@@ -44,6 +46,7 @@ public:
protected:
void audio (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
+ boost::shared_ptr<const AudioContent> _audio_content;
AudioContent::Frame _audio_position;
};
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index 8da607e7e..45c242237 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -61,7 +61,7 @@ using libdcp::Size;
FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> c, bool video, bool audio)
: Decoder (f)
, VideoDecoder (f, c)
- , AudioDecoder (f)
+ , AudioDecoder (f, c)
, SubtitleDecoder (f)
, FFmpeg (c)
, _subtitle_codec_context (0)
diff --git a/src/lib/player.cc b/src/lib/player.cc
index 310e91b6c..02d390365 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -55,6 +55,8 @@ public:
: 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)
@@ -63,11 +65,50 @@ public:
, video_position (c->position ())
, audio_position (c->position ())
{}
+
+ void set_repeat (IncomingVideo video, int num)
+ {
+ cout << "Set repeat " << num << "\n";
+ 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)
+ {
+ cout << "repeating; " << repeat_done << "\n";
+ player->process_video (
+ repeat_video.weak_piece,
+ repeat_video.image,
+ repeat_video.eyes,
+ repeat_video.same,
+ repeat_video.frame,
+ (repeat_done + 1) * (TIME_HZ / player->_film->video_frame_rate ())
+ );
+
+ ++repeat_done;
+ }
shared_ptr<Content> content;
shared_ptr<Decoder> decoder;
Time video_position;
Time audio_position;
+
+ IncomingVideo repeat_video;
+ int repeat_to_do;
+ int repeat_done;
};
Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
@@ -116,6 +157,7 @@ Player::pass ()
for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
if ((*i)->decoder->done ()) {
+ cout << "Scan: done.\n";
continue;
}
@@ -137,20 +179,31 @@ Player::pass ()
}
if (!earliest) {
+ cout << "No earliest: out.\n";
flush ();
return true;
}
+ cout << "Earliest: " << earliest_t << "\n";
+
switch (type) {
case VIDEO:
+ cout << "VIDEO.\n";
if (earliest_t > _video_position) {
emit_black ();
} else {
- earliest->decoder->pass ();
+ if (earliest->repeating ()) {
+ cout << "-repeating.\n";
+ earliest->repeat (this);
+ } else {
+ cout << "-passing.\n";
+ earliest->decoder->pass ();
+ }
}
break;
case AUDIO:
+ cout << "SOUND.\n";
if (earliest_t > _audio_position) {
emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position));
} else {
@@ -188,14 +241,16 @@ Player::pass ()
}
void
-Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, Eyes eyes, bool same, VideoContent::Frame frame)
+Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, Eyes eyes, bool same, VideoContent::Frame frame, Time extra)
{
+ cout << "PLAYER RECEIVES A VIDEO FRAME, extra " << extra << "\n";
+
/* Keep a note of what came in so that we can repeat it if required */
- _last_process_video.weak_piece = weak_piece;
- _last_process_video.image = image;
- _last_process_video.eyes = eyes;
- _last_process_video.same = same;
- _last_process_video.frame = frame;
+ _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;
shared_ptr<Piece> piece = weak_piece.lock ();
if (!piece) {
@@ -225,7 +280,7 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image
work_image = work_image->scale (image_size, _film->scaler(), PIX_FMT_RGB24, true);
- Time time = content->position() + relative_time - content->trim_start ();
+ Time time = content->position() + relative_time + extra - content->trim_start ();
if (_film->with_subtitles () && _out_subtitle.image && time >= _out_subtitle.from && time <= _out_subtitle.to) {
work_image->alpha_blend (_out_subtitle.image, _out_subtitle.position);
@@ -245,16 +300,16 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image
#endif
Video (work_image, eyes, content->colour_conversion(), same, time);
- time += TIME_HZ / _film->video_frame_rate();
-
- if (frc.repeat) {
- Video (work_image, eyes, content->colour_conversion(), true, time);
- time += TIME_HZ / _film->video_frame_rate();
- }
+ time += TIME_HZ / _film->video_frame_rate();
_last_emit_was_black = false;
-
_video_position = piece->video_position = time;
+
+ cout << "frc.repeat=" << frc.repeat << "; vp now " << _video_position << "\n";
+
+ if (frc.repeat > 1 && !piece->repeating ()) {
+ piece->set_repeat (_last_incoming_video, frc.repeat - 1);
+ }
}
void
@@ -375,10 +430,12 @@ Player::seek (Time t, bool accurate)
*/
VideoContent::Frame f = (s + vc->trim_start ()) * _film->video_frame_rate() / (frc.factor() * TIME_HZ);
dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (f, accurate);
+
+ (*i)->reset_repeat ();
}
_video_position = _audio_position = t;
-
+
/* XXX: don't seek audio because we don't need to... */
}
@@ -402,7 +459,7 @@ Player::setup_pieces ()
if (fc) {
shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
- fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4));
+ fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4, 0));
fd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2));
fd->Subtitle.connect (bind (&Player::process_subtitle, this, piece, _1, _2, _3, _4));
@@ -423,7 +480,7 @@ Player::setup_pieces ()
if (!id) {
id.reset (new StillImageDecoder (_film, ic));
- id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4));
+ id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4, 0));
}
piece->decoder = id;
@@ -435,7 +492,7 @@ Player::setup_pieces ()
if (!md) {
md.reset (new MovingImageDecoder (_film, mc));
- md->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4));
+ md->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4, 0));
}
piece->decoder = md;
@@ -626,16 +683,17 @@ Player::update_subtitle ()
bool
Player::repeat_last_video ()
{
- if (!_last_process_video.image) {
+ if (!_last_incoming_video.image) {
return false;
}
process_video (
- _last_process_video.weak_piece,
- _last_process_video.image,
- _last_process_video.eyes,
- _last_process_video.same,
- _last_process_video.frame
+ _last_incoming_video.weak_piece,
+ _last_incoming_video.image,
+ _last_incoming_video.eyes,
+ _last_incoming_video.same,
+ _last_incoming_video.frame,
+ 0
);
return true;
diff --git a/src/lib/player.h b/src/lib/player.h
index 7cce7e723..424a39216 100644
--- a/src/lib/player.h
+++ b/src/lib/player.h
@@ -41,6 +41,16 @@ 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;
+};
class Player : public boost::enable_shared_from_this<Player>, public boost::noncopyable
{
@@ -83,8 +93,9 @@ public:
private:
friend class PlayerWrapper;
+ friend class Piece;
- void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const Image>, Eyes, bool, VideoContent::Frame);
+ 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 ();
@@ -140,13 +151,7 @@ private:
bool _last_emit_was_black;
- struct {
- boost::weak_ptr<Piece> weak_piece;
- boost::shared_ptr<const Image> image;
- Eyes eyes;
- bool same;
- VideoContent::Frame frame;
- } _last_process_video;
+ IncomingVideo _last_incoming_video;
boost::signals2::scoped_connection _playlist_changed_connection;
boost::signals2::scoped_connection _playlist_content_changed_connection;
diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc
index 1fc1ecaf2..09ccf4fbc 100644
--- a/src/lib/sndfile_decoder.cc
+++ b/src/lib/sndfile_decoder.cc
@@ -35,7 +35,7 @@ using boost::shared_ptr;
SndfileDecoder::SndfileDecoder (shared_ptr<const Film> f, shared_ptr<const SndfileContent> c)
: Decoder (f)
- , AudioDecoder (f)
+ , AudioDecoder (f, c)
, _sndfile_content (c)
, _deinterleave_buffer (0)
{
diff --git a/src/lib/util.cc b/src/lib/util.cc
index 1c4347233..15efcc099 100644
--- a/src/lib/util.cc
+++ b/src/lib/util.cc
@@ -744,7 +744,7 @@ audio_channel_name (int c)
assert (MAX_AUDIO_CHANNELS == 6);
/* TRANSLATORS: these are the names of audio channels; Lfe (sub) is the low-frequency
- enhancement channel (sub-woofer)./
+ enhancement channel (sub-woofer).
*/
string const channels[] = {
_("Left"),
@@ -760,24 +760,28 @@ audio_channel_name (int c)
FrameRateConversion::FrameRateConversion (float source, int dcp)
: skip (false)
- , repeat (false)
+ , repeat (1)
, change_speed (false)
{
- if (fabs (source / 2.0 - dcp) < (fabs (source - dcp))) {
+ if (source > (dcp * 2)) {
skip = true;
- } else if (fabs (source * 2 - dcp) < fabs (source - dcp)) {
- repeat = true;
+ }
+
+ if (source < dcp) {
+ repeat = floor (dcp / source);
}
change_speed = !about_equal (source * factor(), dcp);
- if (!skip && !repeat && !change_speed) {
+ if (!skip && repeat == 1 && !change_speed) {
description = _("Content and DCP have the same rate.\n");
} else {
if (skip) {
description = _("DCP will use every other frame of the content.\n");
- } else if (repeat) {
+ } else if (repeat == 2) {
description = _("Each content frame will be doubled in the DCP.\n");
+ } else if (repeat > 2) {
+ description = String::compose (_("Each content frame will be repeated %1 more times in the DCP.\n"), repeat - 1);
}
if (change_speed) {
diff --git a/src/lib/util.h b/src/lib/util.h
index b8ea6ebec..70cb3bb0c 100644
--- a/src/lib/util.h
+++ b/src/lib/util.h
@@ -88,17 +88,15 @@ struct FrameRateConversion
float factor () const {
if (skip) {
return 0.5;
- } else if (repeat) {
- return 2;
}
- return 1;
+ return repeat;
}
/** true to skip every other frame */
bool skip;
- /** true to repeat every frame once */
- bool repeat;
+ /** number of times to use each frame (e.g. 1 is normal, 2 means repeat each frame once, and so on) */
+ int repeat;
/** true if this DCP will run its video faster or slower than the source
* without taking into account `repeat' nor `skip'.
* (e.g. change_speed will be true if