X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=blobdiff_plain;f=src%2Flib%2Fplayer.cc;h=79db7cc2c76d1695ac3201a09a1a470ecc360b30;hp=7156e31228eb17fb369356f41d070e1656ae18e5;hb=386e25f3b9d3fa59cbdeed458d9b3e0d21e338b8;hpb=f0957fd41d1915b8046dc0c2aeb662e1e8c288c7 diff --git a/src/lib/player.cc b/src/lib/player.cc index 7156e3122..79db7cc2c 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Carl Hetherington + Copyright (C) 2013-2019 Carl Hetherington This file is part of DCP-o-matic. @@ -48,6 +48,7 @@ #include "image_decoder.h" #include "compose.hpp" #include "shuffler.h" +#include "timer.h" #include #include #include @@ -75,6 +76,7 @@ using boost::weak_ptr; using boost::dynamic_pointer_cast; using boost::optional; using boost::scoped_ptr; +using namespace dcpomatic; int const PlayerProperty::VIDEO_CONTAINER_SIZE = 700; int const PlayerProperty::PLAYLIST = 701; @@ -91,6 +93,7 @@ Player::Player (shared_ptr film, shared_ptr playlist , _ignore_text (false) , _always_burn_open_subtitles (false) , _fast (false) + , _tolerant (film->tolerant()) , _play_referenced (false) , _audio_merger (_film->audio_frame_rate()) , _shuffler (0) @@ -136,6 +139,7 @@ have_audio (shared_ptr piece) void Player::setup_pieces_unlocked () { + list > old_pieces = _pieces; _pieces.clear (); delete _shuffler; @@ -153,7 +157,15 @@ Player::setup_pieces_unlocked () continue; } - shared_ptr decoder = decoder_factory (_film, i, _fast); + shared_ptr old_decoder; + BOOST_FOREACH (shared_ptr j, old_pieces) { + if (j->content == i) { + old_decoder = j->decoder; + break; + } + } + + shared_ptr decoder = decoder_factory (_film, i, _fast, _tolerant, old_decoder); FrameRateChange frc (_film, i); if (!decoder) { @@ -231,7 +243,6 @@ Player::setup_pieces_unlocked () _last_video_time = DCPTime (); _last_video_eyes = EYES_BOTH; _last_audio_time = DCPTime (); - _suspended = false; } void @@ -247,6 +258,7 @@ Player::playlist_content_change (ChangeType type, int property, bool frequent) } else if (type == CHANGE_TYPE_DONE) { /* A change in our content has gone through. Re-build our pieces. */ setup_pieces (); + _suspended = false; } else if (type == CHANGE_TYPE_CANCELLED) { boost::mutex::scoped_lock lm (_mutex); _suspended = false; @@ -331,6 +343,7 @@ Player::black_player_video_frame (Eyes eyes) const eyes, PART_WHOLE, PresetColourConversion::all().front().conversion, + VIDEO_RANGE_FULL, boost::weak_ptr(), boost::optional() ) @@ -463,6 +476,19 @@ 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) +{ + DCPOMATIC_ASSERT (r); + r->set_entry_point (r->entry_point().get_value_or(0) + reel_trim_start); + r->set_duration (r->actual_duration() - reel_trim_start - reel_trim_end); + if (r->actual_duration() > 0) { + a.push_back ( + ReferencedReelAsset(r, DCPTimePeriod(from, from + DCPTime::from_frames(r->actual_duration(), ffr))) + ); + } +} + list Player::get_reel_assets () { @@ -478,64 +504,56 @@ Player::get_reel_assets () scoped_ptr decoder; try { - decoder.reset (new DCPDecoder (_film, j, false)); + decoder.reset (new DCPDecoder (_film, j, false, false, shared_ptr())); } catch (...) { return a; } - int64_t offset = 0; + DCPOMATIC_ASSERT (j->video_frame_rate ()); + double const cfr = j->video_frame_rate().get(); + Frame const trim_start = j->trim_start().frames_round (cfr); + Frame const trim_end = j->trim_end().frames_round (cfr); + int const ffr = _film->video_frame_rate (); + + /* position in the asset from the start */ + int64_t offset_from_start = 0; + /* position in the asset from the end */ + int64_t offset_from_end = 0; BOOST_FOREACH (shared_ptr k, decoder->reels()) { + /* Assume that main picture duration is the length of the reel */ + offset_from_end += k->main_picture()->actual_duration(); + } - DCPOMATIC_ASSERT (j->video_frame_rate ()); - double const cfr = j->video_frame_rate().get(); - Frame const trim_start = j->trim_start().frames_round (cfr); - Frame const trim_end = j->trim_end().frames_round (cfr); - int const ffr = _film->video_frame_rate (); + BOOST_FOREACH (shared_ptr k, decoder->reels()) { - DCPTime const from = i->position() + DCPTime::from_frames (offset, _film->video_frame_rate()); + /* Assume that main picture duration is the length of the reel */ + int64_t const reel_duration = k->main_picture()->actual_duration(); + + /* See doc/design/trim_reels.svg */ + 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()); if (j->reference_video ()) { - shared_ptr ra = k->main_picture (); - DCPOMATIC_ASSERT (ra); - ra->set_entry_point (ra->entry_point() + trim_start); - ra->set_duration (ra->duration() - trim_start - trim_end); - a.push_back ( - ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr))) - ); + maybe_add_asset (a, k->main_picture(), reel_trim_start, reel_trim_end, from, ffr); } if (j->reference_audio ()) { - shared_ptr ra = k->main_sound (); - DCPOMATIC_ASSERT (ra); - ra->set_entry_point (ra->entry_point() + trim_start); - ra->set_duration (ra->duration() - trim_start - trim_end); - a.push_back ( - ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr))) - ); + maybe_add_asset (a, k->main_sound(), reel_trim_start, reel_trim_end, from, ffr); } if (j->reference_text (TEXT_OPEN_SUBTITLE)) { - shared_ptr ra = k->main_subtitle (); - DCPOMATIC_ASSERT (ra); - ra->set_entry_point (ra->entry_point() + trim_start); - ra->set_duration (ra->duration() - trim_start - trim_end); - a.push_back ( - ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr))) - ); + maybe_add_asset (a, k->main_subtitle(), reel_trim_start, reel_trim_end, from, ffr); } if (j->reference_text (TEXT_CLOSED_CAPTION)) { BOOST_FOREACH (shared_ptr l, k->closed_captions()) { - DCPOMATIC_ASSERT (l); - l->set_entry_point (l->entry_point() + trim_start); - l->set_duration (l->duration() - trim_start - trim_end); - a.push_back ( - ReferencedReelAsset (l, DCPTimePeriod (from, from + DCPTime::from_frames (l->duration(), ffr))) - ); + maybe_add_asset (a, l, reel_trim_start, reel_trim_end, from, ffr); } } - /* Assume that main picture duration is the length of the reel */ - offset += k->main_picture()->duration (); + offset_from_start += reel_duration; + offset_from_end -= reel_duration; } } @@ -596,20 +614,30 @@ Player::pass () which = CONTENT; } - if (!_black.done() && (!earliest_time || _black.position() < *earliest_time)) { + if (!_black.done() && !_ignore_video && (!earliest_time || _black.position() < *earliest_time)) { earliest_time = _black.position (); which = BLACK; } - if (!_silent.done() && (!earliest_time || _silent.position() < *earliest_time)) { + if (!_silent.done() && !_ignore_audio && (!earliest_time || _silent.position() < *earliest_time)) { earliest_time = _silent.position (); which = SILENT; } switch (which) { case CONTENT: + { earliest_content->done = earliest_content->decoder->pass (); + shared_ptr dcp = dynamic_pointer_cast(earliest_content->content); + if (dcp && !_play_referenced && dcp->reference_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); + } break; + } case BLACK: emit_video (black_player_video_frame(EYES_BOTH), _black.position()); _black.set_position (_black.position() + one_video_frame()); @@ -619,11 +647,18 @@ Player::pass () DCPTimePeriod period (_silent.period_at_position()); if (_last_audio_time) { /* Sometimes the thing that happened last finishes fractionally before - this silence. Bodge the start time of the silence to fix it. I'm - not sure if this is the right solution --- maybe the last thing should - be padded `forward' rather than this thing padding `back'. + or after this silence. Bodge the start time of the silence to fix it. + I think this is nothing to worry about since we will just add or + remove a little silence at the end of some content. */ - period.from = min(period.from, *_last_audio_time); + int64_t const error = labs(period.from.get() - _last_audio_time->get()); + /* Let's not worry about less than a frame at 24fps */ + int64_t const too_much_error = DCPTime::from_frames(1, 24).get(); + if (error >= too_much_error) { + _film->log()->log(String::compose("Silence starting before or after last audio by %1", error), LogEntry::TYPE_ERROR); + } + DCPOMATIC_ASSERT (error < too_much_error); + period.from = *_last_audio_time; } if (period.duration() > one_video_frame()) { period.to = period.from + one_video_frame(); @@ -811,6 +846,7 @@ Player::video (weak_ptr wp, ContentVideo video) video.eyes, video.part, piece->content->video->colour_conversion(), + piece->content->video->range(), piece->content, video.frame ) @@ -838,10 +874,12 @@ Player::audio (weak_ptr wp, AudioStreamPtr stream, ContentAudio content_a shared_ptr content = piece->content->audio; DCPOMATIC_ASSERT (content); + int const rfr = content->resampled_frame_rate (_film); + /* Compute time in the DCP */ DCPTime time = resampled_audio_to_dcp (piece, content_audio.frame); /* And the end of this block in the DCP */ - DCPTime end = time + DCPTime::from_frames(content_audio.audio->frames(), content->resampled_frame_rate(_film)); + DCPTime 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()) { @@ -856,7 +894,7 @@ Player::audio (weak_ptr wp, AudioStreamPtr stream, ContentAudio content_a /* Discard it all */ return; } else if (end > piece->content->end(_film)) { - Frame const remaining_frames = DCPTime(piece->content->end(_film) - time).frames_round(_film->audio_frame_rate()); + Frame const remaining_frames = DCPTime(piece->content->end(_film) - time).frames_round(rfr); if (remaining_frames == 0) { return; } @@ -1025,8 +1063,12 @@ Player::seek (DCPTime time, bool accurate) BOOST_FOREACH (shared_ptr i, _pieces) { if (time < i->content->position()) { - /* Before; seek to the start of the content */ - i->decoder->seek (dcp_to_content_time (i, i->content->position()), accurate); + /* 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 */