X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fplayer.cc;h=5951e179db8b9da964762b47efde1d413fce7b5d;hb=ca3343840b28c258c91635cedc133ee064d9d03d;hp=b696f04c196d9cb6c7472448dd77718c4fba768d;hpb=e0255a64d22440d718e5512f34a4f21f0d37a21b;p=dcpomatic.git diff --git a/src/lib/player.cc b/src/lib/player.cc index b696f04c1..5951e179d 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2020 Carl Hetherington + Copyright (C) 2013-2021 Carl Hetherington This file is part of DCP-o-matic. @@ -18,6 +18,7 @@ */ + #include "atmos_decoder.h" #include "player.h" #include "film.h" @@ -62,6 +63,7 @@ #include "i18n.h" + using std::copy; using std::cout; using std::dynamic_pointer_cast; @@ -76,6 +78,7 @@ using std::pair; using std::shared_ptr; using std::vector; using std::weak_ptr; +using std::make_shared; using boost::optional; using boost::scoped_ptr; #if BOOST_VERSION >= 106100 @@ -83,6 +86,7 @@ using namespace boost::placeholders; #endif using namespace dcpomatic; + int const PlayerProperty::VIDEO_CONTAINER_SIZE = 700; int const PlayerProperty::PLAYLIST = 701; int const PlayerProperty::FILM_CONTAINER = 702; @@ -90,6 +94,7 @@ int const PlayerProperty::FILM_VIDEO_FRAME_RATE = 703; int const PlayerProperty::DCP_DECODE_REDUCTION = 704; int const PlayerProperty::PLAYBACK_LENGTH = 705; + Player::Player (shared_ptr film) : _film (film) , _suspended (0) @@ -99,6 +104,7 @@ Player::Player (shared_ptr film) construct (); } + Player::Player (shared_ptr film, shared_ptr playlist_) : _film (film) , _playlist (playlist_) @@ -109,6 +115,7 @@ Player::Player (shared_ptr film, shared_ptr playlist construct (); } + void Player::construct () { @@ -126,11 +133,13 @@ Player::construct () seek (DCPTime (), true); } + Player::~Player () { delete _shuffler; } + void Player::setup_pieces () { @@ -145,12 +154,14 @@ have_video (shared_ptr content) return static_cast(content->video) && content->video->use(); } + bool have_audio (shared_ptr content) { return static_cast(content->audio); } + void Player::setup_pieces_unlocked () { @@ -176,8 +187,9 @@ Player::setup_pieces_unlocked () shared_ptr old_decoder; for (auto j: old_pieces) { - if (j->content == i) { - old_decoder = j->decoder; + auto decoder = j->decoder_for(i); + if (decoder) { + old_decoder = decoder; break; } } @@ -229,13 +241,13 @@ Player::setup_pieces_unlocked () while (j != decoder->text.end()) { (*j)->BitmapStart.connect ( - bind(&Player::bitmap_text_start, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) + bind(&Player::bitmap_text_start, this, weak_ptr(piece), i, weak_ptr((*j)->content()), _1) ); (*j)->PlainStart.connect ( - bind(&Player::plain_text_start, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) + bind(&Player::plain_text_start, this, weak_ptr(piece), i, weak_ptr((*j)->content()), _1) ); (*j)->Stop.connect ( - bind(&Player::subtitle_stop, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) + bind(&Player::subtitle_stop, this, weak_ptr(piece), i, weak_ptr((*j)->content()), _1) ); ++j; @@ -246,11 +258,16 @@ Player::setup_pieces_unlocked () } } - _stream_states.clear (); - for (auto i: _pieces) { - if (i->content->audio) { - for (auto j: i->content->audio->streams()) { - _stream_states[j] = StreamState (i, i->content->position ()); + for (auto i = _pieces.begin(); i != _pieces.end(); ++i) { + if ((*i)->use_video() && (*i)->video_frame_type() != VideoFrameType::THREE_D_LEFT && (*i)->video_frame_type() != VideoFrameType::THREE_D_RIGHT) { + /* Look for content later in the content list with in-use video that overlaps this */ + auto period = DCPTimePeriod((*i)->position(), (*i)->end(_film)); + auto j = i; + ++j; + for (; j != _pieces.end(); ++j) { + if ((*j)->use_video()) { + (*i)->ignore_video = DCPTimePeriod((*j)->position(), (*j)->end(_film)).overlap(period); + } } } } @@ -258,20 +275,38 @@ Player::setup_pieces_unlocked () _black = Empty (_film, playlist(), bind(&have_video, _1), _playback_length); _silent = Empty (_film, playlist(), bind(&have_audio, _1), _playback_length); - _last_video_time = DCPTime (); + _last_video_time = boost::optional(); _last_video_eyes = Eyes::BOTH; - _last_audio_time = DCPTime (); + _last_audio_time = boost::optional(); +} + + +optional +Player::content_time_to_dcp (shared_ptr content, ContentTime t) +{ + boost::mutex::scoped_lock lm (_mutex); + + for (auto i: _pieces) { + auto dcp = i->content_time_to_dcp(content, t); + if (dcp) { + return *dcp; + } + } + + /* We couldn't find this content; perhaps things are being changed over */ + return {}; } + void Player::playlist_content_change (ChangeType type, int property, bool frequent) { if (property == VideoContentProperty::CROP) { if (type == ChangeType::DONE) { - dcp::Size const vcs = video_container_size(); + auto const vcs = video_container_size(); boost::mutex::scoped_lock lm (_mutex); - for (list, DCPTime> >::const_iterator i = _delay.begin(); i != _delay.end(); ++i) { - i->first->reset_metadata (_film, vcs); + for (auto const& i: _delay) { + i.first->reset_metadata (_film, vcs); } } } else { @@ -293,6 +328,7 @@ Player::playlist_content_change (ChangeType type, int property, bool frequent) Change (type, property, frequent); } + void Player::set_video_container_size (dcp::Size s) { @@ -316,6 +352,7 @@ Player::set_video_container_size (dcp::Size s) Change (ChangeType::DONE, PlayerProperty::VIDEO_CONTAINER_SIZE, false); } + void Player::playlist_change (ChangeType type) { @@ -325,6 +362,7 @@ Player::playlist_change (ChangeType type) Change (type, PlayerProperty::PLAYLIST, false); } + void Player::film_change (ChangeType type, Film::Property p) { @@ -356,6 +394,7 @@ Player::film_change (ChangeType type, Film::Property p) } } + shared_ptr Player::black_player_video_frame (Eyes eyes) const { @@ -375,62 +414,6 @@ Player::black_player_video_frame (Eyes eyes) const ); } -Frame -Player::dcp_to_content_video (shared_ptr piece, DCPTime t) const -{ - DCPTime s = t - piece->content->position (); - s = min (piece->content->length_after_trim(_film), s); - s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc)); - - /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange) - then convert that ContentTime to frames at the content's rate. However this fails for - situations like content at 29.9978733fps, DCP at 30fps. The accuracy of the Time type is not - enough to distinguish between the two with low values of time (e.g. 3200 in Time units). - - Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat. - */ - return s.frames_floor (piece->frc.dcp) / piece->frc.factor (); -} - -DCPTime -Player::content_video_to_dcp (shared_ptr piece, Frame f) const -{ - /* See comment in dcp_to_content_video */ - auto const d = DCPTime::from_frames (f * piece->frc.factor(), piece->frc.dcp) - DCPTime(piece->content->trim_start(), piece->frc); - return d + piece->content->position(); -} - -Frame -Player::dcp_to_resampled_audio (shared_ptr piece, DCPTime t) const -{ - auto s = t - piece->content->position (); - s = min (piece->content->length_after_trim(_film), s); - /* See notes in dcp_to_content_video */ - return max (DCPTime (), DCPTime (piece->content->trim_start (), piece->frc) + s).frames_floor (_film->audio_frame_rate ()); -} - -DCPTime -Player::resampled_audio_to_dcp (shared_ptr piece, Frame f) const -{ - /* See comment in dcp_to_content_video */ - return DCPTime::from_frames (f, _film->audio_frame_rate()) - - DCPTime (piece->content->trim_start(), piece->frc) - + piece->content->position(); -} - -ContentTime -Player::dcp_to_content_time (shared_ptr piece, DCPTime t) const -{ - auto s = t - piece->content->position (); - s = min (piece->content->length_after_trim(_film), s); - return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start()); -} - -DCPTime -Player::content_time_to_dcp (shared_ptr piece, ContentTime t) const -{ - return max (DCPTime(), DCPTime(t - piece->content->trim_start(), piece->frc) + piece->content->position()); -} vector Player::get_subtitle_fonts () @@ -449,6 +432,7 @@ Player::get_subtitle_fonts () return fonts; } + /** Set this player never to produce any video data */ void Player::set_ignore_video () @@ -458,6 +442,7 @@ Player::set_ignore_video () setup_pieces_unlocked (); } + void Player::set_ignore_audio () { @@ -466,6 +451,7 @@ Player::set_ignore_audio () setup_pieces_unlocked (); } + void Player::set_ignore_text () { @@ -474,6 +460,7 @@ Player::set_ignore_text () setup_pieces_unlocked (); } + /** Set the player to always burn open texts into the image regardless of the content settings */ void Player::set_always_burn_open_subtitles () @@ -482,6 +469,7 @@ Player::set_always_burn_open_subtitles () _always_burn_open_subtitles = true; } + /** Sets up the player to be faster, possibly at the expense of quality */ void Player::set_fast () @@ -491,6 +479,7 @@ Player::set_fast () setup_pieces_unlocked (); } + void Player::set_play_referenced () { @@ -499,6 +488,7 @@ Player::set_play_referenced () setup_pieces_unlocked (); } + static void maybe_add_asset (list& a, shared_ptr r, Frame reel_trim_start, Frame reel_trim_end, DCPTime from, int const ffr) { @@ -512,6 +502,7 @@ maybe_add_asset (list& a, shared_ptr r, Fra } } + list Player::get_reel_assets () { @@ -520,14 +511,14 @@ Player::get_reel_assets () list a; for (auto i: playlist()->content()) { - shared_ptr j = dynamic_pointer_cast (i); + auto j = dynamic_pointer_cast (i); if (!j) { continue; } scoped_ptr decoder; try { - decoder.reset (new DCPDecoder (_film, j, false, false, shared_ptr())); + decoder.reset (new DCPDecoder(_film, j, false, false, shared_ptr())); } catch (...) { return a; } @@ -556,7 +547,7 @@ Player::get_reel_assets () Frame const reel_trim_start = min(reel_duration, max(int64_t(0), trim_start - offset_from_start)); Frame const reel_trim_end = min(reel_duration, max(int64_t(0), reel_duration - (offset_from_end - trim_end))); - DCPTime const from = i->position() + DCPTime::from_frames (offset_from_start, _film->video_frame_rate()); + auto const from = i->position() + DCPTime::from_frames (offset_from_start, _film->video_frame_rate()); if (j->reference_video ()) { maybe_add_asset (a, k->main_picture(), reel_trim_start, reel_trim_end, from, ffr); } @@ -583,6 +574,7 @@ Player::get_reel_assets () return a; } + bool Player::pass () { @@ -610,15 +602,15 @@ Player::pass () continue; } - DCPTime const t = content_time_to_dcp (i, max(i->decoder->position(), i->content->trim_start())); - if (t > i->content->end(_film)) { + auto const t = i->decoder_position (); + if (t > i->end(_film)) { i->done = true; } else { /* Given two choices at the same time, pick the one with texts so we see it before the video. */ - if (!earliest_time || t < *earliest_time || (t == *earliest_time && !i->decoder->text.empty())) { + if (!earliest_time || t < *earliest_time || (t == *earliest_time && i->has_text())) { earliest_time = t; earliest_content = i; } @@ -651,15 +643,13 @@ Player::pass () switch (which) { case CONTENT: { - LOG_DEBUG_PLAYER ("Calling pass() on %1", earliest_content->content->path(0)); - earliest_content->done = earliest_content->decoder->pass (); - shared_ptr dcp = dynamic_pointer_cast(earliest_content->content); - if (dcp && !_play_referenced && dcp->reference_audio()) { + earliest_content->pass(); + if (!_play_referenced && earliest_content->reference_dcp_audio()) { /* We are skipping some referenced DCP audio content, so we need to update _last_audio_time to `hide' the fact that no audio was emitted during the referenced DCP (though we need to behave as though it was). */ - _last_audio_time = dcp->end (_film); + _last_audio_time = earliest_content->end (_film); } break; } @@ -704,22 +694,20 @@ Player::pass () /* Work out the time before which the audio is definitely all here. This is the earliest last_push_end of one of our streams, or the position of the _silent. */ - DCPTime pull_to = _playback_length; - for (map::const_iterator i = _stream_states.begin(); i != _stream_states.end(); ++i) { - if (!i->second.piece->done && i->second.last_push_end < pull_to) { - pull_to = i->second.last_push_end; - } + auto pull_to = _playback_length; + for (auto i: _pieces) { + i->update_pull_to (pull_to); } if (!_silent.done() && _silent.position() < pull_to) { pull_to = _silent.position(); } LOG_DEBUG_PLAYER("Emitting audio up to %1", to_string(pull_to)); - list, DCPTime> > audio = _audio_merger.pull (pull_to); - for (list, DCPTime> >::iterator i = audio.begin(); i != audio.end(); ++i) { + auto audio = _audio_merger.pull (pull_to); + for (auto i = audio.begin(); i != audio.end(); ++i) { if (_last_audio_time && i->second < *_last_audio_time) { /* This new data comes before the last we emitted (or the last seek); discard it */ - pair, DCPTime> cut = discard_audio (i->first, i->second, *_last_audio_time); + auto cut = discard_audio (i->first, i->second, *_last_audio_time); if (!cut.first) { continue; } @@ -734,14 +722,15 @@ Player::pass () if (done) { _shuffler->flush (); - for (list, DCPTime> >::const_iterator i = _delay.begin(); i != _delay.end(); ++i) { - do_emit_video(i->first, i->second); + for (auto const& i: _delay) { + do_emit_video(i.first, i.second); } } return done; } + /** @return Open subtitles for the frame at the given time, converted to images */ optional Player::open_subtitles_for_frame (DCPTime time) const @@ -767,85 +756,90 @@ Player::open_subtitles_for_frame (DCPTime time) const PositionImage ( i.image, Position ( - lrint (_video_container_size.width * i.rectangle.x), - lrint (_video_container_size.height * i.rectangle.y) + lrint(_video_container_size.width * i.rectangle.x), + lrint(_video_container_size.height * i.rectangle.y) ) ) ); } /* String subtitles (rendered to an image) */ - if (!j.string.empty ()) { - list s = render_text (j.string, j.fonts, _video_container_size, time, vfr); + if (!j.string.empty()) { + auto s = render_text (j.string, j.fonts, _video_container_size, time, vfr); copy (s.begin(), s.end(), back_inserter (captions)); } } - if (captions.empty ()) { - return optional (); + if (captions.empty()) { + return {}; } return merge (captions); } + void Player::video (weak_ptr wp, ContentVideo video) { - shared_ptr piece = wp.lock (); + auto piece = wp.lock (); if (!piece) { return; } - if (!piece->content->video->use()) { + if (!piece->use_video()) { return; } - FrameRateChange frc (_film, piece->content); + auto frc = piece->frame_rate_change(); if (frc.skip && (video.frame % 2) == 1) { return; } /* Time of the first frame we will emit */ - DCPTime const time = content_video_to_dcp (piece, video.frame); + DCPTime const time = piece->content_video_to_dcp (video.frame); LOG_DEBUG_PLAYER("Received video frame %1 at %2", video.frame, to_string(time)); /* Discard if it's before the content's period or the last accurate seek. We can't discard if it's after the content's period here as in that case we still need to fill any gap between `now' and the end of the content's period. */ - if (time < piece->content->position() || (_last_video_time && time < *_last_video_time)) { + if (time < piece->position() || (_last_video_time && time < *_last_video_time)) { + return; + } + + if (piece->ignore_video && piece->ignore_video->contains(time)) { return; } /* Fill gaps that we discover now that we have some video which needs to be emitted. This is where we need to fill to. */ - DCPTime fill_to = min (time, piece->content->end(_film)); + DCPTime fill_to = min (time, piece->end(_film)); if (_last_video_time) { - DCPTime fill_from = max (*_last_video_time, piece->content->position()); + DCPTime fill_from = max (*_last_video_time, piece->position()); /* Fill if we have more than half a frame to do */ if ((fill_to - fill_from) > one_video_frame() / 2) { - LastVideoMap::const_iterator last = _last_video.find (wp); + auto last = _last_video.find (wp); if (_film->three_d()) { - Eyes fill_to_eyes = video.eyes; + auto fill_to_eyes = video.eyes; if (fill_to_eyes == Eyes::BOTH) { fill_to_eyes = Eyes::LEFT; } - if (fill_to == piece->content->end(_film)) { + if (fill_to == piece->end(_film)) { /* Don't fill after the end of the content */ fill_to_eyes = Eyes::LEFT; } - DCPTime j = fill_from; - Eyes eyes = _last_video_eyes.get_value_or(Eyes::LEFT); + auto j = fill_from; + auto eyes = _last_video_eyes.get_value_or(Eyes::LEFT); if (eyes == Eyes::BOTH) { eyes = Eyes::LEFT; } while (j < fill_to || eyes != fill_to_eyes) { if (last != _last_video.end()) { LOG_DEBUG_PLAYER("Fill using last video at %1 in 3D mode", to_string(j)); - shared_ptr copy = last->second->shallow_copy(); + auto copy = last->second->shallow_copy(); copy->set_eyes (eyes); emit_video (copy, j); } else { @@ -869,81 +863,64 @@ Player::video (weak_ptr wp, ContentVideo video) } } - _last_video[wp].reset ( - new PlayerVideo ( - video.image, - piece->content->video->crop (), - piece->content->video->fade (_film, video.frame), - scale_for_display(piece->content->video->scaled_size(_film->frame_size()), _video_container_size, _film->frame_size()), - _video_container_size, - video.eyes, - video.part, - piece->content->video->colour_conversion(), - piece->content->video->range(), - piece->content, - video.frame, - false - ) - ); + _last_video[wp] = piece->player_video (video, _film, _video_container_size); DCPTime t = time; for (int i = 0; i < frc.repeat; ++i) { - if (t < piece->content->end(_film)) { + if (t < piece->end(_film)) { emit_video (_last_video[wp], t); } t += one_video_frame (); } } + void Player::audio (weak_ptr wp, AudioStreamPtr stream, ContentAudio content_audio) { DCPOMATIC_ASSERT (content_audio.audio->frames() > 0); - shared_ptr piece = wp.lock (); + auto piece = wp.lock (); if (!piece) { return; } - shared_ptr content = piece->content->audio; - DCPOMATIC_ASSERT (content); - - int const rfr = content->resampled_frame_rate (_film); + int const rfr = piece->resampled_audio_frame_rate (_film); /* Compute time in the DCP */ - DCPTime time = resampled_audio_to_dcp (piece, content_audio.frame); + auto time = piece->resampled_audio_to_dcp (content_audio.frame, _film); LOG_DEBUG_PLAYER("Received audio frame %1 at %2", content_audio.frame, to_string(time)); /* And the end of this block in the DCP */ - DCPTime end = time + DCPTime::from_frames(content_audio.audio->frames(), rfr); + auto end = time + DCPTime::from_frames(content_audio.audio->frames(), rfr); /* Remove anything that comes before the start or after the end of the content */ - if (time < piece->content->position()) { - pair, DCPTime> cut = discard_audio (content_audio.audio, time, piece->content->position()); + if (time < piece->position()) { + auto cut = discard_audio (content_audio.audio, time, piece->position()); if (!cut.first) { /* This audio is entirely discarded */ return; } content_audio.audio = cut.first; time = cut.second; - } else if (time > piece->content->end(_film)) { + } else if (time > piece->end(_film)) { /* Discard it all */ return; - } else if (end > piece->content->end(_film)) { - Frame const remaining_frames = DCPTime(piece->content->end(_film) - time).frames_round(rfr); + } else if (end > piece->end(_film)) { + Frame const remaining_frames = DCPTime(piece->end(_film) - time).frames_round(rfr); if (remaining_frames == 0) { return; } - content_audio.audio.reset (new AudioBuffers(content_audio.audio, remaining_frames, 0)); + content_audio.audio = make_shared(content_audio.audio, remaining_frames, 0); } DCPOMATIC_ASSERT (content_audio.audio->frames() > 0); /* Gain */ - if (content->gain() != 0) { - shared_ptr gain (new AudioBuffers (content_audio.audio)); - gain->apply_gain (content->gain ()); + if (piece->audio_gain() != 0) { + auto gain = make_shared(content_audio.audio); + gain->apply_gain (piece->audio_gain()); content_audio.audio = gain; } @@ -960,16 +937,17 @@ Player::audio (weak_ptr wp, AudioStreamPtr stream, ContentAudio content_a /* Push */ _audio_merger.push (content_audio.audio, time); - DCPOMATIC_ASSERT (_stream_states.find (stream) != _stream_states.end ()); - _stream_states[stream].last_push_end = time + DCPTime::from_frames (content_audio.audio->frames(), _film->audio_frame_rate()); + piece->set_last_push_end (stream, time + DCPTime::from_frames(content_audio.audio->frames(), _film->audio_frame_rate())); } + void -Player::bitmap_text_start (weak_ptr wp, weak_ptr wc, ContentBitmapText subtitle) +Player::bitmap_text_start (weak_ptr wp, weak_ptr wc, weak_ptr wt, ContentBitmapText subtitle) { - shared_ptr piece = wp.lock (); - shared_ptr text = wc.lock (); - if (!piece || !text) { + auto piece = wp.lock (); + auto content = wc.lock (); + auto text = wt.lock (); + if (!piece || !content || !text) { return; } @@ -986,7 +964,7 @@ Player::bitmap_text_start (weak_ptr wp, weak_ptr wc, C subtitle.sub.rectangle.height *= text->y_scale (); PlayerText ps; - shared_ptr image = subtitle.sub.image; + auto image = subtitle.sub.image; /* We will scale the subtitle up to fit _video_container_size */ int const width = subtitle.sub.rectangle.width * _video_container_size.width; @@ -997,24 +975,28 @@ Player::bitmap_text_start (weak_ptr wp, weak_ptr wc, C dcp::Size scaled_size (width, height); ps.bitmap.push_back (BitmapText(image->scale(scaled_size, dcp::YUVToRGB::REC601, image->pixel_format(), true, _fast), subtitle.sub.rectangle)); - DCPTime from (content_time_to_dcp (piece, subtitle.from())); + auto from = piece->content_time_to_dcp(content, subtitle.from()); + DCPOMATIC_ASSERT (from); - _active_texts[static_cast(text->type())].add_from (wc, ps, from); + _active_texts[static_cast(text->type())].add_from (wt, ps, *from); } + void -Player::plain_text_start (weak_ptr wp, weak_ptr wc, ContentStringText subtitle) +Player::plain_text_start (weak_ptr wp, weak_ptr wc, weak_ptr wt, ContentStringText subtitle) { - shared_ptr piece = wp.lock (); - shared_ptr text = wc.lock (); - if (!piece || !text) { + auto piece = wp.lock (); + auto content = wc.lock (); + auto text = wt.lock (); + if (!piece || !content || !text) { return; } PlayerText ps; - DCPTime const from (content_time_to_dcp (piece, subtitle.from())); + auto const from = piece->content_time_to_dcp(content, subtitle.from()); + DCPOMATIC_ASSERT (from); - if (from > piece->content->end(_film)) { + if (from > piece->end(_film)) { return; } @@ -1038,23 +1020,25 @@ Player::plain_text_start (weak_ptr wp, weak_ptr wc, Co s.set_aspect_adjust (xs / ys); } - s.set_in (dcp::Time(from.seconds(), 1000)); + s.set_in (dcp::Time(from->seconds(), 1000)); ps.string.push_back (StringText (s, text->outline_width())); ps.add_fonts (text->fonts ()); } - _active_texts[static_cast(text->type())].add_from (wc, ps, from); + _active_texts[static_cast(text->type())].add_from (wt, ps, *from); } + void -Player::subtitle_stop (weak_ptr wp, weak_ptr wc, ContentTime to) +Player::subtitle_stop (weak_ptr wp, weak_ptr wc, weak_ptr wt, ContentTime to) { - shared_ptr text = wc.lock (); + auto content = wc.lock (); + auto text = wt.lock (); if (!text) { return; } - if (!_active_texts[static_cast(text->type())].have(wc)) { + if (!_active_texts[static_cast(text->type())].have(wt)) { return; } @@ -1063,20 +1047,22 @@ Player::subtitle_stop (weak_ptr wp, weak_ptr wc, Conte return; } - DCPTime const dcp_to = content_time_to_dcp (piece, to); + auto const dcp_to = piece->content_time_to_dcp(content, to); + DCPOMATIC_ASSERT (dcp_to); - if (dcp_to > piece->content->end(_film)) { + if (*dcp_to > piece->end(_film)) { return; } - pair from = _active_texts[static_cast(text->type())].add_to (wc, dcp_to); + auto from = _active_texts[static_cast(text->type())].add_to(wt, *dcp_to); bool const always = (text->type() == TextType::OPEN_SUBTITLE && _always_burn_open_subtitles); if (text->use() && !always && !text->burn()) { - Text (from.first, text->type(), text->dcp_track().get_value_or(DCPTextTrack()), DCPTimePeriod (from.second, dcp_to)); + Text (from.first, text->type(), text->dcp_track().get_value_or(DCPTextTrack()), DCPTimePeriod (from.second, *dcp_to)); } } + void Player::seek (DCPTime time, bool accurate) { @@ -1104,22 +1090,7 @@ Player::seek (DCPTime time, bool accurate) } for (auto i: _pieces) { - if (time < i->content->position()) { - /* Before; seek to the start of the content. Even if this request is for an inaccurate seek - we must seek this (following) content accurately, otherwise when we come to the end of the current - content we may not start right at the beginning of the next, causing a gap (if the next content has - been trimmed to a point between keyframes, or something). - */ - i->decoder->seek (dcp_to_content_time (i, i->content->position()), true); - i->done = false; - } else if (i->content->position() <= time && time < i->content->end(_film)) { - /* During; seek to position */ - i->decoder->seek (dcp_to_content_time (i, time), accurate); - i->done = false; - } else { - /* After; this piece is done */ - i->done = true; - } + i->seek (_film, time, accurate); } if (accurate) { @@ -1138,9 +1109,20 @@ Player::seek (DCPTime time, bool accurate) _last_video.clear (); } + void Player::emit_video (shared_ptr pv, DCPTime time) { + if (!_film->three_d()) { + if (pv->eyes() == Eyes::LEFT) { + /* Use left-eye images for both eyes... */ + pv->set_eyes (Eyes::BOTH); + } else if (pv->eyes() == Eyes::RIGHT) { + /* ...and discard the right */ + return; + } + } + /* We need a delay to give a little wiggle room to ensure that relevent subtitles arrive at the player before the video that requires them. */ @@ -1155,11 +1137,12 @@ Player::emit_video (shared_ptr pv, DCPTime time) return; } - pair, DCPTime> to_do = _delay.front(); + auto to_do = _delay.front(); _delay.pop_front(); do_emit_video (to_do.first, to_do.second); } + void Player::do_emit_video (shared_ptr pv, DCPTime time) { @@ -1177,6 +1160,7 @@ Player::do_emit_video (shared_ptr pv, DCPTime time) Video (pv, time); } + void Player::emit_audio (shared_ptr data, DCPTime time) { @@ -1191,6 +1175,7 @@ Player::emit_audio (shared_ptr data, DCPTime time) _last_audio_time = time + DCPTime::from_frames (data->frames(), _film->audio_frame_rate()); } + void Player::fill_audio (DCPTimePeriod period) { @@ -1205,7 +1190,7 @@ Player::fill_audio (DCPTimePeriod period) DCPTime block = min (DCPTime::from_seconds (0.5), period.to - t); Frame const samples = block.frames_round(_film->audio_frame_rate()); if (samples) { - shared_ptr silence (new AudioBuffers (_film->audio_channels(), samples)); + auto silence = make_shared(_film->audio_channels(), samples); silence->make_silent (); emit_audio (silence, t); } @@ -1213,25 +1198,28 @@ Player::fill_audio (DCPTimePeriod period) } } + DCPTime Player::one_video_frame () const { return DCPTime::from_frames (1, _film->video_frame_rate ()); } + pair, DCPTime> Player::discard_audio (shared_ptr audio, DCPTime time, DCPTime discard_to) const { - DCPTime const discard_time = discard_to - time; - Frame const discard_frames = discard_time.frames_round(_film->audio_frame_rate()); - Frame remaining_frames = audio->frames() - discard_frames; + auto const discard_time = discard_to - time; + auto const discard_frames = discard_time.frames_round(_film->audio_frame_rate()); + auto remaining_frames = audio->frames() - discard_frames; if (remaining_frames <= 0) { return make_pair(shared_ptr(), DCPTime()); } - shared_ptr cut (new AudioBuffers(audio, remaining_frames, discard_frames)); + auto cut = make_shared(audio, remaining_frames, discard_frames); return make_pair(cut, time + discard_time); } + void Player::set_dcp_decode_reduction (optional reduction) { @@ -1253,21 +1241,6 @@ Player::set_dcp_decode_reduction (optional reduction) Change (ChangeType::DONE, PlayerProperty::DCP_DECODE_REDUCTION, false); } -optional -Player::content_time_to_dcp (shared_ptr content, ContentTime t) -{ - boost::mutex::scoped_lock lm (_mutex); - - for (auto i: _pieces) { - if (i->content == content) { - return content_time_to_dcp (i, t); - } - } - - /* We couldn't find this content; perhaps things are being changed over */ - return {}; -} - shared_ptr Player::playlist () const