]> git.sesse.net Git - nageru/commitdiff
Make the number of cameras dynamic as the frames come in.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 17 Dec 2018 23:29:07 +0000 (00:29 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 17 Dec 2018 23:29:07 +0000 (00:29 +0100)
futatabi/clip_list.cpp
futatabi/clip_list.h
futatabi/defs.h
futatabi/mainwindow.cpp
futatabi/mainwindow.h
shared/post_to_main_thread.h

index 0321727cef00888b4ce96bc719b5ed016d6da897..0a3faf01a09e517e4ac49db957ed9b96fac97c2f 100644 (file)
@@ -58,7 +58,7 @@ int ClipList::columnCount(const QModelIndex &parent) const
 {
        if (parent.isValid())
                return 0;
-       return int(Column::NUM_NON_CAMERA_COLUMNS) + NUM_CAMERAS;
+       return int(Column::NUM_NON_CAMERA_COLUMNS) + num_cameras;
 }
 
 int PlayList::columnCount(const QModelIndex &parent) const
@@ -212,7 +212,7 @@ QVariant ClipList::headerData(int section, Qt::Orientation orientation, int role
        case Column::DURATION:
                return "Duration";
        default:
-               if (section >= int(Column::CAMERA_1) && section < int(Column::CAMERA_1) + NUM_CAMERAS) {
+               if (section >= int(Column::CAMERA_1) && section < int(Column::CAMERA_1) + num_cameras) {
                        return QString::fromStdString("Camera " + to_string(section - int(Column::CAMERA_1) + 1));
                } else {
                        return "";
@@ -319,7 +319,7 @@ bool PlayList::setData(const QModelIndex &index, const QVariant &value, int role
        case Column::CAMERA: {
                bool ok;
                int camera_idx = value.toInt(&ok);
-               if (!ok || camera_idx < 1 || camera_idx > NUM_CAMERAS) {
+               if (!ok || camera_idx < 1 || camera_idx > num_cameras) {
                        return false;
                }
                clips[row].stream_idx = camera_idx - 1;
@@ -390,7 +390,7 @@ void PlayList::move_clips(size_t first, size_t last, int delta)
 
 void ClipList::emit_data_changed(size_t row)
 {
-       emit dataChanged(index(row, 0), index(row, int(Column::NUM_NON_CAMERA_COLUMNS) + NUM_CAMERAS));
+       emit dataChanged(index(row, 0), index(row, int(Column::NUM_NON_CAMERA_COLUMNS) + num_cameras));
        emit any_content_changed();
 }
 
@@ -400,6 +400,19 @@ void PlayList::emit_data_changed(size_t row)
        emit any_content_changed();
 }
 
+void ClipList::change_num_cameras(size_t num_cameras)
+{
+       assert(num_cameras >= this->num_cameras);
+       if (num_cameras == this->num_cameras) {
+               return;
+       }
+
+       beginInsertColumns(QModelIndex(), int(Column::NUM_NON_CAMERA_COLUMNS) + this->num_cameras, int(Column::NUM_NON_CAMERA_COLUMNS) + num_cameras - 1);
+       this->num_cameras = num_cameras;
+       endInsertColumns();
+       emit any_content_changed();
+}
+
 void PlayList::set_currently_playing(int index, double progress)
 {
        int old_index = currently_playing_index;
@@ -444,7 +457,7 @@ Clip deserialize_clip(const ClipProto &clip_proto)
        Clip clip;
        clip.pts_in = clip_proto.pts_in();
        clip.pts_out = clip_proto.pts_out();
-       for (int camera_idx = 0; camera_idx < min(clip_proto.description_size(), NUM_CAMERAS); ++camera_idx) {
+       for (int camera_idx = 0; camera_idx < min(clip_proto.description_size(), MAX_STREAMS); ++camera_idx) {
                clip.descriptions[camera_idx] = clip_proto.description(camera_idx);
        }
        clip.stream_idx = clip_proto.stream_idx();
@@ -456,7 +469,7 @@ void serialize_clip(const Clip &clip, ClipProto *clip_proto)
 {
        clip_proto->set_pts_in(clip.pts_in);
        clip_proto->set_pts_out(clip.pts_out);
-       for (int camera_idx = 0; camera_idx < NUM_CAMERAS; ++camera_idx) {
+       for (int camera_idx = 0; camera_idx < MAX_STREAMS; ++camera_idx) {
                *clip_proto->add_description() = clip.descriptions[camera_idx];
        }
        clip_proto->set_stream_idx(clip.stream_idx);
index d2c7f83cd66795b3a2172a4b4c79e01f7826cd1d..6fe30966a5e01a89ed68551d6b5c0119d0534299 100644 (file)
@@ -12,7 +12,7 @@
 
 struct Clip {
        int64_t pts_in = -1, pts_out = -1;  // pts_in is inclusive, pts_out is exclusive.
-       std::string descriptions[NUM_CAMERAS];
+       std::string descriptions[MAX_STREAMS];
        unsigned stream_idx = 0;  // For the playlist only.
        double fade_time_seconds = 0.5;  // For the playlist only.
 };
@@ -47,7 +47,7 @@ class ClipList : public QAbstractTableModel, public DataChangedReceiver {
        Q_OBJECT
 
 public:
-       explicit ClipList(const ClipListProto &serialized);
+       ClipList(const ClipListProto &serialized);
 
        enum class Column {
                IN,
@@ -76,11 +76,12 @@ public:
 
        ClipListProto serialize() const;
 
+       void change_num_cameras(size_t num_cameras);  // Defaults to 1. Cannot decrease.
        void emit_data_changed(size_t row) override;
 
-       static bool is_camera_column(int column)
+       bool is_camera_column(int column) const
        {
-               return (column >= int(Column::CAMERA_1) && column < int(Column::CAMERA_1) + NUM_CAMERAS);
+               return (column >= int(Column::CAMERA_1) && column < int(Column::CAMERA_1) + num_cameras);
        }
 
 signals:
@@ -88,6 +89,7 @@ signals:
 
 private:
        std::vector<Clip> clips;
+       size_t num_cameras = 1;
 };
 
 class PlayList : public QAbstractTableModel, public DataChangedReceiver {
@@ -139,6 +141,11 @@ public:
 
        ClipListProto serialize() const;
 
+       void change_num_cameras(size_t num_cameras)  // Defaults to 1. Cannot decrease.
+       {
+               this->num_cameras = num_cameras;
+       }
+
        void emit_data_changed(size_t row) override;
 
 signals:
@@ -149,6 +156,7 @@ private:
        int currently_playing_index = -1;
        double play_progress = 0.0;
        std::map<size_t, double> current_progress;
+       size_t num_cameras = 1;
 };
 
 #endif  // !defined (_CLIP_LIST_H)
index f5852310cd52d26311bdf77cb15670d4a7b1b9a0..045a97a22d9d50d412e4487a363575c0f88149f8 100644 (file)
@@ -3,7 +3,6 @@
 
 #define MAX_STREAMS 16
 #define CACHE_SIZE_MB 2048
-#define NUM_CAMERAS 4
 #define MUX_BUFFER_SIZE 10485760
 
 #define DEFAULT_HTTPD_PORT 9096
index 68b29e4be57127b97b3bd058f335ebbfecdf9d11..d4c63bd6cfa7e4d755fbb7496f0da4d0dd558b01 100644 (file)
@@ -179,10 +179,28 @@ MainWindow::MainWindow()
        connect(ui->clip_list->selectionModel(), &QItemSelectionModel::currentChanged,
                this, &MainWindow::clip_list_selection_changed);
 
-       // Make the display rows.
-       unsigned display_rows = (NUM_CAMERAS + 1) / 2;
+       // Find out how many cameras we have in the existing frames;
+       // if none, we start with a single camera.
+       num_cameras = 1;
+       {
+               lock_guard<mutex> lock(frame_mu);
+               for (size_t stream_idx = 1; stream_idx < MAX_STREAMS; ++stream_idx) {
+                       if (!frames[stream_idx].empty()) {
+                               num_cameras = stream_idx + 1;
+                       }
+               }
+       }
+       change_num_cameras();
+}
+
+void MainWindow::change_num_cameras()
+{
+       assert(num_cameras >= displays.size());  // We only add, never remove.
+
+       // Make new display rows.
+       unsigned display_rows = (num_cameras + 1) / 2;
        ui->video_displays->setStretch(1, display_rows);
-       for (unsigned i = 0; i < NUM_CAMERAS; ++i) {
+       for (unsigned i = displays.size(); i < num_cameras; ++i) {
                QFrame *frame = new QFrame(this);
                frame->setAutoFillBackground(true);
 
@@ -210,6 +228,11 @@ MainWindow::MainWindow()
 
                connect(preview_btn, &QPushButton::clicked, [this, i]{ preview_angle_clicked(i); });
        }
+
+       cliplist_clips->change_num_cameras(num_cameras);
+       playlist_clips->change_num_cameras(num_cameras);
+
+       QMetaObject::invokeMethod(this, "relayout", Qt::QueuedConnection);
 }
 
 MainWindow::~MainWindow()
@@ -258,7 +281,7 @@ void MainWindow::queue_clicked()
 
        QModelIndex index = selected->currentIndex();
        Clip clip = *cliplist_clips->clip(index.row());
-       if (ClipList::is_camera_column(index.column())) {
+       if (cliplist_clips->is_camera_column(index.column())) {
                clip.stream_idx = index.column() - int(ClipList::Column::CAMERA_1);
        } else {
                clip.stream_idx = ui->preview_display->get_stream_idx();
@@ -300,7 +323,7 @@ void MainWindow::preview_clicked()
 
        QModelIndex index = selected->currentIndex();
        unsigned stream_idx;
-       if (ClipList::is_camera_column(index.column())) {
+       if (cliplist_clips->is_camera_column(index.column())) {
                stream_idx = index.column() - int(ClipList::Column::CAMERA_1);
        } else {
                stream_idx = ui->preview_display->get_stream_idx();
@@ -734,7 +757,7 @@ bool MainWindow::eventFilter(QObject *watched, QEvent *event)
 
                                int stream_idx = clip->stream_idx + angle_degrees / camera_degrees_per_pixel;
                                stream_idx = std::max(stream_idx, 0);
-                               stream_idx = std::min(stream_idx, NUM_CAMERAS - 1);
+                               stream_idx = std::min<int>(stream_idx, num_cameras - 1);
                                clip->stream_idx = stream_idx;
 
                                last_mousewheel_camera_row = row;
@@ -805,7 +828,7 @@ void MainWindow::playlist_selection_changed()
 void MainWindow::clip_list_selection_changed(const QModelIndex &current, const QModelIndex &)
 {
        int camera_selected = -1;
-       if (ClipList::is_camera_column(current.column())) {
+       if (cliplist_clips->is_camera_column(current.column())) {
                camera_selected = current.column() - int(ClipList::Column::CAMERA_1);
        }
        highlight_camera_input(camera_selected);
@@ -983,7 +1006,7 @@ void MainWindow::quality_toggled(int quality, bool checked)
 
 void MainWindow::highlight_camera_input(int stream_idx)
 {
-       for (unsigned i = 0; i < NUM_CAMERAS; ++i) {
+       for (unsigned i = 0; i < num_cameras; ++i) {
                if (stream_idx == i) {
                        displays[i].frame->setStyleSheet("background: rgb(0,255,0)");
                } else {
@@ -1007,9 +1030,17 @@ pair<string, string> MainWindow::get_queue_status() const {
 
 void MainWindow::display_frame(unsigned stream_idx, const FrameOnDisk &frame)
 {
-       if (stream_idx < NUM_CAMERAS) {
-               displays[stream_idx].display->setFrame(stream_idx, frame);
+       if (stream_idx >= MAX_STREAMS) {
+               fprintf(stderr, "WARNING: Ignoring too-high stream index %u.\n", stream_idx);
+               return;
+       }
+       if (stream_idx >= num_cameras) {
+               post_to_main_thread_and_wait([this, stream_idx]{
+                       num_cameras = stream_idx + 1;
+                       change_num_cameras();
+               });
        }
+       displays[stream_idx].display->setFrame(stream_idx, frame);
 }
 
 template <class Model>
index 97f0f296aef8638372635b13b14d0be019e2a923..8492eb34d09fed7172504e0aaf5c17058830f5d3 100644 (file)
@@ -43,6 +43,7 @@ private:
        QLabel *disk_free_label;
        std::unique_ptr<Player> preview_player, live_player;
        DB db;
+       unsigned num_cameras;
 
        // State when doing a scrub operation on a timestamp with the mouse.
        bool scrubbing = false;
@@ -87,6 +88,7 @@ private:
        };
        std::vector<FrameAndDisplay> displays;
 
+       void change_num_cameras();
        void cue_in_clicked();
        void cue_out_clicked();
        void queue_clicked();
index 0462c7b67ab55f232f4de9c556e51033c38541ca..080ed51e5f4fc2279d9a1f60d73192e592167e3c 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <QApplication>
 #include <QObject>
+#include <future>
 #include <memory>
 
 // http://stackoverflow.com/questions/21646467/how-to-execute-a-functor-in-a-given-thread-in-qt-gcd-style
@@ -13,4 +14,14 @@ static inline void post_to_main_thread(F &&fun)
        QObject::connect(&signalSource, &QObject::destroyed, qApp, std::move(fun));
 }
 
+template<typename F>
+static inline void post_to_main_thread_and_wait(F &&fun)
+{
+       std::promise<void> done_promise;
+       std::future<void> done = done_promise.get_future();
+       post_to_main_thread(std::move(fun));
+       post_to_main_thread([&done_promise] { done_promise.set_value(); });
+       done.wait();
+}
+
 #endif  // !defined(_POST_TO_MAIN_THREAD_H)