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 */
126 dec->set_dcp_times (_film->video_frame_rate(), _film->audio_frame_rate(), (*i)->frc, offset);
127 DCPTime const t = dec->dcp_time - offset;
128 cout << "Peeked " << (*i)->content->paths()[0] << " for " << t << " cf " << ((*i)->content->full_length() - (*i)->content->trim_end ()) << "\n";
129 if (t >= ((*i)->content->full_length() - (*i)->content->trim_end ())) {
130 /* In the end-trimmed part; decoder has nothing else to give us */
133 } else if (t >= (*i)->content->trim_start ()) {
134 /* Within the un-trimmed part; everything's ok */
137 /* Within the start-trimmed part; get something else */
138 (*i)->decoder->consume ();
146 if (dec->dcp_time < earliest_time) {
148 earliest_decoded = dec;
149 earliest_time = dec->dcp_time;
152 if (dynamic_pointer_cast<DecodedAudio> (dec) && dec->dcp_time < earliest_audio) {
153 earliest_audio = dec->dcp_time;
157 if (!earliest_piece) {
162 if (earliest_audio != TIME_MAX) {
163 TimedAudioBuffers<DCPTime> tb = _audio_merger.pull (max (int64_t (0), earliest_audio));
164 Audio (tb.audio, tb.time);
165 /* This assumes that the audio_frames_to_time conversion is exact
166 so that there are no accumulated errors caused by rounding.
168 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
171 /* Emit the earliest thing */
173 shared_ptr<DecodedVideo> dv = dynamic_pointer_cast<DecodedVideo> (earliest_decoded);
174 shared_ptr<DecodedAudio> da = dynamic_pointer_cast<DecodedAudio> (earliest_decoded);
175 shared_ptr<DecodedImageSubtitle> dis = dynamic_pointer_cast<DecodedImageSubtitle> (earliest_decoded);
176 shared_ptr<DecodedTextSubtitle> dts = dynamic_pointer_cast<DecodedTextSubtitle> (earliest_decoded);
179 cout << "Video @ " << dv->dcp_time << " " << (double(dv->dcp_time) / TIME_HZ) << ".\n";
183 cout << "Image sub.\n";
185 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 cout << "seek " << (*i)->content->paths()[0] << " to " << ct << "\n";
436 (*i)->decoder->seek (ct, accurate);
439 _video_position = time_round_up (t, TIME_HZ / _film->video_frame_rate());
440 _audio_position = time_round_up (t, TIME_HZ / _film->audio_frame_rate());
442 _audio_merger.clear (_audio_position);
445 /* We just did an inaccurate seek, so it's likely that the next thing seen
446 out of pass() will be a fair distance from _{video,audio}_position. Setting
447 this flag stops pass() from trying to fix that: we assume that if it
448 was an inaccurate seek then the caller does not care too much about
449 inserting black/silence to keep the time tidy.
451 _just_did_inaccurate_seek = true;
456 Player::setup_pieces ()
458 list<shared_ptr<Piece> > old_pieces = _pieces;
461 ContentList content = _playlist->content ();
463 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
465 if (!(*i)->paths_valid ()) {
469 shared_ptr<Decoder> decoder;
470 optional<FrameRateChange> frc;
472 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
473 DCPTime best_overlap_t = 0;
474 shared_ptr<VideoContent> best_overlap;
475 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
476 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
481 DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
482 if (overlap > best_overlap_t) {
484 best_overlap_t = overlap;
488 optional<FrameRateChange> best_overlap_frc;
490 best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
492 /* No video overlap; e.g. if the DCP is just audio */
493 best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
497 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
499 decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
500 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
504 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
506 /* See if we can re-use an old ImageDecoder */
507 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
508 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
509 if (imd && imd->content() == ic) {
515 decoder.reset (new ImageDecoder (_film, ic));
518 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
522 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
524 decoder.reset (new SndfileDecoder (_film, sc));
525 frc = best_overlap_frc;
529 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
531 decoder.reset (new SubRipDecoder (_film, rc));
532 frc = best_overlap_frc;
535 ContentTime st = (*i)->trim_start() * frc->speed_up;
536 decoder->seek (st, true);
538 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
541 _have_valid_pieces = true;
543 /* The Piece for the _last_incoming_video will no longer be valid */
544 _last_incoming_video.video.reset ();
546 _video_position = _audio_position = 0;
550 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
552 shared_ptr<Content> c = w.lock ();
558 property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
559 property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
560 property == VideoContentProperty::VIDEO_FRAME_TYPE
563 _have_valid_pieces = false;
567 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
568 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
569 property == SubtitleContentProperty::SUBTITLE_SCALE
572 update_subtitle_from_image ();
573 update_subtitle_from_text ();
577 property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO ||
578 property == VideoContentProperty::VIDEO_FRAME_RATE
583 } else if (property == ContentProperty::PATH) {
585 _have_valid_pieces = false;
591 Player::playlist_changed ()
593 _have_valid_pieces = false;
598 Player::set_video_container_size (dcp::Size s)
600 _video_container_size = s;
602 shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
609 _video_container_size,
610 _video_container_size,
611 Scaler::from_id ("bicubic")
617 Player::emit_black ()
619 #ifdef DCPOMATIC_DEBUG
620 _last_video.reset ();
623 Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
624 _video_position += _film->video_frames_to_time (1);
625 _last_emit_was_black = true;
629 Player::emit_silence (DCPTime most)
635 DCPTime t = min (most, TIME_HZ / 2);
636 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ));
637 silence->make_silent ();
638 Audio (silence, _audio_position);
640 _audio_position += t;
644 Player::film_changed (Film::Property p)
646 /* Here we should notice Film properties that affect our output, and
647 alert listeners that our output now would be different to how it was
648 last time we were run.
651 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
657 Player::update_subtitle_from_image ()
659 shared_ptr<Piece> piece = _image_subtitle.piece.lock ();
664 if (!_image_subtitle.subtitle->image) {
665 _out_subtitle.image.reset ();
669 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
672 dcpomatic::Rect<double> in_rect = _image_subtitle.subtitle->rect;
673 dcp::Size scaled_size;
675 in_rect.x += sc->subtitle_x_offset ();
676 in_rect.y += sc->subtitle_y_offset ();
678 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
679 scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
680 scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
682 /* Then we need a corrective translation, consisting of two parts:
684 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
685 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
687 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
688 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
689 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
691 * Combining these two translations gives these expressions.
694 _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
695 _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
697 _out_subtitle.image = _image_subtitle.subtitle->image->scale (
699 Scaler::from_id ("bicubic"),
700 _image_subtitle.subtitle->image->pixel_format (),
704 _out_subtitle.from = _image_subtitle.subtitle->dcp_time + piece->content->position ();
705 _out_subtitle.to = _image_subtitle.subtitle->dcp_time_to + piece->content->position ();
708 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
709 * @return false if this could not be done.
712 Player::repeat_last_video ()
714 if (!_last_incoming_video.video || !_have_valid_pieces) {
719 _last_incoming_video.weak_piece,
720 _last_incoming_video.video
727 Player::update_subtitle_from_text ()
729 if (_text_subtitle.subtitle->subs.empty ()) {
730 _out_subtitle.image.reset ();
734 render_subtitles (_text_subtitle.subtitle->subs, _video_container_size, _out_subtitle.image, _out_subtitle.position);
738 Player::set_approximate_size ()
740 _approximate_size = true;
743 PlayerImage::PlayerImage (
744 shared_ptr<const Image> in,
746 dcp::Size inter_size,
748 Scaler const * scaler
752 , _inter_size (inter_size)
753 , _out_size (out_size)
760 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
762 _subtitle_image = image;
763 _subtitle_position = pos;
767 PlayerImage::image (AVPixelFormat format, bool aligned)
769 shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
771 Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
773 if (_subtitle_image) {
774 out->alpha_blend (_subtitle_image, _subtitle_position);
781 PlayerStatistics::dump (shared_ptr<Log> log) const
783 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
784 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence));
787 PlayerStatistics const &
788 Player::statistics () const