X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=mainwindow.cpp;h=dd3c82764537bc23cb588a1dc9978e54db21b667;hb=3795723be95f2fe82f3c8b8b45b1a905b2c811fd;hp=8f63bee0fbd05108c96e2f573cd45c4f4a00342d;hpb=c4508efc504090116e146a1f38659fe7de8398df;p=nageru diff --git a/mainwindow.cpp b/mainwindow.cpp index 8f63bee..dd3c827 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -2,21 +2,21 @@ #include "clip_list.h" #include "disk_space_estimator.h" +#include "flags.h" +#include "frame_on_disk.h" #include "player.h" #include "post_to_main_thread.h" #include "timebase.h" #include "ui_mainwindow.h" -#include -#include -#include - #include -#include #include #include - +#include +#include #include +#include +#include using namespace std; using namespace std::placeholders; @@ -26,12 +26,10 @@ static ClipList *cliplist_clips; static PlayList *playlist_clips; extern int64_t current_pts; -extern mutex frame_mu; -extern vector frames[MAX_STREAMS]; MainWindow::MainWindow() : ui(new Ui::MainWindow), - db("futatabi.db") + db(global_flags.working_directory + "/futatabi.db") { global_mainwindow = this; ui->setupUi(this); @@ -131,11 +129,12 @@ MainWindow::MainWindow() }); }); live_player->set_next_clip_callback(bind(&MainWindow::live_player_get_next_clip, this)); - live_player->set_progress_callback([this](double played_this_clip, double total_length) { - post_to_main_thread([this, played_this_clip, total_length] { - live_player_clip_progress(played_this_clip, total_length); + live_player->set_progress_callback([this](const map &progress) { + post_to_main_thread([this, progress] { + live_player_clip_progress(progress); }); }); + set_output_status("paused"); defer_timeout = new QTimer(this); defer_timeout->setSingleShot(true); @@ -199,11 +198,23 @@ void MainWindow::queue_clicked() void MainWindow::preview_clicked() { - if (cliplist_clips->empty()) return; + if (ui->playlist->hasFocus()) { + // Allow the playlist as preview iff it has focus and something is selected. + QItemSelectionModel *selected = ui->playlist->selectionModel(); + if (selected->hasSelection()) { + QModelIndex index = selected->currentIndex(); + const Clip &clip = *playlist_clips->clip(index.row()); + preview_player->play_clip(clip, index.row(), clip.stream_idx); + return; + } + } + + if (cliplist_clips->empty()) + return; QItemSelectionModel *selected = ui->clip_list->selectionModel(); if (!selected->hasSelection()) { - preview_player->play_clip(*cliplist_clips->back(), 0); + preview_player->play_clip(*cliplist_clips->back(), cliplist_clips->size() - 1, 0); return; } @@ -215,7 +226,7 @@ void MainWindow::preview_clicked() } else { stream_idx = ui->preview_display->get_stream_idx(); } - preview_player->play_clip(*cliplist_clips->clip(index.row()), stream_idx); + preview_player->play_clip(*cliplist_clips->clip(index.row()), index.row(), stream_idx); } void MainWindow::preview_angle_clicked(unsigned stream_idx) @@ -315,7 +326,8 @@ void MainWindow::state_changed(const StateProto &state) void MainWindow::play_clicked() { - if (playlist_clips->empty()) return; + if (playlist_clips->empty()) + return; QItemSelectionModel *selected = ui->playlist->selectionModel(); int row; @@ -326,7 +338,8 @@ void MainWindow::play_clicked() } const Clip &clip = *playlist_clips->clip(row); - live_player->play_clip(clip, clip.stream_idx); + live_player->play_clip(clip, row, clip.stream_idx); + playlist_clips->set_progress({{ row, 0.0f }}); playlist_clips->set_currently_playing(row, 0.0f); playlist_selection_changed(); } @@ -335,50 +348,71 @@ void MainWindow::live_player_clip_done() { int row = playlist_clips->get_currently_playing(); if (row == -1 || row == int(playlist_clips->size()) - 1) { - ui->live_label->setText("Current output (paused)"); + set_output_status("paused"); + playlist_clips->set_progress({}); playlist_clips->set_currently_playing(-1, 0.0f); } else { + playlist_clips->set_progress({{ row + 1, 0.0f }}); playlist_clips->set_currently_playing(row + 1, 0.0f); } } -Clip MainWindow::live_player_get_next_clip() +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. - promise clip_promise; - future clip = clip_promise.get_future(); + promise> clip_promise; + future> clip = clip_promise.get_future(); post_to_main_thread([this, &clip_promise] { int row = playlist_clips->get_currently_playing(); if (row != -1 && row < int(playlist_clips->size()) - 1) { - clip_promise.set_value(*playlist_clips->clip(row + 1)); + clip_promise.set_value(make_pair(*playlist_clips->clip(row + 1), row + 1)); } else { - clip_promise.set_value(Clip()); + clip_promise.set_value(make_pair(Clip(), 0)); } }); return clip.get(); } -void MainWindow::live_player_clip_progress(double played_this_clip, double total_length) +static string format_duration(double t) { - playlist_clips->set_currently_playing(playlist_clips->get_currently_playing(), played_this_clip / total_length); - - double remaining = total_length - played_this_clip; - for (int row = playlist_clips->get_currently_playing() + 1; row < int(playlist_clips->size()); ++row) { - const Clip clip = *playlist_clips->clip(row); - remaining += double(clip.pts_out - clip.pts_in) / TIMEBASE / 0.5; // FIXME: stop hardcoding speed. - } - int remaining_ms = lrint(remaining * 1e3); + int t_ms = lrint(t * 1e3); - int ms = remaining_ms % 1000; - remaining_ms /= 1000; - int s = remaining_ms % 60; - remaining_ms /= 60; - int m = remaining_ms; + int ms = t_ms % 1000; + t_ms /= 1000; + int s = t_ms % 60; + t_ms /= 60; + int m = t_ms; char buf[256]; - snprintf(buf, sizeof(buf), "Current output (%d:%02d.%03d left)", m, s, ms); - ui->live_label->setText(buf); + snprintf(buf, sizeof(buf), "%d:%02d.%03d", m, s, ms); + return buf; +} + +void MainWindow::live_player_clip_progress(const map &progress) +{ + playlist_clips->set_progress(progress); + + // Look at the last clip and then start counting from there. + assert(!progress.empty()); + auto last_it = progress.end(); + --last_it; + double remaining = 0.0; + double last_fade_time_seconds = 0.0; + for (size_t row = last_it->first; row < playlist_clips->size(); ++row) { + const Clip clip = *playlist_clips->clip(row); + double clip_length = double(clip.pts_out - clip.pts_in) / TIMEBASE / 0.5; // FIXME: stop hardcoding speed. + if (row == last_it->first) { + // A clip we're playing: Subtract the part we've already played. + remaining = clip_length * (1.0 - last_it->second); + } else { + // A clip we haven't played yet: Subtract the part that's overlapping + // with a previous clip (due to fade). + remaining += max(clip_length - last_fade_time_seconds, 0.0); + } + last_fade_time_seconds = min(clip_length, clip.fade_time_seconds); + } + set_output_status(format_duration(remaining) + " left"); } void MainWindow::resizeEvent(QResizeEvent *event) @@ -444,7 +478,8 @@ bool MainWindow::eventFilter(QObject *watched, QEvent *event) } int column = destination->columnAt(mouse->x()); int row = destination->rowAt(mouse->y()); - if (column == -1 || row == -1) return false; + if (column == -1 || row == -1) + return false; if (type == SCRUBBING_CLIP_LIST) { if (ClipList::Column(column) == ClipList::Column::IN) { @@ -543,6 +578,12 @@ bool MainWindow::eventFilter(QObject *watched, QEvent *event) int row = destination->rowAt(wheel->y()); if (column == -1 || row == -1) return false; + // Only adjust pts with the wheel if the given row is selected. + if (!destination->hasFocus() || + row != destination->selectionModel()->currentIndex().row()) { + return false; + } + currently_deferring_model_changes = true; { current_change_id = (watched == ui->clip_list->viewport()) ? "cliplist:" : "playlist:"; @@ -586,6 +627,7 @@ bool MainWindow::eventFilter(QObject *watched, QEvent *event) } } currently_deferring_model_changes = false; + return true; // Don't scroll. } else if (event->type() == QEvent::MouseButtonRelease) { scrubbing = false; } @@ -596,25 +638,29 @@ void MainWindow::preview_single_frame(int64_t pts, unsigned stream_idx, MainWind { if (rounding == LAST_BEFORE) { lock_guard lock(frame_mu); - if (frames[stream_idx].empty()) return; - auto it = lower_bound(frames[stream_idx].begin(), frames[stream_idx].end(), pts); + 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; }); if (it != frames[stream_idx].end()) { - pts = *it; + pts = it->pts; } } else { assert(rounding == FIRST_AT_OR_AFTER); 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); + 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; }); if (it != frames[stream_idx].end()) { - pts = *it; + pts = it->pts; } } Clip fake_clip; fake_clip.pts_in = pts; fake_clip.pts_out = pts + 1; - preview_player->play_clip(fake_clip, stream_idx); + preview_player->play_clip(fake_clip, 0, stream_idx); } void MainWindow::playlist_selection_changed() @@ -628,6 +674,17 @@ void MainWindow::playlist_selection_changed() ui->playlist_move_down_btn->setEnabled( any_selected && selected->selectedRows().back().row() < int(playlist_clips->size()) - 1); ui->play_btn->setEnabled(!playlist_clips->empty()); + + if (!any_selected) { + set_output_status("paused"); + } else { + double remaining = 0.0; + for (int row = selected->selectedRows().front().row(); row < int(playlist_clips->size()); ++row) { + const Clip clip = *playlist_clips->clip(row); + remaining += double(clip.pts_out - clip.pts_in) / TIMEBASE / 0.5; // FIXME: stop hardcoding speed. + } + set_output_status(format_duration(remaining) + " ready"); + } } void MainWindow::clip_list_selection_changed(const QModelIndex ¤t, const QModelIndex &) @@ -667,10 +724,10 @@ void MainWindow::report_disk_space(off_t free_bytes, double estimated_seconds_le std::string label = buf; - post_to_main_thread([this, label]{ - disk_free_label->setText(QString::fromStdString(label)); - ui->menuBar->setCornerWidget(disk_free_label); // Need to set this again for the sizing to get right. - }); + post_to_main_thread([this, label] { + disk_free_label->setText(QString::fromStdString(label)); + ui->menuBar->setCornerWidget(disk_free_label); // Need to set this again for the sizing to get right. + }); } void MainWindow::exit_triggered() @@ -701,3 +758,16 @@ void MainWindow::highlight_camera_input(int stream_idx) ui->input4_frame->setStyleSheet(""); } } + +void MainWindow::set_output_status(const string &status) +{ + ui->live_label->setText(QString::fromStdString("Current output (" + status + ")")); + + lock_guard lock(queue_status_mu); + queue_status = status; +} + +pair MainWindow::get_queue_status() const { + lock_guard lock(queue_status_mu); + return {queue_status, "text/plain"}; +}