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.
23 #include "ffmpeg_decoder.h"
24 #include "ffmpeg_content.h"
25 #include "image_decoder.h"
26 #include "image_content.h"
27 #include "sndfile_decoder.h"
28 #include "sndfile_content.h"
29 #include "subtitle_content.h"
44 using boost::shared_ptr;
45 using boost::weak_ptr;
46 using boost::dynamic_pointer_cast;
47 using boost::optional;
52 Piece (shared_ptr<Content> c, shared_ptr<Decoder> d, FrameRateChange f)
58 shared_ptr<Content> content;
59 shared_ptr<Decoder> decoder;
63 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
68 , _have_valid_pieces (false)
71 , _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1))
72 , _last_emit_was_black (false)
73 , _just_did_inaccurate_seek (false)
74 , _approximate_size (false)
76 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
77 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
78 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
79 set_video_container_size (fit_ratio_within (_film->container()->ratio (), _film->full_frame ()));
83 Player::disable_video ()
89 Player::disable_audio ()
97 if (!_have_valid_pieces) {
101 /* Interrogate all our pieces to find the one with the earliest decoded data */
103 shared_ptr<Piece> earliest_piece;
104 shared_ptr<Decoded> earliest_decoded;
105 DCPTime earliest_time = TIME_MAX;
106 DCPTime earliest_audio = TIME_MAX;
108 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
110 shared_ptr<Decoded> dec = (*i)->decoder->peek ();
113 dec->set_dcp_times ((*i)->frc.speed_up, (*i)->content->position());
116 if (dec && dec->dcp_time < earliest_time) {
118 earliest_decoded = dec;
119 earliest_time = dec->dcp_time;
122 if (dynamic_pointer_cast<DecodedAudio> (dec) && dec->dcp_time < earliest_audio) {
123 earliest_audio = dec->dcp_time;
127 if (!earliest_piece) {
132 if (earliest_audio != TIME_MAX) {
133 TimedAudioBuffers<DCPTime> tb = _audio_merger.pull (earliest_audio);
134 Audio (tb.audio, tb.time);
135 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
138 /* Emit the earliest thing */
140 shared_ptr<DecodedVideo> dv = dynamic_pointer_cast<DecodedVideo> (earliest_decoded);
141 shared_ptr<DecodedAudio> da = dynamic_pointer_cast<DecodedAudio> (earliest_decoded);
142 shared_ptr<DecodedSubtitle> ds = dynamic_pointer_cast<DecodedSubtitle> (earliest_decoded);
145 DCPTime const half_frame = TIME_HZ / (2 * _film->video_frame_rate ());
147 if (_just_did_inaccurate_seek) {
148 /* Just emit; no subtlety */
149 emit_video (earliest_piece, dv);
150 earliest_piece->decoder->get ();
151 } else if (earliest_time > (_video_position + half_frame)) {
153 /* See if we're inside some video content */
154 list<shared_ptr<Piece> >::iterator i = _pieces.begin();
155 while (i != _pieces.end() && ((*i)->content->position() >= _video_position || _video_position >= (*i)->content->end())) {
159 if (i == _pieces.end() || !_last_incoming_video.video || !_have_valid_pieces) {
160 /* We're outside all video content */
163 _last_incoming_video.video->dcp_time = _video_position;
164 emit_video (_last_incoming_video.weak_piece, _last_incoming_video.video);
170 dv->dcp_time >= _video_position &&
171 !earliest_piece->content->trimmed (dv->dcp_time - earliest_piece->content->position ())
174 emit_video (earliest_piece, dv);
177 earliest_piece->decoder->get ();
180 } else if (da && _audio) {
181 if (!_just_did_inaccurate_seek && earliest_time > _audio_position) {
182 emit_silence (earliest_time - _audio_position);
184 emit_audio (earliest_piece, da);
185 earliest_piece->decoder->get ();
187 } else if (ds && _video) {
188 _in_subtitle.piece = earliest_piece;
189 _in_subtitle.subtitle = ds;
191 earliest_piece->decoder->get ();
194 _just_did_inaccurate_seek = false;
200 Player::emit_video (weak_ptr<Piece> weak_piece, shared_ptr<DecodedVideo> video)
202 /* Keep a note of what came in so that we can repeat it if required */
203 _last_incoming_video.weak_piece = weak_piece;
204 _last_incoming_video.video = video;
206 shared_ptr<Piece> piece = weak_piece.lock ();
211 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
214 FrameRateChange frc (content->video_frame_rate(), _film->video_frame_rate());
216 float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio();
217 libdcp::Size image_size = fit_ratio_within (ratio, _video_container_size);
218 if (_approximate_size) {
219 image_size.width &= ~3;
220 image_size.height &= ~3;
223 shared_ptr<PlayerImage> pi (
228 _video_container_size,
234 _film->with_subtitles () &&
235 _out_subtitle.subtitle->image &&
236 video->dcp_time >= _out_subtitle.subtitle->dcp_time && video->dcp_time <= _out_subtitle.subtitle->dcp_time_to
239 Position<int> const container_offset (
240 (_video_container_size.width - image_size.width) / 2,
241 (_video_container_size.height - image_size.width) / 2
244 pi->set_subtitle (_out_subtitle.subtitle->image, _out_subtitle.position + container_offset);
247 #ifdef DCPOMATIC_DEBUG
248 _last_video = piece->content;
251 Video (pi, video->eyes, content->colour_conversion(), video->same, video->dcp_time);
253 _last_emit_was_black = false;
255 /* This is a bit of a hack; don't update _video_position if EYES_RIGHT is on its way */
256 if (video->eyes != EYES_LEFT) {
257 _video_position = rint (video->dcp_time + TIME_HZ / _film->video_frame_rate());
262 Player::emit_audio (weak_ptr<Piece> weak_piece, shared_ptr<DecodedAudio> audio)
264 shared_ptr<Piece> piece = weak_piece.lock ();
269 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
273 if (content->audio_gain() != 0) {
274 shared_ptr<AudioBuffers> gain (new AudioBuffers (audio->data));
275 gain->apply_gain (content->audio_gain ());
279 if (content->trimmed (audio->dcp_time - content->position ())) {
284 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->data->frames()));
285 dcp_mapped->make_silent ();
286 list<pair<int, libdcp::Channel> > map = content->audio_mapping().content_to_dcp ();
287 for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) {
288 if (i->first < audio->data->channels() && i->second < dcp_mapped->channels()) {
289 dcp_mapped->accumulate_channel (audio->data.get(), i->first, i->second);
293 audio->data = dcp_mapped;
296 audio->dcp_time += content->audio_delay() * TIME_HZ / 1000;
297 if (audio->dcp_time < 0) {
298 int const frames = - audio->dcp_time * _film->audio_frame_rate() / TIME_HZ;
299 if (frames >= audio->data->frames ()) {
303 shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->data->channels(), audio->data->frames() - frames));
304 trimmed->copy_from (audio->data.get(), audio->data->frames() - frames, frames, 0);
306 audio->data = trimmed;
310 _audio_merger.push (audio->data, audio->dcp_time);
316 TimedAudioBuffers<DCPTime> tb = _audio_merger.flush ();
318 Audio (tb.audio, tb.time);
319 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
322 while (_video_position < _audio_position) {
326 while (_audio_position < _video_position) {
327 emit_silence (_video_position - _audio_position);
332 /** Seek so that the next pass() will yield (approximately) the requested frame.
333 * Pass accurate = true to try harder to get close to the request.
334 * @return true on error
337 Player::seek (DCPTime t, bool accurate)
339 if (!_have_valid_pieces) {
343 if (_pieces.empty ()) {
347 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
348 /* s is the offset of t from the start position of this content */
349 DCPTime s = t - (*i)->content->position ();
350 s = max (static_cast<DCPTime> (0), s);
351 s = min ((*i)->content->length_after_trim(), s);
353 /* Convert this to the content time */
354 ContentTime ct = (s * (*i)->frc.speed_up) + (*i)->content->trim_start ();
356 /* And seek the decoder */
357 (*i)->decoder->seek (ct, accurate);
360 _video_position = time_round_up (t, TIME_HZ / _film->video_frame_rate());
361 _audio_position = time_round_up (t, TIME_HZ / _film->audio_frame_rate());
363 _audio_merger.clear (_audio_position);
366 /* We just did an inaccurate seek, so it's likely that the next thing seen
367 out of pass() will be a fair distance from _{video,audio}_position. Setting
368 this flag stops pass() from trying to fix that: we assume that if it
369 was an inaccurate seek then the caller does not care too much about
370 inserting black/silence to keep the time tidy.
372 _just_did_inaccurate_seek = true;
377 Player::setup_pieces ()
379 list<shared_ptr<Piece> > old_pieces = _pieces;
382 ContentList content = _playlist->content ();
384 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
386 shared_ptr<Decoder> decoder;
387 optional<FrameRateChange> frc;
389 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
391 decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
392 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
395 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
397 /* See if we can re-use an old ImageDecoder */
398 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
399 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
400 if (imd && imd->content() == ic) {
406 decoder.reset (new ImageDecoder (_film, ic));
409 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
412 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
414 decoder.reset (new SndfileDecoder (_film, sc));
416 /* Working out the frc for this content is a bit tricky: what if it overlaps
417 two pieces of video content with different frame rates? For now, use
418 the one with the best overlap.
421 DCPTime best_overlap_t = 0;
422 shared_ptr<VideoContent> best_overlap;
423 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
424 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
429 DCPTime const overlap = max (vc->position(), sc->position()) - min (vc->end(), sc->end());
430 if (overlap > best_overlap_t) {
432 best_overlap_t = overlap;
437 frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
439 /* No video overlap; e.g. if the DCP is just audio */
440 frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
444 decoder->seek ((*i)->trim_start (), true);
446 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
449 _have_valid_pieces = true;
451 /* The Piece for the _last_incoming_video will no longer be valid */
452 _last_incoming_video.video.reset ();
454 _video_position = _audio_position = 0;
458 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
460 shared_ptr<Content> c = w.lock ();
466 property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
467 property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
468 property == VideoContentProperty::VIDEO_FRAME_TYPE
471 _have_valid_pieces = false;
474 } else if (property == SubtitleContentProperty::SUBTITLE_OFFSET || property == SubtitleContentProperty::SUBTITLE_SCALE) {
479 } else if (property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO) {
483 } else if (property == ContentProperty::PATH) {
490 Player::playlist_changed ()
492 _have_valid_pieces = false;
497 Player::set_video_container_size (libdcp::Size s)
499 _video_container_size = s;
501 shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
508 _video_container_size,
509 _video_container_size,
510 Scaler::from_id ("bicubic")
516 Player::emit_black ()
518 #ifdef DCPOMATIC_DEBUG
519 _last_video.reset ();
522 Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
523 _video_position += _film->video_frames_to_time (1);
524 _last_emit_was_black = true;
528 Player::emit_silence (DCPTime most)
534 DCPTime t = min (most, TIME_HZ / 2);
535 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ));
536 silence->make_silent ();
537 Audio (silence, _audio_position);
539 _audio_position += t;
543 Player::film_changed (Film::Property p)
545 /* Here we should notice Film properties that affect our output, and
546 alert listeners that our output now would be different to how it was
547 last time we were run.
550 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER) {
556 Player::update_subtitle ()
558 shared_ptr<Piece> piece = _in_subtitle.piece.lock ();
563 if (!_in_subtitle.subtitle->image) {
564 _out_subtitle.subtitle->image.reset ();
568 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
571 dcpomatic::Rect<double> in_rect = _in_subtitle.subtitle->rect;
572 libdcp::Size scaled_size;
574 in_rect.y += sc->subtitle_offset ();
576 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
577 scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
578 scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
580 /* Then we need a corrective translation, consisting of two parts:
582 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
583 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
585 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
586 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
587 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
589 * Combining these two translations gives these expressions.
592 _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
593 _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
595 _out_subtitle.subtitle->image = _in_subtitle.subtitle->image->scale (
597 Scaler::from_id ("bicubic"),
598 _in_subtitle.subtitle->image->pixel_format (),
602 _out_subtitle.subtitle->dcp_time = _in_subtitle.subtitle->dcp_time;
603 _out_subtitle.subtitle->dcp_time = _in_subtitle.subtitle->dcp_time;
606 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
607 * @return false if this could not be done.
610 Player::repeat_last_video ()
612 if (!_last_incoming_video.video || !_have_valid_pieces) {
617 _last_incoming_video.weak_piece,
618 _last_incoming_video.video
625 Player::set_approximate_size ()
627 _approximate_size = true;
631 PlayerImage::PlayerImage (
632 shared_ptr<const Image> in,
634 libdcp::Size inter_size,
635 libdcp::Size out_size,
636 Scaler const * scaler
640 , _inter_size (inter_size)
641 , _out_size (out_size)
648 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
650 _subtitle_image = image;
651 _subtitle_position = pos;
655 PlayerImage::image (AVPixelFormat format, bool aligned)
657 shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
659 Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
661 if (_subtitle_image) {
662 out->alpha_blend (_subtitle_image, _subtitle_position);