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 shared_ptr<DCPDecoder> dcp = dynamic_pointer_cast<DCPDecoder> (decoder);
147 if (dcp && _play_referenced) {
148 dcp->set_decode_referenced ();
151 _pieces.push_back (shared_ptr<Piece> (new Piece (i, decoder, frc)));
154 _have_valid_pieces = true;
158 Player::playlist_content_changed (weak_ptr<Content> w, int property, bool frequent)
160 shared_ptr<Content> c = w.lock ();
166 property == ContentProperty::POSITION ||
167 property == ContentProperty::LENGTH ||
168 property == ContentProperty::TRIM_START ||
169 property == ContentProperty::TRIM_END ||
170 property == ContentProperty::PATH ||
171 property == VideoContentProperty::FRAME_TYPE ||
172 property == DCPContentProperty::CAN_BE_PLAYED ||
173 property == SubtitleContentProperty::COLOUR ||
174 property == SubtitleContentProperty::OUTLINE ||
175 property == SubtitleContentProperty::OUTLINE_COLOUR ||
176 property == FFmpegContentProperty::SUBTITLE_STREAM ||
177 property == VideoContentProperty::COLOUR_CONVERSION
180 _have_valid_pieces = false;
184 property == SubtitleContentProperty::LINE_SPACING ||
185 property == SubtitleContentProperty::Y_SCALE
188 /* These changes just need the pieces' decoders to be reset.
189 It's quite possible that other changes could be handled by
190 this branch rather than the _have_valid_pieces = false branch
191 above. This would make things a lot faster.
198 property == ContentProperty::VIDEO_FRAME_RATE ||
199 property == SubtitleContentProperty::USE ||
200 property == SubtitleContentProperty::X_OFFSET ||
201 property == SubtitleContentProperty::Y_OFFSET ||
202 property == SubtitleContentProperty::X_SCALE ||
203 property == SubtitleContentProperty::FONTS ||
204 property == VideoContentProperty::CROP ||
205 property == VideoContentProperty::SCALE ||
206 property == VideoContentProperty::FADE_IN ||
207 property == VideoContentProperty::FADE_OUT
215 Player::set_video_container_size (dcp::Size s)
217 _video_container_size = s;
219 _black_image.reset (new Image (AV_PIX_FMT_RGB24, _video_container_size, true));
220 _black_image->make_black ();
224 Player::playlist_changed ()
226 _have_valid_pieces = false;
231 Player::film_changed (Film::Property p)
233 /* Here we should notice Film properties that affect our output, and
234 alert listeners that our output now would be different to how it was
235 last time we were run.
238 if (p == Film::CONTAINER) {
240 } else if (p == Film::VIDEO_FRAME_RATE) {
241 /* Pieces contain a FrameRateChange which contains the DCP frame rate,
242 so we need new pieces here.
244 _have_valid_pieces = false;
246 } else if (p == Film::AUDIO_PROCESSOR) {
247 if (_film->audio_processor ()) {
248 _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
254 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
256 list<PositionImage> all;
258 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
263 /* We will scale the subtitle up to fit _video_container_size */
264 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
266 /* Then we need a corrective translation, consisting of two parts:
268 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
269 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
271 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
272 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
273 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
275 * Combining these two translations gives these expressions.
282 dcp::YUV_TO_RGB_REC601,
283 i->image->pixel_format (),
288 lrint (_video_container_size.width * i->rectangle.x),
289 lrint (_video_container_size.height * i->rectangle.y)
298 shared_ptr<PlayerVideo>
299 Player::black_player_video_frame (DCPTime time) const
301 return shared_ptr<PlayerVideo> (
303 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
307 _video_container_size,
308 _video_container_size,
311 PresetColourConversion::all().front().conversion
316 /** @return All PlayerVideos at the given time. There may be none if the content
317 * at `time' is a DCP which we are passing through (i.e. referring to by reference)
318 * or 2 if we have 3D.
320 list<shared_ptr<PlayerVideo> >
321 Player::get_video (DCPTime time, bool accurate)
323 if (!_have_valid_pieces) {
327 /* Find subtitles for possible burn-in */
329 PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false, true, accurate);
331 list<PositionImage> sub_images;
333 /* Image subtitles */
334 list<PositionImage> c = transform_image_subtitles (ps.image);
335 copy (c.begin(), c.end(), back_inserter (sub_images));
337 /* Text subtitles (rendered to an image) */
338 if (!ps.text.empty ()) {
339 list<PositionImage> s = render_subtitles (ps.text, ps.fonts, _video_container_size);
340 copy (s.begin (), s.end (), back_inserter (sub_images));
343 optional<PositionImage> subtitles;
344 if (!sub_images.empty ()) {
345 subtitles = merge (sub_images);
348 /* Find pieces containing video which is happening now */
350 list<shared_ptr<Piece> > ov = overlaps (
352 time + DCPTime::from_frames (1, _film->video_frame_rate ()),
356 list<shared_ptr<PlayerVideo> > pvf;
359 /* No video content at this time */
360 pvf.push_back (black_player_video_frame (time));
362 /* Some video content at this time */
363 shared_ptr<Piece> last = *(ov.rbegin ());
364 VideoFrameType const last_type = last->content->video->frame_type ();
366 /* Get video from appropriate piece(s) */
367 BOOST_FOREACH (shared_ptr<Piece> piece, ov) {
369 shared_ptr<VideoDecoder> decoder = piece->decoder->video;
370 DCPOMATIC_ASSERT (decoder);
372 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (piece->content);
373 if (dcp_content && dcp_content->reference_video () && !_play_referenced) {
378 /* always use the last video */
380 /* with a corresponding L/R eye if appropriate */
381 (last_type == VIDEO_FRAME_TYPE_3D_LEFT && piece->content->video->frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) ||
382 (last_type == VIDEO_FRAME_TYPE_3D_RIGHT && piece->content->video->frame_type() == VIDEO_FRAME_TYPE_3D_LEFT);
385 /* We want to use this piece */
386 list<ContentVideo> content_video = decoder->get (dcp_to_content_video (piece, time), accurate);
387 if (content_video.empty ()) {
388 pvf.push_back (black_player_video_frame (time));
390 dcp::Size image_size = piece->content->video->scale().size (
391 piece->content->video, _video_container_size, _film->frame_size ()
394 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
396 shared_ptr<PlayerVideo> (
400 piece->content->video->crop (),
401 piece->content->video->fade (i->frame.index()),
403 _video_container_size,
406 piece->content->video->colour_conversion ()
413 /* Discard unused video */
414 decoder->get (dcp_to_content_video (piece, time), accurate);
420 BOOST_FOREACH (shared_ptr<PlayerVideo> p, pvf) {
421 p->set_subtitle (subtitles.get ());
428 /** @return Audio data or 0 if the only audio data here is referenced DCP data */
429 shared_ptr<AudioBuffers>
430 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
432 if (!_have_valid_pieces) {
436 Frame const length_frames = length.frames_round (_film->audio_frame_rate ());
438 shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
439 audio->make_silent ();
441 list<shared_ptr<Piece> > ov = overlaps (time, time + length, has_audio);
446 bool all_referenced = true;
447 BOOST_FOREACH (shared_ptr<Piece> i, ov) {
448 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (i->content);
449 if (i->content->audio && (!dcp_content || !dcp_content->reference_audio ())) {
450 /* There is audio content which is not from a DCP or not set to be referenced */
451 all_referenced = false;
455 if (all_referenced && !_play_referenced) {
456 return shared_ptr<AudioBuffers> ();
459 BOOST_FOREACH (shared_ptr<Piece> i, ov) {
461 DCPOMATIC_ASSERT (i->content->audio);
462 shared_ptr<AudioDecoder> decoder = i->decoder->audio;
463 DCPOMATIC_ASSERT (decoder);
465 /* The time that we should request from the content */
466 DCPTime request = time - DCPTime::from_seconds (i->content->audio->delay() / 1000.0);
467 Frame request_frames = length_frames;
469 if (request < DCPTime ()) {
470 /* We went off the start of the content, so we will need to offset
471 the stuff we get back.
474 request_frames += request.frames_round (_film->audio_frame_rate ());
475 if (request_frames < 0) {
478 request = DCPTime ();
481 Frame const content_frame = dcp_to_resampled_audio (i, request);
483 BOOST_FOREACH (AudioStreamPtr j, i->content->audio->streams ()) {
485 if (j->channels() == 0) {
486 /* Some content (e.g. DCPs) can have streams with no channels */
490 /* Audio from this piece's decoder stream (which might be more or less than what we asked for) */
491 ContentAudio all = decoder->get (j, content_frame, request_frames, accurate);
494 if (i->content->audio->gain() != 0) {
495 shared_ptr<AudioBuffers> gain (new AudioBuffers (all.audio));
496 gain->apply_gain (i->content->audio->gain ());
501 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all.audio->frames()));
502 dcp_mapped->make_silent ();
503 AudioMapping map = j->mapping ();
504 for (int i = 0; i < map.input_channels(); ++i) {
505 for (int j = 0; j < _film->audio_channels(); ++j) {
506 if (map.get (i, j) > 0) {
507 dcp_mapped->accumulate_channel (
517 if (_audio_processor) {
518 dcp_mapped = _audio_processor->run (dcp_mapped, _film->audio_channels ());
521 all.audio = dcp_mapped;
523 audio->accumulate_frames (
525 content_frame - all.frame,
526 offset.frames_round (_film->audio_frame_rate()),
527 min (Frame (all.audio->frames()), request_frames)
536 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
538 DCPTime s = t - piece->content->position ();
539 s = min (piece->content->length_after_trim(), s);
540 s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc));
542 /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
543 then convert that ContentTime to frames at the content's rate. However this fails for
544 situations like content at 29.9978733fps, DCP at 30fps. The accuracy of the Time type is not
545 enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
547 Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
549 return s.frames_floor (piece->frc.dcp) / piece->frc.factor ();
553 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
555 /* See comment in dcp_to_content_video */
556 DCPTime const d = DCPTime::from_frames (f * piece->frc.factor(), piece->frc.dcp) - DCPTime (piece->content->trim_start (), piece->frc);
557 return max (DCPTime (), d + piece->content->position ());
561 Player::dcp_to_resampled_audio (shared_ptr<const Piece> piece, DCPTime t) const
563 DCPTime s = t - piece->content->position ();
564 s = min (piece->content->length_after_trim(), s);
565 /* See notes in dcp_to_content_video */
566 return max (DCPTime (), DCPTime (piece->content->trim_start (), piece->frc) + s).frames_floor (_film->audio_frame_rate ());
570 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
572 DCPTime s = t - piece->content->position ();
573 s = min (piece->content->length_after_trim(), s);
574 return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start());
578 Player::content_subtitle_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
580 return max (DCPTime (), DCPTime (t - piece->content->trim_start(), piece->frc) + piece->content->position());
583 /** @param burnt true to return only subtitles to be burnt, false to return only
584 * subtitles that should not be burnt. This parameter will be ignored if
585 * _always_burn_subtitles is true; in this case, all subtitles will be returned.
588 Player::get_subtitles (DCPTime time, DCPTime length, bool starting, bool burnt, bool accurate)
590 list<shared_ptr<Piece> > subs = overlaps (time, time + length, has_subtitle);
592 PlayerSubtitles ps (time, length);
594 for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
595 if (!(*j)->content->subtitle->use () || (!_always_burn_subtitles && (burnt != (*j)->content->subtitle->burn ()))) {
599 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> ((*j)->content);
600 if (dcp_content && dcp_content->reference_subtitle () && !_play_referenced) {
604 shared_ptr<SubtitleDecoder> subtitle_decoder = (*j)->decoder->subtitle;
605 ContentTime const from = dcp_to_content_subtitle (*j, time);
606 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
607 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
609 list<ContentImageSubtitle> image = subtitle_decoder->get_image (ContentTimePeriod (from, to), starting, accurate);
610 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
612 /* Apply content's subtitle offsets */
613 i->sub.rectangle.x += (*j)->content->subtitle->x_offset ();
614 i->sub.rectangle.y += (*j)->content->subtitle->y_offset ();
616 /* Apply content's subtitle scale */
617 i->sub.rectangle.width *= (*j)->content->subtitle->x_scale ();
618 i->sub.rectangle.height *= (*j)->content->subtitle->y_scale ();
620 /* Apply a corrective translation to keep the subtitle centred after that scale */
621 i->sub.rectangle.x -= i->sub.rectangle.width * ((*j)->content->subtitle->x_scale() - 1);
622 i->sub.rectangle.y -= i->sub.rectangle.height * ((*j)->content->subtitle->y_scale() - 1);
624 ps.image.push_back (i->sub);
627 list<ContentTextSubtitle> text = subtitle_decoder->get_text (ContentTimePeriod (from, to), starting, accurate);
628 BOOST_FOREACH (ContentTextSubtitle& ts, text) {
629 BOOST_FOREACH (dcp::SubtitleString s, ts.subs) {
630 s.set_h_position (s.h_position() + (*j)->content->subtitle->x_offset ());
631 s.set_v_position (s.v_position() + (*j)->content->subtitle->y_offset ());
632 float const xs = (*j)->content->subtitle->x_scale();
633 float const ys = (*j)->content->subtitle->y_scale();
634 float size = s.size();
636 /* Adjust size to express the common part of the scaling;
637 e.g. if xs = ys = 0.5 we scale size by 2.
639 if (xs > 1e-5 && ys > 1e-5) {
640 size *= 1 / min (1 / xs, 1 / ys);
644 /* Then express aspect ratio changes */
645 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
646 s.set_aspect_adjust (xs / ys);
648 s.set_in (dcp::Time(content_subtitle_to_dcp (*j, ts.period().from).seconds(), 1000));
649 s.set_out (dcp::Time(content_subtitle_to_dcp (*j, ts.period().to).seconds(), 1000));
650 ps.text.push_back (s);
651 ps.add_fonts ((*j)->content->subtitle->fonts ());
659 list<shared_ptr<Font> >
660 Player::get_subtitle_fonts ()
662 if (!_have_valid_pieces) {
666 list<shared_ptr<Font> > fonts;
667 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
668 if (p->content->subtitle) {
669 /* XXX: things may go wrong if there are duplicate font IDs
670 with different font files.
672 list<shared_ptr<Font> > f = p->content->subtitle->fonts ();
673 copy (f.begin(), f.end(), back_inserter (fonts));
680 /** Set this player never to produce any video data */
682 Player::set_ignore_video ()
684 _ignore_video = true;
687 /** Set this player never to produce any audio data */
689 Player::set_ignore_audio ()
691 _ignore_audio = true;
694 /** Set whether or not this player should always burn text subtitles into the image,
695 * regardless of the content settings.
696 * @param burn true to always burn subtitles, false to obey content settings.
699 Player::set_always_burn_subtitles (bool burn)
701 _always_burn_subtitles = burn;
708 _have_valid_pieces = false;
712 Player::set_play_referenced ()
714 _play_referenced = true;
715 _have_valid_pieces = false;
718 list<ReferencedReelAsset>
719 Player::get_reel_assets ()
721 list<ReferencedReelAsset> a;
723 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
724 shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i);
729 scoped_ptr<DCPDecoder> decoder;
731 decoder.reset (new DCPDecoder (j, _film->log()));
737 BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
738 DCPTime const from = i->position() + DCPTime::from_frames (offset, _film->video_frame_rate());
739 if (j->reference_video ()) {
741 ReferencedReelAsset (
743 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_picture()->duration(), _film->video_frame_rate()))
748 if (j->reference_audio ()) {
750 ReferencedReelAsset (
752 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_sound()->duration(), _film->video_frame_rate()))
757 if (j->reference_subtitle ()) {
758 DCPOMATIC_ASSERT (k->main_subtitle ());
760 ReferencedReelAsset (
762 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_subtitle()->duration(), _film->video_frame_rate()))
767 /* Assume that main picture duration is the length of the reel */
768 offset += k->main_picture()->duration ();
775 list<shared_ptr<Piece> >
776 Player::overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> valid)
778 if (!_have_valid_pieces) {
782 list<shared_ptr<Piece> > overlaps;
783 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
784 if (valid (i->content.get ()) && i->content->position() < to && i->content->end() > from) {
785 overlaps.push_back (i);
793 Player::reset_pieces ()
795 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
796 i->decoder->reset ();