From c05dd40c4c8a517f25213046df145d61f8e2691d Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Sun, 25 May 2025 15:58:35 +0200 Subject: [PATCH] Add option to change various OBS video settings --- data/locale/en-US.ini | 24 +- plugins/base/CMakeLists.txt | 2 + plugins/base/macro-action-obs-settings.cpp | 387 +++++++++++++++++++++ plugins/base/macro-action-obs-settings.hpp | 90 +++++ 4 files changed, 499 insertions(+), 4 deletions(-) create mode 100644 plugins/base/macro-action-obs-settings.cpp create mode 100644 plugins/base/macro-action-obs-settings.hpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 33a49dd1..d42dd274 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -1238,6 +1238,18 @@ AdvSceneSwitcher.action.mqtt.topic="Topic:" AdvSceneSwitcher.action.mqtt.qos="QoS:" AdvSceneSwitcher.action.mqtt.retain="Retained message:" AdvSceneSwitcher.action.mqtt.layout="Send message to{{connection}}" +AdvSceneSwitcher.action.obsSetting="OBS setting" +AdvSceneSwitcher.action.obsSetting.action.setFPSType="Set FPS selection type to" +AdvSceneSwitcher.action.obsSetting.action.setFPSCommonValue="Set FPS common selection to" +AdvSceneSwitcher.action.obsSetting.action.setFPSIntegerValue="Set FPS integer selection to" +AdvSceneSwitcher.action.obsSetting.action.setFPSFractionNumeratorValue="Set fractional FPS numerator selection to" +AdvSceneSwitcher.action.obsSetting.action.setFPSFractionDenominatorValue="Set fractional FPS denominator selection to" +AdvSceneSwitcher.action.obsSetting.action.setBaseCanvasX="Set base resolution X value" +AdvSceneSwitcher.action.obsSetting.action.setBaseCanvasY="Set base resolution Y value" +AdvSceneSwitcher.action.obsSetting.action.setOutputCanvasX="Set output resolution X value" +AdvSceneSwitcher.action.obsSetting.action.setOutputCanvasY="Set output resolution Y value" +AdvSceneSwitcher.action.obsSetting.getCurrentValue="Get current value" +AdvSceneSwitcher.action.obsSettings.layout="{{actions}}{{fpsType}}{{fpsIntValue}}{{fpsStringValue}}{{canvasSizeValue}}{{getCurrentValue}}" # Hotkey AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher" @@ -2250,13 +2262,12 @@ AdvSceneSwitcher.script.timeout="Script timeout:{{timeout}}" # This secion is copied from the OBS locale files -# commonly shared locale +# OBS commonly shared locale Browse="Browse" Show="Show" Hide="Hide" - -# properties window +# OBS properties window Basic.PropertiesWindow="Properties for '%1'" Basic.PropertiesWindow.AutoSelectFormat="%1 (autoselect: %2)" Basic.PropertiesWindow.SelectColor="Select color" @@ -2273,7 +2284,7 @@ Basic.PropertiesWindow.AddEditableListFiles="Add files to '%1'" Basic.PropertiesWindow.AddEditableListEntry="Add entry to '%1'" Basic.PropertiesWindow.EditEditableListEntry="Edit entry from '%1'" -# properties view +# OBS properties view Basic.PropertiesView.FPS.Simple="Simple FPS Values" Basic.PropertiesView.FPS.Rational="Rational FPS Values" Basic.PropertiesView.FPS.ValidFPSRanges="Valid FPS Ranges:" @@ -2281,6 +2292,11 @@ Basic.PropertiesView.UrlButton.Text="Open this link in your default web browser? Basic.PropertiesView.UrlButton.Text.Url="URL: %1" Basic.PropertiesView.UrlButton.OpenUrl="Open URL" +# OBS settings +Basic.Settings.Video.FPSCommon="Common FPS Values" +Basic.Settings.Video.FPSInteger="Integer FPS Value" +Basic.Settings.Video.FPSFraction="Fractional FPS Value" + # Legacy tabs below - please don't waste your time adding translations for these :) # Transition Tab AdvSceneSwitcher.transitionTab.title="Transition" diff --git a/plugins/base/CMakeLists.txt b/plugins/base/CMakeLists.txt index 5589458d..35e0697e 100644 --- a/plugins/base/CMakeLists.txt +++ b/plugins/base/CMakeLists.txt @@ -21,6 +21,8 @@ target_sources( macro-action-log.hpp macro-action-media.cpp macro-action-media.hpp + macro-action-obs-settings.cpp + macro-action-obs-settings.hpp macro-action-osc.cpp macro-action-osc.hpp macro-action-plugin-state.cpp diff --git a/plugins/base/macro-action-obs-settings.cpp b/plugins/base/macro-action-obs-settings.cpp new file mode 100644 index 00000000..52636234 --- /dev/null +++ b/plugins/base/macro-action-obs-settings.cpp @@ -0,0 +1,387 @@ +#include "macro-action-obs-settings.hpp" +#include "layout-helpers.hpp" +#include "profile-helpers.hpp" +#include "ui-helpers.hpp" + +#include +#include + +namespace advss { + +const std::string MacroActionOBSSettings::id = "obs_settings"; + +bool MacroActionOBSSettings::_registered = MacroActionFactory::Register( + MacroActionOBSSettings::id, + {MacroActionOBSSettings::Create, MacroActionOBSSettingsEdit::Create, + "AdvSceneSwitcher.action.obsSetting"}); + +const static std::map actionTypes = { + {MacroActionOBSSettings::Action::FPS_TYPE, + "AdvSceneSwitcher.action.obsSetting.action.setFPSType"}, + {MacroActionOBSSettings::Action::FPS_COMMON_VALUE, + "AdvSceneSwitcher.action.obsSetting.action.setFPSCommonValue"}, + {MacroActionOBSSettings::Action::FPS_INT_VALUE, + "AdvSceneSwitcher.action.obsSetting.action.setFPSIntegerValue"}, + {MacroActionOBSSettings::Action::FPS_NUM_VALUE, + "AdvSceneSwitcher.action.obsSetting.action.setFPSFractionNumeratorValue"}, + {MacroActionOBSSettings::Action::FPS_DEN_VALUE, + "AdvSceneSwitcher.action.obsSetting.action.setFPSFractionDenominatorValue"}, + {MacroActionOBSSettings::Action::BASE_CANVAS_X_VALUE, + "AdvSceneSwitcher.action.obsSetting.action.setBaseCanvasX"}, + {MacroActionOBSSettings::Action::BASE_CANVAS_Y_VALUE, + "AdvSceneSwitcher.action.obsSetting.action.setBaseCanvasY"}, + {MacroActionOBSSettings::Action::OUTPUT_X_VALUE, + "AdvSceneSwitcher.action.obsSetting.action.setOutputCanvasX"}, + {MacroActionOBSSettings::Action::OUTPUT_Y_VALUE, + "AdvSceneSwitcher.action.obsSetting.action.setOutputCanvasY"}, +}; + +template +static void setConfigValueHelper(Func func, Args... args) +{ + auto config = obs_frontend_get_profile_config(); + func(config, args...); + if (config_save(config) != CONFIG_SUCCESS) { + blog(LOG_WARNING, "failed to save config!"); + } +} + +template +static auto getConfigValueHelper(Func func, Func defaultFunc, Args... args) + -> std::optional> +{ + using Ret = std::invoke_result_t; + + auto config = obs_frontend_get_profile_config(); + if (config_has_user_value(config, args...)) { + return func(config, args...); + } + if (config_has_default_value(config, args...)) { + return defaultFunc(config, args...); + } + return std::optional{}; +} + +bool MacroActionOBSSettings::PerformAction() +{ + switch (_action) { + case Action::FPS_TYPE: + setConfigValueHelper(config_set_uint, "Video", "FPSType", + static_cast(_fpsType)); + obs_frontend_reset_video(); + break; + case Action::FPS_COMMON_VALUE: + setConfigValueHelper(config_set_string, "Video", "FPSCommon", + _fpsStringValue.c_str()); + obs_frontend_reset_video(); + break; + case Action::FPS_INT_VALUE: + setConfigValueHelper(config_set_uint, "Video", "FPSInt", + static_cast(_fpsIntValue)); + obs_frontend_reset_video(); + break; + case Action::FPS_NUM_VALUE: + setConfigValueHelper(config_set_uint, "Video", "FPSNum", + static_cast(_fpsIntValue)); + obs_frontend_reset_video(); + break; + case Action::FPS_DEN_VALUE: + setConfigValueHelper(config_set_uint, "Video", "FPSDen", + static_cast(_fpsIntValue)); + obs_frontend_reset_video(); + break; + case Action::BASE_CANVAS_X_VALUE: + setConfigValueHelper(config_set_uint, "Video", "BaseCX", + static_cast(_canvasSizeValue)); + obs_frontend_reset_video(); + break; + case Action::BASE_CANVAS_Y_VALUE: + setConfigValueHelper(config_set_uint, "Video", "BaseCY", + static_cast(_canvasSizeValue)); + obs_frontend_reset_video(); + break; + case Action::OUTPUT_X_VALUE: + setConfigValueHelper(config_set_uint, "Video", "OutputCX", + static_cast(_canvasSizeValue)); + obs_frontend_reset_video(); + break; + case Action::OUTPUT_Y_VALUE: + setConfigValueHelper(config_set_uint, "Video", "OutputCY", + static_cast(_canvasSizeValue)); + obs_frontend_reset_video(); + break; + default: + break; + } + return true; +} + +void MacroActionOBSSettings::LogAction() const +{ + auto it = actionTypes.find(_action); + if (it != actionTypes.end()) { + ablog(LOG_INFO, "performed action \"%s\"", it->second.c_str()); + } else { + blog(LOG_WARNING, "ignored unknown obs setting action %d", + static_cast(_action)); + } +} + +bool MacroActionOBSSettings::Save(obs_data_t *obj) const +{ + MacroAction::Save(obj); + obs_data_set_int(obj, "action", static_cast(_action)); + _fpsIntValue.Save(obj, "fpsIntValue"); + _fpsStringValue.Save(obj, "fpsStringValue"); + _canvasSizeValue.Save(obj, "canvasSizeValue"); + return true; +} + +bool MacroActionOBSSettings::Load(obs_data_t *obj) +{ + MacroAction::Load(obj); + _action = static_cast(obs_data_get_int(obj, "action")); + _fpsIntValue.Load(obj, "fpsIntValue"); + _fpsStringValue.Load(obj, "fpsStringValue"); + _canvasSizeValue.Load(obj, "canvasSizeValue"); + return true; +} + +std::shared_ptr MacroActionOBSSettings::Create(Macro *m) +{ + return std::make_shared(m); +} + +std::shared_ptr MacroActionOBSSettings::Copy() const +{ + return std::make_shared(*this); +} + +void MacroActionOBSSettings::ResolveVariablesToFixedValues() +{ + _fpsIntValue.ResolveVariables(); + _canvasSizeValue.ResolveVariables(); +} + +static void populateActionSelection(QComboBox *list) +{ + for (const auto &[value, name] : actionTypes) { + list->addItem(obs_module_text(name.c_str()), + static_cast(value)); + } +} + +static void populateFPSTypeSelection(QComboBox *list) +{ + static const std::map + types = { + {MacroActionOBSSettings::FPSType::COMMON, + "Basic.Settings.Video.FPSCommon"}, + {MacroActionOBSSettings::FPSType::INTEGER, + "Basic.Settings.Video.FPSInteger"}, + {MacroActionOBSSettings::FPSType::FRACTION, + "Basic.Settings.Video.FPSFraction"}, + }; + + for (const auto &[value, name] : types) { + list->addItem(obs_module_text(name.c_str()), + static_cast(value)); + } +} + +MacroActionOBSSettingsEdit::MacroActionOBSSettingsEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent), + _actions(new QComboBox(this)), + _fpsType(new QComboBox(this)), + _fpsIntValue(new VariableSpinBox(this)), + _fpsStringValue(new VariableLineEdit(this)), + _canvasSizeValue(new VariableSpinBox(this)), + _getCurrentValue(new QPushButton(obs_module_text( + "AdvSceneSwitcher.action.obsSetting.getCurrentValue"))) +{ + populateActionSelection(_actions); + populateFPSTypeSelection(_fpsType); + + _fpsIntValue->setMaximum(1000); + _canvasSizeValue->setMaximum(100000); + + QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, + SLOT(ActionChanged(int))); + QWidget::connect(_fpsType, SIGNAL(currentIndexChanged(int)), this, + SLOT(FPSTypeChanged(int))); + QWidget::connect( + _fpsIntValue, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(FPSIntValueChanged(const NumberVariable &))); + QWidget::connect(_fpsStringValue, SIGNAL(editingFinished()), this, + SLOT(FPSStringValueChanged())); + QWidget::connect( + _canvasSizeValue, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, + SLOT(CanvasSizeValueChanged(const NumberVariable &))); + QWidget::connect(_getCurrentValue, SIGNAL(clicked()), this, + SLOT(GetCurrentValueClicked())); + + auto layout = new QHBoxLayout(); + PlaceWidgets( + obs_module_text("AdvSceneSwitcher.action.obsSettings.layout"), + layout, + {{"{{actions}}", _actions}, + {"{{fpsType}}", _fpsType}, + {"{{fpsIntValue}}", _fpsIntValue}, + {"{{fpsStringValue}}", _fpsStringValue}, + {"{{canvasSizeValue}}", _canvasSizeValue}, + {"{{getCurrentValue}}", _getCurrentValue}}); + setLayout(layout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroActionOBSSettingsEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _actions->setCurrentIndex( + _actions->findData(static_cast(_entryData->_action))); + _fpsType->setCurrentIndex( + _actions->findData(static_cast(_entryData->_fpsType))); + _fpsIntValue->SetValue(_entryData->_fpsIntValue); + _fpsStringValue->setText(_entryData->_fpsStringValue); + _canvasSizeValue->SetValue(_entryData->_canvasSizeValue); + SetWidgetVisibility(); +} + +void MacroActionOBSSettingsEdit::FPSTypeChanged(int idx) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_fpsType = static_cast( + _fpsType->itemData(idx).toInt()); + SetWidgetVisibility(); +} + +void MacroActionOBSSettingsEdit::FPSIntValueChanged( + const NumberVariable &value) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_fpsIntValue = value; +} + +void MacroActionOBSSettingsEdit::FPSStringValueChanged() +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_fpsStringValue = _fpsStringValue->text().toStdString(); +} + +void MacroActionOBSSettingsEdit::CanvasSizeValueChanged( + const NumberVariable &value) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_canvasSizeValue = value; +} + +void MacroActionOBSSettingsEdit::GetCurrentValueClicked() +{ + const auto setSpinVideoValue = [this](VariableSpinBox *spin, + const char *name) { + auto value = getConfigValueHelper(config_get_uint, + config_get_default_uint, + "Video", name); + if (!value) { + return; + } + spin->SetFixedValue(static_cast(*value)); + }; + const auto setFpsValue = [&](const char *name) { + setSpinVideoValue(_fpsIntValue, name); + }; + const auto setCanvasValue = [&](const char *name) { + setSpinVideoValue(_canvasSizeValue, name); + }; + + switch (_entryData->_action) { + case MacroActionOBSSettings::Action::FPS_COMMON_VALUE: { + auto value = getConfigValueHelper(config_get_string, + config_get_default_string, + "Video", "FPSCommon"); + if (!value) { + break; + } + _fpsStringValue->setText(QString(*value)); + break; + } + case MacroActionOBSSettings::Action::FPS_INT_VALUE: + setFpsValue("FPSInt"); + break; + case MacroActionOBSSettings::Action::FPS_NUM_VALUE: + setFpsValue("FPSNum"); + break; + case MacroActionOBSSettings::Action::FPS_DEN_VALUE: + setFpsValue("FPSDen"); + break; + case MacroActionOBSSettings::Action::BASE_CANVAS_X_VALUE: + setCanvasValue("BaseCX"); + break; + case MacroActionOBSSettings::Action::BASE_CANVAS_Y_VALUE: + setCanvasValue("BaseCY"); + break; + case MacroActionOBSSettings::Action::OUTPUT_X_VALUE: + setCanvasValue("OutputCX"); + break; + case MacroActionOBSSettings::Action::OUTPUT_Y_VALUE: + setCanvasValue("OutputCY"); + break; + default: + break; + } +} + +void MacroActionOBSSettingsEdit::SetWidgetVisibility() +{ + if (!_entryData) { + return; + } + + const bool isFPSSelection = + _entryData->_action == + MacroActionOBSSettings::Action::FPS_TYPE || + _entryData->_action == + MacroActionOBSSettings::Action::FPS_COMMON_VALUE || + _entryData->_action == + MacroActionOBSSettings::Action::FPS_INT_VALUE || + _entryData->_action == + MacroActionOBSSettings::Action::FPS_DEN_VALUE || + _entryData->_action == + MacroActionOBSSettings::Action::FPS_NUM_VALUE; + + _fpsType->setVisible(_entryData->_action == + MacroActionOBSSettings::Action::FPS_TYPE); + _fpsIntValue->setVisible( + _entryData->_action == + MacroActionOBSSettings::Action::FPS_INT_VALUE || + _entryData->_action == + MacroActionOBSSettings::Action::FPS_NUM_VALUE || + _entryData->_action == + MacroActionOBSSettings::Action::FPS_DEN_VALUE); + _fpsStringValue->setVisible( + _entryData->_action == + MacroActionOBSSettings::Action::FPS_COMMON_VALUE); + _canvasSizeValue->setVisible(!isFPSSelection); + _getCurrentValue->setVisible(_entryData->_action != + MacroActionOBSSettings::Action::FPS_TYPE); +} + +void MacroActionOBSSettingsEdit::ActionChanged(int idx) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_action = static_cast( + _actions->itemData(idx).toInt()); + SetWidgetVisibility(); +} + +} // namespace advss diff --git a/plugins/base/macro-action-obs-settings.hpp b/plugins/base/macro-action-obs-settings.hpp new file mode 100644 index 00000000..81f3da0e --- /dev/null +++ b/plugins/base/macro-action-obs-settings.hpp @@ -0,0 +1,90 @@ +#pragma once +#include "macro-action-edit.hpp" +#include "variable-spinbox.hpp" +#include "variable-line-edit.hpp" + +#include +#include +#include + +namespace advss { + +class MacroActionOBSSettings : public MacroAction { +public: + MacroActionOBSSettings(Macro *m) : MacroAction(m) {} + bool PerformAction(); + void LogAction() const; + bool Save(obs_data_t *obj) const; + bool Load(obs_data_t *obj); + std::string GetId() const { return id; }; + static std::shared_ptr Create(Macro *m); + std::shared_ptr Copy() const; + void ResolveVariablesToFixedValues(); + + enum class Action { + FPS_TYPE, + FPS_COMMON_VALUE, + FPS_INT_VALUE, + FPS_NUM_VALUE, + FPS_DEN_VALUE, + BASE_CANVAS_X_VALUE, + BASE_CANVAS_Y_VALUE, + OUTPUT_X_VALUE, + OUTPUT_Y_VALUE, + }; + Action _action = Action::FPS_COMMON_VALUE; + enum class FPSType { + COMMON, + INTEGER, + FRACTION, + }; + FPSType _fpsType = FPSType::INTEGER; + IntVariable _fpsIntValue = 30; + StringVariable _fpsStringValue = "24 NTSC"; + IntVariable _canvasSizeValue = 1920; + +private: + static bool _registered; + static const std::string id; +}; + +class MacroActionOBSSettingsEdit : public QWidget { + Q_OBJECT + +public: + MacroActionOBSSettingsEdit( + QWidget *parent, + std::shared_ptr entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionOBSSettingsEdit( + parent, + std::dynamic_pointer_cast( + action)); + } + +private slots: + void ActionChanged(int); + void FPSTypeChanged(int); + void FPSIntValueChanged(const NumberVariable &); + void FPSStringValueChanged(); + void CanvasSizeValueChanged(const NumberVariable &); + void GetCurrentValueClicked(); + +private: + void SetWidgetVisibility(); + + QComboBox *_actions; + QComboBox *_fpsType; + VariableSpinBox *_fpsIntValue; + VariableLineEdit *_fpsStringValue; + VariableSpinBox *_canvasSizeValue; + QPushButton *_getCurrentValue; + + std::shared_ptr _entryData; + bool _loading = true; +}; + +} // namespace advss