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 AudioMapping map = content->audio_mapping ();
338 for (int i = 0; i < map.content_channels(); ++i) {
339 for (int j = 0; j < _film->audio_channels(); ++j) {
340 if (map.get (i, static_cast<libdcp::Channel> (j)) > 0) {
341 dcp_mapped->accumulate_channel (
344 static_cast<libdcp::Channel> (j),
345 map.get (i, static_cast<libdcp::Channel> (j))
351 audio->data = dcp_mapped;
354 audio->dcp_time += content->audio_delay() * TIME_HZ / 1000;
355 if (audio->dcp_time < 0) {
356 int const frames = - audio->dcp_time * _film->audio_frame_rate() / TIME_HZ;
357 if (frames >= audio->data->frames ()) {
361 shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->data->channels(), audio->data->frames() - frames));
362 trimmed->copy_from (audio->data.get(), audio->data->frames() - frames, frames, 0);
364 audio->data = trimmed;
368 _audio_merger.push (audio->data, audio->dcp_time);
374 TimedAudioBuffers<DCPTime> tb = _audio_merger.flush ();
375 if (_audio && tb.audio) {
376 Audio (tb.audio, tb.time);
377 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
380 while (_video && _video_position < _audio_position) {
384 while (_audio && _audio_position < _video_position) {
385 emit_silence (_video_position - _audio_position);
390 /** Seek so that the next pass() will yield (approximately) the requested frame.
391 * Pass accurate = true to try harder to get close to the request.
392 * @return true on error
395 Player::seek (DCPTime t, bool accurate)
397 if (!_have_valid_pieces) {
401 if (_pieces.empty ()) {
405 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
406 /* s is the offset of t from the start position of this content */
407 DCPTime s = t - (*i)->content->position ();
408 s = max (static_cast<DCPTime> (0), s);
409 s = min ((*i)->content->length_after_trim(), s);
411 /* Convert this to the content time */
412 ContentTime ct = (s + (*i)->content->trim_start()) * (*i)->frc.speed_up;
414 /* And seek the decoder */
415 (*i)->decoder->seek (ct, accurate);
418 _video_position = time_round_up (t, TIME_HZ / _film->video_frame_rate());
419 _audio_position = time_round_up (t, TIME_HZ / _film->audio_frame_rate());
421 _audio_merger.clear (_audio_position);
424 /* We just did an inaccurate seek, so it's likely that the next thing seen
425 out of pass() will be a fair distance from _{video,audio}_position. Setting
426 this flag stops pass() from trying to fix that: we assume that if it
427 was an inaccurate seek then the caller does not care too much about
428 inserting black/silence to keep the time tidy.
430 _just_did_inaccurate_seek = true;
435 Player::setup_pieces ()
437 list<shared_ptr<Piece> > old_pieces = _pieces;
440 ContentList content = _playlist->content ();
442 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
444 shared_ptr<Decoder> decoder;
445 optional<FrameRateChange> frc;
447 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
449 decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
450 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
453 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
455 /* See if we can re-use an old ImageDecoder */
456 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
457 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
458 if (imd && imd->content() == ic) {
464 decoder.reset (new ImageDecoder (_film, ic));
467 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
470 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
472 decoder.reset (new SndfileDecoder (_film, sc));
474 /* Working out the frc for this content is a bit tricky: what if it overlaps
475 two pieces of video content with different frame rates? For now, use
476 the one with the best overlap.
479 DCPTime best_overlap_t = 0;
480 shared_ptr<VideoContent> best_overlap;
481 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
482 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
487 DCPTime const overlap = max (vc->position(), sc->position()) - min (vc->end(), sc->end());
488 if (overlap > best_overlap_t) {
490 best_overlap_t = overlap;
495 frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
497 /* No video overlap; e.g. if the DCP is just audio */
498 frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
502 ContentTime st = (*i)->trim_start() * frc->speed_up;
503 decoder->seek (st, true);
505 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
508 _have_valid_pieces = true;
510 /* The Piece for the _last_incoming_video will no longer be valid */
511 _last_incoming_video.video.reset ();
513 _video_position = _audio_position = 0;
517 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
519 shared_ptr<Content> c = w.lock ();
525 property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
526 property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
527 property == VideoContentProperty::VIDEO_FRAME_TYPE
530 _have_valid_pieces = false;
533 } else if (property == SubtitleContentProperty::SUBTITLE_OFFSET || property == SubtitleContentProperty::SUBTITLE_SCALE) {
539 property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO ||
540 property == VideoContentProperty::VIDEO_FRAME_RATE
545 } else if (property == ContentProperty::PATH) {
552 Player::playlist_changed ()
554 _have_valid_pieces = false;
559 Player::set_video_container_size (libdcp::Size s)
561 _video_container_size = s;
563 shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
570 _video_container_size,
571 _video_container_size,
572 Scaler::from_id ("bicubic")
578 Player::emit_black ()
580 #ifdef DCPOMATIC_DEBUG
581 _last_video.reset ();
584 Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
585 _video_position += _film->video_frames_to_time (1);
586 _last_emit_was_black = true;
590 Player::emit_silence (DCPTime most)
596 DCPTime t = min (most, TIME_HZ / 2);
597 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ));
598 silence->make_silent ();
599 Audio (silence, _audio_position);
601 _audio_position += t;
605 Player::film_changed (Film::Property p)
607 /* Here we should notice Film properties that affect our output, and
608 alert listeners that our output now would be different to how it was
609 last time we were run.
612 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
618 Player::update_subtitle ()
620 shared_ptr<Piece> piece = _in_subtitle.piece.lock ();
625 if (!_in_subtitle.subtitle->image) {
626 _out_subtitle.image.reset ();
630 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
633 dcpomatic::Rect<double> in_rect = _in_subtitle.subtitle->rect;
634 libdcp::Size scaled_size;
636 in_rect.y += sc->subtitle_offset ();
638 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
639 scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
640 scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
642 /* Then we need a corrective translation, consisting of two parts:
644 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
645 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
647 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
648 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
649 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
651 * Combining these two translations gives these expressions.
654 _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
655 _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
657 _out_subtitle.image = _in_subtitle.subtitle->image->scale (
659 Scaler::from_id ("bicubic"),
664 _out_subtitle.from = _in_subtitle.subtitle->dcp_time;
665 _out_subtitle.to = _in_subtitle.subtitle->dcp_time_to;
668 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
669 * @return false if this could not be done.
672 Player::repeat_last_video ()
674 if (!_last_incoming_video.video || !_have_valid_pieces) {
679 _last_incoming_video.weak_piece,
680 _last_incoming_video.video
687 Player::set_approximate_size ()
689 _approximate_size = true;
693 PlayerImage::PlayerImage (
694 shared_ptr<const Image> in,
696 libdcp::Size inter_size,
697 libdcp::Size out_size,
698 Scaler const * scaler
702 , _inter_size (inter_size)
703 , _out_size (out_size)
710 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
712 _subtitle_image = image;
713 _subtitle_position = pos;
717 PlayerImage::image (AVPixelFormat format, bool aligned)
719 shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
721 Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
723 if (_subtitle_image) {
724 out->alpha_blend (_subtitle_image, _subtitle_position);
731 PlayerStatistics::dump (shared_ptr<Log> log) const
733 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
734 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence));
737 PlayerStatistics const &
738 Player::statistics () const