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 (_video && dynamic_pointer_cast<VideoDecoder> ((*i)->decoder)) {
142 if ((*i)->video_position < earliest_t) {
143 earliest_t = (*i)->video_position;
149 if (_audio && 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 (earliest=" << earliest_t << ", video_position=" << _video_position << ").\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 () && _out_subtitle.image && time >= _out_subtitle.from && time <= _out_subtitle.to) {
229 work_image->alpha_blend (_out_subtitle.image, _out_subtitle.position);
232 if (image_size != _video_container_size) {
233 assert (image_size.width <= _video_container_size.width);
234 assert (image_size.height <= _video_container_size.height);
235 shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
237 im->copy (work_image, Position<int> ((_video_container_size.width - image_size.width) / 2, (_video_container_size.height - image_size.height) / 2));
241 Video (work_image, same, time);
242 time += TIME_HZ / _film->dcp_video_frame_rate();
245 Video (work_image, true, time);
246 time += TIME_HZ / _film->dcp_video_frame_rate();
249 _video_position = piece->video_position = time;
253 Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame)
255 shared_ptr<Piece> piece = weak_piece.lock ();
260 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
264 if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
265 shared_ptr<Resampler> r = resampler (content);
266 audio = r->run (audio);
270 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->dcp_audio_channels(), audio->frames()));
271 dcp_mapped->make_silent ();
272 list<pair<int, libdcp::Channel> > map = content->audio_mapping().content_to_dcp ();
273 for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) {
274 if (i->first < audio->channels() && i->second < dcp_mapped->channels()) {
275 dcp_mapped->accumulate_channel (audio.get(), i->first, i->second);
281 Time time = content->start() + (frame * TIME_HZ / _film->dcp_audio_frame_rate()) + (content->audio_delay() * TIME_HZ / 1000);
283 /* We must cut off anything that comes before the start of all time */
285 int const frames = - time * _film->dcp_audio_frame_rate() / TIME_HZ;
286 if (frames >= audio->frames ()) {
290 shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->channels(), audio->frames() - frames));
291 trimmed->copy_from (audio.get(), audio->frames() - frames, frames, 0);
297 /* The time of this audio may indicate that some of our buffered audio is not going to
298 be added to any more, so it can be emitted.
301 if (time > _audio_position) {
302 /* We can emit some audio from our buffers */
303 OutputAudioFrame const N = _film->time_to_audio_frames (time - _audio_position);
304 if (N > _audio_buffers.frames()) {
305 /* We need some extra silence before whatever is in the buffers */
306 _audio_buffers.ensure_size (N);
307 _audio_buffers.move (0, N - _audio_buffers.frames(), _audio_buffers.frames ());
308 _audio_buffers.make_silent (0, _audio_buffers.frames());
309 _audio_buffers.set_frames (N);
311 assert (N <= _audio_buffers.frames());
312 shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), N));
313 emit->copy_from (&_audio_buffers, N, 0, 0);
314 Audio (emit, _audio_position);
315 _audio_position = piece->audio_position = time + _film->audio_frames_to_time (N);
317 /* And remove it from our buffers */
318 if (_audio_buffers.frames() > N) {
319 _audio_buffers.move (N, 0, _audio_buffers.frames() - N);
321 _audio_buffers.set_frames (_audio_buffers.frames() - N);
324 /* Now accumulate the new audio into our buffers */
325 _audio_buffers.ensure_size (_audio_buffers.frames() + audio->frames());
326 _audio_buffers.accumulate_frames (audio.get(), 0, 0, audio->frames ());
327 _audio_buffers.set_frames (_audio_buffers.frames() + audio->frames());
333 if (_audio_buffers.frames() > 0) {
334 shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), _audio_buffers.frames()));
335 emit->copy_from (&_audio_buffers, _audio_buffers.frames(), 0, 0);
336 Audio (emit, _audio_position);
337 _audio_position += _film->audio_frames_to_time (_audio_buffers.frames ());
338 _audio_buffers.set_frames (0);
341 while (_video_position < _audio_position) {
345 while (_audio_position < _video_position) {
346 emit_silence (_film->time_to_audio_frames (_video_position - _audio_position));
351 /** @return true on error */
353 Player::seek (Time t, bool accurate)
355 if (!_have_valid_pieces) {
357 _have_valid_pieces = true;
360 if (_pieces.empty ()) {
364 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
365 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content);
370 Time s = t - vc->start ();
371 s = max (static_cast<Time> (0), s);
372 s = min (vc->length(), s);
374 (*i)->video_position = (*i)->audio_position = vc->start() + s;
376 FrameRateConversion frc (vc->video_frame_rate(), _film->dcp_video_frame_rate());
377 VideoContent::Frame f = s * vc->video_frame_rate() / (frc.factor() * TIME_HZ);
378 dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (f, accurate);
381 _video_position = _audio_position = t;
382 /* XXX: don't seek audio because we don't need to... */
386 Player::setup_pieces ()
388 list<shared_ptr<Piece> > old_pieces = _pieces;
392 Playlist::ContentList content = _playlist->content ();
393 sort (content.begin(), content.end(), ContentSorter ());
395 for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) {
397 shared_ptr<Piece> piece (new Piece (*i));
399 /* XXX: into content? */
401 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
403 shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
405 fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3));
406 fd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2));
407 fd->Subtitle.connect (bind (&Player::process_subtitle, this, piece, _1, _2, _3, _4));
412 shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i);
414 shared_ptr<ImageMagickDecoder> id;
416 /* See if we can re-use an old ImageMagickDecoder */
417 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
418 shared_ptr<ImageMagickDecoder> imd = dynamic_pointer_cast<ImageMagickDecoder> ((*j)->decoder);
419 if (imd && imd->content() == ic) {
425 id.reset (new ImageMagickDecoder (_film, ic));
426 id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3));
432 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
434 shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
435 sd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2));
440 _pieces.push_back (piece);
444 cout << "=== Player setup:\n";
445 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
446 cout << *(i->get()) << "\n";
452 Player::content_changed (weak_ptr<Content> w, int p)
454 shared_ptr<Content> c = w.lock ();
460 p == ContentProperty::START || p == ContentProperty::LENGTH ||
461 p == VideoContentProperty::VIDEO_CROP || p == VideoContentProperty::VIDEO_RATIO
464 _have_valid_pieces = false;
467 } else if (p == SubtitleContentProperty::SUBTITLE_OFFSET || p == SubtitleContentProperty::SUBTITLE_SCALE) {
474 Player::playlist_changed ()
476 _have_valid_pieces = false;
481 Player::set_video_container_size (libdcp::Size s)
483 _video_container_size = s;
484 _black_frame.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
485 _black_frame->make_black ();
488 shared_ptr<Resampler>
489 Player::resampler (shared_ptr<AudioContent> c)
491 map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
492 if (i != _resamplers.end ()) {
496 shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
502 Player::emit_black ()
504 /* XXX: use same here */
505 Video (_black_frame, false, _video_position);
506 _video_position += _film->video_frames_to_time (1);
510 Player::emit_silence (OutputAudioFrame most)
512 OutputAudioFrame N = min (most, _film->dcp_audio_frame_rate() / 2);
513 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->dcp_audio_channels(), N));
514 silence->make_silent ();
515 Audio (silence, _audio_position);
516 _audio_position += _film->audio_frames_to_time (N);
520 Player::film_changed (Film::Property p)
522 /* Here we should notice Film properties that affect our output, and
523 alert listeners that our output now would be different to how it was
524 last time we were run.
527 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER) {
533 Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to)
535 _in_subtitle.piece = weak_piece;
536 _in_subtitle.image = image;
537 _in_subtitle.rect = rect;
538 _in_subtitle.from = from;
539 _in_subtitle.to = to;
545 Player::update_subtitle ()
547 shared_ptr<Piece> piece = _in_subtitle.piece.lock ();
552 if (!_in_subtitle.image) {
553 _out_subtitle.image.reset ();
557 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
560 dcpomatic::Rect<double> in_rect = _in_subtitle.rect;
561 libdcp::Size scaled_size;
563 in_rect.y += sc->subtitle_offset ();
565 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
566 scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
567 scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
569 /* Then we need a corrective translation, consisting of two parts:
571 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
572 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
574 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
575 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
576 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
578 * Combining these two translations gives these expressions.
581 _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
582 _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
584 _out_subtitle.image = _in_subtitle.image->scale (libdcp::Size (scaled_size.width, scaled_size.height), Scaler::from_id ("bicubic"), true);
585 _out_subtitle.from = _in_subtitle.from + piece->content->start ();
586 _out_subtitle.to = _in_subtitle.to + piece->content->start ();