2 Copyright (C) 2013-2015 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "ffmpeg_decoder.h"
23 #include "audio_buffers.h"
24 #include "ffmpeg_content.h"
25 #include "image_decoder.h"
26 #include "image_content.h"
27 #include "sndfile_decoder.h"
28 #include "sndfile_content.h"
29 #include "subtitle_content.h"
30 #include "subrip_decoder.h"
31 #include "subrip_content.h"
32 #include "dcp_content.h"
35 #include "raw_image_proxy.h"
38 #include "render_subtitles.h"
40 #include "content_video.h"
41 #include "player_video.h"
42 #include "frame_rate_change.h"
43 #include "dcp_content.h"
44 #include "dcp_decoder.h"
45 #include "dcp_subtitle_content.h"
46 #include "dcp_subtitle_decoder.h"
47 #include "audio_processor.h"
50 #include <dcp/reel_sound_asset.h>
51 #include <dcp/reel_subtitle_asset.h>
52 #include <dcp/reel_picture_asset.h>
53 #include <boost/foreach.hpp>
60 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
72 using boost::shared_ptr;
73 using boost::weak_ptr;
74 using boost::dynamic_pointer_cast;
75 using boost::optional;
77 Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist)
79 , _playlist (playlist)
80 , _have_valid_pieces (false)
81 , _ignore_video (false)
82 , _ignore_audio (false)
83 , _always_burn_subtitles (false)
85 , _play_referenced (false)
87 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
88 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
89 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::playlist_content_changed, this, _1, _2, _3));
90 set_video_container_size (_film->frame_size ());
92 film_changed (Film::AUDIO_PROCESSOR);
96 Player::setup_pieces ()
98 list<shared_ptr<Piece> > old_pieces = _pieces;
101 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
103 if (!i->paths_valid ()) {
107 shared_ptr<Decoder> decoder;
108 optional<FrameRateChange> frc;
111 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (i);
113 decoder.reset (new FFmpegDecoder (fc, _film->log(), _fast));
114 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
117 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
119 decoder.reset (new DCPDecoder (dc, _fast));
120 frc = FrameRateChange (dc->video_frame_rate(), _film->video_frame_rate());
124 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (i);
126 /* See if we can re-use an old ImageDecoder */
127 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
128 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
129 if (imd && imd->content() == ic) {
135 decoder.reset (new ImageDecoder (ic));
138 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
142 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (i);
144 decoder.reset (new SndfileDecoder (sc, _fast));
146 /* Work out a FrameRateChange for the best overlap video for this content */
147 DCPTime best_overlap_t;
148 shared_ptr<VideoContent> best_overlap;
149 BOOST_FOREACH (shared_ptr<Content> j, _playlist->content ()) {
150 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (j);
155 DCPTime const overlap = min (vc->end(), i->end()) - max (vc->position(), i->position());
156 if (overlap > best_overlap_t) {
158 best_overlap_t = overlap;
163 frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
165 /* No video overlap; e.g. if the DCP is just audio */
166 frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
170 /* It's questionable whether subtitle content should have a video frame rate; perhaps
171 it should be assumed that any subtitle content has been prepared at the same rate
172 as simultaneous video content (like we do with audio).
176 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (i);
178 decoder.reset (new SubRipDecoder (rc));
179 frc = FrameRateChange (rc->subtitle_video_frame_rate(), _film->video_frame_rate());
182 /* DCPSubtitleContent */
183 shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (i);
185 decoder.reset (new DCPSubtitleDecoder (dsc));
186 frc = FrameRateChange (dsc->subtitle_video_frame_rate(), _film->video_frame_rate());
189 shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> (decoder);
190 if (vd && _ignore_video) {
191 vd->set_ignore_video ();
194 shared_ptr<AudioDecoder> ad = dynamic_pointer_cast<AudioDecoder> (decoder);
195 if (ad && _ignore_audio) {
196 ad->set_ignore_audio ();
199 _pieces.push_back (shared_ptr<Piece> (new Piece (i, decoder, frc.get ())));
202 _have_valid_pieces = true;
206 Player::playlist_content_changed (weak_ptr<Content> w, int property, bool frequent)
208 shared_ptr<Content> c = w.lock ();
214 property == ContentProperty::POSITION ||
215 property == ContentProperty::LENGTH ||
216 property == ContentProperty::TRIM_START ||
217 property == ContentProperty::TRIM_END ||
218 property == ContentProperty::PATH ||
219 property == VideoContentProperty::VIDEO_FRAME_TYPE ||
220 property == DCPContentProperty::CAN_BE_PLAYED
223 _have_valid_pieces = false;
227 property == SubtitleContentProperty::USE_SUBTITLES ||
228 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
229 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
230 property == SubtitleContentProperty::SUBTITLE_X_SCALE ||
231 property == SubtitleContentProperty::SUBTITLE_Y_SCALE ||
232 property == SubtitleContentProperty::FONTS ||
233 property == VideoContentProperty::VIDEO_CROP ||
234 property == VideoContentProperty::VIDEO_SCALE ||
235 property == VideoContentProperty::VIDEO_FRAME_RATE ||
236 property == VideoContentProperty::VIDEO_FADE_IN ||
237 property == VideoContentProperty::VIDEO_FADE_OUT ||
238 property == VideoContentProperty::COLOUR_CONVERSION
246 Player::set_video_container_size (dcp::Size s)
248 _video_container_size = s;
250 _black_image.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
251 _black_image->make_black ();
255 Player::playlist_changed ()
257 _have_valid_pieces = false;
262 Player::film_changed (Film::Property p)
264 /* Here we should notice Film properties that affect our output, and
265 alert listeners that our output now would be different to how it was
266 last time we were run.
269 if (p == Film::CONTAINER) {
271 } else if (p == Film::VIDEO_FRAME_RATE) {
272 /* Pieces contain a FrameRateChange which contains the DCP frame rate,
273 so we need new pieces here.
275 _have_valid_pieces = false;
277 } else if (p == Film::AUDIO_PROCESSOR) {
278 if (_film->audio_processor ()) {
279 _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
285 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
287 list<PositionImage> all;
289 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
294 /* We will scale the subtitle up to fit _video_container_size */
295 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
297 /* Then we need a corrective translation, consisting of two parts:
299 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
300 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
302 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
303 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
304 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
306 * Combining these two translations gives these expressions.
313 dcp::YUV_TO_RGB_REC601,
314 i->image->pixel_format (),
318 lrint (_video_container_size.width * i->rectangle.x),
319 lrint (_video_container_size.height * i->rectangle.y)
328 shared_ptr<PlayerVideo>
329 Player::black_player_video_frame (DCPTime time) const
331 return shared_ptr<PlayerVideo> (
333 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
337 _video_container_size,
338 _video_container_size,
341 PresetColourConversion::all().front().conversion
346 /** @return All PlayerVideos at the given time. There may be none if the content
347 * at `time' is a DCP which we are passing through (i.e. referring to by reference)
348 * or 2 if we have 3D.
350 list<shared_ptr<PlayerVideo> >
351 Player::get_video (DCPTime time, bool accurate)
353 if (!_have_valid_pieces) {
357 /* Find subtitles for possible burn-in */
359 PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false, true);
361 list<PositionImage> sub_images;
363 /* Image subtitles */
364 list<PositionImage> c = transform_image_subtitles (ps.image);
365 copy (c.begin(), c.end(), back_inserter (sub_images));
367 /* Text subtitles (rendered to an image) */
368 if (!ps.text.empty ()) {
369 list<PositionImage> s = render_subtitles (ps.text, ps.fonts, _video_container_size);
370 copy (s.begin (), s.end (), back_inserter (sub_images));
373 optional<PositionImage> subtitles;
374 if (!sub_images.empty ()) {
375 subtitles = merge (sub_images);
378 /* Find pieces containing video which is happening now */
380 list<shared_ptr<Piece> > ov = overlaps<VideoContent> (
382 time + DCPTime::from_frames (1, _film->video_frame_rate ()) - DCPTime::delta()
385 list<shared_ptr<PlayerVideo> > pvf;
388 /* No video content at this time */
389 pvf.push_back (black_player_video_frame (time));
391 /* Some video content at this time */
392 shared_ptr<Piece> last = *(ov.rbegin ());
393 VideoFrameType const last_type = dynamic_pointer_cast<VideoContent> (last->content)->video_frame_type ();
395 /* Get video from appropriate piece(s) */
396 BOOST_FOREACH (shared_ptr<Piece> piece, ov) {
398 shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
399 DCPOMATIC_ASSERT (decoder);
400 shared_ptr<VideoContent> video_content = dynamic_pointer_cast<VideoContent> (piece->content);
401 DCPOMATIC_ASSERT (video_content);
403 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (video_content);
404 if (dcp_content && dcp_content->reference_video () && !_play_referenced) {
409 /* always use the last video */
411 /* with a corresponding L/R eye if appropriate */
412 (last_type == VIDEO_FRAME_TYPE_3D_LEFT && video_content->video_frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) ||
413 (last_type == VIDEO_FRAME_TYPE_3D_RIGHT && video_content->video_frame_type() == VIDEO_FRAME_TYPE_3D_LEFT);
416 /* We want to use this piece */
417 list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate);
418 if (content_video.empty ()) {
419 pvf.push_back (black_player_video_frame (time));
421 dcp::Size image_size = video_content->scale().size (video_content, _video_container_size, _film->frame_size ());
423 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
425 shared_ptr<PlayerVideo> (
428 content_video_to_dcp (piece, i->frame),
429 video_content->crop (),
430 video_content->fade (i->frame),
432 _video_container_size,
435 video_content->colour_conversion ()
442 /* Discard unused video */
443 decoder->get_video (dcp_to_content_video (piece, time), accurate);
449 BOOST_FOREACH (shared_ptr<PlayerVideo> p, pvf) {
450 p->set_subtitle (subtitles.get ());
457 /** @return Audio data or 0 if the only audio data here is referenced DCP data */
458 shared_ptr<AudioBuffers>
459 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
461 if (!_have_valid_pieces) {
465 Frame const length_frames = length.frames_round (_film->audio_frame_rate ());
467 shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
468 audio->make_silent ();
470 list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time, time + length);
475 bool all_referenced = true;
476 BOOST_FOREACH (shared_ptr<Piece> i, ov) {
477 shared_ptr<AudioContent> audio_content = dynamic_pointer_cast<AudioContent> (i->content);
478 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (i->content);
479 if (audio_content && (!dcp_content || !dcp_content->reference_audio ())) {
480 /* There is audio content which is not from a DCP or not set to be referenced */
481 all_referenced = false;
485 if (all_referenced && !_play_referenced) {
486 return shared_ptr<AudioBuffers> ();
489 BOOST_FOREACH (shared_ptr<Piece> i, ov) {
491 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (i->content);
492 DCPOMATIC_ASSERT (content);
493 shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> (i->decoder);
494 DCPOMATIC_ASSERT (decoder);
496 /* The time that we should request from the content */
497 DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
498 Frame request_frames = length_frames;
500 if (request < DCPTime ()) {
501 /* We went off the start of the content, so we will need to offset
502 the stuff we get back.
505 request_frames += request.frames_round (_film->audio_frame_rate ());
506 if (request_frames < 0) {
509 request = DCPTime ();
512 Frame const content_frame = dcp_to_resampled_audio (i, request);
514 BOOST_FOREACH (AudioStreamPtr j, content->audio_streams ()) {
516 if (j->channels() == 0) {
517 /* Some content (e.g. DCPs) can have streams with no channels */
521 /* Audio from this piece's decoder stream (which might be more or less than what we asked for) */
522 ContentAudio all = decoder->get_audio (j, content_frame, request_frames, accurate);
525 if (content->audio_gain() != 0) {
526 shared_ptr<AudioBuffers> gain (new AudioBuffers (all.audio));
527 gain->apply_gain (content->audio_gain ());
532 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all.audio->frames()));
533 dcp_mapped->make_silent ();
534 AudioMapping map = j->mapping ();
535 for (int i = 0; i < map.input_channels(); ++i) {
536 for (int j = 0; j < _film->audio_channels(); ++j) {
537 if (map.get (i, j) > 0) {
538 dcp_mapped->accumulate_channel (
548 if (_audio_processor) {
549 dcp_mapped = _audio_processor->run (dcp_mapped, _film->audio_channels ());
552 all.audio = dcp_mapped;
554 audio->accumulate_frames (
556 content_frame - all.frame,
557 offset.frames_round (_film->audio_frame_rate()),
558 min (Frame (all.audio->frames()), request_frames)
567 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
569 shared_ptr<const VideoContent> vc = dynamic_pointer_cast<const VideoContent> (piece->content);
570 DCPTime s = t - piece->content->position ();
571 s = min (piece->content->length_after_trim(), s);
572 s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc));
574 /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
575 then convert that ContentTime to frames at the content's rate. However this fails for
576 situations like content at 29.9978733fps, DCP at 30fps. The accuracy of the Time type is not
577 enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
579 Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
581 return s.frames_floor (piece->frc.dcp) / piece->frc.factor ();
585 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
587 shared_ptr<const VideoContent> vc = dynamic_pointer_cast<const VideoContent> (piece->content);
588 /* See comment in dcp_to_content_video */
589 DCPTime const d = DCPTime::from_frames (f * piece->frc.factor(), piece->frc.dcp) - DCPTime (piece->content->trim_start (), piece->frc);
590 return max (DCPTime (), d + piece->content->position ());
594 Player::dcp_to_resampled_audio (shared_ptr<const Piece> piece, DCPTime t) const
596 DCPTime s = t - piece->content->position ();
597 s = min (piece->content->length_after_trim(), s);
598 /* See notes in dcp_to_content_video */
599 return max (DCPTime (), DCPTime (piece->content->trim_start (), piece->frc) + s).frames_floor (_film->audio_frame_rate ());
603 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
605 DCPTime s = t - piece->content->position ();
606 s = min (piece->content->length_after_trim(), s);
607 return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start());
611 Player::content_subtitle_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
613 return max (DCPTime (), DCPTime (t - piece->content->trim_start(), piece->frc) + piece->content->position());
616 /** @param burnt true to return only subtitles to be burnt, false to return only
617 * subtitles that should not be burnt. This parameter will be ignored if
618 * _always_burn_subtitles is true; in this case, all subtitles will be returned.
621 Player::get_subtitles (DCPTime time, DCPTime length, bool starting, bool burnt)
623 list<shared_ptr<Piece> > subs = overlaps<SubtitleContent> (time, time + length);
625 PlayerSubtitles ps (time, length);
627 for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
628 shared_ptr<SubtitleContent> subtitle_content = dynamic_pointer_cast<SubtitleContent> ((*j)->content);
629 if (!subtitle_content->use_subtitles () || (!_always_burn_subtitles && (burnt != subtitle_content->burn_subtitles ()))) {
633 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (subtitle_content);
634 if (dcp_content && dcp_content->reference_subtitle () && !_play_referenced) {
638 shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*j)->decoder);
639 ContentTime const from = dcp_to_content_subtitle (*j, time);
640 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
641 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
643 list<ContentImageSubtitle> image = subtitle_decoder->get_image_subtitles (ContentTimePeriod (from, to), starting);
644 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
646 /* Apply content's subtitle offsets */
647 i->sub.rectangle.x += subtitle_content->subtitle_x_offset ();
648 i->sub.rectangle.y += subtitle_content->subtitle_y_offset ();
650 /* Apply content's subtitle scale */
651 i->sub.rectangle.width *= subtitle_content->subtitle_x_scale ();
652 i->sub.rectangle.height *= subtitle_content->subtitle_y_scale ();
654 /* Apply a corrective translation to keep the subtitle centred after that scale */
655 i->sub.rectangle.x -= i->sub.rectangle.width * (subtitle_content->subtitle_x_scale() - 1);
656 i->sub.rectangle.y -= i->sub.rectangle.height * (subtitle_content->subtitle_y_scale() - 1);
658 ps.image.push_back (i->sub);
661 list<ContentTextSubtitle> text = subtitle_decoder->get_text_subtitles (ContentTimePeriod (from, to), starting);
662 BOOST_FOREACH (ContentTextSubtitle& ts, text) {
663 BOOST_FOREACH (dcp::SubtitleString s, ts.subs) {
664 s.set_h_position (s.h_position() + subtitle_content->subtitle_x_offset ());
665 s.set_v_position (s.v_position() + subtitle_content->subtitle_y_offset ());
666 float const xs = subtitle_content->subtitle_x_scale();
667 float const ys = subtitle_content->subtitle_y_scale();
668 float const average = s.size() * (xs + ys) / 2;
669 s.set_size (average);
670 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
671 s.set_aspect_adjust (xs / ys);
673 s.set_in (dcp::Time(content_subtitle_to_dcp (*j, ts.period().from).seconds()));
674 s.set_out (dcp::Time(content_subtitle_to_dcp (*j, ts.period().to).seconds()));
675 ps.text.push_back (s);
676 ps.add_fonts (subtitle_content->fonts ());
684 list<shared_ptr<Font> >
685 Player::get_subtitle_fonts ()
687 if (!_have_valid_pieces) {
691 list<shared_ptr<Font> > fonts;
692 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
693 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (p->content);
695 /* XXX: things may go wrong if there are duplicate font IDs
696 with different font files.
698 list<shared_ptr<Font> > f = sc->fonts ();
699 copy (f.begin(), f.end(), back_inserter (fonts));
706 /** Set this player never to produce any video data */
708 Player::set_ignore_video ()
710 _ignore_video = true;
713 /** Set this player never to produce any audio data */
715 Player::set_ignore_audio ()
717 _ignore_audio = true;
720 /** Set whether or not this player should always burn text subtitles into the image,
721 * regardless of the content settings.
722 * @param burn true to always burn subtitles, false to obey content settings.
725 Player::set_always_burn_subtitles (bool burn)
727 _always_burn_subtitles = burn;
734 _have_valid_pieces = false;
738 Player::set_play_referenced ()
740 _play_referenced = true;
741 _have_valid_pieces = false;
744 list<shared_ptr<dcp::ReelAsset> >
745 Player::get_reel_assets ()
747 list<shared_ptr<dcp::ReelAsset> > a;
749 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
750 shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i);
754 /* XXX: hack hack hack */
755 DCPDecoder decoder (j, false);
756 if (j->reference_video ()) {
757 a.push_back (decoder.reels().front()->main_picture ());
759 if (j->reference_audio ()) {
760 a.push_back (decoder.reels().front()->main_sound ());
762 if (j->reference_subtitle ()) {
763 a.push_back (decoder.reels().front()->main_subtitle ());