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"
40 #include "render_subtitles.h"
42 #include "content_video.h"
43 #include "player_video.h"
44 #include "frame_rate_change.h"
45 #include "dcp_content.h"
46 #include "dcp_decoder.h"
47 #include "dcp_subtitle_content.h"
48 #include "dcp_subtitle_decoder.h"
49 #include <boost/foreach.hpp>
55 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
66 using boost::shared_ptr;
67 using boost::weak_ptr;
68 using boost::dynamic_pointer_cast;
69 using boost::optional;
71 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
74 , _have_valid_pieces (false)
75 , _approximate_size (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 ());
85 Player::setup_pieces ()
87 list<shared_ptr<Piece> > old_pieces = _pieces;
90 ContentList content = _playlist->content ();
92 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
94 if (!(*i)->paths_valid ()) {
98 shared_ptr<Decoder> decoder;
99 optional<FrameRateChange> frc;
101 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
102 DCPTime best_overlap_t;
103 shared_ptr<VideoContent> best_overlap;
104 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
105 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
110 DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
111 if (overlap > best_overlap_t) {
113 best_overlap_t = overlap;
117 optional<FrameRateChange> best_overlap_frc;
119 best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
121 /* No video overlap; e.g. if the DCP is just audio */
122 best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
126 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
128 decoder.reset (new FFmpegDecoder (fc, _film->log()));
129 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
132 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (*i);
134 decoder.reset (new DCPDecoder (dc));
135 frc = FrameRateChange (dc->video_frame_rate(), _film->video_frame_rate());
139 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
141 /* See if we can re-use an old ImageDecoder */
142 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
143 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
144 if (imd && imd->content() == ic) {
150 decoder.reset (new ImageDecoder (ic));
153 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
157 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
159 decoder.reset (new SndfileDecoder (sc));
160 frc = best_overlap_frc;
164 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
166 decoder.reset (new SubRipDecoder (rc));
167 frc = best_overlap_frc;
170 /* DCPSubtitleContent */
171 shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (*i);
173 decoder.reset (new DCPSubtitleDecoder (dsc));
174 frc = best_overlap_frc;
177 shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> (decoder);
178 if (vd && _ignore_video) {
179 vd->set_ignore_video ();
182 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
185 _have_valid_pieces = true;
189 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
191 shared_ptr<Content> c = w.lock ();
197 property == ContentProperty::POSITION ||
198 property == ContentProperty::LENGTH ||
199 property == ContentProperty::TRIM_START ||
200 property == ContentProperty::TRIM_END ||
201 property == ContentProperty::PATH ||
202 property == VideoContentProperty::VIDEO_FRAME_TYPE ||
203 property == DCPContentProperty::CAN_BE_PLAYED
206 _have_valid_pieces = false;
210 property == SubtitleContentProperty::USE_SUBTITLES ||
211 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
212 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
213 property == SubtitleContentProperty::SUBTITLE_X_SCALE ||
214 property == SubtitleContentProperty::SUBTITLE_Y_SCALE ||
215 property == VideoContentProperty::VIDEO_CROP ||
216 property == VideoContentProperty::VIDEO_SCALE ||
217 property == VideoContentProperty::VIDEO_FRAME_RATE ||
218 property == VideoContentProperty::VIDEO_FADE_IN ||
219 property == VideoContentProperty::VIDEO_FADE_OUT
226 /** @param already_resampled true if this data has already been through the chain up to the resampler */
228 Player::playlist_changed ()
230 _have_valid_pieces = false;
235 Player::set_video_container_size (dcp::Size s)
237 _video_container_size = s;
239 _black_image.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
240 _black_image->make_black ();
244 Player::film_changed (Film::Property p)
246 /* Here we should notice Film properties that affect our output, and
247 alert listeners that our output now would be different to how it was
248 last time we were run.
251 if (p == Film::SCALER || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
257 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
259 list<PositionImage> all;
261 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
266 /* We will scale the subtitle up to fit _video_container_size */
267 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
269 /* Then we need a corrective translation, consisting of two parts:
271 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
272 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
274 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
275 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
276 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
278 * Combining these two translations gives these expressions.
285 Scaler::from_id ("bicubic"),
286 i->image->pixel_format (),
290 rint (_video_container_size.width * i->rectangle.x),
291 rint (_video_container_size.height * i->rectangle.y)
301 Player::set_approximate_size ()
303 _approximate_size = true;
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,
317 Scaler::from_id ("bicubic"),
320 Config::instance()->colour_conversions().front().conversion
325 /** @return All PlayerVideos at the given time (there may be two frames for 3D) */
326 list<shared_ptr<PlayerVideo> >
327 Player::get_video (DCPTime time, bool accurate)
329 if (!_have_valid_pieces) {
333 list<shared_ptr<Piece> > ov = overlaps<VideoContent> (
335 time + DCPTime::from_frames (1, _film->video_frame_rate ())
338 list<shared_ptr<PlayerVideo> > pvf;
341 /* No video content at this time */
342 pvf.push_back (black_player_video_frame (time));
344 /* Create a PlayerVideo from the content's video at this time */
346 shared_ptr<Piece> piece = ov.back ();
347 shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
348 DCPOMATIC_ASSERT (decoder);
349 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
350 DCPOMATIC_ASSERT (content);
352 list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate);
353 if (content_video.empty ()) {
354 pvf.push_back (black_player_video_frame (time));
358 dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size (), _approximate_size ? 4 : 1);
360 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
362 shared_ptr<PlayerVideo> (
365 content_video_to_dcp (piece, i->frame),
367 content->fade (i->frame),
369 _video_container_size,
373 content->colour_conversion ()
380 /* Add subtitles (for possible burn-in) to whatever PlayerVideos we got */
382 PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false);
384 list<PositionImage> sub_images;
386 /* Image subtitles */
387 list<PositionImage> c = transform_image_subtitles (ps.image);
388 copy (c.begin(), c.end(), back_inserter (sub_images));
390 /* Text subtitles (rendered to images) */
391 sub_images.push_back (render_subtitles (ps.text, _video_container_size));
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 AudioFrame 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 if (content->audio_frame_rate() == 0) {
427 /* This AudioContent has no audio (e.g. if it is an FFmpegContent with no
433 /* The time that we should request from the content */
434 DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
435 AudioFrame request_frames = length_frames;
437 if (request < DCPTime ()) {
438 /* We went off the start of the content, so we will need to offset
439 the stuff we get back.
442 request_frames += request.frames (_film->audio_frame_rate ());
443 if (request_frames < 0) {
446 request = DCPTime ();
449 AudioFrame const content_frame = dcp_to_content_audio (*i, request);
451 /* Audio from this piece's decoder (which might be more or less than what we asked for) */
452 shared_ptr<ContentAudio> all = decoder->get_audio (content_frame, request_frames, accurate);
455 if (content->audio_gain() != 0) {
456 shared_ptr<AudioBuffers> gain (new AudioBuffers (all->audio));
457 gain->apply_gain (content->audio_gain ());
462 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all->audio->frames()));
463 dcp_mapped->make_silent ();
464 AudioMapping map = content->audio_mapping ();
465 for (int i = 0; i < map.content_channels(); ++i) {
466 for (int j = 0; j < _film->audio_channels(); ++j) {
467 if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
468 dcp_mapped->accumulate_channel (
472 map.get (i, static_cast<dcp::Channel> (j))
478 all->audio = dcp_mapped;
480 audio->accumulate_frames (
482 content_frame - all->frame,
483 offset.frames (_film->audio_frame_rate()),
484 min (AudioFrame (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, VideoFrame 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 s.set_size (s.size() * max (subtitle_content->subtitle_x_scale(), subtitle_content->subtitle_y_scale()));
591 ps.text.push_back (s);
599 list<shared_ptr<Font> >
600 Player::get_subtitle_fonts ()
602 if (!_have_valid_pieces) {
606 list<shared_ptr<Font> > fonts;
607 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
608 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (p->content);
610 /* XXX: things may go wrong if there are duplicate font IDs
611 with different font files.
613 list<shared_ptr<Font> > f = sc->fonts ();
614 copy (f.begin(), f.end(), back_inserter (fonts));
621 /** Set this player never to produce any video data */
623 Player::set_ignore_video ()
625 _ignore_video = true;