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