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)
78 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
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::playlist_changed ()
231 _have_valid_pieces = false;
236 Player::set_video_container_size (dcp::Size s)
238 _video_container_size = s;
240 _black_image.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
241 _black_image->make_black ();
245 Player::film_changed (Film::Property p)
247 /* Here we should notice Film properties that affect our output, and
248 alert listeners that our output now would be different to how it was
249 last time we were run.
252 if (p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
254 } else if (p == Film::AUDIO_PROCESSOR) {
255 if (_film->audio_processor ()) {
256 _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
262 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
264 list<PositionImage> all;
266 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
271 /* We will scale the subtitle up to fit _video_container_size */
272 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
274 /* Then we need a corrective translation, consisting of two parts:
276 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
277 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
279 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
280 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
281 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
283 * Combining these two translations gives these expressions.
290 dcp::YUV_TO_RGB_REC601,
291 i->image->pixel_format (),
295 rint (_video_container_size.width * i->rectangle.x),
296 rint (_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
323 /** @return All PlayerVideos at the given time (there may be two frames for 3D) */
324 list<shared_ptr<PlayerVideo> >
325 Player::get_video (DCPTime time, bool accurate)
327 if (!_have_valid_pieces) {
331 list<shared_ptr<Piece> > ov = overlaps<VideoContent> (
333 time + DCPTime::from_frames (1, _film->video_frame_rate ())
336 list<shared_ptr<PlayerVideo> > pvf;
339 /* No video content at this time */
340 pvf.push_back (black_player_video_frame (time));
342 /* Create a PlayerVideo from the content's video at this time */
344 shared_ptr<Piece> piece = ov.back ();
345 shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
346 DCPOMATIC_ASSERT (decoder);
347 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
348 DCPOMATIC_ASSERT (content);
350 list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate);
351 if (content_video.empty ()) {
352 pvf.push_back (black_player_video_frame (time));
356 dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size ());
358 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
360 shared_ptr<PlayerVideo> (
363 content_video_to_dcp (piece, i->frame),
365 content->fade (i->frame),
367 _video_container_size,
370 content->colour_conversion ()
377 /* Add subtitles (for possible burn-in) to whatever PlayerVideos we got */
379 PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false);
381 list<PositionImage> sub_images;
383 /* Image subtitles */
384 list<PositionImage> c = transform_image_subtitles (ps.image);
385 copy (c.begin(), c.end(), back_inserter (sub_images));
387 /* Text subtitles (rendered to an image) */
388 if (!ps.text.empty ()) {
389 list<PositionImage> s = render_subtitles (ps.text, _video_container_size);
390 copy (s.begin (), s.end (), back_inserter (sub_images));
393 if (!sub_images.empty ()) {
394 for (list<shared_ptr<PlayerVideo> >::const_iterator i = pvf.begin(); i != pvf.end(); ++i) {
395 (*i)->set_subtitle (merge (sub_images));
402 shared_ptr<AudioBuffers>
403 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
405 if (!_have_valid_pieces) {
409 Frame const length_frames = length.frames (_film->audio_frame_rate ());
411 shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
412 audio->make_silent ();
414 list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time, time + length);
419 for (list<shared_ptr<Piece> >::iterator i = ov.begin(); i != ov.end(); ++i) {
421 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> ((*i)->content);
422 DCPOMATIC_ASSERT (content);
423 shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
424 DCPOMATIC_ASSERT (decoder);
426 /* The time that we should request from the content */
427 DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
428 Frame request_frames = length_frames;
430 if (request < DCPTime ()) {
431 /* We went off the start of the content, so we will need to offset
432 the stuff we get back.
435 request_frames += request.frames (_film->audio_frame_rate ());
436 if (request_frames < 0) {
439 request = DCPTime ();
442 Frame const content_frame = dcp_to_content_audio (*i, request);
444 BOOST_FOREACH (AudioStreamPtr j, content->audio_streams ()) {
446 /* Audio from this piece's decoder stream (which might be more or less than what we asked for) */
447 ContentAudio all = decoder->get_audio (j, content_frame, request_frames, accurate);
450 if (content->audio_gain() != 0) {
451 shared_ptr<AudioBuffers> gain (new AudioBuffers (all.audio));
452 gain->apply_gain (content->audio_gain ());
457 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all.audio->frames()));
458 dcp_mapped->make_silent ();
459 AudioMapping map = j->mapping ();
460 for (int i = 0; i < map.input_channels(); ++i) {
461 for (int j = 0; j < _film->audio_channels(); ++j) {
462 if (map.get (i, j) > 0) {
463 dcp_mapped->accumulate_channel (
473 if (_audio_processor) {
474 dcp_mapped = _audio_processor->run (dcp_mapped);
477 all.audio = dcp_mapped;
479 audio->accumulate_frames (
481 content_frame - all.frame,
482 offset.frames (_film->audio_frame_rate()),
483 min (Frame (all.audio->frames()), request_frames)
492 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
494 /* s is the offset of t from the start position of this content */
495 DCPTime s = t - piece->content->position ();
496 s = DCPTime (max (DCPTime::Type (0), s.get ()));
497 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
499 /* Convert this to the content frame */
500 return DCPTime (s + piece->content->trim_start()).frames (_film->video_frame_rate()) / piece->frc.factor ();
504 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
506 DCPTime t = DCPTime::from_frames (f * piece->frc.factor (), _film->video_frame_rate()) - piece->content->trim_start () + piece->content->position ();
507 if (t < DCPTime ()) {
515 Player::dcp_to_content_audio (shared_ptr<const Piece> piece, DCPTime t) const
517 /* s is the offset of t from the start position of this content */
518 DCPTime s = t - piece->content->position ();
519 s = DCPTime (max (DCPTime::Type (0), s.get ()));
520 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
522 /* Convert this to the content frame */
523 return DCPTime (s + piece->content->trim_start()).frames (_film->audio_frame_rate());
527 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
529 /* s is the offset of t from the start position of this content */
530 DCPTime s = t - piece->content->position ();
531 s = DCPTime (max (DCPTime::Type (0), s.get ()));
532 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
534 return ContentTime (s + piece->content->trim_start(), piece->frc);
538 PlayerStatistics::dump (shared_ptr<Log> log) const
540 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat), Log::TYPE_GENERAL);
541 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence.seconds()), Log::TYPE_GENERAL);
544 PlayerStatistics const &
545 Player::statistics () const
551 Player::get_subtitles (DCPTime time, DCPTime length, bool starting)
553 list<shared_ptr<Piece> > subs = overlaps<SubtitleContent> (time, time + length);
555 PlayerSubtitles ps (time, length);
557 for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
558 shared_ptr<SubtitleContent> subtitle_content = dynamic_pointer_cast<SubtitleContent> ((*j)->content);
559 if (!subtitle_content->use_subtitles ()) {
563 shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*j)->decoder);
564 ContentTime const from = dcp_to_content_subtitle (*j, time);
565 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
566 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
568 list<ContentImageSubtitle> image = subtitle_decoder->get_image_subtitles (ContentTimePeriod (from, to), starting);
569 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
571 /* Apply content's subtitle offsets */
572 i->sub.rectangle.x += subtitle_content->subtitle_x_offset ();
573 i->sub.rectangle.y += subtitle_content->subtitle_y_offset ();
575 /* Apply content's subtitle scale */
576 i->sub.rectangle.width *= subtitle_content->subtitle_x_scale ();
577 i->sub.rectangle.height *= subtitle_content->subtitle_y_scale ();
579 /* Apply a corrective translation to keep the subtitle centred after that scale */
580 i->sub.rectangle.x -= i->sub.rectangle.width * (subtitle_content->subtitle_x_scale() - 1);
581 i->sub.rectangle.y -= i->sub.rectangle.height * (subtitle_content->subtitle_y_scale() - 1);
583 ps.image.push_back (i->sub);
586 list<ContentTextSubtitle> text = subtitle_decoder->get_text_subtitles (ContentTimePeriod (from, to), starting);
587 BOOST_FOREACH (ContentTextSubtitle& ts, text) {
588 BOOST_FOREACH (dcp::SubtitleString& s, ts.subs) {
589 s.set_v_position (s.v_position() + subtitle_content->subtitle_y_offset ());
590 float const xs = subtitle_content->subtitle_x_scale();
591 float const ys = subtitle_content->subtitle_y_scale();
592 float const average = s.size() * (xs + ys) / 2;
593 s.set_size (average);
594 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
595 s.set_aspect_adjust (xs / ys);
597 ps.text.push_back (s);
605 list<shared_ptr<Font> >
606 Player::get_subtitle_fonts ()
608 if (!_have_valid_pieces) {
612 list<shared_ptr<Font> > fonts;
613 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
614 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (p->content);
616 /* XXX: things may go wrong if there are duplicate font IDs
617 with different font files.
619 list<shared_ptr<Font> > f = sc->fonts ();
620 copy (f.begin(), f.end(), back_inserter (fonts));
627 /** Set this player never to produce any video data */
629 Player::set_ignore_video ()
631 _ignore_video = true;