From 931727fc4cccafc84023a053a6eff174b5ea8190 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Fri, 21 Dec 2018 15:17:47 +0100 Subject: [PATCH] Add cue point padding, where a clip is elongated (in both directions) by some given time to compensate for operator reaction time and fades. --- futatabi/flags.cpp | 14 +++++++++++++- futatabi/flags.h | 2 ++ futatabi/mainwindow.cpp | 43 +++++++++++++++++++++++++++++++++++++---- futatabi/mainwindow.h | 1 + futatabi/mainwindow.ui | 43 +++++++++++++++++++++++++++++++++++++++++ futatabi/state.proto | 1 + 6 files changed, 99 insertions(+), 5 deletions(-) diff --git a/futatabi/flags.cpp b/futatabi/flags.cpp index 9e15399..aefde27 100644 --- a/futatabi/flags.cpp +++ b/futatabi/flags.cpp @@ -17,7 +17,8 @@ enum LongOption { OPTION_HELP = 1000, OPTION_SLOW_DOWN_INPUT = 1001, OPTION_HTTP_PORT = 1002, - OPTION_TALLY_URL = 1003 + OPTION_TALLY_URL = 1003, + OPTION_CUE_POINT_PADDING = 1004 }; void usage() @@ -36,6 +37,7 @@ void usage() fprintf(stderr, " 2 = default (realtime 720p on fast embedded GPUs)\n"); fprintf(stderr, " 3 = good (realtime 720p on GTX 970 or so)\n"); fprintf(stderr, " 4 = best (not realtime on any current GPU)\n"); + fprintf(stderr, " --cue-point-padding SECS move cue-in/cue-out N seconds earlier/later on set\n"); fprintf(stderr, " -d, --working-directory DIR where to store frames and database\n"); fprintf(stderr, " --http-port PORT which port to listen on for output\n"); fprintf(stderr, " --tally-url URL URL to get tally color from (polled every 100 ms)\n"); @@ -53,6 +55,7 @@ void parse_flags(int argc, char * const argv[]) { "working-directory", required_argument, 0, 'd' }, { "http-port", required_argument, 0, OPTION_HTTP_PORT }, { "tally-url", required_argument, 0, OPTION_TALLY_URL }, + { "cue-point-padding", required_argument, 0, OPTION_CUE_POINT_PADDING }, { 0, 0, 0, 0 } }; for ( ;; ) { @@ -97,6 +100,10 @@ void parse_flags(int argc, char * const argv[]) case OPTION_TALLY_URL: global_flags.tally_url = optarg; break; + case OPTION_CUE_POINT_PADDING: + global_flags.cue_point_padding_seconds = atof(optarg); + global_flags.cue_point_padding_set = true; + break; case OPTION_HELP: usage(); exit(0); @@ -113,4 +120,9 @@ void parse_flags(int argc, char * const argv[]) usage(); exit(1); } + if (global_flags.cue_point_padding_seconds < 0.0) { + fprintf(stderr, "Cue point padding cannot be negative.\n"); + usage(); + exit(1); + } } diff --git a/futatabi/flags.h b/futatabi/flags.h index 5b7d32e..6796c14 100644 --- a/futatabi/flags.h +++ b/futatabi/flags.h @@ -15,6 +15,8 @@ struct Flags { uint16_t http_port = DEFAULT_HTTPD_PORT; double output_framerate = 60000.0 / 1001.0; std::string tally_url; + double cue_point_padding_seconds = 0.0; // Can be changed in the menus. + bool cue_point_padding_set = false; }; extern Flags global_flags; diff --git a/futatabi/mainwindow.cpp b/futatabi/mainwindow.cpp index 38a50df..152358b 100644 --- a/futatabi/mainwindow.cpp +++ b/futatabi/mainwindow.cpp @@ -40,13 +40,16 @@ MainWindow::MainWindow() global_mainwindow = this; ui->setupUi(this); - // Load settings from database if needed. + // Load settings from database. + SettingsProto settings = db.get_settings(); if (!global_flags.interpolation_quality_set) { - SettingsProto settings = db.get_settings(); if (settings.interpolation_quality() != 0) { global_flags.interpolation_quality = settings.interpolation_quality() - 1; } } + if (!global_flags.cue_point_padding_set) { + global_flags.cue_point_padding_seconds = settings.cue_point_padding_seconds(); // Default 0 is fine. + } if (global_flags.interpolation_quality == 0) { // Allocate something just for simplicity; we won't be using it // unless the user changes runtime, in which case 1 is fine. @@ -93,6 +96,28 @@ MainWindow::MainWindow() connect(ui->quality_3_action, &QAction::toggled, bind(&MainWindow::quality_toggled, this, 3, _1)); connect(ui->quality_4_action, &QAction::toggled, bind(&MainWindow::quality_toggled, this, 4, _1)); + // The cue point padding group. + QActionGroup *padding_group = new QActionGroup(ui->interpolation_menu); + padding_group->addAction(ui->padding_0_action); + padding_group->addAction(ui->padding_1_action); + padding_group->addAction(ui->padding_2_action); + padding_group->addAction(ui->padding_5_action); + if (global_flags.cue_point_padding_seconds <= 1e-3) { + ui->padding_0_action->setChecked(true); + } else if (fabs(global_flags.cue_point_padding_seconds - 1.0) < 1e-3) { + ui->padding_1_action->setChecked(true); + } else if (fabs(global_flags.cue_point_padding_seconds - 2.0) < 1e-3) { + ui->padding_2_action->setChecked(true); + } else if (fabs(global_flags.cue_point_padding_seconds - 5.0) < 1e-3) { + ui->padding_5_action->setChecked(true); + } else { + // Nothing to check, which is fine. + } + connect(ui->padding_0_action, &QAction::toggled, bind(&MainWindow::padding_toggled, this, 0.0, _1)); + connect(ui->padding_1_action, &QAction::toggled, bind(&MainWindow::padding_toggled, this, 1.0, _1)); + connect(ui->padding_2_action, &QAction::toggled, bind(&MainWindow::padding_toggled, this, 2.0, _1)); + connect(ui->padding_5_action, &QAction::toggled, bind(&MainWindow::padding_toggled, this, 5.0, _1)); + global_disk_space_estimator = new DiskSpaceEstimator(bind(&MainWindow::report_disk_space, this, _1, _2)); disk_free_label = new QLabel(this); disk_free_label->setStyleSheet("QLabel {padding-right: 5px;}"); @@ -252,7 +277,7 @@ void MainWindow::cue_in_clicked() return; } Clip clip; - clip.pts_in = current_pts; + clip.pts_in = max(current_pts - lrint(global_flags.cue_point_padding_seconds * TIMEBASE), 0); cliplist_clips->add_clip(clip); playlist_selection_changed(); ui->clip_list->scrollToBottom(); @@ -261,7 +286,7 @@ void MainWindow::cue_in_clicked() void MainWindow::cue_out_clicked() { if (!cliplist_clips->empty()) { - cliplist_clips->mutable_back()->pts_out = current_pts; + cliplist_clips->mutable_back()->pts_out = current_pts + lrint(global_flags.cue_point_padding_seconds * TIMEBASE); // TODO: select the row in the clip list? } } @@ -446,6 +471,7 @@ void MainWindow::save_settings() { SettingsProto settings; settings.set_interpolation_quality(global_flags.interpolation_quality + 1); + settings.set_cue_point_padding_seconds(global_flags.cue_point_padding_seconds); db.store_settings(settings); } @@ -1009,6 +1035,15 @@ void MainWindow::quality_toggled(int quality, bool checked) save_settings(); } +void MainWindow::padding_toggled(double seconds, bool checked) +{ + if (!checked) { + return; + } + global_flags.cue_point_padding_seconds = seconds; + save_settings(); +} + void MainWindow::highlight_camera_input(int stream_idx) { for (unsigned i = 0; i < num_cameras; ++i) { diff --git a/futatabi/mainwindow.h b/futatabi/mainwindow.h index 5d7104a..2bb880d 100644 --- a/futatabi/mainwindow.h +++ b/futatabi/mainwindow.h @@ -134,6 +134,7 @@ private: void undo_triggered(); void redo_triggered(); void quality_toggled(int quality, bool checked); + void padding_toggled(double seconds, bool checked); void highlight_camera_input(int stream_idx); diff --git a/futatabi/mainwindow.ui b/futatabi/mainwindow.ui index 0f471e0..36d8a5e 100644 --- a/futatabi/mainwindow.ui +++ b/futatabi/mainwindow.ui @@ -343,9 +343,20 @@ + + + Cue point &padding + + + + + + + + @@ -446,6 +457,38 @@ Best (&4) (not realtime on any current GPU) + + + true + + + &0 seconds + + + + + true + + + &1 second + + + + + true + + + &2 seconds + + + + + true + + + &5 seconds + + diff --git a/futatabi/state.proto b/futatabi/state.proto index c64555b..36a9fa0 100644 --- a/futatabi/state.proto +++ b/futatabi/state.proto @@ -21,4 +21,5 @@ message StateProto { message SettingsProto { int32 interpolation_quality = 1; // 0 = unset, 1 = quality 0, 2 = quality 1, etc. + double cue_point_padding_seconds = 2; } -- 2.39.2