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; };