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"
37 #include "image_proxy.h"
41 #include "render_subtitles.h"
43 #include "content_video.h"
44 #include "player_video.h"
45 #include "frame_rate_change.h"
47 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
58 using boost::shared_ptr;
59 using boost::weak_ptr;
60 using boost::dynamic_pointer_cast;
61 using boost::optional;
63 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
66 , _have_valid_pieces (false)
67 , _approximate_size (false)
69 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
70 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
71 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
72 set_video_container_size (_film->frame_size ());
76 Player::setup_pieces ()
78 list<shared_ptr<Piece> > old_pieces = _pieces;
81 ContentList content = _playlist->content ();
83 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
85 if (!(*i)->paths_valid ()) {
89 shared_ptr<Decoder> decoder;
90 optional<FrameRateChange> frc;
92 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
93 DCPTime best_overlap_t;
94 shared_ptr<VideoContent> best_overlap;
95 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
96 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
101 DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
102 if (overlap > best_overlap_t) {
104 best_overlap_t = overlap;
108 optional<FrameRateChange> best_overlap_frc;
110 best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
112 /* No video overlap; e.g. if the DCP is just audio */
113 best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
117 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
119 decoder.reset (new FFmpegDecoder (fc, _film->log()));
120 frc = FrameRateChange (fc->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));
145 frc = best_overlap_frc;
149 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
151 decoder.reset (new SubRipDecoder (rc));
152 frc = best_overlap_frc;
155 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
158 _have_valid_pieces = true;
162 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
164 shared_ptr<Content> c = w.lock ();
170 property == ContentProperty::POSITION ||
171 property == ContentProperty::LENGTH ||
172 property == ContentProperty::TRIM_START ||
173 property == ContentProperty::TRIM_END ||
174 property == ContentProperty::PATH ||
175 property == VideoContentProperty::VIDEO_FRAME_TYPE
178 _have_valid_pieces = false;
182 property == SubtitleContentProperty::SUBTITLE_USE ||
183 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
184 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
185 property == SubtitleContentProperty::SUBTITLE_SCALE ||
186 property == VideoContentProperty::VIDEO_CROP ||
187 property == VideoContentProperty::VIDEO_SCALE ||
188 property == VideoContentProperty::VIDEO_FRAME_RATE
195 /** @param already_resampled true if this data has already been through the chain up to the resampler */
197 Player::playlist_changed ()
199 _have_valid_pieces = false;
204 Player::set_video_container_size (dcp::Size s)
206 _video_container_size = s;
208 _black_image.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
209 _black_image->make_black ();
213 Player::film_changed (Film::Property p)
215 /* Here we should notice Film properties that affect our output, and
216 alert listeners that our output now would be different to how it was
217 last time we were run.
220 if (p == Film::SCALER || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
226 Player::process_content_image_subtitles (shared_ptr<SubtitleContent> content, list<shared_ptr<ContentImageSubtitle> > subs) const
228 list<PositionImage> all;
230 for (list<shared_ptr<ContentImageSubtitle> >::const_iterator i = subs.begin(); i != subs.end(); ++i) {
235 dcpomatic::Rect<double> in_rect = (*i)->rectangle;
236 dcp::Size scaled_size;
238 in_rect.x += content->subtitle_x_offset ();
239 in_rect.y += content->subtitle_y_offset ();
241 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
242 scaled_size.width = in_rect.width * _video_container_size.width * content->subtitle_scale ();
243 scaled_size.height = in_rect.height * _video_container_size.height * content->subtitle_scale ();
245 /* Then we need a corrective translation, consisting of two parts:
247 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
248 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
250 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
251 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
252 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
254 * Combining these two translations gives these expressions.
261 Scaler::from_id ("bicubic"),
262 (*i)->image->pixel_format (),
266 rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - content->subtitle_scale ()) / 2))),
267 rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - content->subtitle_scale ()) / 2)))
277 Player::process_content_text_subtitles (list<shared_ptr<ContentTextSubtitle> > sub) const
279 list<PositionImage> all;
280 for (list<shared_ptr<ContentTextSubtitle> >::const_iterator i = sub.begin(); i != sub.end(); ++i) {
281 if (!(*i)->subs.empty ()) {
282 all.push_back (render_subtitles ((*i)->subs, _video_container_size));
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 ())),
303 _video_container_size,
304 _video_container_size,
305 Scaler::from_id ("bicubic"),
308 Config::instance()->colour_conversions().front().conversion
313 /** @return All PlayerVideos at the given time (there may be two frames for 3D) */
314 list<shared_ptr<PlayerVideo> >
315 Player::get_video (DCPTime time, bool accurate)
317 if (!_have_valid_pieces) {
321 list<shared_ptr<Piece> > ov = overlaps<VideoContent> (
323 time + DCPTime::from_frames (1, _film->video_frame_rate ())
326 list<shared_ptr<PlayerVideo> > pvf;
329 /* No video content at this time */
330 pvf.push_back (black_player_video_frame (time));
332 /* Create a PlayerVideo from the content's video at this time */
334 shared_ptr<Piece> piece = ov.back ();
335 shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
337 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
340 list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate);
341 if (content_video.empty ()) {
342 pvf.push_back (black_player_video_frame (time));
346 dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size ());
347 if (_approximate_size) {
348 image_size.width &= ~3;
349 image_size.height &= ~3;
352 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
354 shared_ptr<PlayerVideo> (
357 content_video_to_dcp (piece, i->frame),
360 _video_container_size,
364 content->colour_conversion ()
371 /* Add subtitles to whatever PlayerVideos we got */
373 list<shared_ptr<Piece> > subs = overlaps<SubtitleContent> (
375 time + DCPTime::from_frames (1, _film->video_frame_rate ())
378 list<PositionImage> sub_images;
380 for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
381 shared_ptr<SubtitleContent> subtitle_content = dynamic_pointer_cast<SubtitleContent> ((*j)->content);
382 if (!subtitle_content->subtitle_use ()) {
386 shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*j)->decoder);
387 ContentTime const from = dcp_to_content_subtitle (*j, time);
388 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
389 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
391 list<shared_ptr<ContentImageSubtitle> > image_subtitles = subtitle_decoder->get_image_subtitles (ContentTimePeriod (from, to));
392 if (!image_subtitles.empty ()) {
393 list<PositionImage> im = process_content_image_subtitles (
398 copy (im.begin(), im.end(), back_inserter (sub_images));
401 list<shared_ptr<ContentTextSubtitle> > text_subtitles = subtitle_decoder->get_text_subtitles (ContentTimePeriod (from, to));
402 if (!text_subtitles.empty ()) {
403 list<PositionImage> im = process_content_text_subtitles (text_subtitles);
404 copy (im.begin(), im.end(), back_inserter (sub_images));
408 if (!sub_images.empty ()) {
409 for (list<shared_ptr<PlayerVideo> >::const_iterator i = pvf.begin(); i != pvf.end(); ++i) {
410 (*i)->set_subtitle (merge (sub_images));
417 shared_ptr<AudioBuffers>
418 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
420 if (!_have_valid_pieces) {
424 AudioFrame const length_frames = length.frames (_film->audio_frame_rate ());
426 shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
427 audio->make_silent ();
429 list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time, time + length);
434 for (list<shared_ptr<Piece> >::iterator i = ov.begin(); i != ov.end(); ++i) {
436 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> ((*i)->content);
438 shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
441 if (content->audio_frame_rate() == 0) {
442 /* This AudioContent has no audio (e.g. if it is an FFmpegContent with no
448 /* The time that we should request from the content */
449 DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
451 if (request < DCPTime ()) {
452 /* We went off the start of the content, so we will need to offset
453 the stuff we get back.
456 request = DCPTime ();
459 AudioFrame const content_frame = dcp_to_content_audio (*i, request);
461 /* Audio from this piece's decoder (which might be more or less than what we asked for) */
462 shared_ptr<ContentAudio> all = decoder->get_audio (content_frame, length_frames, accurate);
465 if (content->audio_gain() != 0) {
466 shared_ptr<AudioBuffers> gain (new AudioBuffers (all->audio));
467 gain->apply_gain (content->audio_gain ());
472 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all->audio->frames()));
473 dcp_mapped->make_silent ();
474 AudioMapping map = content->audio_mapping ();
475 for (int i = 0; i < map.content_channels(); ++i) {
476 for (int j = 0; j < _film->audio_channels(); ++j) {
477 if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
478 dcp_mapped->accumulate_channel (
482 map.get (i, static_cast<dcp::Channel> (j))
488 all->audio = dcp_mapped;
490 audio->accumulate_frames (
492 content_frame - all->frame,
493 offset.frames (_film->audio_frame_rate()),
494 min (AudioFrame (all->audio->frames()), length_frames) - offset.frames (_film->audio_frame_rate ())
502 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
504 /* s is the offset of t from the start position of this content */
505 DCPTime s = t - piece->content->position ();
506 s = DCPTime (max (DCPTime::Type (0), s.get ()));
507 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
509 /* Convert this to the content frame */
510 return DCPTime (s + piece->content->trim_start()).frames (_film->video_frame_rate()) * piece->frc.factor ();
514 Player::content_video_to_dcp (shared_ptr<const Piece> piece, VideoFrame f) const
516 DCPTime t = DCPTime::from_frames (f / piece->frc.factor (), _film->video_frame_rate()) - piece->content->trim_start () + piece->content->position ();
517 if (t < DCPTime ()) {
525 Player::dcp_to_content_audio (shared_ptr<const Piece> piece, DCPTime t) const
527 /* s is the offset of t from the start position of this content */
528 DCPTime s = t - piece->content->position ();
529 s = DCPTime (max (DCPTime::Type (0), s.get ()));
530 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
532 /* Convert this to the content frame */
533 return DCPTime (s + piece->content->trim_start()).frames (_film->audio_frame_rate());
537 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
539 /* s is the offset of t from the start position of this content */
540 DCPTime s = t - piece->content->position ();
541 s = DCPTime (max (DCPTime::Type (0), s.get ()));
542 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
544 return ContentTime (s + piece->content->trim_start(), piece->frc);
548 PlayerStatistics::dump (shared_ptr<Log> log) const
550 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat), Log::TYPE_GENERAL);
551 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence.seconds()), Log::TYPE_GENERAL);
554 PlayerStatistics const &
555 Player::statistics () const