X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=futatabi%2Fmainwindow.cpp;h=54efa1c1de221c107f3b57cbcc15a74418096fd5;hb=eaba7288c4fb39ca195c9355970293bcaf088dbc;hp=85a86a8a6f7bf5e2e6a621ddfbeacd97d2d646f9;hpb=af8007aa958271ba16b15e7d11a891570488f6c9;p=nageru diff --git a/futatabi/mainwindow.cpp b/futatabi/mainwindow.cpp index 85a86a8..54efa1c 100644 --- a/futatabi/mainwindow.cpp +++ b/futatabi/mainwindow.cpp @@ -1,7 +1,9 @@ #include "mainwindow.h" +#include "shared/aboutdialog.h" #include "clip_list.h" -#include "disk_space_estimator.h" +#include "export.h" +#include "shared/disk_space_estimator.h" #include "flags.h" #include "frame_on_disk.h" #include "player.h" @@ -9,6 +11,9 @@ #include "shared/timebase.h" #include "ui_mainwindow.h" +#include +#include +#include #include #include #include @@ -36,6 +41,10 @@ MainWindow::MainWindow() // The menus. connect(ui->exit_action, &QAction::triggered, this, &MainWindow::exit_triggered); + connect(ui->export_cliplist_clip_multitrack_action, &QAction::triggered, this, &MainWindow::export_cliplist_clip_multitrack_triggered); + connect(ui->export_playlist_clip_interpolated_action, &QAction::triggered, this, &MainWindow::export_playlist_clip_interpolated_triggered); + connect(ui->manual_action, &QAction::triggered, this, &MainWindow::manual_triggered); + connect(ui->about_action, &QAction::triggered, this, &MainWindow::about_triggered); global_disk_space_estimator = new DiskSpaceEstimator(bind(&MainWindow::report_disk_space, this, _1, _2)); disk_free_label = new QLabel(this); @@ -121,8 +130,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, Player::NO_STREAM_OUTPUT)); + live_player.reset(new Player(ui->live_display, Player::HTTPD_STREAM_OUTPUT)); live_player->set_done_callback([this]{ post_to_main_thread([this]{ live_player_clip_done(); @@ -144,6 +153,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) { @@ -154,6 +168,7 @@ void MainWindow::cue_in_clicked() clip.pts_in = current_pts; cliplist_clips->add_clip(clip); playlist_selection_changed(); + ui->clip_list->scrollToBottom(); } void MainWindow::cue_out_clicked() @@ -177,6 +192,7 @@ void MainWindow::queue_clicked() if (clip.pts_out != -1) { playlist_clips->add_clip(clip); playlist_selection_changed(); + ui->playlist->scrollToBottom(); } return; } @@ -193,6 +209,12 @@ void MainWindow::queue_clicked() if (clip.pts_out != -1) { playlist_clips->add_clip(clip); playlist_selection_changed(); + ui->playlist->scrollToBottom(); + if (!ui->playlist->selectionModel()->hasSelection()) { + // TODO: Figure out why this doesn't always seem to actually select the row. + QModelIndex bottom = playlist_clips->index(playlist_clips->size() - 1, 0); + ui->playlist->setCurrentIndex(bottom); + } } } @@ -361,6 +383,9 @@ pair 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> clip_promise; future> clip = clip_promise.get_future(); post_to_main_thread([this, &clip_promise] { @@ -640,8 +665,7 @@ void MainWindow::preview_single_frame(int64_t pts, unsigned stream_idx, MainWind lock_guard lock(frame_mu); if (frames[stream_idx].empty()) return; - auto it = lower_bound(frames[stream_idx].begin(), frames[stream_idx].end(), pts, - [](const FrameOnDisk &frame, int64_t pts) { return frame.pts < pts; }); + auto it = find_last_frame_before(frames[stream_idx], pts); if (it != frames[stream_idx].end()) { pts = it->pts; } @@ -650,8 +674,7 @@ void MainWindow::preview_single_frame(int64_t pts, unsigned stream_idx, MainWind lock_guard lock(frame_mu); if (frames[stream_idx].empty()) return; - auto it = upper_bound(frames[stream_idx].begin(), frames[stream_idx].end(), pts - 1, - [](int64_t pts, const FrameOnDisk &frame) { return pts < frame.pts; }); + auto it = find_first_frame_at_or_after(frames[stream_idx], pts); if (it != frames[stream_idx].end()) { pts = it->pts; } @@ -735,6 +758,68 @@ void MainWindow::exit_triggered() close(); } +void MainWindow::export_cliplist_clip_multitrack_triggered() +{ + QItemSelectionModel *selected = ui->clip_list->selectionModel(); + if (!selected->hasSelection()) { + QMessageBox msgbox; + msgbox.setText("No clip selected in the clip list. Select one and try exporting again."); + msgbox.exec(); + return; + } + + QModelIndex index = selected->currentIndex(); + Clip clip = *cliplist_clips->clip(index.row()); + QString filename = QFileDialog::getSaveFileName(this, + "Export multitrack clip", QString(), tr("Matroska video files (*.mkv)")); + if (filename.isNull()) { + // Cancel. + return; + } + if (!filename.endsWith(".mkv")) { + filename += ".mkv"; + } + export_multitrack_clip(filename.toStdString(), clip); +} + +void MainWindow::export_playlist_clip_interpolated_triggered() +{ + QItemSelectionModel *selected = ui->playlist->selectionModel(); + if (!selected->hasSelection()) { + QMessageBox msgbox; + msgbox.setText("No clip selected in the playlist. Select one and try exporting again."); + msgbox.exec(); + return; + } + + QModelIndex index = selected->currentIndex(); + Clip clip = *playlist_clips->clip(index.row()); + QString filename = QFileDialog::getSaveFileName(this, + "Export interpolated clip", QString(), tr("Matroska video files (*.mkv)")); + if (filename.isNull()) { + // Cancel. + return; + } + if (!filename.endsWith(".mkv")) { + filename += ".mkv"; + } + export_interpolated_clip(filename.toStdString(), clip); +} + +void MainWindow::manual_triggered() +{ + if (!QDesktopServices::openUrl(QUrl("https://nageru.sesse.net/doc/"))) { + QMessageBox msgbox; + msgbox.setText("Could not launch manual in web browser.\nPlease see https://nageru.sesse.net/doc/ manually."); + msgbox.exec(); + } +} + +void MainWindow::about_triggered() +{ + AboutDialog("Futatabi", "Multicamera slow motion video server").exec(); +} + void MainWindow::highlight_camera_input(int stream_idx) { if (stream_idx == 0) {