2 Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
23 #include "audio_buffers.h"
24 #include "content_audio.h"
25 #include "dcp_content.h"
28 #include "raw_image_proxy.h"
31 #include "render_subtitles.h"
33 #include "content_video.h"
34 #include "player_video.h"
35 #include "frame_rate_change.h"
36 #include "audio_processor.h"
38 #include "referenced_reel_asset.h"
39 #include "decoder_factory.h"
41 #include "video_decoder.h"
42 #include "audio_decoder.h"
43 #include "subtitle_content.h"
44 #include "subtitle_decoder.h"
45 #include "ffmpeg_content.h"
46 #include "audio_content.h"
47 #include "content_subtitle.h"
48 #include "dcp_decoder.h"
49 #include "image_decoder.h"
51 #include <dcp/reel_sound_asset.h>
52 #include <dcp/reel_subtitle_asset.h>
53 #include <dcp/reel_picture_asset.h>
54 #include <boost/foreach.hpp>
61 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
73 using boost::shared_ptr;
74 using boost::weak_ptr;
75 using boost::dynamic_pointer_cast;
76 using boost::optional;
77 using boost::scoped_ptr;
80 has_video (Content* c)
82 return static_cast<bool>(c->video);
86 has_audio (Content* c)
88 return static_cast<bool>(c->audio);
92 has_subtitle (Content* c)
94 return static_cast<bool>(c->subtitle);
97 Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist)
99 , _playlist (playlist)
100 , _have_valid_pieces (false)
101 , _ignore_video (false)
102 , _ignore_audio (false)
103 , _always_burn_subtitles (false)
105 , _play_referenced (false)
107 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
108 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
109 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::playlist_content_changed, this, _1, _2, _3));
110 set_video_container_size (_film->frame_size ());
112 film_changed (Film::AUDIO_PROCESSOR);
116 Player::setup_pieces ()
120 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
122 if (!i->paths_valid ()) {
126 shared_ptr<Decoder> decoder = decoder_factory (i, _film->log());
127 FrameRateChange frc (i->active_video_frame_rate(), _film->video_frame_rate());
130 /* Not something that we can decode; e.g. Atmos content */
134 if (decoder->video && _ignore_video) {
135 decoder->video->set_ignore ();
138 if (decoder->audio && _ignore_audio) {
139 decoder->audio->set_ignore ();
142 if (decoder->audio && _fast) {
143 decoder->audio->set_fast ();
146 _pieces.push_back (shared_ptr<Piece> (new Piece (i, decoder, frc)));
149 _have_valid_pieces = true;
153 Player::playlist_content_changed (weak_ptr<Content> w, int property, bool frequent)
155 shared_ptr<Content> c = w.lock ();
161 property == ContentProperty::POSITION ||
162 property == ContentProperty::LENGTH ||
163 property == ContentProperty::TRIM_START ||
164 property == ContentProperty::TRIM_END ||
165 property == ContentProperty::PATH ||
166 property == VideoContentProperty::FRAME_TYPE ||
167 property == DCPContentProperty::CAN_BE_PLAYED ||
168 property == SubtitleContentProperty::COLOUR ||
169 property == SubtitleContentProperty::OUTLINE ||
170 property == SubtitleContentProperty::OUTLINE_COLOUR ||
171 property == FFmpegContentProperty::SUBTITLE_STREAM
174 _have_valid_pieces = false;
178 property == ContentProperty::VIDEO_FRAME_RATE ||
179 property == SubtitleContentProperty::USE ||
180 property == SubtitleContentProperty::X_OFFSET ||
181 property == SubtitleContentProperty::Y_OFFSET ||
182 property == SubtitleContentProperty::X_SCALE ||
183 property == SubtitleContentProperty::Y_SCALE ||
184 property == SubtitleContentProperty::FONTS ||
185 property == VideoContentProperty::CROP ||
186 property == VideoContentProperty::SCALE ||
187 property == VideoContentProperty::FADE_IN ||
188 property == VideoContentProperty::FADE_OUT ||
189 property == VideoContentProperty::COLOUR_CONVERSION
197 Player::set_video_container_size (dcp::Size s)
199 _video_container_size = s;
201 _black_image.reset (new Image (AV_PIX_FMT_RGB24, _video_container_size, true));
202 _black_image->make_black ();
206 Player::playlist_changed ()
208 _have_valid_pieces = false;
213 Player::film_changed (Film::Property p)
215 /* Here we should notice Film properties that affect our output, and
216 alert listeners that our output now would be different to how it was
217 last time we were run.
220 if (p == Film::CONTAINER) {
222 } else if (p == Film::VIDEO_FRAME_RATE) {
223 /* Pieces contain a FrameRateChange which contains the DCP frame rate,
224 so we need new pieces here.
226 _have_valid_pieces = false;
228 } else if (p == Film::AUDIO_PROCESSOR) {
229 if (_film->audio_processor ()) {
230 _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
236 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
238 list<PositionImage> all;
240 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
245 /* We will scale the subtitle up to fit _video_container_size */
246 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
248 /* Then we need a corrective translation, consisting of two parts:
250 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
251 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
253 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
254 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
255 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
257 * Combining these two translations gives these expressions.
264 dcp::YUV_TO_RGB_REC601,
265 i->image->pixel_format (),
270 lrint (_video_container_size.width * i->rectangle.x),
271 lrint (_video_container_size.height * i->rectangle.y)
280 shared_ptr<PlayerVideo>
281 Player::black_player_video_frame (DCPTime time) const
283 return shared_ptr<PlayerVideo> (
285 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
289 _video_container_size,
290 _video_container_size,
293 PresetColourConversion::all().front().conversion
298 /** @return All PlayerVideos at the given time. There may be none if the content
299 * at `time' is a DCP which we are passing through (i.e. referring to by reference)
300 * or 2 if we have 3D.
302 list<shared_ptr<PlayerVideo> >
303 Player::get_video (DCPTime time, bool accurate)
305 if (!_have_valid_pieces) {
309 /* Find subtitles for possible burn-in */
311 PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false, true, accurate);
313 list<PositionImage> sub_images;
315 /* Image subtitles */
316 list<PositionImage> c = transform_image_subtitles (ps.image);
317 copy (c.begin(), c.end(), back_inserter (sub_images));
319 /* Text subtitles (rendered to an image) */
320 if (!ps.text.empty ()) {
321 list<PositionImage> s = render_subtitles (ps.text, ps.fonts, _video_container_size);
322 copy (s.begin (), s.end (), back_inserter (sub_images));
325 optional<PositionImage> subtitles;
326 if (!sub_images.empty ()) {
327 subtitles = merge (sub_images);
330 /* Find pieces containing video which is happening now */
332 list<shared_ptr<Piece> > ov = overlaps (
334 time + DCPTime::from_frames (1, _film->video_frame_rate ()),
338 list<shared_ptr<PlayerVideo> > pvf;
341 /* No video content at this time */
342 pvf.push_back (black_player_video_frame (time));
344 /* Some video content at this time */
345 shared_ptr<Piece> last = *(ov.rbegin ());
346 VideoFrameType const last_type = last->content->video->frame_type ();
348 /* Get video from appropriate piece(s) */
349 BOOST_FOREACH (shared_ptr<Piece> piece, ov) {
351 shared_ptr<VideoDecoder> decoder = piece->decoder->video;
352 DCPOMATIC_ASSERT (decoder);
354 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (piece->content);
355 if (dcp_content && dcp_content->reference_video () && !_play_referenced) {
360 /* always use the last video */
362 /* with a corresponding L/R eye if appropriate */
363 (last_type == VIDEO_FRAME_TYPE_3D_LEFT && piece->content->video->frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) ||
364 (last_type == VIDEO_FRAME_TYPE_3D_RIGHT && piece->content->video->frame_type() == VIDEO_FRAME_TYPE_3D_LEFT);
367 /* We want to use this piece */
368 list<ContentVideo> content_video = decoder->get (dcp_to_content_video (piece, time), accurate);
369 if (content_video.empty ()) {
370 pvf.push_back (black_player_video_frame (time));
372 dcp::Size image_size = piece->content->video->scale().size (
373 piece->content->video, _video_container_size, _film->frame_size ()
376 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
378 shared_ptr<PlayerVideo> (
382 piece->content->video->crop (),
383 piece->content->video->fade (i->frame.index()),
385 _video_container_size,
388 piece->content->video->colour_conversion ()
395 /* Discard unused video */
396 decoder->get (dcp_to_content_video (piece, time), accurate);
402 BOOST_FOREACH (shared_ptr<PlayerVideo> p, pvf) {
403 p->set_subtitle (subtitles.get ());
410 /** @return Audio data or 0 if the only audio data here is referenced DCP data */
411 shared_ptr<AudioBuffers>
412 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
414 if (!_have_valid_pieces) {
418 Frame const length_frames = length.frames_round (_film->audio_frame_rate ());
420 shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
421 audio->make_silent ();
423 list<shared_ptr<Piece> > ov = overlaps (time, time + length, has_audio);
428 bool all_referenced = true;
429 BOOST_FOREACH (shared_ptr<Piece> i, ov) {
430 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (i->content);
431 if (i->content->audio && (!dcp_content || !dcp_content->reference_audio ())) {
432 /* There is audio content which is not from a DCP or not set to be referenced */
433 all_referenced = false;
437 if (all_referenced && !_play_referenced) {
438 return shared_ptr<AudioBuffers> ();
441 BOOST_FOREACH (shared_ptr<Piece> i, ov) {
443 DCPOMATIC_ASSERT (i->content->audio);
444 shared_ptr<AudioDecoder> decoder = i->decoder->audio;
445 DCPOMATIC_ASSERT (decoder);
447 /* The time that we should request from the content */
448 DCPTime request = time - DCPTime::from_seconds (i->content->audio->delay() / 1000.0);
449 Frame request_frames = length_frames;
451 if (request < DCPTime ()) {
452 /* We went off the start of the content, so we will need to offset
453 the stuff we get back.
456 request_frames += request.frames_round (_film->audio_frame_rate ());
457 if (request_frames < 0) {
460 request = DCPTime ();
463 Frame const content_frame = dcp_to_resampled_audio (i, request);
465 BOOST_FOREACH (AudioStreamPtr j, i->content->audio->streams ()) {
467 if (j->channels() == 0) {
468 /* Some content (e.g. DCPs) can have streams with no channels */
472 /* Audio from this piece's decoder stream (which might be more or less than what we asked for) */
473 ContentAudio all = decoder->get (j, content_frame, request_frames, accurate);
476 if (i->content->audio->gain() != 0) {
477 shared_ptr<AudioBuffers> gain (new AudioBuffers (all.audio));
478 gain->apply_gain (i->content->audio->gain ());
483 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all.audio->frames()));
484 dcp_mapped->make_silent ();
485 AudioMapping map = j->mapping ();
486 for (int i = 0; i < map.input_channels(); ++i) {
487 for (int j = 0; j < _film->audio_channels(); ++j) {
488 if (map.get (i, j) > 0) {
489 dcp_mapped->accumulate_channel (
499 if (_audio_processor) {
500 dcp_mapped = _audio_processor->run (dcp_mapped, _film->audio_channels ());
503 all.audio = dcp_mapped;
505 audio->accumulate_frames (
507 content_frame - all.frame,
508 offset.frames_round (_film->audio_frame_rate()),
509 min (Frame (all.audio->frames()), request_frames)
518 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
520 DCPTime s = t - piece->content->position ();
521 s = min (piece->content->length_after_trim(), s);
522 s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc));
524 /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
525 then convert that ContentTime to frames at the content's rate. However this fails for
526 situations like content at 29.9978733fps, DCP at 30fps. The accuracy of the Time type is not
527 enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
529 Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
531 return s.frames_floor (piece->frc.dcp) / piece->frc.factor ();
535 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
537 /* See comment in dcp_to_content_video */
538 DCPTime const d = DCPTime::from_frames (f * piece->frc.factor(), piece->frc.dcp) - DCPTime (piece->content->trim_start (), piece->frc);
539 return max (DCPTime (), d + piece->content->position ());
543 Player::dcp_to_resampled_audio (shared_ptr<const Piece> piece, DCPTime t) const
545 DCPTime s = t - piece->content->position ();
546 s = min (piece->content->length_after_trim(), s);
547 /* See notes in dcp_to_content_video */
548 return max (DCPTime (), DCPTime (piece->content->trim_start (), piece->frc) + s).frames_floor (_film->audio_frame_rate ());
552 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
554 DCPTime s = t - piece->content->position ();
555 s = min (piece->content->length_after_trim(), s);
556 return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start());
560 Player::content_subtitle_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
562 return max (DCPTime (), DCPTime (t - piece->content->trim_start(), piece->frc) + piece->content->position());
565 /** @param burnt true to return only subtitles to be burnt, false to return only
566 * subtitles that should not be burnt. This parameter will be ignored if
567 * _always_burn_subtitles is true; in this case, all subtitles will be returned.
570 Player::get_subtitles (DCPTime time, DCPTime length, bool starting, bool burnt, bool accurate)
572 list<shared_ptr<Piece> > subs = overlaps (time, time + length, has_subtitle);
574 PlayerSubtitles ps (time, length);
576 for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
577 if (!(*j)->content->subtitle->use () || (!_always_burn_subtitles && (burnt != (*j)->content->subtitle->burn ()))) {
581 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> ((*j)->content);
582 if (dcp_content && dcp_content->reference_subtitle () && !_play_referenced) {
586 shared_ptr<SubtitleDecoder> subtitle_decoder = (*j)->decoder->subtitle;
587 ContentTime const from = dcp_to_content_subtitle (*j, time);
588 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
589 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
591 list<ContentImageSubtitle> image = subtitle_decoder->get_image (ContentTimePeriod (from, to), starting, accurate);
592 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
594 /* Apply content's subtitle offsets */
595 i->sub.rectangle.x += (*j)->content->subtitle->x_offset ();
596 i->sub.rectangle.y += (*j)->content->subtitle->y_offset ();
598 /* Apply content's subtitle scale */
599 i->sub.rectangle.width *= (*j)->content->subtitle->x_scale ();
600 i->sub.rectangle.height *= (*j)->content->subtitle->y_scale ();
602 /* Apply a corrective translation to keep the subtitle centred after that scale */
603 i->sub.rectangle.x -= i->sub.rectangle.width * ((*j)->content->subtitle->x_scale() - 1);
604 i->sub.rectangle.y -= i->sub.rectangle.height * ((*j)->content->subtitle->y_scale() - 1);
606 ps.image.push_back (i->sub);
609 list<ContentTextSubtitle> text = subtitle_decoder->get_text (ContentTimePeriod (from, to), starting, accurate);
610 BOOST_FOREACH (ContentTextSubtitle& ts, text) {
611 BOOST_FOREACH (dcp::SubtitleString s, ts.subs) {
612 s.set_h_position (s.h_position() + (*j)->content->subtitle->x_offset ());
613 s.set_v_position (s.v_position() + (*j)->content->subtitle->y_offset ());
614 float const xs = (*j)->content->subtitle->x_scale();
615 float const ys = (*j)->content->subtitle->y_scale();
616 float size = s.size();
618 /* Adjust size to express the common part of the scaling;
619 e.g. if xs = ys = 0.5 we scale size by 2.
621 if (xs > 1e-5 && ys > 1e-5) {
622 size *= 1 / min (1 / xs, 1 / ys);
626 /* Then express aspect ratio changes */
627 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
628 s.set_aspect_adjust (xs / ys);
630 s.set_in (dcp::Time(content_subtitle_to_dcp (*j, ts.period().from).seconds(), 1000));
631 s.set_out (dcp::Time(content_subtitle_to_dcp (*j, ts.period().to).seconds(), 1000));
632 ps.text.push_back (s);
633 ps.add_fonts ((*j)->content->subtitle->fonts ());
641 list<shared_ptr<Font> >
642 Player::get_subtitle_fonts ()
644 if (!_have_valid_pieces) {
648 list<shared_ptr<Font> > fonts;
649 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
650 if (p->content->subtitle) {
651 /* XXX: things may go wrong if there are duplicate font IDs
652 with different font files.
654 list<shared_ptr<Font> > f = p->content->subtitle->fonts ();
655 copy (f.begin(), f.end(), back_inserter (fonts));
662 /** Set this player never to produce any video data */
664 Player::set_ignore_video ()
666 _ignore_video = true;
669 /** Set this player never to produce any audio data */
671 Player::set_ignore_audio ()
673 _ignore_audio = true;
676 /** Set whether or not this player should always burn text subtitles into the image,
677 * regardless of the content settings.
678 * @param burn true to always burn subtitles, false to obey content settings.
681 Player::set_always_burn_subtitles (bool burn)
683 _always_burn_subtitles = burn;
690 _have_valid_pieces = false;
694 Player::set_play_referenced ()
696 _play_referenced = true;
697 _have_valid_pieces = false;
700 list<ReferencedReelAsset>
701 Player::get_reel_assets ()
703 list<ReferencedReelAsset> a;
705 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
706 shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i);
711 scoped_ptr<DCPDecoder> decoder;
713 decoder.reset (new DCPDecoder (j, _film->log()));
719 BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
720 DCPTime const from = i->position() + DCPTime::from_frames (offset, _film->video_frame_rate());
721 if (j->reference_video ()) {
723 ReferencedReelAsset (
725 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_picture()->duration(), _film->video_frame_rate()))
730 if (j->reference_audio ()) {
732 ReferencedReelAsset (
734 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_sound()->duration(), _film->video_frame_rate()))
739 if (j->reference_subtitle ()) {
740 DCPOMATIC_ASSERT (k->main_subtitle ());
742 ReferencedReelAsset (
744 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_subtitle()->duration(), _film->video_frame_rate()))
749 /* Assume that main picture duration is the length of the reel */
750 offset += k->main_picture()->duration ();
757 list<shared_ptr<Piece> >
758 Player::overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> valid)
760 if (!_have_valid_pieces) {
764 list<shared_ptr<Piece> > overlaps;
765 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
766 if (valid (i->content.get ()) && i->content->position() < to && i->content->end() > from) {
767 overlaps.push_back (i);