From: Steinar H. Gunderson Date: Sat, 15 Dec 2018 20:54:36 +0000 (+0100) Subject: Persist quality settings in the database. X-Git-Tag: 1.8.0~17 X-Git-Url: https://git.sesse.net/?p=nageru;a=commitdiff_plain;h=ffe2343de3fe982cc52a449b13f01137819ed42d Persist quality settings in the database. --- diff --git a/futatabi/db.cpp b/futatabi/db.cpp index 39fd557..13e3f6e 100644 --- a/futatabi/db.cpp +++ b/futatabi/db.cpp @@ -18,6 +18,10 @@ DB::DB(const string &filename) CREATE TABLE IF NOT EXISTS state (state BLOB); )", nullptr, nullptr, nullptr); // Ignore errors. + sqlite3_exec(db, R"( + CREATE TABLE IF NOT EXISTS settings (settings BLOB); + )", nullptr, nullptr, nullptr); // Ignore errors. + sqlite3_exec(db, R"( DROP TABLE file; )", nullptr, nullptr, nullptr); // Ignore errors. @@ -116,6 +120,83 @@ void DB::store_state(const StateProto &state) } } +SettingsProto DB::get_settings() +{ + SettingsProto settings; + + sqlite3_stmt *stmt; + int ret = sqlite3_prepare_v2(db, "SELECT settings FROM settings", -1, &stmt, 0); + if (ret != SQLITE_OK) { + fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db)); + exit(1); + } + + ret = sqlite3_step(stmt); + if (ret == SQLITE_ROW) { + bool ok = settings.ParseFromArray(sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0)); + if (!ok) { + fprintf(stderr, "State in database is corrupted!\n"); + exit(1); + } + } else if (ret != SQLITE_DONE) { + fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db)); + exit(1); + } + + ret = sqlite3_finalize(stmt); + if (ret != SQLITE_OK) { + fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db)); + exit(1); + } + + return settings; +} + +void DB::store_settings(const SettingsProto &settings) +{ + string serialized; + settings.SerializeToString(&serialized); + + int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr); + if (ret != SQLITE_OK) { + fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db)); + exit(1); + } + + ret = sqlite3_exec(db, "DELETE FROM settings", nullptr, nullptr, nullptr); + if (ret != SQLITE_OK) { + fprintf(stderr, "DELETE: %s\n", sqlite3_errmsg(db)); + exit(1); + } + + sqlite3_stmt *stmt; + ret = sqlite3_prepare_v2(db, "INSERT INTO settings VALUES (?)", -1, &stmt, 0); + if (ret != SQLITE_OK) { + fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db)); + exit(1); + } + + sqlite3_bind_blob(stmt, 1, serialized.data(), serialized.size(), SQLITE_STATIC); + + ret = sqlite3_step(stmt); + if (ret == SQLITE_ROW) { + fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db)); + exit(1); + } + + ret = sqlite3_finalize(stmt); + if (ret != SQLITE_OK) { + fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db)); + exit(1); + } + + ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr); + if (ret != SQLITE_OK) { + fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db)); + exit(1); + } +} + vector DB::load_frame_file(const string &filename, size_t size, unsigned filename_idx) { FileContentsProto file_contents; diff --git a/futatabi/db.h b/futatabi/db.h index f8032c0..c0c3623 100644 --- a/futatabi/db.h +++ b/futatabi/db.h @@ -17,6 +17,9 @@ public: StateProto get_state(); void store_state(const StateProto &state); + SettingsProto get_settings(); + void store_settings(const SettingsProto &settings); + struct FrameOnDiskAndStreamIdx { FrameOnDisk frame; unsigned stream_idx; diff --git a/futatabi/flags.cpp b/futatabi/flags.cpp index 7a82460..1c606b1 100644 --- a/futatabi/flags.cpp +++ b/futatabi/flags.cpp @@ -10,6 +10,7 @@ using namespace std; Flags global_flags; +int flow_initialized_interpolation_quality; // Long options that have no corresponding short option. enum LongOption { @@ -82,6 +83,7 @@ void parse_flags(int argc, char * const argv[]) break; case 'q': global_flags.interpolation_quality = atoi(optarg); + global_flags.interpolation_quality_set = true; break; case 'd': global_flags.working_directory = optarg; diff --git a/futatabi/flags.h b/futatabi/flags.h index 23be12c..a25b7e5 100644 --- a/futatabi/flags.h +++ b/futatabi/flags.h @@ -10,12 +10,18 @@ struct Flags { std::string stream_source; std::string working_directory = "."; bool slow_down_input = false; - int interpolation_quality = 2; + int interpolation_quality = 2; // Can be changed in the menus. + bool interpolation_quality_set = false; uint16_t http_port = DEFAULT_HTTPD_PORT; double output_framerate = 60000.0 / 1001.0; }; extern Flags global_flags; +// The quality setting that VideoStream was initialized to. The quality cannot +// currently be changed, except turning interpolation completely off, so we compare +// against this to give a warning. +extern int flow_initialized_interpolation_quality; + void usage(); void parse_flags(int argc, char * const argv[]); diff --git a/futatabi/mainwindow.cpp b/futatabi/mainwindow.cpp index 4e53590..8523910 100644 --- a/futatabi/mainwindow.cpp +++ b/futatabi/mainwindow.cpp @@ -50,6 +50,22 @@ MainWindow::MainWindow() global_mainwindow = this; ui->setupUi(this); + // Load settings from database if needed. + 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.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. + flow_initialized_interpolation_quality = 1; + } else { + flow_initialized_interpolation_quality = global_flags.interpolation_quality; + } + save_settings(); + // 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); @@ -61,6 +77,32 @@ MainWindow::MainWindow() ui->undo_action->setEnabled(false); ui->redo_action->setEnabled(false); + // The quality group. + QActionGroup *quality_group = new QActionGroup(ui->interpolation_menu); + quality_group->addAction(ui->quality_0_action); + quality_group->addAction(ui->quality_1_action); + quality_group->addAction(ui->quality_2_action); + quality_group->addAction(ui->quality_3_action); + quality_group->addAction(ui->quality_4_action); + if (global_flags.interpolation_quality == 0) { + ui->quality_0_action->setChecked(true); + } else if (global_flags.interpolation_quality == 1) { + ui->quality_1_action->setChecked(true); + } else if (global_flags.interpolation_quality == 2) { + ui->quality_2_action->setChecked(true); + } else if (global_flags.interpolation_quality == 3) { + ui->quality_3_action->setChecked(true); + } else if (global_flags.interpolation_quality == 4) { + ui->quality_4_action->setChecked(true); + } else { + assert(false); + } + connect(ui->quality_0_action, &QAction::toggled, bind(&MainWindow::quality_toggled, this, 0, _1)); + connect(ui->quality_1_action, &QAction::toggled, bind(&MainWindow::quality_toggled, this, 1, _1)); + connect(ui->quality_2_action, &QAction::toggled, bind(&MainWindow::quality_toggled, this, 2, _1)); + 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)); + 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;}"); @@ -374,6 +416,13 @@ void MainWindow::state_changed(const StateProto &state) } } +void MainWindow::save_settings() +{ + SettingsProto settings; + settings.set_interpolation_quality(global_flags.interpolation_quality + 1); + db.store_settings(settings); +} + void MainWindow::play_clicked() { if (playlist_clips->empty()) @@ -903,6 +952,25 @@ void MainWindow::redo_triggered() db.store_state(state); } +void MainWindow::quality_toggled(int quality, bool checked) +{ + if (!checked) { + return; + } + global_flags.interpolation_quality = quality; + if (quality != 0 && // Turning interpolation off is always possible. + quality != flow_initialized_interpolation_quality) { + QMessageBox msgbox; + msgbox.setText(QString::fromStdString( + "The interpolation quality for the main output cannot be changed at runtime, " + "except being turned completely off; it will take effect for exported files " + "only until next restart. The live output quality thus remains at " + to_string(flow_initialized_interpolation_quality) + ".")); + msgbox.exec(); + } + + save_settings(); +} + void MainWindow::highlight_camera_input(int stream_idx) { if (stream_idx == 0) { diff --git a/futatabi/mainwindow.h b/futatabi/mainwindow.h index 3612674..0fa337f 100644 --- a/futatabi/mainwindow.h +++ b/futatabi/mainwindow.h @@ -92,6 +92,7 @@ private: void defer_timer_expired(); void content_changed(); // In clip_list or play_list. void state_changed(const StateProto &state); // Called post-filtering. + void save_settings(); enum Rounding { FIRST_AT_OR_AFTER, LAST_BEFORE }; void preview_single_frame(int64_t pts, unsigned stream_idx, Rounding rounding); @@ -112,6 +113,7 @@ private: void about_triggered(); void undo_triggered(); void redo_triggered(); + void quality_toggled(int quality, bool checked); void highlight_camera_input(int stream_idx); diff --git a/futatabi/mainwindow.ui b/futatabi/mainwindow.ui index 388e7d8..733ab21 100644 --- a/futatabi/mainwindow.ui +++ b/futatabi/mainwindow.ui @@ -448,7 +448,7 @@ - &File + &Video @@ -457,6 +457,17 @@ + + + Interpolation &quality + + + + + + + + @@ -519,6 +530,46 @@ Ctrl+Y + + + true + + + No interpolation (&0) + + + + + true + + + Fastest (&1) + + + + + true + + + Default (&2) (realtime 720p on fast embedded GPUs) + + + + + true + + + Good (&3) (realtime 720p on GTX 970 or so) + + + + + true + + + Best (&4) (not realtime on any current GPU) + + diff --git a/futatabi/state.proto b/futatabi/state.proto index d76bf02..71e8cd6 100644 --- a/futatabi/state.proto +++ b/futatabi/state.proto @@ -17,3 +17,7 @@ message StateProto { ClipListProto clip_list = 1; ClipListProto play_list = 2; } + +message SettingsProto { + int32 interpolation_quality = 1; // 0 = unset, 1 = quality 0, 2 = quality 1, etc. +} diff --git a/futatabi/video_stream.cpp b/futatabi/video_stream.cpp index 8593936..30d4971 100644 --- a/futatabi/video_stream.cpp +++ b/futatabi/video_stream.cpp @@ -208,18 +208,16 @@ VideoStream::VideoStream(AVFormatContext *file_avctx) check_error(); OperatingPoint op; - if (global_flags.interpolation_quality == 0) { - // Allocate something just for simplicity; we won't be using it. + if (flow_initialized_interpolation_quality == 1) { op = operating_point1; - } else if (global_flags.interpolation_quality == 1) { - op = operating_point1; - } else if (global_flags.interpolation_quality == 2) { + } else if (flow_initialized_interpolation_quality == 2) { op = operating_point2; - } else if (global_flags.interpolation_quality == 3) { + } else if (flow_initialized_interpolation_quality == 3) { op = operating_point3; - } else if (global_flags.interpolation_quality == 4) { + } else if (flow_initialized_interpolation_quality == 4) { op = operating_point4; } else { + // Quality 0 will be changed to 1 in flags.cpp. assert(false); }