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;
550 } else if (property == SubtitleContentProperty::SUBTITLE_OFFSET || property == SubtitleContentProperty::SUBTITLE_SCALE) {
552 update_subtitle_from_image ();
553 update_subtitle_from_text ();
557 property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO ||
558 property == VideoContentProperty::VIDEO_FRAME_RATE
563 } else if (property == ContentProperty::PATH) {
570 Player::playlist_changed ()
572 _have_valid_pieces = false;
577 Player::set_video_container_size (libdcp::Size s)
579 _video_container_size = s;
581 shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
588 _video_container_size,
589 _video_container_size,
590 Scaler::from_id ("bicubic")
596 Player::emit_black ()
598 #ifdef DCPOMATIC_DEBUG
599 _last_video.reset ();
602 Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
603 _video_position += _film->video_frames_to_time (1);
604 _last_emit_was_black = true;
608 Player::emit_silence (DCPTime most)
614 DCPTime t = min (most, TIME_HZ / 2);
615 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ));
616 silence->make_silent ();
617 Audio (silence, _audio_position);
619 _audio_position += t;
623 Player::film_changed (Film::Property p)
625 /* Here we should notice Film properties that affect our output, and
626 alert listeners that our output now would be different to how it was
627 last time we were run.
630 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
636 Player::update_subtitle_from_image ()
638 shared_ptr<Piece> piece = _image_subtitle.piece.lock ();
643 if (!_image_subtitle.subtitle->image) {
644 _out_subtitle.image.reset ();
648 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
651 dcpomatic::Rect<double> in_rect = _image_subtitle.subtitle->rect;
652 libdcp::Size scaled_size;
654 in_rect.y += sc->subtitle_offset ();
656 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
657 scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
658 scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
660 /* Then we need a corrective translation, consisting of two parts:
662 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
663 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
665 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
666 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
667 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
669 * Combining these two translations gives these expressions.
672 _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
673 _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
675 _out_subtitle.image = _image_subtitle.subtitle->image->scale (
677 Scaler::from_id ("bicubic"),
678 _image_subtitle.subtitle->image->pixel_format (),
682 _out_subtitle.from = _image_subtitle.subtitle->dcp_time;
683 _out_subtitle.to = _image_subtitle.subtitle->dcp_time_to;
686 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
687 * @return false if this could not be done.
690 Player::repeat_last_video ()
692 if (!_last_incoming_video.video || !_have_valid_pieces) {
697 _last_incoming_video.weak_piece,
698 _last_incoming_video.video
705 Player::update_subtitle_from_text ()
707 if (_text_subtitle.subtitle->subs.empty ()) {
708 _out_subtitle.image.reset ();
712 render_subtitles (_text_subtitle.subtitle->subs, _video_container_size, _out_subtitle.image, _out_subtitle.position);
716 Player::set_approximate_size ()
718 _approximate_size = true;
721 PlayerImage::PlayerImage (
722 shared_ptr<const Image> in,
724 libdcp::Size inter_size,
725 libdcp::Size out_size,
726 Scaler const * scaler
730 , _inter_size (inter_size)
731 , _out_size (out_size)
738 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
740 _subtitle_image = image;
741 _subtitle_position = pos;
745 PlayerImage::image (AVPixelFormat format, bool aligned)
747 shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
749 Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
751 if (_subtitle_image) {
752 out->alpha_blend (_subtitle_image, _subtitle_position);
759 PlayerStatistics::dump (shared_ptr<Log> log) const
761 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
762 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence));
765 PlayerStatistics const &
766 Player::statistics () const