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)
106 , _audio_merger (_film->audio_channels(), _film->audio_frame_rate())
108 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
109 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
110 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::playlist_content_changed, this, _1, _2, _3));
111 set_video_container_size (_film->frame_size ());
113 film_changed (Film::AUDIO_PROCESSOR);
117 Player::setup_pieces ()
121 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
123 if (!i->paths_valid ()) {
127 shared_ptr<Decoder> decoder = decoder_factory (i, _film->log());
128 FrameRateChange frc (i->active_video_frame_rate(), _film->video_frame_rate());
131 /* Not something that we can decode; e.g. Atmos content */
135 if (decoder->video && _ignore_video) {
136 decoder->video->set_ignore ();
139 if (decoder->audio && _ignore_audio) {
140 decoder->audio->set_ignore ();
143 if (decoder->audio && _fast) {
144 decoder->audio->set_fast ();
147 shared_ptr<DCPDecoder> dcp = dynamic_pointer_cast<DCPDecoder> (decoder);
148 if (dcp && _play_referenced) {
149 dcp->set_decode_referenced ();
152 shared_ptr<Piece> piece (new Piece (i, decoder, frc));
153 _pieces.push_back (piece);
155 if (decoder->video) {
156 decoder->video->Data.connect (bind (&Player::video, this, weak_ptr<Piece> (piece), _1));
159 if (decoder->audio) {
160 decoder->audio->Data.connect (bind (&Player::audio, this, weak_ptr<Piece> (piece), _1, _2));
163 if (decoder->subtitle) {
164 decoder->subtitle->ImageData.connect (bind (&Player::image_subtitle, this, weak_ptr<Piece> (piece), _1));
165 decoder->subtitle->TextData.connect (bind (&Player::text_subtitle, this, weak_ptr<Piece> (piece), _1));
169 _have_valid_pieces = true;
173 Player::playlist_content_changed (weak_ptr<Content> w, int property, bool frequent)
175 shared_ptr<Content> c = w.lock ();
181 property == ContentProperty::POSITION ||
182 property == ContentProperty::LENGTH ||
183 property == ContentProperty::TRIM_START ||
184 property == ContentProperty::TRIM_END ||
185 property == ContentProperty::PATH ||
186 property == VideoContentProperty::FRAME_TYPE ||
187 property == DCPContentProperty::NEEDS_ASSETS ||
188 property == DCPContentProperty::NEEDS_KDM ||
189 property == SubtitleContentProperty::COLOUR ||
190 property == SubtitleContentProperty::OUTLINE ||
191 property == SubtitleContentProperty::SHADOW ||
192 property == SubtitleContentProperty::EFFECT_COLOUR ||
193 property == FFmpegContentProperty::SUBTITLE_STREAM ||
194 property == VideoContentProperty::COLOUR_CONVERSION
197 _have_valid_pieces = false;
201 property == SubtitleContentProperty::LINE_SPACING ||
202 property == SubtitleContentProperty::OUTLINE_WIDTH ||
203 property == SubtitleContentProperty::Y_SCALE ||
204 property == SubtitleContentProperty::FADE_IN ||
205 property == SubtitleContentProperty::FADE_OUT ||
206 property == ContentProperty::VIDEO_FRAME_RATE ||
207 property == SubtitleContentProperty::USE ||
208 property == SubtitleContentProperty::X_OFFSET ||
209 property == SubtitleContentProperty::Y_OFFSET ||
210 property == SubtitleContentProperty::X_SCALE ||
211 property == SubtitleContentProperty::FONTS ||
212 property == VideoContentProperty::CROP ||
213 property == VideoContentProperty::SCALE ||
214 property == VideoContentProperty::FADE_IN ||
215 property == VideoContentProperty::FADE_OUT
223 Player::set_video_container_size (dcp::Size s)
225 _video_container_size = s;
227 _black_image.reset (new Image (AV_PIX_FMT_RGB24, _video_container_size, true));
228 _black_image->make_black ();
232 Player::playlist_changed ()
234 _have_valid_pieces = false;
239 Player::film_changed (Film::Property p)
241 /* Here we should notice Film properties that affect our output, and
242 alert listeners that our output now would be different to how it was
243 last time we were run.
246 if (p == Film::CONTAINER) {
248 } else if (p == Film::VIDEO_FRAME_RATE) {
249 /* Pieces contain a FrameRateChange which contains the DCP frame rate,
250 so we need new pieces here.
252 _have_valid_pieces = false;
254 } else if (p == Film::AUDIO_PROCESSOR) {
255 if (_film->audio_processor ()) {
256 _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
262 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
264 list<PositionImage> all;
266 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
271 /* We will scale the subtitle up to fit _video_container_size */
272 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
274 /* Then we need a corrective translation, consisting of two parts:
276 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
277 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
279 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
280 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
281 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
283 * Combining these two translations gives these expressions.
290 dcp::YUV_TO_RGB_REC601,
291 i->image->pixel_format (),
296 lrint (_video_container_size.width * i->rectangle.x),
297 lrint (_video_container_size.height * i->rectangle.y)
306 shared_ptr<PlayerVideo>
307 Player::black_player_video_frame (DCPTime time) const
309 return shared_ptr<PlayerVideo> (
311 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
315 _video_container_size,
316 _video_container_size,
319 PresetColourConversion::all().front().conversion
325 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
327 DCPTime s = t - piece->content->position ();
328 s = min (piece->content->length_after_trim(), s);
329 s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc));
331 /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
332 then convert that ContentTime to frames at the content's rate. However this fails for
333 situations like content at 29.9978733fps, DCP at 30fps. The accuracy of the Time type is not
334 enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
336 Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
338 return s.frames_floor (piece->frc.dcp) / piece->frc.factor ();
342 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
344 /* See comment in dcp_to_content_video */
345 DCPTime const d = DCPTime::from_frames (f * piece->frc.factor(), piece->frc.dcp) - DCPTime (piece->content->trim_start (), piece->frc);
346 return max (DCPTime (), d + piece->content->position ());
350 Player::dcp_to_resampled_audio (shared_ptr<const Piece> piece, DCPTime t) const
352 DCPTime s = t - piece->content->position ();
353 s = min (piece->content->length_after_trim(), s);
354 /* See notes in dcp_to_content_video */
355 return max (DCPTime (), DCPTime (piece->content->trim_start (), piece->frc) + s).frames_floor (_film->audio_frame_rate ());
359 Player::resampled_audio_to_dcp (shared_ptr<const Piece> piece, Frame f) const
361 /* See comment in dcp_to_content_video */
362 DCPTime const d = DCPTime::from_frames (f, _film->audio_frame_rate()) - DCPTime (piece->content->trim_start (), piece->frc);
363 return max (DCPTime (), d + piece->content->position ());
367 Player::dcp_to_content_time (shared_ptr<const Piece> piece, DCPTime t) const
369 DCPTime s = t - piece->content->position ();
370 s = min (piece->content->length_after_trim(), s);
371 return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start());
375 Player::content_time_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
377 return max (DCPTime (), DCPTime (t - piece->content->trim_start(), piece->frc) + piece->content->position());
380 list<shared_ptr<Font> >
381 Player::get_subtitle_fonts ()
383 if (!_have_valid_pieces) {
387 list<shared_ptr<Font> > fonts;
388 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
389 if (p->content->subtitle) {
390 /* XXX: things may go wrong if there are duplicate font IDs
391 with different font files.
393 list<shared_ptr<Font> > f = p->content->subtitle->fonts ();
394 copy (f.begin(), f.end(), back_inserter (fonts));
401 /** Set this player never to produce any video data */
403 Player::set_ignore_video ()
405 _ignore_video = true;
408 /** Set this player never to produce any audio data */
410 Player::set_ignore_audio ()
412 _ignore_audio = true;
415 /** Set whether or not this player should always burn text subtitles into the image,
416 * regardless of the content settings.
417 * @param burn true to always burn subtitles, false to obey content settings.
420 Player::set_always_burn_subtitles (bool burn)
422 _always_burn_subtitles = burn;
429 _have_valid_pieces = false;
433 Player::set_play_referenced ()
435 _play_referenced = true;
436 _have_valid_pieces = false;
439 list<ReferencedReelAsset>
440 Player::get_reel_assets ()
442 list<ReferencedReelAsset> a;
444 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
445 shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i);
450 scoped_ptr<DCPDecoder> decoder;
452 decoder.reset (new DCPDecoder (j, _film->log()));
458 BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
460 DCPOMATIC_ASSERT (j->video_frame_rate ());
461 double const cfr = j->video_frame_rate().get();
462 Frame const trim_start = j->trim_start().frames_round (cfr);
463 Frame const trim_end = j->trim_end().frames_round (cfr);
464 int const ffr = _film->video_frame_rate ();
466 DCPTime const from = i->position() + DCPTime::from_frames (offset, _film->video_frame_rate());
467 if (j->reference_video ()) {
468 shared_ptr<dcp::ReelAsset> ra = k->main_picture ();
469 DCPOMATIC_ASSERT (ra);
470 ra->set_entry_point (ra->entry_point() + trim_start);
471 ra->set_duration (ra->duration() - trim_start - trim_end);
473 ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr)))
477 if (j->reference_audio ()) {
478 shared_ptr<dcp::ReelAsset> ra = k->main_sound ();
479 DCPOMATIC_ASSERT (ra);
480 ra->set_entry_point (ra->entry_point() + trim_start);
481 ra->set_duration (ra->duration() - trim_start - trim_end);
483 ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr)))
487 if (j->reference_subtitle ()) {
488 shared_ptr<dcp::ReelAsset> ra = k->main_subtitle ();
489 DCPOMATIC_ASSERT (ra);
490 ra->set_entry_point (ra->entry_point() + trim_start);
491 ra->set_duration (ra->duration() - trim_start - trim_end);
493 ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr)))
497 /* Assume that main picture duration is the length of the reel */
498 offset += k->main_picture()->duration ();
505 list<shared_ptr<Piece> >
506 Player::overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> valid)
508 if (!_have_valid_pieces) {
512 list<shared_ptr<Piece> > overlaps;
513 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
514 if (valid (i->content.get ()) && i->content->position() < to && i->content->end() > from) {
515 overlaps.push_back (i);
525 if (!_have_valid_pieces) {
529 shared_ptr<Piece> earliest;
530 DCPTime earliest_position;
531 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
532 DCPTime const t = i->content->position() + DCPTime (i->decoder->position(), i->frc);
533 if (!earliest || t < earliest_position) {
534 earliest_position = t;
543 cout << "Pass " << earliest->content->path(0) << "\n";
544 earliest->decoder->pass ();
546 /* Emit any audio that is ready */
548 pair<shared_ptr<AudioBuffers>, DCPTime> audio = _audio_merger.pull (earliest_position);
549 if (audio.first->frames() > 0) {
550 DCPOMATIC_ASSERT (audio.second >= _last_audio_time);
551 DCPTime t = _last_audio_time;
552 while (t < audio.second) {
553 /* Silence up to the time of this new audio */
554 DCPTime block = min (DCPTime::from_seconds (0.5), audio.second - t);
555 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), block.frames_round(_film->audio_frame_rate())));
556 silence->make_silent ();
561 Audio (audio.first, audio.second);
562 _last_audio_time = audio.second;
569 Player::video (weak_ptr<Piece> wp, ContentVideo video)
571 shared_ptr<Piece> piece = wp.lock ();
576 /* Time and period of the frame we will emit */
577 DCPTime const time = content_video_to_dcp (piece, video.frame.index());
578 DCPTimePeriod const period (time, time + DCPTime::from_frames (1, _film->video_frame_rate()));
580 /* Get any subtitles */
582 optional<PositionImage> subtitles;
584 BOOST_FOREACH (PlayerSubtitles i, _subtitles) {
586 if (!i.period.overlap (period)) {
590 list<PositionImage> sub_images;
592 /* Image subtitles */
593 list<PositionImage> c = transform_image_subtitles (i.image);
594 copy (c.begin(), c.end(), back_inserter (sub_images));
596 /* Text subtitles (rendered to an image) */
597 if (!i.text.empty ()) {
598 list<PositionImage> s = render_subtitles (i.text, i.fonts, _video_container_size, time);
599 copy (s.begin (), s.end (), back_inserter (sub_images));
602 if (!sub_images.empty ()) {
603 subtitles = merge (sub_images);
609 if (_last_video_time) {
610 for (DCPTime i = _last_video_time.get(); i < time; i += DCPTime::from_frames (1, _film->video_frame_rate())) {
611 if (_playlist->video_content_at(i) && _last_video) {
612 Video (_last_video->clone (i));
614 Video (black_player_video_frame (i));
623 piece->content->video->crop (),
624 piece->content->video->fade (video.frame.index()),
625 piece->content->video->scale().size (
626 piece->content->video, _video_container_size, _film->frame_size ()
628 _video_container_size,
631 piece->content->video->colour_conversion ()
636 _last_video->set_subtitle (subtitles.get ());
639 _last_video_time = time;
641 cout << "Video @ " << to_string(_last_video_time.get()) << "\n";
644 /* Discard any subtitles we no longer need */
646 for (list<PlayerSubtitles>::iterator i = _subtitles.begin (); i != _subtitles.end(); ) {
647 list<PlayerSubtitles>::iterator tmp = i;
650 if (i->period.to < time) {
651 _subtitles.erase (i);
659 Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_audio)
661 shared_ptr<Piece> piece = wp.lock ();
666 shared_ptr<AudioContent> content = piece->content->audio;
667 DCPOMATIC_ASSERT (content);
669 shared_ptr<AudioBuffers> audio = content_audio.audio;
672 if (content->gain() != 0) {
673 shared_ptr<AudioBuffers> gain (new AudioBuffers (audio));
674 gain->apply_gain (content->gain ());
678 /* XXX: end-trimming used to be checked here */
680 /* Compute time in the DCP */
681 DCPTime const time = resampled_audio_to_dcp (piece, content_audio.frame) + DCPTime::from_seconds (content->delay() / 1000);
684 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames()));
685 dcp_mapped->make_silent ();
687 AudioMapping map = stream->mapping ();
688 for (int i = 0; i < map.input_channels(); ++i) {
689 for (int j = 0; j < dcp_mapped->channels(); ++j) {
690 if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
691 dcp_mapped->accumulate_channel (
694 static_cast<dcp::Channel> (j),
695 map.get (i, static_cast<dcp::Channel> (j))
703 if (_audio_processor) {
704 audio = _audio_processor->run (audio, _film->audio_channels ());
707 _audio_merger.push (audio, time);
711 Player::image_subtitle (weak_ptr<Piece> wp, ContentImageSubtitle subtitle)
713 shared_ptr<Piece> piece = wp.lock ();
718 /* Apply content's subtitle offsets */
719 subtitle.sub.rectangle.x += piece->content->subtitle->x_offset ();
720 subtitle.sub.rectangle.y += piece->content->subtitle->y_offset ();
722 /* Apply content's subtitle scale */
723 subtitle.sub.rectangle.width *= piece->content->subtitle->x_scale ();
724 subtitle.sub.rectangle.height *= piece->content->subtitle->y_scale ();
726 /* Apply a corrective translation to keep the subtitle centred after that scale */
727 subtitle.sub.rectangle.x -= subtitle.sub.rectangle.width * (piece->content->subtitle->x_scale() - 1);
728 subtitle.sub.rectangle.y -= subtitle.sub.rectangle.height * (piece->content->subtitle->y_scale() - 1);
731 ps.image.push_back (subtitle.sub);
732 ps.period = DCPTimePeriod (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to));
734 if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) {
735 _subtitles.push_back (ps);
742 Player::text_subtitle (weak_ptr<Piece> wp, ContentTextSubtitle subtitle)
744 shared_ptr<Piece> piece = wp.lock ();
751 BOOST_FOREACH (dcp::SubtitleString s, subtitle.subs) {
752 s.set_h_position (s.h_position() + piece->content->subtitle->x_offset ());
753 s.set_v_position (s.v_position() + piece->content->subtitle->y_offset ());
754 float const xs = piece->content->subtitle->x_scale();
755 float const ys = piece->content->subtitle->y_scale();
756 float size = s.size();
758 /* Adjust size to express the common part of the scaling;
759 e.g. if xs = ys = 0.5 we scale size by 2.
761 if (xs > 1e-5 && ys > 1e-5) {
762 size *= 1 / min (1 / xs, 1 / ys);
766 /* Then express aspect ratio changes */
767 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
768 s.set_aspect_adjust (xs / ys);
771 ps.period = DCPTimePeriod (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to));
773 s.set_in (dcp::Time(ps.period.from.seconds(), 1000));
774 s.set_out (dcp::Time(ps.period.to.seconds(), 1000));
775 ps.text.push_back (SubtitleString (s, piece->content->subtitle->outline_width()));
776 ps.add_fonts (piece->content->subtitle->fonts ());
779 if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) {
780 _subtitles.push_back (ps);
787 Player::seek (DCPTime time, bool accurate)
789 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
790 if (i->content->position() <= time && time < i->content->end()) {
791 i->decoder->seek (dcp_to_content_time (i, time), accurate);
796 _last_video_time = time - DCPTime::from_frames (1, _film->video_frame_rate ());
798 _last_video_time = optional<DCPTime> ();