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;
79 Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist)
81 , _playlist (playlist)
82 , _have_valid_pieces (false)
83 , _ignore_video (false)
84 , _ignore_audio (false)
85 , _always_burn_subtitles (false)
87 , _play_referenced (false)
88 , _audio_merger (_film->audio_channels(), _film->audio_frame_rate())
90 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
91 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
92 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::playlist_content_changed, this, _1, _2, _3));
93 set_video_container_size (_film->frame_size ());
95 film_changed (Film::AUDIO_PROCESSOR);
99 Player::setup_pieces ()
103 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
105 if (!i->paths_valid ()) {
109 shared_ptr<Decoder> decoder = decoder_factory (i, _film->log());
110 FrameRateChange frc (i->active_video_frame_rate(), _film->video_frame_rate());
113 /* Not something that we can decode; e.g. Atmos content */
117 if (decoder->video && _ignore_video) {
118 decoder->video->set_ignore ();
121 if (decoder->audio && _ignore_audio) {
122 decoder->audio->set_ignore ();
125 if (decoder->audio && _fast) {
126 decoder->audio->set_fast ();
129 shared_ptr<DCPDecoder> dcp = dynamic_pointer_cast<DCPDecoder> (decoder);
130 if (dcp && _play_referenced) {
131 dcp->set_decode_referenced ();
134 shared_ptr<Piece> piece (new Piece (i, decoder, frc));
135 _pieces.push_back (piece);
137 if (decoder->video) {
138 decoder->video->Data.connect (bind (&Player::video, this, weak_ptr<Piece> (piece), _1));
141 if (decoder->audio) {
142 decoder->audio->Data.connect (bind (&Player::audio, this, weak_ptr<Piece> (piece), _1, _2));
145 if (decoder->subtitle) {
146 decoder->subtitle->ImageData.connect (bind (&Player::image_subtitle, this, weak_ptr<Piece> (piece), _1));
147 decoder->subtitle->TextData.connect (bind (&Player::text_subtitle, this, weak_ptr<Piece> (piece), _1));
151 _have_valid_pieces = true;
155 Player::playlist_content_changed (weak_ptr<Content> w, int property, bool frequent)
157 shared_ptr<Content> c = w.lock ();
163 property == ContentProperty::POSITION ||
164 property == ContentProperty::LENGTH ||
165 property == ContentProperty::TRIM_START ||
166 property == ContentProperty::TRIM_END ||
167 property == ContentProperty::PATH ||
168 property == VideoContentProperty::FRAME_TYPE ||
169 property == DCPContentProperty::NEEDS_ASSETS ||
170 property == DCPContentProperty::NEEDS_KDM ||
171 property == SubtitleContentProperty::COLOUR ||
172 property == SubtitleContentProperty::OUTLINE ||
173 property == SubtitleContentProperty::SHADOW ||
174 property == SubtitleContentProperty::EFFECT_COLOUR ||
175 property == FFmpegContentProperty::SUBTITLE_STREAM ||
176 property == VideoContentProperty::COLOUR_CONVERSION
179 _have_valid_pieces = false;
183 property == SubtitleContentProperty::LINE_SPACING ||
184 property == SubtitleContentProperty::OUTLINE_WIDTH ||
185 property == SubtitleContentProperty::Y_SCALE ||
186 property == SubtitleContentProperty::FADE_IN ||
187 property == SubtitleContentProperty::FADE_OUT ||
188 property == ContentProperty::VIDEO_FRAME_RATE ||
189 property == SubtitleContentProperty::USE ||
190 property == SubtitleContentProperty::X_OFFSET ||
191 property == SubtitleContentProperty::Y_OFFSET ||
192 property == SubtitleContentProperty::X_SCALE ||
193 property == SubtitleContentProperty::FONTS ||
194 property == VideoContentProperty::CROP ||
195 property == VideoContentProperty::SCALE ||
196 property == VideoContentProperty::FADE_IN ||
197 property == VideoContentProperty::FADE_OUT
205 Player::set_video_container_size (dcp::Size s)
207 _video_container_size = s;
209 _black_image.reset (new Image (AV_PIX_FMT_RGB24, _video_container_size, true));
210 _black_image->make_black ();
214 Player::playlist_changed ()
216 _have_valid_pieces = false;
221 Player::film_changed (Film::Property p)
223 /* Here we should notice Film properties that affect our output, and
224 alert listeners that our output now would be different to how it was
225 last time we were run.
228 if (p == Film::CONTAINER) {
230 } else if (p == Film::VIDEO_FRAME_RATE) {
231 /* Pieces contain a FrameRateChange which contains the DCP frame rate,
232 so we need new pieces here.
234 _have_valid_pieces = false;
236 } else if (p == Film::AUDIO_PROCESSOR) {
237 if (_film->audio_processor ()) {
238 _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
244 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
246 list<PositionImage> all;
248 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
253 /* We will scale the subtitle up to fit _video_container_size */
254 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
256 /* Then we need a corrective translation, consisting of two parts:
258 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
259 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
261 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
262 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
263 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
265 * Combining these two translations gives these expressions.
272 dcp::YUV_TO_RGB_REC601,
273 i->image->pixel_format (),
278 lrint (_video_container_size.width * i->rectangle.x),
279 lrint (_video_container_size.height * i->rectangle.y)
288 shared_ptr<PlayerVideo>
289 Player::black_player_video_frame () const
291 return shared_ptr<PlayerVideo> (
293 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
296 _video_container_size,
297 _video_container_size,
300 PresetColourConversion::all().front().conversion
306 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
308 DCPTime s = t - piece->content->position ();
309 s = min (piece->content->length_after_trim(), s);
310 s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc));
312 /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
313 then convert that ContentTime to frames at the content's rate. However this fails for
314 situations like content at 29.9978733fps, DCP at 30fps. The accuracy of the Time type is not
315 enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
317 Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
319 return s.frames_floor (piece->frc.dcp) / piece->frc.factor ();
323 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
325 /* See comment in dcp_to_content_video */
326 DCPTime const d = DCPTime::from_frames (f * piece->frc.factor(), piece->frc.dcp) - DCPTime (piece->content->trim_start (), piece->frc);
327 return max (DCPTime (), d + piece->content->position ());
331 Player::dcp_to_resampled_audio (shared_ptr<const Piece> piece, DCPTime t) const
333 DCPTime s = t - piece->content->position ();
334 s = min (piece->content->length_after_trim(), s);
335 /* See notes in dcp_to_content_video */
336 return max (DCPTime (), DCPTime (piece->content->trim_start (), piece->frc) + s).frames_floor (_film->audio_frame_rate ());
340 Player::resampled_audio_to_dcp (shared_ptr<const Piece> piece, Frame f) const
342 /* See comment in dcp_to_content_video */
343 DCPTime const d = DCPTime::from_frames (f, _film->audio_frame_rate()) - DCPTime (piece->content->trim_start (), piece->frc);
344 return max (DCPTime (), d + piece->content->position ());
348 Player::dcp_to_content_time (shared_ptr<const Piece> piece, DCPTime t) const
350 DCPTime s = t - piece->content->position ();
351 s = min (piece->content->length_after_trim(), s);
352 return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start());
356 Player::content_time_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
358 return max (DCPTime (), DCPTime (t - piece->content->trim_start(), piece->frc) + piece->content->position());
361 list<shared_ptr<Font> >
362 Player::get_subtitle_fonts ()
364 if (!_have_valid_pieces) {
368 list<shared_ptr<Font> > fonts;
369 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
370 if (p->content->subtitle) {
371 /* XXX: things may go wrong if there are duplicate font IDs
372 with different font files.
374 list<shared_ptr<Font> > f = p->content->subtitle->fonts ();
375 copy (f.begin(), f.end(), back_inserter (fonts));
382 /** Set this player never to produce any video data */
384 Player::set_ignore_video ()
386 _ignore_video = true;
389 /** Set this player never to produce any audio data */
391 Player::set_ignore_audio ()
393 _ignore_audio = true;
396 /** Set whether or not this player should always burn text subtitles into the image,
397 * regardless of the content settings.
398 * @param burn true to always burn subtitles, false to obey content settings.
401 Player::set_always_burn_subtitles (bool burn)
403 _always_burn_subtitles = burn;
410 _have_valid_pieces = false;
414 Player::set_play_referenced ()
416 _play_referenced = true;
417 _have_valid_pieces = false;
420 list<ReferencedReelAsset>
421 Player::get_reel_assets ()
423 list<ReferencedReelAsset> a;
425 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
426 shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i);
431 scoped_ptr<DCPDecoder> decoder;
433 decoder.reset (new DCPDecoder (j, _film->log()));
439 BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
441 DCPOMATIC_ASSERT (j->video_frame_rate ());
442 double const cfr = j->video_frame_rate().get();
443 Frame const trim_start = j->trim_start().frames_round (cfr);
444 Frame const trim_end = j->trim_end().frames_round (cfr);
445 int const ffr = _film->video_frame_rate ();
447 DCPTime const from = i->position() + DCPTime::from_frames (offset, _film->video_frame_rate());
448 if (j->reference_video ()) {
449 shared_ptr<dcp::ReelAsset> ra = k->main_picture ();
450 DCPOMATIC_ASSERT (ra);
451 ra->set_entry_point (ra->entry_point() + trim_start);
452 ra->set_duration (ra->duration() - trim_start - trim_end);
454 ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr)))
458 if (j->reference_audio ()) {
459 shared_ptr<dcp::ReelAsset> ra = k->main_sound ();
460 DCPOMATIC_ASSERT (ra);
461 ra->set_entry_point (ra->entry_point() + trim_start);
462 ra->set_duration (ra->duration() - trim_start - trim_end);
464 ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr)))
468 if (j->reference_subtitle ()) {
469 shared_ptr<dcp::ReelAsset> ra = k->main_subtitle ();
470 DCPOMATIC_ASSERT (ra);
471 ra->set_entry_point (ra->entry_point() + trim_start);
472 ra->set_duration (ra->duration() - trim_start - trim_end);
474 ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr)))
478 /* Assume that main picture duration is the length of the reel */
479 offset += k->main_picture()->duration ();
486 list<shared_ptr<Piece> >
487 Player::overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> valid)
489 if (!_have_valid_pieces) {
493 list<shared_ptr<Piece> > overlaps;
494 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
495 if (valid (i->content.get ()) && i->content->position() < to && i->content->end() > from) {
496 overlaps.push_back (i);
506 if (!_have_valid_pieces) {
510 shared_ptr<Piece> earliest;
511 DCPTime earliest_position;
512 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
513 DCPTime const t = i->content->position() + DCPTime (i->decoder->position(), i->frc);
514 if (!earliest || t < earliest_position) {
515 earliest_position = t;
524 cout << "Pass " << earliest->content->path(0) << "\n";
525 earliest->decoder->pass ();
527 /* Emit any audio that is ready */
529 pair<shared_ptr<AudioBuffers>, DCPTime> audio = _audio_merger.pull (earliest_position);
530 if (audio.first->frames() > 0) {
531 DCPOMATIC_ASSERT (audio.second >= _last_audio_time);
532 DCPTime t = _last_audio_time;
533 while (t < audio.second) {
534 /* Silence up to the time of this new audio */
535 DCPTime block = min (DCPTime::from_seconds (0.5), audio.second - t);
536 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), block.frames_round(_film->audio_frame_rate())));
537 silence->make_silent ();
542 Audio (audio.first, audio.second);
543 _last_audio_time = audio.second;
550 Player::video (weak_ptr<Piece> wp, ContentVideo video)
552 shared_ptr<Piece> piece = wp.lock ();
557 /* Time and period of the frame we will emit */
558 DCPTime const time = content_video_to_dcp (piece, video.frame.index());
559 DCPTimePeriod const period (time, time + DCPTime::from_frames (1, _film->video_frame_rate()));
561 /* Get any subtitles */
563 optional<PositionImage> subtitles;
565 for (list<pair<PlayerSubtitles, DCPTimePeriod> >::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
567 if (!i->second.overlap (period)) {
571 list<PositionImage> sub_images;
573 /* Image subtitles */
574 list<PositionImage> c = transform_image_subtitles (i->first.image);
575 copy (c.begin(), c.end(), back_inserter (sub_images));
577 /* Text subtitles (rendered to an image) */
578 if (!i->first.text.empty ()) {
579 list<PositionImage> s = render_subtitles (i->first.text, i->first.fonts, _video_container_size, time);
580 copy (s.begin (), s.end (), back_inserter (sub_images));
583 if (!sub_images.empty ()) {
584 subtitles = merge (sub_images);
590 if (_last_video_time) {
591 for (DCPTime i = _last_video_time.get(); i < time; i += DCPTime::from_frames (1, _film->video_frame_rate())) {
592 if (_playlist->video_content_at(i) && _last_video) {
593 Video (shared_ptr<PlayerVideo> (new PlayerVideo (*_last_video)), i);
595 Video (black_player_video_frame (), i);
603 piece->content->video->crop (),
604 piece->content->video->fade (video.frame.index()),
605 piece->content->video->scale().size (
606 piece->content->video, _video_container_size, _film->frame_size ()
608 _video_container_size,
611 piece->content->video->colour_conversion ()
616 _last_video->set_subtitle (subtitles.get ());
619 _last_video_time = time;
621 cout << "Video @ " << to_string(_last_video_time.get()) << "\n";
622 Video (_last_video, *_last_video_time);
624 /* Discard any subtitles we no longer need */
626 for (list<pair<PlayerSubtitles, DCPTimePeriod> >::iterator i = _subtitles.begin (); i != _subtitles.end(); ) {
627 list<pair<PlayerSubtitles, DCPTimePeriod> >::iterator tmp = i;
630 if (i->second.to < time) {
631 _subtitles.erase (i);
639 Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_audio)
641 shared_ptr<Piece> piece = wp.lock ();
646 shared_ptr<AudioContent> content = piece->content->audio;
647 DCPOMATIC_ASSERT (content);
649 shared_ptr<AudioBuffers> audio = content_audio.audio;
652 if (content->gain() != 0) {
653 shared_ptr<AudioBuffers> gain (new AudioBuffers (audio));
654 gain->apply_gain (content->gain ());
658 /* XXX: end-trimming used to be checked here */
660 /* Compute time in the DCP */
661 DCPTime const time = resampled_audio_to_dcp (piece, content_audio.frame) + DCPTime::from_seconds (content->delay() / 1000);
664 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames()));
665 dcp_mapped->make_silent ();
667 AudioMapping map = stream->mapping ();
668 for (int i = 0; i < map.input_channels(); ++i) {
669 for (int j = 0; j < dcp_mapped->channels(); ++j) {
670 if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
671 dcp_mapped->accumulate_channel (
674 static_cast<dcp::Channel> (j),
675 map.get (i, static_cast<dcp::Channel> (j))
683 if (_audio_processor) {
684 audio = _audio_processor->run (audio, _film->audio_channels ());
687 _audio_merger.push (audio, time);
691 Player::image_subtitle (weak_ptr<Piece> wp, ContentImageSubtitle subtitle)
693 shared_ptr<Piece> piece = wp.lock ();
698 /* Apply content's subtitle offsets */
699 subtitle.sub.rectangle.x += piece->content->subtitle->x_offset ();
700 subtitle.sub.rectangle.y += piece->content->subtitle->y_offset ();
702 /* Apply content's subtitle scale */
703 subtitle.sub.rectangle.width *= piece->content->subtitle->x_scale ();
704 subtitle.sub.rectangle.height *= piece->content->subtitle->y_scale ();
706 /* Apply a corrective translation to keep the subtitle centred after that scale */
707 subtitle.sub.rectangle.x -= subtitle.sub.rectangle.width * (piece->content->subtitle->x_scale() - 1);
708 subtitle.sub.rectangle.y -= subtitle.sub.rectangle.height * (piece->content->subtitle->y_scale() - 1);
711 ps.image.push_back (subtitle.sub);
712 DCPTimePeriod period (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to));
714 if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) {
715 _subtitles.push_back (make_pair (ps, period));
717 Subtitle (ps, period);
722 Player::text_subtitle (weak_ptr<Piece> wp, ContentTextSubtitle subtitle)
724 shared_ptr<Piece> piece = wp.lock ();
730 DCPTimePeriod const period (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to));
732 BOOST_FOREACH (dcp::SubtitleString s, subtitle.subs) {
733 s.set_h_position (s.h_position() + piece->content->subtitle->x_offset ());
734 s.set_v_position (s.v_position() + piece->content->subtitle->y_offset ());
735 float const xs = piece->content->subtitle->x_scale();
736 float const ys = piece->content->subtitle->y_scale();
737 float size = s.size();
739 /* Adjust size to express the common part of the scaling;
740 e.g. if xs = ys = 0.5 we scale size by 2.
742 if (xs > 1e-5 && ys > 1e-5) {
743 size *= 1 / min (1 / xs, 1 / ys);
747 /* Then express aspect ratio changes */
748 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
749 s.set_aspect_adjust (xs / ys);
752 s.set_in (dcp::Time(period.from.seconds(), 1000));
753 s.set_out (dcp::Time(period.to.seconds(), 1000));
754 ps.text.push_back (SubtitleString (s, piece->content->subtitle->outline_width()));
755 ps.add_fonts (piece->content->subtitle->fonts ());
758 if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) {
759 _subtitles.push_back (make_pair (ps, period));
761 Subtitle (ps, period);
766 Player::seek (DCPTime time, bool accurate)
768 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
769 if (i->content->position() <= time && time < i->content->end()) {
770 i->decoder->seek (dcp_to_content_time (i, time), accurate);
775 _last_video_time = time - DCPTime::from_frames (1, _film->video_frame_rate ());
777 _last_video_time = optional<DCPTime> ();