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 (DCPTime time) const
291 return shared_ptr<PlayerVideo> (
293 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
297 _video_container_size,
298 _video_container_size,
301 PresetColourConversion::all().front().conversion
307 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
309 DCPTime s = t - piece->content->position ();
310 s = min (piece->content->length_after_trim(), s);
311 s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc));
313 /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
314 then convert that ContentTime to frames at the content's rate. However this fails for
315 situations like content at 29.9978733fps, DCP at 30fps. The accuracy of the Time type is not
316 enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
318 Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
320 return s.frames_floor (piece->frc.dcp) / piece->frc.factor ();
324 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
326 /* See comment in dcp_to_content_video */
327 DCPTime const d = DCPTime::from_frames (f * piece->frc.factor(), piece->frc.dcp) - DCPTime (piece->content->trim_start (), piece->frc);
328 return max (DCPTime (), d + piece->content->position ());
332 Player::dcp_to_resampled_audio (shared_ptr<const Piece> piece, DCPTime t) const
334 DCPTime s = t - piece->content->position ();
335 s = min (piece->content->length_after_trim(), s);
336 /* See notes in dcp_to_content_video */
337 return max (DCPTime (), DCPTime (piece->content->trim_start (), piece->frc) + s).frames_floor (_film->audio_frame_rate ());
341 Player::resampled_audio_to_dcp (shared_ptr<const Piece> piece, Frame f) const
343 /* See comment in dcp_to_content_video */
344 DCPTime const d = DCPTime::from_frames (f, _film->audio_frame_rate()) - DCPTime (piece->content->trim_start (), piece->frc);
345 return max (DCPTime (), d + piece->content->position ());
349 Player::dcp_to_content_time (shared_ptr<const Piece> piece, DCPTime t) const
351 DCPTime s = t - piece->content->position ();
352 s = min (piece->content->length_after_trim(), s);
353 return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start());
357 Player::content_time_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
359 return max (DCPTime (), DCPTime (t - piece->content->trim_start(), piece->frc) + piece->content->position());
362 list<shared_ptr<Font> >
363 Player::get_subtitle_fonts ()
365 if (!_have_valid_pieces) {
369 list<shared_ptr<Font> > fonts;
370 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
371 if (p->content->subtitle) {
372 /* XXX: things may go wrong if there are duplicate font IDs
373 with different font files.
375 list<shared_ptr<Font> > f = p->content->subtitle->fonts ();
376 copy (f.begin(), f.end(), back_inserter (fonts));
383 /** Set this player never to produce any video data */
385 Player::set_ignore_video ()
387 _ignore_video = true;
390 /** Set this player never to produce any audio data */
392 Player::set_ignore_audio ()
394 _ignore_audio = true;
397 /** Set whether or not this player should always burn text subtitles into the image,
398 * regardless of the content settings.
399 * @param burn true to always burn subtitles, false to obey content settings.
402 Player::set_always_burn_subtitles (bool burn)
404 _always_burn_subtitles = burn;
411 _have_valid_pieces = false;
415 Player::set_play_referenced ()
417 _play_referenced = true;
418 _have_valid_pieces = false;
421 list<ReferencedReelAsset>
422 Player::get_reel_assets ()
424 list<ReferencedReelAsset> a;
426 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
427 shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i);
432 scoped_ptr<DCPDecoder> decoder;
434 decoder.reset (new DCPDecoder (j, _film->log()));
440 BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
442 DCPOMATIC_ASSERT (j->video_frame_rate ());
443 double const cfr = j->video_frame_rate().get();
444 Frame const trim_start = j->trim_start().frames_round (cfr);
445 Frame const trim_end = j->trim_end().frames_round (cfr);
446 int const ffr = _film->video_frame_rate ();
448 DCPTime const from = i->position() + DCPTime::from_frames (offset, _film->video_frame_rate());
449 if (j->reference_video ()) {
450 shared_ptr<dcp::ReelAsset> ra = k->main_picture ();
451 DCPOMATIC_ASSERT (ra);
452 ra->set_entry_point (ra->entry_point() + trim_start);
453 ra->set_duration (ra->duration() - trim_start - trim_end);
455 ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr)))
459 if (j->reference_audio ()) {
460 shared_ptr<dcp::ReelAsset> ra = k->main_sound ();
461 DCPOMATIC_ASSERT (ra);
462 ra->set_entry_point (ra->entry_point() + trim_start);
463 ra->set_duration (ra->duration() - trim_start - trim_end);
465 ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr)))
469 if (j->reference_subtitle ()) {
470 shared_ptr<dcp::ReelAsset> ra = k->main_subtitle ();
471 DCPOMATIC_ASSERT (ra);
472 ra->set_entry_point (ra->entry_point() + trim_start);
473 ra->set_duration (ra->duration() - trim_start - trim_end);
475 ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr)))
479 /* Assume that main picture duration is the length of the reel */
480 offset += k->main_picture()->duration ();
487 list<shared_ptr<Piece> >
488 Player::overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> valid)
490 if (!_have_valid_pieces) {
494 list<shared_ptr<Piece> > overlaps;
495 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
496 if (valid (i->content.get ()) && i->content->position() < to && i->content->end() > from) {
497 overlaps.push_back (i);
507 if (!_have_valid_pieces) {
511 shared_ptr<Piece> earliest;
512 DCPTime earliest_position;
513 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
514 DCPTime const t = i->content->position() + DCPTime (i->decoder->position(), i->frc);
515 if (!earliest || t < earliest_position) {
516 earliest_position = t;
525 cout << "Pass " << earliest->content->path(0) << "\n";
526 earliest->decoder->pass ();
528 /* Emit any audio that is ready */
530 pair<shared_ptr<AudioBuffers>, DCPTime> audio = _audio_merger.pull (earliest_position);
531 if (audio.first->frames() > 0) {
532 DCPOMATIC_ASSERT (audio.second >= _last_audio_time);
533 DCPTime t = _last_audio_time;
534 while (t < audio.second) {
535 /* Silence up to the time of this new audio */
536 DCPTime block = min (DCPTime::from_seconds (0.5), audio.second - t);
537 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), block.frames_round(_film->audio_frame_rate())));
538 silence->make_silent ();
543 Audio (audio.first, audio.second);
544 _last_audio_time = audio.second;
551 Player::video (weak_ptr<Piece> wp, ContentVideo video)
553 shared_ptr<Piece> piece = wp.lock ();
558 /* Time and period of the frame we will emit */
559 DCPTime const time = content_video_to_dcp (piece, video.frame.index());
560 DCPTimePeriod const period (time, time + DCPTime::from_frames (1, _film->video_frame_rate()));
562 /* Get any subtitles */
564 optional<PositionImage> subtitles;
566 BOOST_FOREACH (PlayerSubtitles i, _subtitles) {
568 if (!i.period.overlap (period)) {
572 list<PositionImage> sub_images;
574 /* Image subtitles */
575 list<PositionImage> c = transform_image_subtitles (i.image);
576 copy (c.begin(), c.end(), back_inserter (sub_images));
578 /* Text subtitles (rendered to an image) */
579 if (!i.text.empty ()) {
580 list<PositionImage> s = render_subtitles (i.text, i.fonts, _video_container_size, time);
581 copy (s.begin (), s.end (), back_inserter (sub_images));
584 if (!sub_images.empty ()) {
585 subtitles = merge (sub_images);
591 if (_last_video_time) {
592 for (DCPTime i = _last_video_time.get(); i < time; i += DCPTime::from_frames (1, _film->video_frame_rate())) {
593 if (_playlist->video_content_at(i) && _last_video) {
594 Video (_last_video->clone (i));
596 Video (black_player_video_frame (i));
605 piece->content->video->crop (),
606 piece->content->video->fade (video.frame.index()),
607 piece->content->video->scale().size (
608 piece->content->video, _video_container_size, _film->frame_size ()
610 _video_container_size,
613 piece->content->video->colour_conversion ()
618 _last_video->set_subtitle (subtitles.get ());
621 _last_video_time = time;
623 cout << "Video @ " << to_string(_last_video_time.get()) << "\n";
626 /* Discard any subtitles we no longer need */
628 for (list<PlayerSubtitles>::iterator i = _subtitles.begin (); i != _subtitles.end(); ) {
629 list<PlayerSubtitles>::iterator tmp = i;
632 if (i->period.to < time) {
633 _subtitles.erase (i);
641 Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_audio)
643 shared_ptr<Piece> piece = wp.lock ();
648 shared_ptr<AudioContent> content = piece->content->audio;
649 DCPOMATIC_ASSERT (content);
651 shared_ptr<AudioBuffers> audio = content_audio.audio;
654 if (content->gain() != 0) {
655 shared_ptr<AudioBuffers> gain (new AudioBuffers (audio));
656 gain->apply_gain (content->gain ());
660 /* XXX: end-trimming used to be checked here */
662 /* Compute time in the DCP */
663 DCPTime const time = resampled_audio_to_dcp (piece, content_audio.frame) + DCPTime::from_seconds (content->delay() / 1000);
666 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames()));
667 dcp_mapped->make_silent ();
669 AudioMapping map = stream->mapping ();
670 for (int i = 0; i < map.input_channels(); ++i) {
671 for (int j = 0; j < dcp_mapped->channels(); ++j) {
672 if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
673 dcp_mapped->accumulate_channel (
676 static_cast<dcp::Channel> (j),
677 map.get (i, static_cast<dcp::Channel> (j))
685 if (_audio_processor) {
686 audio = _audio_processor->run (audio, _film->audio_channels ());
689 _audio_merger.push (audio, time);
693 Player::image_subtitle (weak_ptr<Piece> wp, ContentImageSubtitle subtitle)
695 shared_ptr<Piece> piece = wp.lock ();
700 /* Apply content's subtitle offsets */
701 subtitle.sub.rectangle.x += piece->content->subtitle->x_offset ();
702 subtitle.sub.rectangle.y += piece->content->subtitle->y_offset ();
704 /* Apply content's subtitle scale */
705 subtitle.sub.rectangle.width *= piece->content->subtitle->x_scale ();
706 subtitle.sub.rectangle.height *= piece->content->subtitle->y_scale ();
708 /* Apply a corrective translation to keep the subtitle centred after that scale */
709 subtitle.sub.rectangle.x -= subtitle.sub.rectangle.width * (piece->content->subtitle->x_scale() - 1);
710 subtitle.sub.rectangle.y -= subtitle.sub.rectangle.height * (piece->content->subtitle->y_scale() - 1);
713 ps.image.push_back (subtitle.sub);
714 ps.period = DCPTimePeriod (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to));
716 if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) {
717 _subtitles.push_back (ps);
724 Player::text_subtitle (weak_ptr<Piece> wp, ContentTextSubtitle subtitle)
726 shared_ptr<Piece> piece = wp.lock ();
733 BOOST_FOREACH (dcp::SubtitleString s, subtitle.subs) {
734 s.set_h_position (s.h_position() + piece->content->subtitle->x_offset ());
735 s.set_v_position (s.v_position() + piece->content->subtitle->y_offset ());
736 float const xs = piece->content->subtitle->x_scale();
737 float const ys = piece->content->subtitle->y_scale();
738 float size = s.size();
740 /* Adjust size to express the common part of the scaling;
741 e.g. if xs = ys = 0.5 we scale size by 2.
743 if (xs > 1e-5 && ys > 1e-5) {
744 size *= 1 / min (1 / xs, 1 / ys);
748 /* Then express aspect ratio changes */
749 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
750 s.set_aspect_adjust (xs / ys);
753 ps.period = DCPTimePeriod (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to));
755 s.set_in (dcp::Time(ps.period.from.seconds(), 1000));
756 s.set_out (dcp::Time(ps.period.to.seconds(), 1000));
757 ps.text.push_back (SubtitleString (s, piece->content->subtitle->outline_width()));
758 ps.add_fonts (piece->content->subtitle->fonts ());
761 if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) {
762 _subtitles.push_back (ps);
769 Player::seek (DCPTime time, bool accurate)
771 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
772 if (i->content->position() <= time && time < i->content->end()) {
773 i->decoder->seek (dcp_to_content_time (i, time), accurate);
778 _last_video_time = time - DCPTime::from_frames (1, _film->video_frame_rate ());
780 _last_video_time = optional<DCPTime> ();