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);
178 cout << "Video @ " << dv->dcp_time << " " << (double(dv->dcp_time) / TIME_HZ) << ".\n";
182 cout << "Image sub.\n";
184 cout << "Text sub.\n";
188 /* Will be set to false if we shouldn't consume the peeked DecodedThing */
193 if (_just_did_inaccurate_seek) {
195 /* Just emit; no subtlety */
196 emit_video (earliest_piece, dv);
197 step_video_position (dv);
199 } else if (dv->dcp_time > _video_position) {
203 list<shared_ptr<Piece> >::iterator i = _pieces.begin();
204 while (i != _pieces.end() && ((*i)->content->position() >= _video_position || _video_position >= (*i)->content->end())) {
208 if (i == _pieces.end() || !_last_incoming_video.video || !_have_valid_pieces) {
209 /* We're outside all video content */
211 _statistics.video.black++;
213 /* We're inside some video; repeat the frame */
214 _last_incoming_video.video->dcp_time = _video_position;
215 emit_video (_last_incoming_video.weak_piece, _last_incoming_video.video);
216 step_video_position (_last_incoming_video.video);
217 _statistics.video.repeat++;
222 } else if (dv->dcp_time == _video_position) {
224 emit_video (earliest_piece, dv);
225 step_video_position (dv);
226 _statistics.video.good++;
228 /* Too far behind: skip */
229 _statistics.video.skip++;
232 _just_did_inaccurate_seek = false;
234 } else if (da && _audio) {
236 if (da->dcp_time > _audio_position) {
238 emit_silence (da->dcp_time - _audio_position);
240 _statistics.audio.silence += (da->dcp_time - _audio_position);
241 } else if (da->dcp_time == _audio_position) {
243 emit_audio (earliest_piece, da);
244 _statistics.audio.good += da->data->frames();
246 /* Too far behind: skip */
247 _statistics.audio.skip += da->data->frames();
250 } else if (dis && _video) {
251 _image_subtitle.piece = earliest_piece;
252 _image_subtitle.subtitle = dis;
253 update_subtitle_from_image ();
254 } else if (dts && _video) {
255 _text_subtitle.piece = earliest_piece;
256 _text_subtitle.subtitle = dts;
257 update_subtitle_from_text ();
261 earliest_piece->decoder->consume ();
268 Player::emit_video (weak_ptr<Piece> weak_piece, shared_ptr<DecodedVideo> video)
270 /* Keep a note of what came in so that we can repeat it if required */
271 _last_incoming_video.weak_piece = weak_piece;
272 _last_incoming_video.video = video;
274 shared_ptr<Piece> piece = weak_piece.lock ();
279 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
282 FrameRateChange frc (content->video_frame_rate(), _film->video_frame_rate());
284 float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio();
285 dcp::Size image_size = fit_ratio_within (ratio, _video_container_size);
286 if (_approximate_size) {
287 image_size.width &= ~3;
288 image_size.height &= ~3;
291 shared_ptr<PlayerImage> pi (
296 _video_container_size,
302 _film->with_subtitles () &&
303 _out_subtitle.image &&
304 video->dcp_time >= _out_subtitle.from && video->dcp_time <= _out_subtitle.to
307 Position<int> const container_offset (
308 (_video_container_size.width - image_size.width) / 2,
309 (_video_container_size.height - image_size.height) / 2
312 pi->set_subtitle (_out_subtitle.image, _out_subtitle.position + container_offset);
316 #ifdef DCPOMATIC_DEBUG
317 _last_video = piece->content;
320 Video (pi, video->eyes, content->colour_conversion(), video->same, video->dcp_time);
322 _last_emit_was_black = false;
326 Player::step_video_position (shared_ptr<DecodedVideo> video)
328 /* This is a bit of a hack; don't update _video_position if EYES_RIGHT is on its way */
329 if (video->eyes != EYES_LEFT) {
330 /* This assumes that the video_frames_to_time conversion is exact
331 so that there are no accumulated errors caused by rounding.
333 _video_position += _film->video_frames_to_time (1);
338 Player::emit_audio (weak_ptr<Piece> weak_piece, shared_ptr<DecodedAudio> audio)
340 shared_ptr<Piece> piece = weak_piece.lock ();
345 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
349 if (content->audio_gain() != 0) {
350 shared_ptr<AudioBuffers> gain (new AudioBuffers (audio->data));
351 gain->apply_gain (content->audio_gain ());
356 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->data->frames()));
357 dcp_mapped->make_silent ();
358 AudioMapping map = content->audio_mapping ();
359 for (int i = 0; i < map.content_channels(); ++i) {
360 for (int j = 0; j < _film->audio_channels(); ++j) {
361 if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
362 dcp_mapped->accumulate_channel (
365 static_cast<dcp::Channel> (j),
366 map.get (i, static_cast<dcp::Channel> (j))
372 audio->data = dcp_mapped;
375 audio->dcp_time += content->audio_delay() * TIME_HZ / 1000;
376 if (audio->dcp_time < 0) {
377 int const frames = - audio->dcp_time * _film->audio_frame_rate() / TIME_HZ;
378 if (frames >= audio->data->frames ()) {
382 shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->data->channels(), audio->data->frames() - frames));
383 trimmed->copy_from (audio->data.get(), audio->data->frames() - frames, frames, 0);
385 audio->data = trimmed;
389 _audio_merger.push (audio->data, audio->dcp_time);
395 TimedAudioBuffers<DCPTime> tb = _audio_merger.flush ();
396 if (_audio && tb.audio) {
397 Audio (tb.audio, tb.time);
398 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
401 while (_video && _video_position < _audio_position) {
405 while (_audio && _audio_position < _video_position) {
406 emit_silence (_video_position - _audio_position);
410 /** Seek so that the next pass() will yield (approximately) the requested frame.
411 * Pass accurate = true to try harder to get close to the request.
412 * @return true on error
415 Player::seek (DCPTime t, bool accurate)
417 if (!_have_valid_pieces) {
421 if (_pieces.empty ()) {
425 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
426 /* s is the offset of t from the start position of this content */
427 DCPTime s = t - (*i)->content->position ();
428 s = max (static_cast<DCPTime> (0), s);
429 s = min ((*i)->content->length_after_trim(), s);
431 /* Convert this to the content time */
432 ContentTime ct = (s + (*i)->content->trim_start()) * (*i)->frc.speed_up;
434 /* And seek the decoder */
435 (*i)->decoder->seek (ct, accurate);
438 _video_position = time_round_up (t, TIME_HZ / _film->video_frame_rate());
439 _audio_position = time_round_up (t, TIME_HZ / _film->audio_frame_rate());
441 _audio_merger.clear (_audio_position);
444 /* We just did an inaccurate seek, so it's likely that the next thing seen
445 out of pass() will be a fair distance from _{video,audio}_position. Setting
446 this flag stops pass() from trying to fix that: we assume that if it
447 was an inaccurate seek then the caller does not care too much about
448 inserting black/silence to keep the time tidy.
450 _just_did_inaccurate_seek = true;
455 Player::setup_pieces ()
457 list<shared_ptr<Piece> > old_pieces = _pieces;
460 ContentList content = _playlist->content ();
462 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
464 if (!(*i)->paths_valid ()) {
468 shared_ptr<Decoder> decoder;
469 optional<FrameRateChange> frc;
471 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
472 DCPTime best_overlap_t = 0;
473 shared_ptr<VideoContent> best_overlap;
474 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
475 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
480 DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
481 if (overlap > best_overlap_t) {
483 best_overlap_t = overlap;
487 optional<FrameRateChange> best_overlap_frc;
489 best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
491 /* No video overlap; e.g. if the DCP is just audio */
492 best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
496 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
498 decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
499 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
503 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
505 /* See if we can re-use an old ImageDecoder */
506 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
507 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
508 if (imd && imd->content() == ic) {
514 decoder.reset (new ImageDecoder (_film, ic));
517 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
521 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
523 decoder.reset (new SndfileDecoder (_film, sc));
524 frc = best_overlap_frc;
528 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
530 decoder.reset (new SubRipDecoder (_film, rc));
531 frc = best_overlap_frc;
534 ContentTime st = (*i)->trim_start() * frc->speed_up;
535 decoder->seek (st, true);
537 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
540 _have_valid_pieces = true;
542 /* The Piece for the _last_incoming_video will no longer be valid */
543 _last_incoming_video.video.reset ();
545 _video_position = _audio_position = 0;
549 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
551 shared_ptr<Content> c = w.lock ();
557 property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
558 property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
559 property == VideoContentProperty::VIDEO_FRAME_TYPE
562 _have_valid_pieces = false;
566 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
567 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
568 property == SubtitleContentProperty::SUBTITLE_SCALE
571 update_subtitle_from_image ();
572 update_subtitle_from_text ();
576 property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO ||
577 property == VideoContentProperty::VIDEO_FRAME_RATE
582 } else if (property == ContentProperty::PATH) {
584 _have_valid_pieces = false;
590 Player::playlist_changed ()
592 _have_valid_pieces = false;
597 Player::set_video_container_size (dcp::Size s)
599 _video_container_size = s;
601 shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
608 _video_container_size,
609 _video_container_size,
610 Scaler::from_id ("bicubic")
616 Player::emit_black ()
618 #ifdef DCPOMATIC_DEBUG
619 _last_video.reset ();
622 Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
623 _video_position += _film->video_frames_to_time (1);
624 _last_emit_was_black = true;
628 Player::emit_silence (DCPTime most)
634 DCPTime t = min (most, TIME_HZ / 2);
635 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ));
636 silence->make_silent ();
637 Audio (silence, _audio_position);
639 _audio_position += t;
643 Player::film_changed (Film::Property p)
645 /* Here we should notice Film properties that affect our output, and
646 alert listeners that our output now would be different to how it was
647 last time we were run.
650 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
656 Player::update_subtitle_from_image ()
658 shared_ptr<Piece> piece = _image_subtitle.piece.lock ();
663 if (!_image_subtitle.subtitle->image) {
664 _out_subtitle.image.reset ();
668 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
671 dcpomatic::Rect<double> in_rect = _image_subtitle.subtitle->rect;
672 dcp::Size scaled_size;
674 in_rect.x += sc->subtitle_x_offset ();
675 in_rect.y += sc->subtitle_y_offset ();
677 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
678 scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
679 scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
681 /* Then we need a corrective translation, consisting of two parts:
683 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
684 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
686 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
687 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
688 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
690 * Combining these two translations gives these expressions.
693 _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
694 _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
696 _out_subtitle.image = _image_subtitle.subtitle->image->scale (
698 Scaler::from_id ("bicubic"),
699 _image_subtitle.subtitle->image->pixel_format (),
703 _out_subtitle.from = _image_subtitle.subtitle->dcp_time + piece->content->position ();
704 _out_subtitle.to = _image_subtitle.subtitle->dcp_time_to + piece->content->position ();
707 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
708 * @return false if this could not be done.
711 Player::repeat_last_video ()
713 if (!_last_incoming_video.video || !_have_valid_pieces) {
718 _last_incoming_video.weak_piece,
719 _last_incoming_video.video
726 Player::update_subtitle_from_text ()
728 if (_text_subtitle.subtitle->subs.empty ()) {
729 _out_subtitle.image.reset ();
733 render_subtitles (_text_subtitle.subtitle->subs, _video_container_size, _out_subtitle.image, _out_subtitle.position);
737 Player::set_approximate_size ()
739 _approximate_size = true;
742 PlayerImage::PlayerImage (
743 shared_ptr<const Image> in,
745 dcp::Size inter_size,
747 Scaler const * scaler
751 , _inter_size (inter_size)
752 , _out_size (out_size)
759 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
761 _subtitle_image = image;
762 _subtitle_position = pos;
766 PlayerImage::image (AVPixelFormat format, bool aligned)
768 shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
770 Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
772 if (_subtitle_image) {
773 out->alpha_blend (_subtitle_image, _subtitle_position);
780 PlayerStatistics::dump (shared_ptr<Log> log) const
782 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
783 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence));
786 PlayerStatistics const &
787 Player::statistics () const