UI: Refactor Virtual Camera source selector dialog

This commit is contained in:
tytan652 2022-08-22 16:04:13 +02:00 committed by Jim
parent dc5813c947
commit 501a3e926d
9 changed files with 238 additions and 252 deletions

View file

@ -259,6 +259,7 @@ target_sources(
window-basic-transform.cpp
window-basic-transform.hpp
window-basic-preview.hpp
window-basic-vcam.hpp
window-basic-vcam-config.cpp
window-basic-vcam-config.hpp
window-dock.cpp

View file

@ -181,7 +181,7 @@ static void OBSStopVirtualCam(void *data, calldata_t *params)
Q_ARG(int, code));
obs_output_set_media(output->virtualCam, nullptr, nullptr);
OBSBasicVCamConfig::StopVideo();
output->DestroyVirtualCamView();
}
/* ------------------------------------------------------------------------ */
@ -229,23 +229,30 @@ inline BasicOutputHandler::BasicOutputHandler(OBSBasic *main_) : main(main_)
bool BasicOutputHandler::StartVirtualCam()
{
if (main->vcamEnabled) {
video_t *video = OBSBasicVCamConfig::StartVideo();
if (!video)
if (!main->vcamEnabled)
return false;
if (!virtualCamView)
virtualCamView = obs_view_create();
UpdateVirtualCamOutputSource();
if (!virtualCamVideo) {
virtualCamVideo = obs_view_add(virtualCamView);
if (!virtualCamVideo)
return false;
obs_output_set_media(virtualCam, video, obs_get_audio());
if (!Active())
SetupOutputs();
bool success = obs_output_start(virtualCam);
if (!success)
OBSBasicVCamConfig::StopVideo();
return success;
}
return false;
obs_output_set_media(virtualCam, virtualCamVideo, obs_get_audio());
if (!Active())
SetupOutputs();
bool success = obs_output_start(virtualCam);
if (!success)
DestroyVirtualCamView();
return success;
}
void BasicOutputHandler::StopVirtualCam()
@ -263,6 +270,85 @@ bool BasicOutputHandler::VirtualCamActive() const
return false;
}
void BasicOutputHandler::UpdateVirtualCamOutputSource()
{
if (!main->vcamEnabled || !virtualCamView)
return;
OBSSourceAutoRelease source;
switch (main->vcamConfig.type) {
case VCamOutputType::InternalOutput:
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;
}
break;
case VCamOutputType::SceneOutput:
source = obs_get_source_by_name(main->vcamConfig.scene.c_str());
break;
case VCamOutputType::SourceOutput:
OBSSource s =
obs_get_source_by_name(main->vcamConfig.source.c_str());
if (!vCamSourceScene)
vCamSourceScene =
obs_scene_create_private("vcam_source");
source = obs_source_get_ref(
obs_scene_get_source(vCamSourceScene));
if (vCamSourceSceneItem &&
(obs_sceneitem_get_source(vCamSourceSceneItem) != s)) {
obs_sceneitem_remove(vCamSourceSceneItem);
vCamSourceSceneItem = nullptr;
}
if (!vCamSourceSceneItem) {
vCamSourceSceneItem = obs_scene_add(vCamSourceScene, s);
obs_source_release(s);
obs_sceneitem_set_bounds_type(vCamSourceSceneItem,
OBS_BOUNDS_SCALE_INNER);
obs_sceneitem_set_bounds_alignment(vCamSourceSceneItem,
OBS_ALIGN_CENTER);
const struct vec2 size = {
(float)obs_source_get_width(source),
(float)obs_source_get_height(source),
};
obs_sceneitem_set_bounds(vCamSourceSceneItem, &size);
}
break;
}
OBSSourceAutoRelease current = obs_view_get_source(virtualCamView, 0);
if (source != current)
obs_view_set_source(virtualCamView, 0, source);
}
void BasicOutputHandler::DestroyVirtualCamView()
{
obs_view_remove(virtualCamView);
obs_view_set_source(virtualCamView, 0, nullptr);
virtualCamVideo = nullptr;
obs_view_destroy(virtualCamView);
virtualCamView = nullptr;
if (!vCamSourceScene)
return;
obs_scene_release(vCamSourceScene);
vCamSourceScene = nullptr;
vCamSourceSceneItem = nullptr;
}
/* ------------------------------------------------------------------------ */
struct SimpleOutput : BasicOutputHandler {

View file

@ -16,6 +16,11 @@ struct BasicOutputHandler {
bool virtualCamActive = false;
OBSBasic *main;
obs_view_t *virtualCamView = nullptr;
video_t *virtualCamVideo = nullptr;
obs_scene_t *vCamSourceScene = nullptr;
obs_sceneitem_t *vCamSourceSceneItem = nullptr;
std::string outputType;
std::string lastError;
@ -57,6 +62,9 @@ struct BasicOutputHandler {
virtual void Update() = 0;
virtual void SetupOutputs() = 0;
virtual void UpdateVirtualCamOutputSource();
virtual void DestroyVirtualCamView();
inline bool Active() const
{
return streamingActive || recordingActive || delayActive ||

View file

@ -21,6 +21,7 @@
#include <QMessageBox>
#include <util/dstr.hpp>
#include "window-basic-main.hpp"
#include "window-basic-main-outputs.hpp"
#include "window-basic-vcam-config.hpp"
#include "display-helpers.hpp"
#include "window-namedialog.hpp"
@ -286,7 +287,8 @@ void OBSBasic::OverrideTransition(OBSSource transition)
obs_transition_swap_end(transition, oldTransition);
// Transition overrides don't raise an event so we need to call update directly
OBSBasicVCamConfig::UpdateOutputSource();
if (vcamEnabled)
outputHandler->UpdateVirtualCamOutputSource();
}
}
@ -428,6 +430,9 @@ void OBSBasic::SetTransition(OBSSource transition)
ui->transitionRemove->setEnabled(configurable);
ui->transitionProps->setEnabled(configurable);
if (vcamEnabled && vcamConfig.internal == VCamInternalType::Default)
outputHandler->UpdateVirtualCamOutputSource();
if (api)
api->on_event(OBS_FRONTEND_EVENT_TRANSITION_CHANGED);
}
@ -695,6 +700,13 @@ void OBSBasic::SetCurrentScene(OBSSource scene, bool force)
currentScene = itemScene.Get();
ui->scenes->setCurrentItem(item);
ui->scenes->blockSignals(false);
if (vcamEnabled &&
vcamConfig.internal ==
VCamInternalType::Preview)
outputHandler
->UpdateVirtualCamOutputSource();
if (api)
api->on_event(
OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);

View file

@ -763,8 +763,27 @@ void OBSBasic::Save(const char *file)
obs_data_set_double(saveData, "scaling_off_y",
ui->preview->GetScrollY());
if (vcamEnabled)
OBSBasicVCamConfig::SaveData(saveData, true);
if (vcamEnabled) {
OBSDataAutoRelease obj = obs_data_create();
obs_data_set_int(obj, "type", (int)vcamConfig.type);
switch (vcamConfig.type) {
case VCamOutputType::InternalOutput:
obs_data_set_int(obj, "internal",
(int)vcamConfig.internal);
break;
case VCamOutputType::SceneOutput:
obs_data_set_string(obj, "scene",
vcamConfig.scene.c_str());
break;
case VCamOutputType::SourceOutput:
obs_data_set_string(obj, "source",
vcamConfig.source.c_str());
break;
}
obs_data_set_obj(saveData, "virtual-camera", obj);
}
if (api) {
OBSDataAutoRelease moduleObj = obs_data_create();
@ -1178,8 +1197,16 @@ retryScene:
ui->preview->SetFixedScaling(fixedScaling);
emit ui->preview->DisplayResized();
if (vcamEnabled)
OBSBasicVCamConfig::SaveData(data, false);
if (vcamEnabled) {
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.scene = obs_data_get_string(obj, "scene");
vcamConfig.source = obs_data_get_string(obj, "source");
}
/* ---------------------- */
@ -1225,6 +1252,9 @@ retryScene:
disableSaving--;
if (vcamEnabled && vcamConfig.internal == VCamInternalType::Preview)
outputHandler->UpdateVirtualCamOutputSource();
if (api) {
api->on_event(OBS_FRONTEND_EVENT_SCENE_CHANGED);
api->on_event(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);
@ -1696,8 +1726,6 @@ void OBSBasic::ReplayBufferClicked()
void OBSBasic::AddVCamButton()
{
OBSBasicVCamConfig::Init();
vcamButton = new ControlsSplitButton(
QTStr("Basic.Main.StartVirtualCam"), "vcamButton",
&OBSBasic::VCamButtonClicked);
@ -2752,8 +2780,6 @@ OBSBasic::~OBSBasic()
delete cef;
cef = nullptr;
#endif
OBSBasicVCamConfig::DestroyView();
}
void OBSBasic::SaveProjectNow()
@ -5098,6 +5124,9 @@ void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current,
SetCurrentScene(source);
if (vcamEnabled && vcamConfig.internal == VCamInternalType::Preview)
outputHandler->UpdateVirtualCamOutputSource();
if (api)
api->on_event(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);
@ -7855,8 +7884,19 @@ void OBSBasic::VCamButtonClicked()
void OBSBasic::VCamConfigButtonClicked()
{
OBSBasicVCamConfig config(this);
config.exec();
OBSBasicVCamConfig dialog(vcamConfig, this);
connect(&dialog, &OBSBasicVCamConfig::Accepted, this,
&OBSBasic::UpdateVirtualCamConfig);
dialog.exec();
}
void OBSBasic::UpdateVirtualCamConfig(const VCamConfig &config)
{
vcamConfig = config;
outputHandler->UpdateVirtualCamOutputSource();
}
void OBSBasic::on_settingsButton_clicked()

View file

@ -28,6 +28,7 @@
#include <memory>
#include "window-main.hpp"
#include "window-basic-interaction.hpp"
#include "window-basic-vcam.hpp"
#include "window-basic-properties.hpp"
#include "window-basic-transform.hpp"
#include "window-basic-adv-audio.hpp"
@ -51,6 +52,7 @@ class QMessageBox;
class QListWidgetItem;
class VolControl;
class OBSBasicStats;
class OBSBasicVCamConfig;
#include "ui_OBSBasic.h"
#include "ui_ColorSelect.h"
@ -309,6 +311,7 @@ private:
QPointer<ControlsSplitButton> vcamButton;
bool vcamEnabled = false;
VCamConfig vcamConfig;
QScopedPointer<QSystemTrayIcon> trayIcon;
QPointer<QAction> sysTrayStream;
@ -819,6 +822,8 @@ private slots:
void LockVolumeControl(bool lock);
void ResetProxyStyleSliders();
void UpdateVirtualCamConfig(const VCamConfig &config);
private:
/* OBS Callbacks */
static void SceneReordered(void *data, calldata_t *params);

View file

@ -5,43 +5,21 @@
#include <util/util.hpp>
#include <util/platform.h>
enum class VCamOutputType {
Internal,
Scene,
Source,
};
enum class VCamInternalType {
Default,
Preview,
};
struct VCamConfig {
VCamOutputType type = VCamOutputType::Internal;
VCamInternalType internal = VCamInternalType::Default;
std::string scene;
std::string source;
};
static VCamConfig *vCamConfig = nullptr;
OBSBasicVCamConfig::OBSBasicVCamConfig(QWidget *parent)
: QDialog(parent), ui(new Ui::OBSBasicVCamConfig)
OBSBasicVCamConfig::OBSBasicVCamConfig(const VCamConfig &_config,
QWidget *parent)
: config(_config), QDialog(parent), ui(new Ui::OBSBasicVCamConfig)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
ui->setupUi(this);
auto type = (int)vCamConfig->type;
ui->outputType->setCurrentIndex(type);
OutputTypeChanged(type);
connect(ui->outputType,
static_cast<void (QComboBox::*)(int)>(
&QComboBox::currentIndexChanged),
this, &OBSBasicVCamConfig::OutputTypeChanged);
ui->outputType->setCurrentIndex(config.type);
OutputTypeChanged(config.type);
connect(ui->outputType, SIGNAL(currentIndexChanged(int)), this,
SLOT(OutputTypeChanged(int)));
connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
&OBSBasicVCamConfig::Save);
&OBSBasicVCamConfig::UpdateConfig);
}
void OBSBasicVCamConfig::OutputTypeChanged(int type)
@ -50,25 +28,25 @@ void OBSBasicVCamConfig::OutputTypeChanged(int type)
list->clear();
switch ((VCamOutputType)type) {
case VCamOutputType::Internal:
case VCamOutputType::InternalOutput:
list->addItem(QTStr("Basic.VCam.InternalDefault"));
list->addItem(QTStr("Basic.VCam.InternalPreview"));
list->setCurrentIndex((int)vCamConfig->internal);
list->setCurrentIndex(config.internal);
break;
case VCamOutputType::Scene: {
case VCamOutputType::SceneOutput: {
// Scenes in default order
BPtr<char *> scenes = obs_frontend_get_scene_names();
for (char **temp = scenes; *temp; temp++) {
list->addItem(*temp);
if (vCamConfig->scene.compare(*temp) == 0)
if (config.scene.compare(*temp) == 0)
list->setCurrentIndex(list->count() - 1);
}
break;
}
case VCamOutputType::Source: {
case VCamOutputType::SourceOutput: {
// Sources in alphabetical order
std::vector<std::string> sources;
auto AddSource = [&](obs_source_t *source) {
@ -97,7 +75,7 @@ void OBSBasicVCamConfig::OutputTypeChanged(int type)
for (auto &&source : sources) {
list->addItem(source.c_str());
if (vCamConfig->source == source)
if (config.source == source)
list->setCurrentIndex(list->count() - 1);
}
break;
@ -105,193 +83,27 @@ void OBSBasicVCamConfig::OutputTypeChanged(int type)
}
}
void OBSBasicVCamConfig::Save()
void OBSBasicVCamConfig::UpdateConfig()
{
auto type = (VCamOutputType)ui->outputType->currentIndex();
auto out = ui->outputSelection;
VCamOutputType type = (VCamOutputType)ui->outputType->currentIndex();
switch (type) {
case VCamOutputType::Internal:
vCamConfig->internal = (VCamInternalType)out->currentIndex();
case VCamOutputType::InternalOutput:
config.internal =
(VCamInternalType)ui->outputSelection->currentIndex();
break;
case VCamOutputType::Scene:
vCamConfig->scene = out->currentText().toStdString();
case VCamOutputType::SceneOutput:
config.scene = ui->outputSelection->currentText().toStdString();
break;
case VCamOutputType::Source:
vCamConfig->source = out->currentText().toStdString();
case VCamOutputType::SourceOutput:
config.source =
ui->outputSelection->currentText().toStdString();
break;
default:
// unknown value, don't save type
return;
}
vCamConfig->type = type;
config.type = type;
// If already running just update the source
if (obs_frontend_virtualcam_active())
UpdateOutputSource();
}
void OBSBasicVCamConfig::SaveData(obs_data_t *data, bool saving)
{
if (saving) {
OBSDataAutoRelease obj = obs_data_create();
obs_data_set_int(obj, "type", (int)vCamConfig->type);
obs_data_set_int(obj, "internal", (int)vCamConfig->internal);
obs_data_set_string(obj, "scene", vCamConfig->scene.c_str());
obs_data_set_string(obj, "source", vCamConfig->source.c_str());
obs_data_set_obj(data, "virtual-camera", obj);
} else {
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->scene = obs_data_get_string(obj, "scene");
vCamConfig->source = obs_data_get_string(obj, "source");
}
}
static void EventCallback(enum obs_frontend_event event, void *)
{
if (vCamConfig->type != VCamOutputType::Internal)
return;
// Update output source if the preview scene changes
// or if the default transition is changed
switch (event) {
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
if (vCamConfig->internal != VCamInternalType::Preview)
return;
break;
case OBS_FRONTEND_EVENT_TRANSITION_CHANGED:
if (vCamConfig->internal != VCamInternalType::Default)
return;
break;
default:
return;
}
OBSBasicVCamConfig::UpdateOutputSource();
}
static auto staticConfig = VCamConfig{};
void OBSBasicVCamConfig::Init()
{
if (vCamConfig)
return;
vCamConfig = &staticConfig;
obs_frontend_add_event_callback(EventCallback, nullptr);
}
static obs_view_t *view = nullptr;
static video_t *video = nullptr;
static obs_scene_t *sourceScene = nullptr;
static obs_sceneitem_t *sourceSceneItem = nullptr;
video_t *OBSBasicVCamConfig::StartVideo()
{
if (!view)
view = obs_view_create();
UpdateOutputSource();
if (!video)
video = obs_view_add(view);
return video;
}
void OBSBasicVCamConfig::StopVideo()
{
obs_view_remove(view);
obs_view_set_source(view, 0, nullptr);
video = nullptr;
if (sourceScene) {
obs_scene_release(sourceScene);
sourceScene = nullptr;
sourceSceneItem = nullptr;
}
}
void OBSBasicVCamConfig::DestroyView()
{
StopVideo();
obs_view_destroy(view);
view = nullptr;
}
void OBSBasicVCamConfig::UpdateOutputSource()
{
if (!view)
return;
obs_source_t *source = nullptr;
switch ((VCamOutputType)vCamConfig->type) {
case VCamOutputType::Internal:
switch (vCamConfig->internal) {
case VCamInternalType::Default:
source = obs_get_output_source(0);
break;
case VCamInternalType::Preview:
OBSSource s = OBSBasic::Get()->GetCurrentSceneSource();
obs_source_get_ref(s);
source = s;
break;
}
break;
case VCamOutputType::Scene:
source = obs_get_source_by_name(vCamConfig->scene.c_str());
break;
case VCamOutputType::Source:
auto rawSource =
obs_get_source_by_name(vCamConfig->source.c_str());
if (!rawSource)
break;
// Use a scene transform to fit the source size to the canvas
if (!sourceScene)
sourceScene = obs_scene_create_private(nullptr);
source = obs_source_get_ref(obs_scene_get_source(sourceScene));
if (sourceSceneItem) {
if (obs_sceneitem_get_source(sourceSceneItem) !=
rawSource) {
obs_sceneitem_remove(sourceSceneItem);
sourceSceneItem = nullptr;
}
}
if (!sourceSceneItem) {
sourceSceneItem = obs_scene_add(sourceScene, rawSource);
obs_source_release(rawSource);
obs_sceneitem_set_bounds_type(sourceSceneItem,
OBS_BOUNDS_SCALE_INNER);
obs_sceneitem_set_bounds_alignment(sourceSceneItem,
OBS_ALIGN_CENTER);
const struct vec2 size = {
(float)obs_source_get_width(source),
(float)obs_source_get_height(source),
};
obs_sceneitem_set_bounds(sourceSceneItem, &size);
}
break;
}
auto current = obs_view_get_source(view, 0);
if (source != current)
obs_view_set_source(view, 0, source);
obs_source_release(source);
obs_source_release(current);
emit Accepted(config);
}

View file

@ -4,27 +4,28 @@
#include <QDialog>
#include <memory>
#include "window-basic-vcam.hpp"
#include "ui_OBSBasicVCamConfig.h"
struct VCamConfig;
class OBSBasicVCamConfig : public QDialog {
Q_OBJECT
VCamConfig config;
public:
static void Init();
static video_t *StartVideo();
static void StopVideo();
static void DestroyView();
static void UpdateOutputSource();
static void SaveData(obs_data_t *data, bool saving);
explicit OBSBasicVCamConfig(QWidget *parent = 0);
explicit OBSBasicVCamConfig(const VCamConfig &config,
QWidget *parent = 0);
private slots:
void OutputTypeChanged(int type);
void Save();
void UpdateConfig();
private:
std::unique_ptr<Ui::OBSBasicVCamConfig> ui;
signals:
void Accepted(const VCamConfig &config);
};

21
UI/window-basic-vcam.hpp Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include <string>
enum VCamOutputType {
InternalOutput,
SceneOutput,
SourceOutput,
};
enum VCamInternalType {
Default,
Preview,
};
struct VCamConfig {
VCamOutputType type = VCamOutputType::InternalOutput;
VCamInternalType internal = VCamInternalType::Default;
std::string scene;
std::string source;
};