Don't request idle get if we are starting playback again.
[dcpomatic.git] / src / wx / film_viewer.cc
index 5b2c5ff95abc78f21a9cbf47314bb9cef4f3eed5..495b82567c20a0d560a1e3de95a57e28a33d236a 100644 (file)
@@ -79,10 +79,7 @@ rtaudio_callback (void* out, void *, unsigned int frames, double, RtAudioStreamS
 }
 
 FilmViewer::FilmViewer (wxWindow* p)
-       /* XXX: make this configurable */
-//     : _video_view (new GLVideoView(p))
-       : _video_view (new SimpleVideoView(this, p))
-       , _coalesce_player_changes (false)
+       : _coalesce_player_changes (false)
        , _audio (DCPOMATIC_RTAUDIO_API)
        , _audio_channels (0)
        , _audio_block_size (1024)
@@ -94,15 +91,23 @@ FilmViewer::FilmViewer (wxWindow* p)
        , _eyes (EYES_LEFT)
        , _pad_black (false)
 #ifdef DCPOMATIC_VARIANT_SWAROOP
-       , _in_watermark (false)
        , _background_image (false)
 #endif
        , _state_timer ("viewer")
        , _gets (0)
+       , _idle_get (false)
 {
-       /* XXX: maybe this should be proxied through the VideoView */
-       _video_view->get()->Bind (wxEVT_SIZE, boost::bind (&FilmViewer::video_view_sized, this));
-       _timer.Bind  (wxEVT_TIMER, boost::bind (&FilmViewer::timer, this));
+       switch (Config::instance()->video_view_type()) {
+       case Config::VIDEO_VIEW_OPENGL:
+               _video_view = new GLVideoView (this, p);
+               break;
+       case Config::VIDEO_VIEW_SIMPLE:
+               _video_view = new SimpleVideoView (this, p);
+               break;
+       }
+
+       _video_view->Sized.connect (boost::bind(&FilmViewer::video_view_sized, this));
+       _timer.Bind (wxEVT_TIMER, boost::bind(&FilmViewer::timer, this));
 
        set_film (shared_ptr<Film> ());
 
@@ -115,6 +120,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)
 {
@@ -127,13 +160,12 @@ FilmViewer::set_film (shared_ptr<Film> film)
        _player_video.first.reset ();
        _player_video.second = DCPTime ();
 
-       _frame.reset ();
+       _video_view->set_image (shared_ptr<Image>());
        _closed_captions_dialog->clear ();
 
        if (!_film) {
                _player.reset ();
                recreate_butler ();
-               _frame.reset ();
                refresh_view ();
                return;
        }
@@ -186,14 +218,26 @@ FilmViewer::recreate_butler ()
                   Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
                               Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
                */
-               map.set (dcp::LEFT,   0, 1 / sqrt(2)); // L -> Lt
-               map.set (dcp::RIGHT,  1, 1 / sqrt(2)); // R -> Rt
-               map.set (dcp::CENTRE, 0, 1 / 2.0); // C -> Lt
-               map.set (dcp::CENTRE, 1, 1 / 2.0); // C -> Rt
-               map.set (dcp::LFE,    0, 1 / sqrt(10)); // Lfe -> Lt
-               map.set (dcp::LFE,    1, 1 / sqrt(10)); // Lfe -> Rt
-               map.set (dcp::LS,     0, 1 / sqrt(2)); // Ls -> Lt
-               map.set (dcp::RS,     1, 1 / sqrt(2)); // Rs -> Rt
+               if (_film->audio_channels() > 0) {
+                       map.set (dcp::LEFT,   0, 1 / sqrt(2)); // L -> Lt
+               }
+               if (_film->audio_channels() > 1) {
+                       map.set (dcp::RIGHT,  1, 1 / sqrt(2)); // R -> Rt
+               }
+               if (_film->audio_channels() > 2) {
+                       map.set (dcp::CENTRE, 0, 1 / 2.0); // C -> Lt
+                       map.set (dcp::CENTRE, 1, 1 / 2.0); // C -> Rt
+               }
+               if (_film->audio_channels() > 3) {
+                       map.set (dcp::LFE,    0, 1 / sqrt(10)); // Lfe -> Lt
+                       map.set (dcp::LFE,    1, 1 / sqrt(10)); // Lfe -> Rt
+               }
+               if (_film->audio_channels() > 4) {
+                       map.set (dcp::LS,     0, 1 / sqrt(2)); // Ls -> Lt
+               }
+               if (_film->audio_channels() > 5) {
+                       map.set (dcp::RS,     1, 1 / sqrt(2)); // Rs -> Rt
+               }
        }
 
        _butler.reset (new Butler(_player, map, _audio_channels, bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true));
@@ -211,24 +255,34 @@ FilmViewer::recreate_butler ()
 void
 FilmViewer::refresh_view ()
 {
-       _state_timer.set ("refresh-view");
-       _video_view->get()->Refresh ();
-       _video_view->get()->Update ();
+       _state_timer.set ("update-view");
+       _video_view->update ();
        _state_timer.unset ();
 }
 
-void
-FilmViewer::get ()
+/** 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 (&e);
+               _player_video = _butler->get_video (!lazy, &e);
                if (!_player_video.first && e == Butler::AGAIN) {
-                       signal_manager->when_idle (boost::bind(&FilmViewer::get, this));
-                       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 &&
@@ -237,16 +291,23 @@ FilmViewer::get ()
                _player_video.first->eyes() != EYES_BOTH
                );
 
-       _butler->rethrow ();
+       try {
+               _butler->rethrow ();
+       } catch (DecodeError& e) {
+               error_dialog (_video_view->get(), e.what());
+       }
 
        display_player_video ();
+       PositionChanged ();
+
+       return true;
 }
 
 void
 FilmViewer::display_player_video ()
 {
        if (!_player_video.first) {
-               _frame.reset ();
+               _video_view->set_image (shared_ptr<Image>());
                refresh_view ();
                return;
        }
@@ -280,9 +341,9 @@ FilmViewer::display_player_video ()
 
        _state_timer.set ("get image");
 
-       /* XXX: do we need to store _frame? */
-       _frame = _player_video.first->image (bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true);
-       _video_view->set_image (_frame);
+       _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);
@@ -304,8 +365,7 @@ FilmViewer::timer ()
                return;
        }
 
-       get ();
-       PositionChanged ();
+       get (false);
        DCPTime const next = _video_position + one_video_frame();
 
        if (next >= _film->length()) {
@@ -322,28 +382,6 @@ FilmViewer::timer ()
        }
 }
 
-bool
-#ifdef DCPOMATIC_VARIANT_SWAROOP
-XXX
-FilmViewer::maybe_draw_background_image (wxPaintDC& dc)
-{
-       optional<boost::filesystem::path> bg = Config::instance()->player_background_image();
-       if (bg) {
-               wxImage image (std_to_wx(bg->string()));
-               wxBitmap bitmap (image);
-               dc.DrawBitmap (bitmap, max(0, (_panel_size.width - image.GetSize().GetWidth()) / 2), max(0, (_panel_size.height - image.GetSize().GetHeight()) / 2));
-               return true;
-       }
-
-       return false;
-}
-#else
-FilmViewer::maybe_draw_background_image (wxPaintDC &)
-{
-       return false;
-}
-#endif
-
 void
 FilmViewer::set_outline_content (bool o)
 {
@@ -394,10 +432,6 @@ FilmViewer::calculate_sizes ()
        _out_size.width = max (64, _out_size.width);
        _out_size.height = max (64, _out_size.height);
 
-       /* Make OpenGL happy; XXX: only do this in GLVideoView? Is the round-to-4 constraint a thing? */
-       _out_size.width &= ~3;
-       _out_size.height &= ~3;
-
        _player->set_video_container_size (_out_size);
 }
 
@@ -549,13 +583,12 @@ FilmViewer::seek (DCPTime t, bool accurate)
 
        _closed_captions_dialog->clear ();
        _butler->seek (t, accurate);
-       get ();
 
-       if (was_running) {
+       if (!was_running) {
+               request_idle_get ();
+       } else {
                start ();
        }
-
-       PositionChanged ();
 }
 
 void