]> git.sesse.net Git - nageru/commitdiff
Fix some Futatabi shutdown problems.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 12 Dec 2018 21:30:51 +0000 (22:30 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 12 Dec 2018 21:30:51 +0000 (22:30 +0100)
futatabi/mainwindow.cpp
futatabi/mainwindow.h
futatabi/player.cpp
futatabi/player.h
futatabi/video_stream.cpp
futatabi/video_stream.h

index 1412e251fcf3b75ea771d65e7a59625a81ba1a3d..24a9d5b7e4e7db58e1d6bc894e060ba23548e485 100644 (file)
@@ -126,8 +126,8 @@ MainWindow::MainWindow()
                this, &MainWindow::playlist_selection_changed);
        playlist_selection_changed();  // First time set-up.
 
-       preview_player = new Player(ui->preview_display, /*also_output_to_stream=*/false);
-       live_player = new Player(ui->live_display, /*also_output_to_stream=*/true);
+       preview_player.reset(new Player(ui->preview_display, /*also_output_to_stream=*/false));
+       live_player.reset(new Player(ui->live_display, /*also_output_to_stream=*/true));
        live_player->set_done_callback([this]{
                post_to_main_thread([this]{
                        live_player_clip_done();
@@ -149,6 +149,11 @@ MainWindow::MainWindow()
                this, &MainWindow::clip_list_selection_changed);
 }
 
+MainWindow::~MainWindow()
+{
+       // Empty so that we can forward-declare Player in the .h file.
+}
+
 void MainWindow::cue_in_clicked()
 {
        if (!cliplist_clips->empty() && cliplist_clips->back()->pts_out < 0) {
@@ -374,6 +379,9 @@ pair<Clip, size_t> MainWindow::live_player_get_next_clip()
 {
        // playlist_clips can only be accessed on the main thread.
        // Hopefully, we won't have to wait too long for this to come back.
+       //
+       // TODO: If MainWindow is in the process of being destroyed and waiting
+       // for Player to shut down, we could have a deadlock here.
        promise<pair<Clip, size_t>> clip_promise;
        future<pair<Clip, size_t>> clip = clip_promise.get_future();
        post_to_main_thread([this, &clip_promise] {
index 4e3800ef083ae6df812719735f9a219ddcac9fdb..57814b3e55eab51e3afd68f2fd7bd1e738a33c4c 100644 (file)
@@ -5,6 +5,7 @@
 #include "db.h"
 #include "state.pb.h"
 
+#include <memory>
 #include <mutex>
 #include <QLabel>
 #include <QMainWindow>
@@ -24,6 +25,7 @@ class MainWindow : public QMainWindow {
 
 public:
        MainWindow();
+       ~MainWindow();
 
        // HTTP callback. TODO: Does perhaps not belong to MainWindow?
        std::pair<std::string, std::string> get_queue_status() const;
@@ -33,7 +35,7 @@ public:
 
 private:
        QLabel *disk_free_label;
-       Player *preview_player, *live_player;
+       std::unique_ptr<Player> preview_player, live_player;
        DB db;
 
        // State when doing a scrub operation on a timestamp with the mouse.
index d926974712e22a9da0a7346a7ee8ac218b012418..e20a43cc26bf84fe920ac9dff3b2892d96ee6644 100644 (file)
@@ -54,7 +54,7 @@ void Player::thread_func(bool also_output_to_stream)
        bool got_next_clip = false;
        double next_clip_fade_time = -1.0;
 
-       for ( ;; ) {
+       while (!should_quit) {
 wait_for_clip:
                bool clip_ready;
                steady_clock::time_point before_sleep = steady_clock::now();
@@ -63,8 +63,11 @@ wait_for_clip:
                {
                        unique_lock<mutex> lock(queue_state_mu);
                        clip_ready = new_clip_changed.wait_for(lock, milliseconds(100), [this] {
-                               return new_clip_ready && current_clip.pts_in != -1;
+                               return should_quit || (new_clip_ready && current_clip.pts_in != -1);
                        });
+                       if (should_quit) {
+                               return;
+                       }
                        new_clip_ready = false;
                        playing = true;
                }
@@ -114,7 +117,7 @@ got_clip:
 
                int64_t in_pts_start_next_clip = -1;
                steady_clock::time_point next_frame_start;
-               for (int frameno = 0; ; ++frameno) {  // Ends when the clip ends.
+               for (int frameno = 0; !should_quit; ++frameno) {  // Ends when the clip ends.
                        double out_pts = out_pts_origin + TIMEBASE * frameno / output_framerate;
                        next_frame_start =
                                origin + microseconds(lrint((out_pts - out_pts_origin) * 1e6 / TIMEBASE));
@@ -198,8 +201,11 @@ got_clip:
                                if (video_stream == nullptr) {
                                        // No queue, just wait until the right time and then show the frame.
                                        new_clip_changed.wait_until(lock, next_frame_start, [this]{
-                                               return new_clip_ready || override_stream_idx != -1;
+                                               return should_quit || new_clip_ready || override_stream_idx != -1;
                                        });
+                               if (should_quit) {
+                                       return;
+                               }
                                } else {
                                        // If the queue is full (which is really the state we'd like to be in),
                                        // wait until there's room for one more frame (ie., one was output from
@@ -211,9 +217,12 @@ got_clip:
                                                if (num_queued_frames < max_queued_frames) {
                                                        return true;
                                                }
-                                               return new_clip_ready || override_stream_idx != -1;
+                                               return should_quit || new_clip_ready || override_stream_idx != -1;
                                        });
                                }
+                               if (should_quit) {
+                                       return;
+                               }
                                if (new_clip_ready) {
                                        if (video_stream != nullptr) {
                                                lock.unlock();  // Urg.
@@ -307,6 +316,10 @@ got_clip:
                        }
                }
 
+               if (should_quit) {
+                       return;
+               }
+
                // The clip ended.
 
                // Last-ditch effort to get the next clip (if e.g. the fade time was zero seconds).
@@ -373,7 +386,17 @@ bool Player::find_surrounding_frames(int64_t pts, int stream_idx, FrameOnDisk *f
 Player::Player(JPEGFrameView *destination, bool also_output_to_stream)
        : destination(destination)
 {
-       thread(&Player::thread_func, this, also_output_to_stream).detach();
+       player_thread = thread(&Player::thread_func, this, also_output_to_stream);
+}
+
+Player::~Player()
+{
+       should_quit = true;
+       if (video_stream != nullptr) {
+               video_stream->stop();
+       }
+       new_clip_changed.notify_all();
+       player_thread.join();
 }
 
 void Player::play_clip(const Clip &clip, size_t clip_idx, unsigned stream_idx)
index c7f8e070a71a0ce697e1c21edd14727a3892762f..b57bada647f2cb48439265cf8e534a6179c4d8f3 100644 (file)
@@ -12,6 +12,7 @@ extern "C" {
 #include <condition_variable>
 #include <functional>
 #include <mutex>
+#include <thread>
 
 class JPEGFrameView;
 class VideoStream;
@@ -21,6 +22,7 @@ class QSurfaceFormat;
 class Player : public QueueInterface {
 public:
        Player(JPEGFrameView *destination, bool also_output_to_stream);
+       ~Player();
 
        void play_clip(const Clip &clip, size_t clip_idx, unsigned stream_idx);
        void override_angle(unsigned stream_idx);  // For the current clip only.
@@ -55,6 +57,9 @@ private:
        // Returns false if pts is after the last frame.
        bool find_surrounding_frames(int64_t pts, int stream_idx, FrameOnDisk *frame_lower, FrameOnDisk *frame_upper);
 
+       std::thread player_thread;
+       std::atomic<bool> should_quit{false};
+
        JPEGFrameView *destination;
        done_callback_func done_callback;
        next_clip_callback_func next_clip_callback;
index c4cf52e0779a9093709174466afdf79bc8df5c74..6749f0d8df7887a0eeb9cc57b8c50fa0289d41a4 100644 (file)
@@ -269,6 +269,8 @@ void VideoStream::start()
 
 void VideoStream::stop()
 {
+       should_quit = true;
+       clear_queue();
        encode_thread.join();
 }
 
@@ -552,7 +554,7 @@ void VideoStream::encode_thread_func()
                exit(1);
        }
 
-       for ( ;; ) {
+       while (!should_quit) {
                QueuedFrame qf;
                {
                        unique_lock<mutex> lock(queue_lock);
index d0634e0d7b563c84d9c12b1471376009afb8003e..d4cb18eb7e91e758fc53d6e99adb90e7a2741e45 100644 (file)
@@ -13,6 +13,7 @@ extern "C" {
 #include "shared/ref_counted_gl_sync.h"
 #include "queue_spot_holder.h"
 
+#include <atomic>
 #include <chrono>
 #include <condition_variable>
 #include <deque>
@@ -66,6 +67,7 @@ private:
 
        void encode_thread_func();
        std::thread encode_thread;
+       std::atomic<bool> should_quit{false};
 
        static int write_packet2_thunk(void *opaque, uint8_t *buf, int buf_size, AVIODataMarkerType type, int64_t time);
        int write_packet2(uint8_t *buf, int buf_size, AVIODataMarkerType type, int64_t time);