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);
295 #ifdef DCPOMATIC_DEBUG
296 _last_video = piece->content;
299 Video (pi, video->eyes, content->colour_conversion(), video->same, video->dcp_time);
301 _last_emit_was_black = false;
305 Player::step_video_position (shared_ptr<DecodedVideo> video)
307 /* This is a bit of a hack; don't update _video_position if EYES_RIGHT is on its way */
308 if (video->eyes != EYES_LEFT) {
309 /* This assumes that the video_frames_to_time conversion is exact
310 so that there are no accumulated errors caused by rounding.
312 _video_position += _film->video_frames_to_time (1);
317 Player::emit_audio (weak_ptr<Piece> weak_piece, shared_ptr<DecodedAudio> audio)
319 shared_ptr<Piece> piece = weak_piece.lock ();
324 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
328 if (content->audio_gain() != 0) {
329 shared_ptr<AudioBuffers> gain (new AudioBuffers (audio->data));
330 gain->apply_gain (content->audio_gain ());
335 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->data->frames()));
336 dcp_mapped->make_silent ();
337 list<pair<int, libdcp::Channel> > map = content->audio_mapping().content_to_dcp ();
338 for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) {
339 if (i->first < audio->data->channels() && i->second < dcp_mapped->channels()) {
340 dcp_mapped->accumulate_channel (audio->data.get(), i->first, i->second);
344 audio->data = dcp_mapped;
347 audio->dcp_time += content->audio_delay() * TIME_HZ / 1000;
348 if (audio->dcp_time < 0) {
349 int const frames = - audio->dcp_time * _film->audio_frame_rate() / TIME_HZ;
350 if (frames >= audio->data->frames ()) {
354 shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->data->channels(), audio->data->frames() - frames));
355 trimmed->copy_from (audio->data.get(), audio->data->frames() - frames, frames, 0);
357 audio->data = trimmed;
361 _audio_merger.push (audio->data, audio->dcp_time);
367 TimedAudioBuffers<DCPTime> tb = _audio_merger.flush ();
368 if (_audio && tb.audio) {
369 Audio (tb.audio, tb.time);
370 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
373 while (_video && _video_position < _audio_position) {
377 while (_audio && _audio_position < _video_position) {
378 emit_silence (_video_position - _audio_position);
383 /** Seek so that the next pass() will yield (approximately) the requested frame.
384 * Pass accurate = true to try harder to get close to the request.
385 * @return true on error
388 Player::seek (DCPTime t, bool accurate)
390 if (!_have_valid_pieces) {
394 if (_pieces.empty ()) {
398 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
399 /* s is the offset of t from the start position of this content */
400 DCPTime s = t - (*i)->content->position ();
401 s = max (static_cast<DCPTime> (0), s);
402 s = min ((*i)->content->length_after_trim(), s);
404 /* Convert this to the content time */
405 ContentTime ct = (s + (*i)->content->trim_start()) * (*i)->frc.speed_up;
407 /* And seek the decoder */
408 (*i)->decoder->seek (ct, accurate);
411 _video_position = time_round_up (t, TIME_HZ / _film->video_frame_rate());
412 _audio_position = time_round_up (t, TIME_HZ / _film->audio_frame_rate());
414 _audio_merger.clear (_audio_position);
417 /* We just did an inaccurate seek, so it's likely that the next thing seen
418 out of pass() will be a fair distance from _{video,audio}_position. Setting
419 this flag stops pass() from trying to fix that: we assume that if it
420 was an inaccurate seek then the caller does not care too much about
421 inserting black/silence to keep the time tidy.
423 _just_did_inaccurate_seek = true;
428 Player::setup_pieces ()
430 list<shared_ptr<Piece> > old_pieces = _pieces;
433 ContentList content = _playlist->content ();
435 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
437 shared_ptr<Decoder> decoder;
438 optional<FrameRateChange> frc;
440 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
442 decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
443 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
446 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
448 /* See if we can re-use an old ImageDecoder */
449 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
450 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
451 if (imd && imd->content() == ic) {
457 decoder.reset (new ImageDecoder (_film, ic));
460 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
463 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
465 decoder.reset (new SndfileDecoder (_film, sc));
467 /* Working out the frc for this content is a bit tricky: what if it overlaps
468 two pieces of video content with different frame rates? For now, use
469 the one with the best overlap.
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(), sc->position()) - min (vc->end(), sc->end());
481 if (overlap > best_overlap_t) {
483 best_overlap_t = overlap;
488 frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
490 /* No video overlap; e.g. if the DCP is just audio */
491 frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
495 ContentTime st = (*i)->trim_start() * frc->speed_up;
496 decoder->seek (st, true);
498 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
501 _have_valid_pieces = true;
503 /* The Piece for the _last_incoming_video will no longer be valid */
504 _last_incoming_video.video.reset ();
506 _video_position = _audio_position = 0;
510 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
512 shared_ptr<Content> c = w.lock ();
518 property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
519 property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
520 property == VideoContentProperty::VIDEO_FRAME_TYPE
523 _have_valid_pieces = false;
526 } else if (property == SubtitleContentProperty::SUBTITLE_OFFSET || property == SubtitleContentProperty::SUBTITLE_SCALE) {
531 } else if (property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO) {
535 } else if (property == ContentProperty::PATH) {
542 Player::playlist_changed ()
544 _have_valid_pieces = false;
549 Player::set_video_container_size (libdcp::Size s)
551 _video_container_size = s;
553 shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
560 _video_container_size,
561 _video_container_size,
562 Scaler::from_id ("bicubic")
568 Player::emit_black ()
570 #ifdef DCPOMATIC_DEBUG
571 _last_video.reset ();
574 Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
575 _video_position += _film->video_frames_to_time (1);
576 _last_emit_was_black = true;
580 Player::emit_silence (DCPTime most)
586 DCPTime t = min (most, TIME_HZ / 2);
587 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ));
588 silence->make_silent ();
589 Audio (silence, _audio_position);
591 _audio_position += t;
595 Player::film_changed (Film::Property p)
597 /* Here we should notice Film properties that affect our output, and
598 alert listeners that our output now would be different to how it was
599 last time we were run.
602 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER) {
608 Player::update_subtitle ()
610 shared_ptr<Piece> piece = _in_subtitle.piece.lock ();
615 if (!_in_subtitle.subtitle->image) {
616 _out_subtitle.image.reset ();
620 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
623 dcpomatic::Rect<double> in_rect = _in_subtitle.subtitle->rect;
624 libdcp::Size scaled_size;
626 in_rect.y += sc->subtitle_offset ();
628 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
629 scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
630 scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
632 /* Then we need a corrective translation, consisting of two parts:
634 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
635 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
637 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
638 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
639 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
641 * Combining these two translations gives these expressions.
644 _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
645 _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
647 _out_subtitle.image = _in_subtitle.subtitle->image->scale (
649 Scaler::from_id ("bicubic"),
654 _out_subtitle.from = _in_subtitle.subtitle->dcp_time;
655 _out_subtitle.to = _in_subtitle.subtitle->dcp_time_to;
658 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
659 * @return false if this could not be done.
662 Player::repeat_last_video ()
664 if (!_last_incoming_video.video || !_have_valid_pieces) {
669 _last_incoming_video.weak_piece,
670 _last_incoming_video.video
677 Player::set_approximate_size ()
679 _approximate_size = true;
683 PlayerImage::PlayerImage (
684 shared_ptr<const Image> in,
686 libdcp::Size inter_size,
687 libdcp::Size out_size,
688 Scaler const * scaler
692 , _inter_size (inter_size)
693 , _out_size (out_size)
700 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
702 _subtitle_image = image;
703 _subtitle_position = pos;
707 PlayerImage::image (AVPixelFormat format, bool aligned)
709 shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
711 Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
713 if (_subtitle_image) {
714 out->alpha_blend (_subtitle_image, _subtitle_position);
721 PlayerStatistics::dump (shared_ptr<Log> log) const
723 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
724 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence));
727 PlayerStatistics const &
728 Player::statistics () const