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 () && _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 SimpleImage (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);
269 _last_resampler.reset ();
273 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->dcp_audio_channels(), audio->frames()));
274 dcp_mapped->make_silent ();
275 list<pair<int, libdcp::Channel> > map = content->audio_mapping().content_to_dcp ();
276 for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) {
277 dcp_mapped->accumulate_channel (audio.get(), i->first, i->second);
282 Time time = content->start() + (frame * TIME_HZ / _film->dcp_audio_frame_rate()) + (content->audio_delay() * TIME_HZ / 1000);
284 /* We must cut off anything that comes before the start of all time */
286 int const frames = - time * _film->dcp_audio_frame_rate() / TIME_HZ;
287 if (frames >= audio->frames ()) {
291 shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->channels(), audio->frames() - frames));
292 trimmed->copy_from (audio.get(), audio->frames() - frames, frames, 0);
298 /* The time of this audio may indicate that some of our buffered audio is not going to
299 be added to any more, so it can be emitted.
302 if (time > _audio_position) {
303 /* We can emit some audio from our buffers */
304 OutputAudioFrame const N = _film->time_to_audio_frames (time - _audio_position);
305 if (N > _audio_buffers.frames()) {
306 /* We need some extra silence before whatever is in the buffers */
307 _audio_buffers.ensure_size (N);
308 _audio_buffers.move (0, N - _audio_buffers.frames(), _audio_buffers.frames ());
309 _audio_buffers.make_silent (0, _audio_buffers.frames());
310 _audio_buffers.set_frames (N);
313 if (N > _audio_buffers.frames()) {
314 cout << "N=" << N << ", ab=" << _audio_buffers.frames() << "\n";
316 assert (N <= _audio_buffers.frames());
317 shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), N));
318 emit->copy_from (&_audio_buffers, N, 0, 0);
319 Audio (emit, _audio_position);
320 _audio_position = piece->audio_position = time + _film->audio_frames_to_time (N);
322 /* And remove it from our buffers */
323 if (_audio_buffers.frames() > N) {
324 _audio_buffers.move (N, 0, _audio_buffers.frames() - N);
326 _audio_buffers.set_frames (_audio_buffers.frames() - N);
329 /* Now accumulate the new audio into our buffers */
330 _audio_buffers.ensure_size (_audio_buffers.frames() + audio->frames());
331 _audio_buffers.accumulate_frames (audio.get(), 0, 0, audio->frames ());
332 _audio_buffers.set_frames (_audio_buffers.frames() + audio->frames());
338 if (_audio_buffers.frames() > 0) {
339 shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), _audio_buffers.frames()));
340 emit->copy_from (&_audio_buffers, _audio_buffers.frames(), 0, 0);
341 Audio (emit, _audio_position);
342 _audio_position += _film->audio_frames_to_time (_audio_buffers.frames ());
343 _audio_buffers.set_frames (0);
346 if (_last_resampler) {
347 // shared_ptr<const AudioBuffers> resamp = _last_resampler->flush ();
348 // Audio (resamp, _audio_position);
349 // _audio_position += _film->audio_frames_to_time (resamp->frames ());
352 while (_video_position < _audio_position) {
356 while (_audio_position < _video_position) {
357 emit_silence (_film->time_to_audio_frames (_video_position - _audio_position));
362 /** @return true on error */
364 Player::seek (Time t, bool accurate)
366 if (!_have_valid_pieces) {
368 _have_valid_pieces = true;
371 if (_pieces.empty ()) {
375 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
376 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content);
381 Time s = t - vc->start ();
382 s = max (static_cast<Time> (0), s);
383 s = min (vc->length(), s);
385 FrameRateConversion frc (vc->video_frame_rate(), _film->dcp_video_frame_rate());
386 VideoContent::Frame f = s * vc->video_frame_rate() / (frc.factor() * TIME_HZ);
387 dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (f, accurate);
390 /* XXX: don't seek audio because we don't need to... */
394 Player::setup_pieces ()
396 list<shared_ptr<Piece> > old_pieces = _pieces;
400 Playlist::ContentList content = _playlist->content ();
401 sort (content.begin(), content.end(), ContentSorter ());
403 for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) {
405 shared_ptr<Piece> piece (new Piece (*i));
407 /* XXX: into content? */
409 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
411 shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
413 fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3));
414 fd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2));
415 fd->Subtitle.connect (bind (&Player::process_subtitle, this, piece, _1, _2, _3, _4));
420 shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i);
422 shared_ptr<ImageMagickDecoder> id;
424 /* See if we can re-use an old ImageMagickDecoder */
425 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
426 shared_ptr<ImageMagickDecoder> imd = dynamic_pointer_cast<ImageMagickDecoder> ((*j)->decoder);
427 if (imd && imd->content() == ic) {
433 id.reset (new ImageMagickDecoder (_film, ic));
434 id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3));
440 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
442 shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
443 sd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2));
448 _pieces.push_back (piece);
452 cout << "=== Player setup:\n";
453 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
454 cout << *(i->get()) << "\n";
460 Player::content_changed (weak_ptr<Content> w, int p)
462 shared_ptr<Content> c = w.lock ();
468 p == ContentProperty::START || p == ContentProperty::LENGTH ||
469 p == VideoContentProperty::VIDEO_CROP || p == VideoContentProperty::VIDEO_RATIO
472 _have_valid_pieces = false;
475 } else if (p == SubtitleContentProperty::SUBTITLE_OFFSET || p == SubtitleContentProperty::SUBTITLE_SCALE) {
482 Player::playlist_changed ()
484 _have_valid_pieces = false;
489 Player::set_video_container_size (libdcp::Size s)
491 _video_container_size = s;
492 _black_frame.reset (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true));
493 _black_frame->make_black ();
496 shared_ptr<Resampler>
497 Player::resampler (shared_ptr<AudioContent> c)
499 map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
500 if (i != _resamplers.end ()) {
504 shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
510 Player::emit_black ()
512 /* XXX: use same here */
513 Video (_black_frame, false, _video_position);
514 _video_position += _film->video_frames_to_time (1);
518 Player::emit_silence (OutputAudioFrame most)
520 OutputAudioFrame N = min (most, _film->dcp_audio_frame_rate() / 2);
521 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->dcp_audio_channels(), N));
522 silence->make_silent ();
523 Audio (silence, _audio_position);
524 _audio_position += _film->audio_frames_to_time (N);
528 Player::film_changed (Film::Property p)
530 /* Here we should notice Film properties that affect our output, and
531 alert listeners that our output now would be different to how it was
532 last time we were run.
535 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER) {
541 Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to)
543 _in_subtitle.piece = weak_piece;
544 _in_subtitle.image = image;
545 _in_subtitle.rect = rect;
546 _in_subtitle.from = from;
547 _in_subtitle.to = to;
553 Player::update_subtitle ()
555 shared_ptr<Piece> piece = _in_subtitle.piece.lock ();
560 if (!_in_subtitle.image) {
561 _out_subtitle.image.reset ();
565 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
568 dcpomatic::Rect<double> in_rect = _in_subtitle.rect;
569 libdcp::Size scaled_size;
571 in_rect.y += sc->subtitle_offset ();
573 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
574 scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
575 scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
577 /* Then we need a corrective translation, consisting of two parts:
579 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
580 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
582 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
583 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
584 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
586 * Combining these two translations gives these expressions.
589 _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
590 _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
592 _out_subtitle.image = _in_subtitle.image->scale (libdcp::Size (scaled_size.width, scaled_size.height), Scaler::from_id ("bicubic"), true);
593 _out_subtitle.from = _in_subtitle.from + piece->content->start ();
594 _out_subtitle.to = _in_subtitle.to + piece->content->start ();