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.
24 #include "ffmpeg_decoder.h"
25 #include "ffmpeg_content.h"
26 #include "image_decoder.h"
27 #include "image_content.h"
28 #include "sndfile_decoder.h"
29 #include "sndfile_content.h"
30 #include "subtitle_content.h"
45 using boost::shared_ptr;
46 using boost::weak_ptr;
47 using boost::dynamic_pointer_cast;
48 using boost::optional;
53 Piece (shared_ptr<Content> c, shared_ptr<Decoder> d, FrameRateChange f)
59 shared_ptr<Content> content;
60 shared_ptr<Decoder> decoder;
64 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
69 , _have_valid_pieces (false)
72 , _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1))
73 , _last_emit_was_black (false)
74 , _just_did_inaccurate_seek (false)
75 , _approximate_size (false)
77 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
78 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
79 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
80 set_video_container_size (fit_ratio_within (_film->container()->ratio (), _film->full_frame ()));
84 Player::disable_video ()
90 Player::disable_audio ()
98 if (!_have_valid_pieces) {
102 /* Interrogate all our pieces to find the one with the earliest decoded data */
104 shared_ptr<Piece> earliest_piece;
105 shared_ptr<Decoded> earliest_decoded;
106 DCPTime earliest_time = TIME_MAX;
107 DCPTime earliest_audio = TIME_MAX;
109 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
111 DCPTime const offset = (*i)->content->position() - (*i)->content->trim_start();
114 shared_ptr<Decoded> dec;
116 dec = (*i)->decoder->peek ();
118 /* Decoder has nothing else to give us */
122 dec->set_dcp_times (_film->video_frame_rate(), _film->audio_frame_rate(), (*i)->frc, offset);
123 DCPTime const t = dec->dcp_time - offset;
124 if (t >= ((*i)->content->full_length() - (*i)->content->trim_end ())) {
125 /* In the end-trimmed part; decoder has nothing else to give us */
128 } else if (t >= (*i)->content->trim_start ()) {
129 /* Within the un-trimmed part; everything's ok */
132 /* Within the start-trimmed part; get something else */
133 (*i)->decoder->consume ();
141 if (dec->dcp_time < earliest_time) {
143 earliest_decoded = dec;
144 earliest_time = dec->dcp_time;
147 if (dynamic_pointer_cast<DecodedAudio> (dec) && dec->dcp_time < earliest_audio) {
148 earliest_audio = dec->dcp_time;
152 if (!earliest_piece) {
157 if (earliest_audio != TIME_MAX) {
158 TimedAudioBuffers<DCPTime> tb = _audio_merger.pull (max (int64_t (0), earliest_audio));
159 Audio (tb.audio, tb.time);
160 /* This assumes that the audio_frames_to_time conversion is exact
161 so that there are no accumulated errors caused by rounding.
163 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
166 /* Emit the earliest thing */
168 shared_ptr<DecodedVideo> dv = dynamic_pointer_cast<DecodedVideo> (earliest_decoded);
169 shared_ptr<DecodedAudio> da = dynamic_pointer_cast<DecodedAudio> (earliest_decoded);
170 shared_ptr<DecodedSubtitle> ds = dynamic_pointer_cast<DecodedSubtitle> (earliest_decoded);
172 /* Will be set to false if we shouldn't consume the peeked DecodedThing */
177 if (_just_did_inaccurate_seek) {
179 /* Just emit; no subtlety */
180 emit_video (earliest_piece, dv);
181 step_video_position (dv);
183 } else if (dv->dcp_time > _video_position) {
187 list<shared_ptr<Piece> >::iterator i = _pieces.begin();
188 while (i != _pieces.end() && ((*i)->content->position() >= _video_position || _video_position >= (*i)->content->end())) {
192 if (i == _pieces.end() || !_last_incoming_video.video || !_have_valid_pieces) {
193 /* We're outside all video content */
195 _statistics.video.black++;
197 /* We're inside some video; repeat the frame */
198 _last_incoming_video.video->dcp_time = _video_position;
199 emit_video (_last_incoming_video.weak_piece, _last_incoming_video.video);
200 step_video_position (_last_incoming_video.video);
201 _statistics.video.repeat++;
206 } else if (dv->dcp_time == _video_position) {
208 emit_video (earliest_piece, dv);
209 step_video_position (dv);
210 _statistics.video.good++;
212 /* Too far behind: skip */
213 _statistics.video.skip++;
216 _just_did_inaccurate_seek = false;
218 } else if (da && _audio) {
220 if (da->dcp_time > _audio_position) {
222 emit_silence (da->dcp_time - _audio_position);
224 _statistics.audio.silence += (da->dcp_time - _audio_position);
225 } else if (da->dcp_time == _audio_position) {
227 emit_audio (earliest_piece, da);
228 _statistics.audio.good += da->data->frames();
230 /* Too far behind: skip */
231 _statistics.audio.skip += da->data->frames();
234 } else if (ds && _video) {
235 _in_subtitle.piece = earliest_piece;
236 _in_subtitle.subtitle = ds;
241 earliest_piece->decoder->consume ();
248 Player::emit_video (weak_ptr<Piece> weak_piece, shared_ptr<DecodedVideo> video)
250 /* Keep a note of what came in so that we can repeat it if required */
251 _last_incoming_video.weak_piece = weak_piece;
252 _last_incoming_video.video = video;
254 shared_ptr<Piece> piece = weak_piece.lock ();
259 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
262 FrameRateChange frc (content->video_frame_rate(), _film->video_frame_rate());
264 float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio();
265 libdcp::Size image_size = fit_ratio_within (ratio, _video_container_size);
266 if (_approximate_size) {
267 image_size.width &= ~3;
268 image_size.height &= ~3;
271 shared_ptr<PlayerImage> pi (
276 _video_container_size,
282 _film->with_subtitles () &&
283 _out_subtitle.image &&
284 video->dcp_time >= _out_subtitle.from && video->dcp_time <= _out_subtitle.to
287 Position<int> const container_offset (
288 (_video_container_size.width - image_size.width) / 2,
289 (_video_container_size.height - image_size.height) / 2
292 pi->set_subtitle (_out_subtitle.image, _out_subtitle.position + container_offset);
296 #ifdef DCPOMATIC_DEBUG
297 _last_video = piece->content;
300 Video (pi, video->eyes, content->colour_conversion(), video->same, video->dcp_time);
302 _last_emit_was_black = false;
306 Player::step_video_position (shared_ptr<DecodedVideo> video)
308 /* This is a bit of a hack; don't update _video_position if EYES_RIGHT is on its way */
309 if (video->eyes != EYES_LEFT) {
310 /* This assumes that the video_frames_to_time conversion is exact
311 so that there are no accumulated errors caused by rounding.
313 _video_position += _film->video_frames_to_time (1);
318 Player::emit_audio (weak_ptr<Piece> weak_piece, shared_ptr<DecodedAudio> audio)
320 shared_ptr<Piece> piece = weak_piece.lock ();
325 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
329 if (content->audio_gain() != 0) {
330 shared_ptr<AudioBuffers> gain (new AudioBuffers (audio->data));
331 gain->apply_gain (content->audio_gain ());
336 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->data->frames()));
337 dcp_mapped->make_silent ();
338 AudioMapping map = content->audio_mapping ();
339 for (int i = 0; i < map.content_channels(); ++i) {
340 for (int j = 0; j < _film->audio_channels(); ++j) {
341 if (map.get (i, static_cast<libdcp::Channel> (j)) > 0) {
342 dcp_mapped->accumulate_channel (
345 static_cast<libdcp::Channel> (j),
346 map.get (i, static_cast<libdcp::Channel> (j))
352 audio->data = dcp_mapped;
355 audio->dcp_time += content->audio_delay() * TIME_HZ / 1000;
356 if (audio->dcp_time < 0) {
357 int const frames = - audio->dcp_time * _film->audio_frame_rate() / TIME_HZ;
358 if (frames >= audio->data->frames ()) {
362 shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->data->channels(), audio->data->frames() - frames));
363 trimmed->copy_from (audio->data.get(), audio->data->frames() - frames, frames, 0);
365 audio->data = trimmed;
369 _audio_merger.push (audio->data, audio->dcp_time);
375 TimedAudioBuffers<DCPTime> tb = _audio_merger.flush ();
376 if (_audio && tb.audio) {
377 Audio (tb.audio, tb.time);
378 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
381 while (_video && _video_position < _audio_position) {
385 while (_audio && _audio_position < _video_position) {
386 emit_silence (_video_position - _audio_position);
391 /** Seek so that the next pass() will yield (approximately) the requested frame.
392 * Pass accurate = true to try harder to get close to the request.
393 * @return true on error
396 Player::seek (DCPTime t, bool accurate)
398 if (!_have_valid_pieces) {
402 if (_pieces.empty ()) {
406 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
407 /* s is the offset of t from the start position of this content */
408 DCPTime s = t - (*i)->content->position ();
409 s = max (static_cast<DCPTime> (0), s);
410 s = min ((*i)->content->length_after_trim(), s);
412 /* Convert this to the content time */
413 ContentTime ct = (s + (*i)->content->trim_start()) * (*i)->frc.speed_up;
415 /* And seek the decoder */
416 (*i)->decoder->seek (ct, accurate);
419 _video_position = time_round_up (t, TIME_HZ / _film->video_frame_rate());
420 _audio_position = time_round_up (t, TIME_HZ / _film->audio_frame_rate());
422 _audio_merger.clear (_audio_position);
425 /* We just did an inaccurate seek, so it's likely that the next thing seen
426 out of pass() will be a fair distance from _{video,audio}_position. Setting
427 this flag stops pass() from trying to fix that: we assume that if it
428 was an inaccurate seek then the caller does not care too much about
429 inserting black/silence to keep the time tidy.
431 _just_did_inaccurate_seek = true;
436 Player::setup_pieces ()
438 list<shared_ptr<Piece> > old_pieces = _pieces;
441 ContentList content = _playlist->content ();
443 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
445 shared_ptr<Decoder> decoder;
446 optional<FrameRateChange> frc;
448 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
450 decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
451 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
454 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
456 /* See if we can re-use an old ImageDecoder */
457 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
458 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
459 if (imd && imd->content() == ic) {
465 decoder.reset (new ImageDecoder (_film, ic));
468 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
471 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
473 decoder.reset (new SndfileDecoder (_film, sc));
475 /* Working out the frc for this content is a bit tricky: what if it overlaps
476 two pieces of video content with different frame rates? For now, use
477 the one with the best overlap.
480 DCPTime best_overlap_t = 0;
481 shared_ptr<VideoContent> best_overlap;
482 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
483 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
488 DCPTime const overlap = max (vc->position(), sc->position()) - min (vc->end(), sc->end());
489 if (overlap > best_overlap_t) {
491 best_overlap_t = overlap;
496 frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
498 /* No video overlap; e.g. if the DCP is just audio */
499 frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
503 ContentTime st = (*i)->trim_start() * frc->speed_up;
504 decoder->seek (st, true);
506 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
509 _have_valid_pieces = true;
511 /* The Piece for the _last_incoming_video will no longer be valid */
512 _last_incoming_video.video.reset ();
514 _video_position = _audio_position = 0;
518 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
520 shared_ptr<Content> c = w.lock ();
526 property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
527 property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
528 property == VideoContentProperty::VIDEO_FRAME_TYPE
531 _have_valid_pieces = false;
534 } else if (property == SubtitleContentProperty::SUBTITLE_OFFSET || property == SubtitleContentProperty::SUBTITLE_SCALE) {
540 property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO ||
541 property == VideoContentProperty::VIDEO_FRAME_RATE
546 } else if (property == ContentProperty::PATH) {
553 Player::playlist_changed ()
555 _have_valid_pieces = false;
560 Player::set_video_container_size (libdcp::Size s)
562 _video_container_size = s;
564 shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
571 _video_container_size,
572 _video_container_size,
573 Scaler::from_id ("bicubic")
579 Player::emit_black ()
581 #ifdef DCPOMATIC_DEBUG
582 _last_video.reset ();
585 Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
586 _video_position += _film->video_frames_to_time (1);
587 _last_emit_was_black = true;
591 Player::emit_silence (DCPTime most)
597 DCPTime t = min (most, TIME_HZ / 2);
598 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ));
599 silence->make_silent ();
600 Audio (silence, _audio_position);
602 _audio_position += t;
606 Player::film_changed (Film::Property p)
608 /* Here we should notice Film properties that affect our output, and
609 alert listeners that our output now would be different to how it was
610 last time we were run.
613 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
619 Player::update_subtitle ()
621 shared_ptr<Piece> piece = _in_subtitle.piece.lock ();
626 if (!_in_subtitle.subtitle->image) {
627 _out_subtitle.image.reset ();
631 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
634 dcpomatic::Rect<double> in_rect = _in_subtitle.subtitle->rect;
635 libdcp::Size scaled_size;
637 in_rect.y += sc->subtitle_offset ();
639 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
640 scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
641 scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
643 /* Then we need a corrective translation, consisting of two parts:
645 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
646 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
648 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
649 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
650 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
652 * Combining these two translations gives these expressions.
655 _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
656 _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
658 _out_subtitle.image = _in_subtitle.subtitle->image->scale (
660 Scaler::from_id ("bicubic"),
666 _out_subtitle.from = _in_subtitle.subtitle->dcp_time;
667 _out_subtitle.to = _in_subtitle.subtitle->dcp_time_to;
670 Time from = _in_subtitle.from;
671 Time to = _in_subtitle.to;
672 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (piece->content);
674 from = rint (from * vc->video_frame_rate() / _film->video_frame_rate());
675 to = rint (to * vc->video_frame_rate() / _film->video_frame_rate());
678 _out_subtitle.from = from * piece->content->position ();
679 _out_subtitle.to = to + piece->content->position ();
683 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
684 * @return false if this could not be done.
687 Player::repeat_last_video ()
689 if (!_last_incoming_video.video || !_have_valid_pieces) {
694 _last_incoming_video.weak_piece,
695 _last_incoming_video.video
702 Player::set_approximate_size ()
704 _approximate_size = true;
708 PlayerImage::PlayerImage (
709 shared_ptr<const Image> in,
711 libdcp::Size inter_size,
712 libdcp::Size out_size,
713 Scaler const * scaler
717 , _inter_size (inter_size)
718 , _out_size (out_size)
725 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
727 _subtitle_image = image;
728 _subtitle_position = pos;
732 PlayerImage::image (AVPixelFormat format, bool aligned)
734 shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
736 Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
738 if (_subtitle_image) {
739 out->alpha_blend (_subtitle_image, _subtitle_position);
746 PlayerStatistics::dump (shared_ptr<Log> log) const
748 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
749 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence));
752 PlayerStatistics const &
753 Player::statistics () const