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"
39 #include "render_subtitles.h"
41 #include "content_video.h"
42 #include "player_video.h"
43 #include "frame_rate_change.h"
44 #include "dcp_content.h"
45 #include "dcp_decoder.h"
46 #include "dcp_subtitle_content.h"
47 #include "dcp_subtitle_decoder.h"
48 #include <boost/foreach.hpp>
54 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
65 using boost::shared_ptr;
66 using boost::weak_ptr;
67 using boost::dynamic_pointer_cast;
68 using boost::optional;
70 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
73 , _have_valid_pieces (false)
74 , _ignore_video (false)
76 _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
77 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
78 _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
79 set_video_container_size (_film->frame_size ());
83 Player::setup_pieces ()
85 list<shared_ptr<Piece> > old_pieces = _pieces;
88 ContentList content = _playlist->content ();
90 for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
92 if (!(*i)->paths_valid ()) {
96 shared_ptr<Decoder> decoder;
97 optional<FrameRateChange> frc;
99 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
100 DCPTime best_overlap_t;
101 shared_ptr<VideoContent> best_overlap;
102 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
103 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
108 DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
109 if (overlap > best_overlap_t) {
111 best_overlap_t = overlap;
115 optional<FrameRateChange> best_overlap_frc;
117 best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
119 /* No video overlap; e.g. if the DCP is just audio */
120 best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
124 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
126 decoder.reset (new FFmpegDecoder (fc, _film->log()));
127 frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
130 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (*i);
132 decoder.reset (new DCPDecoder (dc));
133 frc = FrameRateChange (dc->video_frame_rate(), _film->video_frame_rate());
137 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
139 /* See if we can re-use an old ImageDecoder */
140 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
141 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
142 if (imd && imd->content() == ic) {
148 decoder.reset (new ImageDecoder (ic));
151 frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
155 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
157 decoder.reset (new SndfileDecoder (sc));
158 frc = best_overlap_frc;
162 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
164 decoder.reset (new SubRipDecoder (rc));
165 frc = best_overlap_frc;
168 /* DCPSubtitleContent */
169 shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (*i);
171 decoder.reset (new DCPSubtitleDecoder (dsc));
172 frc = best_overlap_frc;
175 shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> (decoder);
176 if (vd && _ignore_video) {
177 vd->set_ignore_video ();
180 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
183 _have_valid_pieces = true;
187 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
189 shared_ptr<Content> c = w.lock ();
195 property == ContentProperty::POSITION ||
196 property == ContentProperty::LENGTH ||
197 property == ContentProperty::TRIM_START ||
198 property == ContentProperty::TRIM_END ||
199 property == ContentProperty::PATH ||
200 property == VideoContentProperty::VIDEO_FRAME_TYPE ||
201 property == DCPContentProperty::CAN_BE_PLAYED
204 _have_valid_pieces = false;
208 property == SubtitleContentProperty::USE_SUBTITLES ||
209 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
210 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
211 property == SubtitleContentProperty::SUBTITLE_X_SCALE ||
212 property == SubtitleContentProperty::SUBTITLE_Y_SCALE ||
213 property == VideoContentProperty::VIDEO_CROP ||
214 property == VideoContentProperty::VIDEO_SCALE ||
215 property == VideoContentProperty::VIDEO_FRAME_RATE ||
216 property == VideoContentProperty::VIDEO_FADE_IN ||
217 property == VideoContentProperty::VIDEO_FADE_OUT
225 Player::playlist_changed ()
227 _have_valid_pieces = false;
232 Player::set_video_container_size (dcp::Size s)
234 _video_container_size = s;
236 _black_image.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
237 _black_image->make_black ();
241 Player::film_changed (Film::Property p)
243 /* Here we should notice Film properties that affect our output, and
244 alert listeners that our output now would be different to how it was
245 last time we were run.
248 if (p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
254 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
256 list<PositionImage> all;
258 for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
263 /* We will scale the subtitle up to fit _video_container_size */
264 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
266 /* Then we need a corrective translation, consisting of two parts:
268 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
269 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
271 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
272 * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
273 * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
275 * Combining these two translations gives these expressions.
282 dcp::YUV_TO_RGB_REC601,
283 i->image->pixel_format (),
287 rint (_video_container_size.width * i->rectangle.x),
288 rint (_video_container_size.height * i->rectangle.y)
297 shared_ptr<PlayerVideo>
298 Player::black_player_video_frame (DCPTime time) const
300 return shared_ptr<PlayerVideo> (
302 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
306 _video_container_size,
307 _video_container_size,
310 Config::instance()->colour_conversions().front().conversion
315 /** @return All PlayerVideos at the given time (there may be two frames for 3D) */
316 list<shared_ptr<PlayerVideo> >
317 Player::get_video (DCPTime time, bool accurate)
319 if (!_have_valid_pieces) {
323 list<shared_ptr<Piece> > ov = overlaps<VideoContent> (
325 time + DCPTime::from_frames (1, _film->video_frame_rate ())
328 list<shared_ptr<PlayerVideo> > pvf;
331 /* No video content at this time */
332 pvf.push_back (black_player_video_frame (time));
334 /* Create a PlayerVideo from the content's video at this time */
336 shared_ptr<Piece> piece = ov.back ();
337 shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
338 DCPOMATIC_ASSERT (decoder);
339 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
340 DCPOMATIC_ASSERT (content);
342 list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate);
343 if (content_video.empty ()) {
344 pvf.push_back (black_player_video_frame (time));
348 dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size ());
350 for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
352 shared_ptr<PlayerVideo> (
355 content_video_to_dcp (piece, i->frame),
357 content->fade (i->frame),
359 _video_container_size,
362 content->colour_conversion ()
369 /* Add subtitles (for possible burn-in) to whatever PlayerVideos we got */
371 PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false);
373 list<PositionImage> sub_images;
375 /* Image subtitles */
376 list<PositionImage> c = transform_image_subtitles (ps.image);
377 copy (c.begin(), c.end(), back_inserter (sub_images));
379 /* Text subtitles (rendered to an image) */
380 if (!ps.text.empty ()) {
381 sub_images.push_back (render_subtitles (ps.text, _video_container_size));
384 if (!sub_images.empty ()) {
385 for (list<shared_ptr<PlayerVideo> >::const_iterator i = pvf.begin(); i != pvf.end(); ++i) {
386 (*i)->set_subtitle (merge (sub_images));
393 shared_ptr<AudioBuffers>
394 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
396 if (!_have_valid_pieces) {
400 AudioFrame const length_frames = length.frames (_film->audio_frame_rate ());
402 shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
403 audio->make_silent ();
405 list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time, time + length);
410 for (list<shared_ptr<Piece> >::iterator i = ov.begin(); i != ov.end(); ++i) {
412 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> ((*i)->content);
413 DCPOMATIC_ASSERT (content);
414 shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
415 DCPOMATIC_ASSERT (decoder);
417 if (content->audio_frame_rate() == 0) {
418 /* This AudioContent has no audio (e.g. if it is an FFmpegContent with no
424 /* The time that we should request from the content */
425 DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
426 AudioFrame request_frames = length_frames;
428 if (request < DCPTime ()) {
429 /* We went off the start of the content, so we will need to offset
430 the stuff we get back.
433 request_frames += request.frames (_film->audio_frame_rate ());
434 if (request_frames < 0) {
437 request = DCPTime ();
440 AudioFrame const content_frame = dcp_to_content_audio (*i, request);
442 /* Audio from this piece's decoder (which might be more or less than what we asked for) */
443 shared_ptr<ContentAudio> all = decoder->get_audio (content_frame, request_frames, accurate);
446 if (content->audio_gain() != 0) {
447 shared_ptr<AudioBuffers> gain (new AudioBuffers (all->audio));
448 gain->apply_gain (content->audio_gain ());
453 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all->audio->frames()));
454 dcp_mapped->make_silent ();
455 AudioMapping map = content->audio_mapping ();
456 for (int i = 0; i < map.content_channels(); ++i) {
457 for (int j = 0; j < _film->audio_channels(); ++j) {
458 if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
459 dcp_mapped->accumulate_channel (
463 map.get (i, static_cast<dcp::Channel> (j))
469 all->audio = dcp_mapped;
471 audio->accumulate_frames (
473 content_frame - all->frame,
474 offset.frames (_film->audio_frame_rate()),
475 min (AudioFrame (all->audio->frames()), request_frames)
483 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
485 /* s is the offset of t from the start position of this content */
486 DCPTime s = t - piece->content->position ();
487 s = DCPTime (max (DCPTime::Type (0), s.get ()));
488 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
490 /* Convert this to the content frame */
491 return DCPTime (s + piece->content->trim_start()).frames (_film->video_frame_rate()) / piece->frc.factor ();
495 Player::content_video_to_dcp (shared_ptr<const Piece> piece, VideoFrame f) const
497 DCPTime t = DCPTime::from_frames (f * piece->frc.factor (), _film->video_frame_rate()) - piece->content->trim_start () + piece->content->position ();
498 if (t < DCPTime ()) {
506 Player::dcp_to_content_audio (shared_ptr<const Piece> piece, DCPTime t) const
508 /* s is the offset of t from the start position of this content */
509 DCPTime s = t - piece->content->position ();
510 s = DCPTime (max (DCPTime::Type (0), s.get ()));
511 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
513 /* Convert this to the content frame */
514 return DCPTime (s + piece->content->trim_start()).frames (_film->audio_frame_rate());
518 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
520 /* s is the offset of t from the start position of this content */
521 DCPTime s = t - piece->content->position ();
522 s = DCPTime (max (DCPTime::Type (0), s.get ()));
523 s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
525 return ContentTime (s + piece->content->trim_start(), piece->frc);
529 PlayerStatistics::dump (shared_ptr<Log> log) const
531 log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat), Log::TYPE_GENERAL);
532 log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence.seconds()), Log::TYPE_GENERAL);
535 PlayerStatistics const &
536 Player::statistics () const
542 Player::get_subtitles (DCPTime time, DCPTime length, bool starting)
544 list<shared_ptr<Piece> > subs = overlaps<SubtitleContent> (time, time + length);
546 PlayerSubtitles ps (time, length);
548 for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
549 shared_ptr<SubtitleContent> subtitle_content = dynamic_pointer_cast<SubtitleContent> ((*j)->content);
550 if (!subtitle_content->use_subtitles ()) {
554 shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*j)->decoder);
555 ContentTime const from = dcp_to_content_subtitle (*j, time);
556 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
557 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
559 list<ContentImageSubtitle> image = subtitle_decoder->get_image_subtitles (ContentTimePeriod (from, to), starting);
560 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
562 /* Apply content's subtitle offsets */
563 i->sub.rectangle.x += subtitle_content->subtitle_x_offset ();
564 i->sub.rectangle.y += subtitle_content->subtitle_y_offset ();
566 /* Apply content's subtitle scale */
567 i->sub.rectangle.width *= subtitle_content->subtitle_x_scale ();
568 i->sub.rectangle.height *= subtitle_content->subtitle_y_scale ();
570 /* Apply a corrective translation to keep the subtitle centred after that scale */
571 i->sub.rectangle.x -= i->sub.rectangle.width * (subtitle_content->subtitle_x_scale() - 1);
572 i->sub.rectangle.y -= i->sub.rectangle.height * (subtitle_content->subtitle_y_scale() - 1);
574 ps.image.push_back (i->sub);
577 list<ContentTextSubtitle> text = subtitle_decoder->get_text_subtitles (ContentTimePeriod (from, to), starting);
578 BOOST_FOREACH (ContentTextSubtitle& ts, text) {
579 BOOST_FOREACH (dcp::SubtitleString& s, ts.subs) {
580 s.set_v_position (s.v_position() + subtitle_content->subtitle_y_offset ());
581 s.set_size (s.size() * max (subtitle_content->subtitle_x_scale(), subtitle_content->subtitle_y_scale()));
582 ps.text.push_back (s);
590 list<shared_ptr<Font> >
591 Player::get_subtitle_fonts ()
593 if (!_have_valid_pieces) {
597 list<shared_ptr<Font> > fonts;
598 BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
599 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (p->content);
601 /* XXX: things may go wrong if there are duplicate font IDs
602 with different font files.
604 list<shared_ptr<Font> > f = sc->fonts ();
605 copy (f.begin(), f.end(), back_inserter (fonts));
612 /** Set this player never to produce any video data */
614 Player::set_ignore_video ()
616 _ignore_video = true;