2 Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "ffmpeg_decoder.h"
24 #include "ffmpeg_content.h"
25 #include "imagemagick_decoder.h"
26 #include "imagemagick_content.h"
27 #include "sndfile_decoder.h"
28 #include "sndfile_content.h"
29 #include "subtitle_content.h"
34 #include "resampler.h"
44 using boost::shared_ptr;
45 using boost::weak_ptr;
46 using boost::dynamic_pointer_cast;
48 #define DEBUG_PLAYER 1
53 Piece (shared_ptr<Content> c)
55 , video_position (c->start ())
56 , audio_position (c->start ())
59 Piece (shared_ptr<Content> c, shared_ptr<Decoder> d)
62 , video_position (c->start ())
63 , audio_position (c->start ())
66 shared_ptr<Content> content;
67 shared_ptr<Decoder> decoder;
73 std::ostream& operator<<(std::ostream& s, Piece const & p)
75 if (dynamic_pointer_cast<FFmpegContent> (p.content)) {
77 } else if (dynamic_pointer_cast<ImageMagickContent> (p.content)) {
79 } else if (dynamic_pointer_cast<SndfileContent> (p.content)) {
83 s << " at " << p.content->start() << " until " << p.content->end();
89 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
94 , _have_valid_pieces (false)
97 , _audio_buffers (f->dcp_audio_channels(), 0)
99 _playlist->Changed.connect (bind (&Player::playlist_changed, this));
100 _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2));
101 _film->Changed.connect (bind (&Player::film_changed, this, _1));
102 set_video_container_size (_film->container()->size (_film->full_frame ()));
106 Player::disable_video ()
112 Player::disable_audio ()
120 if (!_have_valid_pieces) {
122 _have_valid_pieces = true;
129 Time earliest_t = TIME_MAX;
130 shared_ptr<Piece> earliest;
136 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
137 if ((*i)->decoder->done ()) {
141 if (dynamic_pointer_cast<VideoDecoder> ((*i)->decoder)) {
142 if ((*i)->video_position < earliest_t) {
143 earliest_t = (*i)->video_position;
149 if (dynamic_pointer_cast<AudioDecoder> ((*i)->decoder)) {
150 if ((*i)->audio_position < earliest_t) {
151 earliest_t = (*i)->audio_position;
160 cout << "no earliest piece.\n";
169 if (earliest_t > _video_position) {
171 cout << "no video here; emitting black frame.\n";
176 cout << "Pass " << *earliest << "\n";
178 earliest->decoder->pass ();
183 if (earliest_t > _audio_position) {
185 cout << "no audio here; emitting silence.\n";
187 emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position));
190 cout << "Pass " << *earliest << "\n";
192 earliest->decoder->pass ();
198 cout << "\tpost pass " << _video_position << " " << _audio_position << "\n";
205 Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, bool same, VideoContent::Frame frame)
207 shared_ptr<Piece> piece = weak_piece.lock ();
212 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
215 FrameRateConversion frc (content->video_frame_rate(), _film->dcp_video_frame_rate());
216 if (frc.skip && (frame % 2) == 1) {
220 shared_ptr<Image> work_image = image->crop (content->crop(), true);
222 libdcp::Size const image_size = content->ratio()->size (_video_container_size);
224 work_image = work_image->scale_and_convert_to_rgb (image_size, _film->scaler(), true);
226 Time time = content->start() + (frame * frc.factor() * TIME_HZ / _film->dcp_video_frame_rate());
228 if (_film->with_subtitles ()) {
229 shared_ptr<Subtitle> sub;
230 if (_subtitle && _subtitle->displayed_at (time - _subtitle_content_time)) {
231 sub = _subtitle->subtitle ();
235 dcpomatic::Rect const tx = subtitle_transformed_area (
236 float (image_size.width) / content->video_size().width,
237 float (image_size.height) / content->video_size().height,
238 sub->area(), _subtitle_offset, _subtitle_scale
241 shared_ptr<Image> im = sub->image()->scale (tx.size(), _film->scaler(), true);
242 work_image->alpha_blend (im, tx.position());
246 if (image_size != _video_container_size) {
247 assert (image_size.width <= _video_container_size.width);
248 assert (image_size.height <= _video_container_size.height);
249 shared_ptr<Image> im (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true));
251 im->copy (work_image, Position ((_video_container_size.width - image_size.width) / 2, (_video_container_size.height - image_size.height) / 2));
255 Video (work_image, same, time);
256 time += TIME_HZ / _film->dcp_video_frame_rate();
259 Video (work_image, true, time);
260 time += TIME_HZ / _film->dcp_video_frame_rate();
263 _video_position = piece->video_position = time;
267 Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame)
269 shared_ptr<Piece> piece = weak_piece.lock ();
274 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
277 if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
278 audio = resampler(content)->run (audio);
282 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->dcp_audio_channels(), audio->frames()));
283 dcp_mapped->make_silent ();
284 list<pair<int, libdcp::Channel> > map = content->audio_mapping().content_to_dcp ();
285 for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) {
286 dcp_mapped->accumulate_channel (audio.get(), i->first, i->second);
291 /* The time of this audio may indicate that some of our buffered audio is not going to
292 be added to any more, so it can be emitted.
295 Time const time = content->start() + (frame * TIME_HZ / _film->dcp_audio_frame_rate());
297 if (time > _audio_position) {
298 /* We can emit some audio from our buffers */
299 OutputAudioFrame const N = _film->time_to_audio_frames (time - _audio_position);
300 assert (N <= _audio_buffers.frames());
301 shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), N));
302 emit->copy_from (&_audio_buffers, N, 0, 0);
303 Audio (emit, _audio_position);
304 _audio_position = piece->audio_position = time + _film->audio_frames_to_time (N);
306 /* And remove it from our buffers */
307 if (_audio_buffers.frames() > N) {
308 _audio_buffers.move (N, 0, _audio_buffers.frames() - N);
310 _audio_buffers.set_frames (_audio_buffers.frames() - N);
313 /* Now accumulate the new audio into our buffers */
314 _audio_buffers.ensure_size (_audio_buffers.frames() + audio->frames());
315 _audio_buffers.accumulate_frames (audio.get(), 0, 0, audio->frames ());
316 _audio_buffers.set_frames (_audio_buffers.frames() + audio->frames());
322 if (_audio_buffers.frames() > 0) {
323 shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), _audio_buffers.frames()));
324 emit->copy_from (&_audio_buffers, _audio_buffers.frames(), 0, 0);
325 Audio (emit, _audio_position);
326 _audio_position += _film->audio_frames_to_time (_audio_buffers.frames ());
327 _audio_buffers.set_frames (0);
330 while (_video_position < _audio_position) {
334 while (_audio_position < _video_position) {
335 emit_silence (_film->time_to_audio_frames (_video_position - _audio_position));
340 /** @return true on error */
342 Player::seek (Time t, bool accurate)
344 if (!_have_valid_pieces) {
346 _have_valid_pieces = true;
349 if (_pieces.empty ()) {
353 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
354 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content);
359 Time s = t - vc->start ();
360 s = max (static_cast<Time> (0), s);
361 s = min (vc->length(), s);
363 FrameRateConversion frc (vc->video_frame_rate(), _film->dcp_video_frame_rate());
364 VideoContent::Frame f = s * vc->video_frame_rate() / (frc.factor() * TIME_HZ);
365 dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (f, accurate);
368 /* XXX: don't seek audio because we don't need to... */
372 Player::setup_pieces ()
374 list<shared_ptr<Piece> > old_pieces = _pieces;
378 Playlist::ContentList content = _playlist->content ();
379 sort (content.begin(), content.end(), ContentSorter ());
381 for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) {
383 shared_ptr<Piece> piece (new Piece (*i));
385 /* XXX: into content? */
387 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
389 shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
391 fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3));
392 fd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2));
393 fd->Subtitle.connect (bind (&Player::process_subtitle, this, piece, _1));
398 shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i);
400 shared_ptr<ImageMagickDecoder> id;
402 /* See if we can re-use an old ImageMagickDecoder */
403 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
404 shared_ptr<ImageMagickDecoder> imd = dynamic_pointer_cast<ImageMagickDecoder> ((*j)->decoder);
405 if (imd && imd->content() == ic) {
411 id.reset (new ImageMagickDecoder (_film, ic));
412 id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3));
418 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
420 shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
421 sd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2));
426 _pieces.push_back (piece);
430 cout << "=== Player setup:\n";
431 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
432 cout << *(i->get()) << "\n";
438 Player::content_changed (weak_ptr<Content> w, int p)
440 shared_ptr<Content> c = w.lock ();
446 p == ContentProperty::START || p == ContentProperty::LENGTH ||
447 p == VideoContentProperty::VIDEO_CROP || p == VideoContentProperty::VIDEO_RATIO
450 _have_valid_pieces = false;
456 Player::playlist_changed ()
458 _have_valid_pieces = false;
463 Player::set_video_container_size (libdcp::Size s)
465 _video_container_size = s;
466 _black_frame.reset (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true));
467 _black_frame->make_black ();
470 shared_ptr<Resampler>
471 Player::resampler (shared_ptr<AudioContent> c)
473 map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
474 if (i != _resamplers.end ()) {
478 shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
484 Player::emit_black ()
486 /* XXX: use same here */
487 Video (_black_frame, false, _video_position);
488 _video_position += _film->video_frames_to_time (1);
492 Player::emit_silence (OutputAudioFrame most)
494 OutputAudioFrame N = min (most, _film->dcp_audio_frame_rate() / 2);
495 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->dcp_audio_channels(), N));
496 silence->make_silent ();
497 Audio (silence, _audio_position);
498 _audio_position += _film->audio_frames_to_time (N);
502 Player::film_changed (Film::Property p)
504 /* Here we should notice Film properties that affect our output, and
505 alert listeners that our output now would be different to how it was
506 last time we were run.
509 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER) {
515 Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<TimedSubtitle> sub)
517 shared_ptr<Piece> piece = weak_piece.lock ();
522 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
526 _subtitle_content_time = piece->content->start ();
527 _subtitle_offset = sc->subtitle_offset ();
528 _subtitle_scale = sc->subtitle_scale ();