connect(play, &QShortcut::activated, ui->play_btn, &QPushButton::click);
connect(ui->play_btn, &QPushButton::clicked, this, &MainWindow::play_clicked);
+ QShortcut *preview_1 = new QShortcut(QKeySequence(Qt::Key_1), this);
+ connect(preview_1, &QShortcut::activated, ui->preview_1_btn, &QPushButton::click);
+ connect(ui->preview_1_btn, &QPushButton::clicked, [this]{ preview_angle_clicked(0); });
+
+ QShortcut *preview_2 = new QShortcut(QKeySequence(Qt::Key_2), this);
+ connect(preview_2, &QShortcut::activated, ui->preview_2_btn, &QPushButton::click);
+ connect(ui->preview_2_btn, &QPushButton::clicked, [this]{ preview_angle_clicked(1); });
+
+ QShortcut *preview_3 = new QShortcut(QKeySequence(Qt::Key_3), this);
+ connect(preview_3, &QShortcut::activated, ui->preview_3_btn, &QPushButton::click);
+ connect(ui->preview_3_btn, &QPushButton::clicked, [this]{ preview_angle_clicked(2); });
+
+ QShortcut *preview_4 = new QShortcut(QKeySequence(Qt::Key_4), this);
+ connect(preview_4, &QShortcut::activated, ui->preview_4_btn, &QPushButton::click);
+ connect(ui->preview_4_btn, &QPushButton::clicked, [this]{ preview_angle_clicked(3); });
+
preview_player = new Player(ui->preview_display);
live_player = new Player(ui->live_display);
live_player->set_done_callback([this]{
}
}
+void MainWindow::preview_angle_clicked(unsigned stream_idx)
+{
+ preview_player->override_angle(stream_idx);
+
+ // Change the selection if we were previewing a clip from the clip list.
+ // (The only other thing we could be showing is a pts scrub, and if so,
+ // that would be selected.)
+ QItemSelectionModel *selected = ui->clip_list->selectionModel();
+ if (selected->hasSelection()) {
+ QModelIndex cell = selected->selectedIndexes()[0];
+ int column = int(ClipList::Column::CAMERA_1) + stream_idx;
+ selected->setCurrentIndex(cell.sibling(cell.row(), column), QItemSelectionModel::ClearAndSelect);
+ }
+}
+
void MainWindow::play_clicked()
{
if (playlist_clips->empty()) return;
void cue_out_clicked();
void queue_clicked();
void preview_clicked();
+ void preview_angle_clicked(unsigned stream_idx);
void play_clicked();
void live_player_clip_done();
return new_clip_ready && current_clip.pts_in != -1;
});
new_clip_ready = false;
+ playing = true;
}
Clip clip;
// Sleep until the next frame start, or until there's a new clip we're supposed to play.
{
unique_lock<mutex> lock(queue_state_mu);
- aborted = new_clip_changed.wait_until(lock, next_frame_start, [this]{
- return new_clip_ready;
+ new_clip_changed.wait_until(lock, next_frame_start, [this]{
+ return new_clip_ready || override_stream_idx != -1;
});
- if (aborted) break;
+ if (new_clip_ready) break;
+ if (override_stream_idx != -1) {
+ stream_idx = override_stream_idx;
+ override_stream_idx = -1;
+ continue;
+ }
}
destination->setFrame(stream_idx, next_pts);
}
+ {
+ unique_lock<mutex> lock(queue_state_mu);
+ playing = false;
+ }
if (done_callback != nullptr && !aborted) {
done_callback();
}
{
lock_guard<mutex> lock(queue_state_mu);
new_clip_ready = true;
+ override_stream_idx = -1;
new_clip_changed.notify_all();
}
}
+
+void Player::override_angle(unsigned stream_idx)
+{
+ // Corner case: If a new clip is waiting to be played, change its stream and then we're done.
+ {
+ unique_lock<mutex> lock(queue_state_mu);
+ if (new_clip_ready) {
+ lock_guard<mutex> lock2(mu);
+ current_stream_idx = stream_idx;
+ return;
+ }
+ }
+
+ // If we are playing a clip, set override_stream_idx, and the player thread will
+ // pick it up and change its internal index.
+ {
+ unique_lock<mutex> lock(queue_state_mu);
+ if (playing) {
+ override_stream_idx = stream_idx;
+ new_clip_changed.notify_all();
+ }
+ }
+
+ // OK, so we're standing still, presumably at the end of a clip.
+ // Look at the current pts_out (if it exists), and show the closest
+ // thing we've got.
+ int64_t pts_out;
+ {
+ lock_guard<mutex> lock(mu);
+ if (current_clip.pts_out < 0) {
+ return;
+ }
+ pts_out = current_clip.pts_out;
+ }
+
+ lock_guard<mutex> lock(frame_mu);
+ auto it = upper_bound(frames[stream_idx].begin(), frames[stream_idx].end(), pts_out);
+ if (it == frames[stream_idx].end()) {
+ return;
+ }
+ destination->setFrame(stream_idx, *it);
+}
Player(JPEGFrameView *destination);
void play_clip(const Clip &clip, unsigned stream_idx);
+ void override_angle(unsigned stream_idx); // For the current clip only.
// Not thread-safe to set concurrently with playing.
// Will be called back from the player thread.
Clip current_clip; // Under mu. Can have pts_in = -1 for no clip.
unsigned current_stream_idx; // Under mu.
- bool new_clip_ready = false; // Under queue_state_mu.
std::mutex queue_state_mu;
std::condition_variable new_clip_changed;
+ bool new_clip_ready = false; // Under queue_state_mu.
+ bool playing = false; // Under queue_state_mu.
+ int override_stream_idx = -1; // Under queue_state_mu.
};
#endif // !defined(_PLAYER_H)
</layout>
</item>
<item>
- <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0">
+ <layout class="QVBoxLayout" name="preview_pane" stretch="1,0">
<item>
<widget class="JPEGFrameView" name="preview_display" native="true"/>
</item>
<item>
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Preview output</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="spacing">
+ <number>0</number>
</property>
- </widget>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Preview output</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="preview_1_btn">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>17</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>1</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="preview_2_btn">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>17</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>2</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="preview_3_btn">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>17</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>3</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="preview_4_btn">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>17</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>4</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
</layout>
</item>