2 Copyright (C) 2013-2020 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
23 #include "audio_buffers.h"
24 #include "content_audio.h"
25 #include "dcp_content.h"
28 #include "raw_image_proxy.h"
31 #include "render_text.h"
33 #include "content_video.h"
34 #include "player_video.h"
35 #include "frame_rate_change.h"
36 #include "audio_processor.h"
38 #include "referenced_reel_asset.h"
39 #include "decoder_factory.h"
41 #include "video_decoder.h"
42 #include "audio_decoder.h"
43 #include "text_content.h"
44 #include "text_decoder.h"
45 #include "ffmpeg_content.h"
46 #include "audio_content.h"
47 #include "dcp_decoder.h"
48 #include "image_decoder.h"
49 #include "compose.hpp"
53 #include <dcp/reel_sound_asset.h>
54 #include <dcp/reel_subtitle_asset.h>
55 #include <dcp/reel_picture_asset.h>
56 #include <dcp/reel_closed_caption_asset.h>
57 #include <boost/foreach.hpp>
74 using boost::shared_ptr;
75 using boost::weak_ptr;
76 using boost::dynamic_pointer_cast;
77 using boost::optional;
78 using boost::scoped_ptr;
79 using namespace dcpomatic;
81 int const PlayerProperty::VIDEO_CONTAINER_SIZE = 700;
82 int const PlayerProperty::PLAYLIST = 701;
83 int const PlayerProperty::FILM_CONTAINER = 702;
84 int const PlayerProperty::FILM_VIDEO_FRAME_RATE = 703;
85 int const PlayerProperty::DCP_DECODE_REDUCTION = 704;
87 Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist)
89 , _playlist (playlist)
91 , _ignore_video (false)
92 , _ignore_audio (false)
93 , _ignore_text (false)
94 , _always_burn_open_subtitles (false)
96 , _tolerant (film->tolerant())
97 , _play_referenced (false)
98 , _audio_merger (_film->audio_frame_rate())
101 _film_changed_connection = _film->Change.connect (bind (&Player::film_change, this, _1, _2));
102 /* The butler must hear about this first, so since we are proxying this through to the butler we must
105 _playlist_change_connection = _playlist->Change.connect (bind (&Player::playlist_change, this, _1), boost::signals2::at_front);
106 _playlist_content_change_connection = _playlist->ContentChange.connect (bind(&Player::playlist_content_change, this, _1, _3, _4));
107 set_video_container_size (_film->frame_size ());
109 film_change (CHANGE_TYPE_DONE, Film::AUDIO_PROCESSOR);
112 seek (DCPTime (), true);
121 Player::setup_pieces ()
123 boost::mutex::scoped_lock lm (_mutex);
124 setup_pieces_unlocked ();
128 have_video (shared_ptr<Piece> piece)
130 return piece->decoder && piece->decoder->video;
134 have_audio (shared_ptr<Piece> piece)
136 return piece->decoder && piece->decoder->audio;
140 Player::setup_pieces_unlocked ()
142 list<shared_ptr<Piece> > old_pieces = _pieces;
146 _shuffler = new Shuffler();
147 _shuffler->Video.connect(bind(&Player::video, this, _1, _2));
149 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
151 if (!i->paths_valid ()) {
155 if (_ignore_video && _ignore_audio && i->text.empty()) {
156 /* We're only interested in text and this content has none */
160 shared_ptr<Decoder> old_decoder;
161 BOOST_FOREACH (shared_ptr<Piece> j, old_pieces) {
162 if (j->content == i) {
163 old_decoder = j->decoder;
168 shared_ptr<Decoder> decoder = decoder_factory (_film, i, _fast, _tolerant, old_decoder);
169 FrameRateChange frc (_film, i);
172 /* Not something that we can decode; e.g. Atmos content */
176 if (decoder->video && _ignore_video) {
177 decoder->video->set_ignore (true);
180 if (decoder->audio && _ignore_audio) {
181 decoder->audio->set_ignore (true);
185 BOOST_FOREACH (shared_ptr<TextDecoder> i, decoder->text) {
186 i->set_ignore (true);
190 shared_ptr<DCPDecoder> dcp = dynamic_pointer_cast<DCPDecoder> (decoder);
192 dcp->set_decode_referenced (_play_referenced);
193 if (_play_referenced) {
194 dcp->set_forced_reduction (_dcp_decode_reduction);
198 shared_ptr<Piece> piece (new Piece (i, decoder, frc));
199 _pieces.push_back (piece);
201 if (decoder->video) {
202 if (i->video->frame_type() == VIDEO_FRAME_TYPE_3D_LEFT || i->video->frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) {
203 /* We need a Shuffler to cope with 3D L/R video data arriving out of sequence */
204 decoder->video->Data.connect (bind (&Shuffler::video, _shuffler, weak_ptr<Piece>(piece), _1));
206 decoder->video->Data.connect (bind (&Player::video, this, weak_ptr<Piece>(piece), _1));
210 if (decoder->audio) {
211 decoder->audio->Data.connect (bind (&Player::audio, this, weak_ptr<Piece> (piece), _1, _2));
214 list<shared_ptr<TextDecoder> >::const_iterator j = decoder->text.begin();
216 while (j != decoder->text.end()) {
217 (*j)->BitmapStart.connect (
218 bind(&Player::bitmap_text_start, this, weak_ptr<Piece>(piece), weak_ptr<const TextContent>((*j)->content()), _1)
220 (*j)->PlainStart.connect (
221 bind(&Player::plain_text_start, this, weak_ptr<Piece>(piece), weak_ptr<const TextContent>((*j)->content()), _1)
224 bind(&Player::subtitle_stop, this, weak_ptr<Piece>(piece), weak_ptr<const TextContent>((*j)->content()), _1)
231 _stream_states.clear ();
232 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
233 if (i->content->audio) {
234 BOOST_FOREACH (AudioStreamPtr j, i->content->audio->streams()) {
235 _stream_states[j] = StreamState (i, i->content->position ());
240 _black = Empty (_film, _pieces, bind(&have_video, _1));
241 _silent = Empty (_film, _pieces, bind(&have_audio, _1));
243 _last_video_time = DCPTime ();
244 _last_video_eyes = EYES_BOTH;
245 _last_audio_time = DCPTime ();
247 /* Cached value to save recalculating it on every ::pass */
248 _film_length = _film->length ();
252 Player::playlist_content_change (ChangeType type, int property, bool frequent)
254 if (type == CHANGE_TYPE_PENDING) {
255 /* The player content is probably about to change, so we can't carry on
256 until that has happened and we've rebuilt our pieces. Stop pass()
257 and seek() from working until then.
260 } else if (type == CHANGE_TYPE_DONE) {
261 /* A change in our content has gone through. Re-build our pieces. */
264 } else if (type == CHANGE_TYPE_CANCELLED) {
268 Change (type, property, frequent);
272 Player::set_video_container_size (dcp::Size s)
274 Change (CHANGE_TYPE_PENDING, PlayerProperty::VIDEO_CONTAINER_SIZE, false);
277 boost::mutex::scoped_lock lm (_mutex);
279 if (s == _video_container_size) {
281 Change (CHANGE_TYPE_CANCELLED, PlayerProperty::VIDEO_CONTAINER_SIZE, false);
285 _video_container_size = s;
287 _black_image.reset (new Image (AV_PIX_FMT_RGB24, _video_container_size, true));
288 _black_image->make_black ();
291 Change (CHANGE_TYPE_DONE, PlayerProperty::VIDEO_CONTAINER_SIZE, false);
295 Player::playlist_change (ChangeType type)
297 if (type == CHANGE_TYPE_DONE) {
300 Change (type, PlayerProperty::PLAYLIST, false);
304 Player::film_change (ChangeType type, Film::Property p)
306 /* Here we should notice Film properties that affect our output, and
307 alert listeners that our output now would be different to how it was
308 last time we were run.
311 if (p == Film::CONTAINER) {
312 Change (type, PlayerProperty::FILM_CONTAINER, false);
313 } else if (p == Film::VIDEO_FRAME_RATE) {
314 /* Pieces contain a FrameRateChange which contains the DCP frame rate,
315 so we need new pieces here.
317 if (type == CHANGE_TYPE_DONE) {
320 Change (type, PlayerProperty::FILM_VIDEO_FRAME_RATE, false);
321 } else if (p == Film::AUDIO_PROCESSOR) {
322 if (type == CHANGE_TYPE_DONE && _film->audio_processor ()) {
323 boost::mutex::scoped_lock lm (_mutex);
324 _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
326 } else if (p == Film::AUDIO_CHANNELS) {
327 if (type == CHANGE_TYPE_DONE) {
328 boost::mutex::scoped_lock lm (_mutex);
329 _audio_merger.clear ();
334 shared_ptr<PlayerVideo>
335 Player::black_player_video_frame (Eyes eyes) const
337 return shared_ptr<PlayerVideo> (
339 shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
342 _video_container_size,
343 _video_container_size,
346 PresetColourConversion::all().front().conversion,
348 boost::weak_ptr<Content>(),
349 boost::optional<Frame>(),
356 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
358 DCPTime s = t - piece->content->position ();
359 s = min (piece->content->length_after_trim(_film), s);
360 s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc));
362 /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
363 then convert that ContentTime to frames at the content's rate. However this fails for
364 situations like content at 29.9978733fps, DCP at 30fps. The accuracy of the Time type is not
365 enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
367 Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
369 return s.frames_floor (piece->frc.dcp) / piece->frc.factor ();
373 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
375 /* See comment in dcp_to_content_video */
376 DCPTime const d = DCPTime::from_frames (f * piece->frc.factor(), piece->frc.dcp) - DCPTime(piece->content->trim_start(), piece->frc);
377 return d + piece->content->position();
381 Player::dcp_to_resampled_audio (shared_ptr<const Piece> piece, DCPTime t) const
383 DCPTime s = t - piece->content->position ();
384 s = min (piece->content->length_after_trim(_film), s);
385 /* See notes in dcp_to_content_video */
386 return max (DCPTime (), DCPTime (piece->content->trim_start (), piece->frc) + s).frames_floor (_film->audio_frame_rate ());
390 Player::resampled_audio_to_dcp (shared_ptr<const Piece> piece, Frame f) const
392 /* See comment in dcp_to_content_video */
393 return DCPTime::from_frames (f, _film->audio_frame_rate())
394 - DCPTime (piece->content->trim_start(), piece->frc)
395 + piece->content->position();
399 Player::dcp_to_content_time (shared_ptr<const Piece> piece, DCPTime t) const
401 DCPTime s = t - piece->content->position ();
402 s = min (piece->content->length_after_trim(_film), s);
403 return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start());
407 Player::content_time_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
409 return max (DCPTime (), DCPTime (t - piece->content->trim_start(), piece->frc) + piece->content->position());
412 list<shared_ptr<Font> >
413 Player::get_subtitle_fonts ()
415 boost::mutex::scoped_lock lm (_mutex);
417 list<shared_ptr<Font> > fonts;
418 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
419 BOOST_FOREACH (shared_ptr<TextContent> j, i->content->text) {
420 /* XXX: things may go wrong if there are duplicate font IDs
421 with different font files.
423 list<shared_ptr<Font> > f = j->fonts ();
424 copy (f.begin(), f.end(), back_inserter (fonts));
431 /** Set this player never to produce any video data */
433 Player::set_ignore_video ()
435 boost::mutex::scoped_lock lm (_mutex);
436 _ignore_video = true;
437 setup_pieces_unlocked ();
441 Player::set_ignore_audio ()
443 boost::mutex::scoped_lock lm (_mutex);
444 _ignore_audio = true;
445 setup_pieces_unlocked ();
449 Player::set_ignore_text ()
451 boost::mutex::scoped_lock lm (_mutex);
453 setup_pieces_unlocked ();
456 /** Set the player to always burn open texts into the image regardless of the content settings */
458 Player::set_always_burn_open_subtitles ()
460 boost::mutex::scoped_lock lm (_mutex);
461 _always_burn_open_subtitles = true;
464 /** Sets up the player to be faster, possibly at the expense of quality */
468 boost::mutex::scoped_lock lm (_mutex);
470 setup_pieces_unlocked ();
474 Player::set_play_referenced ()
476 boost::mutex::scoped_lock lm (_mutex);
477 _play_referenced = true;
478 setup_pieces_unlocked ();
482 maybe_add_asset (list<ReferencedReelAsset>& a, shared_ptr<dcp::ReelAsset> r, Frame reel_trim_start, Frame reel_trim_end, DCPTime from, int const ffr)
484 DCPOMATIC_ASSERT (r);
485 r->set_entry_point (r->entry_point().get_value_or(0) + reel_trim_start);
486 r->set_duration (r->actual_duration() - reel_trim_start - reel_trim_end);
487 if (r->actual_duration() > 0) {
489 ReferencedReelAsset(r, DCPTimePeriod(from, from + DCPTime::from_frames(r->actual_duration(), ffr)))
494 list<ReferencedReelAsset>
495 Player::get_reel_assets ()
497 /* Does not require a lock on _mutex as it's only called from DCPEncoder */
499 list<ReferencedReelAsset> a;
501 BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
502 shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i);
507 scoped_ptr<DCPDecoder> decoder;
509 decoder.reset (new DCPDecoder (_film, j, false, false, shared_ptr<DCPDecoder>()));
514 DCPOMATIC_ASSERT (j->video_frame_rate ());
515 double const cfr = j->video_frame_rate().get();
516 Frame const trim_start = j->trim_start().frames_round (cfr);
517 Frame const trim_end = j->trim_end().frames_round (cfr);
518 int const ffr = _film->video_frame_rate ();
520 /* position in the asset from the start */
521 int64_t offset_from_start = 0;
522 /* position in the asset from the end */
523 int64_t offset_from_end = 0;
524 BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
525 /* Assume that main picture duration is the length of the reel */
526 offset_from_end += k->main_picture()->actual_duration();
529 BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
531 /* Assume that main picture duration is the length of the reel */
532 int64_t const reel_duration = k->main_picture()->actual_duration();
534 /* See doc/design/trim_reels.svg */
535 Frame const reel_trim_start = min(reel_duration, max(int64_t(0), trim_start - offset_from_start));
536 Frame const reel_trim_end = min(reel_duration, max(int64_t(0), reel_duration - (offset_from_end - trim_end)));
538 DCPTime const from = i->position() + DCPTime::from_frames (offset_from_start, _film->video_frame_rate());
539 if (j->reference_video ()) {
540 maybe_add_asset (a, k->main_picture(), reel_trim_start, reel_trim_end, from, ffr);
543 if (j->reference_audio ()) {
544 maybe_add_asset (a, k->main_sound(), reel_trim_start, reel_trim_end, from, ffr);
547 if (j->reference_text (TEXT_OPEN_SUBTITLE)) {
548 maybe_add_asset (a, k->main_subtitle(), reel_trim_start, reel_trim_end, from, ffr);
551 if (j->reference_text (TEXT_CLOSED_CAPTION)) {
552 BOOST_FOREACH (shared_ptr<dcp::ReelClosedCaptionAsset> l, k->closed_captions()) {
553 maybe_add_asset (a, l, reel_trim_start, reel_trim_end, from, ffr);
557 offset_from_start += reel_duration;
558 offset_from_end -= reel_duration;
568 boost::mutex::scoped_lock lm (_mutex);
569 DCPOMATIC_ASSERT (_film_length);
572 /* We can't pass in this state */
576 if (*_film_length == DCPTime()) {
577 /* Special case of an empty Film; just give one black frame */
578 emit_video (black_player_video_frame(EYES_BOTH), DCPTime());
582 /* Find the decoder or empty which is farthest behind where we are and make it emit some data */
584 shared_ptr<Piece> earliest_content;
585 optional<DCPTime> earliest_time;
587 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
592 DCPTime const t = content_time_to_dcp (i, max(i->decoder->position(), i->content->trim_start()));
593 if (t > i->content->end(_film)) {
597 /* Given two choices at the same time, pick the one with texts so we see it before
600 if (!earliest_time || t < *earliest_time || (t == *earliest_time && !i->decoder->text.empty())) {
602 earliest_content = i;
616 if (earliest_content) {
620 if (!_black.done() && !_ignore_video && (!earliest_time || _black.position() < *earliest_time)) {
621 earliest_time = _black.position ();
625 if (!_silent.done() && !_ignore_audio && (!earliest_time || _silent.position() < *earliest_time)) {
626 earliest_time = _silent.position ();
633 earliest_content->done = earliest_content->decoder->pass ();
634 shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent>(earliest_content->content);
635 if (dcp && !_play_referenced && dcp->reference_audio()) {
636 /* We are skipping some referenced DCP audio content, so we need to update _last_audio_time
637 to `hide' the fact that no audio was emitted during the referenced DCP (though
638 we need to behave as though it was).
640 _last_audio_time = dcp->end (_film);
645 emit_video (black_player_video_frame(EYES_BOTH), _black.position());
646 _black.set_position (_black.position() + one_video_frame());
650 DCPTimePeriod period (_silent.period_at_position());
651 if (_last_audio_time) {
652 /* Sometimes the thing that happened last finishes fractionally before
653 or after this silence. Bodge the start time of the silence to fix it.
654 I think this is nothing to worry about since we will just add or
655 remove a little silence at the end of some content.
657 int64_t const error = labs(period.from.get() - _last_audio_time->get());
658 /* Let's not worry about less than a frame at 24fps */
659 int64_t const too_much_error = DCPTime::from_frames(1, 24).get();
660 if (error >= too_much_error) {
661 _film->log()->log(String::compose("Silence starting before or after last audio by %1", error), LogEntry::TYPE_ERROR);
663 DCPOMATIC_ASSERT (error < too_much_error);
664 period.from = *_last_audio_time;
666 if (period.duration() > one_video_frame()) {
667 period.to = period.from + one_video_frame();
670 _silent.set_position (period.to);
678 /* Emit any audio that is ready */
680 /* Work out the time before which the audio is definitely all here. This is the earliest last_push_end of one
681 of our streams, or the position of the _silent.
683 DCPTime pull_to = *_film_length;
684 for (map<AudioStreamPtr, StreamState>::const_iterator i = _stream_states.begin(); i != _stream_states.end(); ++i) {
685 if (!i->second.piece->done && i->second.last_push_end < pull_to) {
686 pull_to = i->second.last_push_end;
689 if (!_silent.done() && _silent.position() < pull_to) {
690 pull_to = _silent.position();
693 list<pair<shared_ptr<AudioBuffers>, DCPTime> > audio = _audio_merger.pull (pull_to);
694 for (list<pair<shared_ptr<AudioBuffers>, DCPTime> >::iterator i = audio.begin(); i != audio.end(); ++i) {
695 if (_last_audio_time && i->second < *_last_audio_time) {
696 /* This new data comes before the last we emitted (or the last seek); discard it */
697 pair<shared_ptr<AudioBuffers>, DCPTime> cut = discard_audio (i->first, i->second, *_last_audio_time);
702 } else if (_last_audio_time && i->second > *_last_audio_time) {
703 /* There's a gap between this data and the last we emitted; fill with silence */
704 fill_audio (DCPTimePeriod (*_last_audio_time, i->second));
707 emit_audio (i->first, i->second);
712 for (list<pair<shared_ptr<PlayerVideo>, DCPTime> >::const_iterator i = _delay.begin(); i != _delay.end(); ++i) {
713 do_emit_video(i->first, i->second);
720 /** @return Open subtitles for the frame at the given time, converted to images */
721 optional<PositionImage>
722 Player::open_subtitles_for_frame (DCPTime time) const
724 list<PositionImage> captions;
725 int const vfr = _film->video_frame_rate();
729 _active_texts[TEXT_OPEN_SUBTITLE].get_burnt(DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), _always_burn_open_subtitles)
732 /* Bitmap subtitles */
733 BOOST_FOREACH (BitmapText i, j.bitmap) {
738 /* i.image will already have been scaled to fit _video_container_size */
739 dcp::Size scaled_size (i.rectangle.width * _video_container_size.width, i.rectangle.height * _video_container_size.height);
745 lrint (_video_container_size.width * i.rectangle.x),
746 lrint (_video_container_size.height * i.rectangle.y)
752 /* String subtitles (rendered to an image) */
753 if (!j.string.empty ()) {
754 list<PositionImage> s = render_text (j.string, j.fonts, _video_container_size, time, vfr);
755 copy (s.begin(), s.end(), back_inserter (captions));
759 if (captions.empty ()) {
760 return optional<PositionImage> ();
763 return merge (captions);
767 Player::video (weak_ptr<Piece> wp, ContentVideo video)
769 shared_ptr<Piece> piece = wp.lock ();
774 FrameRateChange frc (_film, piece->content);
775 if (frc.skip && (video.frame % 2) == 1) {
779 /* Time of the first frame we will emit */
780 DCPTime const time = content_video_to_dcp (piece, video.frame);
782 /* Discard if it's before the content's period or the last accurate seek. We can't discard
783 if it's after the content's period here as in that case we still need to fill any gap between
784 `now' and the end of the content's period.
786 if (time < piece->content->position() || (_last_video_time && time < *_last_video_time)) {
790 /* Fill gaps that we discover now that we have some video which needs to be emitted.
791 This is where we need to fill to.
793 DCPTime fill_to = min (time, piece->content->end(_film));
795 if (_last_video_time) {
796 DCPTime fill_from = max (*_last_video_time, piece->content->position());
798 /* Fill if we have more than half a frame to do */
799 if ((fill_to - fill_from) > one_video_frame() / 2) {
800 LastVideoMap::const_iterator last = _last_video.find (wp);
801 if (_film->three_d()) {
802 Eyes fill_to_eyes = video.eyes;
803 if (fill_to_eyes == EYES_BOTH) {
804 fill_to_eyes = EYES_LEFT;
806 if (fill_to == piece->content->end(_film)) {
807 /* Don't fill after the end of the content */
808 fill_to_eyes = EYES_LEFT;
810 DCPTime j = fill_from;
811 Eyes eyes = _last_video_eyes.get_value_or(EYES_LEFT);
812 if (eyes == EYES_BOTH) {
815 while (j < fill_to || eyes != fill_to_eyes) {
816 if (last != _last_video.end()) {
817 shared_ptr<PlayerVideo> copy = last->second->shallow_copy();
818 copy->set_eyes (eyes);
819 emit_video (copy, j);
821 emit_video (black_player_video_frame(eyes), j);
823 if (eyes == EYES_RIGHT) {
824 j += one_video_frame();
826 eyes = increment_eyes (eyes);
829 for (DCPTime j = fill_from; j < fill_to; j += one_video_frame()) {
830 if (last != _last_video.end()) {
831 emit_video (last->second, j);
833 emit_video (black_player_video_frame(EYES_BOTH), j);
840 _last_video[wp].reset (
843 piece->content->video->crop (),
844 piece->content->video->fade (_film, video.frame),
845 piece->content->video->scale().size (
846 piece->content->video, _video_container_size, _film->frame_size ()
848 _video_container_size,
851 piece->content->video->colour_conversion(),
852 piece->content->video->range(),
860 for (int i = 0; i < frc.repeat; ++i) {
861 if (t < piece->content->end(_film)) {
862 emit_video (_last_video[wp], t);
864 t += one_video_frame ();
869 Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_audio)
871 DCPOMATIC_ASSERT (content_audio.audio->frames() > 0);
873 shared_ptr<Piece> piece = wp.lock ();
878 shared_ptr<AudioContent> content = piece->content->audio;
879 DCPOMATIC_ASSERT (content);
881 int const rfr = content->resampled_frame_rate (_film);
883 /* Compute time in the DCP */
884 DCPTime time = resampled_audio_to_dcp (piece, content_audio.frame);
885 /* And the end of this block in the DCP */
886 DCPTime end = time + DCPTime::from_frames(content_audio.audio->frames(), rfr);
888 /* Remove anything that comes before the start or after the end of the content */
889 if (time < piece->content->position()) {
890 pair<shared_ptr<AudioBuffers>, DCPTime> cut = discard_audio (content_audio.audio, time, piece->content->position());
892 /* This audio is entirely discarded */
895 content_audio.audio = cut.first;
897 } else if (time > piece->content->end(_film)) {
900 } else if (end > piece->content->end(_film)) {
901 Frame const remaining_frames = DCPTime(piece->content->end(_film) - time).frames_round(rfr);
902 if (remaining_frames == 0) {
905 content_audio.audio.reset (new AudioBuffers(content_audio.audio, remaining_frames, 0));
908 DCPOMATIC_ASSERT (content_audio.audio->frames() > 0);
912 if (content->gain() != 0) {
913 shared_ptr<AudioBuffers> gain (new AudioBuffers (content_audio.audio));
914 gain->apply_gain (content->gain ());
915 content_audio.audio = gain;
920 content_audio.audio = remap (content_audio.audio, _film->audio_channels(), stream->mapping());
924 if (_audio_processor) {
925 content_audio.audio = _audio_processor->run (content_audio.audio, _film->audio_channels ());
930 _audio_merger.push (content_audio.audio, time);
931 DCPOMATIC_ASSERT (_stream_states.find (stream) != _stream_states.end ());
932 _stream_states[stream].last_push_end = time + DCPTime::from_frames (content_audio.audio->frames(), _film->audio_frame_rate());
936 Player::bitmap_text_start (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, ContentBitmapText subtitle)
938 shared_ptr<Piece> piece = wp.lock ();
939 shared_ptr<const TextContent> text = wc.lock ();
940 if (!piece || !text) {
944 /* Apply content's subtitle offsets */
945 subtitle.sub.rectangle.x += text->x_offset ();
946 subtitle.sub.rectangle.y += text->y_offset ();
948 /* Apply a corrective translation to keep the subtitle centred after the scale that is coming up */
949 subtitle.sub.rectangle.x -= subtitle.sub.rectangle.width * ((text->x_scale() - 1) / 2);
950 subtitle.sub.rectangle.y -= subtitle.sub.rectangle.height * ((text->y_scale() - 1) / 2);
952 /* Apply content's subtitle scale */
953 subtitle.sub.rectangle.width *= text->x_scale ();
954 subtitle.sub.rectangle.height *= text->y_scale ();
957 shared_ptr<Image> image = subtitle.sub.image;
959 /* We will scale the subtitle up to fit _video_container_size */
960 int const width = subtitle.sub.rectangle.width * _video_container_size.width;
961 int const height = subtitle.sub.rectangle.height * _video_container_size.height;
962 if (width == 0 || height == 0) {
966 dcp::Size scaled_size (width, height);
967 ps.bitmap.push_back (BitmapText(image->scale(scaled_size, dcp::YUV_TO_RGB_REC601, image->pixel_format(), true, _fast), subtitle.sub.rectangle));
968 DCPTime from (content_time_to_dcp (piece, subtitle.from()));
970 _active_texts[text->type()].add_from (wc, ps, from);
974 Player::plain_text_start (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, ContentStringText subtitle)
976 shared_ptr<Piece> piece = wp.lock ();
977 shared_ptr<const TextContent> text = wc.lock ();
978 if (!piece || !text) {
983 DCPTime const from (content_time_to_dcp (piece, subtitle.from()));
985 if (from > piece->content->end(_film)) {
989 BOOST_FOREACH (dcp::SubtitleString s, subtitle.subs) {
990 s.set_h_position (s.h_position() + text->x_offset ());
991 s.set_v_position (s.v_position() + text->y_offset ());
992 float const xs = text->x_scale();
993 float const ys = text->y_scale();
994 float size = s.size();
996 /* Adjust size to express the common part of the scaling;
997 e.g. if xs = ys = 0.5 we scale size by 2.
999 if (xs > 1e-5 && ys > 1e-5) {
1000 size *= 1 / min (1 / xs, 1 / ys);
1004 /* Then express aspect ratio changes */
1005 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
1006 s.set_aspect_adjust (xs / ys);
1009 s.set_in (dcp::Time(from.seconds(), 1000));
1010 ps.string.push_back (StringText (s, text->outline_width()));
1011 ps.add_fonts (text->fonts ());
1014 _active_texts[text->type()].add_from (wc, ps, from);
1018 Player::subtitle_stop (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, ContentTime to)
1020 shared_ptr<const TextContent> text = wc.lock ();
1025 if (!_active_texts[text->type()].have(wc)) {
1029 shared_ptr<Piece> piece = wp.lock ();
1034 DCPTime const dcp_to = content_time_to_dcp (piece, to);
1036 if (dcp_to > piece->content->end(_film)) {
1040 pair<PlayerText, DCPTime> from = _active_texts[text->type()].add_to (wc, dcp_to);
1042 bool const always = (text->type() == TEXT_OPEN_SUBTITLE && _always_burn_open_subtitles);
1043 if (text->use() && !always && !text->burn()) {
1044 Text (from.first, text->type(), text->dcp_track().get_value_or(DCPTextTrack()), DCPTimePeriod (from.second, dcp_to));
1049 Player::seek (DCPTime time, bool accurate)
1051 boost::mutex::scoped_lock lm (_mutex);
1054 /* We can't seek in this state */
1059 _shuffler->clear ();
1064 if (_audio_processor) {
1065 _audio_processor->flush ();
1068 _audio_merger.clear ();
1069 for (int i = 0; i < TEXT_COUNT; ++i) {
1070 _active_texts[i].clear ();
1073 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
1074 if (time < i->content->position()) {
1075 /* Before; seek to the start of the content. Even if this request is for an inaccurate seek
1076 we must seek this (following) content accurately, otherwise when we come to the end of the current
1077 content we may not start right at the beginning of the next, causing a gap (if the next content has
1078 been trimmed to a point between keyframes, or something).
1080 i->decoder->seek (dcp_to_content_time (i, i->content->position()), true);
1082 } else if (i->content->position() <= time && time < i->content->end(_film)) {
1083 /* During; seek to position */
1084 i->decoder->seek (dcp_to_content_time (i, time), accurate);
1087 /* After; this piece is done */
1093 _last_video_time = time;
1094 _last_video_eyes = EYES_LEFT;
1095 _last_audio_time = time;
1097 _last_video_time = optional<DCPTime>();
1098 _last_video_eyes = optional<Eyes>();
1099 _last_audio_time = optional<DCPTime>();
1102 _black.set_position (time);
1103 _silent.set_position (time);
1105 _last_video.clear ();
1109 Player::emit_video (shared_ptr<PlayerVideo> pv, DCPTime time)
1111 /* We need a delay to give a little wiggle room to ensure that relevent subtitles arrive at the
1112 player before the video that requires them.
1114 _delay.push_back (make_pair (pv, time));
1116 if (pv->eyes() == EYES_BOTH || pv->eyes() == EYES_RIGHT) {
1117 _last_video_time = time + one_video_frame();
1119 _last_video_eyes = increment_eyes (pv->eyes());
1121 if (_delay.size() < 3) {
1125 pair<shared_ptr<PlayerVideo>, DCPTime> to_do = _delay.front();
1127 do_emit_video (to_do.first, to_do.second);
1131 Player::do_emit_video (shared_ptr<PlayerVideo> pv, DCPTime time)
1133 if (pv->eyes() == EYES_BOTH || pv->eyes() == EYES_RIGHT) {
1134 for (int i = 0; i < TEXT_COUNT; ++i) {
1135 _active_texts[i].clear_before (time);
1139 optional<PositionImage> subtitles = open_subtitles_for_frame (time);
1141 pv->set_text (subtitles.get ());
1148 Player::emit_audio (shared_ptr<AudioBuffers> data, DCPTime time)
1150 /* Log if the assert below is about to fail */
1151 if (_last_audio_time && labs(time.get() - _last_audio_time->get()) > 1) {
1152 _film->log()->log(String::compose("Out-of-sequence emit %1 vs %2", to_string(time), to_string(*_last_audio_time)), LogEntry::TYPE_WARNING);
1155 /* This audio must follow on from the previous, allowing for half a sample (at 48kHz) leeway */
1156 DCPOMATIC_ASSERT (!_last_audio_time || labs(time.get() - _last_audio_time->get()) < 2);
1157 Audio (data, time, _film->audio_frame_rate());
1158 _last_audio_time = time + DCPTime::from_frames (data->frames(), _film->audio_frame_rate());
1162 Player::fill_audio (DCPTimePeriod period)
1164 if (period.from == period.to) {
1168 DCPOMATIC_ASSERT (period.from < period.to);
1170 DCPTime t = period.from;
1171 while (t < period.to) {
1172 DCPTime block = min (DCPTime::from_seconds (0.5), period.to - t);
1173 Frame const samples = block.frames_round(_film->audio_frame_rate());
1175 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), samples));
1176 silence->make_silent ();
1177 emit_audio (silence, t);
1184 Player::one_video_frame () const
1186 return DCPTime::from_frames (1, _film->video_frame_rate ());
1189 pair<shared_ptr<AudioBuffers>, DCPTime>
1190 Player::discard_audio (shared_ptr<const AudioBuffers> audio, DCPTime time, DCPTime discard_to) const
1192 DCPTime const discard_time = discard_to - time;
1193 Frame const discard_frames = discard_time.frames_round(_film->audio_frame_rate());
1194 Frame remaining_frames = audio->frames() - discard_frames;
1195 if (remaining_frames <= 0) {
1196 return make_pair(shared_ptr<AudioBuffers>(), DCPTime());
1198 shared_ptr<AudioBuffers> cut (new AudioBuffers(audio, remaining_frames, discard_frames));
1199 return make_pair(cut, time + discard_time);
1203 Player::set_dcp_decode_reduction (optional<int> reduction)
1205 Change (CHANGE_TYPE_PENDING, PlayerProperty::DCP_DECODE_REDUCTION, false);
1208 boost::mutex::scoped_lock lm (_mutex);
1210 if (reduction == _dcp_decode_reduction) {
1212 Change (CHANGE_TYPE_CANCELLED, PlayerProperty::DCP_DECODE_REDUCTION, false);
1216 _dcp_decode_reduction = reduction;
1217 setup_pieces_unlocked ();
1220 Change (CHANGE_TYPE_DONE, PlayerProperty::DCP_DECODE_REDUCTION, false);
1224 Player::content_time_to_dcp (shared_ptr<Content> content, ContentTime t)
1226 boost::mutex::scoped_lock lm (_mutex);
1228 BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
1229 if (i->content == content) {
1230 return content_time_to_dcp (i, t);
1234 /* We couldn't find this content; perhaps things are being changed over */
1235 return optional<DCPTime>();