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.
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);
359 if (_approximate_size) {
360 image_size.width &= ~3;
361 image_size.height &= ~3;
364 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
366 shared_ptr<PlayerVideo> (
369 content_video_to_dcp (piece, i->frame),
371 content->fade (i->frame),
373 _video_container_size,
377 content->colour_conversion ()
384 /* Add subtitles (for possible burn-in) to whatever PlayerVideos we got */
386 PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false);
388 list<PositionImage> sub_images;
390 /* Image subtitles */
391 list<PositionImage> c = transform_image_subtitles (ps.image);
392 copy (c.begin(), c.end(), back_inserter (sub_images));
394 /* Text subtitles (rendered to images) */
395 sub_images.push_back (render_subtitles (ps.text, _video_container_size));
397 if (!sub_images.empty ()) {
398 for (list<shared_ptr<PlayerVideo> >::const_iterator i = pvf.begin(); i != pvf.end(); ++i) {
399 (*i)->set_subtitle (merge (sub_images));
406 shared_ptr<AudioBuffers>
407 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
409 if (!_have_valid_pieces) {
413 AudioFrame const length_frames = length.frames (_film->audio_frame_rate ());
415 shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
416 audio->make_silent ();
418 list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time, time + length);
423 for (list<shared_ptr<Piece> >::iterator i = ov.begin(); i != ov.end(); ++i) {
425 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> ((*i)->content);
426 DCPOMATIC_ASSERT (content);
427 shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
428 DCPOMATIC_ASSERT (decoder);
430 if (content->audio_frame_rate() == 0) {
431 /* This AudioContent has no audio (e.g. if it is an FFmpegContent with no
437 /* The time that we should request from the content */
438 DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
440 if (request < DCPTime ()) {
441 /* We went off the start of the content, so we will need to offset
442 the stuff we get back.
445 request = DCPTime ();
448 AudioFrame const content_frame = dcp_to_content_audio (*i, request);
450 /* Audio from this piece's decoder (which might be more or less than what we asked for) */
451 shared_ptr<ContentAudio> all = decoder->get_audio (content_frame, length_frames, accurate);
454 if (content->audio_gain() != 0) {
455 shared_ptr<AudioBuffers> gain (new AudioBuffers (all->audio));
456 gain->apply_gain (content->audio_gain ());
461 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all->audio->frames()));
462 dcp_mapped->make_silent ();
463 AudioMapping map = content->audio_mapping ();
464 for (int i = 0; i < map.content_channels(); ++i) {
465 for (int j = 0; j < _film->audio_channels(); ++j) {
466 if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
467 dcp_mapped->accumulate_channel (
471 map.get (i, static_cast<dcp::Channel> (j))
477 all->audio = dcp_mapped;
479 audio->accumulate_frames (
481 content_frame - all->frame,
482 offset.frames (_film->audio_frame_rate()),
483 min (AudioFrame (all->audio->frames()), length_frames) - offset.frames (_film->audio_frame_rate ())
491 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
493 /* s is the offset of t from the start position of this content */
494 DCPTime s = t - piece->content->position ();
495 s = DCPTime (max (DCPTime::Type (0), s.get ()));
496 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
498 /* Convert this to the content frame */
499 return DCPTime (s + piece->content->trim_start()).frames (_film->video_frame_rate()) / piece->frc.factor ();
503 Player::content_video_to_dcp (shared_ptr<const Piece> piece, VideoFrame f) const
505 DCPTime t = DCPTime::from_frames (f * piece->frc.factor (), _film->video_frame_rate()) - piece->content->trim_start () + piece->content->position ();
506 if (t < DCPTime ()) {
514 Player::dcp_to_content_audio (shared_ptr<const Piece> piece, DCPTime t) const
516 /* s is the offset of t from the start position of this content */
517 DCPTime s = t - piece->content->position ();
518 s = DCPTime (max (DCPTime::Type (0), s.get ()));
519 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
521 /* Convert this to the content frame */
522 return DCPTime (s + piece->content->trim_start()).frames (_film->audio_frame_rate());
526 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
528 /* s is the offset of t from the start position of this content */
529 DCPTime s = t - piece->content->position ();
530 s = DCPTime (max (DCPTime::Type (0), s.get ()));
531 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
533 return ContentTime (s + piece->content->trim_start(), piece->frc);
537 PlayerStatistics::dump (shared_ptr<Log> log) const
539 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat), Log::TYPE_GENERAL);
540 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence.seconds()), Log::TYPE_GENERAL);
543 PlayerStatistics const &
544 Player::statistics () const
550 Player::get_subtitles (DCPTime time, DCPTime length, bool starting)
552 list<shared_ptr<Piece> > subs = overlaps<SubtitleContent> (time, time + length);
554 PlayerSubtitles ps (time, length);
556 for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
557 shared_ptr<SubtitleContent> subtitle_content = dynamic_pointer_cast<SubtitleContent> ((*j)->content);
558 if (!subtitle_content->use_subtitles ()) {
562 shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*j)->decoder);
563 ContentTime const from = dcp_to_content_subtitle (*j, time);
564 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
565 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
567 list<ContentImageSubtitle> image = subtitle_decoder->get_image_subtitles (ContentTimePeriod (from, to), starting);
568 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
570 /* Apply content's subtitle offsets */
571 i->sub.rectangle.x += subtitle_content->subtitle_x_offset ();
572 i->sub.rectangle.y += subtitle_content->subtitle_y_offset ();
574 /* Apply content's subtitle scale */
575 i->sub.rectangle.width *= subtitle_content->subtitle_x_scale ();
576 i->sub.rectangle.height *= subtitle_content->subtitle_y_scale ();
578 /* Apply a corrective translation to keep the subtitle centred after that scale */
579 i->sub.rectangle.x -= i->sub.rectangle.width * (subtitle_content->subtitle_x_scale() - 1);
580 i->sub.rectangle.y -= i->sub.rectangle.height * (subtitle_content->subtitle_y_scale() - 1);
582 ps.image.push_back (i->sub);
585 list<ContentTextSubtitle> text = subtitle_decoder->get_text_subtitles (ContentTimePeriod (from, to), starting);
586 BOOST_FOREACH (ContentTextSubtitle& ts, text) {
587 BOOST_FOREACH (dcp::SubtitleString& s, ts.subs) {
588 s.set_v_position (s.v_position() + subtitle_content->subtitle_y_offset ());
589 s.set_size (s.size() * max (subtitle_content->subtitle_x_scale(), subtitle_content->subtitle_y_scale()));
590 ps.text.push_back (s);
598 list<shared_ptr<Font> >
599 Player::get_subtitle_fonts ()
601 if (!_have_valid_pieces) {
605 list<shared_ptr<Font> > fonts;
606 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
607 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (p->content);
609 /* XXX: things may go wrong if there are duplicate font IDs
610 with different font files.
612 list<shared_ptr<Font> > f = sc->fonts ();
613 copy (f.begin(), f.end(), back_inserter (fonts));
620 /** Set this player never to produce any video data */
622 Player::set_ignore_video ()
624 _ignore_video = true;