From 6e3e839e23309b1214ff8d9954e97e6194db61a8 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Tue, 18 Dec 2018 00:29:07 +0100 Subject: [PATCH] Make the number of cameras dynamic as the frames come in. --- futatabi/clip_list.cpp | 25 +++++++++++++----- futatabi/clip_list.h | 16 ++++++++--- futatabi/defs.h | 1 - futatabi/mainwindow.cpp | 51 +++++++++++++++++++++++++++++------- futatabi/mainwindow.h | 2 ++ shared/post_to_main_thread.h | 11 ++++++++ 6 files changed, 85 insertions(+), 21 deletions(-) diff --git a/futatabi/clip_list.cpp b/futatabi/clip_list.cpp index 0321727..0a3faf0 100644 --- a/futatabi/clip_list.cpp +++ b/futatabi/clip_list.cpp @@ -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); diff --git a/futatabi/clip_list.h b/futatabi/clip_list.h index d2c7f83..6fe3096 100644 --- a/futatabi/clip_list.h +++ b/futatabi/clip_list.h @@ -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 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 current_progress; + size_t num_cameras = 1; }; #endif // !defined (_CLIP_LIST_H) diff --git a/futatabi/defs.h b/futatabi/defs.h index f585231..045a97a 100644 --- a/futatabi/defs.h +++ b/futatabi/defs.h @@ -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 diff --git a/futatabi/mainwindow.cpp b/futatabi/mainwindow.cpp index 68b29e4..d4c63bd 100644 --- a/futatabi/mainwindow.cpp +++ b/futatabi/mainwindow.cpp @@ -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 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(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 ¤t, 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 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 diff --git a/futatabi/mainwindow.h b/futatabi/mainwindow.h index 97f0f29..8492eb3 100644 --- a/futatabi/mainwindow.h +++ b/futatabi/mainwindow.h @@ -43,6 +43,7 @@ private: QLabel *disk_free_label; std::unique_ptr 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 displays; + void change_num_cameras(); void cue_in_clicked(); void cue_out_clicked(); void queue_clicked(); diff --git a/shared/post_to_main_thread.h b/shared/post_to_main_thread.h index 0462c7b..080ed51 100644 --- a/shared/post_to_main_thread.h +++ b/shared/post_to_main_thread.h @@ -3,6 +3,7 @@ #include #include +#include #include // 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 +static inline void post_to_main_thread_and_wait(F &&fun) +{ + std::promise done_promise; + std::future 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) -- 2.39.2