X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fwx%2Fvideo_view.cc;h=387d4052fd87b1f02298b8c6ba8dbac083b43dc8;hb=a70ec9be6a312c63e7655b3a266cbf353e8e1795;hp=eb85079c3795285adf4f5548ff94f86ea4f5e91d;hpb=ac25cd82d5d29c79b46033a742aaea33c700a524;p=dcpomatic.git diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index eb85079c3..387d4052f 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2019 Carl Hetherington + Copyright (C) 2019-2021 Carl Hetherington This file is part of DCP-o-matic. @@ -18,11 +18,158 @@ */ + #include "video_view.h" +#include "wx_util.h" +#include "film_viewer.h" +#include "lib/butler.h" +#include "lib/dcpomatic_log.h" +#include +#include + + +using std::pair; +using std::shared_ptr; +using boost::optional; + + +static constexpr int TOO_MANY_DROPPED_FRAMES = 20; +static constexpr int TOO_MANY_DROPPED_PERIOD = 5.0; + + +VideoView::VideoView (FilmViewer* viewer) + : _viewer (viewer) + , _state_timer ("viewer") +{ + +} + void VideoView::clear () { + boost::mutex::scoped_lock lm (_mutex); _player_video.first.reset (); _player_video.second = dcpomatic::DCPTime (); } + + +/** Could be called from any thread. + * @param non_blocking true to return false quickly if no video is available quickly. + * @return FAIL if there's no frame, AGAIN if the method should be called again, or SUCCESS + * if there is a frame. + */ +VideoView::NextFrameResult +VideoView::get_next_frame (bool non_blocking) +{ + if (length() == dcpomatic::DCPTime()) { + return FAIL; + } + + auto butler = _viewer->butler (); + if (!butler) { + return FAIL; + } + add_get (); + + boost::mutex::scoped_lock lm (_mutex); + + do { + Butler::Error e; + auto pv = butler->get_video (!non_blocking, &e); + if (e.code == Butler::Error::DIED) { + LOG_ERROR ("Butler died with %1", e.summary()); + } + if (!pv.first) { + return e.code == Butler::Error::AGAIN ? AGAIN : FAIL; + } + _player_video = pv; + } while ( + _player_video.first && + _three_d && + _eyes != _player_video.first->eyes() && + _player_video.first->eyes() != Eyes::BOTH + ); + + if (_player_video.first && _player_video.first->error()) { + ++_errored; + } + + return SUCCESS; +} + + +dcpomatic::DCPTime +VideoView::one_video_frame () const +{ + return dcpomatic::DCPTime::from_frames (1, video_frame_rate()); +} + + +/** @return Time in ms until the next frame is due, or empty if nothing is due */ +optional +VideoView::time_until_next_frame () const +{ + if (length() == dcpomatic::DCPTime()) { + /* There's no content, so this doesn't matter */ + return optional(); + } + + auto const next = position() + one_video_frame(); + auto const time = _viewer->audio_time().get_value_or(position()); + if (next < time) { + return 0; + } + return (next.seconds() - time.seconds()) * 1000; +} + + +void +VideoView::start () +{ + boost::mutex::scoped_lock lm (_mutex); + _dropped = 0; + _errored = 0; + gettimeofday(&_dropped_check_period_start, nullptr); +} + + +bool +VideoView::reset_metadata (shared_ptr film, dcp::Size player_video_container_size) +{ + auto pv = player_video (); + if (!pv.first) { + return false; + } + + if (!pv.first->reset_metadata(film, player_video_container_size)) { + return false; + } + + update (); + return true; +} + + +void +VideoView::add_dropped () +{ + bool too_many = false; + + { + boost::mutex::scoped_lock lm (_mutex); + ++_dropped; + if (_dropped > TOO_MANY_DROPPED_FRAMES) { + struct timeval now; + gettimeofday (&now, nullptr); + double const elapsed = seconds(now) - seconds(_dropped_check_period_start); + too_many = elapsed < TOO_MANY_DROPPED_PERIOD; + _dropped = 0; + _dropped_check_period_start = now; + } + } + + if (too_many) { + emit (boost::bind(boost::ref(TooManyDropped))); + } +}