From 9e95c15a6754fa571199c36d73ac2dc1d45e5c64 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Fri, 21 Jul 2023 22:39:02 +0200 Subject: [PATCH] Propagate errors on video opening back to the UI. This is kludgy, but it will work for now. --- mainwindow.cpp | 5 ++++- video_widget.cpp | 33 +++++++++++++++++++++++---------- video_widget.h | 5 +++-- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 7eec2cb..5a1b745 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -42,7 +42,10 @@ MainWindow::MainWindow(EventsModel *events, PlayersModel *players, ui = new Ui::MainWindow; ui->setupUi(this); - ui->video->open("/home/sesse/dev/stats/ultimate-prores.mkv"); + if (!ui->video->open("/home/sesse/dev/stats/ultimate-prores.mkv")) { + // TODO: Pop up a dialog box here instead + fprintf(stderr, "WARNING: Video opening failed\n"); + } ui->video->play(); ui->event_view->setModel(events); diff --git a/video_widget.cpp b/video_widget.cpp index 7bafa64..e6708a9 100644 --- a/video_widget.cpp +++ b/video_widget.cpp @@ -594,30 +594,40 @@ void VideoWidget::fixup_zoom_matrix() zoom_matrix[7] = std::max(zoom_matrix[7], 1.0 - zoom_matrix[4]); // Top side (y=1). } -void VideoWidget::open(const string &filename) +bool VideoWidget::open(const string &filename) { stop(); internal_rewind(); pathname = filename; play(); + + while (running == STARTING) { + // Poor man's condition variable... + usleep(10000); + sched_yield(); + } + return (running != VIDEO_FILE_ERROR); } void VideoWidget::play() { - if (running) { + if (running != NOT_RUNNING && running != VIDEO_FILE_ERROR) { std::lock_guard lock(queue_mu); command_queue.push_back(QueuedCommand { QueuedCommand::RESUME }); producer_thread_should_quit.wakeup(); return; } - running = true; + running = STARTING; producer_thread_should_quit.unquit(); + if (producer_thread.joinable()) { + producer_thread.join(); + } producer_thread = std::thread(&VideoWidget::producer_thread_func, this); } void VideoWidget::pause() { - if (!running) { + if (running == NOT_RUNNING || running == VIDEO_FILE_ERROR) { return; } std::lock_guard lock(queue_mu); @@ -627,7 +637,7 @@ void VideoWidget::pause() void VideoWidget::seek(int64_t relative_seek_ms) { - if (!running) { + if (running == NOT_RUNNING || running == VIDEO_FILE_ERROR) { return; } std::lock_guard lock(queue_mu); @@ -637,7 +647,7 @@ void VideoWidget::seek(int64_t relative_seek_ms) void VideoWidget::seek_frames(int64_t relative_seek_frames) { - if (!running) { + if (running == NOT_RUNNING || running == VIDEO_FILE_ERROR) { return; } std::lock_guard lock(queue_mu); @@ -647,7 +657,7 @@ void VideoWidget::seek_frames(int64_t relative_seek_frames) void VideoWidget::seek_absolute(int64_t position_ms) { - if (!running) { + if (running == NOT_RUNNING || running == VIDEO_FILE_ERROR) { return; } std::lock_guard lock(queue_mu); @@ -657,10 +667,9 @@ void VideoWidget::seek_absolute(int64_t position_ms) void VideoWidget::stop() { - if (!running) { + if (running == NOT_RUNNING || running == VIDEO_FILE_ERROR) { return; } - running = false; producer_thread_should_quit.quit(); producer_thread.join(); } @@ -669,7 +678,9 @@ void VideoWidget::producer_thread_func() { if (!producer_thread_should_quit.should_quit()) { if (!play_video(pathname)) { - // TODO: Send the error back to the UI somehow. + running = VIDEO_FILE_ERROR; + } else { + running = NOT_RUNNING; } } } @@ -875,6 +886,8 @@ bool VideoWidget::play_video(const string &pathname) internal_rewind(); + running = RUNNING; + // Main loop. int consecutive_errors = 0; double rate = 1.0; diff --git a/video_widget.h b/video_widget.h index dc7d0ab..c0f28d7 100644 --- a/video_widget.h +++ b/video_widget.h @@ -25,7 +25,7 @@ public: VideoWidget(QWidget *parent); ~VideoWidget() { stop(); } - void open(const std::string &filename); + bool open(const std::string &filename); // False on error. void play(); uint64_t get_position() const { return last_position; } // In milliseconds. void pause(); @@ -77,7 +77,8 @@ private: int64_t pts_origin; int64_t last_pts; std::atomic last_position{0}; // TODO: sort of redundant wrt. last_pts (but this one can be read from the other thread) - std::atomic running{false}; + enum RunState { NOT_RUNNING, STARTING, RUNNING, VIDEO_FILE_ERROR }; // NOT_RUNNING and VIDEO_FILE_ERROR both imply that the thread isn't running and can freely be restarted. + std::atomic running{NOT_RUNNING}; bool paused = false; std::chrono::steady_clock::time_point start, next_frame_start, last_frame; -- 2.39.2