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 "raw_image_proxy.h"
41 #include "render_subtitles.h"
43 #include "content_video.h"
44 #include "player_video.h"
45 #include "frame_rate_change.h"
46 #include "dcp_content.h"
47 #include "dcp_decoder.h"
48 #include "dcp_subtitle_content.h"
49 #include "dcp_subtitle_decoder.h"
51 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
62 using boost::shared_ptr;
63 using boost::weak_ptr;
64 using boost::dynamic_pointer_cast;
65 using boost::optional;
67 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
70 , _have_valid_pieces (false)
71 , _approximate_size (false)
73 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
74 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
75 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
76 set_video_container_size (_film->frame_size ());
80 Player::setup_pieces ()
82 list<shared_ptr<Piece> > old_pieces = _pieces;
85 ContentList content = _playlist->content ();
87 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
89 if (!(*i)->paths_valid ()) {
93 shared_ptr<Decoder> decoder;
94 optional<FrameRateChange> frc;
96 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
97 DCPTime best_overlap_t;
98 shared_ptr<VideoContent> best_overlap;
99 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
100 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
105 DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
106 if (overlap > best_overlap_t) {
108 best_overlap_t = overlap;
112 optional<FrameRateChange> best_overlap_frc;
114 best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
116 /* No video overlap; e.g. if the DCP is just audio */
117 best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
121 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
123 decoder.reset (new FFmpegDecoder (fc, _film->log()));
124 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
127 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (*i);
129 decoder.reset (new DCPDecoder (dc, _film->log ()));
130 frc = FrameRateChange (dc->video_frame_rate(), _film->video_frame_rate());
134 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
136 /* See if we can re-use an old ImageDecoder */
137 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
138 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
139 if (imd && imd->content() == ic) {
145 decoder.reset (new ImageDecoder (ic));
148 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
152 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
154 decoder.reset (new SndfileDecoder (sc));
155 frc = best_overlap_frc;
159 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
161 decoder.reset (new SubRipDecoder (rc));
162 frc = best_overlap_frc;
165 /* DCPSubtitleContent */
166 shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (*i);
168 decoder.reset (new DCPSubtitleDecoder (dsc));
169 frc = best_overlap_frc;
172 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
175 _have_valid_pieces = true;
179 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
181 shared_ptr<Content> c = w.lock ();
187 property == ContentProperty::POSITION ||
188 property == ContentProperty::LENGTH ||
189 property == ContentProperty::TRIM_START ||
190 property == ContentProperty::TRIM_END ||
191 property == ContentProperty::PATH ||
192 property == VideoContentProperty::VIDEO_FRAME_TYPE
195 _have_valid_pieces = false;
199 property == SubtitleContentProperty::SUBTITLE_USE ||
200 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
201 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
202 property == SubtitleContentProperty::SUBTITLE_SCALE ||
203 property == VideoContentProperty::VIDEO_CROP ||
204 property == VideoContentProperty::VIDEO_SCALE ||
205 property == VideoContentProperty::VIDEO_FRAME_RATE
212 /** @param already_resampled true if this data has already been through the chain up to the resampler */
214 Player::playlist_changed ()
216 _have_valid_pieces = false;
221 Player::set_video_container_size (dcp::Size s)
223 _video_container_size = s;
225 _black_image.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
226 _black_image->make_black ();
230 Player::film_changed (Film::Property p)
232 /* Here we should notice Film properties that affect our output, and
233 alert listeners that our output now would be different to how it was
234 last time we were run.
237 if (p == Film::SCALER || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
243 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
245 list<PositionImage> all;
247 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
252 /* We will scale the subtitle up to fit _video_container_size */
253 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
255 /* Then we need a corrective translation, consisting of two parts:
257 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
258 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
260 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
261 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
262 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
264 * Combining these two translations gives these expressions.
271 Scaler::from_id ("bicubic"),
272 i->image->pixel_format (),
276 rint (_video_container_size.width * i->rectangle.x),
277 rint (_video_container_size.height * i->rectangle.y)
287 Player::set_approximate_size ()
289 _approximate_size = true;
292 shared_ptr<PlayerVideo>
293 Player::black_player_video_frame (DCPTime time) const
295 return shared_ptr<PlayerVideo> (
297 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image, _film->log ())),
300 _video_container_size,
301 _video_container_size,
302 Scaler::from_id ("bicubic"),
305 Config::instance()->colour_conversions().front().conversion
310 /** @return All PlayerVideos at the given time (there may be two frames for 3D) */
311 list<shared_ptr<PlayerVideo> >
312 Player::get_video (DCPTime time, bool accurate)
314 if (!_have_valid_pieces) {
318 list<shared_ptr<Piece> > ov = overlaps<VideoContent> (
320 time + DCPTime::from_frames (1, _film->video_frame_rate ())
323 list<shared_ptr<PlayerVideo> > pvf;
326 /* No video content at this time */
327 pvf.push_back (black_player_video_frame (time));
329 /* Create a PlayerVideo from the content's video at this time */
331 shared_ptr<Piece> piece = ov.back ();
332 shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
334 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
337 list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate);
338 if (content_video.empty ()) {
339 pvf.push_back (black_player_video_frame (time));
343 dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size ());
344 if (_approximate_size) {
345 image_size.width &= ~3;
346 image_size.height &= ~3;
349 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
351 shared_ptr<PlayerVideo> (
354 content_video_to_dcp (piece, i->frame),
357 _video_container_size,
361 content->colour_conversion ()
368 /* Add subtitles (for possible burn-in) to whatever PlayerVideos we got */
370 PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false);
372 list<PositionImage> sub_images;
374 /* Image subtitles */
375 list<PositionImage> c = transform_image_subtitles (ps.image);
376 copy (c.begin(), c.end(), back_inserter (sub_images));
378 /* Text subtitles (rendered to images) */
379 sub_images.push_back (render_subtitles (ps.text, _video_container_size));
381 if (!sub_images.empty ()) {
382 for (list<shared_ptr<PlayerVideo> >::const_iterator i = pvf.begin(); i != pvf.end(); ++i) {
383 (*i)->set_subtitle (merge (sub_images));
390 shared_ptr<AudioBuffers>
391 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
393 if (!_have_valid_pieces) {
397 AudioFrame const length_frames = length.frames (_film->audio_frame_rate ());
399 shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
400 audio->make_silent ();
402 list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time, time + length);
407 for (list<shared_ptr<Piece> >::iterator i = ov.begin(); i != ov.end(); ++i) {
409 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> ((*i)->content);
411 shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
414 if (content->audio_frame_rate() == 0) {
415 /* This AudioContent has no audio (e.g. if it is an FFmpegContent with no
421 /* The time that we should request from the content */
422 DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
424 if (request < DCPTime ()) {
425 /* We went off the start of the content, so we will need to offset
426 the stuff we get back.
429 request = DCPTime ();
432 AudioFrame const content_frame = dcp_to_content_audio (*i, request);
434 /* Audio from this piece's decoder (which might be more or less than what we asked for) */
435 shared_ptr<ContentAudio> all = decoder->get_audio (content_frame, length_frames, accurate);
438 if (content->audio_gain() != 0) {
439 shared_ptr<AudioBuffers> gain (new AudioBuffers (all->audio));
440 gain->apply_gain (content->audio_gain ());
445 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all->audio->frames()));
446 dcp_mapped->make_silent ();
447 AudioMapping map = content->audio_mapping ();
448 for (int i = 0; i < map.content_channels(); ++i) {
449 for (int j = 0; j < _film->audio_channels(); ++j) {
450 if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
451 dcp_mapped->accumulate_channel (
455 map.get (i, static_cast<dcp::Channel> (j))
461 all->audio = dcp_mapped;
463 audio->accumulate_frames (
465 content_frame - all->frame,
466 offset.frames (_film->audio_frame_rate()),
467 min (AudioFrame (all->audio->frames()), length_frames) - offset.frames (_film->audio_frame_rate ())
475 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
477 /* s is the offset of t from the start position of this content */
478 DCPTime s = t - piece->content->position ();
479 s = DCPTime (max (DCPTime::Type (0), s.get ()));
480 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
482 /* Convert this to the content frame */
483 return DCPTime (s + piece->content->trim_start()).frames (_film->video_frame_rate()) * piece->frc.factor ();
487 Player::content_video_to_dcp (shared_ptr<const Piece> piece, VideoFrame f) const
489 DCPTime t = DCPTime::from_frames (f / piece->frc.factor (), _film->video_frame_rate()) - piece->content->trim_start () + piece->content->position ();
490 if (t < DCPTime ()) {
498 Player::dcp_to_content_audio (shared_ptr<const Piece> piece, DCPTime t) const
500 /* s is the offset of t from the start position of this content */
501 DCPTime s = t - piece->content->position ();
502 s = DCPTime (max (DCPTime::Type (0), s.get ()));
503 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
505 /* Convert this to the content frame */
506 return DCPTime (s + piece->content->trim_start()).frames (_film->audio_frame_rate());
510 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
512 /* s is the offset of t from the start position of this content */
513 DCPTime s = t - piece->content->position ();
514 s = DCPTime (max (DCPTime::Type (0), s.get ()));
515 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
517 return ContentTime (s + piece->content->trim_start(), piece->frc);
521 PlayerStatistics::dump (shared_ptr<Log> log) const
523 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat), Log::TYPE_GENERAL);
524 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence.seconds()), Log::TYPE_GENERAL);
527 PlayerStatistics const &
528 Player::statistics () const
534 Player::get_subtitles (DCPTime time, DCPTime length, bool starting)
536 list<shared_ptr<Piece> > subs = overlaps<SubtitleContent> (time, time + length);
538 PlayerSubtitles ps (time, length);
540 for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
541 shared_ptr<SubtitleContent> subtitle_content = dynamic_pointer_cast<SubtitleContent> ((*j)->content);
542 if (!subtitle_content->subtitle_use ()) {
546 shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*j)->decoder);
547 ContentTime const from = dcp_to_content_subtitle (*j, time);
548 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
549 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
551 list<ContentImageSubtitle> image = subtitle_decoder->get_image_subtitles (ContentTimePeriod (from, to), starting);
552 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
554 /* Apply content's subtitle offsets */
555 i->sub.rectangle.x += subtitle_content->subtitle_x_offset ();
556 i->sub.rectangle.y += subtitle_content->subtitle_y_offset ();
558 /* Apply content's subtitle scale */
559 i->sub.rectangle.width *= subtitle_content->subtitle_scale ();
560 i->sub.rectangle.height *= subtitle_content->subtitle_scale ();
562 /* Apply a corrective translation to keep the subtitle centred after that scale */
563 i->sub.rectangle.x -= i->sub.rectangle.width * (subtitle_content->subtitle_scale() - 1);
564 i->sub.rectangle.y -= i->sub.rectangle.height * (subtitle_content->subtitle_scale() - 1);
566 ps.image.push_back (i->sub);
569 list<ContentTextSubtitle> text = subtitle_decoder->get_text_subtitles (ContentTimePeriod (from, to), starting);
570 for (list<ContentTextSubtitle>::const_iterator i = text.begin(); i != text.end(); ++i) {
571 copy (i->subs.begin(), i->subs.end(), back_inserter (ps.text));