]> git.sesse.net Git - nageru/commitdiff
Some refactoring of the player code, and begin working on the playlist.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Tue, 12 Jun 2018 22:24:32 +0000 (00:24 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Tue, 12 Jun 2018 22:34:23 +0000 (00:34 +0200)
clip_list.cpp
clip_list.h
main.cpp
mainwindow.cpp
mainwindow.h
player.cpp
player.h
ui_mainwindow.ui

index 2599dcc2d246cbbeceabd00ba48d940192cd1b0f..0d9fc6c66e49c97a35b30e4b44be25ceac667467 100644 (file)
@@ -15,7 +15,11 @@ int ClipList::rowCount(const QModelIndex &parent) const {
 
 int ClipList::columnCount(const QModelIndex &parent) const {
        if (parent.isValid()) return 0;
-       return Column::NUM_COLUMNS;
+       if (display_type == ListDisplay::CLIP_LIST) {
+               return int(ClipListColumn::NUM_COLUMNS);
+       } else {
+               return int(PlayListColumn::NUM_COLUMNS);
+       }
 }
 
 QVariant ClipList::data(const QModelIndex &parent, int role) const {
@@ -54,23 +58,40 @@ QVariant ClipList::headerData(int section, Qt::Orientation orientation, int role
        if (orientation != Qt::Horizontal)
                return QVariant();
 
-       switch (section) {
-       case Column::IN:
-               return "In";
-       case Column::OUT:
-               return "Out";
-       case Column::DURATION:
-               return "Duration";
-       case Column::CAMERA_1:
-               return "Camera 1";
-       case Column::CAMERA_2:
-               return "Camera 2";
-       case Column::CAMERA_3:
-               return "Camera 3";
-       case Column::CAMERA_4:
-               return "Camera 4";
-       default:
-               return "";
+       if (display_type == ListDisplay::CLIP_LIST) {
+               switch (ClipListColumn(section)) {
+               case ClipListColumn::IN:
+                       return "In";
+               case ClipListColumn::OUT:
+                       return "Out";
+               case ClipListColumn::DURATION:
+                       return "Duration";
+               case ClipListColumn::CAMERA_1:
+                       return "Camera 1";
+               case ClipListColumn::CAMERA_2:
+                       return "Camera 2";
+               case ClipListColumn::CAMERA_3:
+                       return "Camera 3";
+               case ClipListColumn::CAMERA_4:
+                       return "Camera 4";
+               default:
+                       return "";
+               }
+       } else {
+               switch (PlayListColumn(section)) {
+               case PlayListColumn::IN:
+                       return "In";
+               case PlayListColumn::OUT:
+                       return "Out";
+               case PlayListColumn::DURATION:
+                       return "Duration";
+               case PlayListColumn::CAMERA:
+                       return "Camera";
+               case PlayListColumn::DESCRIPTION:
+                       return "Description";
+               default:
+                       return "";
+               }
        }
 }
 
@@ -83,5 +104,9 @@ void ClipList::add_clip(const Clip &clip)
 
 void ClipList::emit_data_changed(size_t row)
 {
-       emit dataChanged(index(row, 0), index(row, 6));
+       if (display_type == ListDisplay::CLIP_LIST) {
+               emit dataChanged(index(row, 0), index(row, int(ClipListColumn::NUM_COLUMNS)));
+       } else {
+               emit dataChanged(index(row, 0), index(row, int(PlayListColumn::NUM_COLUMNS)));
+       }
 }
index 1cdbf355d7db0410883eb32137d519d9db8d8cfb..ddb4a4a7d862f24e2f91c91a7d078e225675df3e 100644 (file)
 struct Clip {
        int64_t pts_in = -1, pts_out = -1;
        std::vector<std::string> descriptions;  // One per camera.
+       unsigned stream_idx = 0;  // For the playlist only.
 };
 
+// FIXME: This should be split into a separate clip list and play list model.
 class ClipList : public QAbstractTableModel {
        Q_OBJECT
 
 public:
-       enum Column {
+       enum class ListDisplay {
+               CLIP_LIST,
+               PLAY_LIST
+       };
+       ClipList(ListDisplay display_type) : display_type(display_type) {}
+
+       enum class ClipListColumn {
                IN,
                OUT,
                DURATION,
@@ -27,6 +35,14 @@ public:
                CAMERA_4,
                NUM_COLUMNS
        };
+       enum class PlayListColumn {
+               IN,
+               OUT,
+               DURATION,
+               CAMERA,
+               DESCRIPTION,
+               NUM_COLUMNS
+       };
 
        int rowCount(const QModelIndex &parent) const override;
        int columnCount(const QModelIndex &parent) const override;
@@ -66,6 +82,7 @@ public:
 
 private:
        std::vector<Clip> clips;
+       ListDisplay display_type;
 };
 
 #endif  // !defined (_CLIP_LIST_H)
index a0e93420688acebd0e742d2632b10b4a6cd2733b..52c105f76b492875919e1c2575b8f9eeb43891dc 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -52,7 +52,6 @@ int main(int argc, char **argv)
        mainWindow.show();
 
        thread(record_thread_func).detach();
-       start_player_thread();
 
        return app.exec();
 }
index 1a5953d2ded019cbdf23f3484083834ba4a75b6e..aba38db9f734a202f87a9aa6aa4f2c280ee81916 100644 (file)
@@ -13,7 +13,7 @@ using namespace std;
 
 MainWindow *global_mainwindow = nullptr;
 extern int64_t current_pts;
-ClipList *clips;
+ClipList *cliplist_clips, *playlist_clips;
 
 MainWindow::MainWindow()
        : ui(new Ui::MainWindow)
@@ -21,46 +21,89 @@ MainWindow::MainWindow()
        global_mainwindow = this;
        ui->setupUi(this);
 
-       clips = new ClipList;
-       ui->clip_list->setModel(clips);
+       cliplist_clips = new ClipList(ClipList::ListDisplay::CLIP_LIST);
+       ui->clip_list->setModel(cliplist_clips);
+
+       playlist_clips = new ClipList(ClipList::ListDisplay::PLAY_LIST);
+       ui->playlist->setModel(playlist_clips);
 
-       // TODO: Make these into buttons.
        // TODO: These are too big for lambdas.
        QShortcut *cue_in = new QShortcut(QKeySequence(Qt::Key_A), this);
-       connect(cue_in, &QShortcut::activated, []{
-               if (!clips->empty() && clips->back()->pts_out < 0) {
-                       clips->back()->pts_in = current_pts;
+       connect(cue_in, &QShortcut::activated, ui->cue_in_btn, &QPushButton::click);
+       connect(ui->cue_in_btn, &QPushButton::clicked, []{
+               if (!cliplist_clips->empty() && cliplist_clips->back()->pts_out < 0) {
+                       cliplist_clips->back()->pts_in = current_pts;
                        return;
                }
                Clip clip;
                clip.pts_in = current_pts;
-               clips->add_clip(clip);
+               cliplist_clips->add_clip(clip);
        });
 
        QShortcut *cue_out = new QShortcut(QKeySequence(Qt::Key_S), this);
-       connect(cue_out, &QShortcut::activated, []{
-               if (!clips->empty()) {
-                       clips->back()->pts_out = current_pts;
+       connect(cue_out, &QShortcut::activated, ui->cue_out_btn, &QPushButton::click);
+       connect(ui->cue_out_btn, &QPushButton::clicked, []{
+               if (!cliplist_clips->empty()) {
+                       cliplist_clips->back()->pts_out = current_pts;
                        // TODO: select the row in the clip list?
                }
        });
 
-       QShortcut *preview_shortcut = new QShortcut(QKeySequence(Qt::Key_W), this);
-       connect(preview_shortcut, &QShortcut::activated, this, &MainWindow::preview_clicked);
+       QShortcut *queue = new QShortcut(QKeySequence(Qt::Key_Q), this);
+       connect(queue, &QShortcut::activated, ui->queue_btn, &QPushButton::click);
+       connect(ui->queue_btn, &QPushButton::clicked, this, &MainWindow::queue_clicked);
+
+       QShortcut *preview = new QShortcut(QKeySequence(Qt::Key_W), this);
+       connect(preview, &QShortcut::activated, ui->preview_btn, &QPushButton::click);
+       connect(ui->preview_btn, &QPushButton::clicked, this, &MainWindow::preview_clicked);
+
+       QShortcut *play = new QShortcut(QKeySequence(Qt::Key_Space), this);
+       connect(play, &QShortcut::activated, ui->play_btn, &QPushButton::click);
+       connect(ui->play_btn, &QPushButton::clicked, this, &MainWindow::play_clicked);
+
+       preview_player = new Player(ui->preview_display);
+}
+
+void MainWindow::queue_clicked()
+{
+       QItemSelectionModel *selected = ui->clip_list->selectionModel();
+       if (!selected->hasSelection()) {
+               Clip clip = *cliplist_clips->back();
+               clip.stream_idx = 0;
+               playlist_clips->add_clip(clip);
+               return;
+       }
+
+       QModelIndex index = selected->currentIndex();
+       if (index.column() >= int(ClipList::ClipListColumn::CAMERA_1) &&
+           index.column() <= int(ClipList::ClipListColumn::CAMERA_4)) {
+               Clip clip = *cliplist_clips->clip(index.row());
+               clip.stream_idx = index.column() - int(ClipList::ClipListColumn::CAMERA_1);
+               playlist_clips->add_clip(clip);
+       }
 }
 
 void MainWindow::preview_clicked()
 {
        QItemSelectionModel *selected = ui->clip_list->selectionModel();
        if (!selected->hasSelection()) {
-               play_clip(*clips->back(), 0);
+               preview_player->play_clip(*cliplist_clips->back(), 0);
                return;
        }
 
        QModelIndex index = selected->currentIndex();
-       if (index.column() >= ClipList::Column::CAMERA_1 &&
-           index.column() <= ClipList::Column::CAMERA_4) {
-               unsigned stream_idx = index.column() - ClipList::Column::CAMERA_1;
-               play_clip(*clips->clip(index.row()), stream_idx);
+       if (index.column() >= int(ClipList::ClipListColumn::CAMERA_1) &&
+           index.column() <= int(ClipList::ClipListColumn::CAMERA_4)) {
+               unsigned stream_idx = index.column() - int(ClipList::ClipListColumn::CAMERA_1);
+               preview_player->play_clip(*cliplist_clips->clip(index.row()), stream_idx);
+       }
+}
+
+void MainWindow::play_clicked()
+{
+       QItemSelectionModel *selected = ui->playlist->selectionModel();
+       if (!selected->hasSelection()) {
+               ui->playlist->selectRow(0);
+               return;
        }
 }
index a1254b1fdd915828fa0bd4bd016f73eb72c54a19..583f0e2ebe28033ae659d7d12c756bbec9b8dc83 100644 (file)
@@ -9,6 +9,8 @@ namespace Ui {
 class MainWindow;
 }  // namespace Ui
 
+class Player;
+
 class MainWindow : public QMainWindow
 {
        Q_OBJECT
@@ -20,7 +22,11 @@ public:
        Ui::MainWindow *ui;
 
 private:
+       Player *preview_player;
+
+       void queue_clicked();
        void preview_clicked();
+       void play_clicked();
 };
 
 extern MainWindow *global_mainwindow;
index a2c1a9371edcc8a465ddea7e1884b370e1890ef3..6e45f39cd3fc5cc2aaa6ac5b4770ef09b5ac5085 100644 (file)
@@ -7,10 +7,8 @@
 
 #include "clip_list.h"
 #include "defs.h"
-#include "mainwindow.h"
-#include "ffmpeg_raii.h"
-#include "post_to_main_thread.h"
-#include "ui_mainwindow.h"
+#include "jpeg_frame_view.h"
+#include "player.h"
 
 using namespace std;
 using namespace std::chrono;
@@ -18,38 +16,27 @@ using namespace std::chrono;
 extern mutex frame_mu;
 extern vector<int64_t> frames[MAX_STREAMS];
 
-struct PlaylistClip {
-       Clip clip;
-       unsigned stream_idx;
-};
-vector<PlaylistClip> current_cue_playlist;
-mutex playlist_mu;
-
-enum { PAUSED, PLAYING } cue_state = PAUSED;
-mutex cue_state_mu;
-condition_variable cue_is_playing;
-//int cue_playlist_index = -1;
-//int64_t cue_playlist_pos = 0;
-
-int preview_thread_func()
+void Player::thread_func()
 {
        for ( ;; ) {
                // Wait until we're supposed to play something.
                {
                        unique_lock<mutex> lock(cue_state_mu);
-                       cue_is_playing.wait(lock, []{
+                       cue_is_playing.wait(lock, [this]{
                                return cue_state == PLAYING;
                                //return current_cue_status.origin != steady_clock::time_point::max();
                        });
                }
 
-               PlaylistClip clip;
+               Clip clip;
+               unsigned stream_idx;
                {
-                       lock_guard<mutex> lock2(playlist_mu);
-                       clip = current_cue_playlist[0];
+                       lock_guard<mutex> lock2(mu);
+                       clip = current_clip;
+                       stream_idx = current_stream_idx;
                }
                steady_clock::time_point origin = steady_clock::now();
-               int64_t pts_origin = clip.clip.pts_in;
+               int64_t pts_origin = clip.pts_in;
 
                int64_t next_pts = pts_origin;
 
@@ -60,19 +47,19 @@ int preview_thread_func()
                        steady_clock::time_point next_frame_start =
                                origin + microseconds((next_pts - pts_origin) * int(1000000 / speed) / 12800);
                        this_thread::sleep_until(next_frame_start);
-                       global_mainwindow->ui->preview_display->setFrame(clip.stream_idx, next_pts);
+                       destination->setFrame(stream_idx, next_pts);
 
                        // Find the next frame.
                        {
                                lock_guard<mutex> lock2(frame_mu);
-                               auto it = upper_bound(frames[clip.stream_idx].begin(),
-                                       frames[clip.stream_idx].end(),
+                               auto it = upper_bound(frames[stream_idx].begin(),
+                                       frames[stream_idx].end(),
                                        next_pts);
-                               if (it == frames[clip.stream_idx].end()) {
+                               if (it == frames[stream_idx].end()) {
                                        eof = true;
                                } else {
                                        next_pts = *it;
-                                       if (next_pts >= clip.clip.pts_out) {
+                                       if (next_pts >= clip.pts_out) {
                                                eof = true;
                                        }
                                }
@@ -80,7 +67,7 @@ int preview_thread_func()
                        if (eof) break;
                }
 
-               // TODO: advance the playlist and look for the next element.
+               // TODO: callback so that the next playlist item can be cued.
                {
                        unique_lock<mutex> lock(cue_state_mu);
                        cue_state = PAUSED;
@@ -88,17 +75,18 @@ int preview_thread_func()
        }
 }
 
-void start_player_thread()
+Player::Player(JPEGFrameView *destination)
+       : destination(destination)
 {
-       thread(preview_thread_func).detach();
+       thread(&Player::thread_func, this).detach();
 }
 
-void play_clip(const Clip &clip, unsigned stream_idx)
+void Player::play_clip(const Clip &clip, unsigned stream_idx)
 {
        {
-               lock_guard<mutex> lock(playlist_mu);
-               current_cue_playlist.clear();
-               current_cue_playlist.push_back(PlaylistClip{ clip, stream_idx });
+               lock_guard<mutex> lock(mu);
+               current_clip = clip;
+               current_stream_idx = stream_idx;
        }
 
        {
index 960bc22fcd713f2f7a58d4a9e168f78958206c0e..debf9194474baacacefdc448b14e5bbce6caecfa 100644 (file)
--- a/player.h
+++ b/player.h
@@ -3,7 +3,29 @@
 
 #include "clip_list.h"
 
-void start_player_thread();
-void play_clip(const Clip &clip, unsigned stream_idx);
+#include <condition_variable>
+#include <mutex>
+
+class JPEGFrameView;
+
+class Player {
+public:
+       Player(JPEGFrameView *destination);
+
+       void play_clip(const Clip &clip, unsigned stream_idx);
+
+private:
+       void thread_func();
+
+       JPEGFrameView *destination;
+
+       std::mutex mu;
+       Clip current_clip;  // Under mu.
+       unsigned current_stream_idx;  // Under mu.
+
+       enum { PAUSED, PLAYING } cue_state = PAUSED;  // Under cue_state_mu.
+       std::mutex cue_state_mu;
+       std::condition_variable cue_is_playing;
+};
 
 #endif  // !defined(_PLAYER_H)
index 9f8fee72fdd37b243197382b0c35c623fd00a327..7e51adc5d009ce7ca330836acf7503cb2a5c01d4 100644 (file)
       </widget>
       <widget class="QWidget" name="verticalLayoutWidget_4">
        <layout class="QVBoxLayout" name="verticalLayout_4">
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout_2">
+          <item>
+           <widget class="QPushButton" name="queue_btn">
+            <property name="text">
+             <string>Queue (&amp;Q)</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="preview_btn">
+            <property name="text">
+             <string>Preview (&amp;W)</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="cue_in_btn">
+            <property name="text">
+             <string>Cue in (&amp;A)</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="cue_out_btn">
+            <property name="text">
+             <string>Cue out (&amp;S)</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="play_btn">
+            <property name="text">
+             <string>Play (space)</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
         <item>
          <widget class="QTableView" name="clip_list"/>
         </item>
         <item>
-         <widget class="QTableWidget" name="playlist">
-          <column>
-           <property name="text">
-            <string>In</string>
-           </property>
-          </column>
-          <column>
-           <property name="text">
-            <string>Out</string>
-           </property>
-          </column>
-          <column>
-           <property name="text">
-            <string>Duration</string>
-           </property>
-          </column>
-          <column>
-           <property name="text">
-            <string>Camera</string>
-           </property>
-          </column>
-          <column>
-           <property name="text">
-            <string>Description</string>
-           </property>
-          </column>
+         <widget class="QTableView" name="playlist">
+          <property name="selectionMode">
+           <enum>QAbstractItemView::SingleSelection</enum>
+          </property>
+          <property name="selectionBehavior">
+           <enum>QAbstractItemView::SelectRows</enum>
+          </property>
          </widget>
         </item>
        </layout>