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"
31 #include "subrip_decoder.h"
32 #include "subrip_content.h"
39 #include "render_subtitles.h"
48 using boost::shared_ptr;
49 using boost::weak_ptr;
50 using boost::dynamic_pointer_cast;
51 using boost::optional;
56 Piece (shared_ptr<Content> c, shared_ptr<Decoder> d, FrameRateChange f)
62 shared_ptr<Content> content;
63 shared_ptr<Decoder> decoder;
67 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
72 , _have_valid_pieces (false)
75 , _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1))
76 , _last_emit_was_black (false)
77 , _just_did_inaccurate_seek (false)
78 , _approximate_size (false)
80 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
81 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
82 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
83 set_video_container_size (fit_ratio_within (_film->container()->ratio (), _film->full_frame ()));
87 Player::disable_video ()
93 Player::disable_audio ()
101 if (!_have_valid_pieces) {
105 /* Interrogate all our pieces to find the one with the earliest decoded data */
107 shared_ptr<Piece> earliest_piece;
108 shared_ptr<Decoded> earliest_decoded;
109 DCPTime earliest_time = TIME_MAX;
110 DCPTime earliest_audio = TIME_MAX;
112 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
114 DCPTime const offset = (*i)->content->position() - (*i)->content->trim_start();
117 shared_ptr<Decoded> dec;
119 dec = (*i)->decoder->peek ();
121 /* Decoder has nothing else to give us */
125 dec->set_dcp_times (_film->video_frame_rate(), _film->audio_frame_rate(), (*i)->frc, offset);
126 DCPTime const t = dec->dcp_time - offset;
127 if (t >= ((*i)->content->full_length() - (*i)->content->trim_end ())) {
128 /* In the end-trimmed part; decoder has nothing else to give us */
131 } else if (t >= (*i)->content->trim_start ()) {
132 /* Within the un-trimmed part; everything's ok */
135 /* Within the start-trimmed part; get something else */
136 (*i)->decoder->consume ();
144 if (dec->dcp_time < earliest_time) {
146 earliest_decoded = dec;
147 earliest_time = dec->dcp_time;
150 if (dynamic_pointer_cast<DecodedAudio> (dec) && dec->dcp_time < earliest_audio) {
151 earliest_audio = dec->dcp_time;
155 if (!earliest_piece) {
160 if (earliest_audio != TIME_MAX) {
161 TimedAudioBuffers<DCPTime> tb = _audio_merger.pull (max (int64_t (0), earliest_audio));
162 Audio (tb.audio, tb.time);
163 /* This assumes that the audio_frames_to_time conversion is exact
164 so that there are no accumulated errors caused by rounding.
166 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
169 /* Emit the earliest thing */
171 shared_ptr<DecodedVideo> dv = dynamic_pointer_cast<DecodedVideo> (earliest_decoded);
172 shared_ptr<DecodedAudio> da = dynamic_pointer_cast<DecodedAudio> (earliest_decoded);
173 shared_ptr<DecodedImageSubtitle> dis = dynamic_pointer_cast<DecodedImageSubtitle> (earliest_decoded);
174 shared_ptr<DecodedTextSubtitle> dts = dynamic_pointer_cast<DecodedTextSubtitle> (earliest_decoded);
176 /* Will be set to false if we shouldn't consume the peeked DecodedThing */
181 if (_just_did_inaccurate_seek) {
183 /* Just emit; no subtlety */
184 emit_video (earliest_piece, dv);
185 step_video_position (dv);
187 } else if (dv->dcp_time > _video_position) {
191 list<shared_ptr<Piece> >::iterator i = _pieces.begin();
192 while (i != _pieces.end() && ((*i)->content->position() >= _video_position || _video_position >= (*i)->content->end())) {
196 if (i == _pieces.end() || !_last_incoming_video.video || !_have_valid_pieces) {
197 /* We're outside all video content */
199 _statistics.video.black++;
201 /* We're inside some video; repeat the frame */
202 _last_incoming_video.video->dcp_time = _video_position;
203 emit_video (_last_incoming_video.weak_piece, _last_incoming_video.video);
204 step_video_position (_last_incoming_video.video);
205 _statistics.video.repeat++;
210 } else if (dv->dcp_time == _video_position) {
212 emit_video (earliest_piece, dv);
213 step_video_position (dv);
214 _statistics.video.good++;
216 /* Too far behind: skip */
217 _statistics.video.skip++;
220 _just_did_inaccurate_seek = false;
222 } else if (da && _audio) {
224 if (da->dcp_time > _audio_position) {
226 emit_silence (da->dcp_time - _audio_position);
228 _statistics.audio.silence += (da->dcp_time - _audio_position);
229 } else if (da->dcp_time == _audio_position) {
231 emit_audio (earliest_piece, da);
232 _statistics.audio.good += da->data->frames();
234 /* Too far behind: skip */
235 _statistics.audio.skip += da->data->frames();
238 } else if (dis && _video) {
239 _image_subtitle.piece = earliest_piece;
240 _image_subtitle.subtitle = dis;
241 update_subtitle_from_image ();
242 } else if (dts && _video) {
243 _text_subtitle.piece = earliest_piece;
244 _text_subtitle.subtitle = dts;
245 update_subtitle_from_text ();
249 earliest_piece->decoder->consume ();
256 Player::emit_video (weak_ptr<Piece> weak_piece, shared_ptr<DecodedVideo> video)
258 /* Keep a note of what came in so that we can repeat it if required */
259 _last_incoming_video.weak_piece = weak_piece;
260 _last_incoming_video.video = video;
262 shared_ptr<Piece> piece = weak_piece.lock ();
267 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
270 FrameRateChange frc (content->video_frame_rate(), _film->video_frame_rate());
272 float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio();
273 libdcp::Size image_size = fit_ratio_within (ratio, _video_container_size);
274 if (_approximate_size) {
275 image_size.width &= ~3;
276 image_size.height &= ~3;
279 shared_ptr<PlayerImage> pi (
284 _video_container_size,
290 _film->with_subtitles () &&
291 _out_subtitle.image &&
292 video->dcp_time >= _out_subtitle.from && video->dcp_time <= _out_subtitle.to
295 Position<int> const container_offset (
296 (_video_container_size.width - image_size.width) / 2,
297 (_video_container_size.height - image_size.height) / 2
300 pi->set_subtitle (_out_subtitle.image, _out_subtitle.position + container_offset);
304 #ifdef DCPOMATIC_DEBUG
305 _last_video = piece->content;
308 Video (pi, video->eyes, content->colour_conversion(), video->same, video->dcp_time);
310 _last_emit_was_black = false;
314 Player::step_video_position (shared_ptr<DecodedVideo> video)
316 /* This is a bit of a hack; don't update _video_position if EYES_RIGHT is on its way */
317 if (video->eyes != EYES_LEFT) {
318 /* This assumes that the video_frames_to_time conversion is exact
319 so that there are no accumulated errors caused by rounding.
321 _video_position += _film->video_frames_to_time (1);
326 Player::emit_audio (weak_ptr<Piece> weak_piece, shared_ptr<DecodedAudio> audio)
328 shared_ptr<Piece> piece = weak_piece.lock ();
333 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
337 if (content->audio_gain() != 0) {
338 shared_ptr<AudioBuffers> gain (new AudioBuffers (audio->data));
339 gain->apply_gain (content->audio_gain ());
344 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->data->frames()));
345 dcp_mapped->make_silent ();
346 AudioMapping map = content->audio_mapping ();
347 for (int i = 0; i < map.content_channels(); ++i) {
348 for (int j = 0; j < _film->audio_channels(); ++j) {
349 if (map.get (i, static_cast<libdcp::Channel> (j)) > 0) {
350 dcp_mapped->accumulate_channel (
353 static_cast<libdcp::Channel> (j),
354 map.get (i, static_cast<libdcp::Channel> (j))
360 audio->data = dcp_mapped;
363 audio->dcp_time += content->audio_delay() * TIME_HZ / 1000;
364 if (audio->dcp_time < 0) {
365 int const frames = - audio->dcp_time * _film->audio_frame_rate() / TIME_HZ;
366 if (frames >= audio->data->frames ()) {
370 shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->data->channels(), audio->data->frames() - frames));
371 trimmed->copy_from (audio->data.get(), audio->data->frames() - frames, frames, 0);
373 audio->data = trimmed;
377 _audio_merger.push (audio->data, audio->dcp_time);
383 TimedAudioBuffers<DCPTime> tb = _audio_merger.flush ();
384 if (_audio && tb.audio) {
385 Audio (tb.audio, tb.time);
386 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
389 while (_video && _video_position < _audio_position) {
393 while (_audio && _audio_position < _video_position) {
394 emit_silence (_video_position - _audio_position);
399 /** Seek so that the next pass() will yield (approximately) the requested frame.
400 * Pass accurate = true to try harder to get close to the request.
401 * @return true on error
404 Player::seek (DCPTime t, bool accurate)
406 if (!_have_valid_pieces) {
410 if (_pieces.empty ()) {
414 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
415 /* s is the offset of t from the start position of this content */
416 DCPTime s = t - (*i)->content->position ();
417 s = max (static_cast<DCPTime> (0), s);
418 s = min ((*i)->content->length_after_trim(), s);
420 /* Convert this to the content time */
421 ContentTime ct = (s + (*i)->content->trim_start()) * (*i)->frc.speed_up;
423 /* And seek the decoder */
424 (*i)->decoder->seek (ct, accurate);
427 _video_position = time_round_up (t, TIME_HZ / _film->video_frame_rate());
428 _audio_position = time_round_up (t, TIME_HZ / _film->audio_frame_rate());
430 _audio_merger.clear (_audio_position);
433 /* We just did an inaccurate seek, so it's likely that the next thing seen
434 out of pass() will be a fair distance from _{video,audio}_position. Setting
435 this flag stops pass() from trying to fix that: we assume that if it
436 was an inaccurate seek then the caller does not care too much about
437 inserting black/silence to keep the time tidy.
439 _just_did_inaccurate_seek = true;
444 Player::setup_pieces ()
446 list<shared_ptr<Piece> > old_pieces = _pieces;
449 ContentList content = _playlist->content ();
451 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
453 shared_ptr<Decoder> decoder;
454 optional<FrameRateChange> frc;
456 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
457 DCPTime best_overlap_t = 0;
458 shared_ptr<VideoContent> best_overlap;
459 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
460 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
465 DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
466 if (overlap > best_overlap_t) {
468 best_overlap_t = overlap;
472 optional<FrameRateChange> best_overlap_frc;
474 best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
476 /* No video overlap; e.g. if the DCP is just audio */
477 best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
481 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
483 decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
484 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
488 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
490 /* See if we can re-use an old ImageDecoder */
491 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
492 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
493 if (imd && imd->content() == ic) {
499 decoder.reset (new ImageDecoder (_film, ic));
502 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
506 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
508 decoder.reset (new SndfileDecoder (_film, sc));
509 frc = best_overlap_frc;
513 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
515 decoder.reset (new SubRipDecoder (_film, rc));
516 frc = best_overlap_frc;
519 ContentTime st = (*i)->trim_start() * frc->speed_up;
520 decoder->seek (st, true);
522 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
525 _have_valid_pieces = true;
527 /* The Piece for the _last_incoming_video will no longer be valid */
528 _last_incoming_video.video.reset ();
530 _video_position = _audio_position = 0;
534 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
536 shared_ptr<Content> c = w.lock ();
542 property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
543 property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
544 property == VideoContentProperty::VIDEO_FRAME_TYPE
547 _have_valid_pieces = false;
551 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
552 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
553 property == SubtitleContentProperty::SUBTITLE_SCALE
556 update_subtitle_from_image ();
557 update_subtitle_from_text ();
561 property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO ||
562 property == VideoContentProperty::VIDEO_FRAME_RATE
567 } else if (property == ContentProperty::PATH) {
574 Player::playlist_changed ()
576 _have_valid_pieces = false;
581 Player::set_video_container_size (libdcp::Size s)
583 _video_container_size = s;
585 shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
592 _video_container_size,
593 _video_container_size,
594 Scaler::from_id ("bicubic")
600 Player::emit_black ()
602 #ifdef DCPOMATIC_DEBUG
603 _last_video.reset ();
606 Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
607 _video_position += _film->video_frames_to_time (1);
608 _last_emit_was_black = true;
612 Player::emit_silence (DCPTime most)
618 DCPTime t = min (most, TIME_HZ / 2);
619 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ));
620 silence->make_silent ();
621 Audio (silence, _audio_position);
623 _audio_position += t;
627 Player::film_changed (Film::Property p)
629 /* Here we should notice Film properties that affect our output, and
630 alert listeners that our output now would be different to how it was
631 last time we were run.
634 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
640 Player::update_subtitle_from_image ()
642 shared_ptr<Piece> piece = _image_subtitle.piece.lock ();
647 if (!_image_subtitle.subtitle->image) {
648 _out_subtitle.image.reset ();
652 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
655 dcpomatic::Rect<double> in_rect = _image_subtitle.subtitle->rect;
656 libdcp::Size scaled_size;
658 in_rect.x += sc->subtitle_x_offset ();
659 in_rect.y += sc->subtitle_y_offset ();
661 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
662 scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
663 scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
665 /* Then we need a corrective translation, consisting of two parts:
667 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
668 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
670 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
671 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
672 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
674 * Combining these two translations gives these expressions.
677 _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
678 _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
680 _out_subtitle.image = _image_subtitle.subtitle->image->scale (
682 Scaler::from_id ("bicubic"),
683 _image_subtitle.subtitle->image->pixel_format (),
687 _out_subtitle.from = _image_subtitle.subtitle->dcp_time + piece->content->position ();
688 _out_subtitle.to = _image_subtitle.subtitle->dcp_time_to + piece->content->position ();
691 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
692 * @return false if this could not be done.
695 Player::repeat_last_video ()
697 if (!_last_incoming_video.video || !_have_valid_pieces) {
702 _last_incoming_video.weak_piece,
703 _last_incoming_video.video
710 Player::update_subtitle_from_text ()
712 if (_text_subtitle.subtitle->subs.empty ()) {
713 _out_subtitle.image.reset ();
717 render_subtitles (_text_subtitle.subtitle->subs, _video_container_size, _out_subtitle.image, _out_subtitle.position);
721 Player::set_approximate_size ()
723 _approximate_size = true;
726 PlayerImage::PlayerImage (
727 shared_ptr<const Image> in,
729 libdcp::Size inter_size,
730 libdcp::Size out_size,
731 Scaler const * scaler
735 , _inter_size (inter_size)
736 , _out_size (out_size)
743 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
745 _subtitle_image = image;
746 _subtitle_position = pos;
750 PlayerImage::image (AVPixelFormat format, bool aligned)
752 shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
754 Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
756 if (_subtitle_image) {
757 out->alpha_blend (_subtitle_image, _subtitle_position);
764 PlayerStatistics::dump (shared_ptr<Log> log) const
766 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
767 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence));
770 PlayerStatistics const &
771 Player::statistics () const