diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index f4d2f5a72..0201b3d3c 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -326,6 +326,7 @@ void Config::ReadValues() { UISettings::values.movie_record_path = ReadSetting("movieRecordPath").toString(); UISettings::values.movie_playback_path = ReadSetting("moviePlaybackPath").toString(); UISettings::values.screenshot_path = ReadSetting("screenshotPath").toString(); + UISettings::values.video_dumping_path = ReadSetting("videoDumpingPath").toString(); UISettings::values.game_dir_deprecated = ReadSetting("gameListRootDir", ".").toString(); UISettings::values.game_dir_deprecated_deepscan = ReadSetting("gameListDeepScan", false).toBool(); @@ -594,6 +595,7 @@ void Config::SaveValues() { WriteSetting("movieRecordPath", UISettings::values.movie_record_path); WriteSetting("moviePlaybackPath", UISettings::values.movie_playback_path); WriteSetting("screenshotPath", UISettings::values.screenshot_path); + WriteSetting("videoDumpingPath", UISettings::values.video_dumping_path); qt_config->beginWriteArray("gamedirs"); for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { qt_config->setArrayIndex(i); diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 0ae154b89..29aa75d1d 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -57,6 +57,7 @@ #include "common/scm_rev.h" #include "common/scope_exit.h" #include "core/core.h" +#include "core/dumping/backend.h" #include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/archive_source_sd_savedata.h" #include "core/frontend/applets/default_applets.h" @@ -67,6 +68,8 @@ #include "core/movie.h" #include "core/settings.h" #include "game_list_p.h" +#include "video_core/renderer_base.h" +#include "video_core/video_core.h" #ifdef USE_DISCORD_PRESENCE #include "citra_qt/discord_impl.h" @@ -601,6 +604,17 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Capture_Screenshot, &QAction::triggered, this, &GMainWindow::OnCaptureScreenshot); +#ifndef ENABLE_FFMPEG + ui.action_Dump_Video->setEnabled(false); +#endif + connect(ui.action_Dump_Video, &QAction::triggered, [this] { + if (ui.action_Dump_Video->isChecked()) { + OnStartVideoDumping(); + } else { + OnStopVideoDumping(); + } + }); + // Help connect(ui.action_Open_Citra_Folder, &QAction::triggered, this, &GMainWindow::OnOpenCitraFolder); @@ -862,10 +876,25 @@ void GMainWindow::BootGame(const QString& filename) { if (ui.action_Fullscreen->isChecked()) { ShowFullscreen(); } + + if (video_dumping_on_start) { + Layout::FramebufferLayout layout{ + Layout::FrameLayoutFromResolutionScale(VideoCore::GetResolutionScaleFactor())}; + Core::System::GetInstance().VideoDumper().StartDumping(video_dumping_path.toStdString(), + "webm", layout); + video_dumping_on_start = false; + video_dumping_path.clear(); + } OnStartGame(); } void GMainWindow::ShutdownGame() { + if (Core::System::GetInstance().VideoDumper().IsDumping()) { + game_shutdown_delayed = true; + OnStopVideoDumping(); + return; + } + discord_rpc->Pause(); OnStopRecordingPlayback(); emu_thread->RequestStop(); @@ -1595,6 +1624,51 @@ void GMainWindow::OnCaptureScreenshot() { OnStartGame(); } +void GMainWindow::OnStartVideoDumping() { + const QString path = QFileDialog::getSaveFileName( + this, tr("Save Video"), UISettings::values.video_dumping_path, tr("WebM Videos (*.webm)")); + if (path.isEmpty()) { + ui.action_Dump_Video->setChecked(false); + return; + } + UISettings::values.video_dumping_path = QFileInfo(path).path(); + if (emulation_running) { + Layout::FramebufferLayout layout{ + Layout::FrameLayoutFromResolutionScale(VideoCore::GetResolutionScaleFactor())}; + Core::System::GetInstance().VideoDumper().StartDumping(path.toStdString(), "webm", layout); + } else { + video_dumping_on_start = true; + video_dumping_path = path; + } +} + +void GMainWindow::OnStopVideoDumping() { + ui.action_Dump_Video->setChecked(false); + + if (video_dumping_on_start) { + video_dumping_on_start = false; + video_dumping_path.clear(); + } else { + const bool was_dumping = Core::System::GetInstance().VideoDumper().IsDumping(); + if (!was_dumping) + return; + OnPauseGame(); + + auto future = + QtConcurrent::run([] { Core::System::GetInstance().VideoDumper().StopDumping(); }); + auto* future_watcher = new QFutureWatcher(this); + connect(future_watcher, &QFutureWatcher::finished, this, [this] { + if (game_shutdown_delayed) { + game_shutdown_delayed = false; + ShutdownGame(); + } else { + OnStartGame(); + } + }); + future_watcher->setFuture(future); + } +} + void GMainWindow::UpdateStatusBar() { if (emu_thread == nullptr) { status_bar_update_timer.stop(); diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 068a8da8a..780589141 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -184,6 +184,8 @@ private slots: void OnPlayMovie(); void OnStopRecordingPlayback(); void OnCaptureScreenshot(); + void OnStartVideoDumping(); + void OnStopVideoDumping(); void OnCoreError(Core::System::ResultStatus, std::string); /// Called whenever a user selects Help->About Citra void OnMenuAboutCitra(); @@ -230,6 +232,12 @@ private: bool movie_record_on_start = false; QString movie_record_path; + // Video dumping + bool video_dumping_on_start = false; + QString video_dumping_path; + // Whether game shutdown is delayed due to video dumping + bool game_shutdown_delayed = false; + // Debugger panes ProfilerWidget* profilerWidget; MicroProfileDialog* microProfileDialog; diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 486585f24..54cd98e8f 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -158,6 +158,7 @@ + @@ -336,6 +337,14 @@ Capture Screenshot + + + true + + + Dump Video + + true diff --git a/src/citra_qt/ui_settings.h b/src/citra_qt/ui_settings.h index 47e4b1b32..80cab3689 100644 --- a/src/citra_qt/ui_settings.h +++ b/src/citra_qt/ui_settings.h @@ -93,6 +93,7 @@ struct Values { QString movie_record_path; QString movie_playback_path; QString screenshot_path; + QString video_dumping_path; QString game_dir_deprecated; bool game_dir_deprecated_deepscan; QList game_dirs;