X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fplayer.cc;h=3db2fe6c9eb19074be6f265f7fd4ffe6918ca61d;hb=d7b23d44dec9d6357619e8e009e564e475215470;hp=bb085af8fa6240cf24e030bef027e06890aeccfd;hpb=c94f29d333e1b81aabbc396920250844460d46f6;p=dcpomatic.git diff --git a/src/lib/player.cc b/src/lib/player.cc index bb085af8f..3db2fe6c9 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -18,6 +18,7 @@ */ #include +#include #include "player.h" #include "film.h" #include "ffmpeg_decoder.h" @@ -71,6 +72,7 @@ Player::Player (shared_ptr f, shared_ptr p) , _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1)) , _last_emit_was_black (false) , _just_did_inaccurate_seek (false) + , _approximate_size (false) { _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this)); _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3)); @@ -106,13 +108,37 @@ Player::pass () for (list >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) { - shared_ptr dec = (*i)->decoder->peek (); + DCPTime const offset = (*i)->content->position() - (*i)->content->trim_start(); + + bool done = false; + shared_ptr dec; + while (!done) { + dec = (*i)->decoder->peek (); + if (!dec) { + /* Decoder has nothing else to give us */ + break; + } + + dec->set_dcp_times (_film->video_frame_rate(), _film->audio_frame_rate(), (*i)->frc, offset); + DCPTime const t = dec->dcp_time - offset; + if (t >= ((*i)->content->full_length() - (*i)->content->trim_end ())) { + /* In the end-trimmed part; decoder has nothing else to give us */ + dec.reset (); + done = true; + } else if (t >= (*i)->content->trim_start ()) { + /* Within the un-trimmed part; everything's ok */ + done = true; + } else { + /* Within the start-trimmed part; get something else */ + (*i)->decoder->consume (); + } + } - if (dec) { - dec->set_dcp_times ((*i)->frc.speed_up, (*i)->content->position()); + if (!dec) { + continue; } - if (dec && dec->dcp_time < earliest_time) { + if (dec->dcp_time < earliest_time) { earliest_piece = *i; earliest_decoded = dec; earliest_time = dec->dcp_time; @@ -129,8 +155,11 @@ Player::pass () } if (earliest_audio != TIME_MAX) { - TimedAudioBuffers tb = _audio_merger.pull (earliest_audio); + TimedAudioBuffers tb = _audio_merger.pull (max (int64_t (0), earliest_audio)); Audio (tb.audio, tb.time); + /* This assumes that the audio_frames_to_time conversion is exact + so that there are no accumulated errors caused by rounding. + */ _audio_position += _film->audio_frames_to_time (tb.audio->frames ()); } @@ -140,10 +169,21 @@ Player::pass () shared_ptr da = dynamic_pointer_cast (earliest_decoded); shared_ptr ds = dynamic_pointer_cast (earliest_decoded); - if (dv) { - if (!_just_did_inaccurate_seek && earliest_time > _video_position) { + /* Will be set to false if we shouldn't consume the peeked DecodedThing */ + bool consume = true; + + if (dv && _video) { + + if (_just_did_inaccurate_seek) { + + /* Just emit; no subtlety */ + emit_video (earliest_piece, dv); + step_video_position (dv); + + } else if (dv->dcp_time > _video_position) { + + /* Too far ahead */ - /* See if we're inside some video content */ list >::iterator i = _pieces.begin(); while (i != _pieces.end() && ((*i)->content->position() >= _video_position || _video_position >= (*i)->content->end())) { ++i; @@ -152,30 +192,55 @@ Player::pass () if (i == _pieces.end() || !_last_incoming_video.video || !_have_valid_pieces) { /* We're outside all video content */ emit_black (); + _statistics.video.black++; } else { + /* We're inside some video; repeat the frame */ _last_incoming_video.video->dcp_time = _video_position; emit_video (_last_incoming_video.weak_piece, _last_incoming_video.video); + step_video_position (_last_incoming_video.video); + _statistics.video.repeat++; } - } else { + + consume = false; + + } else if (dv->dcp_time == _video_position) { + /* We're ok */ emit_video (earliest_piece, dv); - earliest_piece->decoder->get (); - } - } else if (da) { - if (!_just_did_inaccurate_seek && earliest_time > _audio_position) { - emit_silence (earliest_time - _audio_position); + step_video_position (dv); + _statistics.video.good++; } else { + /* Too far behind: skip */ + _statistics.video.skip++; + } + + _just_did_inaccurate_seek = false; + + } else if (da && _audio) { + + if (da->dcp_time > _audio_position) { + /* Too far ahead */ + emit_silence (da->dcp_time - _audio_position); + consume = false; + _statistics.audio.silence += (da->dcp_time - _audio_position); + } else if (da->dcp_time == _audio_position) { + /* We're ok */ emit_audio (earliest_piece, da); - earliest_piece->decoder->get (); + _statistics.audio.good += da->data->frames(); + } else { + /* Too far behind: skip */ + _statistics.audio.skip += da->data->frames(); } - } else if (ds) { + + } else if (ds && _video) { _in_subtitle.piece = earliest_piece; _in_subtitle.subtitle = ds; update_subtitle (); - earliest_piece->decoder->get (); } - _just_did_inaccurate_seek = false; - + if (consume) { + earliest_piece->decoder->consume (); + } + return false; } @@ -195,19 +260,13 @@ Player::emit_video (weak_ptr weak_piece, shared_ptr video) assert (content); FrameRateChange frc (content->video_frame_rate(), _film->video_frame_rate()); -#if 0 - XXX - if (frc.skip && (frame % 2) == 1) { - return; - } -#endif - - if (content->trimmed (video->dcp_time - content->position ())) { - return; - } float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio(); - libdcp::Size const image_size = fit_ratio_within (ratio, _video_container_size); + libdcp::Size image_size = fit_ratio_within (ratio, _video_container_size); + if (_approximate_size) { + image_size.width &= ~3; + image_size.height &= ~3; + } shared_ptr pi ( new PlayerImage ( @@ -221,16 +280,16 @@ Player::emit_video (weak_ptr weak_piece, shared_ptr video) if ( _film->with_subtitles () && - _out_subtitle.subtitle->image && - video->dcp_time >= _out_subtitle.subtitle->dcp_time && video->dcp_time <= _out_subtitle.subtitle->dcp_time_to + _out_subtitle.image && + video->dcp_time >= _out_subtitle.from && video->dcp_time <= _out_subtitle.to ) { Position const container_offset ( (_video_container_size.width - image_size.width) / 2, - (_video_container_size.height - image_size.width) / 2 + (_video_container_size.height - image_size.height) / 2 ); - pi->set_subtitle (_out_subtitle.subtitle->image, _out_subtitle.position + container_offset); + pi->set_subtitle (_out_subtitle.image, _out_subtitle.position + container_offset); } #ifdef DCPOMATIC_DEBUG @@ -238,9 +297,20 @@ Player::emit_video (weak_ptr weak_piece, shared_ptr video) #endif Video (pi, video->eyes, content->colour_conversion(), video->same, video->dcp_time); - + _last_emit_was_black = false; - _video_position = rint (video->dcp_time + TIME_HZ / _film->video_frame_rate()); +} + +void +Player::step_video_position (shared_ptr video) +{ + /* This is a bit of a hack; don't update _video_position if EYES_RIGHT is on its way */ + if (video->eyes != EYES_LEFT) { + /* This assumes that the video_frames_to_time conversion is exact + so that there are no accumulated errors caused by rounding. + */ + _video_position += _film->video_frames_to_time (1); + } } void @@ -261,10 +331,6 @@ Player::emit_audio (weak_ptr weak_piece, shared_ptr audio) audio->data = gain; } - if (content->trimmed (audio->dcp_time - content->position ())) { - return; - } - /* Remap channels */ shared_ptr dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->data->frames())); dcp_mapped->make_silent (); @@ -299,16 +365,16 @@ void Player::flush () { TimedAudioBuffers tb = _audio_merger.flush (); - if (tb.audio) { + if (_audio && tb.audio) { Audio (tb.audio, tb.time); _audio_position += _film->audio_frames_to_time (tb.audio->frames ()); } - while (_video_position < _audio_position) { + while (_video && _video_position < _audio_position) { emit_black (); } - while (_audio_position < _video_position) { + while (_audio && _audio_position < _video_position) { emit_silence (_video_position - _audio_position); } @@ -336,7 +402,7 @@ Player::seek (DCPTime t, bool accurate) s = min ((*i)->content->length_after_trim(), s); /* Convert this to the content time */ - ContentTime ct = (s * (*i)->frc.speed_up) + (*i)->content->trim_start (); + ContentTime ct = (s + (*i)->content->trim_start()) * (*i)->frc.speed_up; /* And seek the decoder */ (*i)->decoder->seek (ct, accurate); @@ -426,12 +492,18 @@ Player::setup_pieces () } } - decoder->seek ((*i)->trim_start (), true); + ContentTime st = (*i)->trim_start() * frc->speed_up; + decoder->seek (st, true); _pieces.push_back (shared_ptr (new Piece (*i, decoder, frc.get ()))); } _have_valid_pieces = true; + + /* The Piece for the _last_incoming_video will no longer be valid */ + _last_incoming_video.video.reset (); + + _video_position = _audio_position = 0; } void @@ -515,6 +587,7 @@ Player::emit_silence (DCPTime most) shared_ptr silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ)); silence->make_silent (); Audio (silence, _audio_position); + _audio_position += t; } @@ -540,7 +613,7 @@ Player::update_subtitle () } if (!_in_subtitle.subtitle->image) { - _out_subtitle.subtitle->image.reset (); + _out_subtitle.image.reset (); return; } @@ -570,16 +643,16 @@ Player::update_subtitle () _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2))); _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2))); - - _out_subtitle.subtitle->image = _in_subtitle.subtitle->image->scale ( + + _out_subtitle.image = _in_subtitle.subtitle->image->scale ( scaled_size, Scaler::from_id ("bicubic"), - _in_subtitle.subtitle->image->pixel_format (), + PIX_FMT_RGBA, true ); - - _out_subtitle.subtitle->dcp_time = _in_subtitle.subtitle->dcp_time; - _out_subtitle.subtitle->dcp_time = _in_subtitle.subtitle->dcp_time; + + _out_subtitle.from = _in_subtitle.subtitle->dcp_time; + _out_subtitle.to = _in_subtitle.subtitle->dcp_time_to; } /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles. @@ -600,6 +673,13 @@ Player::repeat_last_video () return true; } +void +Player::set_approximate_size () +{ + _approximate_size = true; +} + + PlayerImage::PlayerImage ( shared_ptr in, Crop crop, @@ -624,10 +704,10 @@ PlayerImage::set_subtitle (shared_ptr image, Position pos) } shared_ptr -PlayerImage::image () +PlayerImage::image (AVPixelFormat format, bool aligned) { - shared_ptr out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false); - + shared_ptr out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned); + Position const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2); if (_subtitle_image) { @@ -636,3 +716,16 @@ PlayerImage::image () return out; } + +void +PlayerStatistics::dump (shared_ptr log) const +{ + log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat)); + log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence)); +} + +PlayerStatistics const & +Player::statistics () const +{ + return _statistics; +}