From c76b1fd7fe41a7e371ae1fe1dad21c87a19839f1 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 20 Oct 2019 22:13:46 +0200 Subject: Move ::timer into SimpleVideoView. --- src/wx/film_viewer.cc | 27 +-------------------------- src/wx/film_viewer.h | 6 ++++-- src/wx/simple_video_view.cc | 28 ++++++++++++++++++++++++++++ src/wx/simple_video_view.h | 2 ++ src/wx/video_view.h | 3 +++ 5 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 509cc9426..9dbb0091a 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -108,7 +108,6 @@ FilmViewer::FilmViewer (wxWindow* p) } _video_view->Sized.connect (boost::bind(&FilmViewer::video_view_sized, this)); - _timer.Bind (wxEVT_TIMER, boost::bind(&FilmViewer::timer, this)); set_film (shared_ptr ()); @@ -335,30 +334,6 @@ FilmViewer::display_player_video () _closed_captions_dialog->update (time()); } -void -FilmViewer::timer () -{ - if (!_film || !_playing || _suspended) { - return; - } - - get (false); - DCPTime const next = _video_position + one_video_frame(); - - if (next >= _film->length()) { - stop (); - Finished (); - return; - } - - LOG_DEBUG_PLAYER("%1 -> %2; delay %3", next.seconds(), time().seconds(), max((next.seconds() - time().seconds()) * 1000, 1.0)); - _timer.Start (max ((next.seconds() - time().seconds()) * 1000, 1.0), wxTIMER_ONE_SHOT); - - if (_butler) { - _butler->rethrow (); - } -} - void FilmViewer::set_outline_content (bool o) { @@ -454,7 +429,7 @@ FilmViewer::start () _playing = true; _dropped = 0; - timer (); + _video_view->timer (); Started (position()); } diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 1f20c3321..d64eef4a9 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -149,8 +149,11 @@ public: boost::signals2::signal PlaybackPermitted; private: + + /* XXX_b: to remove */ + friend class SimpleVideoView; + void video_view_sized (); - void timer (); void calculate_sizes (); void player_change (ChangeType type, int, bool); bool get (bool lazy); @@ -172,7 +175,6 @@ private: boost::shared_ptr _player; VideoView* _video_view; - wxTimer _timer; bool _coalesce_player_changes; std::list _pending_player_changes; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 66af8f19f..baf566aee 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -22,6 +22,8 @@ #include "film_viewer.h" #include "wx_util.h" #include "lib/image.h" +#include "lib/dcpomatic_log.h" +#include "lib/butler.h" #include #include #include @@ -45,6 +47,8 @@ SimpleVideoView::SimpleVideoView (FilmViewer* viewer, wxWindow* parent) _panel->Bind (wxEVT_PAINT, boost::bind (&SimpleVideoView::paint, this)); _panel->Bind (wxEVT_SIZE, boost::bind(boost::ref(Sized))); + + _timer.Bind (wxEVT_TIMER, boost::bind(&SimpleVideoView::timer, this)); } void @@ -135,3 +139,27 @@ SimpleVideoView::update () _panel->Refresh (); _panel->Update (); } + +void +SimpleVideoView::timer () +{ + if (!_viewer->_film || !_viewer->_playing) { + return; + } + + _viewer->get (false); + DCPTime const next = _viewer->_video_position + _viewer->one_video_frame(); + + if (next >= _viewer->_film->length()) { + _viewer->stop (); + _viewer->Finished (); + return; + } + + LOG_DEBUG_PLAYER("%1 -> %2; delay %3", next.seconds(), _viewer->time().seconds(), max((next.seconds() - _viewer->time().seconds()) * 1000, 1.0)); + _timer.Start (max ((next.seconds() - _viewer->time().seconds()) * 1000, 1.0), wxTIMER_ONE_SHOT); + + if (_viewer->_butler) { + _viewer->_butler->rethrow (); + } +} diff --git a/src/wx/simple_video_view.h b/src/wx/simple_video_view.h index 686a1a1f3..82c77a4ef 100644 --- a/src/wx/simple_video_view.h +++ b/src/wx/simple_video_view.h @@ -40,7 +40,9 @@ public: private: void paint (); + void timer (); wxPanel* _panel; boost::shared_ptr _image; + wxTimer _timer; }; diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 892ffab12..8b86b2ba1 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -46,6 +46,9 @@ public: boost::signals2::signal Sized; + /* XXX_b: to remove */ + virtual void timer () {} + protected: FilmViewer* _viewer; -- cgit v1.2.3 From 64ff02a143c8364c49554d9631fb6be5f273b54d Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 20 Oct 2019 22:14:53 +0200 Subject: Use FilmViewer::{film,playing,position}() --- src/wx/simple_video_view.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index baf566aee..56e50ebed 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -143,14 +143,14 @@ SimpleVideoView::update () void SimpleVideoView::timer () { - if (!_viewer->_film || !_viewer->_playing) { + if (!_viewer->film() || !_viewer->playing()) { return; } _viewer->get (false); - DCPTime const next = _viewer->_video_position + _viewer->one_video_frame(); + DCPTime const next = _viewer->position() + _viewer->one_video_frame(); - if (next >= _viewer->_film->length()) { + if (next >= _viewer->film()->length()) { _viewer->stop (); _viewer->Finished (); return; -- cgit v1.2.3 From d6d5e36cbeb285c78c33d88a09882d964f83d563 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 20 Oct 2019 22:20:06 +0200 Subject: Remove redundant FilmViewer::video_position() and use FilmViewer::position() instead. --- src/wx/film_viewer.h | 3 --- src/wx/simple_video_view.cc | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index d64eef4a9..d0041bd95 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -135,9 +135,6 @@ public: bool pad_black () const { return _pad_black; } - dcpomatic::DCPTime video_position () const { - return _video_position; - } boost::signals2::signal)> ImageChanged; boost::signals2::signal PositionChanged; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 56e50ebed..9aa73fe7f 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -83,10 +83,10 @@ SimpleVideoView::paint () #ifdef DCPOMATIC_VARIANT_SWAROOP DCPTime const period = DCPTime::from_seconds(Config::instance()->player_watermark_period() * 60); - int64_t n = _viewer->video_position().get() / period.get(); + int64_t n = _viewer->position().get() / period.get(); DCPTime from(n * period.get()); DCPTime to = from + DCPTime::from_seconds(Config::instance()->player_watermark_duration() / 1000.0); - if (from <= _viewer->video_position() && _viewer->video_position() <= to) { + if (from <= _viewer->position() && _viewer->position() <= to) { if (!_in_watermark) { _in_watermark = true; _watermark_x = rand() % panel_size.GetWidth(); -- cgit v1.2.3 From ff64d7f42884bb21e61c5f5b242c41415a5934b1 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 20 Oct 2019 22:24:41 +0200 Subject: Remove temporary access to timer() method. --- src/wx/film_viewer.cc | 2 +- src/wx/simple_video_view.cc | 6 ++++++ src/wx/simple_video_view.h | 2 ++ src/wx/video_view.h | 6 +++--- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 9dbb0091a..be555ad91 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -429,7 +429,7 @@ FilmViewer::start () _playing = true; _dropped = 0; - _video_view->timer (); + _video_view->start (); Started (position()); } diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 9aa73fe7f..37daecda2 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -163,3 +163,9 @@ SimpleVideoView::timer () _viewer->_butler->rethrow (); } } + +void +SimpleVideoView::start () +{ + timer (); +} diff --git a/src/wx/simple_video_view.h b/src/wx/simple_video_view.h index 82c77a4ef..798356cee 100644 --- a/src/wx/simple_video_view.h +++ b/src/wx/simple_video_view.h @@ -38,6 +38,8 @@ public: void update (); + void start (); + private: void paint (); void timer (); diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 8b86b2ba1..dda18058d 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -44,10 +44,10 @@ public: virtual wxWindow* get () const = 0; virtual void update () = 0; - boost::signals2::signal Sized; + /* XXX_b: make pure */ + virtual void start () {} - /* XXX_b: to remove */ - virtual void timer () {} + boost::signals2::signal Sized; protected: FilmViewer* _viewer; -- cgit v1.2.3 From 7c33cdd95a23ff784c0e0731a9d1444ce9bb8f09 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 20 Oct 2019 22:49:07 +0200 Subject: Move FilmViewer::get() into SimpleVideoView. --- src/wx/film_viewer.cc | 45 +-------------------------------------------- src/wx/film_viewer.h | 1 - src/wx/simple_video_view.cc | 45 ++++++++++++++++++++++++++++++++++++++++++++- src/wx/simple_video_view.h | 1 + src/wx/video_view.h | 5 +++++ 5 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index be555ad91..0b6fcd7b3 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -140,7 +140,7 @@ FilmViewer::idle_handler () return; } - if (get(true)) { + if (_video_view->get(true)) { _idle_get = false; } else { /* get() could not complete quickly so we'll try again later */ @@ -236,49 +236,6 @@ FilmViewer::refresh_view () _state_timer.unset (); } -/** Try to get a frame from the butler and display it. - * @param lazy true to return false quickly if no video is available quickly (i.e. we are waiting for the butler). - * false to ask the butler to block until it has video (unless it is suspended). - * @return true on success, false if we did nothing because it would have taken too long. - */ -bool -FilmViewer::get (bool lazy) -{ - DCPOMATIC_ASSERT (_butler); - ++_gets; - - do { - Butler::Error e; - _player_video = _butler->get_video (!lazy, &e); - if (!_player_video.first && e == Butler::AGAIN) { - if (lazy) { - /* No video available; return saying we failed */ - return false; - } else { - /* Player was suspended; come back later */ - signal_manager->when_idle (boost::bind(&FilmViewer::get, this, false)); - return false; - } - } - } while ( - _player_video.first && - _film->three_d() && - _eyes != _player_video.first->eyes() && - _player_video.first->eyes() != EYES_BOTH - ); - - try { - _butler->rethrow (); - } catch (DecodeError& e) { - error_dialog (_video_view->get(), e.what()); - } - - display_player_video (); - PositionChanged (); - - return true; -} - void FilmViewer::display_player_video () { diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index d0041bd95..077d8a6a5 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -153,7 +153,6 @@ private: void video_view_sized (); void calculate_sizes (); void player_change (ChangeType type, int, bool); - bool get (bool lazy); void idle_handler (); void request_idle_get (); void display_player_video (); diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 37daecda2..7d2080003 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -147,7 +147,7 @@ SimpleVideoView::timer () return; } - _viewer->get (false); + get (false); DCPTime const next = _viewer->position() + _viewer->one_video_frame(); if (next >= _viewer->film()->length()) { @@ -169,3 +169,46 @@ SimpleVideoView::start () { timer (); } + +/** Try to get a frame from the butler and display it. + * @param lazy true to return false quickly if no video is available quickly (i.e. we are waiting for the butler). + * false to ask the butler to block until it has video (unless it is suspended). + * @return true on success, false if we did nothing because it would have taken too long. + */ +bool +SimpleVideoView::get (bool lazy) +{ + DCPOMATIC_ASSERT (_viewer->_butler); + _viewer->_gets++; + + do { + Butler::Error e; + _viewer->_player_video = _viewer->_butler->get_video (!lazy, &e); + if (!_viewer->_player_video.first && e == Butler::AGAIN) { + if (lazy) { + /* No video available; return saying we failed */ + return false; + } else { + /* Player was suspended; come back later */ + signal_manager->when_idle (boost::bind(&SimpleVideoView::get, this, false)); + return false; + } + } + } while ( + _viewer->_player_video.first && + _viewer->film()->three_d() && + _viewer->_eyes != _viewer->_player_video.first->eyes() && + _viewer->_player_video.first->eyes() != EYES_BOTH + ); + + try { + _viewer->_butler->rethrow (); + } catch (DecodeError& e) { + error_dialog (get(), e.what()); + } + + _viewer->display_player_video (); + _viewer->PositionChanged (); + + return true; +} diff --git a/src/wx/simple_video_view.h b/src/wx/simple_video_view.h index 798356cee..d27141652 100644 --- a/src/wx/simple_video_view.h +++ b/src/wx/simple_video_view.h @@ -43,6 +43,7 @@ public: private: void paint (); void timer (); + bool get (bool lazy); wxPanel* _panel; boost::shared_ptr _image; diff --git a/src/wx/video_view.h b/src/wx/video_view.h index dda18058d..80d6a50fc 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -49,6 +49,11 @@ public: boost::signals2::signal Sized; + /* XXX_b: to remove */ + virtual bool get (bool) { + return true; + } + protected: FilmViewer* _viewer; -- cgit v1.2.3 From 444e1d4e0062677220d32b78b97a4b730156b514 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 20 Oct 2019 22:54:45 +0200 Subject: Move display_player_video into SimpleVideoView. --- src/wx/film_viewer.cc | 57 +------------------------------------------ src/wx/film_viewer.h | 1 - src/wx/simple_video_view.cc | 59 ++++++++++++++++++++++++++++++++++++++++++++- src/wx/simple_video_view.h | 1 + src/wx/video_view.h | 2 ++ 5 files changed, 62 insertions(+), 58 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 0b6fcd7b3..178311ac9 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -236,61 +236,6 @@ FilmViewer::refresh_view () _state_timer.unset (); } -void -FilmViewer::display_player_video () -{ - if (!_player_video.first) { - _video_view->set_image (shared_ptr()); - refresh_view (); - return; - } - - if (_playing && !_suspended && (time() - _player_video.second) > one_video_frame()) { - /* Too late; just drop this frame before we try to get its image (which will be the time-consuming - part if this frame is J2K). - */ - _video_position = _player_video.second; - ++_dropped; - return; - } - - /* In an ideal world, what we would do here is: - * - * 1. convert to XYZ exactly as we do in the DCP creation path. - * 2. convert back to RGB for the preview display, compensating - * for the monitor etc. etc. - * - * but this is inefficient if the source is RGB. Since we don't - * (currently) care too much about the precise accuracy of the preview's - * colour mapping (and we care more about its speed) we try to short- - * circuit this "ideal" situation in some cases. - * - * The content's specified colour conversion indicates the colourspace - * which the content is in (according to the user). - * - * PlayerVideo::image (bound to PlayerVideo::force) will take the source - * image and convert it (from whatever the user has said it is) to RGB. - */ - - _state_timer.set ("get image"); - - _video_view->set_image ( - _player_video.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true) - ); - - _state_timer.set ("ImageChanged"); - ImageChanged (_player_video.first); - _state_timer.unset (); - - _video_position = _player_video.second; - _inter_position = _player_video.first->inter_position (); - _inter_size = _player_video.first->inter_size (); - - refresh_view (); - - _closed_captions_dialog->update (time()); -} - void FilmViewer::set_outline_content (bool o) { @@ -469,7 +414,7 @@ FilmViewer::quick_refresh () return false; } - display_player_video (); + _video_view->display_player_video (); return true; } diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 077d8a6a5..916491d30 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -155,7 +155,6 @@ private: void player_change (ChangeType type, int, bool); void idle_handler (); void request_idle_get (); - void display_player_video (); void film_change (ChangeType, Film::Property); void recreate_butler (); void config_changed (Config::Property); diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 7d2080003..456d8cd30 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -21,6 +21,7 @@ #include "simple_video_view.h" #include "film_viewer.h" #include "wx_util.h" +#include "closed_captions_dialog.h" #include "lib/image.h" #include "lib/dcpomatic_log.h" #include "lib/butler.h" @@ -31,6 +32,7 @@ using std::max; using std::string; using boost::optional; +using boost::shared_ptr; using namespace dcpomatic; SimpleVideoView::SimpleVideoView (FilmViewer* viewer, wxWindow* parent) @@ -207,8 +209,63 @@ SimpleVideoView::get (bool lazy) error_dialog (get(), e.what()); } - _viewer->display_player_video (); + display_player_video (); _viewer->PositionChanged (); return true; } + +void +SimpleVideoView::display_player_video () +{ + if (!_viewer->_player_video.first) { + set_image (shared_ptr()); + _viewer->refresh_view (); + return; + } + + if (_viewer->playing() && (_viewer->time() - _viewer->_player_video.second) > _viewer->one_video_frame()) { + /* Too late; just drop this frame before we try to get its image (which will be the time-consuming + part if this frame is J2K). + */ + _viewer->_video_position = _viewer->_player_video.second; + ++_viewer->_dropped; + return; + } + + /* In an ideal world, what we would do here is: + * + * 1. convert to XYZ exactly as we do in the DCP creation path. + * 2. convert back to RGB for the preview display, compensating + * for the monitor etc. etc. + * + * but this is inefficient if the source is RGB. Since we don't + * (currently) care too much about the precise accuracy of the preview's + * colour mapping (and we care more about its speed) we try to short- + * circuit this "ideal" situation in some cases. + * + * The content's specified colour conversion indicates the colourspace + * which the content is in (according to the user). + * + * PlayerVideo::image (bound to PlayerVideo::force) will take the source + * image and convert it (from whatever the user has said it is) to RGB. + */ + + _viewer->_state_timer.set ("get image"); + + set_image ( + _viewer->_player_video.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true) + ); + + _viewer->_state_timer.set ("ImageChanged"); + _viewer->ImageChanged (_viewer->_player_video.first); + _viewer->_state_timer.unset (); + + _viewer->_video_position = _viewer->_player_video.second; + _viewer->_inter_position = _viewer->_player_video.first->inter_position (); + _viewer->_inter_size = _viewer->_player_video.first->inter_size (); + + _viewer->refresh_view (); + + _viewer->_closed_captions_dialog->update (_viewer->time()); +} diff --git a/src/wx/simple_video_view.h b/src/wx/simple_video_view.h index d27141652..8527cdbe7 100644 --- a/src/wx/simple_video_view.h +++ b/src/wx/simple_video_view.h @@ -44,6 +44,7 @@ private: void paint (); void timer (); bool get (bool lazy); + void display_player_video (); wxPanel* _panel; boost::shared_ptr _image; diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 80d6a50fc..873df162b 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -53,6 +53,8 @@ public: virtual bool get (bool) { return true; } + /* XXX_b: to remove */ + virtual void display_player_video () {} protected: FilmViewer* _viewer; -- cgit v1.2.3 From ac25cd82d5d29c79b46033a742aaea33c700a524 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 20 Oct 2019 23:03:27 +0200 Subject: Move _player_video into VideoView. --- src/wx/film_viewer.cc | 7 +++---- src/wx/film_viewer.h | 1 - src/wx/simple_video_view.cc | 26 +++++++++++++------------- src/wx/video_view.cc | 28 ++++++++++++++++++++++++++++ src/wx/video_view.h | 8 ++++++++ src/wx/wscript | 1 + 6 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 src/wx/video_view.cc diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 178311ac9..8b90cd9c9 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -157,8 +157,7 @@ FilmViewer::set_film (shared_ptr film) _film = film; _video_position = DCPTime (); - _player_video.first.reset (); - _player_video.second = DCPTime (); + _video_view->clear (); _video_view->set_image (shared_ptr()); _closed_captions_dialog->clear (); @@ -406,11 +405,11 @@ FilmViewer::slow_refresh () bool FilmViewer::quick_refresh () { - if (!_player_video.first) { + if (!_video_view->_player_video.first) { return false; } - if (!_player_video.first->reset_metadata (_film, _player->video_container_size(), _film->frame_size())) { + if (!_video_view->_player_video.first->reset_metadata (_film, _player->video_container_size(), _film->frame_size())) { return false; } diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 916491d30..e98fbe176 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -173,7 +173,6 @@ private: bool _coalesce_player_changes; std::list _pending_player_changes; - std::pair, dcpomatic::DCPTime> _player_video; dcpomatic::DCPTime _video_position; Position _inter_position; dcp::Size _inter_size; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 456d8cd30..91f7df5ff 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -185,8 +185,8 @@ SimpleVideoView::get (bool lazy) do { Butler::Error e; - _viewer->_player_video = _viewer->_butler->get_video (!lazy, &e); - if (!_viewer->_player_video.first && e == Butler::AGAIN) { + _player_video = _viewer->_butler->get_video (!lazy, &e); + if (!_player_video.first && e == Butler::AGAIN) { if (lazy) { /* No video available; return saying we failed */ return false; @@ -197,10 +197,10 @@ SimpleVideoView::get (bool lazy) } } } while ( - _viewer->_player_video.first && + _player_video.first && _viewer->film()->three_d() && - _viewer->_eyes != _viewer->_player_video.first->eyes() && - _viewer->_player_video.first->eyes() != EYES_BOTH + _viewer->_eyes != _player_video.first->eyes() && + _player_video.first->eyes() != EYES_BOTH ); try { @@ -218,17 +218,17 @@ SimpleVideoView::get (bool lazy) void SimpleVideoView::display_player_video () { - if (!_viewer->_player_video.first) { + if (!_player_video.first) { set_image (shared_ptr()); _viewer->refresh_view (); return; } - if (_viewer->playing() && (_viewer->time() - _viewer->_player_video.second) > _viewer->one_video_frame()) { + if (_viewer->playing() && (_viewer->time() - _player_video.second) > _viewer->one_video_frame()) { /* Too late; just drop this frame before we try to get its image (which will be the time-consuming part if this frame is J2K). */ - _viewer->_video_position = _viewer->_player_video.second; + _viewer->_video_position = _player_video.second; ++_viewer->_dropped; return; } @@ -254,16 +254,16 @@ SimpleVideoView::display_player_video () _viewer->_state_timer.set ("get image"); set_image ( - _viewer->_player_video.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true) + _player_video.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true) ); _viewer->_state_timer.set ("ImageChanged"); - _viewer->ImageChanged (_viewer->_player_video.first); + _viewer->ImageChanged (_player_video.first); _viewer->_state_timer.unset (); - _viewer->_video_position = _viewer->_player_video.second; - _viewer->_inter_position = _viewer->_player_video.first->inter_position (); - _viewer->_inter_size = _viewer->_player_video.first->inter_size (); + _viewer->_video_position = _player_video.second; + _viewer->_inter_position = _player_video.first->inter_position (); + _viewer->_inter_size = _player_video.first->inter_size (); _viewer->refresh_view (); diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc new file mode 100644 index 000000000..eb85079c3 --- /dev/null +++ b/src/wx/video_view.cc @@ -0,0 +1,28 @@ +/* + Copyright (C) 2019 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + +#include "video_view.h" + +void +VideoView::clear () +{ + _player_video.first.reset (); + _player_video.second = dcpomatic::DCPTime (); +} diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 873df162b..f4a8ea22b 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -21,12 +21,14 @@ #ifndef DCPOMATIC_VIDEO_VIEW_H #define DCPOMATIC_VIDEO_VIEW_H +#include "lib/dcpomatic_time.h" #include #include class Image; class wxWindow; class FilmViewer; +class PlayerVideo; class VideoView { @@ -47,6 +49,8 @@ public: /* XXX_b: make pure */ virtual void start () {} + void clear (); + boost::signals2::signal Sized; /* XXX_b: to remove */ @@ -57,7 +61,11 @@ public: virtual void display_player_video () {} protected: + /* XXX_b: to remove */ + friend class FilmViewer; + FilmViewer* _viewer; + std::pair, dcpomatic::DCPTime> _player_video; #ifdef DCPOMATIC_VARIANT_SWAROOP bool _in_watermark; diff --git a/src/wx/wscript b/src/wx/wscript index f4fc4927e..5dbf75ecb 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -145,6 +145,7 @@ sources = """ update_dialog.cc verify_dcp_dialog.cc video_panel.cc + video_view.cc video_waveform_dialog.cc video_waveform_plot.cc wx_util.cc -- cgit v1.2.3 From 3b173e309f9f2736aa7e7d09900c5baee4ef31b3 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 20 Oct 2019 23:05:59 +0200 Subject: Add accessor for butler. --- src/wx/film_viewer.h | 3 +++ src/wx/simple_video_view.cc | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index e98fbe176..51419a54d 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -135,6 +135,9 @@ public: bool pad_black () const { return _pad_black; } + boost::shared_ptr butler () const { + return _butler; + } boost::signals2::signal)> ImageChanged; boost::signals2::signal PositionChanged; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 91f7df5ff..f0c63d15a 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -161,8 +161,8 @@ SimpleVideoView::timer () LOG_DEBUG_PLAYER("%1 -> %2; delay %3", next.seconds(), _viewer->time().seconds(), max((next.seconds() - _viewer->time().seconds()) * 1000, 1.0)); _timer.Start (max ((next.seconds() - _viewer->time().seconds()) * 1000, 1.0), wxTIMER_ONE_SHOT); - if (_viewer->_butler) { - _viewer->_butler->rethrow (); + if (_viewer->butler()) { + _viewer->butler()->rethrow (); } } @@ -180,12 +180,12 @@ SimpleVideoView::start () bool SimpleVideoView::get (bool lazy) { - DCPOMATIC_ASSERT (_viewer->_butler); + DCPOMATIC_ASSERT (_viewer->butler()); _viewer->_gets++; do { Butler::Error e; - _player_video = _viewer->_butler->get_video (!lazy, &e); + _player_video = _viewer->butler()->get_video (!lazy, &e); if (!_player_video.first && e == Butler::AGAIN) { if (lazy) { /* No video available; return saying we failed */ @@ -204,7 +204,7 @@ SimpleVideoView::get (bool lazy) ); try { - _viewer->_butler->rethrow (); + _viewer->butler()->rethrow (); } catch (DecodeError& e) { error_dialog (get(), e.what()); } -- cgit v1.2.3 From 0a3f387f5d39da2ca38ec90a9593c1b598040dd7 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 20 Oct 2019 23:24:15 +0200 Subject: Add FilmViewer::time_until_next_frame. --- src/wx/film_viewer.cc | 8 ++++++++ src/wx/film_viewer.h | 3 ++- src/wx/simple_video_view.cc | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 8b90cd9c9..373e6d717 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -633,3 +633,11 @@ FilmViewer::set_pad_black (bool p) { _pad_black = p; } + +/* XXX_b: comment */ +int +FilmViewer::time_until_next_frame () const +{ + DCPTime const next = position() + one_video_frame(); + return max ((next.seconds() - time().seconds()) * 1000, 1.0); +} diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 51419a54d..55356f188 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -119,7 +119,7 @@ public: return _gets; } - /* Some accessors that VideoView classes need */ + /* Some accessors and utility methods that VideoView classes need */ dcp::Size out_size () const { return _out_size; } @@ -138,6 +138,7 @@ public: boost::shared_ptr butler () const { return _butler; } + int time_until_next_frame () const; boost::signals2::signal)> ImageChanged; boost::signals2::signal PositionChanged; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index f0c63d15a..c195bbc35 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -159,7 +159,7 @@ SimpleVideoView::timer () } LOG_DEBUG_PLAYER("%1 -> %2; delay %3", next.seconds(), _viewer->time().seconds(), max((next.seconds() - _viewer->time().seconds()) * 1000, 1.0)); - _timer.Start (max ((next.seconds() - _viewer->time().seconds()) * 1000, 1.0), wxTIMER_ONE_SHOT); + _timer.Start (_viewer->time_until_next_frame()), wxTIMER_ONE_SHOT); if (_viewer->butler()) { _viewer->butler()->rethrow (); -- cgit v1.2.3 From ca3393a75c1685d1615ff678b58bd3b75fe79cd2 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 20 Oct 2019 23:24:32 +0200 Subject: Add FIXME. --- src/wx/simple_video_view.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wx/simple_video_view.h b/src/wx/simple_video_view.h index 8527cdbe7..1d5242a1a 100644 --- a/src/wx/simple_video_view.h +++ b/src/wx/simple_video_view.h @@ -43,6 +43,7 @@ public: private: void paint (); void timer (); + /* XXX_b: rename this (there's already a get() in the parent) */ bool get (bool lazy); void display_player_video (); -- cgit v1.2.3 From 5eb8b5c3a1566aef638e9d9df03b88d320735092 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 21 Oct 2019 00:55:52 +0200 Subject: Barely-functioning GL playback with new arrangement. --- src/lib/cross.cc | 11 ++++++ src/lib/cross.h | 1 + src/tools/dcpomatic.cc | 9 ++++- src/tools/dcpomatic_player.cc | 9 ++++- src/tools/wscript | 2 ++ src/wx/film_viewer.h | 2 ++ src/wx/gl_video_view.cc | 78 ++++++++++++++++++++++++++++++++++++++++--- src/wx/gl_video_view.h | 18 +++++++--- src/wx/simple_video_view.cc | 37 ++++++-------------- src/wx/video_view.cc | 34 +++++++++++++++++++ src/wx/video_view.h | 2 ++ wscript | 3 ++ 12 files changed, 168 insertions(+), 38 deletions(-) diff --git a/src/lib/cross.cc b/src/lib/cross.cc index 8d82f7a51..5d35d5a4b 100644 --- a/src/lib/cross.cc +++ b/src/lib/cross.cc @@ -75,6 +75,17 @@ dcpomatic_sleep_seconds (int s) #endif } +void +dcpomatic_sleep_milliseconds (int ms) +{ +#ifdef DCPOMATIC_POSIX + usleep (ms * 1000); +#endif +#ifdef DCPOMATIC_WINDOWS + Sleep (ms); +#endif +} + /** @return A string of CPU information (model name etc.) */ string cpu_info () diff --git a/src/lib/cross.h b/src/lib/cross.h index 67709463a..584fa2b42 100644 --- a/src/lib/cross.h +++ b/src/lib/cross.h @@ -39,6 +39,7 @@ class Log; struct AVIOContext; void dcpomatic_sleep_seconds (int); +void dcpomatic_sleep_milliseconds (int); extern std::string cpu_info (); extern void run_ffprobe (boost::filesystem::path, boost::filesystem::path); extern std::list > mount_info (); diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index 68bf20732..47851a218 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -92,6 +92,9 @@ #include #include #include +#ifdef __WXGTK__ +#include +#endif #ifdef __WXMSW__ #include #endif @@ -1503,7 +1506,11 @@ public: : wxApp () , _frame (0) , _splash (0) - {} + { +#ifdef DCPOMATIC_LINUX + XInitThreads (); +#endif + } private: diff --git a/src/tools/dcpomatic_player.cc b/src/tools/dcpomatic_player.cc index f68d0ead2..6bd5b36ff 100644 --- a/src/tools/dcpomatic_player.cc +++ b/src/tools/dcpomatic_player.cc @@ -66,6 +66,9 @@ #include #include #include +#ifdef __WXGTK__ +#include +#endif #ifdef __WXOSX__ #include #endif @@ -1044,7 +1047,11 @@ public: App () : wxApp () , _frame (0) - {} + { +#ifdef DCPOMATIC_LINUX + XInitThreads (); +#endif + } private: diff --git a/src/tools/wscript b/src/tools/wscript index 8fd23cfb3..3b2c0a04c 100644 --- a/src/tools/wscript +++ b/src/tools/wscript @@ -68,6 +68,8 @@ def build(bld): obj.uselib += ' WXWIDGETS' if not bld.env.TARGET_OSX: obj.uselib += ' GL GLU' + if bld.env.TARGET_LINUX: + obj.uselib += ' X11' obj.includes = ['..'] obj.use = ['libdcpomatic2', 'libdcpomatic2-wx'] obj.source = '%s.cc' % t diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 55356f188..a59468c36 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -153,6 +153,8 @@ private: /* XXX_b: to remove */ friend class SimpleVideoView; + friend class GLVideoView; + friend class VideoView; void video_view_sized (); void calculate_sizes (); diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index a0f83db6d..c69ab210a 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -23,6 +23,8 @@ #include "lib/image.h" #include "lib/dcpomatic_assert.h" #include "lib/exceptions.h" +#include "lib/cross.h" +#include "lib/player_video.h" #include #include @@ -52,9 +54,9 @@ using boost::optional; GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) : VideoView (viewer) , _vsync_enabled (false) + , _thread (0) { _canvas = new wxGLCanvas (parent, wxID_ANY, 0, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE); - _context = new wxGLContext (_canvas); _canvas->Bind (wxEVT_PAINT, boost::bind(&GLVideoView::paint, this)); _canvas->Bind (wxEVT_SIZE, boost::bind(boost::ref(Sized))); @@ -93,8 +95,13 @@ GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) GLVideoView::~GLVideoView () { + if (_thread) { + _thread->interrupt (); + _thread->join (); + } + delete _thread; + glDeleteTextures (1, &_id); - delete _context; } static void @@ -109,11 +116,14 @@ static void void GLVideoView::paint () { + /* XXX_b: can't do this yet */ +#if 0 _viewer->state_timer().set("paint-panel"); _canvas->SetCurrent (*_context); wxPaintDC dc (_canvas); draw (); _viewer->state_timer().unset(); +#endif } void @@ -122,8 +132,9 @@ GLVideoView::update () if (!_canvas->IsShownOnScreen()) { return; } - wxClientDC dc (_canvas); - draw (); + /* XXX_b */ +// wxClientDC dc (_canvas); +// draw (); } void @@ -248,3 +259,62 @@ GLVideoView::set_image (shared_ptr image) glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); check_gl_error ("glTexParameterf"); } + +void +GLVideoView::start () +{ + _thread = new boost::thread (boost::bind(&GLVideoView::thread, this)); +} + +void +GLVideoView::thread () +try +{ + /* XXX_b: check all calls and signal emissions in this method & protect them if necessary */ + { + boost::mutex::scoped_lock lm (_context_mutex); + _context = new wxGLContext (_canvas); + _canvas->SetCurrent (*_context); + } + + while (true) { + if (!_viewer->film() || !_viewer->playing()) { + dcpomatic_sleep_milliseconds (40); + continue; + } + + dcpomatic::DCPTime const next = _viewer->position() + _viewer->one_video_frame(); + + if (next >= _viewer->film()->length()) { + _viewer->stop (); + _viewer->Finished (); + return; + } + + get_next_frame (false); + set_image (_player_video.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); + draw (); + _viewer->_video_position = _player_video.second; + + std::cout << "sleep " << _viewer->time_until_next_frame() << "\n"; + dcpomatic_sleep_milliseconds (_viewer->time_until_next_frame()); + } + + { + boost::mutex::scoped_lock lm (_context_mutex); + delete _context; + } +} +catch (boost::thread_interrupted& e) +{ + /* XXX_b: store exceptions here */ + delete _context; + return; +} + +wxGLContext * +GLVideoView::context () const +{ + boost::mutex::scoped_lock lm (_context_mutex); + return _context; +} diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index ba4c7cfdc..e32a1ede9 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -23,6 +23,7 @@ #include #include #include +#include #undef None #undef Success @@ -37,18 +38,25 @@ public: return _canvas; } void update (); + void start (); bool vsync_enabled () const { return _vsync_enabled; } private: - void paint (); - void draw (); + void paint (); + void draw (); + void thread (); + wxGLContext* context () const; wxGLCanvas* _canvas; - wxGLContext* _context; - GLuint _id; - boost::optional _size; + + wxGLContext* _context; + mutable boost::mutex _context_mutex; + + GLuint _id; + boost::optional _size; bool _vsync_enabled; + boost::thread* _thread; }; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index c195bbc35..1e97adb30 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -159,7 +159,7 @@ SimpleVideoView::timer () } LOG_DEBUG_PLAYER("%1 -> %2; delay %3", next.seconds(), _viewer->time().seconds(), max((next.seconds() - _viewer->time().seconds()) * 1000, 1.0)); - _timer.Start (_viewer->time_until_next_frame()), wxTIMER_ONE_SHOT); + _timer.Start (_viewer->time_until_next_frame(), wxTIMER_ONE_SHOT); if (_viewer->butler()) { _viewer->butler()->rethrow (); @@ -180,33 +180,16 @@ SimpleVideoView::start () bool SimpleVideoView::get (bool lazy) { - DCPOMATIC_ASSERT (_viewer->butler()); - _viewer->_gets++; - - do { - Butler::Error e; - _player_video = _viewer->butler()->get_video (!lazy, &e); - if (!_player_video.first && e == Butler::AGAIN) { - if (lazy) { - /* No video available; return saying we failed */ - return false; - } else { - /* Player was suspended; come back later */ - signal_manager->when_idle (boost::bind(&SimpleVideoView::get, this, false)); - return false; - } + bool r = get_next_frame (lazy); + if (!r) { + if (lazy) { + /* No video available; return saying we failed */ + return false; + } else { + /* Player was suspended; come back later */ + signal_manager->when_idle (boost::bind(&SimpleVideoView::get, this, false)); + return false; } - } while ( - _player_video.first && - _viewer->film()->three_d() && - _viewer->_eyes != _player_video.first->eyes() && - _player_video.first->eyes() != EYES_BOTH - ); - - try { - _viewer->butler()->rethrow (); - } catch (DecodeError& e) { - error_dialog (get(), e.what()); } display_player_video (); diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index eb85079c3..22cad3979 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -19,6 +19,9 @@ */ #include "video_view.h" +#include "wx_util.h" +#include "film_viewer.h" +#include "lib/butler.h" void VideoView::clear () @@ -26,3 +29,34 @@ VideoView::clear () _player_video.first.reset (); _player_video.second = dcpomatic::DCPTime (); } + +/** @param non_blocking true to return false quickly if no video is available quickly. + * @return false if we gave up because it would take too long, otherwise true. + */ +bool +VideoView::get_next_frame (bool non_blocking) +{ + DCPOMATIC_ASSERT (_viewer->butler()); + _viewer->_gets++; + + do { + Butler::Error e; + _player_video = _viewer->butler()->get_video (!non_blocking, &e); + if (!_player_video.first && e == Butler::AGAIN) { + return false; + } + } while ( + _player_video.first && + _viewer->film()->three_d() && + _viewer->_eyes != _player_video.first->eyes() && + _player_video.first->eyes() != EYES_BOTH + ); + + try { + _viewer->butler()->rethrow (); + } catch (DecodeError& e) { + error_dialog (get(), e.what()); + } + + return true; +} diff --git a/src/wx/video_view.h b/src/wx/video_view.h index f4a8ea22b..827d1bf73 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -64,6 +64,8 @@ protected: /* XXX_b: to remove */ friend class FilmViewer; + bool get_next_frame (bool non_blocking); + FilmViewer* _viewer; std::pair, dcpomatic::DCPTime> _player_video; diff --git a/wscript b/wscript index 9f45778e9..fe1431316 100644 --- a/wscript +++ b/wscript @@ -444,6 +444,9 @@ def configure(conf): conf.env['CXXFLAGS_AVCODEC'] = [] conf.env['CXXFLAGS_AVUTIL'] = [] + if conf.env.TARGET_LINUX: + conf.env.LIB_X11 = ['X11'] + # Boost if conf.options.static_boost: conf.env.STLIB_BOOST_THREAD = ['boost_thread'] -- cgit v1.2.3 From 15e82df97cc99b94a7028313dff4eba213ecd84d Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 5 Nov 2019 23:26:22 +0100 Subject: Hack around changes in main branch with respect to suspend/resume. --- src/wx/film_viewer.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 373e6d717..dc6ed10c4 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -306,7 +306,7 @@ FilmViewer::resume () _audio.setStreamTime (_video_position.seconds()); _audio.startStream (); } - timer (); + _video_view->start (); } } @@ -461,9 +461,6 @@ FilmViewer::seek (DCPTime t, bool accurate) if (!_playing) { request_idle_get (); - } else { - /* Make sure we get a frame so that _video_position is set up before we resume */ - while (!get(true)) {} } resume (); -- cgit v1.2.3 From 694a9f48880efd428c8137e975de3581ad0a75a9 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 5 Nov 2019 23:51:20 +0100 Subject: Remove PositionChanged in favour of consumers having their own GUI-thread timers. --- src/wx/controls.cc | 9 ++++++--- src/wx/controls.h | 4 +++- src/wx/film_viewer.cc | 2 -- src/wx/film_viewer.h | 1 - src/wx/simple_video_view.cc | 1 - src/wx/swaroop_controls.cc | 11 +++++++---- src/wx/swaroop_controls.h | 4 +++- src/wx/timeline.cc | 8 ++++---- src/wx/timeline.h | 4 ++-- 9 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/wx/controls.cc b/src/wx/controls.cc index b173b43ff..71af4e8cf 100644 --- a/src/wx/controls.cc +++ b/src/wx/controls.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2018 Carl Hetherington + Copyright (C) 2018-2019 Carl Hetherington This file is part of DCP-o-matic. @@ -67,6 +67,7 @@ Controls::Controls (wxWindow* parent, shared_ptr viewer, bool editor , _forward_button (new Button (this, wxT(">"))) , _frame_number (new StaticText (this, wxT(""))) , _timecode (new StaticText (this, wxT(""))) + , _timer (this) { _v_sizer = new wxBoxSizer (wxVERTICAL); SetSizer (_v_sizer); @@ -137,10 +138,12 @@ Controls::Controls (wxWindow* parent, shared_ptr viewer, bool editor _jump_to_selected->SetValue (Config::instance()->jump_to_selected ()); } - _viewer->PositionChanged.connect (boost::bind(&Controls::position_changed, this)); _viewer->Started.connect (boost::bind(&Controls::started, this)); _viewer->Stopped.connect (boost::bind(&Controls::stopped, this)); + Bind (wxEVT_TIMER, boost::bind(&Controls::update_position, this)); + _timer.Start (80, wxTIMER_CONTINUOUS); + set_film (_viewer->film()); setup_sensitivity (); @@ -172,7 +175,7 @@ Controls::stopped () } void -Controls::position_changed () +Controls::update_position () { if (!_slider_being_moved) { update_position_label (); diff --git a/src/wx/controls.h b/src/wx/controls.h index dfa11e6d7..014464456 100644 --- a/src/wx/controls.h +++ b/src/wx/controls.h @@ -87,7 +87,7 @@ private: void image_changed (boost::weak_ptr); void outline_content_changed (); void eye_changed (); - void position_changed (); + void update_position (); void film_change (ChangeType, Film::Property); typedef std::pair, boost::filesystem::path> CPL; @@ -105,6 +105,8 @@ private: ClosedCaptionsDialog* _closed_captions_dialog; + wxTimer _timer; + boost::signals2::scoped_connection _film_change_connection; boost::signals2::scoped_connection _config_changed_connection; }; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index dc6ed10c4..5c73f292c 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -256,7 +256,6 @@ FilmViewer::video_view_sized () if (!quick_refresh()) { slow_refresh (); } - PositionChanged (); } void @@ -380,7 +379,6 @@ FilmViewer::player_change (ChangeType type, int property, bool frequent) if (!refreshed) { slow_refresh (); } - PositionChanged (); } void diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index a59468c36..6b6aa78f5 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -141,7 +141,6 @@ public: int time_until_next_frame () const; boost::signals2::signal)> ImageChanged; - boost::signals2::signal PositionChanged; boost::signals2::signal Started; boost::signals2::signal Stopped; /** While playing back we reached the end of the film (emitted from GUI thread) */ diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 1e97adb30..619a35cce 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -193,7 +193,6 @@ SimpleVideoView::get (bool lazy) } display_player_video (); - _viewer->PositionChanged (); return true; } diff --git a/src/wx/swaroop_controls.cc b/src/wx/swaroop_controls.cc index 5ce6c45fc..add9bf3e0 100644 --- a/src/wx/swaroop_controls.cc +++ b/src/wx/swaroop_controls.cc @@ -54,6 +54,7 @@ SwaroopControls::SwaroopControls (wxWindow* parent, shared_ptr viewe , _previous_button (new Button(this, "Previous")) , _current_disable_timeline (false) , _current_disable_next (false) + , _timer (this) { _button_sizer->Add (_previous_button, 0, wxEXPAND); _button_sizer->Add (_play_button, 0, wxEXPAND); @@ -112,10 +113,13 @@ SwaroopControls::SwaroopControls (wxWindow* parent, shared_ptr viewe _spl_view->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&SwaroopControls::spl_selection_changed, this)); _spl_view->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&SwaroopControls::spl_selection_changed, this)); _viewer->Finished.connect (boost::bind(&SwaroopControls::viewer_finished, this)); - _viewer->PositionChanged.connect (boost::bind(&SwaroopControls::viewer_position_changed, this)); _refresh_spl_view->Bind (wxEVT_BUTTON, boost::bind(&SwaroopControls::update_playlist_directory, this)); _refresh_content_view->Bind (wxEVT_BUTTON, boost::bind(&ContentView::update, _content_view)); + /* Write position every two minutes if we're playing */ + Bind (wxEVT_TIMER, boost::bind(&SwaroopControls::write_position, this)); + _timer.Start (2 * 60 * 1000, wxTIMER_CONTINUOUS); + _content_view->update (); update_playlist_directory (); @@ -153,10 +157,9 @@ SwaroopControls::check_restart () } void -SwaroopControls::viewer_position_changed () +SwaroopControls::write_position () { - /* Write position every two minutes if we're playing */ - if (!_selected_playlist || !_viewer->playing() || _viewer->position().get() % (2 * 60 * DCPTime::HZ)) { + if (!_selected_playlist || !_viewer->playing()) { return; } diff --git a/src/wx/swaroop_controls.h b/src/wx/swaroop_controls.h index 11dbcfc10..a8bb41dea 100644 --- a/src/wx/swaroop_controls.h +++ b/src/wx/swaroop_controls.h @@ -54,7 +54,7 @@ private: void setup_sensitivity (); void config_changed (int); void viewer_finished (); - void viewer_position_changed (); + void write_position (); void reset_film (); void update_current_content (); bool can_do_previous (); @@ -84,4 +84,6 @@ private: std::vector _playlists; boost::optional _selected_playlist; int _selected_playlist_position; + + wxTimer _timer; }; diff --git a/src/wx/timeline.cc b/src/wx/timeline.cc index daeeb0a51..9f71847f5 100644 --- a/src/wx/timeline.cc +++ b/src/wx/timeline.cc @@ -82,6 +82,7 @@ Timeline::Timeline (wxWindow* parent, ContentPanel* cp, shared_ptr film, w , _y_scroll_rate (16) , _pixels_per_track (48) , _first_resize (true) + , _timer (this) { #ifndef __WXOSX__ _labels_canvas->SetDoubleBuffered (true); @@ -116,16 +117,15 @@ Timeline::Timeline (wxWindow* parent, ContentPanel* cp, shared_ptr film, w _film_changed_connection = film->Change.connect (bind (&Timeline::film_change, this, _1, _2)); _film_content_change_connection = film->ContentChange.connect (bind (&Timeline::film_content_change, this, _1, _3, _4)); - shared_ptr vp = viewer.lock (); - DCPOMATIC_ASSERT (vp); - _viewer_position_change_connection = vp->PositionChanged.connect (bind(&Timeline::position_change, this)); + Bind (wxEVT_TIMER, boost::bind(&Timeline::update_playhead, this)); + _timer.Start (200, wxTIMER_CONTINUOUS); setup_scrollbars (); _labels_canvas->ShowScrollbars (wxSHOW_SB_NEVER, wxSHOW_SB_NEVER); } void -Timeline::position_change () +Timeline::update_playhead () { Refresh (); } diff --git a/src/wx/timeline.h b/src/wx/timeline.h index ef887dab8..44a897371 100644 --- a/src/wx/timeline.h +++ b/src/wx/timeline.h @@ -104,7 +104,7 @@ private: void set_pixels_per_second (double pps); void set_pixels_per_track (int h); void zoom_all (); - void position_change (); + void update_playhead (); boost::shared_ptr event_to_view (wxMouseEvent &); TimelineContentViewList selected_views () const; @@ -137,11 +137,11 @@ private: int _y_scroll_rate; int _pixels_per_track; bool _first_resize; + wxTimer _timer; static double const _minimum_pixels_per_second; static int const _minimum_pixels_per_track; boost::signals2::scoped_connection _film_changed_connection; boost::signals2::scoped_connection _film_content_change_connection; - boost::signals2::scoped_connection _viewer_position_change_connection; }; -- cgit v1.2.3 From 805487369e57e5eb57911805ba6de78b653d79ad Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Nov 2019 01:09:13 +0100 Subject: Various timing hacks and development. --- src/wx/film_viewer.cc | 27 ++++++++++++++++----------- src/wx/film_viewer.h | 5 ++--- src/wx/gl_video_view.cc | 23 ++++++++++++++++++----- src/wx/gl_video_view.h | 3 +++ src/wx/simple_video_view.cc | 14 ++++++-------- src/wx/simple_video_view.h | 4 +--- src/wx/video_view.h | 10 ++++++---- 7 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 5c73f292c..f40ed229f 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -122,7 +122,7 @@ FilmViewer::~FilmViewer () /** Ask for ::get() to be called next time we are idle */ void -FilmViewer::request_idle_get () +FilmViewer::request_idle_display_next_frame () { if (_idle_get) { return; @@ -140,7 +140,7 @@ FilmViewer::idle_handler () return; } - if (_video_view->get(true)) { + if (_video_view->display_next_frame(true)) { _idle_get = false; } else { /* get() could not complete quickly so we'll try again later */ @@ -156,7 +156,6 @@ FilmViewer::set_film (shared_ptr film) } _film = film; - _video_position = DCPTime (); _video_view->clear (); _video_view->set_image (shared_ptr()); @@ -302,7 +301,7 @@ FilmViewer::resume () --_suspended; if (_playing && !_suspended) { if (_audio.isStreamOpen()) { - _audio.setStreamTime (_video_position.seconds()); + _audio.setStreamTime (_video_view->position().seconds()); _audio.startStream (); } _video_view->start (); @@ -323,7 +322,7 @@ FilmViewer::start () } if (_audio.isStreamOpen()) { - _audio.setStreamTime (_video_position.seconds()); + _audio.setStreamTime (_video_view->position().seconds()); _audio.startStream (); } @@ -393,7 +392,7 @@ FilmViewer::film_change (ChangeType type, Film::Property p) void FilmViewer::slow_refresh () { - seek (_video_position, true); + seek (_video_view->position(), true); } /** Try to re-get the current frame quickly by resetting the metadata @@ -458,7 +457,9 @@ FilmViewer::seek (DCPTime t, bool accurate) _butler->seek (t, accurate); if (!_playing) { - request_idle_get (); + request_idle_display_next_frame (); + } else { + while (!_video_view->display_next_frame(false)) {} } resume (); @@ -536,7 +537,7 @@ FilmViewer::uncorrected_time () const return DCPTime::from_seconds (const_cast(&_audio)->getStreamTime()); } - return _video_position; + return _video_view->position(); } DCPTime @@ -547,7 +548,7 @@ FilmViewer::time () const DCPTime::from_frames (average_latency(), _film->audio_frame_rate()); } - return _video_position; + return _video_view->position(); } int @@ -620,7 +621,7 @@ FilmViewer::show_closed_captions () void FilmViewer::seek_by (DCPTime by, bool accurate) { - seek (_video_position + by, accurate); + seek (_video_view->position() + by, accurate); } void @@ -634,5 +635,9 @@ int FilmViewer::time_until_next_frame () const { DCPTime const next = position() + one_video_frame(); - return max ((next.seconds() - time().seconds()) * 1000, 1.0); + std::cout << to_string(next) << " " << to_string(time()) << " " << ((next.seconds() - time().seconds()) * 1000) << "\n"; + if (next < time()) { + return 0; + } + return (next.seconds() - time().seconds()) * 1000; } diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 6b6aa78f5..beb1a5d74 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -69,7 +69,7 @@ public: void seek_by (dcpomatic::DCPTime by, bool accurate); /** @return our `playhead' position; this may not lie exactly on a frame boundary */ dcpomatic::DCPTime position () const { - return _video_position; + return _video_view->position(); } dcpomatic::DCPTime one_video_frame () const; @@ -159,7 +159,7 @@ private: void calculate_sizes (); void player_change (ChangeType type, int, bool); void idle_handler (); - void request_idle_get (); + void request_idle_display_next_frame (); void film_change (ChangeType, Film::Property); void recreate_butler (); void config_changed (Config::Property); @@ -178,7 +178,6 @@ private: bool _coalesce_player_changes; std::list _pending_player_changes; - dcpomatic::DCPTime _video_position; Position _inter_position; dcp::Size _inter_size; diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index c69ab210a..ebf8b8fe2 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -55,6 +55,7 @@ GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) : VideoView (viewer) , _vsync_enabled (false) , _thread (0) + , _one_shot (false) { _canvas = new wxGLCanvas (parent, wxID_ANY, 0, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE); _canvas->Bind (wxEVT_PAINT, boost::bind(&GLVideoView::paint, this)); @@ -105,7 +106,7 @@ GLVideoView::~GLVideoView () } static void - check_gl_error (char const * last) +check_gl_error (char const * last) { GLenum const e = glGetError (); if (e != GL_NO_ERROR) { @@ -278,25 +279,29 @@ try } while (true) { - if (!_viewer->film() || !_viewer->playing()) { + if ((!_viewer->film() || !_viewer->playing()) && !_one_shot) { dcpomatic_sleep_milliseconds (40); continue; } + _one_shot = false; + dcpomatic::DCPTime const next = _viewer->position() + _viewer->one_video_frame(); if (next >= _viewer->film()->length()) { _viewer->stop (); _viewer->Finished (); - return; + continue; } get_next_frame (false); set_image (_player_video.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); draw (); - _viewer->_video_position = _player_video.second; - std::cout << "sleep " << _viewer->time_until_next_frame() << "\n"; + while (_viewer->time_until_next_frame() < 5) { + get_next_frame (true); + } + dcpomatic_sleep_milliseconds (_viewer->time_until_next_frame()); } @@ -318,3 +323,11 @@ GLVideoView::context () const boost::mutex::scoped_lock lm (_context_mutex); return _context; } + +bool +GLVideoView::display_next_frame (bool non_blocking) +{ + bool const g = get_next_frame (non_blocking); + _one_shot = true; + return g; +} diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index e32a1ede9..4ad4b1283 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -40,6 +40,8 @@ public: void update (); void start (); + bool display_next_frame (bool); + bool vsync_enabled () const { return _vsync_enabled; } @@ -59,4 +61,5 @@ private: boost::optional _size; bool _vsync_enabled; boost::thread* _thread; + bool _one_shot; }; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 619a35cce..e66ed815e 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -149,7 +149,7 @@ SimpleVideoView::timer () return; } - get (false); + display_next_frame (false); DCPTime const next = _viewer->position() + _viewer->one_video_frame(); if (next >= _viewer->film()->length()) { @@ -173,21 +173,21 @@ SimpleVideoView::start () } /** Try to get a frame from the butler and display it. - * @param lazy true to return false quickly if no video is available quickly (i.e. we are waiting for the butler). + * @param non_blocking true to return false quickly if no video is available quickly (i.e. we are waiting for the butler). * false to ask the butler to block until it has video (unless it is suspended). * @return true on success, false if we did nothing because it would have taken too long. */ bool -SimpleVideoView::get (bool lazy) +SimpleVideoView::display_next_frame (bool non_blocking) { - bool r = get_next_frame (lazy); + bool r = get_next_frame (non_blocking); if (!r) { - if (lazy) { + if (non_blocking) { /* No video available; return saying we failed */ return false; } else { /* Player was suspended; come back later */ - signal_manager->when_idle (boost::bind(&SimpleVideoView::get, this, false)); + signal_manager->when_idle (boost::bind(&SimpleVideoView::display_next_frame, this, false)); return false; } } @@ -210,7 +210,6 @@ SimpleVideoView::display_player_video () /* Too late; just drop this frame before we try to get its image (which will be the time-consuming part if this frame is J2K). */ - _viewer->_video_position = _player_video.second; ++_viewer->_dropped; return; } @@ -243,7 +242,6 @@ SimpleVideoView::display_player_video () _viewer->ImageChanged (_player_video.first); _viewer->_state_timer.unset (); - _viewer->_video_position = _player_video.second; _viewer->_inter_position = _player_video.first->inter_position (); _viewer->_inter_size = _player_video.first->inter_size (); diff --git a/src/wx/simple_video_view.h b/src/wx/simple_video_view.h index 1d5242a1a..e8bb932e4 100644 --- a/src/wx/simple_video_view.h +++ b/src/wx/simple_video_view.h @@ -37,14 +37,12 @@ public: } void update (); - void start (); + bool display_next_frame (bool non_blocking); private: void paint (); void timer (); - /* XXX_b: rename this (there's already a get() in the parent) */ - bool get (bool lazy); void display_player_video (); wxPanel* _panel; diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 827d1bf73..d6e76ada7 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -53,13 +53,15 @@ public: boost::signals2::signal Sized; - /* XXX_b: to remove */ - virtual bool get (bool) { - return true; - } + virtual bool display_next_frame (bool) = 0; + /* XXX_b: to remove */ virtual void display_player_video () {} + dcpomatic::DCPTime position () const { + return _player_video.second; + } + protected: /* XXX_b: to remove */ friend class FilmViewer; -- cgit v1.2.3 From 046d84f45621f7e128cb30160a315f98881c6f4b Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 17 Nov 2019 18:13:36 +0100 Subject: A little thread safety. --- src/wx/film_viewer.cc | 32 +++++++++++++++---------- src/wx/film_viewer.h | 7 ++++-- src/wx/gl_video_view.cc | 58 ++++++++++++++++++++++++++++++++++++++------- src/wx/gl_video_view.h | 6 +++++ src/wx/simple_video_view.cc | 6 ++--- src/wx/video_view.cc | 23 ++++++++++++++++++ src/wx/video_view.h | 22 +++++++++++++++++ 7 files changed, 128 insertions(+), 26 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index f40ed229f..f3250dbfa 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -156,9 +156,9 @@ FilmViewer::set_film (shared_ptr film) } _film = film; - _video_view->clear (); - _video_view->set_image (shared_ptr()); + _video_view->set_film (_film); + _video_view->clear (); _closed_captions_dialog->clear (); if (!_film) { @@ -326,8 +326,8 @@ FilmViewer::start () _audio.startStream (); } - _playing = true; _dropped = 0; + _playing = true; _video_view->start (); Started (position()); } @@ -345,6 +345,7 @@ FilmViewer::stop () } _playing = false; + _video_view->stop (); Stopped (position()); return true; } @@ -540,6 +541,17 @@ FilmViewer::uncorrected_time () const return _video_view->position(); } +optional +FilmViewer::audio_time () const +{ + if (!_audio.isStreamRunning()) { + return optional(); + } + + return DCPTime::from_seconds (const_cast(&_audio)->getStreamTime ()) - + DCPTime::from_frames (average_latency(), _film->audio_frame_rate()); +} + DCPTime FilmViewer::time () const { @@ -630,14 +642,10 @@ FilmViewer::set_pad_black (bool p) _pad_black = p; } -/* XXX_b: comment */ -int -FilmViewer::time_until_next_frame () const +/* May be called from a non-UI thread */ +void +FilmViewer::emit_finished () { - DCPTime const next = position() + one_video_frame(); - std::cout << to_string(next) << " " << to_string(time()) << " " << ((next.seconds() - time().seconds()) * 1000) << "\n"; - if (next < time()) { - return 0; - } - return (next.seconds() - time().seconds()) * 1000; + emit (boost::bind(boost::ref(Finished))); } + diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index beb1a5d74..eaf46f1e6 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -27,6 +27,7 @@ #include "lib/config.h" #include "lib/player_text.h" #include "lib/timer.h" +#include "lib/signaller.h" #include #include @@ -42,7 +43,7 @@ class ClosedCaptionsDialog; /** @class FilmViewer * @brief A wx widget to view a Film. */ -class FilmViewer +class FilmViewer : public Signaller { public: FilmViewer (wxWindow *); @@ -77,6 +78,7 @@ public: bool stop (); void suspend (); void resume (); + bool playing () const { return _playing; } @@ -138,13 +140,13 @@ public: boost::shared_ptr butler () const { return _butler; } - int time_until_next_frame () const; boost::signals2::signal)> ImageChanged; boost::signals2::signal Started; boost::signals2::signal Stopped; /** While playing back we reached the end of the film (emitted from GUI thread) */ boost::signals2::signal Finished; + void emit_finished (); boost::signals2::signal PlaybackPermitted; @@ -165,6 +167,7 @@ private: void config_changed (Config::Property); dcpomatic::DCPTime time () const; + boost::optional audio_time () const; dcpomatic::DCPTime uncorrected_time () const; Frame average_latency () const; diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index ebf8b8fe2..c3a611283 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -267,6 +267,31 @@ GLVideoView::start () _thread = new boost::thread (boost::bind(&GLVideoView::thread, this)); } +void +GLVideoView::stop () +{ + if (_thread) { + _thread->interrupt (); + _thread->join (); + } + delete _thread; + _thread = 0; +} + +bool +GLVideoView::one_shot () const +{ + boost::mutex::scoped_lock lm (_one_shot_mutex); + return _one_shot; +} + +void +GLVideoView::set_one_shot (bool s) +{ + boost::mutex::scoped_lock lm (_one_shot_mutex); + _one_shot = s; +} + void GLVideoView::thread () try @@ -279,30 +304,37 @@ try } while (true) { - if ((!_viewer->film() || !_viewer->playing()) && !_one_shot) { + if (!film() && !one_shot()) { + /* XXX: this should be an indefinite wait until + one of our conditions becomes true. + */ dcpomatic_sleep_milliseconds (40); continue; } - _one_shot = false; + set_one_shot (false); - dcpomatic::DCPTime const next = _viewer->position() + _viewer->one_video_frame(); + dcpomatic::DCPTime const next = position() + one_video_frame(); - if (next >= _viewer->film()->length()) { + if (next >= film()->length()) { _viewer->stop (); - _viewer->Finished (); + _viewer->emit_finished (); continue; } get_next_frame (false); - set_image (_player_video.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); + { + boost::mutex::scoped_lock lm (_mutex); + set_image (_player_video.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); + } draw (); - while (_viewer->time_until_next_frame() < 5) { + while (time_until_next_frame() < 5) { get_next_frame (true); } - dcpomatic_sleep_milliseconds (_viewer->time_until_next_frame()); + boost::this_thread::interruption_point (); + dcpomatic_sleep_milliseconds (time_until_next_frame()); } { @@ -328,6 +360,14 @@ bool GLVideoView::display_next_frame (bool non_blocking) { bool const g = get_next_frame (non_blocking); - _one_shot = true; + set_one_shot (true); return g; } + +dcpomatic::DCPTime +GLVideoView::one_video_frame () const +{ + return dcpomatic::DCPTime::from_frames (1, film()->video_frame_rate()); +} + + diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index 4ad4b1283..cf42432a9 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -19,6 +19,7 @@ */ #include "video_view.h" +#include "lib/signaller.h" #include #include #include @@ -39,6 +40,7 @@ public: } void update (); void start (); + void stop (); bool display_next_frame (bool); @@ -51,6 +53,9 @@ private: void draw (); void thread (); wxGLContext* context () const; + bool one_shot () const; + void set_one_shot (bool s); + dcpomatic::DCPTime one_video_frame () const; wxGLCanvas* _canvas; @@ -61,5 +66,6 @@ private: boost::optional _size; bool _vsync_enabled; boost::thread* _thread; + mutable boost::mutex _one_shot_mutex; bool _one_shot; }; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index e66ed815e..a00524f7d 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -145,21 +145,21 @@ SimpleVideoView::update () void SimpleVideoView::timer () { - if (!_viewer->film() || !_viewer->playing()) { + if (!film() || !_viewer->playing()) { return; } display_next_frame (false); DCPTime const next = _viewer->position() + _viewer->one_video_frame(); - if (next >= _viewer->film()->length()) { + if (next >= film()->length()) { _viewer->stop (); _viewer->Finished (); return; } LOG_DEBUG_PLAYER("%1 -> %2; delay %3", next.seconds(), _viewer->time().seconds(), max((next.seconds() - _viewer->time().seconds()) * 1000, 1.0)); - _timer.Start (_viewer->time_until_next_frame(), wxTIMER_ONE_SHOT); + _timer.Start (time_until_next_frame(), wxTIMER_ONE_SHOT); if (_viewer->butler()) { _viewer->butler()->rethrow (); diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index 22cad3979..e1a8b7306 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -26,6 +26,7 @@ void VideoView::clear () { + boost::mutex::scoped_lock lm (_mutex); _player_video.first.reset (); _player_video.second = dcpomatic::DCPTime (); } @@ -39,6 +40,8 @@ VideoView::get_next_frame (bool non_blocking) DCPOMATIC_ASSERT (_viewer->butler()); _viewer->_gets++; + boost::mutex::scoped_lock lm (_mutex); + do { Butler::Error e; _player_video = _viewer->butler()->get_video (!non_blocking, &e); @@ -60,3 +63,23 @@ VideoView::get_next_frame (bool non_blocking) return true; } + +dcpomatic::DCPTime +VideoView::one_video_frame () const +{ + return dcpomatic::DCPTime::from_frames (1, film()->video_frame_rate()); +} + +/* XXX_b: comment */ +int +VideoView::time_until_next_frame () const +{ + dcpomatic::DCPTime const next = position() + one_video_frame(); + dcpomatic::DCPTime const time = _viewer->audio_time().get_value_or(position()); + std::cout << to_string(next) << " " << to_string(time) << " " << ((next.seconds() - time.seconds()) * 1000) << "\n"; + if (next < time) { + return 0; + } + return (next.seconds() - time.seconds()) * 1000; +} + diff --git a/src/wx/video_view.h b/src/wx/video_view.h index d6e76ada7..d9ef2a65f 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -24,6 +24,7 @@ #include "lib/dcpomatic_time.h" #include #include +#include class Image; class wxWindow; @@ -48,6 +49,8 @@ public: /* XXX_b: make pure */ virtual void start () {} + /* XXX_b: make pure */ + virtual void stop () {} void clear (); @@ -59,23 +62,42 @@ public: virtual void display_player_video () {} dcpomatic::DCPTime position () const { + boost::mutex::scoped_lock lm (_mutex); return _player_video.second; } + void set_film (boost::shared_ptr film) { + boost::mutex::scoped_lock lm (_mutex); + _film = film; + } + protected: /* XXX_b: to remove */ friend class FilmViewer; bool get_next_frame (bool non_blocking); + int time_until_next_frame () const; + dcpomatic::DCPTime one_video_frame () const; + + boost::shared_ptr film () const { + boost::mutex::scoped_lock lm (_mutex); + return _film; + } FilmViewer* _viewer; std::pair, dcpomatic::DCPTime> _player_video; + /** Mutex protecting all the state in VideoView */ + mutable boost::mutex _mutex; + #ifdef DCPOMATIC_VARIANT_SWAROOP bool _in_watermark; int _watermark_x; int _watermark_y; #endif + +private: + boost::shared_ptr _film; }; #endif -- cgit v1.2.3 From 86a866d5f3f5bf2fec67d1c813524479c6727eab Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 19 Nov 2019 17:07:35 +0100 Subject: Clean up access to stuff from Film. --- src/lib/film.cc | 7 +++++++ src/lib/film.h | 7 +++++++ src/lib/playlist.cc | 11 ++++++++++- src/lib/playlist.h | 2 ++ src/wx/film_viewer.cc | 16 ++++++++++++++-- src/wx/film_viewer.h | 1 + src/wx/gl_video_view.cc | 40 ++++------------------------------------ src/wx/gl_video_view.h | 5 ----- src/wx/simple_video_view.cc | 4 ++-- src/wx/video_view.cc | 2 +- src/wx/video_view.h | 22 ++++++++++++++++------ 11 files changed, 64 insertions(+), 53 deletions(-) diff --git a/src/lib/film.cc b/src/lib/film.cc index 2a50e8c81..aa71834a1 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -169,6 +169,7 @@ Film::Film (optional dir) _playlist_change_connection = _playlist->Change.connect (bind (&Film::playlist_change, this, _1)); _playlist_order_changed_connection = _playlist->OrderChanged.connect (bind (&Film::playlist_order_changed, this)); _playlist_content_change_connection = _playlist->ContentChange.connect (bind (&Film::playlist_content_change, this, _1, _2, _3, _4)); + _playlist_length_change_connection = _playlist->LengthChange.connect (bind(&Film::playlist_length_change, this)); if (dir) { /* Make state.directory a complete path without ..s (where possible) @@ -1292,6 +1293,12 @@ Film::playlist_content_change (ChangeType type, weak_ptr c, int p, bool } } +void +Film::playlist_length_change () +{ + LengthChange (); +} + void Film::playlist_change (ChangeType type) { diff --git a/src/lib/film.h b/src/lib/film.h index 68f8b5334..c72251880 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -390,6 +390,11 @@ public: /** Emitted when some property of our content has changed */ mutable boost::signals2::signal, int, bool)> ContentChange; + /** Emitted when the film's length might have changed; this is not like a normal + property as its value is derived from the playlist, so it has its own signal. + */ + mutable boost::signals2::signal LengthChange; + /** Emitted when we have something important to tell the user */ boost::signals2::signal Message; @@ -409,6 +414,7 @@ private: void playlist_change (ChangeType); void playlist_order_changed (); void playlist_content_change (ChangeType type, boost::weak_ptr, int, bool frequent); + void playlist_length_change (); void maybe_add_content (boost::weak_ptr, boost::weak_ptr, bool disable_audio_analysis); void audio_analysis_finished (); void check_settings_consistency (); @@ -486,6 +492,7 @@ private: boost::signals2::scoped_connection _playlist_change_connection; boost::signals2::scoped_connection _playlist_order_changed_connection; boost::signals2::scoped_connection _playlist_content_change_connection; + boost::signals2::scoped_connection _playlist_length_change_connection; std::list _job_connections; std::list _audio_analysis_connections; diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc index 9e96c693a..73a3214d3 100644 --- a/src/lib/playlist.cc +++ b/src/lib/playlist.cc @@ -113,6 +113,9 @@ Playlist::content_change (weak_ptr weak_film, ChangeType type, weak_ if (changed) { OrderChanged (); } + + /* The length might have changed, and that's good enough for this signal */ + LengthChange (); } } @@ -281,6 +284,8 @@ Playlist::add (shared_ptr film, shared_ptr c) } Change (CHANGE_TYPE_DONE); + + LengthChange (); } void @@ -312,6 +317,8 @@ Playlist::remove (shared_ptr c) } /* This won't change order, so it does not need a sort */ + + LengthChange (); } void @@ -334,9 +341,11 @@ Playlist::remove (ContentList c) } } + Change (CHANGE_TYPE_DONE); + /* This won't change order, so it does not need a sort */ - Change (CHANGE_TYPE_DONE); + LengthChange (); } class FrameRateCandidate diff --git a/src/lib/playlist.h b/src/lib/playlist.h index d7db75d0f..b6e23b4a5 100644 --- a/src/lib/playlist.h +++ b/src/lib/playlist.h @@ -78,6 +78,8 @@ public: /** Emitted when content has been added to or removed from the playlist; implies OrderChanged */ mutable boost::signals2::signal Change; mutable boost::signals2::signal OrderChanged; + /** Emitted when the length might have changed; may sometimes be emitted when it has not */ + mutable boost::signals2::signal LengthChange; mutable boost::signals2::signal, int, bool)> ContentChange; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index f3250dbfa..31093cb0a 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -157,7 +157,6 @@ FilmViewer::set_film (shared_ptr film) _film = film; - _video_view->set_film (_film); _video_view->clear (); _closed_captions_dialog->clear (); @@ -184,6 +183,7 @@ FilmViewer::set_film (shared_ptr film) _player->set_play_referenced (); _film->Change.connect (boost::bind (&FilmViewer::film_change, this, _1, _2)); + _film->LengthChange.connect (boost::bind(&FilmViewer::film_length_change, this)); _player->Change.connect (boost::bind (&FilmViewer::player_change, this, _1, _2, _3)); /* Keep about 1 second's worth of history samples */ @@ -384,11 +384,23 @@ FilmViewer::player_change (ChangeType type, int property, bool frequent) void FilmViewer::film_change (ChangeType type, Film::Property p) { - if (type == CHANGE_TYPE_DONE && p == Film::AUDIO_CHANNELS) { + if (type != CHANGE_TYPE_DONE) { + return; + } + + if (p == Film::AUDIO_CHANNELS) { recreate_butler (); + } else if (p == Film::VIDEO_FRAME_RATE) { + _video_view->set_video_frame_rate (_film->video_frame_rate()); } } +void +FilmViewer::film_length_change () +{ + _video_view->set_length (_film->length()); +} + /** Re-get the current frame slowly by seeking */ void FilmViewer::slow_refresh () diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index eaf46f1e6..e42c37a8d 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -165,6 +165,7 @@ private: void film_change (ChangeType, Film::Property); void recreate_butler (); void config_changed (Config::Property); + void film_length_change (); dcpomatic::DCPTime time () const; boost::optional audio_time () const; diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index c3a611283..3cf58757d 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -55,7 +55,6 @@ GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) : VideoView (viewer) , _vsync_enabled (false) , _thread (0) - , _one_shot (false) { _canvas = new wxGLCanvas (parent, wxID_ANY, 0, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE); _canvas->Bind (wxEVT_PAINT, boost::bind(&GLVideoView::paint, this)); @@ -278,20 +277,6 @@ GLVideoView::stop () _thread = 0; } -bool -GLVideoView::one_shot () const -{ - boost::mutex::scoped_lock lm (_one_shot_mutex); - return _one_shot; -} - -void -GLVideoView::set_one_shot (bool s) -{ - boost::mutex::scoped_lock lm (_one_shot_mutex); - _one_shot = s; -} - void GLVideoView::thread () try @@ -303,20 +288,12 @@ try _canvas->SetCurrent (*_context); } - while (true) { - if (!film() && !one_shot()) { - /* XXX: this should be an indefinite wait until - one of our conditions becomes true. - */ - dcpomatic_sleep_milliseconds (40); - continue; - } - - set_one_shot (false); + std::cout << "Here we go " << video_frame_rate() << " " << to_string(length()) << "\n"; + while (true) { dcpomatic::DCPTime const next = position() + one_video_frame(); - if (next >= film()->length()) { + if (next >= length()) { _viewer->stop (); _viewer->emit_finished (); continue; @@ -359,15 +336,6 @@ GLVideoView::context () const bool GLVideoView::display_next_frame (bool non_blocking) { - bool const g = get_next_frame (non_blocking); - set_one_shot (true); - return g; + return get_next_frame (non_blocking); } -dcpomatic::DCPTime -GLVideoView::one_video_frame () const -{ - return dcpomatic::DCPTime::from_frames (1, film()->video_frame_rate()); -} - - diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index cf42432a9..73db3535d 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -53,9 +53,6 @@ private: void draw (); void thread (); wxGLContext* context () const; - bool one_shot () const; - void set_one_shot (bool s); - dcpomatic::DCPTime one_video_frame () const; wxGLCanvas* _canvas; @@ -66,6 +63,4 @@ private: boost::optional _size; bool _vsync_enabled; boost::thread* _thread; - mutable boost::mutex _one_shot_mutex; - bool _one_shot; }; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index a00524f7d..dcf30cd1a 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -145,14 +145,14 @@ SimpleVideoView::update () void SimpleVideoView::timer () { - if (!film() || !_viewer->playing()) { + if (!_viewer->playing()) { return; } display_next_frame (false); DCPTime const next = _viewer->position() + _viewer->one_video_frame(); - if (next >= film()->length()) { + if (next >= length()) { _viewer->stop (); _viewer->Finished (); return; diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index e1a8b7306..6478ff2a6 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -67,7 +67,7 @@ VideoView::get_next_frame (bool non_blocking) dcpomatic::DCPTime VideoView::one_video_frame () const { - return dcpomatic::DCPTime::from_frames (1, film()->video_frame_rate()); + return dcpomatic::DCPTime::from_frames (1, video_frame_rate()); } /* XXX_b: comment */ diff --git a/src/wx/video_view.h b/src/wx/video_view.h index d9ef2a65f..06067130c 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -39,6 +39,7 @@ public: #ifdef DCPOMATIC_VARIANT_SWAROOP , _in_watermark (false) #endif + , _video_frame_rate (0) {} virtual ~VideoView () {} @@ -66,9 +67,14 @@ public: return _player_video.second; } - void set_film (boost::shared_ptr film) { + void set_video_frame_rate (int r) { boost::mutex::scoped_lock lm (_mutex); - _film = film; + _video_frame_rate = r; + } + + void set_length (dcpomatic::DCPTime len) { + boost::mutex::scoped_lock lm (_mutex); + _length = len; } protected: @@ -78,10 +84,13 @@ protected: bool get_next_frame (bool non_blocking); int time_until_next_frame () const; dcpomatic::DCPTime one_video_frame () const; - - boost::shared_ptr film () const { + int video_frame_rate () const { + boost::mutex::scoped_lock lm (_mutex); + return _video_frame_rate; + } + dcpomatic::DCPTime length () const { boost::mutex::scoped_lock lm (_mutex); - return _film; + return _length; } FilmViewer* _viewer; @@ -97,7 +106,8 @@ protected: #endif private: - boost::shared_ptr _film; + int _video_frame_rate; + dcpomatic::DCPTime _length; }; #endif -- cgit v1.2.3 From 73713b742a2be95ea736d2d3d889f5b535a0f0f1 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 19 Nov 2019 17:13:25 +0100 Subject: Naming tweak. --- src/lib/film.cc | 2 +- src/lib/playlist.cc | 2 +- src/lib/playlist.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/film.cc b/src/lib/film.cc index aa71834a1..90b18ea70 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -167,7 +167,7 @@ Film::Film (optional dir) set_isdcf_date_today (); _playlist_change_connection = _playlist->Change.connect (bind (&Film::playlist_change, this, _1)); - _playlist_order_changed_connection = _playlist->OrderChanged.connect (bind (&Film::playlist_order_changed, this)); + _playlist_order_changed_connection = _playlist->OrderChange.connect (bind (&Film::playlist_order_changed, this)); _playlist_content_change_connection = _playlist->ContentChange.connect (bind (&Film::playlist_content_change, this, _1, _2, _3, _4)); _playlist_length_change_connection = _playlist->LengthChange.connect (bind(&Film::playlist_length_change, this)); diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc index 73a3214d3..48053bbf4 100644 --- a/src/lib/playlist.cc +++ b/src/lib/playlist.cc @@ -111,7 +111,7 @@ Playlist::content_change (weak_ptr weak_film, ChangeType type, weak_ } if (changed) { - OrderChanged (); + OrderChange (); } /* The length might have changed, and that's good enough for this signal */ diff --git a/src/lib/playlist.h b/src/lib/playlist.h index b6e23b4a5..dc984aacf 100644 --- a/src/lib/playlist.h +++ b/src/lib/playlist.h @@ -77,7 +77,7 @@ public: /** Emitted when content has been added to or removed from the playlist; implies OrderChanged */ mutable boost::signals2::signal Change; - mutable boost::signals2::signal OrderChanged; + mutable boost::signals2::signal OrderChange; /** Emitted when the length might have changed; may sometimes be emitted when it has not */ mutable boost::signals2::signal LengthChange; -- cgit v1.2.3 From edfb627f1226814ac804473b54d781ffd6db2700 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 19 Nov 2019 23:06:50 +0100 Subject: Remove GL context accessor and hence lock. --- src/wx/gl_video_view.cc | 19 +++---------------- src/wx/gl_video_view.h | 3 --- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index 3cf58757d..df45a143f 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -282,11 +282,8 @@ GLVideoView::thread () try { /* XXX_b: check all calls and signal emissions in this method & protect them if necessary */ - { - boost::mutex::scoped_lock lm (_context_mutex); - _context = new wxGLContext (_canvas); - _canvas->SetCurrent (*_context); - } + _context = new wxGLContext (_canvas); + _canvas->SetCurrent (*_context); std::cout << "Here we go " << video_frame_rate() << " " << to_string(length()) << "\n"; @@ -314,10 +311,7 @@ try dcpomatic_sleep_milliseconds (time_until_next_frame()); } - { - boost::mutex::scoped_lock lm (_context_mutex); - delete _context; - } + delete _context; } catch (boost::thread_interrupted& e) { @@ -326,13 +320,6 @@ catch (boost::thread_interrupted& e) return; } -wxGLContext * -GLVideoView::context () const -{ - boost::mutex::scoped_lock lm (_context_mutex); - return _context; -} - bool GLVideoView::display_next_frame (bool non_blocking) { diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index 73db3535d..44a4057fd 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -52,12 +52,9 @@ private: void paint (); void draw (); void thread (); - wxGLContext* context () const; wxGLCanvas* _canvas; - wxGLContext* _context; - mutable boost::mutex _context_mutex; GLuint _id; boost::optional _size; -- cgit v1.2.3 From 798819f74c6d194b95d3458f88b7ad60ef5f282c Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 19 Nov 2019 23:57:14 +0100 Subject: Nicer protection of _player_video. Always run GL thread rather than starting/stopping it. --- src/wx/gl_video_view.cc | 41 ++++++++++++++++++++++++----------------- src/wx/gl_video_view.h | 6 ++++++ src/wx/simple_video_view.cc | 12 ++++++------ src/wx/video_view.h | 15 +++++++++++---- 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index df45a143f..3549cfe8f 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -54,7 +54,8 @@ using boost::optional; GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) : VideoView (viewer) , _vsync_enabled (false) - , _thread (0) + , _playing (false) + , _one_shot (false) { _canvas = new wxGLCanvas (parent, wxID_ANY, 0, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE); _canvas->Bind (wxEVT_PAINT, boost::bind(&GLVideoView::paint, this)); @@ -91,14 +92,14 @@ GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) glGenTextures (1, &_id); glBindTexture (GL_TEXTURE_2D, _id); glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + + _thread = new boost::thread (boost::bind(&GLVideoView::thread, this)); } GLVideoView::~GLVideoView () { - if (_thread) { - _thread->interrupt (); - _thread->join (); - } + _thread->interrupt (); + _thread->join (); delete _thread; glDeleteTextures (1, &_id); @@ -263,18 +264,16 @@ GLVideoView::set_image (shared_ptr image) void GLVideoView::start () { - _thread = new boost::thread (boost::bind(&GLVideoView::thread, this)); + boost::mutex::scoped_lock lm (_playing_mutex); + _playing = true; + _playing_condition.notify_all (); } void GLVideoView::stop () { - if (_thread) { - _thread->interrupt (); - _thread->join (); - } - delete _thread; - _thread = 0; + boost::mutex::scoped_lock lm (_playing_mutex); + _playing = false; } void @@ -288,6 +287,13 @@ try std::cout << "Here we go " << video_frame_rate() << " " << to_string(length()) << "\n"; while (true) { + boost::mutex::scoped_lock lm (_playing_mutex); + while (!_playing || !_one_shot) { + _playing_condition.wait (lm); + } + _one_shot = false; + lm.unlock (); + dcpomatic::DCPTime const next = position() + one_video_frame(); if (next >= length()) { @@ -297,10 +303,7 @@ try } get_next_frame (false); - { - boost::mutex::scoped_lock lm (_mutex); - set_image (_player_video.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); - } + set_image (player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); draw (); while (time_until_next_frame() < 5) { @@ -323,6 +326,10 @@ catch (boost::thread_interrupted& e) bool GLVideoView::display_next_frame (bool non_blocking) { - return get_next_frame (non_blocking); + bool const r = get_next_frame (non_blocking); + boost::mutex::scoped_lock lm (_playing_mutex); + _one_shot = true; + _playing_condition.notify_all (); + return r; } diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index 44a4057fd..22b6d8513 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -25,6 +25,7 @@ #include #include #include +#include #undef None #undef Success @@ -60,4 +61,9 @@ private: boost::optional _size; bool _vsync_enabled; boost::thread* _thread; + + boost::mutex _playing_mutex; + boost::condition _playing_condition; + bool _playing; + bool _one_shot; }; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index dcf30cd1a..135892e07 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -200,13 +200,13 @@ SimpleVideoView::display_next_frame (bool non_blocking) void SimpleVideoView::display_player_video () { - if (!_player_video.first) { + if (!player_video().first) { set_image (shared_ptr()); _viewer->refresh_view (); return; } - if (_viewer->playing() && (_viewer->time() - _player_video.second) > _viewer->one_video_frame()) { + if (_viewer->playing() && (_viewer->time() - player_video().second) > _viewer->one_video_frame()) { /* Too late; just drop this frame before we try to get its image (which will be the time-consuming part if this frame is J2K). */ @@ -235,15 +235,15 @@ SimpleVideoView::display_player_video () _viewer->_state_timer.set ("get image"); set_image ( - _player_video.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true) + player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true) ); _viewer->_state_timer.set ("ImageChanged"); - _viewer->ImageChanged (_player_video.first); + _viewer->ImageChanged (player_video().first); _viewer->_state_timer.unset (); - _viewer->_inter_position = _player_video.first->inter_position (); - _viewer->_inter_size = _player_video.first->inter_size (); + _viewer->_inter_position = player_video().first->inter_position (); + _viewer->_inter_size = player_video().first->inter_size (); _viewer->refresh_view (); diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 06067130c..8d763204c 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -84,20 +84,23 @@ protected: bool get_next_frame (bool non_blocking); int time_until_next_frame () const; dcpomatic::DCPTime one_video_frame () const; + int video_frame_rate () const { boost::mutex::scoped_lock lm (_mutex); return _video_frame_rate; } + dcpomatic::DCPTime length () const { boost::mutex::scoped_lock lm (_mutex); return _length; } - FilmViewer* _viewer; - std::pair, dcpomatic::DCPTime> _player_video; + std::pair, dcpomatic::DCPTime> player_video () const { + boost::mutex::scoped_lock lm (_mutex); + return _player_video; + } - /** Mutex protecting all the state in VideoView */ - mutable boost::mutex _mutex; + FilmViewer* _viewer; #ifdef DCPOMATIC_VARIANT_SWAROOP bool _in_watermark; @@ -106,6 +109,10 @@ protected: #endif private: + /** Mutex protecting all the state in VideoView */ + mutable boost::mutex _mutex; + + std::pair, dcpomatic::DCPTime> _player_video; int _video_frame_rate; dcpomatic::DCPTime _length; }; -- cgit v1.2.3 From 8993f85b88d6540d32709de704bc2fbcd3bf1d59 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Nov 2019 00:34:01 +0100 Subject: Use VideoView::one_video_frame. --- src/wx/simple_video_view.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 135892e07..7aeb317b2 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -206,7 +206,7 @@ SimpleVideoView::display_player_video () return; } - if (_viewer->playing() && (_viewer->time() - player_video().second) > _viewer->one_video_frame()) { + if (_viewer->playing() && (_viewer->time() - player_video().second) > one_video_frame()) { /* Too late; just drop this frame before we try to get its image (which will be the time-consuming part if this frame is J2K). */ -- cgit v1.2.3 From 92caaad24055ad3517b974d19416091a98da4cf2 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Nov 2019 00:50:58 +0100 Subject: Start thread when the window will have been created. Fix logic error. --- src/wx/gl_video_view.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index 3549cfe8f..236b40518 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -54,6 +54,7 @@ using boost::optional; GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) : VideoView (viewer) , _vsync_enabled (false) + , _thread (0) , _playing (false) , _one_shot (false) { @@ -92,8 +93,6 @@ GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) glGenTextures (1, &_id); glBindTexture (GL_TEXTURE_2D, _id); glPixelStorei (GL_UNPACK_ALIGNMENT, 1); - - _thread = new boost::thread (boost::bind(&GLVideoView::thread, this)); } GLVideoView::~GLVideoView () @@ -264,6 +263,10 @@ GLVideoView::set_image (shared_ptr image) void GLVideoView::start () { + if (!_thread) { + _thread = new boost::thread (boost::bind(&GLVideoView::thread, this)); + } + boost::mutex::scoped_lock lm (_playing_mutex); _playing = true; _playing_condition.notify_all (); @@ -288,7 +291,7 @@ try while (true) { boost::mutex::scoped_lock lm (_playing_mutex); - while (!_playing || !_one_shot) { + while (!_playing && !_one_shot) { _playing_condition.wait (lm); } _one_shot = false; -- cgit v1.2.3 From 5ee3fcbd31065275faad4c06a7805dcbcb338812 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Nov 2019 00:57:37 +0100 Subject: Add comment. --- src/wx/video_view.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index 6478ff2a6..f2886a48c 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -55,6 +55,7 @@ VideoView::get_next_frame (bool non_blocking) _player_video.first->eyes() != EYES_BOTH ); + /* XXX_b: this is called from the GL thread so it shouldn't be opening error dialogs */ try { _viewer->butler()->rethrow (); } catch (DecodeError& e) { -- cgit v1.2.3 From 4f7e9f125716a27ed9e2a8e30f067100cbee773a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Nov 2019 21:25:55 +0100 Subject: Fix display when there is no film. --- src/wx/gl_video_view.cc | 65 +++++++++++++++++++++++++++++-------------------- src/wx/gl_video_view.h | 2 ++ src/wx/video_view.cc | 12 +++++++-- src/wx/video_view.h | 1 + 4 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index 236b40518..a1be8ca30 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -61,6 +61,7 @@ GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) _canvas = new wxGLCanvas (parent, wxID_ANY, 0, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE); _canvas->Bind (wxEVT_PAINT, boost::bind(&GLVideoView::paint, this)); _canvas->Bind (wxEVT_SIZE, boost::bind(boost::ref(Sized))); + _canvas->Bind (wxEVT_CREATE, boost::bind(&GLVideoView::create, this)); #if defined(DCPOMATIC_LINUX) && defined(DCPOMATIC_HAVE_GLX_SWAP_INTERVAL_EXT) if (_canvas->IsExtensionSupported("GLX_EXT_swap_control")) { @@ -116,14 +117,7 @@ check_gl_error (char const * last) void GLVideoView::paint () { - /* XXX_b: can't do this yet */ -#if 0 - _viewer->state_timer().set("paint-panel"); - _canvas->SetCurrent (*_context); - wxPaintDC dc (_canvas); - draw (); - _viewer->state_timer().unset(); -#endif + request_one_shot (); } void @@ -169,9 +163,11 @@ GLVideoView::draw () glTranslatef (0, 0, 0); + dcp::Size const out_size = _viewer->out_size (); + if (_size) { + /* Render our image (texture) */ glBegin (GL_QUADS); - glTexCoord2f (0, 1); glVertex2f (0, _size->height); glTexCoord2f (1, 1); @@ -180,11 +176,19 @@ GLVideoView::draw () glVertex2f (_size->width, 0); glTexCoord2f (0, 0); glVertex2f (0, 0); - + glEnd (); + } else { + /* No image, so just fill with black */ + glBegin (GL_QUADS); + glColor3ub (0, 0, 0); + glVertex2f (0, 0); + glVertex2f (out_size.width, 0); + glVertex2f (out_size.width, out_size.height); + glVertex2f (0, out_size.height); + glVertex2f (0, 0); glEnd (); } - dcp::Size const out_size = _viewer->out_size (); wxSize const canvas_size = _canvas->GetSize (); if (!_viewer->pad_black() && out_size.width < canvas_size.GetWidth()) { @@ -263,10 +267,6 @@ GLVideoView::set_image (shared_ptr image) void GLVideoView::start () { - if (!_thread) { - _thread = new boost::thread (boost::bind(&GLVideoView::thread, this)); - } - boost::mutex::scoped_lock lm (_playing_mutex); _playing = true; _playing_condition.notify_all (); @@ -287,8 +287,6 @@ try _context = new wxGLContext (_canvas); _canvas->SetCurrent (*_context); - std::cout << "Here we go " << video_frame_rate() << " " << to_string(length()) << "\n"; - while (true) { boost::mutex::scoped_lock lm (_playing_mutex); while (!_playing && !_one_shot) { @@ -297,16 +295,18 @@ try _one_shot = false; lm.unlock (); - dcpomatic::DCPTime const next = position() + one_video_frame(); + if (length() != dcpomatic::DCPTime()) { + dcpomatic::DCPTime const next = position() + one_video_frame(); - if (next >= length()) { - _viewer->stop (); - _viewer->emit_finished (); - continue; - } + if (next >= length()) { + _viewer->stop (); + _viewer->emit_finished (); + continue; + } - get_next_frame (false); - set_image (player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); + get_next_frame (false); + set_image (player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); + } draw (); while (time_until_next_frame() < 5) { @@ -330,9 +330,22 @@ bool GLVideoView::display_next_frame (bool non_blocking) { bool const r = get_next_frame (non_blocking); + request_one_shot (); + return r; +} + +void +GLVideoView::request_one_shot () +{ boost::mutex::scoped_lock lm (_playing_mutex); _one_shot = true; _playing_condition.notify_all (); - return r; } +void +GLVideoView::create () +{ + if (!_thread) { + _thread = new boost::thread (boost::bind(&GLVideoView::thread, this)); + } +} diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index 22b6d8513..a9264f05d 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -53,6 +53,8 @@ private: void paint (); void draw (); void thread (); + void request_one_shot (); + void create (); wxGLCanvas* _canvas; wxGLContext* _context; diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index f2886a48c..f8d44dc70 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -37,6 +37,10 @@ VideoView::clear () bool VideoView::get_next_frame (bool non_blocking) { + if (_length == dcpomatic::DCPTime()) { + return true; + } + DCPOMATIC_ASSERT (_viewer->butler()); _viewer->_gets++; @@ -71,13 +75,17 @@ VideoView::one_video_frame () const return dcpomatic::DCPTime::from_frames (1, video_frame_rate()); } -/* XXX_b: comment */ +/** @return Time in ms until the next frame is due */ int VideoView::time_until_next_frame () const { + if (length() == dcpomatic::DCPTime()) { + /* There's no content, so this doesn't matter */ + return 0; + } + dcpomatic::DCPTime const next = position() + one_video_frame(); dcpomatic::DCPTime const time = _viewer->audio_time().get_value_or(position()); - std::cout << to_string(next) << " " << to_string(time) << " " << ((next.seconds() - time.seconds()) * 1000) << "\n"; if (next < time) { return 0; } diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 8d763204c..142cfd022 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -114,6 +114,7 @@ private: std::pair, dcpomatic::DCPTime> _player_video; int _video_frame_rate; + /** length of the film we are playing, or 0 if there is none */ dcpomatic::DCPTime _length; }; -- cgit v1.2.3 From 83c9e9c858072ab919916269790dcc65565fdd25 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Nov 2019 22:18:33 +0100 Subject: Don't try opening error dialogues from the GL thread. --- src/wx/gl_video_view.cc | 16 ++++++++++++++++ src/wx/gl_video_view.h | 3 +++ src/wx/simple_video_view.cc | 6 ++++++ src/wx/video_view.cc | 7 ------- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index a1be8ca30..836b3eb35 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -20,11 +20,13 @@ #include "gl_video_view.h" #include "film_viewer.h" +#include "wx_util.h" #include "lib/image.h" #include "lib/dcpomatic_assert.h" #include "lib/exceptions.h" #include "lib/cross.h" #include "lib/player_video.h" +#include "lib/butler.h" #include #include @@ -63,6 +65,10 @@ GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) _canvas->Bind (wxEVT_SIZE, boost::bind(boost::ref(Sized))); _canvas->Bind (wxEVT_CREATE, boost::bind(&GLVideoView::create, this)); + _canvas->Bind (wxEVT_TIMER, boost::bind(&GLVideoView::check_for_butler_errors, this)); + _timer.reset (new wxTimer(_canvas)); + _timer->Start (2000); + #if defined(DCPOMATIC_LINUX) && defined(DCPOMATIC_HAVE_GLX_SWAP_INTERVAL_EXT) if (_canvas->IsExtensionSupported("GLX_EXT_swap_control")) { /* Enable vsync */ @@ -105,6 +111,16 @@ GLVideoView::~GLVideoView () glDeleteTextures (1, &_id); } +void +GLVideoView::check_for_butler_errors () +{ + try { + _viewer->butler()->rethrow (); + } catch (DecodeError& e) { + error_dialog (get(), e.what()); + } +} + static void check_gl_error (char const * last) { diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index a9264f05d..614393024 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -55,6 +55,7 @@ private: void thread (); void request_one_shot (); void create (); + void check_for_butler_errors (); wxGLCanvas* _canvas; wxGLContext* _context; @@ -68,4 +69,6 @@ private: boost::condition _playing_condition; bool _playing; bool _one_shot; + + boost::shared_ptr _timer; }; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 7aeb317b2..f928770ad 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -194,6 +194,12 @@ SimpleVideoView::display_next_frame (bool non_blocking) display_player_video (); + try { + _viewer->butler()->rethrow (); + } catch (DecodeError& e) { + error_dialog (get(), e.what()); + } + return true; } diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index f8d44dc70..ede0708c2 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -59,13 +59,6 @@ VideoView::get_next_frame (bool non_blocking) _player_video.first->eyes() != EYES_BOTH ); - /* XXX_b: this is called from the GL thread so it shouldn't be opening error dialogs */ - try { - _viewer->butler()->rethrow (); - } catch (DecodeError& e) { - error_dialog (get(), e.what()); - } - return true; } -- cgit v1.2.3 From 89780827d5d6ec9127eae0108d10f71dc79d1a72 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Nov 2019 22:25:40 +0100 Subject: Implement GLView::update. --- src/wx/gl_video_view.cc | 12 ++---------- src/wx/gl_video_view.h | 1 - src/wx/video_view.h | 3 +++ 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index 836b3eb35..934a91ad5 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -61,7 +61,7 @@ GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) , _one_shot (false) { _canvas = new wxGLCanvas (parent, wxID_ANY, 0, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE); - _canvas->Bind (wxEVT_PAINT, boost::bind(&GLVideoView::paint, this)); + _canvas->Bind (wxEVT_PAINT, boost::bind(&GLVideoView::update, this)); _canvas->Bind (wxEVT_SIZE, boost::bind(boost::ref(Sized))); _canvas->Bind (wxEVT_CREATE, boost::bind(&GLVideoView::create, this)); @@ -130,21 +130,13 @@ check_gl_error (char const * last) } } -void -GLVideoView::paint () -{ - request_one_shot (); -} - void GLVideoView::update () { if (!_canvas->IsShownOnScreen()) { return; } - /* XXX_b */ -// wxClientDC dc (_canvas); -// draw (); + request_one_shot (); } void diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index 614393024..162438a5b 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -50,7 +50,6 @@ public: } private: - void paint (); void draw (); void thread (); void request_one_shot (); diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 142cfd022..656d8621e 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -46,6 +46,9 @@ public: virtual void set_image (boost::shared_ptr image) = 0; virtual wxWindow* get () const = 0; + /** Redraw the view after something has changed like content outlining, + * the film being removed, etc. + */ virtual void update () = 0; /* XXX_b: make pure */ -- cgit v1.2.3 From 7da4fe1c2de3c184a22dbcd1195fc6cffb83ac51 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Nov 2019 22:30:36 +0100 Subject: Small re-use cleanup. --- src/wx/film_viewer.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 31093cb0a..c1d04be03 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -567,12 +567,7 @@ FilmViewer::audio_time () const DCPTime FilmViewer::time () const { - if (_audio.isStreamRunning ()) { - return DCPTime::from_seconds (const_cast(&_audio)->getStreamTime ()) - - DCPTime::from_frames (average_latency(), _film->audio_frame_rate()); - } - - return _video_view->position(); + return audio_time().get_value_or(_video_view->position()); } int -- cgit v1.2.3 From 5aff601c454fa756c0ab71ae4bcf8f7f4ce28737 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Nov 2019 23:07:55 +0100 Subject: Move _dropped into VideoView. --- src/wx/film_viewer.cc | 8 ++++++-- src/wx/film_viewer.h | 5 +---- src/wx/gl_video_view.cc | 3 +++ src/wx/simple_video_view.cc | 3 ++- src/wx/video_view.cc | 17 +++++++++++++++++ src/wx/video_view.h | 24 ++++++++++++++---------- 6 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index c1d04be03..cb9c85b3d 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -86,7 +86,6 @@ FilmViewer::FilmViewer (wxWindow* p) , _playing (false) , _suspended (0) , _latency_history_count (0) - , _dropped (0) , _closed_captions_dialog (new ClosedCaptionsDialog(p, this)) , _outline_content (false) , _eyes (EYES_LEFT) @@ -326,7 +325,6 @@ FilmViewer::start () _audio.startStream (); } - _dropped = 0; _playing = true; _video_view->start (); Started (position()); @@ -656,3 +654,9 @@ FilmViewer::emit_finished () emit (boost::bind(boost::ref(Finished))); } +int +FilmViewer::dropped () const +{ + return _video_view->dropped (); +} + diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index e42c37a8d..93a311981 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -92,9 +92,7 @@ public: void slow_refresh (); - int dropped () const { - return _dropped; - } + int dropped () const; int audio_callback (void* out, unsigned int frames); @@ -200,7 +198,6 @@ private: mutable boost::mutex _latency_history_mutex; int _latency_history_count; - int _dropped; boost::optional _dcp_decode_reduction; ClosedCaptionsDialog* _closed_captions_dialog; diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index 934a91ad5..55c6cc969 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -275,6 +275,8 @@ GLVideoView::set_image (shared_ptr image) void GLVideoView::start () { + VideoView::start (); + boost::mutex::scoped_lock lm (_playing_mutex); _playing = true; _playing_condition.notify_all (); @@ -319,6 +321,7 @@ try while (time_until_next_frame() < 5) { get_next_frame (true); + add_dropped (); } boost::this_thread::interruption_point (); diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index f928770ad..33e2834c5 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -169,6 +169,7 @@ SimpleVideoView::timer () void SimpleVideoView::start () { + VideoView::start (); timer (); } @@ -216,7 +217,7 @@ SimpleVideoView::display_player_video () /* Too late; just drop this frame before we try to get its image (which will be the time-consuming part if this frame is J2K). */ - ++_viewer->_dropped; + add_dropped (); return; } diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index ede0708c2..d54487a93 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -23,6 +23,17 @@ #include "film_viewer.h" #include "lib/butler.h" +VideoView::VideoView (FilmViewer* viewer) + : _viewer (viewer) +#ifdef DCPOMATIC_VARIANT_SWAROOP + , _in_watermark (false) +#endif + , _video_frame_rate (0) + , _dropped (0) +{ + +} + void VideoView::clear () { @@ -85,3 +96,9 @@ VideoView::time_until_next_frame () const return (next.seconds() - time.seconds()) * 1000; } +void +VideoView::start () +{ + boost::mutex::scoped_lock lm (_mutex); + _dropped = 0; +} diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 656d8621e..8d9dce68d 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -34,14 +34,7 @@ class PlayerVideo; class VideoView { public: - VideoView (FilmViewer* viewer) - : _viewer (viewer) -#ifdef DCPOMATIC_VARIANT_SWAROOP - , _in_watermark (false) -#endif - , _video_frame_rate (0) - {} - + VideoView (FilmViewer* viewer); virtual ~VideoView () {} virtual void set_image (boost::shared_ptr image) = 0; @@ -51,8 +44,7 @@ public: */ virtual void update () = 0; - /* XXX_b: make pure */ - virtual void start () {} + virtual void start (); /* XXX_b: make pure */ virtual void stop () {} @@ -65,6 +57,11 @@ public: /* XXX_b: to remove */ virtual void display_player_video () {} + int dropped () const { + boost::mutex::scoped_lock lm (_mutex); + return _dropped; + } + dcpomatic::DCPTime position () const { boost::mutex::scoped_lock lm (_mutex); return _player_video.second; @@ -103,6 +100,11 @@ protected: return _player_video; } + void add_dropped () { + boost::mutex::scoped_lock lm (_mutex); + ++_dropped; + } + FilmViewer* _viewer; #ifdef DCPOMATIC_VARIANT_SWAROOP @@ -119,6 +121,8 @@ private: int _video_frame_rate; /** length of the film we are playing, or 0 if there is none */ dcpomatic::DCPTime _length; + + int _dropped; }; #endif -- cgit v1.2.3 From 79355249a27ead72bc2bcd0a84e7a280a3d9a826 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Nov 2019 23:36:56 +0100 Subject: Move _inter_position out of FilmViewer. --- src/wx/film_viewer.h | 4 ---- src/wx/gl_video_view.cc | 7 ++++--- src/wx/gl_video_view.h | 3 ++- src/wx/simple_video_view.cc | 5 ++--- src/wx/simple_video_view.h | 2 ++ 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 93a311981..5c2f7cb11 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -126,9 +126,6 @@ public: dcp::Size inter_size () const { return _inter_size; } - Position inter_position () const { - return _inter_position; - } bool outline_content () const { return _outline_content; } @@ -180,7 +177,6 @@ private: bool _coalesce_player_changes; std::list _pending_player_changes; - Position _inter_position; dcp::Size _inter_size; /** Size of our output (including padding if we have any) */ diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index 55c6cc969..93a53b411 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -140,7 +140,7 @@ GLVideoView::update () } void -GLVideoView::draw () +GLVideoView::draw (Position inter_position) { glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); check_gl_error ("glClear"); @@ -232,7 +232,6 @@ GLVideoView::draw () if (_viewer->outline_content()) { glColor3ub (255, 0, 0); glBegin (GL_LINE_LOOP); - Position inter_position = _viewer->inter_position (); dcp::Size inter_size = _viewer->inter_size (); glVertex2f (inter_position.x, inter_position.y + (canvas_size.GetHeight() - out_size.height) / 2); glVertex2f (inter_position.x + inter_size.width, inter_position.y + (canvas_size.GetHeight() - out_size.height) / 2); @@ -305,6 +304,7 @@ try _one_shot = false; lm.unlock (); + Position inter_position; if (length() != dcpomatic::DCPTime()) { dcpomatic::DCPTime const next = position() + one_video_frame(); @@ -316,8 +316,9 @@ try get_next_frame (false); set_image (player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); + inter_position = player_video().first->inter_position(); } - draw (); + draw (inter_position); while (time_until_next_frame() < 5) { get_next_frame (true); diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index 162438a5b..01434d569 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -20,6 +20,7 @@ #include "video_view.h" #include "lib/signaller.h" +#include "lib/position.h" #include #include #include @@ -50,7 +51,7 @@ public: } private: - void draw (); + void draw (Position inter_position); void thread (); void request_one_shot (); void create (); diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 33e2834c5..6a8a863d7 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -125,12 +125,11 @@ SimpleVideoView::paint () } if (_viewer->outline_content()) { - Position inter_position = _viewer->inter_position (); dcp::Size inter_size = _viewer->inter_size (); wxPen p (wxColour (255, 0, 0), 2); dc.SetPen (p); dc.SetBrush (*wxTRANSPARENT_BRUSH); - dc.DrawRectangle (inter_position.x, inter_position.y + (panel_size.GetHeight() - out_size.height) / 2, inter_size.width, inter_size.height); + dc.DrawRectangle (_inter_position.x, _inter_position.y + (panel_size.GetHeight() - out_size.height) / 2, inter_size.width, inter_size.height); } _viewer->state_timer().unset(); } @@ -249,7 +248,7 @@ SimpleVideoView::display_player_video () _viewer->ImageChanged (player_video().first); _viewer->_state_timer.unset (); - _viewer->_inter_position = player_video().first->inter_position (); + _inter_position = player_video().first->inter_position (); _viewer->_inter_size = player_video().first->inter_size (); _viewer->refresh_view (); diff --git a/src/wx/simple_video_view.h b/src/wx/simple_video_view.h index e8bb932e4..f318e778b 100644 --- a/src/wx/simple_video_view.h +++ b/src/wx/simple_video_view.h @@ -19,6 +19,7 @@ */ #include "video_view.h" +#include "lib/position.h" #include class FilmViewer; @@ -48,4 +49,5 @@ private: wxPanel* _panel; boost::shared_ptr _image; wxTimer _timer; + Position _inter_position; }; -- cgit v1.2.3 From 6a3d9962dc774becf50dd92f0cca90c536343eda Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Nov 2019 23:41:58 +0100 Subject: Same thing with inter_size. --- src/wx/film_viewer.h | 5 ----- src/wx/gl_video_view.cc | 7 ++++--- src/wx/gl_video_view.h | 2 +- src/wx/simple_video_view.cc | 5 ++--- src/wx/simple_video_view.h | 2 ++ 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 5c2f7cb11..8cacf80d3 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -123,9 +123,6 @@ public: dcp::Size out_size () const { return _out_size; } - dcp::Size inter_size () const { - return _inter_size; - } bool outline_content () const { return _outline_content; } @@ -177,8 +174,6 @@ private: bool _coalesce_player_changes; std::list _pending_player_changes; - dcp::Size _inter_size; - /** Size of our output (including padding if we have any) */ dcp::Size _out_size; diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index 93a53b411..ce152787a 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -140,7 +140,7 @@ GLVideoView::update () } void -GLVideoView::draw (Position inter_position) +GLVideoView::draw (Position inter_position, dcp::Size inter_size) { glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); check_gl_error ("glClear"); @@ -232,7 +232,6 @@ GLVideoView::draw (Position inter_position) if (_viewer->outline_content()) { glColor3ub (255, 0, 0); glBegin (GL_LINE_LOOP); - dcp::Size inter_size = _viewer->inter_size (); glVertex2f (inter_position.x, inter_position.y + (canvas_size.GetHeight() - out_size.height) / 2); glVertex2f (inter_position.x + inter_size.width, inter_position.y + (canvas_size.GetHeight() - out_size.height) / 2); glVertex2f (inter_position.x + inter_size.width, inter_position.y + (canvas_size.GetHeight() - out_size.height) / 2 + inter_size.height); @@ -305,6 +304,7 @@ try lm.unlock (); Position inter_position; + dcp::Size inter_size; if (length() != dcpomatic::DCPTime()) { dcpomatic::DCPTime const next = position() + one_video_frame(); @@ -317,8 +317,9 @@ try get_next_frame (false); set_image (player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); inter_position = player_video().first->inter_position(); + inter_size = player_video().first->inter_size(); } - draw (inter_position); + draw (inter_position, inter_size); while (time_until_next_frame() < 5) { get_next_frame (true); diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index 01434d569..4f509049b 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -51,7 +51,7 @@ public: } private: - void draw (Position inter_position); + void draw (Position inter_position, dcp::Size inter_size); void thread (); void request_one_shot (); void create (); diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 6a8a863d7..c8fe7b9ad 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -125,11 +125,10 @@ SimpleVideoView::paint () } if (_viewer->outline_content()) { - dcp::Size inter_size = _viewer->inter_size (); wxPen p (wxColour (255, 0, 0), 2); dc.SetPen (p); dc.SetBrush (*wxTRANSPARENT_BRUSH); - dc.DrawRectangle (_inter_position.x, _inter_position.y + (panel_size.GetHeight() - out_size.height) / 2, inter_size.width, inter_size.height); + dc.DrawRectangle (_inter_position.x, _inter_position.y + (panel_size.GetHeight() - out_size.height) / 2, _inter_size.width, _inter_size.height); } _viewer->state_timer().unset(); } @@ -249,7 +248,7 @@ SimpleVideoView::display_player_video () _viewer->_state_timer.unset (); _inter_position = player_video().first->inter_position (); - _viewer->_inter_size = player_video().first->inter_size (); + _inter_size = player_video().first->inter_size (); _viewer->refresh_view (); diff --git a/src/wx/simple_video_view.h b/src/wx/simple_video_view.h index f318e778b..86451fa66 100644 --- a/src/wx/simple_video_view.h +++ b/src/wx/simple_video_view.h @@ -20,6 +20,7 @@ #include "video_view.h" #include "lib/position.h" +#include #include class FilmViewer; @@ -50,4 +51,5 @@ private: boost::shared_ptr _image; wxTimer _timer; Position _inter_position; + dcp::Size _inter_size; }; -- cgit v1.2.3 From 386e25f3b9d3fa59cbdeed458d9b3e0d21e338b8 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Nov 2019 23:46:40 +0100 Subject: Accessor for ClosedCaptionsDialog. --- src/wx/film_viewer.h | 3 +++ src/wx/simple_video_view.cc | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 8cacf80d3..c6fbd66e4 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -132,6 +132,9 @@ public: boost::shared_ptr butler () const { return _butler; } + ClosedCaptionsDialog* closed_captions_dialog () const { + return _closed_captions_dialog; + } boost::signals2::signal)> ImageChanged; boost::signals2::signal Started; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index c8fe7b9ad..e71d89412 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -252,5 +252,5 @@ SimpleVideoView::display_player_video () _viewer->refresh_view (); - _viewer->_closed_captions_dialog->update (_viewer->time()); + _viewer->closed_captions_dialog()->update (_viewer->time()); } -- cgit v1.2.3 From 9001a63be211fd8e97431f8fc07c66af01554f5a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 21 Nov 2019 00:05:12 +0100 Subject: Move _state_timer into VideoView. --- src/wx/film_viewer.cc | 3 --- src/wx/film_viewer.h | 13 ++++--------- src/wx/simple_video_view.cc | 12 +++++++----- src/wx/video_view.cc | 1 + src/wx/video_view.h | 7 +++++++ 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index cb9c85b3d..c323c8281 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -93,7 +93,6 @@ FilmViewer::FilmViewer (wxWindow* p) #ifdef DCPOMATIC_VARIANT_SWAROOP , _background_image (false) #endif - , _state_timer ("viewer") , _gets (0) , _idle_get (false) { @@ -228,9 +227,7 @@ FilmViewer::recreate_butler () void FilmViewer::refresh_view () { - _state_timer.set ("update-view"); _video_view->update (); - _state_timer.unset (); } void diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index c6fbd66e4..0699b12ae 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -107,18 +107,14 @@ public: } #endif - StateTimer const & state_timer () const { - return _state_timer; - } - - StateTimer& state_timer () { - return _state_timer; - } - int gets () const { return _gets; } + StateTimer const & state_timer () const { + return _video_view->state_timer (); + } + /* Some accessors and utility methods that VideoView classes need */ dcp::Size out_size () const { return _out_size; @@ -207,7 +203,6 @@ private: bool _background_image; #endif - StateTimer _state_timer; int _gets; /** true if an get() is required next time we are idle */ diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index e71d89412..437aed4fa 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -56,7 +56,7 @@ SimpleVideoView::SimpleVideoView (FilmViewer* viewer, wxWindow* parent) void SimpleVideoView::paint () { - _viewer->state_timer().set("paint-panel"); + _state_timer.set("paint-panel"); wxPaintDC dc (_panel); dcp::Size const out_size = _viewer->out_size (); @@ -130,14 +130,16 @@ SimpleVideoView::paint () dc.SetBrush (*wxTRANSPARENT_BRUSH); dc.DrawRectangle (_inter_position.x, _inter_position.y + (panel_size.GetHeight() - out_size.height) / 2, _inter_size.width, _inter_size.height); } - _viewer->state_timer().unset(); + _state_timer.unset(); } void SimpleVideoView::update () { + _state_timer.set ("update-view"); _panel->Refresh (); _panel->Update (); + _state_timer.unset (); } void @@ -237,15 +239,15 @@ SimpleVideoView::display_player_video () * image and convert it (from whatever the user has said it is) to RGB. */ - _viewer->_state_timer.set ("get image"); + _state_timer.set ("get image"); set_image ( player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true) ); - _viewer->_state_timer.set ("ImageChanged"); + _state_timer.set ("ImageChanged"); _viewer->ImageChanged (player_video().first); - _viewer->_state_timer.unset (); + _state_timer.unset (); _inter_position = player_video().first->inter_position (); _inter_size = player_video().first->inter_size (); diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index d54487a93..e65888656 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -28,6 +28,7 @@ VideoView::VideoView (FilmViewer* viewer) #ifdef DCPOMATIC_VARIANT_SWAROOP , _in_watermark (false) #endif + , _state_timer ("viewer") , _video_frame_rate (0) , _dropped (0) { diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 8d9dce68d..4a94aa879 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -22,6 +22,7 @@ #define DCPOMATIC_VIDEO_VIEW_H #include "lib/dcpomatic_time.h" +#include "lib/timer.h" #include #include #include @@ -62,6 +63,10 @@ public: return _dropped; } + StateTimer const & state_timer () const { + return _state_timer; + } + dcpomatic::DCPTime position () const { boost::mutex::scoped_lock lm (_mutex); return _player_video.second; @@ -113,6 +118,8 @@ protected: int _watermark_y; #endif + StateTimer _state_timer; + private: /** Mutex protecting all the state in VideoView */ mutable boost::mutex _mutex; -- cgit v1.2.3 From 7f5d317d3ba2bdb1838e942c436ad9f5de321bb4 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 21 Nov 2019 22:07:26 +0100 Subject: Remove FilmViewer::refresh_view. --- src/wx/film_viewer.cc | 12 +++--------- src/wx/film_viewer.h | 3 +-- src/wx/simple_video_view.cc | 4 ++-- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index c323c8281..7437612af 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -161,7 +161,7 @@ FilmViewer::set_film (shared_ptr film) if (!_film) { _player.reset (); recreate_butler (); - refresh_view (); + _video_view->update (); return; } @@ -224,17 +224,11 @@ FilmViewer::recreate_butler () resume (); } -void -FilmViewer::refresh_view () -{ - _video_view->update (); -} - void FilmViewer::set_outline_content (bool o) { _outline_content = o; - refresh_view (); + _video_view->update (); } void @@ -478,7 +472,7 @@ FilmViewer::config_changed (Config::Property p) { #ifdef DCPOMATIC_VARIANT_SWAROOP if (p == Config::PLAYER_BACKGROUND_IMAGE) { - refresh_view (); + _video_view->update (); return; } #endif diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 0699b12ae..21195e747 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -99,7 +99,7 @@ public: #ifdef DCPOMATIC_VARIANT_SWAROOP void set_background_image (bool b) { _background_image = b; - refresh_view (); + _video_view->update (); } bool background_image () const { @@ -163,7 +163,6 @@ private: dcpomatic::DCPTime uncorrected_time () const; Frame average_latency () const; - void refresh_view (); bool quick_refresh (); boost::shared_ptr _film; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 437aed4fa..63dc10200 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -209,7 +209,7 @@ SimpleVideoView::display_player_video () { if (!player_video().first) { set_image (shared_ptr()); - _viewer->refresh_view (); + update (); return; } @@ -252,7 +252,7 @@ SimpleVideoView::display_player_video () _inter_position = player_video().first->inter_position (); _inter_size = player_video().first->inter_size (); - _viewer->refresh_view (); + update (); _viewer->closed_captions_dialog()->update (_viewer->time()); } -- cgit v1.2.3 From b3b371294ed5e6cc18ef64ba1b06ca76726b903a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 21 Nov 2019 22:38:33 +0100 Subject: Remove some friends from FilmViewer. --- src/wx/closed_captions_dialog.cc | 19 ++++++++++++++++--- src/wx/closed_captions_dialog.h | 5 +++-- src/wx/film_viewer.cc | 11 ++++++++--- src/wx/film_viewer.h | 18 ++++-------------- src/wx/simple_video_view.cc | 2 -- src/wx/video_view.cc | 6 ++++-- src/wx/video_view.h | 18 ++++++++++++++++++ 7 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/wx/closed_captions_dialog.cc b/src/wx/closed_captions_dialog.cc index 061262cdd..afcf5fa87 100644 --- a/src/wx/closed_captions_dialog.cc +++ b/src/wx/closed_captions_dialog.cc @@ -53,6 +53,7 @@ ClosedCaptionsDialog::ClosedCaptionsDialog (wxWindow* parent, FilmViewer* viewer , _display (new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(640, (640 / 10) + 64))) , _track (new wxChoice(this, wxID_ANY)) , _current_in_lines (false) + , _timer (this) { _lines.resize (CLOSED_CAPTION_LINES); @@ -65,18 +66,30 @@ ClosedCaptionsDialog::ClosedCaptionsDialog (wxWindow* parent, FilmViewer* viewer sizer->Add (track_sizer, 0, wxALL, DCPOMATIC_SIZER_GAP); sizer->Add (_display, 1, wxEXPAND); + Bind (wxEVT_SHOW, boost::bind(&ClosedCaptionsDialog::shown, this, _1)); + Bind (wxEVT_TIMER, boost::bind(&ClosedCaptionsDialog::update, this)); _display->Bind (wxEVT_PAINT, boost::bind(&ClosedCaptionsDialog::paint, this)); _track->Bind (wxEVT_CHOICE, boost::bind(&ClosedCaptionsDialog::track_selected, this)); SetSizerAndFit (sizer); } +void +ClosedCaptionsDialog::shown (wxShowEvent ev) +{ + if (ev.IsShown ()) { + _timer.Start (40); + } else { + _timer.Stop (); + } +} + void ClosedCaptionsDialog::track_selected () { _current = optional (); _viewer->slow_refresh (); - update (_last_update); + update (); } void @@ -131,9 +144,9 @@ private: }; void -ClosedCaptionsDialog::update (DCPTime time) +ClosedCaptionsDialog::update () { - _last_update = time; + DCPTime const time = _viewer->time (); if (_current_in_lines && _current && _current->period.to > time) { /* Current one is fine */ diff --git a/src/wx/closed_captions_dialog.h b/src/wx/closed_captions_dialog.h index fb4e9d59b..5c366ca7b 100644 --- a/src/wx/closed_captions_dialog.h +++ b/src/wx/closed_captions_dialog.h @@ -31,11 +31,12 @@ class ClosedCaptionsDialog : public wxDialog public: explicit ClosedCaptionsDialog (wxWindow* parent, FilmViewer* viewer); - void update (dcpomatic::DCPTime); void clear (); void set_film_and_butler (boost::shared_ptr, boost::weak_ptr); private: + void shown (wxShowEvent); + void update (); void paint (); void track_selected (); @@ -47,5 +48,5 @@ private: std::vector _lines; std::vector _tracks; boost::weak_ptr _butler; - dcpomatic::DCPTime _last_update; + wxTimer _timer; }; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 7437612af..dcbfedc81 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -88,12 +88,10 @@ FilmViewer::FilmViewer (wxWindow* p) , _latency_history_count (0) , _closed_captions_dialog (new ClosedCaptionsDialog(p, this)) , _outline_content (false) - , _eyes (EYES_LEFT) , _pad_black (false) #ifdef DCPOMATIC_VARIANT_SWAROOP , _background_image (false) #endif - , _gets (0) , _idle_get (false) { switch (Config::instance()->video_view_type()) { @@ -234,7 +232,7 @@ FilmViewer::set_outline_content (bool o) void FilmViewer::set_eyes (Eyes e) { - _eyes = e; + _video_view->set_eyes (e); slow_refresh (); } @@ -651,3 +649,10 @@ FilmViewer::dropped () const return _video_view->dropped (); } +int +FilmViewer::gets () const +{ + return _video_view->gets (); +} + + diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 21195e747..ec481f34e 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -92,7 +92,11 @@ public: void slow_refresh (); + dcpomatic::DCPTime time () const; + boost::optional audio_time () const; + int dropped () const; + int gets () const; int audio_callback (void* out, unsigned int frames); @@ -107,10 +111,6 @@ public: } #endif - int gets () const { - return _gets; - } - StateTimer const & state_timer () const { return _video_view->state_timer (); } @@ -143,11 +143,6 @@ public: private: - /* XXX_b: to remove */ - friend class SimpleVideoView; - friend class GLVideoView; - friend class VideoView; - void video_view_sized (); void calculate_sizes (); void player_change (ChangeType type, int, bool); @@ -158,8 +153,6 @@ private: void config_changed (Config::Property); void film_length_change (); - dcpomatic::DCPTime time () const; - boost::optional audio_time () const; dcpomatic::DCPTime uncorrected_time () const; Frame average_latency () const; @@ -192,7 +185,6 @@ private: ClosedCaptionsDialog* _closed_captions_dialog; bool _outline_content; - Eyes _eyes; /** true to pad the viewer panel with black, false to use the normal window background colour. */ @@ -202,8 +194,6 @@ private: bool _background_image; #endif - int _gets; - /** true if an get() is required next time we are idle */ bool _idle_get; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 63dc10200..22e4db887 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -253,6 +253,4 @@ SimpleVideoView::display_player_video () _inter_size = player_video().first->inter_size (); update (); - - _viewer->closed_captions_dialog()->update (_viewer->time()); } diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index e65888656..5dd857fab 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -30,7 +30,9 @@ VideoView::VideoView (FilmViewer* viewer) #endif , _state_timer ("viewer") , _video_frame_rate (0) + , _eyes (EYES_LEFT) , _dropped (0) + , _gets (0) { } @@ -54,7 +56,7 @@ VideoView::get_next_frame (bool non_blocking) } DCPOMATIC_ASSERT (_viewer->butler()); - _viewer->_gets++; + add_get (); boost::mutex::scoped_lock lm (_mutex); @@ -67,7 +69,7 @@ VideoView::get_next_frame (bool non_blocking) } while ( _player_video.first && _viewer->film()->three_d() && - _viewer->_eyes != _player_video.first->eyes() && + _eyes != _player_video.first->eyes() && _player_video.first->eyes() != EYES_BOTH ); diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 4a94aa879..fd2684e41 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -23,6 +23,7 @@ #include "lib/dcpomatic_time.h" #include "lib/timer.h" +#include "lib/types.h" #include #include #include @@ -63,6 +64,11 @@ public: return _dropped; } + int gets () const { + boost::mutex::scoped_lock lm (_mutex); + return _gets; + } + StateTimer const & state_timer () const { return _state_timer; } @@ -82,6 +88,11 @@ public: _length = len; } + void set_eyes (Eyes eyes) { + boost::mutex::scoped_lock lm (_mutex); + _eyes = eyes; + } + protected: /* XXX_b: to remove */ friend class FilmViewer; @@ -110,6 +121,11 @@ protected: ++_dropped; } + void add_get () { + boost::mutex::scoped_lock lm (_mutex); + ++_gets; + } + FilmViewer* _viewer; #ifdef DCPOMATIC_VARIANT_SWAROOP @@ -128,8 +144,10 @@ private: int _video_frame_rate; /** length of the film we are playing, or 0 if there is none */ dcpomatic::DCPTime _length; + Eyes _eyes; int _dropped; + int _gets; }; #endif -- cgit v1.2.3 From f77529bdfa01ae13f889442900988fc401b63c62 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 21 Nov 2019 23:43:23 +0100 Subject: Various cleanups and thread-safety. --- src/wx/film_viewer.cc | 20 +++++++++++++++++--- src/wx/film_viewer.h | 3 ++- src/wx/gl_video_view.cc | 43 +++++++++++++++++++++++++++---------------- src/wx/gl_video_view.h | 4 ++++ src/wx/video_view.cc | 15 ++++++++++----- src/wx/video_view.h | 10 +++++++++- 6 files changed, 69 insertions(+), 26 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index dcbfedc81..df66a8ade 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -334,6 +334,8 @@ FilmViewer::stop () _playing = false; _video_view->stop (); Stopped (position()); + + _video_view->rethrow (); return true; } @@ -379,6 +381,8 @@ FilmViewer::film_change (ChangeType type, Film::Property p) recreate_butler (); } else if (p == Film::VIDEO_FRAME_RATE) { _video_view->set_video_frame_rate (_film->video_frame_rate()); + } else if (p == Film::THREE_D) { + _video_view->set_three_d (_film->three_d()); } } @@ -636,11 +640,21 @@ FilmViewer::set_pad_black (bool p) _pad_black = p; } -/* May be called from a non-UI thread */ +/** Called when a player has finished the current film. + * May be called from a non-UI thread. + */ void -FilmViewer::emit_finished () +FilmViewer::finished () { - emit (boost::bind(boost::ref(Finished))); + emit (boost::bind(&FilmViewer::ui_finished, this)); +} + +/** Called by finished() in the UI thread */ +void +FilmViewer::ui_finished () +{ + stop (); + Finished (); } int diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index ec481f34e..60cde60d0 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -131,13 +131,13 @@ public: ClosedCaptionsDialog* closed_captions_dialog () const { return _closed_captions_dialog; } + void finished (); boost::signals2::signal)> ImageChanged; boost::signals2::signal Started; boost::signals2::signal Stopped; /** While playing back we reached the end of the film (emitted from GUI thread) */ boost::signals2::signal Finished; - void emit_finished (); boost::signals2::signal PlaybackPermitted; @@ -152,6 +152,7 @@ private: void recreate_butler (); void config_changed (Config::Property); void film_length_change (); + void ui_finished (); dcpomatic::DCPTime uncorrected_time () const; Frame average_latency () const; diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index ce152787a..869d555cb 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -133,8 +133,11 @@ check_gl_error (char const * last) void GLVideoView::update () { - if (!_canvas->IsShownOnScreen()) { - return; + { + boost::mutex::scoped_lock lm (_canvas_mutex); + if (!_canvas->IsShownOnScreen()) { + return; + } } request_one_shot (); } @@ -155,16 +158,22 @@ GLVideoView::draw (Position inter_position, dcp::Size inter_size) check_gl_error ("glDisable GL_DEPTH_TEST"); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - if (_canvas->GetSize().x < 64 || _canvas->GetSize().y < 0) { + wxSize canvas_size; + { + boost::mutex::scoped_lock lm (_canvas_mutex); + canvas_size = _canvas->GetSize (); + } + + if (canvas_size.GetWidth() < 64 || canvas_size.GetHeight() < 0) { return; } - glViewport (0, 0, _canvas->GetSize().x, _canvas->GetSize().y); + glViewport (0, 0, canvas_size.GetWidth(), canvas_size.GetHeight()); check_gl_error ("glViewport"); glMatrixMode (GL_PROJECTION); glLoadIdentity (); - gluOrtho2D (0, _canvas->GetSize().x, _canvas->GetSize().y, 0); + gluOrtho2D (0, canvas_size.GetWidth(), canvas_size.GetHeight(), 0); check_gl_error ("gluOrtho2d"); glMatrixMode (GL_MODELVIEW); glLoadIdentity (); @@ -197,8 +206,6 @@ GLVideoView::draw (Position inter_position, dcp::Size inter_size) glEnd (); } - wxSize const canvas_size = _canvas->GetSize (); - if (!_viewer->pad_black() && out_size.width < canvas_size.GetWidth()) { glBegin (GL_QUADS); /* XXX: these colours are right for GNOME; may need adjusting for other OS */ @@ -241,6 +248,8 @@ GLVideoView::draw (Position inter_position, dcp::Size inter_size) } glFlush(); + + boost::mutex::scoped_lock lm (_canvas_mutex); _canvas->SwapBuffers(); } @@ -291,9 +300,11 @@ void GLVideoView::thread () try { - /* XXX_b: check all calls and signal emissions in this method & protect them if necessary */ - _context = new wxGLContext (_canvas); - _canvas->SetCurrent (*_context); + { + boost::mutex::scoped_lock lm (_canvas_mutex); + _context = new wxGLContext (_canvas); //local + _canvas->SetCurrent (*_context); + } while (true) { boost::mutex::scoped_lock lm (_playing_mutex); @@ -309,12 +320,12 @@ try dcpomatic::DCPTime const next = position() + one_video_frame(); if (next >= length()) { - _viewer->stop (); - _viewer->emit_finished (); + _viewer->finished (); continue; } get_next_frame (false); + //-- set_image (player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); inter_position = player_video().first->inter_position(); inter_size = player_video().first->inter_size(); @@ -330,13 +341,13 @@ try dcpomatic_sleep_milliseconds (time_until_next_frame()); } - delete _context; + /* XXX: leaks _context, but that seems preferable to deleting it here + * without also deleting the wxGLCanvas. + */ } catch (boost::thread_interrupted& e) { - /* XXX_b: store exceptions here */ - delete _context; - return; + store_current (); } bool diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index 4f509049b..827b12861 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -57,6 +57,10 @@ private: void create (); void check_for_butler_errors (); + /* Mutex for use of _canvas; it's only contended when our ::thread + is started up so this may be overkill. + */ + boost::mutex _canvas_mutex; wxGLCanvas* _canvas; wxGLContext* _context; diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index 5dd857fab..ee14f4a33 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -23,6 +23,8 @@ #include "film_viewer.h" #include "lib/butler.h" +using boost::shared_ptr; + VideoView::VideoView (FilmViewer* viewer) : _viewer (viewer) #ifdef DCPOMATIC_VARIANT_SWAROOP @@ -31,6 +33,7 @@ VideoView::VideoView (FilmViewer* viewer) , _state_timer ("viewer") , _video_frame_rate (0) , _eyes (EYES_LEFT) + , _three_d (false) , _dropped (0) , _gets (0) { @@ -45,30 +48,32 @@ VideoView::clear () _player_video.second = dcpomatic::DCPTime (); } -/** @param non_blocking true to return false quickly if no video is available quickly. +/** Could be called from any thread. + * @param non_blocking true to return false quickly if no video is available quickly. * @return false if we gave up because it would take too long, otherwise true. */ bool VideoView::get_next_frame (bool non_blocking) { - if (_length == dcpomatic::DCPTime()) { + if (length() == dcpomatic::DCPTime()) { return true; } - DCPOMATIC_ASSERT (_viewer->butler()); + shared_ptr butler = _viewer->butler (); + DCPOMATIC_ASSERT (butler); add_get (); boost::mutex::scoped_lock lm (_mutex); do { Butler::Error e; - _player_video = _viewer->butler()->get_video (!non_blocking, &e); + _player_video = butler->get_video (!non_blocking, &e); if (!_player_video.first && e == Butler::AGAIN) { return false; } } while ( _player_video.first && - _viewer->film()->three_d() && + _three_d && _eyes != _player_video.first->eyes() && _player_video.first->eyes() != EYES_BOTH ); diff --git a/src/wx/video_view.h b/src/wx/video_view.h index fd2684e41..5d5d33163 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -24,16 +24,18 @@ #include "lib/dcpomatic_time.h" #include "lib/timer.h" #include "lib/types.h" +#include "lib/exception_store.h" #include #include #include +#include class Image; class wxWindow; class FilmViewer; class PlayerVideo; -class VideoView +class VideoView : public ExceptionStore, public boost::noncopyable { public: VideoView (FilmViewer* viewer); @@ -93,6 +95,11 @@ public: _eyes = eyes; } + void set_three_d (bool t) { + boost::mutex::scoped_lock lm (_mutex); + _three_d = t; + } + protected: /* XXX_b: to remove */ friend class FilmViewer; @@ -145,6 +152,7 @@ private: /** length of the film we are playing, or 0 if there is none */ dcpomatic::DCPTime _length; Eyes _eyes; + bool _three_d; int _dropped; int _gets; -- cgit v1.2.3 From 166f44ff1b500f684417d660bb349d35383996ee Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 22 Nov 2019 00:10:35 +0100 Subject: More tidying up. --- src/wx/film_viewer.cc | 11 +---------- src/wx/gl_video_view.cc | 1 - src/wx/simple_video_view.cc | 12 ++++++------ src/wx/simple_video_view.h | 2 +- src/wx/video_view.cc | 17 +++++++++++++++++ src/wx/video_view.h | 28 +++++++++++++++------------- 6 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index df66a8ade..c7b32ff26 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -406,16 +406,7 @@ FilmViewer::slow_refresh () bool FilmViewer::quick_refresh () { - if (!_video_view->_player_video.first) { - return false; - } - - if (!_video_view->_player_video.first->reset_metadata (_film, _player->video_container_size(), _film->frame_size())) { - return false; - } - - _video_view->display_player_video (); - return true; + return _video_view->refresh_metadata (_film, _player->video_container_size(), _film->frame_size()); } void diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index 869d555cb..2da16f1df 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -325,7 +325,6 @@ try } get_next_frame (false); - //-- set_image (player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); inter_position = player_video().first->inter_position(); inter_size = player_video().first->inter_size(); diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 22e4db887..6eabbc0b0 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -134,9 +134,9 @@ SimpleVideoView::paint () } void -SimpleVideoView::update () +SimpleVideoView::refresh_panel () { - _state_timer.set ("update-view"); + _state_timer.set ("refresh-panel"); _panel->Refresh (); _panel->Update (); _state_timer.unset (); @@ -193,7 +193,7 @@ SimpleVideoView::display_next_frame (bool non_blocking) } } - display_player_video (); + update (); try { _viewer->butler()->rethrow (); @@ -205,11 +205,11 @@ SimpleVideoView::display_next_frame (bool non_blocking) } void -SimpleVideoView::display_player_video () +SimpleVideoView::update () { if (!player_video().first) { set_image (shared_ptr()); - update (); + refresh_panel (); return; } @@ -252,5 +252,5 @@ SimpleVideoView::display_player_video () _inter_position = player_video().first->inter_position (); _inter_size = player_video().first->inter_size (); - update (); + refresh_panel (); } diff --git a/src/wx/simple_video_view.h b/src/wx/simple_video_view.h index 86451fa66..a6a5cf47f 100644 --- a/src/wx/simple_video_view.h +++ b/src/wx/simple_video_view.h @@ -43,9 +43,9 @@ public: bool display_next_frame (bool non_blocking); private: + void refresh_panel (); void paint (); void timer (); - void display_player_video (); wxPanel* _panel; boost::shared_ptr _image; diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index ee14f4a33..7e9e1a947 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -110,3 +110,20 @@ VideoView::start () boost::mutex::scoped_lock lm (_mutex); _dropped = 0; } + +bool +VideoView::refresh_metadata (shared_ptr film, dcp::Size video_container_size, dcp::Size film_frame_size) +{ + boost::mutex::scoped_lock lm (_mutex); + if (!_player_video.first) { + return false; + } + + if (!_player_video.first->reset_metadata (film, video_container_size, film_frame_size)) { + return false; + } + + update (); + return true; +} + diff --git a/src/wx/video_view.h b/src/wx/video_view.h index 5d5d33163..ad492bd43 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -41,25 +41,25 @@ public: VideoView (FilmViewer* viewer); virtual ~VideoView () {} - virtual void set_image (boost::shared_ptr image) = 0; + /** @return the thing displaying the image */ virtual wxWindow* get () const = 0; - /** Redraw the view after something has changed like content outlining, - * the film being removed, etc. - */ + /** Re-make and display the image from the current _player_video */ virtual void update () = 0; - + /** Called when playback starts */ virtual void start (); - /* XXX_b: make pure */ + /** Called when playback stops */ virtual void stop () {} + /** Get the next frame and display it; used after seek */ + virtual bool display_next_frame (bool) = 0; void clear (); + bool refresh_metadata (boost::shared_ptr film, dcp::Size video_container_size, dcp::Size film_frame_size); + /** Emitted from the GUI thread when our display changes in size */ boost::signals2::signal Sized; - virtual bool display_next_frame (bool) = 0; - /* XXX_b: to remove */ - virtual void display_player_video () {} + /* Accessors for FilmViewer */ int dropped () const { boost::mutex::scoped_lock lm (_mutex); @@ -80,6 +80,11 @@ public: return _player_video.second; } + + /* Setters for FilmViewer so it can tell us our state and + * we can then use (thread) safely. + */ + void set_video_frame_rate (int r) { boost::mutex::scoped_lock lm (_mutex); _video_frame_rate = r; @@ -101,9 +106,6 @@ public: } protected: - /* XXX_b: to remove */ - friend class FilmViewer; - bool get_next_frame (bool non_blocking); int time_until_next_frame () const; dcpomatic::DCPTime one_video_frame () const; @@ -144,7 +146,7 @@ protected: StateTimer _state_timer; private: - /** Mutex protecting all the state in VideoView */ + /** Mutex protecting all the state in this class */ mutable boost::mutex _mutex; std::pair, dcpomatic::DCPTime> _player_video; -- cgit v1.2.3 From bdb0887facaf9cb16a7fcfab722cd83171fff8bd Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 22 Nov 2019 00:37:36 +0100 Subject: Fix some crashes. --- src/wx/film_viewer.cc | 3 +++ src/wx/gl_video_view.cc | 7 ++++--- src/wx/video_view.cc | 4 +++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index c7b32ff26..f921f83bd 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -406,6 +406,9 @@ FilmViewer::slow_refresh () bool FilmViewer::quick_refresh () { + if (!_video_view || !_film) { + return true; + } return _video_view->refresh_metadata (_film, _player->video_container_size(), _film->frame_size()); } diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index 2da16f1df..bfc611d37 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -325,9 +325,10 @@ try } get_next_frame (false); - set_image (player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); - inter_position = player_video().first->inter_position(); - inter_size = player_video().first->inter_size(); + shared_ptr pv = player_video().first; + set_image (pv->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); + inter_position = pv->inter_position(); + inter_size = pv->inter_size(); } draw (inter_position, inter_size); diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index 7e9e1a947..7f93f765e 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -60,7 +60,9 @@ VideoView::get_next_frame (bool non_blocking) } shared_ptr butler = _viewer->butler (); - DCPOMATIC_ASSERT (butler); + if (!butler) { + return false; + } add_get (); boost::mutex::scoped_lock lm (_mutex); -- cgit v1.2.3 From 706f73390eb37f16b220177650ea7e70c2462150 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 22 Nov 2019 01:52:53 +0100 Subject: Check that get_next_frame succeeds. --- src/wx/gl_video_view.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index bfc611d37..ddae9bb3c 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -326,9 +326,11 @@ try get_next_frame (false); shared_ptr pv = player_video().first; - set_image (pv->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); - inter_position = pv->inter_position(); - inter_size = pv->inter_size(); + if (pv) { + set_image (pv->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true)); + inter_position = pv->inter_position(); + inter_size = pv->inter_size(); + } } draw (inter_position, inter_size); -- cgit v1.2.3 From b33437685e43427459e7ea752f3cd3d621878573 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 22 Nov 2019 21:04:05 +0100 Subject: Add assert. --- src/wx/film_viewer.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index f921f83bd..17573b438 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -286,6 +286,7 @@ FilmViewer::suspend () void FilmViewer::resume () { + DCPOMATIC_ASSERT (_suspended > 0); --_suspended; if (_playing && !_suspended) { if (_audio.isStreamOpen()) { -- cgit v1.2.3 From 98b1d9ae3188a7419182b747caa16739bf649039 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 22 Nov 2019 21:04:14 +0100 Subject: Fix event handling (hopefully). --- src/wx/controls.cc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/wx/controls.cc b/src/wx/controls.cc index 71af4e8cf..0c12ba463 100644 --- a/src/wx/controls.cc +++ b/src/wx/controls.cc @@ -120,14 +120,7 @@ Controls::Controls (wxWindow* parent, shared_ptr viewer, bool editor _slider->Bind (wxEVT_SCROLL_THUMBTRACK, boost::bind(&Controls::slider_moved, this, false)); _slider->Bind (wxEVT_SCROLL_PAGEUP, boost::bind(&Controls::slider_moved, this, true)); _slider->Bind (wxEVT_SCROLL_PAGEDOWN, boost::bind(&Controls::slider_moved, this, true)); - _slider->Bind (wxEVT_SCROLL_CHANGED, boost::bind(&Controls::slider_released, this)); -#ifdef DCPOMATIC_OSX - /* _CHANGED is not received on OS X (at least, not when the - slider is dragged), so use this instead. Perhaps all - platforms could just use _THUMBRELEASE. - */ _slider->Bind (wxEVT_SCROLL_THUMBRELEASE, boost::bind(&Controls::slider_released, this)); -#endif _rewind_button->Bind (wxEVT_LEFT_DOWN, boost::bind(&Controls::rewind_clicked, this, _1)); _back_button->Bind (wxEVT_LEFT_DOWN, boost::bind(&Controls::back_clicked, this, _1)); _forward_button->Bind (wxEVT_LEFT_DOWN, boost::bind(&Controls::forward_clicked, this, _1)); -- cgit v1.2.3 From ac69a0eb81e23a341545e05fa12a0e26070cb222 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 23 Nov 2019 10:41:01 +0100 Subject: Set up things correctly on initialisation. --- src/wx/film_viewer.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 17573b438..704088645 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -182,6 +182,10 @@ FilmViewer::set_film (shared_ptr film) _film->LengthChange.connect (boost::bind(&FilmViewer::film_length_change, this)); _player->Change.connect (boost::bind (&FilmViewer::player_change, this, _1, _2, _3)); + film_change (CHANGE_TYPE_DONE, Film::VIDEO_FRAME_RATE); + film_change (CHANGE_TYPE_DONE, Film::THREE_D); + film_length_change (); + /* Keep about 1 second's worth of history samples */ _latency_history_count = _film->audio_frame_rate() / _audio_block_size; -- cgit v1.2.3 From 89e92b3e7effafd2ca3aa1e9300777f2d2fb6183 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 23 Nov 2019 10:42:16 +0100 Subject: Don't busy-wait when there's nothing to play. --- src/wx/gl_video_view.cc | 8 ++++++-- src/wx/simple_video_view.cc | 2 +- src/wx/video_view.cc | 8 +++++--- src/wx/video_view.h | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index ddae9bb3c..a461939a7 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -334,13 +334,17 @@ try } draw (inter_position, inter_size); - while (time_until_next_frame() < 5) { + while (true) { + optional n = time_until_next_frame(); + if (!n || *n > 5) { + break; + } get_next_frame (true); add_dropped (); } boost::this_thread::interruption_point (); - dcpomatic_sleep_milliseconds (time_until_next_frame()); + dcpomatic_sleep_milliseconds (time_until_next_frame().get_value_or(0)); } /* XXX: leaks _context, but that seems preferable to deleting it here diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 6eabbc0b0..e349b865c 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -159,7 +159,7 @@ SimpleVideoView::timer () } LOG_DEBUG_PLAYER("%1 -> %2; delay %3", next.seconds(), _viewer->time().seconds(), max((next.seconds() - _viewer->time().seconds()) * 1000, 1.0)); - _timer.Start (time_until_next_frame(), wxTIMER_ONE_SHOT); + _timer.Start (time_until_next_frame().get_value_or(0), wxTIMER_ONE_SHOT); if (_viewer->butler()) { _viewer->butler()->rethrow (); diff --git a/src/wx/video_view.cc b/src/wx/video_view.cc index 7f93f765e..4edc2cd23 100644 --- a/src/wx/video_view.cc +++ b/src/wx/video_view.cc @@ -22,8 +22,10 @@ #include "wx_util.h" #include "film_viewer.h" #include "lib/butler.h" +#include using boost::shared_ptr; +using boost::optional; VideoView::VideoView (FilmViewer* viewer) : _viewer (viewer) @@ -89,13 +91,13 @@ VideoView::one_video_frame () const return dcpomatic::DCPTime::from_frames (1, video_frame_rate()); } -/** @return Time in ms until the next frame is due */ -int +/** @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 0; + return optional(); } dcpomatic::DCPTime const next = position() + one_video_frame(); diff --git a/src/wx/video_view.h b/src/wx/video_view.h index ad492bd43..f9e067043 100644 --- a/src/wx/video_view.h +++ b/src/wx/video_view.h @@ -107,7 +107,7 @@ public: protected: bool get_next_frame (bool non_blocking); - int time_until_next_frame () const; + boost::optional time_until_next_frame () const; dcpomatic::DCPTime one_video_frame () const; int video_frame_rate () const { -- cgit v1.2.3 From 1d796edf59da644e91bd443505fb76e0397a1439 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 23 Nov 2019 10:44:39 +0100 Subject: Basic stress-test function for player. --- hacks/stress.py | 22 ++++++ src/tools/dcpomatic_player.cc | 151 +++++++++++++++++++++++++++++++++++++++++- src/wx/controls.cc | 10 +++ src/wx/controls.h | 5 ++ src/wx/standard_controls.cc | 16 +++++ src/wx/standard_controls.h | 5 ++ wscript | 4 ++ 7 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 hacks/stress.py diff --git a/hacks/stress.py b/hacks/stress.py new file mode 100644 index 000000000..46d06382b --- /dev/null +++ b/hacks/stress.py @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import random + +def make(dcp, output, seeks): + with open(output, 'w') as f: + # Open the DCP and start it playing + print("O %s" % dcp, file=f) + print("P", file=f) + for i in range(seeks): + # Wait a bit + print("W %d" % random.randint(500, 60000), file=f) + # Seek + print("K %d" % random.randint(0, 4095), file=f) + # Make sure we're still playing + print("P", file=f) + print("S", file=f) + + +make("/home/carl/DCP/Examples/BohemianRhapsody_TLR-7_S_DE-XX_DE_51_2K_TCFG_20180514_TM_IOP_OV/", "boho", 64) + + diff --git a/src/tools/dcpomatic_player.cc b/src/tools/dcpomatic_player.cc index 6bd5b36ff..328093d8a 100644 --- a/src/tools/dcpomatic_player.cc +++ b/src/tools/dcpomatic_player.cc @@ -73,6 +73,7 @@ #include #endif #include +#include #include #ifdef check @@ -93,8 +94,61 @@ using boost::optional; using boost::dynamic_pointer_cast; using boost::thread; using boost::bind; +using dcp::raw_convert; using namespace dcpomatic; +#ifdef DCPOMATIC_PLAYER_STRESS_TEST +#define STRESS_TEST_CHECK_INTERVAL 20 + +class Command +{ +public: + enum Type { + NONE, + OPEN, + PLAY, + WAIT, + STOP, + SEEK, + }; + + Command(string line) + : type (NONE) + , int_param (0) + { + vector bits; + boost::split (bits, line, boost::is_any_of(" ")); + if (bits[0] == "O") { + if (bits.size() != 2) { + return; + } + type = OPEN; + string_param = bits[1]; + } else if (bits[0] == "P") { + type = PLAY; + } else if (bits[0] == "W") { + if (bits.size() != 2) { + return; + } + type = WAIT; + int_param = raw_convert(bits[1]); + } else if (bits[0] == "S") { + type = STOP; + } else if (bits[0] == "K") { + if (bits.size() != 2) { + return; + } + type = SEEK; + int_param = raw_convert(bits[1]); + } + } + + Type type; + string string_param; + int int_param; +}; +#endif + enum { ID_file_open = 1, ID_file_add_ov, @@ -139,7 +193,10 @@ public: , _system_information_dialog (0) , _view_full_screen (0) , _view_dual_screen (0) - { +#ifdef DCPOMATIC_PLAYER_STRESS_TEST + , _timer (this) +#endif +{ dcpomatic_log.reset (new NullLog()); #if defined(DCPOMATIC_WINDOWS) @@ -236,6 +293,70 @@ public: #endif } +#ifdef DCPOMATIC_PLAYER_STRESS_TEST + void stress (boost::filesystem::path script_file) + { + Bind (wxEVT_TIMER, boost::bind(&DOMFrame::check_commands, this)); + _timer.Start(STRESS_TEST_CHECK_INTERVAL); + vector lines; + string const script = dcp::file_to_string(script_file); + boost::split (lines, script, boost::is_any_of("\n")); + BOOST_FOREACH (string i, lines) { + _commands.push_back (Command(i)); + } + _current_command = _commands.begin(); + } + + void check_commands () + { + if (_current_command == _commands.end()) { + _timer.Stop (); + cout << "ST: finished.\n"; + return; + } + + switch (_current_command->type) { + case Command::OPEN: + cout << "ST: load " << _current_command->string_param << "\n"; + load_dcp (_current_command->string_param); + ++_current_command; + break; + case Command::PLAY: + cout << "ST: play\n"; + _controls->play (); + ++_current_command; + break; + case Command::WAIT: + if (_wait_remaining) { + _wait_remaining = *_wait_remaining - STRESS_TEST_CHECK_INTERVAL; + if (_wait_remaining < 0) { + cout << "ST: wait done.\n"; + _wait_remaining = optional(); + ++_current_command; + } + } else { + _wait_remaining = _current_command->int_param; + cout << "ST: waiting for " << *_wait_remaining << ".\n"; + } + break; + case Command::STOP: + cout << "ST: stop\n"; + _controls->stop (); + ++_current_command; + break; + case Command::NONE: + ++_current_command; + break; + case Command::SEEK: + /* int_param here is a number between 0 and 4095, corresponding to the possible slider positions */ + cout << "ST: seek to " << _current_command->int_param << "\n"; + _controls->seek (_current_command->int_param); + ++_current_command; + break; + } + } +#endif + #ifdef DCPOMATIC_VARIANT_SWAROOP void monitor_checker_state_changed () { @@ -1004,11 +1125,20 @@ private: wxMenuItem* _tools_verify; wxMenuItem* _view_full_screen; wxMenuItem* _view_dual_screen; +#ifdef DCPOMATIC_PLAYER_STRESS_TEST + wxTimer _timer; + list _commands; + list::const_iterator _current_command; + optional _wait_remaining; +#endif }; static const wxCmdLineEntryDesc command_line_description[] = { { wxCMD_LINE_PARAM, 0, 0, "DCP to load or create", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, { wxCMD_LINE_OPTION, "c", "config", "Directory containing config.xml", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, +#ifdef DCPOMATIC_PLAYER_STRESS_TEST + { wxCMD_LINE_OPTION, "s", "stress", "File containing description of stress test", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, +#endif { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 } }; @@ -1124,6 +1254,16 @@ private: } } +#ifdef DCPOMATIC_PLAYER_STRESS_TEST + if (_stress) { + try { + _frame->stress (_stress.get()); + } catch (exception& e) { + error_dialog (0, wxString::Format("Could not load stress test file %s", std_to_wx(*_stress))); + } + } +#endif + Bind (wxEVT_IDLE, boost::bind (&App::idle, this)); if (Config::instance()->check_for_updates ()) { @@ -1157,6 +1297,12 @@ private: if (parser.Found("c", &config)) { Config::override_path = wx_to_std (config); } +#ifdef DCPOMATIC_PLAYER_STRESS_TEST + wxString stress; + if (parser.Found("s", &stress)) { + _stress = wx_to_std (stress); + } +#endif return true; } @@ -1217,6 +1363,9 @@ private: DOMFrame* _frame; string _dcp_to_load; +#ifdef DCPOMATIC_PLAYER_STRESS_TEST + boost::optional _stress; +#endif }; IMPLEMENT_APP (App) diff --git a/src/wx/controls.cc b/src/wx/controls.cc index 0c12ba463..27139f1f5 100644 --- a/src/wx/controls.cc +++ b/src/wx/controls.cc @@ -406,3 +406,13 @@ Controls::film_change (ChangeType type, Film::Property p) } } } + +#ifdef DCPOMATIC_PLAYER_STRESS_TEST +void +Controls::seek (int slider) +{ + _slider->SetValue (slider); + slider_moved (false); + slider_released (); +} +#endif diff --git a/src/wx/controls.h b/src/wx/controls.h index 014464456..1b6a379cc 100644 --- a/src/wx/controls.h +++ b/src/wx/controls.h @@ -53,6 +53,11 @@ public: virtual void log (wxString) {} virtual void set_film (boost::shared_ptr film); +#ifdef DCPOMATIC_PLAYER_STRESS_TEST + virtual void play () {}; + virtual void stop () {}; + void seek (int slider); +#endif boost::shared_ptr film () const; void back_frame (); void forward_frame (); diff --git a/src/wx/standard_controls.cc b/src/wx/standard_controls.cc index 956f82c96..e9a31c86b 100644 --- a/src/wx/standard_controls.cc +++ b/src/wx/standard_controls.cc @@ -74,3 +74,19 @@ StandardControls::setup_sensitivity () bool const active_job = _active_job && *_active_job != "examine_content"; _play_button->Enable (_film && !_film->content().empty() && !active_job); } + +#ifdef DCPOMATIC_PLAYER_STRESS_TEST +void +StandardControls::play () +{ + _play_button->SetValue (true); + play_clicked (); +} + +void +StandardControls::stop () +{ + _play_button->SetValue (false); + play_clicked (); +} +#endif diff --git a/src/wx/standard_controls.h b/src/wx/standard_controls.h index b485c48cc..f79e4a178 100644 --- a/src/wx/standard_controls.h +++ b/src/wx/standard_controls.h @@ -25,6 +25,11 @@ class StandardControls : public Controls public: StandardControls (wxWindow* parent, boost::shared_ptr viewer, bool editor_controls); +#ifdef DCPOMATIC_PLAYER_STRESS_TEST + void play (); + void stop (); +#endif + private: void check_play_state (); void play_clicked (); diff --git a/wscript b/wscript index fe1431316..c61b86782 100644 --- a/wscript +++ b/wscript @@ -73,6 +73,7 @@ def options(opt): opt.add_option('--workaround-gssapi', action='store_true', default=False, help='link to gssapi_krb5') opt.add_option('--force-cpp11', action='store_true', default=False, help='force use of C++11') opt.add_option('--variant', help='build variant (swaroop-studio, swaroop-theater)', choices=['swaroop-studio', 'swaroop-theater']) + opt.add_option('--enable-player-stress-test', action='store_true', default=False, help='build the player with stress testing enabled') opt.add_option('--use-lld', action='store_true', default=False, help='use lld linker') def configure(conf): @@ -133,6 +134,9 @@ def configure(conf): if conf.options.variant.startswith('swaroop-'): conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_VARIANT_SWAROOP') + if conf.options.enable_player_stress_test: + conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_PLAYER_STRESS_TEST') + if conf.options.use_lld: try: conf.find_program('ld.lld') -- cgit v1.2.3 From 4f0575fcb518d959e8dcf581ec8181609782b4ef Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 25 Nov 2019 21:24:03 +0100 Subject: Use glTexSubImage2D when possible, as suggested by https://www.khronos.org/opengl/wiki/Common_Mistakes --- src/wx/gl_video_view.cc | 15 +++++++++++++-- src/wx/gl_video_view.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index a461939a7..d47ad87f4 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -55,6 +55,7 @@ using boost::optional; GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) : VideoView (viewer) + , _have_storage (false) , _vsync_enabled (false) , _thread (0) , _playing (false) @@ -264,11 +265,21 @@ GLVideoView::set_image (shared_ptr image) DCPOMATIC_ASSERT (image->pixel_format() == AV_PIX_FMT_RGB24); DCPOMATIC_ASSERT (!image->aligned()); + if (image->size() != _size) { + _have_storage = false; + } + _size = image->size (); glPixelStorei (GL_UNPACK_ALIGNMENT, 1); check_gl_error ("glPixelStorei"); - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB8, _size->width, _size->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->data()[0]); - check_gl_error ("glTexImage2D"); + if (_have_storage) { + glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, _size->width, _size->height, GL_RGB, GL_UNSIGNED_BYTE, image->data()[0]); + check_gl_error ("glTexSubImage2D"); + } else { + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB8, _size->width, _size->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->data()[0]); + _have_storage = true; + check_gl_error ("glTexImage2D"); + } glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index 827b12861..2f3c8c2a1 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -66,6 +66,7 @@ private: GLuint _id; boost::optional _size; + bool _have_storage; bool _vsync_enabled; boost::thread* _thread; -- cgit v1.2.3 From 0b21bfba60be812a5d04cc5c10868e04b5579404 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 26 Nov 2019 00:32:37 +0100 Subject: Fix problems when starting to play after a seek, if the start of playback happens while we are awaiting an idle handler to go through to update the view after the seek. If this idle handler has not gone through the viewer will report an incorrect position, which is used by the FilmViewer to start the audio stream at the wrong time. --- src/wx/film_viewer.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 704088645..3a593fc55 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -314,6 +314,14 @@ FilmViewer::start () return; } + /* We are about to set up the audio stream from the position of the video view. + If there is `lazy' seek in progress we need to wait for it to go through so that + _video_view->position() gives us a sensible answer. + */ + while (_idle_get) { + idle_handler (); + } + if (_audio.isStreamOpen()) { _audio.setStreamTime (_video_view->position().seconds()); _audio.startStream (); -- cgit v1.2.3 From c967096b5061e4bbb3a275d4daf04a2b9c084468 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 26 Nov 2019 00:33:46 +0100 Subject: Some simple-video-view tidy-ups. --- src/wx/simple_video_view.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index e349b865c..ae13fe1bc 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -85,10 +85,10 @@ SimpleVideoView::paint () #ifdef DCPOMATIC_VARIANT_SWAROOP DCPTime const period = DCPTime::from_seconds(Config::instance()->player_watermark_period() * 60); - int64_t n = _viewer->position().get() / period.get(); + int64_t n = position().get() / period.get(); DCPTime from(n * period.get()); DCPTime to = from + DCPTime::from_seconds(Config::instance()->player_watermark_duration() / 1000.0); - if (from <= _viewer->position() && _viewer->position() <= to) { + if (from <= position() && position() <= to) { if (!_in_watermark) { _in_watermark = true; _watermark_x = rand() % panel_size.GetWidth(); @@ -150,11 +150,10 @@ SimpleVideoView::timer () } display_next_frame (false); - DCPTime const next = _viewer->position() + _viewer->one_video_frame(); + DCPTime const next = position() + _viewer->one_video_frame(); if (next >= length()) { - _viewer->stop (); - _viewer->Finished (); + _viewer->finished (); return; } -- cgit v1.2.3 From b1589146e73ad97d3f29e9d5cafe61e5424796a4 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 26 Nov 2019 00:37:42 +0100 Subject: Some comments. --- src/wx/film_viewer.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 3a593fc55..9c3a9c81e 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -322,6 +322,9 @@ FilmViewer::start () idle_handler (); } + /* Take the video view's idea of position as our `playhead' and start the + audio stream (which is the timing reference) there. + */ if (_audio.isStreamOpen()) { _audio.setStreamTime (_video_view->position().seconds()); _audio.startStream (); @@ -468,8 +471,14 @@ FilmViewer::seek (DCPTime t, bool accurate) _butler->seek (t, accurate); if (!_playing) { + /* We're not playing, so let the GUI thread get on and + come back later to get the next frame after the seek. + */ request_idle_display_next_frame (); } else { + /* We're going to start playing again straight away + so wait for the seek to finish. + */ while (!_video_view->display_next_frame(false)) {} } -- cgit v1.2.3 From 23590dc430e4ef2351209e30a26ba04fecca2872 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 12 Dec 2019 00:45:16 +0100 Subject: First version of player stress-test management script. --- src/tools/stress | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100755 src/tools/stress diff --git a/src/tools/stress b/src/tools/stress new file mode 100755 index 000000000..f861753e6 --- /dev/null +++ b/src/tools/stress @@ -0,0 +1,57 @@ +#!/usr/bin/python3.7 + +import argparse +import subprocess +import sys +import random + +def hms_to_seconds(h): + s = h.split(':') + assert(1 <= len(s) and len(s) <= 3) + if len(s) == 1: + return int(h) + elif len(s) == 2: + return int(s[0]) * 60 + int(s[1]) + elif len(s) == 3: + return ((int(s[0]) * 60 + int(s[1])) * 60) + int(s[2]) + +def seek(dcp_seconds): + print("O %s" % args.dcp) + print("P") + test_seconds = hms_to_seconds(args.length) + while test_seconds > 0: + wait = random.randint(500, dcp_seconds * 1000) + # Wait some milliseconds + print("W %d" % wait) + # Seek + print("S %d" % random.randint(0, 4095)) + # Make sure we're stil playing + print("P") + test_seconds -= wait / 1000 + +def repeat(dcp_seconds): + print("O %s" % args.dcp) + test_seconds = hms_to_seconds(args.length) + while test_seconds > 0: + print("P") + print("W %d" % (dcp_seconds * 1000)) + test_seconds -= dcp_seconds + +parser = argparse.ArgumentParser() +parser.add_argument('-d', '--dcp', help='DCP to make a script for', required=True) +parser.add_argument('-t', '--type', help='script type: seek - seek a lot, repeat - play back DCP over and over', required=True) +parser.add_argument('-l', '--length', help='approximate test length in H:M:S', required=True) +args = parser.parse_args() + +for l in subprocess.run(['dcpinfo', args.dcp], capture_output=True).stdout.splitlines(): + if l.startswith(b'Total:'): + b = l.split(b':') + dcp_seconds = (int(b[1]) * 60 + int(b[2])) * 60 + int(b[3]) +if args.type == 'seek': + seek(dcp_seconds) +elif args.type == 'repeat': + repeat(dcp_seconds) +else: + print('Unknown type %s' % args.type, file=sys.stderr) + sys.exit(1) + -- cgit v1.2.3