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 shared_ptr<Piece> piece (new Piece (i, decoder, frc));
152 _pieces.push_back (piece);
154 if (decoder->video) {
155 decoder->video->Data.connect (bind (&Player::video, this, weak_ptr<Piece> (piece), _1));
158 if (decoder->audio) {
159 decoder->audio->Data.connect (bind (&Player::audio, this, weak_ptr<Piece> (piece), _1));
162 if (decoder->subtitle) {
163 decoder->subtitle->ImageData.connect (bind (&Player::image_subtitle, this, weak_ptr<Piece> (piece), _1));
164 decoder->subtitle->TextData.connect (bind (&Player::text_subtitle, this, weak_ptr<Piece> (piece), _1));
168 _have_valid_pieces = true;
172 Player::playlist_content_changed (weak_ptr<Content> w, int property, bool frequent)
174 shared_ptr<Content> c = w.lock ();
180 property == ContentProperty::POSITION ||
181 property == ContentProperty::LENGTH ||
182 property == ContentProperty::TRIM_START ||
183 property == ContentProperty::TRIM_END ||
184 property == ContentProperty::PATH ||
185 property == VideoContentProperty::FRAME_TYPE ||
186 property == DCPContentProperty::NEEDS_ASSETS ||
187 property == DCPContentProperty::NEEDS_KDM ||
188 property == SubtitleContentProperty::COLOUR ||
189 property == SubtitleContentProperty::OUTLINE ||
190 property == SubtitleContentProperty::SHADOW ||
191 property == SubtitleContentProperty::EFFECT_COLOUR ||
192 property == FFmpegContentProperty::SUBTITLE_STREAM ||
193 property == VideoContentProperty::COLOUR_CONVERSION
196 _have_valid_pieces = false;
200 property == SubtitleContentProperty::LINE_SPACING ||
201 property == SubtitleContentProperty::OUTLINE_WIDTH ||
202 property == SubtitleContentProperty::Y_SCALE ||
203 property == SubtitleContentProperty::FADE_IN ||
204 property == SubtitleContentProperty::FADE_OUT ||
205 property == ContentProperty::VIDEO_FRAME_RATE ||
206 property == SubtitleContentProperty::USE ||
207 property == SubtitleContentProperty::X_OFFSET ||
208 property == SubtitleContentProperty::Y_OFFSET ||
209 property == SubtitleContentProperty::X_SCALE ||
210 property == SubtitleContentProperty::FONTS ||
211 property == VideoContentProperty::CROP ||
212 property == VideoContentProperty::SCALE ||
213 property == VideoContentProperty::FADE_IN ||
214 property == VideoContentProperty::FADE_OUT
222 Player::set_video_container_size (dcp::Size s)
224 _video_container_size = s;
226 _black_image.reset (new Image (AV_PIX_FMT_RGB24, _video_container_size, true));
227 _black_image->make_black ();
231 Player::playlist_changed ()
233 _have_valid_pieces = false;
238 Player::film_changed (Film::Property p)
240 /* Here we should notice Film properties that affect our output, and
241 alert listeners that our output now would be different to how it was
242 last time we were run.
245 if (p == Film::CONTAINER) {
247 } else if (p == Film::VIDEO_FRAME_RATE) {
248 /* Pieces contain a FrameRateChange which contains the DCP frame rate,
249 so we need new pieces here.
251 _have_valid_pieces = false;
253 } else if (p == Film::AUDIO_PROCESSOR) {
254 if (_film->audio_processor ()) {
255 _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
261 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
263 list<PositionImage> all;
265 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
270 /* We will scale the subtitle up to fit _video_container_size */
271 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
273 /* Then we need a corrective translation, consisting of two parts:
275 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
276 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
278 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
279 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
280 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
282 * Combining these two translations gives these expressions.
289 dcp::YUV_TO_RGB_REC601,
290 i->image->pixel_format (),
295 lrint (_video_container_size.width * i->rectangle.x),
296 lrint (_video_container_size.height * i->rectangle.y)
305 shared_ptr<PlayerVideo>
306 Player::black_player_video_frame (DCPTime time) const
308 return shared_ptr<PlayerVideo> (
310 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
314 _video_container_size,
315 _video_container_size,
318 PresetColourConversion::all().front().conversion
324 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
326 DCPTime s = t - piece->content->position ();
327 s = min (piece->content->length_after_trim(), s);
328 s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc));
330 /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
331 then convert that ContentTime to frames at the content's rate. However this fails for
332 situations like content at 29.9978733fps, DCP at 30fps. The accuracy of the Time type is not
333 enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
335 Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
337 return s.frames_floor (piece->frc.dcp) / piece->frc.factor ();
341 Player::content_video_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 * piece->frc.factor(), piece->frc.dcp) - DCPTime (piece->content->trim_start (), piece->frc);
345 return max (DCPTime (), d + piece->content->position ());
349 Player::dcp_to_resampled_audio (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 /* See notes in dcp_to_content_video */
354 return max (DCPTime (), DCPTime (piece->content->trim_start (), piece->frc) + s).frames_floor (_film->audio_frame_rate ());
358 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
360 DCPTime s = t - piece->content->position ();
361 s = min (piece->content->length_after_trim(), s);
362 return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start());
366 Player::content_subtitle_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
368 return max (DCPTime (), DCPTime (t - piece->content->trim_start(), piece->frc) + piece->content->position());
371 list<shared_ptr<Font> >
372 Player::get_subtitle_fonts ()
374 if (!_have_valid_pieces) {
378 list<shared_ptr<Font> > fonts;
379 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
380 if (p->content->subtitle) {
381 /* XXX: things may go wrong if there are duplicate font IDs
382 with different font files.
384 list<shared_ptr<Font> > f = p->content->subtitle->fonts ();
385 copy (f.begin(), f.end(), back_inserter (fonts));
392 /** Set this player never to produce any video data */
394 Player::set_ignore_video ()
396 _ignore_video = true;
399 /** Set this player never to produce any audio data */
401 Player::set_ignore_audio ()
403 _ignore_audio = true;
406 /** Set whether or not this player should always burn text subtitles into the image,
407 * regardless of the content settings.
408 * @param burn true to always burn subtitles, false to obey content settings.
411 Player::set_always_burn_subtitles (bool burn)
413 _always_burn_subtitles = burn;
420 _have_valid_pieces = false;
424 Player::set_play_referenced ()
426 _play_referenced = true;
427 _have_valid_pieces = false;
430 list<ReferencedReelAsset>
431 Player::get_reel_assets ()
433 list<ReferencedReelAsset> a;
435 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
436 shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i);
441 scoped_ptr<DCPDecoder> decoder;
443 decoder.reset (new DCPDecoder (j, _film->log()));
449 BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
451 DCPOMATIC_ASSERT (j->video_frame_rate ());
452 double const cfr = j->video_frame_rate().get();
453 Frame const trim_start = j->trim_start().frames_round (cfr);
454 Frame const trim_end = j->trim_end().frames_round (cfr);
455 int const ffr = _film->video_frame_rate ();
457 DCPTime const from = i->position() + DCPTime::from_frames (offset, _film->video_frame_rate());
458 if (j->reference_video ()) {
459 shared_ptr<dcp::ReelAsset> ra = k->main_picture ();
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_audio ()) {
469 shared_ptr<dcp::ReelAsset> ra = k->main_sound ();
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 if (j->reference_subtitle ()) {
479 shared_ptr<dcp::ReelAsset> ra = k->main_subtitle ();
480 DCPOMATIC_ASSERT (ra);
481 ra->set_entry_point (ra->entry_point() + trim_start);
482 ra->set_duration (ra->duration() - trim_start - trim_end);
484 ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr)))
488 /* Assume that main picture duration is the length of the reel */
489 offset += k->main_picture()->duration ();
496 list<shared_ptr<Piece> >
497 Player::overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> valid)
499 if (!_have_valid_pieces) {
503 list<shared_ptr<Piece> > overlaps;
504 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
505 if (valid (i->content.get ()) && i->content->position() < to && i->content->end() > from) {
506 overlaps.push_back (i);
516 if (!_have_valid_pieces) {
520 shared_ptr<Piece> earliest;
521 DCPTime earliest_position;
522 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
523 /* Convert i->decoder->position() to DCPTime and work out the earliest */
526 earliest->decoder->pass ();
528 /* XXX: collect audio and maybe emit some */
532 Player::video (weak_ptr<Piece> wp, ContentVideo video)
534 shared_ptr<Piece> piece = wp.lock ();
539 /* Get subs to burn in and burn them in */
544 DCPTime time = content_video_to_dcp (piece, video.frame.index());
546 dcp::Size image_size = piece->content->video->scale().size (
547 piece->content->video, _video_container_size, _film->frame_size ()
551 shared_ptr<PlayerVideo> (
555 piece->content->video->crop (),
556 piece->content->video->fade (video.frame.index()),
558 _video_container_size,
561 piece->content->video->colour_conversion ()
569 Player::audio (weak_ptr<Piece> piece, ContentAudio video)
571 /* gain, remap, processor */
572 /* Put into merge buffer */
576 Player::image_subtitle (weak_ptr<Piece> piece, ContentImageSubtitle subtitle)
578 /* Store for video to see */
582 Player::text_subtitle (weak_ptr<Piece> piece, ContentTextSubtitle subtitle)
584 /* Store for video to see, or emit */
588 Player::seek (DCPTime time, bool accurate)
591 _last_video = time - DCPTime::from_frames (1, _film->video_frame_rate ());