From 7cd7ca80f8b3dde1e4a1b4cdf256ade2a9af65f4 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Tue, 15 Nov 2022 10:00:41 +0100 Subject: [PATCH] UI: Use main video on the virtual camera if program This change allows the virtual camera to really output what is in the program view, some plugin interract with this view but their changes does not appear on the virtual camera. --- UI/data/locale/en-US.ini | 6 +-- UI/forms/OBSBasicVCamConfig.ui | 28 +++++------ UI/window-basic-main-outputs.cpp | 33 +++++++------ UI/window-basic-main-transitions.cpp | 12 +---- UI/window-basic-main.cpp | 68 ++++++++++++++++++++++----- UI/window-basic-main.hpp | 4 ++ UI/window-basic-vcam-config.cpp | 69 ++++++++++++++++++++-------- UI/window-basic-vcam-config.hpp | 9 +++- UI/window-basic-vcam.hpp | 8 ++-- 9 files changed, 159 insertions(+), 78 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index a81df1674..f9cbe73ff 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -740,9 +740,9 @@ Basic.Main.VirtualCamConfig="Configure Virtual Camera" Basic.VCam.VirtualCamera="Virtual Camera" Basic.VCam.OutputType="Output Type" Basic.VCam.OutputSelection="Output Selection" -Basic.VCam.Internal="Internal" -Basic.VCam.InternalDefault="Program Output (Default)" -Basic.VCam.InternalPreview="Preview Output" +Basic.VCam.OutputType.Program="Program (Default)" +Basic.VCam.OutputSelection.NoSelection="No selection for this output type" +Basic.VCam.RestartWarning="The virtual camera will be restarted to apply this change" # basic mode file menu Basic.MainMenu.File="&File" diff --git a/UI/forms/OBSBasicVCamConfig.ui b/UI/forms/OBSBasicVCamConfig.ui index 973007abd..81c1f1879 100644 --- a/UI/forms/OBSBasicVCamConfig.ui +++ b/UI/forms/OBSBasicVCamConfig.ui @@ -22,23 +22,7 @@ - - - - Basic.VCam.Internal - - - - - Basic.Scene - - - - - Basic.Main.Source - - - + @@ -50,6 +34,16 @@ + + + + Basic.VCam.RestartWarning + + + false + + + diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 27d1826cd..22256c9ca 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -301,13 +301,17 @@ bool BasicOutputHandler::StartVirtualCam() if (!main->vcamEnabled) return false; - if (!virtualCamView) + bool typeIsProgram = main->vcamConfig.type == + VCamOutputType::ProgramView; + + if (!virtualCamView && !typeIsProgram) virtualCamView = obs_view_create(); UpdateVirtualCamOutputSource(); if (!virtualCamVideo) { - virtualCamVideo = obs_view_add(virtualCamView); + virtualCamVideo = typeIsProgram ? obs_get_video() + : obs_view_add(virtualCamView); if (!virtualCamVideo) return false; @@ -361,19 +365,17 @@ void BasicOutputHandler::UpdateVirtualCamOutputSource() OBSSourceAutoRelease source; switch (main->vcamConfig.type) { - case VCamOutputType::InternalOutput: + case VCamOutputType::Invalid: + case VCamOutputType::ProgramView: DestroyVirtualCameraScene(); - switch (main->vcamConfig.internal) { - case VCamInternalType::Default: - source = obs_get_output_source(0); - break; - case VCamInternalType::Preview: - OBSSource s = main->GetCurrentSceneSource(); - obs_source_get_ref(s); - source = s.Get(); - break; - } + return; + case VCamOutputType::PreviewOutput: { + DestroyVirtualCameraScene(); + OBSSource s = main->GetCurrentSceneSource(); + obs_source_get_ref(s); + source = s.Get(); break; + } case VCamOutputType::SceneOutput: DestroyVirtualCameraScene(); source = obs_get_source_by_name(main->vcamConfig.scene.c_str()); @@ -418,6 +420,11 @@ void BasicOutputHandler::UpdateVirtualCamOutputSource() void BasicOutputHandler::DestroyVirtualCamView() { + if (main->vcamConfig.type == VCamOutputType::ProgramView) { + virtualCamVideo = nullptr; + return; + } + obs_view_remove(virtualCamView); obs_view_set_source(virtualCamView, 0, nullptr); virtualCamVideo = nullptr; diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index f54d75d40..327e425ad 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -283,10 +283,6 @@ void OBSBasic::OverrideTransition(OBSSource transition) obs_transition_swap_begin(transition, oldTransition); obs_set_output_source(0, transition); obs_transition_swap_end(transition, oldTransition); - - // Transition overrides don't raise an event so we need to call update directly - if (vcamEnabled) - outputHandler->UpdateVirtualCamOutputSource(); } } @@ -426,10 +422,6 @@ void OBSBasic::SetTransition(OBSSource transition) ui->transitionRemove->setEnabled(configurable); ui->transitionProps->setEnabled(configurable); - if (vcamEnabled && vcamConfig.type == VCamOutputType::InternalOutput && - vcamConfig.internal == VCamInternalType::Default) - outputHandler->UpdateVirtualCamOutputSource(); - if (api) api->on_event(OBS_FRONTEND_EVENT_TRANSITION_CHANGED); } @@ -697,8 +689,8 @@ void OBSBasic::SetCurrentScene(OBSSource scene, bool force) ui->scenes->blockSignals(false); if (vcamEnabled && - vcamConfig.internal == - VCamInternalType::Preview) + vcamConfig.type == + VCamOutputType::PreviewOutput) outputHandler ->UpdateVirtualCamOutputSource(); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 3493aac5d..f25fc3251 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -795,11 +795,11 @@ void OBSBasic::Save(const char *file) if (vcamEnabled) { OBSDataAutoRelease obj = obs_data_create(); - obs_data_set_int(obj, "type", (int)vcamConfig.type); + obs_data_set_int(obj, "type2", (int)vcamConfig.type); switch (vcamConfig.type) { - case VCamOutputType::InternalOutput: - obs_data_set_int(obj, "internal", - (int)vcamConfig.internal); + case VCamOutputType::Invalid: + case VCamOutputType::ProgramView: + case VCamOutputType::PreviewOutput: break; case VCamOutputType::SceneOutput: obs_data_set_string(obj, "scene", @@ -1239,9 +1239,26 @@ retryScene: OBSDataAutoRelease obj = obs_data_get_obj(data, "virtual-camera"); - vcamConfig.type = (VCamOutputType)obs_data_get_int(obj, "type"); - vcamConfig.internal = - (VCamInternalType)obs_data_get_int(obj, "internal"); + vcamConfig.type = + (VCamOutputType)obs_data_get_int(obj, "type2"); + if (vcamConfig.type == VCamOutputType::Invalid) + vcamConfig.type = + (VCamOutputType)obs_data_get_int(obj, "type"); + + if (vcamConfig.type == VCamOutputType::Invalid) { + VCamInternalType internal = + (VCamInternalType)obs_data_get_int(obj, + "internal"); + + switch (internal) { + case VCamInternalType::Default: + vcamConfig.type = VCamOutputType::ProgramView; + break; + case VCamInternalType::Preview: + vcamConfig.type = VCamOutputType::PreviewOutput; + break; + } + } vcamConfig.scene = obs_data_get_string(obj, "scene"); vcamConfig.source = obs_data_get_string(obj, "source"); } @@ -4873,8 +4890,7 @@ void OBSBasic::ClearSceneData() /* Reset VCam to default to clear its private scene and any references * it holds. It will be reconfigured during loading. */ if (vcamEnabled) { - vcamConfig.type = VCamOutputType::InternalOutput; - vcamConfig.internal = VCamInternalType::Default; + vcamConfig.type = VCamOutputType::ProgramView; outputHandler->UpdateVirtualCamOutputSource(); } @@ -5300,8 +5316,7 @@ void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current, SetCurrentScene(source); - if (vcamEnabled && vcamConfig.type == VCamOutputType::InternalOutput && - vcamConfig.internal == VCamInternalType::Preview) + if (vcamEnabled && vcamConfig.type == VCamOutputType::PreviewOutput) outputHandler->UpdateVirtualCamOutputSource(); if (api) @@ -7935,6 +7950,13 @@ void OBSBasic::OnVirtualCamStop(int) blog(LOG_INFO, VIRTUAL_CAM_STOP); OnDeactivate(); + + if (!restartingVCam) + return; + + /* Restarting needs to be delayed to make sure that the virtual camera + * implementation is stopped and avoid race condition. */ + QTimer::singleShot(100, this, &OBSBasic::RestartingVirtualCam); } void OBSBasic::on_streamButton_clicked() @@ -8089,10 +8111,13 @@ void OBSBasic::VCamButtonClicked() void OBSBasic::VCamConfigButtonClicked() { - OBSBasicVCamConfig dialog(vcamConfig, this); + OBSBasicVCamConfig dialog(vcamConfig, outputHandler->VirtualCamActive(), + this); connect(&dialog, &OBSBasicVCamConfig::Accepted, this, &OBSBasic::UpdateVirtualCamConfig); + connect(&dialog, &OBSBasicVCamConfig::AcceptedAndRestart, this, + &OBSBasic::RestartVirtualCam); dialog.exec(); } @@ -8104,6 +8129,25 @@ void OBSBasic::UpdateVirtualCamConfig(const VCamConfig &config) outputHandler->UpdateVirtualCamOutputSource(); } +void OBSBasic::RestartVirtualCam(const VCamConfig &config) +{ + restartingVCam = true; + + StopVirtualCam(); + + vcamConfig = config; +} + +void OBSBasic::RestartingVirtualCam() +{ + if (!restartingVCam) + return; + + outputHandler->UpdateVirtualCamOutputSource(); + StartVirtualCam(); + restartingVCam = false; +} + void OBSBasic::on_settingsButton_clicked() { on_action_Settings_triggered(); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 44c5aa513..86509213d 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -656,6 +656,8 @@ private: void UpdatePreviewOverflowSettings(); + bool restartingVCam = false; + public slots: void DeferSaveBegin(); void DeferSaveEnd(); @@ -830,6 +832,8 @@ private slots: void ResetProxyStyleSliders(); void UpdateVirtualCamConfig(const VCamConfig &config); + void RestartVirtualCam(const VCamConfig &config); + void RestartingVirtualCam(); private: /* OBS Callbacks */ diff --git a/UI/window-basic-vcam-config.cpp b/UI/window-basic-vcam-config.cpp index cd3b2eca3..854006d09 100644 --- a/UI/window-basic-vcam-config.cpp +++ b/UI/window-basic-vcam-config.cpp @@ -5,35 +5,55 @@ #include #include +#include + OBSBasicVCamConfig::OBSBasicVCamConfig(const VCamConfig &_config, - QWidget *parent) - : config(_config), QDialog(parent), ui(new Ui::OBSBasicVCamConfig) + bool _vcamActive, QWidget *parent) + : config(_config), + vcamActive(_vcamActive), + activeType(_config.type), + QDialog(parent), + ui(new Ui::OBSBasicVCamConfig) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); ui->setupUi(this); - ui->outputType->setCurrentIndex(config.type); - OutputTypeChanged(config.type); + ui->outputType->addItem(QTStr("Basic.VCam.OutputType.Program"), + (int)VCamOutputType::ProgramView); + ui->outputType->addItem(QTStr("Preview"), + (int)VCamOutputType::PreviewOutput); + ui->outputType->addItem(QTStr("Basic.Scene"), + (int)VCamOutputType::SceneOutput); + ui->outputType->addItem(QTStr("Basic.Main.Source"), + (int)VCamOutputType::SourceOutput); + + ui->outputType->setCurrentIndex( + ui->outputType->findData((int)config.type)); + OutputTypeChanged(); connect(ui->outputType, SIGNAL(currentIndexChanged(int)), this, - SLOT(OutputTypeChanged(int))); + SLOT(OutputTypeChanged())); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &OBSBasicVCamConfig::UpdateConfig); } -void OBSBasicVCamConfig::OutputTypeChanged(int type) +void OBSBasicVCamConfig::OutputTypeChanged() { + VCamOutputType type = + (VCamOutputType)ui->outputType->currentData().toInt(); + ui->outputSelection->setDisabled(false); + auto list = ui->outputSelection; list->clear(); - switch ((VCamOutputType)type) { - case VCamOutputType::InternalOutput: - list->addItem(QTStr("Basic.VCam.InternalDefault")); - list->addItem(QTStr("Basic.VCam.InternalPreview")); - list->setCurrentIndex(config.internal); + switch (type) { + case VCamOutputType::Invalid: + case VCamOutputType::ProgramView: + case VCamOutputType::PreviewOutput: + ui->outputSelection->setDisabled(true); + list->addItem(QTStr("Basic.VCam.OutputSelection.NoSelection")); break; - case VCamOutputType::SceneOutput: { // Scenes in default order BPtr scenes = obs_frontend_get_scene_names(); @@ -45,7 +65,6 @@ void OBSBasicVCamConfig::OutputTypeChanged(int type) } break; } - case VCamOutputType::SourceOutput: { // Sources in alphabetical order std::vector sources; @@ -81,15 +100,25 @@ void OBSBasicVCamConfig::OutputTypeChanged(int type) break; } } + + if (!vcamActive) + return; + + requireRestart = (activeType == VCamOutputType::ProgramView && + type != VCamOutputType::ProgramView) || + (activeType != VCamOutputType::ProgramView && + type == VCamOutputType::ProgramView); + + ui->warningLabel->setVisible(requireRestart); } void OBSBasicVCamConfig::UpdateConfig() { - VCamOutputType type = (VCamOutputType)ui->outputType->currentIndex(); + VCamOutputType type = + (VCamOutputType)ui->outputType->currentData().toInt(); switch (type) { - case VCamOutputType::InternalOutput: - config.internal = - (VCamInternalType)ui->outputSelection->currentIndex(); + case VCamOutputType::ProgramView: + case VCamOutputType::PreviewOutput: break; case VCamOutputType::SceneOutput: config.scene = ui->outputSelection->currentText().toStdString(); @@ -105,5 +134,9 @@ void OBSBasicVCamConfig::UpdateConfig() config.type = type; - emit Accepted(config); + if (requireRestart) { + emit AcceptedAndRestart(config); + } else { + emit Accepted(config); + } } diff --git a/UI/window-basic-vcam-config.hpp b/UI/window-basic-vcam-config.hpp index 2c48b45be..79cecb132 100644 --- a/UI/window-basic-vcam-config.hpp +++ b/UI/window-basic-vcam-config.hpp @@ -15,12 +15,16 @@ class OBSBasicVCamConfig : public QDialog { VCamConfig config; + bool vcamActive; + VCamOutputType activeType; + bool requireRestart; + public: - explicit OBSBasicVCamConfig(const VCamConfig &config, + explicit OBSBasicVCamConfig(const VCamConfig &config, bool VCamActive, QWidget *parent = 0); private slots: - void OutputTypeChanged(int type); + void OutputTypeChanged(); void UpdateConfig(); private: @@ -28,4 +32,5 @@ private: signals: void Accepted(const VCamConfig &config); + void AcceptedAndRestart(const VCamConfig &config); }; diff --git a/UI/window-basic-vcam.hpp b/UI/window-basic-vcam.hpp index 0b538006d..464313412 100644 --- a/UI/window-basic-vcam.hpp +++ b/UI/window-basic-vcam.hpp @@ -3,19 +3,21 @@ #include enum VCamOutputType { - InternalOutput, + Invalid, SceneOutput, SourceOutput, + ProgramView, + PreviewOutput, }; +// Kept for config upgrade enum VCamInternalType { Default, Preview, }; struct VCamConfig { - VCamOutputType type = VCamOutputType::InternalOutput; - VCamInternalType internal = VCamInternalType::Default; + VCamOutputType type = VCamOutputType::ProgramView; std::string scene; std::string source; };