Differentiate between stop and suspend in player.
[dcpomatic.git] / src / wx / film_viewer.cc
index edb451e74b8f1ce15d1947dc7792651342258f83..9b01cd394bdee06f65e30915e381f7f3a1b6f8cd 100644 (file)
@@ -84,6 +84,7 @@ FilmViewer::FilmViewer (wxWindow* p)
        , _audio_channels (0)
        , _audio_block_size (1024)
        , _playing (false)
+       , _suspended (0)
        , _latency_history_count (0)
        , _dropped (0)
        , _closed_captions_dialog (new ClosedCaptionsDialog(p, this))
@@ -95,6 +96,7 @@ FilmViewer::FilmViewer (wxWindow* p)
 #endif
        , _state_timer ("viewer")
        , _gets (0)
+       , _idle_get (false)
 {
        switch (Config::instance()->video_view_type()) {
        case Config::VIDEO_VIEW_OPENGL:
@@ -119,6 +121,34 @@ FilmViewer::~FilmViewer ()
        stop ();
 }
 
+/** Ask for ::get() to be called next time we are idle */
+void
+FilmViewer::request_idle_get ()
+{
+       if (_idle_get) {
+               return;
+       }
+
+       _idle_get = true;
+       DCPOMATIC_ASSERT (signal_manager);
+       signal_manager->when_idle (boost::bind(&FilmViewer::idle_handler, this));
+}
+
+void
+FilmViewer::idle_handler ()
+{
+       if (!_idle_get) {
+               return;
+       }
+
+       if (get(true)) {
+               _idle_get = false;
+       } else {
+               /* get() could not complete quickly so we'll try again later */
+               signal_manager->when_idle (boost::bind(&FilmViewer::idle_handler, this));
+       }
+}
+
 void
 FilmViewer::set_film (shared_ptr<Film> film)
 {
@@ -171,10 +201,11 @@ FilmViewer::set_film (shared_ptr<Film> film)
 void
 FilmViewer::recreate_butler ()
 {
-       bool const was_running = stop ();
+       suspend ();
        _butler.reset ();
 
        if (!_film) {
+               resume ();
                return;
        }
 
@@ -218,9 +249,7 @@ FilmViewer::recreate_butler ()
 
        _closed_captions_dialog->set_film_and_butler (_film, _butler);
 
-       if (was_running) {
-               start ();
-       }
+       resume ();
 }
 
 void
@@ -231,12 +260,12 @@ FilmViewer::refresh_view ()
        _state_timer.unset ();
 }
 
-/** @param lazy true if it is *not* important that the display be updated as quickly as possible.
- *  If lazy is true we will try to return from this method quickly and postpone any time-consuming
- *  work until the UI is next idle.  Otherwise we will block here and try to get the image on
- *  screen as soon as possible.
+/** 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.
  */
-void
+bool
 FilmViewer::get (bool lazy)
 {
        DCPOMATIC_ASSERT (_butler);
@@ -246,8 +275,14 @@ FilmViewer::get (bool lazy)
                Butler::Error e;
                _player_video = _butler->get_video (!lazy, &e);
                if (!_player_video.first && e == Butler::AGAIN) {
-                       signal_manager->when_idle (boost::bind(&FilmViewer::get, this, lazy));
-                       return;
+                       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 &&
@@ -262,11 +297,10 @@ FilmViewer::get (bool lazy)
                error_dialog (_video_view->get(), e.what());
        }
 
-       if (lazy) {
-               signal_manager->when_idle (boost::bind(&FilmViewer::display_player_video, this));
-       } else {
-               display_player_video ();
-       }
+       display_player_video ();
+       PositionChanged ();
+
+       return true;
 }
 
 void
@@ -278,7 +312,7 @@ FilmViewer::display_player_video ()
                return;
        }
 
-       if (_playing && (time() - _player_video.second) > one_video_frame()) {
+       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).
                */
@@ -327,12 +361,11 @@ FilmViewer::display_player_video ()
 void
 FilmViewer::timer ()
 {
-       if (!_film || !_playing) {
+       if (!_film || !_playing || _suspended) {
                return;
        }
 
        get (false);
-       PositionChanged ();
        DCPTime const next = _video_position + one_video_frame();
 
        if (next >= _film->length()) {
@@ -402,6 +435,28 @@ FilmViewer::calculate_sizes ()
        _player->set_video_container_size (_out_size);
 }
 
+void
+FilmViewer::suspend ()
+{
+       ++_suspended;
+       if (_audio.isStreamRunning()) {
+               _audio.abortStream();
+       }
+}
+
+void
+FilmViewer::resume ()
+{
+       --_suspended;
+       if (_playing && !_suspended) {
+               if (_audio.isStreamOpen()) {
+                       _audio.setStreamTime (_video_position.seconds());
+                       _audio.startStream ();
+               }
+               timer ();
+       }
+}
+
 void
 FilmViewer::start ()
 {
@@ -546,17 +601,19 @@ FilmViewer::seek (DCPTime t, bool accurate)
                t = _film->length ();
        }
 
-       bool const was_running = stop ();
+       suspend ();
 
        _closed_captions_dialog->clear ();
        _butler->seek (t, accurate);
-       get (true);
 
-       if (was_running) {
-               start ();
+       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)) {}
        }
 
-       PositionChanged ();
+       resume ();
 }
 
 void