+
+void
+GLVideoView::start ()
+{
+ VideoView::start ();
+
+ boost::mutex::scoped_lock lm (_playing_mutex);
+ _playing = true;
+ _playing_condition.notify_all ();
+}
+
+void
+GLVideoView::stop ()
+{
+ boost::mutex::scoped_lock lm (_playing_mutex);
+ _playing = false;
+}
+
+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);
+
+ while (true) {
+ boost::mutex::scoped_lock lm (_playing_mutex);
+ while (!_playing && !_one_shot) {
+ _playing_condition.wait (lm);
+ }
+ _one_shot = false;
+ lm.unlock ();
+
+ Position<int> inter_position;
+ dcp::Size inter_size;
+ if (length() != dcpomatic::DCPTime()) {
+ dcpomatic::DCPTime const next = position() + one_video_frame();
+
+ 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));
+ inter_position = player_video().first->inter_position();
+ inter_size = player_video().first->inter_size();
+ }
+ draw (inter_position, inter_size);
+
+ while (time_until_next_frame() < 5) {
+ get_next_frame (true);
+ add_dropped ();
+ }
+
+ boost::this_thread::interruption_point ();
+ dcpomatic_sleep_milliseconds (time_until_next_frame());
+ }
+
+ delete _context;
+}
+catch (boost::thread_interrupted& e)
+{
+ /* XXX_b: store exceptions here */
+ delete _context;
+ return;
+}
+
+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 ();
+}
+
+void
+GLVideoView::create ()
+{
+ if (!_thread) {
+ _thread = new boost::thread (boost::bind(&GLVideoView::thread, this));
+ }
+}