+ dc.DrawRectangle (_inter_position.x, _inter_position.y + (panel_size.GetHeight() - out_size.height) / 2, _inter_size.width, _inter_size.height);
+ }
+
+ auto subs = _viewer->outline_subtitles();
+ if (subs) {
+ wxPen p (outline_subtitles_colour(), 2);
+ dc.SetPen (p);
+ dc.SetBrush (*wxTRANSPARENT_BRUSH);
+ dc.DrawRectangle (subs->x * out_size.width, subs->y * out_size.height, subs->width * out_size.width, subs->height * out_size.height);
+ }
+
+ _state_timer.unset();
+}
+
+
+void
+SimpleVideoView::refresh_panel ()
+{
+ _state_timer.set ("refresh-panel");
+ _panel->Refresh ();
+ _panel->Update ();
+ _state_timer.unset ();
+}
+
+
+void
+SimpleVideoView::timer ()
+{
+ if (!_viewer->playing()) {
+ return;
+ }
+
+ display_next_frame (false);
+ auto const next = position() + _viewer->one_video_frame();
+
+ if (next >= length()) {
+ _viewer->finished ();
+ return;
+ }
+
+ LOG_DEBUG_VIDEO_VIEW("%1 -> %2; delay %3", next.seconds(), _viewer->time().seconds(), max((next.seconds() - _viewer->time().seconds()) * 1000, 1.0));
+ _timer.Start (max(1, time_until_next_frame().get_value_or(0)), wxTIMER_ONE_SHOT);
+
+ if (_viewer->butler()) {
+ _viewer->butler()->rethrow ();
+ }
+}
+
+
+void
+SimpleVideoView::start ()
+{
+ VideoView::start ();
+ timer ();
+}
+
+
+/** Try to get a frame from the butler and display it.
+ * @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.
+ */
+VideoView::NextFrameResult
+SimpleVideoView::display_next_frame (bool non_blocking)
+{
+ auto const r = get_next_frame (non_blocking);
+ if (r != SUCCESS) {
+ return r;