2 Copyright (C) 2013-2014 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.
24 #include "ffmpeg_decoder.h"
25 #include "audio_buffers.h"
26 #include "ffmpeg_content.h"
27 #include "image_decoder.h"
28 #include "image_content.h"
29 #include "sndfile_decoder.h"
30 #include "sndfile_content.h"
31 #include "subtitle_content.h"
32 #include "subrip_decoder.h"
33 #include "subrip_content.h"
34 #include "dcp_content.h"
38 #include "raw_image_proxy.h"
42 #include "render_subtitles.h"
44 #include "content_video.h"
45 #include "player_video.h"
46 #include "frame_rate_change.h"
47 #include "dcp_content.h"
48 #include "dcp_decoder.h"
49 #include "dcp_subtitle_content.h"
50 #include "dcp_subtitle_decoder.h"
52 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
63 using boost::shared_ptr;
64 using boost::weak_ptr;
65 using boost::dynamic_pointer_cast;
66 using boost::optional;
68 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
71 , _have_valid_pieces (false)
72 , _approximate_size (false)
74 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
75 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
76 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
77 set_video_container_size (_film->frame_size ());
81 Player::setup_pieces ()
83 list<shared_ptr<Piece> > old_pieces = _pieces;
86 ContentList content = _playlist->content ();
88 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
90 if (!(*i)->paths_valid ()) {
94 shared_ptr<Decoder> decoder;
95 optional<FrameRateChange> frc;
97 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
98 DCPTime best_overlap_t;
99 shared_ptr<VideoContent> best_overlap;
100 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
101 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
106 DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
107 if (overlap > best_overlap_t) {
109 best_overlap_t = overlap;
113 optional<FrameRateChange> best_overlap_frc;
115 best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
117 /* No video overlap; e.g. if the DCP is just audio */
118 best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
122 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
124 decoder.reset (new FFmpegDecoder (fc, _film->log()));
125 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
128 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (*i);
130 decoder.reset (new DCPDecoder (dc, _film->log ()));
131 frc = FrameRateChange (dc->video_frame_rate(), _film->video_frame_rate());
135 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
137 /* See if we can re-use an old ImageDecoder */
138 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
139 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
140 if (imd && imd->content() == ic) {
146 decoder.reset (new ImageDecoder (ic));
149 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
153 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
155 decoder.reset (new SndfileDecoder (sc));
156 frc = best_overlap_frc;
160 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
162 decoder.reset (new SubRipDecoder (rc));
163 frc = best_overlap_frc;
166 /* DCPSubtitleContent */
167 shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (*i);
169 decoder.reset (new DCPSubtitleDecoder (dsc));
170 frc = best_overlap_frc;
173 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
176 _have_valid_pieces = true;
180 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
182 shared_ptr<Content> c = w.lock ();
188 property == ContentProperty::POSITION ||
189 property == ContentProperty::LENGTH ||
190 property == ContentProperty::TRIM_START ||
191 property == ContentProperty::TRIM_END ||
192 property == ContentProperty::PATH ||
193 property == VideoContentProperty::VIDEO_FRAME_TYPE ||
194 property == DCPContentProperty::CAN_BE_PLAYED
197 _have_valid_pieces = false;
201 property == SubtitleContentProperty::USE_SUBTITLES ||
202 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
203 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
204 property == SubtitleContentProperty::SUBTITLE_X_SCALE ||
205 property == SubtitleContentProperty::SUBTITLE_Y_SCALE ||
206 property == VideoContentProperty::VIDEO_CROP ||
207 property == VideoContentProperty::VIDEO_SCALE ||
208 property == VideoContentProperty::VIDEO_FRAME_RATE
215 /** @param already_resampled true if this data has already been through the chain up to the resampler */
217 Player::playlist_changed ()
219 _have_valid_pieces = false;
224 Player::set_video_container_size (dcp::Size s)
226 _video_container_size = s;
228 _black_image.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
229 _black_image->make_black ();
233 Player::film_changed (Film::Property p)
235 /* Here we should notice Film properties that affect our output, and
236 alert listeners that our output now would be different to how it was
237 last time we were run.
240 if (p == Film::SCALER || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
246 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
248 list<PositionImage> all;
250 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
255 /* We will scale the subtitle up to fit _video_container_size */
256 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
258 /* Then we need a corrective translation, consisting of two parts:
260 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
261 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
263 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
264 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
265 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
267 * Combining these two translations gives these expressions.
274 Scaler::from_id ("bicubic"),
275 i->image->pixel_format (),
279 rint (_video_container_size.width * i->rectangle.x),
280 rint (_video_container_size.height * i->rectangle.y)
290 Player::set_approximate_size ()
292 _approximate_size = true;
295 shared_ptr<PlayerVideo>
296 Player::black_player_video_frame (DCPTime time) const
298 return shared_ptr<PlayerVideo> (
300 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image, _film->log ())),
304 _video_container_size,
305 _video_container_size,
306 Scaler::from_id ("bicubic"),
309 Config::instance()->colour_conversions().front().conversion
314 /** @return All PlayerVideos at the given time (there may be two frames for 3D) */
315 list<shared_ptr<PlayerVideo> >
316 Player::get_video (DCPTime time, bool accurate)
318 if (!_have_valid_pieces) {
322 list<shared_ptr<Piece> > ov = overlaps<VideoContent> (
324 time + DCPTime::from_frames (1, _film->video_frame_rate ())
327 list<shared_ptr<PlayerVideo> > pvf;
330 /* No video content at this time */
331 pvf.push_back (black_player_video_frame (time));
333 /* Create a PlayerVideo from the content's video at this time */
335 shared_ptr<Piece> piece = ov.back ();
336 shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
338 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
341 list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate);
342 if (content_video.empty ()) {
343 pvf.push_back (black_player_video_frame (time));
347 dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size (), _approximate_size ? 4 : 1);
348 if (_approximate_size) {
349 image_size.width &= ~3;
350 image_size.height &= ~3;
353 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
355 shared_ptr<PlayerVideo> (
358 content_video_to_dcp (piece, i->frame),
360 content->fade (i->frame),
362 _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 images) */
384 sub_images.push_back (render_subtitles (ps.text, _video_container_size));
386 if (!sub_images.empty ()) {
387 for (list<shared_ptr<PlayerVideo> >::const_iterator i = pvf.begin(); i != pvf.end(); ++i) {
388 (*i)->set_subtitle (merge (sub_images));
395 shared_ptr<AudioBuffers>
396 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
398 if (!_have_valid_pieces) {
402 AudioFrame const length_frames = length.frames (_film->audio_frame_rate ());
404 shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
405 audio->make_silent ();
407 list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time, time + length);
412 for (list<shared_ptr<Piece> >::iterator i = ov.begin(); i != ov.end(); ++i) {
414 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> ((*i)->content);
416 shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
419 if (content->audio_frame_rate() == 0) {
420 /* This AudioContent has no audio (e.g. if it is an FFmpegContent with no
426 /* The time that we should request from the content */
427 DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
429 if (request < DCPTime ()) {
430 /* We went off the start of the content, so we will need to offset
431 the stuff we get back.
434 request = DCPTime ();
437 AudioFrame const content_frame = dcp_to_content_audio (*i, request);
439 /* Audio from this piece's decoder (which might be more or less than what we asked for) */
440 shared_ptr<ContentAudio> all = decoder->get_audio (content_frame, length_frames, accurate);
443 if (content->audio_gain() != 0) {
444 shared_ptr<AudioBuffers> gain (new AudioBuffers (all->audio));
445 gain->apply_gain (content->audio_gain ());
450 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all->audio->frames()));
451 dcp_mapped->make_silent ();
452 AudioMapping map = content->audio_mapping ();
453 for (int i = 0; i < map.content_channels(); ++i) {
454 for (int j = 0; j < _film->audio_channels(); ++j) {
455 if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
456 dcp_mapped->accumulate_channel (
460 map.get (i, static_cast<dcp::Channel> (j))
466 all->audio = dcp_mapped;
468 audio->accumulate_frames (
470 content_frame - all->frame,
471 offset.frames (_film->audio_frame_rate()),
472 min (AudioFrame (all->audio->frames()), length_frames) - offset.frames (_film->audio_frame_rate ())
480 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
482 /* s is the offset of t from the start position of this content */
483 DCPTime s = t - piece->content->position ();
484 s = DCPTime (max (DCPTime::Type (0), s.get ()));
485 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
487 /* Convert this to the content frame */
488 return DCPTime (s + piece->content->trim_start()).frames (_film->video_frame_rate()) * piece->frc.factor ();
492 Player::content_video_to_dcp (shared_ptr<const Piece> piece, VideoFrame f) const
494 DCPTime t = DCPTime::from_frames (f / piece->frc.factor (), _film->video_frame_rate()) - piece->content->trim_start () + piece->content->position ();
495 if (t < DCPTime ()) {
503 Player::dcp_to_content_audio (shared_ptr<const Piece> piece, DCPTime t) const
505 /* s is the offset of t from the start position of this content */
506 DCPTime s = t - piece->content->position ();
507 s = DCPTime (max (DCPTime::Type (0), s.get ()));
508 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
510 /* Convert this to the content frame */
511 return DCPTime (s + piece->content->trim_start()).frames (_film->audio_frame_rate());
515 Player::dcp_to_content_subtitle (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 return ContentTime (s + piece->content->trim_start(), piece->frc);
526 PlayerStatistics::dump (shared_ptr<Log> log) const
528 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat), Log::TYPE_GENERAL);
529 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence.seconds()), Log::TYPE_GENERAL);
532 PlayerStatistics const &
533 Player::statistics () const
539 Player::get_subtitles (DCPTime time, DCPTime length, bool starting)
541 list<shared_ptr<Piece> > subs = overlaps<SubtitleContent> (time, time + length);
543 PlayerSubtitles ps (time, length);
545 for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
546 shared_ptr<SubtitleContent> subtitle_content = dynamic_pointer_cast<SubtitleContent> ((*j)->content);
547 if (!subtitle_content->use_subtitles ()) {
551 shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*j)->decoder);
552 ContentTime const from = dcp_to_content_subtitle (*j, time);
553 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
554 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
556 list<ContentImageSubtitle> image = subtitle_decoder->get_image_subtitles (ContentTimePeriod (from, to), starting);
557 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
559 /* Apply content's subtitle offsets */
560 i->sub.rectangle.x += subtitle_content->subtitle_x_offset ();
561 i->sub.rectangle.y += subtitle_content->subtitle_y_offset ();
563 /* Apply content's subtitle scale */
564 i->sub.rectangle.width *= subtitle_content->subtitle_x_scale ();
565 i->sub.rectangle.height *= subtitle_content->subtitle_y_scale ();
567 /* Apply a corrective translation to keep the subtitle centred after that scale */
568 i->sub.rectangle.x -= i->sub.rectangle.width * (subtitle_content->subtitle_x_scale() - 1);
569 i->sub.rectangle.y -= i->sub.rectangle.height * (subtitle_content->subtitle_y_scale() - 1);
571 ps.image.push_back (i->sub);
574 list<ContentTextSubtitle> text = subtitle_decoder->get_text_subtitles (ContentTimePeriod (from, to), starting);
575 for (list<ContentTextSubtitle>::const_iterator i = text.begin(); i != text.end(); ++i) {
576 copy (i->subs.begin(), i->subs.end(), back_inserter (ps.text));