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_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
80 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
81 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
82 set_video_container_size (_film->frame_size ());
84 film_changed (Film::AUDIO_PROCESSOR);
88 Player::setup_pieces ()
90 list<shared_ptr<Piece> > old_pieces = _pieces;
93 ContentList content = _playlist->content ();
95 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
97 if (!(*i)->paths_valid ()) {
101 shared_ptr<Decoder> decoder;
102 optional<FrameRateChange> frc;
104 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
105 DCPTime best_overlap_t;
106 shared_ptr<VideoContent> best_overlap;
107 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
108 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
113 DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
114 if (overlap > best_overlap_t) {
116 best_overlap_t = overlap;
120 optional<FrameRateChange> best_overlap_frc;
122 best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
124 /* No video overlap; e.g. if the DCP is just audio */
125 best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
129 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
131 decoder.reset (new FFmpegDecoder (fc, _film->log()));
132 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
135 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (*i);
137 decoder.reset (new DCPDecoder (dc));
138 frc = FrameRateChange (dc->video_frame_rate(), _film->video_frame_rate());
142 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
144 /* See if we can re-use an old ImageDecoder */
145 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
146 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
147 if (imd && imd->content() == ic) {
153 decoder.reset (new ImageDecoder (ic));
156 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
160 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
162 decoder.reset (new SndfileDecoder (sc));
163 frc = best_overlap_frc;
167 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
169 decoder.reset (new SubRipDecoder (rc));
170 frc = best_overlap_frc;
173 /* DCPSubtitleContent */
174 shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (*i);
176 decoder.reset (new DCPSubtitleDecoder (dsc));
177 frc = best_overlap_frc;
180 shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> (decoder);
181 if (vd && _ignore_video) {
182 vd->set_ignore_video ();
185 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
188 _have_valid_pieces = true;
192 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
194 shared_ptr<Content> c = w.lock ();
200 property == ContentProperty::POSITION ||
201 property == ContentProperty::LENGTH ||
202 property == ContentProperty::TRIM_START ||
203 property == ContentProperty::TRIM_END ||
204 property == ContentProperty::PATH ||
205 property == VideoContentProperty::VIDEO_FRAME_TYPE ||
206 property == DCPContentProperty::CAN_BE_PLAYED
209 _have_valid_pieces = false;
213 property == SubtitleContentProperty::USE_SUBTITLES ||
214 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
215 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
216 property == SubtitleContentProperty::SUBTITLE_X_SCALE ||
217 property == SubtitleContentProperty::SUBTITLE_Y_SCALE ||
218 property == VideoContentProperty::VIDEO_CROP ||
219 property == VideoContentProperty::VIDEO_SCALE ||
220 property == VideoContentProperty::VIDEO_FRAME_RATE ||
221 property == VideoContentProperty::VIDEO_FADE_IN ||
222 property == VideoContentProperty::VIDEO_FADE_OUT
230 Player::playlist_changed ()
232 _have_valid_pieces = false;
237 Player::set_video_container_size (dcp::Size s)
239 _video_container_size = s;
241 _black_image.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
242 _black_image->make_black ();
246 Player::film_changed (Film::Property p)
248 /* Here we should notice Film properties that affect our output, and
249 alert listeners that our output now would be different to how it was
250 last time we were run.
253 if (p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
255 } else if (p == Film::AUDIO_PROCESSOR) {
256 if (_film->audio_processor ()) {
257 _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
263 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
265 list<PositionImage> all;
267 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
272 /* We will scale the subtitle up to fit _video_container_size */
273 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
275 /* Then we need a corrective translation, consisting of two parts:
277 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
278 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
280 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
281 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
282 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
284 * Combining these two translations gives these expressions.
291 dcp::YUV_TO_RGB_REC601,
292 i->image->pixel_format (),
296 rint (_video_container_size.width * i->rectangle.x),
297 rint (_video_container_size.height * i->rectangle.y)
306 shared_ptr<PlayerVideo>
307 Player::black_player_video_frame (DCPTime time) const
309 return shared_ptr<PlayerVideo> (
311 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
315 _video_container_size,
316 _video_container_size,
319 PresetColourConversion::all().front().conversion
324 /** @return All PlayerVideos at the given time (there may be two frames for 3D) */
325 list<shared_ptr<PlayerVideo> >
326 Player::get_video (DCPTime time, bool accurate)
328 if (!_have_valid_pieces) {
332 list<shared_ptr<Piece> > ov = overlaps<VideoContent> (
334 time + DCPTime::from_frames (1, _film->video_frame_rate ())
337 list<shared_ptr<PlayerVideo> > pvf;
340 /* No video content at this time */
341 pvf.push_back (black_player_video_frame (time));
343 /* Create a PlayerVideo from the content's video at this time */
345 shared_ptr<Piece> piece = ov.back ();
346 shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
347 DCPOMATIC_ASSERT (decoder);
348 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
349 DCPOMATIC_ASSERT (content);
351 list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate);
352 if (content_video.empty ()) {
353 pvf.push_back (black_player_video_frame (time));
357 dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size ());
359 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
361 shared_ptr<PlayerVideo> (
364 content_video_to_dcp (piece, i->frame),
366 content->fade (i->frame),
368 _video_container_size,
371 content->colour_conversion ()
378 /* Add subtitles (for possible burn-in) to whatever PlayerVideos we got */
380 PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false);
382 list<PositionImage> sub_images;
384 /* Image subtitles */
385 list<PositionImage> c = transform_image_subtitles (ps.image);
386 copy (c.begin(), c.end(), back_inserter (sub_images));
388 /* Text subtitles (rendered to an image) */
389 if (_burn_subtitles && !ps.text.empty ()) {
390 list<PositionImage> s = render_subtitles (ps.text, _video_container_size);
391 copy (s.begin (), s.end (), back_inserter (sub_images));
394 if (!sub_images.empty ()) {
395 for (list<shared_ptr<PlayerVideo> >::const_iterator i = pvf.begin(); i != pvf.end(); ++i) {
396 (*i)->set_subtitle (merge (sub_images));
403 shared_ptr<AudioBuffers>
404 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
406 if (!_have_valid_pieces) {
410 Frame const length_frames = length.frames (_film->audio_frame_rate ());
412 shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
413 audio->make_silent ();
415 list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time, time + length);
420 for (list<shared_ptr<Piece> >::iterator i = ov.begin(); i != ov.end(); ++i) {
422 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> ((*i)->content);
423 DCPOMATIC_ASSERT (content);
424 shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
425 DCPOMATIC_ASSERT (decoder);
427 /* The time that we should request from the content */
428 DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
429 Frame request_frames = length_frames;
431 if (request < DCPTime ()) {
432 /* We went off the start of the content, so we will need to offset
433 the stuff we get back.
436 request_frames += request.frames (_film->audio_frame_rate ());
437 if (request_frames < 0) {
440 request = DCPTime ();
443 Frame const content_frame = dcp_to_content_audio (*i, request);
445 BOOST_FOREACH (AudioStreamPtr j, content->audio_streams ()) {
447 /* Audio from this piece's decoder stream (which might be more or less than what we asked for) */
448 ContentAudio all = decoder->get_audio (j, content_frame, request_frames, accurate);
451 if (content->audio_gain() != 0) {
452 shared_ptr<AudioBuffers> gain (new AudioBuffers (all.audio));
453 gain->apply_gain (content->audio_gain ());
458 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all.audio->frames()));
459 dcp_mapped->make_silent ();
460 AudioMapping map = j->mapping ();
461 for (int i = 0; i < map.input_channels(); ++i) {
462 for (int j = 0; j < _film->audio_channels(); ++j) {
463 if (map.get (i, j) > 0) {
464 dcp_mapped->accumulate_channel (
474 if (_audio_processor) {
475 dcp_mapped = _audio_processor->run (dcp_mapped);
478 all.audio = dcp_mapped;
480 audio->accumulate_frames (
482 content_frame - all.frame,
483 offset.frames (_film->audio_frame_rate()),
484 min (Frame (all.audio->frames()), request_frames)
493 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
495 /* s is the offset of t from the start position of this content */
496 DCPTime s = t - piece->content->position ();
497 s = DCPTime (max (DCPTime::Type (0), s.get ()));
498 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
500 /* Convert this to the content frame */
501 return DCPTime (s + piece->content->trim_start()).frames (_film->video_frame_rate()) / piece->frc.factor ();
505 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
507 DCPTime t = DCPTime::from_frames (f * piece->frc.factor (), _film->video_frame_rate()) - piece->content->trim_start () + piece->content->position ();
508 if (t < DCPTime ()) {
516 Player::dcp_to_content_audio (shared_ptr<const Piece> piece, DCPTime t) const
518 /* s is the offset of t from the start position of this content */
519 DCPTime s = t - piece->content->position ();
520 s = DCPTime (max (DCPTime::Type (0), s.get ()));
521 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
523 /* Convert this to the content frame */
524 return DCPTime (s + piece->content->trim_start()).frames (_film->audio_frame_rate());
528 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
530 /* s is the offset of t from the start position of this content */
531 DCPTime s = t - piece->content->position ();
532 s = DCPTime (max (DCPTime::Type (0), s.get ()));
533 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
535 return ContentTime (s + piece->content->trim_start(), piece->frc);
539 PlayerStatistics::dump (shared_ptr<Log> log) const
541 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat), Log::TYPE_GENERAL);
542 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence.seconds()), Log::TYPE_GENERAL);
545 PlayerStatistics const &
546 Player::statistics () const
552 Player::get_subtitles (DCPTime time, DCPTime length, bool starting)
554 list<shared_ptr<Piece> > subs = overlaps<SubtitleContent> (time, time + length);
556 PlayerSubtitles ps (time, length);
558 for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
559 shared_ptr<SubtitleContent> subtitle_content = dynamic_pointer_cast<SubtitleContent> ((*j)->content);
560 if (!subtitle_content->use_subtitles ()) {
564 shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*j)->decoder);
565 ContentTime const from = dcp_to_content_subtitle (*j, time);
566 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
567 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
569 list<ContentImageSubtitle> image = subtitle_decoder->get_image_subtitles (ContentTimePeriod (from, to), starting);
570 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
572 /* Apply content's subtitle offsets */
573 i->sub.rectangle.x += subtitle_content->subtitle_x_offset ();
574 i->sub.rectangle.y += subtitle_content->subtitle_y_offset ();
576 /* Apply content's subtitle scale */
577 i->sub.rectangle.width *= subtitle_content->subtitle_x_scale ();
578 i->sub.rectangle.height *= subtitle_content->subtitle_y_scale ();
580 /* Apply a corrective translation to keep the subtitle centred after that scale */
581 i->sub.rectangle.x -= i->sub.rectangle.width * (subtitle_content->subtitle_x_scale() - 1);
582 i->sub.rectangle.y -= i->sub.rectangle.height * (subtitle_content->subtitle_y_scale() - 1);
584 ps.image.push_back (i->sub);
587 list<ContentTextSubtitle> text = subtitle_decoder->get_text_subtitles (ContentTimePeriod (from, to), starting);
588 BOOST_FOREACH (ContentTextSubtitle& ts, text) {
589 BOOST_FOREACH (dcp::SubtitleString& s, ts.subs) {
590 s.set_h_position (s.h_position() + subtitle_content->subtitle_x_offset ());
591 s.set_v_position (s.v_position() + subtitle_content->subtitle_y_offset ());
592 float const xs = subtitle_content->subtitle_x_scale();
593 float const ys = subtitle_content->subtitle_y_scale();
594 float const average = s.size() * (xs + ys) / 2;
595 s.set_size (average);
596 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
597 s.set_aspect_adjust (xs / ys);
599 ps.text.push_back (s);
607 list<shared_ptr<Font> >
608 Player::get_subtitle_fonts ()
610 if (!_have_valid_pieces) {
614 list<shared_ptr<Font> > fonts;
615 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
616 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (p->content);
618 /* XXX: things may go wrong if there are duplicate font IDs
619 with different font files.
621 list<shared_ptr<Font> > f = sc->fonts ();
622 copy (f.begin(), f.end(), back_inserter (fonts));
629 /** Set this player never to produce any video data */
631 Player::set_ignore_video ()
633 _ignore_video = true;
636 /** Set whether or not this player should burn text subtitles into the image.
637 * @param burn true to burn subtitles, false to not.
640 Player::set_burn_subtitles (bool burn)
642 _burn_subtitles = burn;