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"
36 #include "raw_image_proxy.h"
39 #include "render_subtitles.h"
41 #include "content_video.h"
42 #include "player_video.h"
43 #include "frame_rate_change.h"
44 #include "dcp_content.h"
45 #include "dcp_decoder.h"
46 #include "dcp_subtitle_content.h"
47 #include "dcp_subtitle_decoder.h"
48 #include "audio_processor.h"
49 #include <boost/foreach.hpp>
55 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
67 using boost::shared_ptr;
68 using boost::weak_ptr;
69 using boost::dynamic_pointer_cast;
70 using boost::optional;
72 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
75 , _have_valid_pieces (false)
76 , _ignore_video (false)
77 , _burn_subtitles (f->burn_subtitles ())
79 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
80 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
81 set_video_container_size (_film->frame_size ());
83 film_changed (Film::AUDIO_PROCESSOR);
87 Player::setup_pieces ()
89 list<shared_ptr<Piece> > old_pieces = _pieces;
92 ContentList content = _playlist->content ();
94 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
96 if (!(*i)->paths_valid ()) {
100 shared_ptr<Decoder> decoder;
101 optional<FrameRateChange> frc;
103 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
104 DCPTime best_overlap_t;
105 shared_ptr<VideoContent> best_overlap;
106 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
107 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
112 DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
113 if (overlap > best_overlap_t) {
115 best_overlap_t = overlap;
119 optional<FrameRateChange> best_overlap_frc;
121 best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
123 /* No video overlap; e.g. if the DCP is just audio */
124 best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
128 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
130 decoder.reset (new FFmpegDecoder (fc, _film->log()));
131 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
134 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (*i);
136 decoder.reset (new DCPDecoder (dc));
137 frc = FrameRateChange (dc->video_frame_rate(), _film->video_frame_rate());
141 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
143 /* See if we can re-use an old ImageDecoder */
144 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
145 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
146 if (imd && imd->content() == ic) {
152 decoder.reset (new ImageDecoder (ic));
155 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
159 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
161 decoder.reset (new SndfileDecoder (sc));
162 frc = best_overlap_frc;
166 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
168 decoder.reset (new SubRipDecoder (rc));
169 frc = best_overlap_frc;
172 /* DCPSubtitleContent */
173 shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (*i);
175 decoder.reset (new DCPSubtitleDecoder (dsc));
176 frc = best_overlap_frc;
179 shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> (decoder);
180 if (vd && _ignore_video) {
181 vd->set_ignore_video ();
184 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
187 _have_valid_pieces = true;
191 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
193 shared_ptr<Content> c = w.lock ();
199 property == ContentProperty::POSITION ||
200 property == ContentProperty::LENGTH ||
201 property == ContentProperty::TRIM_START ||
202 property == ContentProperty::TRIM_END ||
203 property == ContentProperty::PATH ||
204 property == VideoContentProperty::VIDEO_FRAME_TYPE ||
205 property == DCPContentProperty::CAN_BE_PLAYED
208 _have_valid_pieces = false;
212 property == SubtitleContentProperty::USE_SUBTITLES ||
213 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
214 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
215 property == SubtitleContentProperty::SUBTITLE_X_SCALE ||
216 property == SubtitleContentProperty::SUBTITLE_Y_SCALE ||
217 property == VideoContentProperty::VIDEO_CROP ||
218 property == VideoContentProperty::VIDEO_SCALE ||
219 property == VideoContentProperty::VIDEO_FRAME_RATE ||
220 property == VideoContentProperty::VIDEO_FADE_IN ||
221 property == VideoContentProperty::VIDEO_FADE_OUT
229 Player::set_video_container_size (dcp::Size s)
231 _video_container_size = s;
233 _black_image.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
234 _black_image->make_black ();
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::CONTENT) {
246 _have_valid_pieces = false;
248 } else if (p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
250 } else if (p == Film::AUDIO_PROCESSOR) {
251 if (_film->audio_processor ()) {
252 _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
258 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
260 list<PositionImage> all;
262 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
267 /* We will scale the subtitle up to fit _video_container_size */
268 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
270 /* Then we need a corrective translation, consisting of two parts:
272 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
273 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
275 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
276 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
277 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
279 * Combining these two translations gives these expressions.
286 dcp::YUV_TO_RGB_REC601,
287 i->image->pixel_format (),
291 rint (_video_container_size.width * i->rectangle.x),
292 rint (_video_container_size.height * i->rectangle.y)
301 shared_ptr<PlayerVideo>
302 Player::black_player_video_frame (DCPTime time) const
304 return shared_ptr<PlayerVideo> (
306 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
310 _video_container_size,
311 _video_container_size,
314 PresetColourConversion::all().front().conversion
319 /** @return All PlayerVideos at the given time (there may be two frames for 3D) */
320 list<shared_ptr<PlayerVideo> >
321 Player::get_video (DCPTime time, bool accurate)
323 if (!_have_valid_pieces) {
327 list<shared_ptr<Piece> > ov = overlaps<VideoContent> (
329 time + DCPTime::from_frames (1, _film->video_frame_rate ())
332 list<shared_ptr<PlayerVideo> > pvf;
335 /* No video content at this time */
336 pvf.push_back (black_player_video_frame (time));
338 /* Create a PlayerVideo from the content's video at this time */
340 shared_ptr<Piece> piece = ov.back ();
341 shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
342 DCPOMATIC_ASSERT (decoder);
343 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
344 DCPOMATIC_ASSERT (content);
346 list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate);
347 if (content_video.empty ()) {
348 pvf.push_back (black_player_video_frame (time));
352 dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size ());
354 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
356 shared_ptr<PlayerVideo> (
359 content_video_to_dcp (piece, i->frame),
361 content->fade (i->frame),
363 _video_container_size,
366 content->colour_conversion ()
373 /* Add subtitles (for possible burn-in) to whatever PlayerVideos we got */
375 PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false);
377 list<PositionImage> sub_images;
379 /* Image subtitles */
380 list<PositionImage> c = transform_image_subtitles (ps.image);
381 copy (c.begin(), c.end(), back_inserter (sub_images));
383 /* Text subtitles (rendered to an image) */
384 if (_burn_subtitles && !ps.text.empty ()) {
385 list<PositionImage> s = render_subtitles (ps.text, _video_container_size);
386 copy (s.begin (), s.end (), back_inserter (sub_images));
389 if (!sub_images.empty ()) {
390 for (list<shared_ptr<PlayerVideo> >::const_iterator i = pvf.begin(); i != pvf.end(); ++i) {
391 (*i)->set_subtitle (merge (sub_images));
398 shared_ptr<AudioBuffers>
399 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
401 if (!_have_valid_pieces) {
405 Frame const length_frames = length.frames (_film->audio_frame_rate ());
407 shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
408 audio->make_silent ();
410 list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time, time + length);
415 for (list<shared_ptr<Piece> >::iterator i = ov.begin(); i != ov.end(); ++i) {
417 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> ((*i)->content);
418 DCPOMATIC_ASSERT (content);
419 shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
420 DCPOMATIC_ASSERT (decoder);
422 /* The time that we should request from the content */
423 DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
424 Frame request_frames = length_frames;
426 if (request < DCPTime ()) {
427 /* We went off the start of the content, so we will need to offset
428 the stuff we get back.
431 request_frames += request.frames (_film->audio_frame_rate ());
432 if (request_frames < 0) {
435 request = DCPTime ();
438 Frame const content_frame = dcp_to_content_audio (*i, request);
440 BOOST_FOREACH (AudioStreamPtr j, content->audio_streams ()) {
442 /* Audio from this piece's decoder stream (which might be more or less than what we asked for) */
443 ContentAudio all = decoder->get_audio (j, content_frame, request_frames, accurate);
446 if (content->audio_gain() != 0) {
447 shared_ptr<AudioBuffers> gain (new AudioBuffers (all.audio));
448 gain->apply_gain (content->audio_gain ());
453 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all.audio->frames()));
454 dcp_mapped->make_silent ();
455 AudioMapping map = j->mapping ();
456 for (int i = 0; i < map.input_channels(); ++i) {
457 for (int j = 0; j < _film->audio_channels(); ++j) {
458 if (map.get (i, j) > 0) {
459 dcp_mapped->accumulate_channel (
469 if (_audio_processor) {
470 dcp_mapped = _audio_processor->run (dcp_mapped);
473 all.audio = dcp_mapped;
475 audio->accumulate_frames (
477 content_frame - all.frame,
478 offset.frames (_film->audio_frame_rate()),
479 min (Frame (all.audio->frames()), request_frames)
488 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
490 /* s is the offset of t from the start position of this content */
491 DCPTime s = t - piece->content->position ();
492 s = DCPTime (max (DCPTime::Type (0), s.get ()));
493 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
495 /* Convert this to the content frame */
496 return DCPTime (s + piece->content->trim_start()).frames (_film->video_frame_rate()) / piece->frc.factor ();
500 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
502 DCPTime t = DCPTime::from_frames (f * piece->frc.factor (), _film->video_frame_rate()) - piece->content->trim_start () + piece->content->position ();
503 if (t < DCPTime ()) {
511 Player::dcp_to_content_audio (shared_ptr<const Piece> piece, DCPTime t) const
513 /* s is the offset of t from the start position of this content */
514 DCPTime s = t - piece->content->position ();
515 s = DCPTime (max (DCPTime::Type (0), s.get ()));
516 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
518 /* Convert this to the content frame */
519 return DCPTime (s + piece->content->trim_start()).frames (_film->audio_frame_rate());
523 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
525 /* s is the offset of t from the start position of this content */
526 DCPTime s = t - piece->content->position ();
527 s = DCPTime (max (DCPTime::Type (0), s.get ()));
528 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
530 return ContentTime (s + piece->content->trim_start(), piece->frc);
534 PlayerStatistics::dump (shared_ptr<Log> log) const
536 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat), Log::TYPE_GENERAL);
537 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence.seconds()), Log::TYPE_GENERAL);
540 PlayerStatistics const &
541 Player::statistics () const
547 Player::get_subtitles (DCPTime time, DCPTime length, bool starting)
549 list<shared_ptr<Piece> > subs = overlaps<SubtitleContent> (time, time + length);
551 PlayerSubtitles ps (time, length);
553 for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
554 shared_ptr<SubtitleContent> subtitle_content = dynamic_pointer_cast<SubtitleContent> ((*j)->content);
555 if (!subtitle_content->use_subtitles ()) {
559 shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*j)->decoder);
560 ContentTime const from = dcp_to_content_subtitle (*j, time);
561 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
562 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
564 list<ContentImageSubtitle> image = subtitle_decoder->get_image_subtitles (ContentTimePeriod (from, to), starting);
565 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
567 /* Apply content's subtitle offsets */
568 i->sub.rectangle.x += subtitle_content->subtitle_x_offset ();
569 i->sub.rectangle.y += subtitle_content->subtitle_y_offset ();
571 /* Apply content's subtitle scale */
572 i->sub.rectangle.width *= subtitle_content->subtitle_x_scale ();
573 i->sub.rectangle.height *= subtitle_content->subtitle_y_scale ();
575 /* Apply a corrective translation to keep the subtitle centred after that scale */
576 i->sub.rectangle.x -= i->sub.rectangle.width * (subtitle_content->subtitle_x_scale() - 1);
577 i->sub.rectangle.y -= i->sub.rectangle.height * (subtitle_content->subtitle_y_scale() - 1);
579 ps.image.push_back (i->sub);
582 list<ContentTextSubtitle> text = subtitle_decoder->get_text_subtitles (ContentTimePeriod (from, to), starting);
583 BOOST_FOREACH (ContentTextSubtitle& ts, text) {
584 BOOST_FOREACH (dcp::SubtitleString& s, ts.subs) {
585 s.set_h_position (s.h_position() + subtitle_content->subtitle_x_offset ());
586 s.set_v_position (s.v_position() + subtitle_content->subtitle_y_offset ());
587 float const xs = subtitle_content->subtitle_x_scale();
588 float const ys = subtitle_content->subtitle_y_scale();
589 float const average = s.size() * (xs + ys) / 2;
590 s.set_size (average);
591 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
592 s.set_aspect_adjust (xs / ys);
594 ps.text.push_back (s);
602 list<shared_ptr<Font> >
603 Player::get_subtitle_fonts ()
605 if (!_have_valid_pieces) {
609 list<shared_ptr<Font> > fonts;
610 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
611 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (p->content);
613 /* XXX: things may go wrong if there are duplicate font IDs
614 with different font files.
616 list<shared_ptr<Font> > f = sc->fonts ();
617 copy (f.begin(), f.end(), back_inserter (fonts));
624 /** Set this player never to produce any video data */
626 Player::set_ignore_video ()
628 _ignore_video = true;
631 /** Set whether or not this player should burn text subtitles into the image.
632 * @param burn true to burn subtitles, false to not.
635 Player::set_burn_subtitles (bool burn)
637 _burn_subtitles = burn;