From 3517cb889c4e1d348033bc6aeeeaa0189296827d Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 1 Oct 2018 22:02:25 +0200 Subject: [PATCH] Make for somewhat cleaner shutdown. --- jpeg_frame_view.cpp | 20 +++++++++++++++----- jpeg_frame_view.h | 5 +++++ main.cpp | 21 ++++++++++++++++----- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/jpeg_frame_view.cpp b/jpeg_frame_view.cpp index ef1cded..bc71d00 100644 --- a/jpeg_frame_view.cpp +++ b/jpeg_frame_view.cpp @@ -44,12 +44,14 @@ struct LRUFrame { size_t last_used; }; +thread JPEGFrameView::jpeg_decoder_thread; mutex cache_mu; map cache; // Under cache_mu. condition_variable any_pending_decodes, cache_updated; deque> pending_decodes; // Under cache_mu. atomic event_counter{0}; extern QGLWidget *global_share_widget; +extern atomic should_quit; shared_ptr decode_jpeg(const string &filename) { @@ -193,20 +195,21 @@ shared_ptr decode_jpeg_with_cache(JPEGID id, CacheMissBehavior cache_miss return frame; } -void jpeg_decoder_thread() +void jpeg_decoder_thread_func() { size_t num_decoded = 0, num_dropped = 0; pthread_setname_np(pthread_self(), "JPEGDecoder"); - for ( ;; ) { + while (!should_quit.load()) { JPEGID id; JPEGFrameView *dest; CacheMissBehavior cache_miss_behavior = DECODE_IF_NOT_IN_CACHE; { unique_lock lock(cache_mu); // TODO: Perhaps under another lock? any_pending_decodes.wait(lock, [] { - return !pending_decodes.empty(); + return !pending_decodes.empty() || should_quit.load(); }); + if (should_quit.load()) break; id = pending_decodes.front().first; dest = pending_decodes.front().second; pending_decodes.pop_front(); @@ -229,8 +232,9 @@ void jpeg_decoder_thread() // put directly into the cache from VideoStream. unique_lock lock(cache_mu); cache_updated.wait(lock, [id] { - return cache.count(id) != 0; + return cache.count(id) != 0 || should_quit.load(); }); + if (should_quit.load()) break; found_in_cache = true; // Don't count it as a decode. auto it = cache.find(id); @@ -267,6 +271,12 @@ void jpeg_decoder_thread() } } +void JPEGFrameView::shutdown() +{ + any_pending_decodes.notify_all(); + jpeg_decoder_thread.join(); +} + JPEGFrameView::JPEGFrameView(QWidget *parent) : QGLWidget(parent, global_share_widget) { } @@ -304,7 +314,7 @@ void JPEGFrameView::initializeGL() static once_flag once; call_once(once, [] { resource_pool = new ResourcePool; - std::thread(&jpeg_decoder_thread).detach(); + jpeg_decoder_thread = std::thread(jpeg_decoder_thread_func); }); ImageFormat inout_format; diff --git a/jpeg_frame_view.h b/jpeg_frame_view.h index 7c41b78..d0cd47c 100644 --- a/jpeg_frame_view.h +++ b/jpeg_frame_view.h @@ -11,6 +11,7 @@ #include #include +#include #include "jpeg_frame.h" @@ -44,6 +45,8 @@ public: void setDecodedFrame(std::shared_ptr frame); void set_overlay(const std::string &text); // Blank for none. + static void shutdown(); + signals: void clicked(); @@ -72,6 +75,8 @@ private: bool overlay_input_needs_refresh = false; int gl_width, gl_height; + + static std::thread jpeg_decoder_thread; }; #endif // !defined(_JPEG_FRAME_VIEW_H) diff --git a/main.cpp b/main.cpp index 42b1867..dc80bc2 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,8 @@ extern "C" { using namespace std; using namespace std::chrono; -std::mutex RefCountedGLsync::fence_lock; +mutex RefCountedGLsync::fence_lock; +atomic should_quit{false}; int64_t start_pts = -1; @@ -101,12 +103,18 @@ int main(int argc, char **argv) MainWindow mainWindow; mainWindow.show(); + init_jpeg_vaapi(); + load_existing_frames(); - thread(record_thread_func).detach(); + thread record_thread(record_thread_func); - init_jpeg_vaapi(); + int ret = app.exec(); - return app.exec(); + should_quit = true; + record_thread.join(); + JPEGFrameView::shutdown(); + + return ret; } void load_existing_frames() @@ -163,13 +171,16 @@ int record_thread_func() int64_t last_pts = -1; int64_t pts_offset; - for ( ;; ) { + while (!should_quit.load()) { AVPacket pkt; unique_ptr pkt_cleanup( &pkt, av_packet_unref); av_init_packet(&pkt); pkt.data = nullptr; pkt.size = 0; + + // TODO: Make it possible to abort av_read_frame() (use an interrupt callback); + // right now, should_quit will be ignored if it's hung on I/O. if (av_read_frame(format_ctx.get(), &pkt) != 0) { break; } -- 2.39.2