Compare commits

...

21 Commits
5.4.1 ... 5.0.0

Author SHA1 Message Date
a25427c7cc base: Final changes before release 2022-07-02 11:08:18 -07:00
99f93c4bdd websocketserver: Log disconnect code and reason on disconnects
No clue why I wasn't doing this before.
2022-07-02 10:53:46 -07:00
6af3af61d5 requesthandler: Add Outputs requests
Co-authored-by: Ruggero Tomaselli <ruggerotomaselli@gmail.com>
Co-authored-by: tt2468 <tt2468@irltoolkit.com>
2022-07-02 10:31:47 -07:00
14a1547e11 docs: Fix lint formatting error 2022-07-02 08:30:02 -07:00
60ade46481 requesthandler: Fix oopsies in OpenVideoMixProjector 2022-07-02 08:27:32 -07:00
7c21e5732e requesthandler: Add projector creation requests
I didn't think I'd be able to make remotely usable requests using OBS'
existing projector API, but I'm actually pretty happy with how it
turned out.

Closes #929

Co-authored-by: Brendan Allan <brendonovich@outlook.com>
2022-07-02 08:23:03 -07:00
f5db53f217 requesthandler: Echo request details in response of CallVendorRequest
I generally don't like to echo data provided to obs-websocket in
requests, but since we do that for the request type in base requests,
this particular case seems fair.

Closes #919
2022-07-02 07:11:44 -07:00
3400f88665 CI: Exclude qr from code format too 2022-07-02 07:03:46 -07:00
ad3cb5dcfd requesthandler, eventhandler: Add outputPath fields when record stops
Closes #934
2022-07-02 07:00:51 -07:00
5ecda806bd requesthandler: Rework and fix a few data consistency checks
Some stuff led to possible crashes, other stuff simply didn't work.
Should be much better now.

Closes #942
2022-07-02 05:57:35 -07:00
af3f29169c utils: Clean up a few iteration functions 2022-07-02 05:57:25 -07:00
af97978841 utils: Move from explicit enum converters to nlohmann macros 2022-07-02 05:57:08 -07:00
4f89378c45 utils: A few casting nitpicks 2022-07-02 05:50:37 -07:00
4fc8a3aecc CI: Fix deps formatting excludes 2022-07-02 05:49:57 -07:00
1626ae5546 Revert "Config, websocketserver: Add feature to bind to loopback (default)"
This reverts commit 1da0214201.
2022-07-02 05:49:02 -07:00
8b7fd3dd46 Revert "forms: Add configuration to enable external access"
This reverts commit 1cd12c1023.
2022-07-02 05:44:08 -07:00
73848a7370 docs: Fix EventSubscription::All subscribes 2022-07-02 05:43:57 -07:00
2e48dd24c4 base: Format code 2022-07-02 05:42:20 -07:00
ad1f28480c base: Update a few files with updated info 2022-07-02 05:36:52 -07:00
91cabe1202 websocketapi: Fix build on Windows 64 bit 2022-05-13 19:16:18 -07:00
f869f3df76 CI: Update to new branch name for release 2022-05-13 18:49:17 -07:00
83 changed files with 2988 additions and 2373 deletions

107
.clang-format Normal file
View File

@ -0,0 +1,107 @@
# please use clang-format version 8 or later
Standard: Cpp11
AccessModifierOffset: -8
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
#AllowAllArgumentsOnNextLine: false # requires clang-format 9
#AllowAllConstructorInitializersOnNextLine: false # requires clang-format 9
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
#AllowShortLambdasOnASingleLine: Inline # requires clang-format 9
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakStringLiterals: false # apparently unpredictable
ColumnLimit: 132
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
FixNamespaceComments: false
ForEachMacros:
- 'json_object_foreach'
- 'json_object_foreach_safe'
- 'json_array_foreach'
IncludeBlocks: Preserve
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 8
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
#ObjCBinPackProtocolList: Auto # requires clang-format 7
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
#SpaceAfterLogicalNot: false # requires clang-format 9
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
#SpaceBeforeCtorInitializerColon: true # requires clang-format 7
#SpaceBeforeInheritanceColon: true # requires clang-format 7
SpaceBeforeParens: ControlStatements
#SpaceBeforeRangeBasedForLoopColon: true # requires clang-format 7
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
#StatementMacros: # requires clang-format 8
# - 'Q_OBJECT'
TabWidth: 8
#TypenameMacros: # requires clang-format 9
# - 'DARRAY'
UseTab: ForContinuationAndIndentation
---
Language: ObjC

View File

@ -27,10 +27,11 @@ body:
- macOS 10.15 - macOS 10.15
- macOS 10.14 - macOS 10.14
- macOS 10.13 - macOS 10.13
- Ubuntu 22.04 LTS
- Ubuntu 21.04 - Ubuntu 21.04
- Ubuntu 20.10 - Ubuntu 20.10
- Ubuntu 20.04 - Ubuntu 20.04 LTS
- Ubuntu 18.04 - Ubuntu 18.04 LTS
- Other - Other
validations: validations:
required: true required: true
@ -48,6 +49,11 @@ body:
label: OBS Studio Version label: OBS Studio Version
description: What version of OBS Studio are you using? description: What version of OBS Studio are you using?
options: options:
- 27.2.4
- 27.2.3
- 27.2.2
- 27.2.1
- 27.2.0
- 27.1.3 - 27.1.3
- 27.1.1 - 27.1.1
- 27.1.0 - 27.1.0
@ -69,6 +75,7 @@ body:
label: obs-websocket Version label: obs-websocket Version
description: What version of obs-websocket are you using? description: What version of obs-websocket are you using?
options: options:
- 5.0.0
- 5.0.0-beta1 - 5.0.0-beta1
- 5.0.0-alpha3 - 5.0.0-alpha3
- 5.0.0-alpha2 - 5.0.0-alpha2

View File

@ -29,6 +29,6 @@ Tested OS(s):
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> <!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] I have read the [Contributing Guidelines](https://github.com/obsproject/obs-websocket/wiki/Contributing-Guidelines). - [ ] I have read the [Contributing Guidelines](https://github.com/obsproject/obs-websocket/wiki/Contributing-Guidelines).
- [ ] All commit messages are properly formatted and commits squashed where appropriate. - [ ] All commit messages are properly formatted and commits squashed where appropriate.
- [ ] My code is not on the `master` branch. - [ ] My code is not on `master` or a `release/*` branch.
- [ ] The code has been tested. - [ ] The code has been tested.
- [ ] I have included updates to all appropriate documentation. - [ ] I have included updates to all appropriate documentation.

View File

@ -5,7 +5,7 @@ on:
paths-ignore: paths-ignore:
- 'docs/generated/**' - 'docs/generated/**'
branches: branches:
- master - release/5.0.0
jobs: jobs:
docs-build: docs-build:

View File

@ -3,10 +3,10 @@ name: Code Quality
on: on:
push: push:
branches: branches:
- master - release/5.0.0
pull_request: pull_request:
branches: branches:
- master - release/5.0.0
jobs: jobs:
markdown: markdown:

View File

@ -5,7 +5,7 @@ on:
paths-ignore: paths-ignore:
- 'docs/**' - 'docs/**'
branches: branches:
- master - release/5.0.0
tags: tags:
- '[45].[0-9]+.[0-9]+*' - '[45].[0-9]+.[0-9]+*'
pull_request: pull_request:
@ -13,7 +13,7 @@ on:
- 'docs/**' - 'docs/**'
- '**.md' - '**.md'
branches: branches:
- master - release/5.0.0
jobs: jobs:
windows: windows:
@ -334,9 +334,9 @@ jobs:
cd ./build cd ./build
sudo checkinstall -y --type=debian --fstrans=no -nodoc \ sudo checkinstall -y --type=debian --fstrans=no -nodoc \
--backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion="$CHECKINSTALL_VERSION" \ --backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion="$CHECKINSTALL_VERSION" \
--pkglicense="GPLv2.0" --maintainer="${{ github.event.pusher.email }}" --pkggroup="video" \ --pkglicense="GPLv2.0" --maintainer="tt2468@irltoolkit.com" --pkggroup="video" \
--pkgsource="${{ github.event.repository.html_url }}" \ --pkgsource="${{ github.event.repository.html_url }}" \
--requires="obs-studio,libqt5network5,libqt5concurrent5,qt5-image-formats-plugins" \ --requires="obs-studio,libqt5network5,qt5-image-formats-plugins" \
--pakdir="../package" --pakdir="../package"
sudo chmod ao+r ../package/* sudo chmod ao+r ../package/*
sudo mv ../package/* ../package/${{ env.LINUX_FILENAME }} sudo mv ../package/* ../package/${{ env.LINUX_FILENAME }}

57
CI/check-format.sh Executable file
View File

@ -0,0 +1,57 @@
#!/usr/bin/env bash
# Original source https://github.com/Project-OSRM/osrm-backend/blob/master/scripts/format.sh
set -o errexit
set -o pipefail
set -o nounset
if [ ${#} -eq 1 ]; then
VERBOSITY="--verbose"
else
VERBOSITY=""
fi
# Runs the Clang Formatter in parallel on the code base.
# Return codes:
# - 1 there are files to be formatted
# - 0 everything looks fine
# Get CPU count
OS=$(uname)
NPROC=1
if [[ ${OS} = "Linux" ]] ; then
NPROC=$(nproc)
elif [[ ${OS} = "Darwin" ]] ; then
NPROC=$(sysctl -n hw.physicalcpu)
fi
# Discover clang-format
if type clang-format-12 2> /dev/null ; then
CLANG_FORMAT=clang-format-12
elif type clang-format 2> /dev/null ; then
# Clang format found, but need to check version
CLANG_FORMAT=clang-format
V=$(clang-format --version)
if [[ $V != *"version 12.0"* ]]; then
echo "clang-format is not 12.0 (returned ${V})"
exit 1
fi
else
echo "No appropriate clang-format found (expected clang-format-12.0.0, or clang-format)"
exit 1
fi
find . -type d \( \
-path ./\*build\* -o \
-path ./deps/websocketpp -o \
-path ./deps/asio -o \
-path ./deps/json -o \
-path ./deps/qr \
\) -prune -false -type f -o \
-name '*.h' -or \
-name '*.hpp' -or \
-name '*.m' -or \
-name '*.m,' -or \
-name '*.c' -or \
-name '*.cpp' \
| xargs -L100 -P ${NPROC} ${CLANG_FORMAT} ${VERBOSITY} -i -style=file -fallback-style=none

View File

@ -85,7 +85,7 @@ configure_file(
) )
# Inlude sources # Include sources
set(obs-websocket_SOURCES set(obs-websocket_SOURCES
src/obs-websocket.cpp src/obs-websocket.cpp
src/Config.cpp src/Config.cpp
@ -129,7 +129,6 @@ set(obs-websocket_SOURCES
src/utils/Json.cpp src/utils/Json.cpp
src/utils/Obs.cpp src/utils/Obs.cpp
src/utils/Obs_StringHelper.cpp src/utils/Obs_StringHelper.cpp
src/utils/Obs_EnumHelper.cpp
src/utils/Obs_NumberHelper.cpp src/utils/Obs_NumberHelper.cpp
src/utils/Obs_ArrayHelper.cpp src/utils/Obs_ArrayHelper.cpp
src/utils/Obs_ObjectHelper.cpp src/utils/Obs_ObjectHelper.cpp

View File

@ -8,7 +8,6 @@
WebSocket API for OBS Studio. WebSocket API for OBS Studio.
[![CI Multiplatform Build](https://github.com/obsproject/obs-websocket/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/obsproject/obs-websocket/actions/workflows/main.yml)
[![Discord](https://img.shields.io/discord/715691013825364120.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/WBaSQ3A) [![Discord](https://img.shields.io/discord/715691013825364120.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/WBaSQ3A)
[![Financial Contributors on Open Collective](https://opencollective.com/obs-websocket-dev/all/badge.svg?label=financial+contributors)](https://opencollective.com/obs-websocket-dev) [![Financial Contributors on Open Collective](https://opencollective.com/obs-websocket-dev/all/badge.svg?label=financial+contributors)](https://opencollective.com/obs-websocket-dev)

View File

@ -9,14 +9,11 @@ OBSWebSocket.Settings.DebugEnable="Enable Debug Logging"
OBSWebSocket.Settings.DebugEnableHoverText="Enables debug logging for the current instance of OBS. Does not persist on load.\nUse --websocket_debug to enable on load." OBSWebSocket.Settings.DebugEnableHoverText="Enables debug logging for the current instance of OBS. Does not persist on load.\nUse --websocket_debug to enable on load."
OBSWebSocket.Settings.ServerSettingsTitle="Server Settings" OBSWebSocket.Settings.ServerSettingsTitle="Server Settings"
OBSWebSocket.Settings.ServerPort="Server Port"
OBSWebSocket.Settings.AllowExternal="Allow External Access"
OBSWebSocket.Settings.AllowExternalHoverText="Allows clients from outside this computer to connect to obs-websocket."
OBSWebSocket.Settings.AuthRequired="Enable Authentication" OBSWebSocket.Settings.AuthRequired="Enable Authentication"
OBSWebSocket.Settings.Password="Server Password" OBSWebSocket.Settings.Password="Server Password"
OBSWebSocket.Settings.GeneratePassword="Generate Password" OBSWebSocket.Settings.GeneratePassword="Generate Password"
OBSWebSocket.Settings.ServerPort="Server Port"
OBSWebSocket.Settings.ShowConnectInfo="Show Connect Info" OBSWebSocket.Settings.ShowConnectInfo="Show Connect Info"
OBSWebSocket.Settings.ShowConnectInfoHoverText="Connect Info is not available if external connections are disabled."
OBSWebSocket.Settings.ShowConnectInfoWarningTitle="Warning: Currently Live" OBSWebSocket.Settings.ShowConnectInfoWarningTitle="Warning: Currently Live"
OBSWebSocket.Settings.ShowConnectInfoWarningMessage="It appears that an output (stream, recording, etc.) is currently active." OBSWebSocket.Settings.ShowConnectInfoWarningMessage="It appears that an output (stream, recording, etc.) is currently active."
OBSWebSocket.Settings.ShowConnectInfoWarningInfoText="Are you sure that you want to show your connect info?" OBSWebSocket.Settings.ShowConnectInfoWarningInfoText="Are you sure that you want to show your connect info?"

View File

@ -1,6 +1,6 @@
#define MyAppName "obs-websocket" #define MyAppName "obs-websocket"
#define MyAppVersion "@OBS_WEBSOCKET_VERSION@" #define MyAppVersion "@OBS_WEBSOCKET_VERSION@"
#define MyAppPublisher "obs-websocket" #define MyAppPublisher "OBS Project"
#define MyAppURL "http://github.com/obsproject/obs-websocket" #define MyAppURL "http://github.com/obsproject/obs-websocket"
[Setup] [Setup]

View File

@ -30,8 +30,8 @@ obs_websocket_vendor vendor;
bool obs_module_load(void) bool obs_module_load(void)
{ {
blog(LOG_INFO, "Example obs-websocket-api plugin loaded!"); blog(LOG_INFO, "Example obs-websocket-api plugin loaded!");
return true; return true;
} }
void example_request_cb(obs_data_t *request_data, obs_data_t *response_data, void *priv_data); void example_request_cb(obs_data_t *request_data, obs_data_t *response_data, void *priv_data);
@ -60,7 +60,8 @@ void obs_module_post_load(void)
blog(LOG_ERROR, "Failed to call GetVersion due to obs-websocket not being installed."); blog(LOG_ERROR, "Failed to call GetVersion due to obs-websocket not being installed.");
return; return;
} }
blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s", response->status_code, response->comment, response->response_data); blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
response->status_code, response->comment, response->response_data);
obs_websocket_request_response_free(response); obs_websocket_request_response_free(response);
} }

View File

@ -28,8 +28,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
extern "C" { extern "C" {
#endif #endif
typedef void* obs_websocket_vendor; typedef void *obs_websocket_vendor;
typedef void (*obs_websocket_request_callback_function)(obs_data_t*, obs_data_t*, void*); typedef void (*obs_websocket_request_callback_function)(obs_data_t *, obs_data_t *, void *);
struct obs_websocket_request_response { struct obs_websocket_request_response {
unsigned int status_code; unsigned int status_code;
@ -56,7 +56,7 @@ static inline proc_handler_t *obs_websocket_get_ph(void)
calldata_t cd = {0}; calldata_t cd = {0};
if (!proc_handler_call(global_ph, "obs_websocket_api_get_ph", &cd)) if (!proc_handler_call(global_ph, "obs_websocket_api_get_ph", &cd))
blog(LOG_DEBUG, "Unable to fetch obs-websocket proc handler object. obs-websocket not installed?"); blog(LOG_DEBUG, "Unable to fetch obs-websocket proc handler object. obs-websocket not installed?");
proc_handler_t *ret = (proc_handler_t*)calldata_ptr(&cd, "ph"); proc_handler_t *ret = (proc_handler_t *)calldata_ptr(&cd, "ph");
calldata_free(&cd); calldata_free(&cd);
return ret; return ret;
@ -120,7 +120,7 @@ static inline obs_websocket_request_response *obs_websocket_call_request(const c
proc_handler_call(_ph, "call_request", &cd); proc_handler_call(_ph, "call_request", &cd);
auto ret = (struct obs_websocket_request_response*)calldata_ptr(&cd, "response"); auto ret = (struct obs_websocket_request_response *)calldata_ptr(&cd, "response");
calldata_free(&cd); calldata_free(&cd);
@ -161,7 +161,8 @@ static inline obs_websocket_vendor obs_websocket_register_vendor(const char *ven
} }
// Registers a new request for a vendor // Registers a new request for a vendor
static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor vendor, const char *request_type, obs_websocket_request_callback_function request_callback, void* priv_data) static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor vendor, const char *request_type,
obs_websocket_request_callback_function request_callback, void *priv_data)
{ {
calldata_t cd = {0}; calldata_t cd = {0};
@ -198,7 +199,7 @@ static inline bool obs_websocket_vendor_emit_event(obs_websocket_vendor vendor,
calldata_t cd = {0}; calldata_t cd = {0};
calldata_set_string(&cd, "type", event_name); calldata_set_string(&cd, "type", event_name);
calldata_set_ptr(&cd, "data", (void*)event_data); calldata_set_ptr(&cd, "data", (void *)event_data);
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_event_emit", &cd); bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_event_emit", &cd);
calldata_free(&cd); calldata_free(&cd);

View File

@ -28,7 +28,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#define PARAM_FIRSTLOAD "FirstLoad" #define PARAM_FIRSTLOAD "FirstLoad"
#define PARAM_ENABLED "ServerEnabled" #define PARAM_ENABLED "ServerEnabled"
#define PARAM_PORT "ServerPort" #define PARAM_PORT "ServerPort"
#define PARAM_BINDLOOPBACK "BindLoopback"
#define PARAM_ALERTS "AlertsEnabled" #define PARAM_ALERTS "AlertsEnabled"
#define PARAM_AUTHREQUIRED "AuthRequired" #define PARAM_AUTHREQUIRED "AuthRequired"
#define PARAM_PASSWORD "ServerPassword" #define PARAM_PASSWORD "ServerPassword"
@ -38,25 +37,24 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#define CMDLINE_WEBSOCKET_PASSWORD "websocket_password" #define CMDLINE_WEBSOCKET_PASSWORD "websocket_password"
#define CMDLINE_WEBSOCKET_DEBUG "websocket_debug" #define CMDLINE_WEBSOCKET_DEBUG "websocket_debug"
Config::Config() : Config::Config()
PortOverridden(false), : PortOverridden(false),
PasswordOverridden(false), PasswordOverridden(false),
FirstLoad(true), FirstLoad(true),
ServerEnabled(true), ServerEnabled(true),
ServerPort(4455), ServerPort(4455),
BindLoopback(true), Ipv4Only(false),
Ipv4Only(false), DebugEnabled(false),
DebugEnabled(false), AlertsEnabled(false),
AlertsEnabled(false), AuthRequired(true),
AuthRequired(true), ServerPassword("")
ServerPassword("")
{ {
SetDefaultsToGlobalStore(); SetDefaultsToGlobalStore();
} }
void Config::Load() void Config::Load()
{ {
config_t* obsConfig = GetConfigStore(); config_t *obsConfig = GetConfigStore();
if (!obsConfig) { if (!obsConfig) {
blog(LOG_ERROR, "[Config::Load] Unable to fetch OBS config!"); blog(LOG_ERROR, "[Config::Load] Unable to fetch OBS config!");
return; return;
@ -66,7 +64,6 @@ void Config::Load()
ServerEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED); ServerEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED);
AlertsEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS); AlertsEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS);
ServerPort = config_get_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT); ServerPort = config_get_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT);
BindLoopback = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK);
AuthRequired = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED); AuthRequired = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED);
ServerPassword = config_get_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD); ServerPassword = config_get_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD);
@ -123,7 +120,7 @@ void Config::Load()
void Config::Save() void Config::Save()
{ {
config_t* obsConfig = GetConfigStore(); config_t *obsConfig = GetConfigStore();
if (!obsConfig) { if (!obsConfig) {
blog(LOG_ERROR, "[Config::Save] Unable to fetch OBS config!"); blog(LOG_ERROR, "[Config::Save] Unable to fetch OBS config!");
return; return;
@ -134,7 +131,6 @@ void Config::Save()
if (!PortOverridden) { if (!PortOverridden) {
config_set_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort); config_set_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
} }
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK, BindLoopback);
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled); config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
if (!PasswordOverridden) { if (!PasswordOverridden) {
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired); config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
@ -146,7 +142,7 @@ void Config::Save()
void Config::SetDefaultsToGlobalStore() void Config::SetDefaultsToGlobalStore()
{ {
config_t* obsConfig = GetConfigStore(); config_t *obsConfig = GetConfigStore();
if (!obsConfig) { if (!obsConfig) {
blog(LOG_ERROR, "[Config::SetDefaultsToGlobalStore] Unable to fetch OBS config!"); blog(LOG_ERROR, "[Config::SetDefaultsToGlobalStore] Unable to fetch OBS config!");
return; return;
@ -155,13 +151,12 @@ void Config::SetDefaultsToGlobalStore()
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, FirstLoad); config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, FirstLoad);
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, ServerEnabled); config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, ServerEnabled);
config_set_default_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort); config_set_default_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK, BindLoopback);
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled); config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired); config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_default_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword)); config_set_default_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
} }
config_t* Config::GetConfigStore() config_t *Config::GetConfigStore()
{ {
return obs_frontend_get_global_config(); return obs_frontend_get_global_config();
} }

View File

@ -30,7 +30,7 @@ struct Config {
void Load(); void Load();
void Save(); void Save();
void SetDefaultsToGlobalStore(); void SetDefaultsToGlobalStore();
config_t* GetConfigStore(); config_t *GetConfigStore();
std::atomic<bool> PortOverridden; std::atomic<bool> PortOverridden;
std::atomic<bool> PasswordOverridden; std::atomic<bool> PasswordOverridden;
@ -38,7 +38,6 @@ struct Config {
std::atomic<bool> FirstLoad; std::atomic<bool> FirstLoad;
std::atomic<bool> ServerEnabled; std::atomic<bool> ServerEnabled;
std::atomic<uint16_t> ServerPort; std::atomic<uint16_t> ServerPort;
std::atomic<bool> BindLoopback;
std::atomic<bool> Ipv4Only; std::atomic<bool> Ipv4Only;
std::atomic<bool> DebugEnabled; std::atomic<bool> DebugEnabled;
std::atomic<bool> AlertsEnabled; std::atomic<bool> AlertsEnabled;

View File

@ -3,7 +3,11 @@
#include "obs-websocket.h" #include "obs-websocket.h"
#include "utils/Json.h" #include "utils/Json.h"
#define RETURN_STATUS(status) { calldata_set_bool(cd, "success", status); return; } #define RETURN_STATUS(status) \
{ \
calldata_set_bool(cd, "success", status); \
return; \
}
#define RETURN_SUCCESS() RETURN_STATUS(true); #define RETURN_SUCCESS() RETURN_STATUS(true);
#define RETURN_FAILURE() RETURN_STATUS(false); #define RETURN_FAILURE() RETURN_STATUS(false);
@ -15,7 +19,7 @@ WebSocketApi::Vendor *get_vendor(calldata_t *cd)
return nullptr; return nullptr;
} }
return static_cast<WebSocketApi::Vendor*>(voidVendor); return static_cast<WebSocketApi::Vendor *>(voidVendor);
} }
WebSocketApi::WebSocketApi() WebSocketApi::WebSocketApi()
@ -25,11 +29,15 @@ WebSocketApi::WebSocketApi()
_procHandler = proc_handler_create(); _procHandler = proc_handler_create();
proc_handler_add(_procHandler, "bool get_api_version(out int version)", &get_api_version, nullptr); proc_handler_add(_procHandler, "bool get_api_version(out int version)", &get_api_version, nullptr);
proc_handler_add(_procHandler, "bool call_request(in string request_type, in string request_data, out ptr response)", &call_request, nullptr); proc_handler_add(_procHandler, "bool call_request(in string request_type, in string request_data, out ptr response)",
&call_request, nullptr);
proc_handler_add(_procHandler, "bool vendor_register(in string name, out ptr vendor)", &vendor_register_cb, this); proc_handler_add(_procHandler, "bool vendor_register(in string name, out ptr vendor)", &vendor_register_cb, this);
proc_handler_add(_procHandler, "bool vendor_request_register(in ptr vendor, in string type, in ptr callback)", &vendor_request_register_cb, this); proc_handler_add(_procHandler, "bool vendor_request_register(in ptr vendor, in string type, in ptr callback)",
proc_handler_add(_procHandler, "bool vendor_request_unregister(in ptr vendor, in string type)", &vendor_request_unregister_cb, this); &vendor_request_register_cb, this);
proc_handler_add(_procHandler, "bool vendor_event_emit(in ptr vendor, in string type, in ptr data)", &vendor_event_emit_cb, this); proc_handler_add(_procHandler, "bool vendor_request_unregister(in ptr vendor, in string type)",
&vendor_request_unregister_cb, this);
proc_handler_add(_procHandler, "bool vendor_event_emit(in ptr vendor, in string type, in ptr data)", &vendor_event_emit_cb,
this);
proc_handler_t *ph = obs_get_proc_handler(); proc_handler_t *ph = obs_get_proc_handler();
assert(ph != NULL); assert(ph != NULL);
@ -58,7 +66,8 @@ void WebSocketApi::SetEventCallback(EventCallback cb)
_eventCallback = cb; _eventCallback = cb;
} }
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType, obs_data_t *requestData, obs_data_t *responseData) enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType,
obs_data_t *requestData, obs_data_t *responseData)
{ {
std::shared_lock l(_mutex); std::shared_lock l(_mutex);
@ -85,9 +94,9 @@ enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::str
void WebSocketApi::get_ph_cb(void *priv_data, calldata_t *cd) void WebSocketApi::get_ph_cb(void *priv_data, calldata_t *cd)
{ {
auto c = static_cast<WebSocketApi*>(priv_data); auto c = static_cast<WebSocketApi *>(priv_data);
calldata_set_ptr(cd, "ph", (void*)c->_procHandler); calldata_set_ptr(cd, "ph", (void *)c->_procHandler);
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
@ -107,7 +116,7 @@ void WebSocketApi::call_request(void *, calldata_t *cd)
if (!request_type) if (!request_type)
RETURN_FAILURE(); RETURN_FAILURE();
auto response = static_cast<obs_websocket_request_response*>(bzalloc(sizeof(struct obs_websocket_request_response))); auto response = static_cast<obs_websocket_request_response *>(bzalloc(sizeof(struct obs_websocket_request_response)));
if (!response) if (!response)
RETURN_FAILURE(); RETURN_FAILURE();
@ -119,7 +128,7 @@ void WebSocketApi::call_request(void *, calldata_t *cd)
Request request(request_type, requestData); Request request(request_type, requestData);
RequestResult requestResult = requestHandler.ProcessRequest(request); RequestResult requestResult = requestHandler.ProcessRequest(request);
response->status_code = (uint)requestResult.StatusCode; response->status_code = (unsigned int)requestResult.StatusCode;
if (!requestResult.Comment.empty()) if (!requestResult.Comment.empty())
response->comment = bstrdup(requestResult.Comment.c_str()); response->comment = bstrdup(requestResult.Comment.c_str());
if (requestResult.ResponseData.is_object()) { if (requestResult.ResponseData.is_object()) {
@ -129,14 +138,15 @@ void WebSocketApi::call_request(void *, calldata_t *cd)
calldata_set_ptr(cd, "response", response); calldata_set_ptr(cd, "response", response);
blog_debug("[WebSocketApi::call_request] Request %s called, response status code is %u", request_type, response->status_code); blog_debug("[WebSocketApi::call_request] Request %s called, response status code is %u", request_type,
response->status_code);
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd) void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
{ {
auto c = static_cast<WebSocketApi*>(priv_data); auto c = static_cast<WebSocketApi *>(priv_data);
const char *vendorName; const char *vendorName;
if (!calldata_get_string(cd, "name", &vendorName) || strlen(vendorName) == 0) { if (!calldata_get_string(cd, "name", &vendorName) || strlen(vendorName) == 0) {
@ -148,18 +158,19 @@ void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
std::unique_lock l(c->_mutex); std::unique_lock l(c->_mutex);
if (c->_vendors.count(vendorName)) { if (c->_vendors.count(vendorName)) {
blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed because `%s` is already a registered vendor.", vendorName); blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed because `%s` is already a registered vendor.",
vendorName);
RETURN_FAILURE(); RETURN_FAILURE();
} }
Vendor* v = new Vendor(); Vendor *v = new Vendor();
v->_name = vendorName; v->_name = vendorName;
c->_vendors[vendorName] = v; c->_vendors[vendorName] = v;
blog_debug("[WebSocketApi::vendor_register_cb] [vendorName: %s] Registered new vendor.", v->_name.c_str()); blog_debug("[WebSocketApi::vendor_register_cb] [vendorName: %s] Registered new vendor.", v->_name.c_str());
calldata_set_ptr(cd, "vendor", static_cast<void*>(v)); calldata_set_ptr(cd, "vendor", static_cast<void *>(v));
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
@ -172,28 +183,35 @@ void WebSocketApi::vendor_request_register_cb(void *, calldata_t *cd)
const char *requestType; const char *requestType;
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) { if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing or empty `type` string.", v->_name.c_str()); blog(LOG_WARNING,
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing or empty `type` string.",
v->_name.c_str());
RETURN_FAILURE(); RETURN_FAILURE();
} }
void *voidCallback; void *voidCallback;
if (!calldata_get_ptr(cd, "callback", &voidCallback) || !voidCallback) { if (!calldata_get_ptr(cd, "callback", &voidCallback) || !voidCallback) {
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing `callback` pointer.", v->_name.c_str()); blog(LOG_WARNING,
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing `callback` pointer.",
v->_name.c_str());
RETURN_FAILURE(); RETURN_FAILURE();
} }
auto cb = static_cast<obs_websocket_request_callback*>(voidCallback); auto cb = static_cast<obs_websocket_request_callback *>(voidCallback);
std::unique_lock l(v->_mutex); std::unique_lock l(v->_mutex);
if (v->_requests.count(requestType)) { if (v->_requests.count(requestType)) {
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is already a registered request.", v->_name.c_str(), requestType); blog(LOG_WARNING,
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is already a registered request.",
v->_name.c_str(), requestType);
RETURN_FAILURE(); RETURN_FAILURE();
} }
v->_requests[requestType] = *cb; v->_requests[requestType] = *cb;
blog_debug("[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Registered new vendor request: %s", v->_name.c_str(), requestType); blog_debug("[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Registered new vendor request: %s",
v->_name.c_str(), requestType);
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
@ -206,27 +224,32 @@ void WebSocketApi::vendor_request_unregister_cb(void *, calldata_t *cd)
const char *requestType; const char *requestType;
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) { if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
blog(LOG_WARNING, "[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Failed due to missing `type` string.", v->_name.c_str()); blog(LOG_WARNING,
"[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Failed due to missing `type` string.",
v->_name.c_str());
RETURN_FAILURE(); RETURN_FAILURE();
} }
std::unique_lock l(v->_mutex); std::unique_lock l(v->_mutex);
if (!v->_requests.count(requestType)) { if (!v->_requests.count(requestType)) {
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is not a registered request.", v->_name.c_str(), requestType); blog(LOG_WARNING,
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is not a registered request.",
v->_name.c_str(), requestType);
RETURN_FAILURE(); RETURN_FAILURE();
} }
v->_requests.erase(requestType); v->_requests.erase(requestType);
blog_debug("[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Unregistered vendor request: %s", v->_name.c_str(), requestType); blog_debug("[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Unregistered vendor request: %s",
v->_name.c_str(), requestType);
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
void WebSocketApi::vendor_event_emit_cb(void *priv_data, calldata_t *cd) void WebSocketApi::vendor_event_emit_cb(void *priv_data, calldata_t *cd)
{ {
auto c = static_cast<WebSocketApi*>(priv_data); auto c = static_cast<WebSocketApi *>(priv_data);
Vendor *v = get_vendor(cd); Vendor *v = get_vendor(cd);
if (!v) if (!v)
@ -234,17 +257,19 @@ void WebSocketApi::vendor_event_emit_cb(void *priv_data, calldata_t *cd)
const char *eventType; const char *eventType;
if (!calldata_get_string(cd, "type", &eventType) || strlen(eventType) == 0) { if (!calldata_get_string(cd, "type", &eventType) || strlen(eventType) == 0) {
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `type` string.", v->_name.c_str()); blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `type` string.",
v->_name.c_str());
RETURN_FAILURE(); RETURN_FAILURE();
} }
void *voidEventData; void *voidEventData;
if (!calldata_get_ptr(cd, "data", &voidEventData)) { if (!calldata_get_ptr(cd, "data", &voidEventData)) {
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `data` pointer.", v->_name.c_str()); blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `data` pointer.",
v->_name.c_str());
RETURN_FAILURE(); RETURN_FAILURE();
} }
auto eventData = static_cast<obs_data_t*>(voidEventData); auto eventData = static_cast<obs_data_t *>(voidEventData);
if (!c->_eventCallback) if (!c->_eventCallback)
RETURN_FAILURE(); RETURN_FAILURE();

View File

@ -10,39 +10,40 @@
#include "../lib/obs-websocket-api.h" #include "../lib/obs-websocket-api.h"
class WebSocketApi { class WebSocketApi {
public: public:
enum RequestReturnCode { enum RequestReturnCode {
Normal, Normal,
NoVendor, NoVendor,
NoVendorRequest, NoVendorRequest,
}; };
typedef std::function<void(std::string, std::string, obs_data_t*)> EventCallback; typedef std::function<void(std::string, std::string, obs_data_t *)> EventCallback;
struct Vendor { struct Vendor {
std::shared_mutex _mutex;
std::string _name;
std::map<std::string, obs_websocket_request_callback> _requests;
};
WebSocketApi();
~WebSocketApi();
void SetEventCallback(EventCallback cb);
enum RequestReturnCode PerformVendorRequest(std::string vendorName, std::string requestName, obs_data_t *requestData, obs_data_t *responseData);
static void get_ph_cb(void *priv_data, calldata_t *cd);
static void get_api_version(void *, calldata_t *cd);
static void call_request(void *, calldata_t *cd);
static void vendor_register_cb(void *priv_data, calldata_t *cd);
static void vendor_request_register_cb(void *priv_data, calldata_t *cd);
static void vendor_request_unregister_cb(void *priv_data, calldata_t *cd);
static void vendor_event_emit_cb(void *priv_data, calldata_t *cd);
private:
std::shared_mutex _mutex; std::shared_mutex _mutex;
EventCallback _eventCallback; std::string _name;
proc_handler_t *_procHandler; std::map<std::string, obs_websocket_request_callback> _requests;
std::map<std::string, Vendor*> _vendors; };
WebSocketApi();
~WebSocketApi();
void SetEventCallback(EventCallback cb);
enum RequestReturnCode PerformVendorRequest(std::string vendorName, std::string requestName, obs_data_t *requestData,
obs_data_t *responseData);
static void get_ph_cb(void *priv_data, calldata_t *cd);
static void get_api_version(void *, calldata_t *cd);
static void call_request(void *, calldata_t *cd);
static void vendor_register_cb(void *priv_data, calldata_t *cd);
static void vendor_request_register_cb(void *priv_data, calldata_t *cd);
static void vendor_request_unregister_cb(void *priv_data, calldata_t *cd);
static void vendor_event_emit_cb(void *priv_data, calldata_t *cd);
private:
std::shared_mutex _mutex;
EventCallback _eventCallback;
proc_handler_t *_procHandler;
std::map<std::string, Vendor *> _vendors;
}; };

View File

@ -19,18 +19,18 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "EventHandler.h" #include "EventHandler.h"
EventHandler::EventHandler() : EventHandler::EventHandler()
_obsLoaded(false), : _obsLoaded(false),
_inputVolumeMetersRef(0), _inputVolumeMetersRef(0),
_inputActiveStateChangedRef(0), _inputActiveStateChangedRef(0),
_inputShowStateChangedRef(0), _inputShowStateChangedRef(0),
_sceneItemTransformChangedRef(0) _sceneItemTransformChangedRef(0)
{ {
blog_debug("[EventHandler::EventHandler] Setting up..."); blog_debug("[EventHandler::EventHandler] Setting up...");
obs_frontend_add_event_callback(OnFrontendEvent, this); obs_frontend_add_event_callback(OnFrontendEvent, this);
signal_handler_t* coreSignalHandler = obs_get_signal_handler(); signal_handler_t *coreSignalHandler = obs_get_signal_handler();
if (coreSignalHandler) { if (coreSignalHandler) {
signal_handler_connect(coreSignalHandler, "source_create", SourceCreatedMultiHandler, this); signal_handler_connect(coreSignalHandler, "source_create", SourceCreatedMultiHandler, this);
signal_handler_connect(coreSignalHandler, "source_destroy", SourceDestroyedMultiHandler, this); signal_handler_connect(coreSignalHandler, "source_destroy", SourceDestroyedMultiHandler, this);
@ -49,7 +49,7 @@ EventHandler::~EventHandler()
obs_frontend_remove_event_callback(OnFrontendEvent, this); obs_frontend_remove_event_callback(OnFrontendEvent, this);
signal_handler_t* coreSignalHandler = obs_get_signal_handler(); signal_handler_t *coreSignalHandler = obs_get_signal_handler();
if (coreSignalHandler) { if (coreSignalHandler) {
signal_handler_disconnect(coreSignalHandler, "source_create", SourceCreatedMultiHandler, this); signal_handler_disconnect(coreSignalHandler, "source_create", SourceCreatedMultiHandler, this);
signal_handler_disconnect(coreSignalHandler, "source_destroy", SourceDestroyedMultiHandler, this); signal_handler_disconnect(coreSignalHandler, "source_destroy", SourceDestroyedMultiHandler, this);
@ -80,7 +80,8 @@ void EventHandler::ProcessSubscription(uint64_t eventSubscriptions)
if (_inputVolumeMetersHandler) if (_inputVolumeMetersHandler)
blog(LOG_WARNING, "[EventHandler::ProcessSubscription] Input volume meter handler already exists!"); blog(LOG_WARNING, "[EventHandler::ProcessSubscription] Input volume meter handler already exists!");
else else
_inputVolumeMetersHandler = std::make_unique<Utils::Obs::VolumeMeter::Handler>(std::bind(&EventHandler::HandleInputVolumeMeters, this, std::placeholders::_1)); _inputVolumeMetersHandler = std::make_unique<Utils::Obs::VolumeMeter::Handler>(
std::bind(&EventHandler::HandleInputVolumeMeters, this, std::placeholders::_1));
} }
} }
if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0) if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0)
@ -124,7 +125,7 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu
// Disconnect all existing signals from the source to prevent multiple connections // Disconnect all existing signals from the source to prevent multiple connections
DisconnectSourceSignals(source); DisconnectSourceSignals(source);
signal_handler_t* sh = obs_source_get_signal_handler(source); signal_handler_t *sh = obs_source_get_signal_handler(source);
obs_source_type sourceType = obs_source_get_type(source); obs_source_type sourceType = obs_source_get_type(source);
@ -166,8 +167,8 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu
signal_handler_connect(sh, "reorder_filters", HandleSourceFilterListReindexed, this); signal_handler_connect(sh, "reorder_filters", HandleSourceFilterListReindexed, this);
signal_handler_connect(sh, "filter_add", FilterAddMultiHandler, this); signal_handler_connect(sh, "filter_add", FilterAddMultiHandler, this);
signal_handler_connect(sh, "filter_remove", FilterRemoveMultiHandler, this); signal_handler_connect(sh, "filter_remove", FilterRemoveMultiHandler, this);
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){ auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param) {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
eventHandler->ConnectSourceSignals(filter); eventHandler->ConnectSourceSignals(filter);
}; };
obs_source_enum_filters(source, enumFilters, this); obs_source_enum_filters(source, enumFilters, this);
@ -193,7 +194,7 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
if (!source) if (!source)
return; return;
signal_handler_t* sh = obs_source_get_signal_handler(source); signal_handler_t *sh = obs_source_get_signal_handler(source);
obs_source_type sourceType = obs_source_get_type(source); obs_source_type sourceType = obs_source_get_type(source);
@ -235,8 +236,8 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
signal_handler_disconnect(sh, "reorder_filters", HandleSourceFilterListReindexed, this); signal_handler_disconnect(sh, "reorder_filters", HandleSourceFilterListReindexed, this);
signal_handler_disconnect(sh, "filter_add", FilterAddMultiHandler, this); signal_handler_disconnect(sh, "filter_add", FilterAddMultiHandler, this);
signal_handler_disconnect(sh, "filter_remove", FilterRemoveMultiHandler, this); signal_handler_disconnect(sh, "filter_remove", FilterRemoveMultiHandler, this);
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){ auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param) {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
eventHandler->DisconnectSourceSignals(filter); eventHandler->DisconnectSourceSignals(filter);
}; };
obs_source_enum_filters(source, enumFilters, this); obs_source_enum_filters(source, enumFilters, this);
@ -258,236 +259,233 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_data) void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_data)
{ {
auto eventHandler = static_cast<EventHandler*>(private_data); auto eventHandler = static_cast<EventHandler *>(private_data);
if (!eventHandler->_obsLoaded.load() && event != OBS_FRONTEND_EVENT_FINISHED_LOADING) if (!eventHandler->_obsLoaded.load() && event != OBS_FRONTEND_EVENT_FINISHED_LOADING)
return; return;
switch (event) { switch (event) {
// General // General
case OBS_FRONTEND_EVENT_FINISHED_LOADING: case OBS_FRONTEND_EVENT_FINISHED_LOADING:
blog_debug("[EventHandler::OnFrontendEvent] OBS has finished loading. Connecting final handlers and enabling events..."); blog_debug(
"[EventHandler::OnFrontendEvent] OBS has finished loading. Connecting final handlers and enabling events...");
// Connect source signals and enable events only after OBS has fully loaded (to reduce extra logging). // Connect source signals and enable events only after OBS has fully loaded (to reduce extra logging).
eventHandler->_obsLoaded.store(true); eventHandler->_obsLoaded.store(true);
// In the case that plugins become hotloadable, this will have to go back into `EventHandler::EventHandler()` // In the case that plugins become hotloadable, this will have to go back into `EventHandler::EventHandler()`
// Enumerate inputs and connect each one // Enumerate inputs and connect each one
{ {
auto enumInputs = [](void *param, obs_source_t *source) { auto enumInputs = [](void *param, obs_source_t *source) {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
eventHandler->ConnectSourceSignals(source); eventHandler->ConnectSourceSignals(source);
return true; return true;
}; };
obs_enum_sources(enumInputs, private_data); obs_enum_sources(enumInputs, private_data);
}
// Enumerate scenes and connect each one
{
auto enumScenes = [](void *param, obs_source_t *source) {
auto eventHandler = static_cast<EventHandler *>(param);
eventHandler->ConnectSourceSignals(source);
return true;
};
obs_enum_scenes(enumScenes, private_data);
}
// Enumerate all scene transitions and connect each one
{
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t *transition = transitions.sources.array[i];
eventHandler->ConnectSourceSignals(transition);
} }
obs_frontend_source_list_free(&transitions);
}
// Enumerate scenes and connect each one blog_debug("[EventHandler::OnFrontendEvent] Finished.");
{
auto enumScenes = [](void *param, obs_source_t *source) { if (eventHandler->_obsLoadedCallback)
auto eventHandler = static_cast<EventHandler*>(param); eventHandler->_obsLoadedCallback();
eventHandler->ConnectSourceSignals(source);
return true; break;
}; case OBS_FRONTEND_EVENT_EXIT:
obs_enum_scenes(enumScenes, private_data); eventHandler->HandleExitStarted();
blog_debug("[EventHandler::OnFrontendEvent] OBS is unloading. Disabling events...");
// Disconnect source signals and disable events when OBS starts unloading (to reduce extra logging).
eventHandler->_obsLoaded.store(false);
// In the case that plugins become hotloadable, this will have to go back into `EventHandler::~EventHandler()`
// Enumerate inputs and disconnect each one
{
auto enumInputs = [](void *param, obs_source_t *source) {
auto eventHandler = static_cast<EventHandler *>(param);
eventHandler->DisconnectSourceSignals(source);
return true;
};
obs_enum_sources(enumInputs, private_data);
}
// Enumerate scenes and disconnect each one
{
auto enumScenes = [](void *param, obs_source_t *source) {
auto eventHandler = static_cast<EventHandler *>(param);
eventHandler->DisconnectSourceSignals(source);
return true;
};
obs_enum_scenes(enumScenes, private_data);
}
// Enumerate all scene transitions and disconnect each one
{
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t *transition = transitions.sources.array[i];
eventHandler->DisconnectSourceSignals(transition);
} }
obs_frontend_source_list_free(&transitions);
}
// Enumerate all scene transitions and connect each one blog_debug("[EventHandler::OnFrontendEvent] Finished.");
{
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t* transition = transitions.sources.array[i];
eventHandler->ConnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
blog_debug("[EventHandler::OnFrontendEvent] Finished."); break;
case OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED:
eventHandler->HandleStudioModeStateChanged(true);
break;
case OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED:
eventHandler->HandleStudioModeStateChanged(false);
break;
if (eventHandler->_obsLoadedCallback) // Config
eventHandler->_obsLoadedCallback(); case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING: {
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t *transition = transitions.sources.array[i];
eventHandler->DisconnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
eventHandler->HandleCurrentSceneCollectionChanging();
break;
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED: {
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t *transition = transitions.sources.array[i];
eventHandler->ConnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
eventHandler->HandleCurrentSceneCollectionChanged();
break;
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED:
eventHandler->HandleSceneCollectionListChanged();
break;
case OBS_FRONTEND_EVENT_PROFILE_CHANGING:
eventHandler->HandleCurrentProfileChanging();
break;
case OBS_FRONTEND_EVENT_PROFILE_CHANGED:
eventHandler->HandleCurrentProfileChanged();
break;
case OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED:
eventHandler->HandleProfileListChanged();
break;
break; // Scenes
case OBS_FRONTEND_EVENT_EXIT: case OBS_FRONTEND_EVENT_SCENE_CHANGED:
eventHandler->HandleExitStarted(); eventHandler->HandleCurrentProgramSceneChanged();
break;
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
eventHandler->HandleCurrentPreviewSceneChanged();
break;
case OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED:
eventHandler->HandleSceneListChanged();
break;
blog_debug("[EventHandler::OnFrontendEvent] OBS is unloading. Disabling events..."); // Transitions
// Disconnect source signals and disable events when OBS starts unloading (to reduce extra logging). case OBS_FRONTEND_EVENT_TRANSITION_CHANGED:
eventHandler->_obsLoaded.store(false); eventHandler->HandleCurrentSceneTransitionChanged();
break;
case OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED: {
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t *transition = transitions.sources.array[i];
eventHandler->ConnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
} break;
case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED:
eventHandler->HandleCurrentSceneTransitionDurationChanged();
break;
// In the case that plugins become hotloadable, this will have to go back into `EventHandler::~EventHandler()` // Outputs
// Enumerate inputs and disconnect each one case OBS_FRONTEND_EVENT_STREAMING_STARTING:
{ eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STARTING);
auto enumInputs = [](void *param, obs_source_t *source) { break;
auto eventHandler = static_cast<EventHandler*>(param); case OBS_FRONTEND_EVENT_STREAMING_STARTED:
eventHandler->DisconnectSourceSignals(source); eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
return true; break;
}; case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
obs_enum_sources(enumInputs, private_data); eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPING);
} break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTING:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STARTING);
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPING:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPING);
break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
break;
case OBS_FRONTEND_EVENT_RECORDING_PAUSED:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_PAUSED);
break;
case OBS_FRONTEND_EVENT_RECORDING_UNPAUSED:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_RESUMED);
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING:
eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STARTING);
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED:
eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING:
eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPING);
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED:
eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
break;
case OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED:
eventHandler->HandleVirtualcamStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
break;
case OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED:
eventHandler->HandleVirtualcamStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
eventHandler->HandleReplayBufferSaved();
break;
// Enumerate scenes and disconnect each one default:
{ break;
auto enumScenes = [](void *param, obs_source_t *source) {
auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->DisconnectSourceSignals(source);
return true;
};
obs_enum_scenes(enumScenes, private_data);
}
// Enumerate all scene transitions and disconnect each one
{
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t* transition = transitions.sources.array[i];
eventHandler->DisconnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
blog_debug("[EventHandler::OnFrontendEvent] Finished.");
break;
case OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED:
eventHandler->HandleStudioModeStateChanged(true);
break;
case OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED:
eventHandler->HandleStudioModeStateChanged(false);
break;
// Config
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:
{
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t* transition = transitions.sources.array[i];
eventHandler->DisconnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
eventHandler->HandleCurrentSceneCollectionChanging();
break;
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED:
{
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t* transition = transitions.sources.array[i];
eventHandler->ConnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
eventHandler->HandleCurrentSceneCollectionChanged();
break;
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED:
eventHandler->HandleSceneCollectionListChanged();
break;
case OBS_FRONTEND_EVENT_PROFILE_CHANGING:
eventHandler->HandleCurrentProfileChanging();
break;
case OBS_FRONTEND_EVENT_PROFILE_CHANGED:
eventHandler->HandleCurrentProfileChanged();
break;
case OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED:
eventHandler->HandleProfileListChanged();
break;
// Scenes
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
eventHandler->HandleCurrentProgramSceneChanged();
break;
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
eventHandler->HandleCurrentPreviewSceneChanged();
break;
case OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED:
eventHandler->HandleSceneListChanged();
break;
// Transitions
case OBS_FRONTEND_EVENT_TRANSITION_CHANGED:
eventHandler->HandleCurrentSceneTransitionChanged();
break;
case OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED:
{
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t* transition = transitions.sources.array[i];
eventHandler->ConnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
break;
case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED:
eventHandler->HandleCurrentSceneTransitionDurationChanged();
break;
// Outputs
case OBS_FRONTEND_EVENT_STREAMING_STARTING:
eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STARTING);
break;
case OBS_FRONTEND_EVENT_STREAMING_STARTED:
eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPING);
break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTING:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STARTING);
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPING:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPING);
break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
break;
case OBS_FRONTEND_EVENT_RECORDING_PAUSED:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_PAUSED);
break;
case OBS_FRONTEND_EVENT_RECORDING_UNPAUSED:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_RESUMED);
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING:
eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STARTING);
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED:
eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING:
eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPING);
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED:
eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
break;
case OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED:
eventHandler->HandleVirtualcamStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
break;
case OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED:
eventHandler->HandleVirtualcamStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
eventHandler->HandleReplayBufferSaved();
break;
default:
break;
} }
} }
// Only called for creation of a public source // Only called for creation of a public source
void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data) void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
// Don't react to signals until OBS has finished loading // Don't react to signals until OBS has finished loading
if (!eventHandler->_obsLoaded.load()) if (!eventHandler->_obsLoaded.load())
@ -500,14 +498,14 @@ void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data)
eventHandler->ConnectSourceSignals(source); eventHandler->ConnectSourceSignals(source);
switch (obs_source_get_type(source)) { switch (obs_source_get_type(source)) {
case OBS_SOURCE_TYPE_INPUT: case OBS_SOURCE_TYPE_INPUT:
eventHandler->HandleInputCreated(source); eventHandler->HandleInputCreated(source);
break; break;
case OBS_SOURCE_TYPE_SCENE: case OBS_SOURCE_TYPE_SCENE:
eventHandler->HandleSceneCreated(source); eventHandler->HandleSceneCreated(source);
break; break;
default: default:
break; break;
} }
} }
@ -515,7 +513,7 @@ void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data)
// Used as a fallback if an input/scene is not explicitly removed // Used as a fallback if an input/scene is not explicitly removed
void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data) void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
// We can't use any smart types here because releasing the source will cause infinite recursion // We can't use any smart types here because releasing the source will cause infinite recursion
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
@ -530,18 +528,18 @@ void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
return; return;
switch (obs_source_get_type(source)) { switch (obs_source_get_type(source)) {
case OBS_SOURCE_TYPE_INPUT: case OBS_SOURCE_TYPE_INPUT:
// Only emit removed if the input has not already been removed. This is the case when removing the last scene item of an input. // Only emit removed if the input has not already been removed. This is the case when removing the last scene item of an input.
if (!obs_source_removed(source)) if (!obs_source_removed(source))
eventHandler->HandleInputRemoved(source); eventHandler->HandleInputRemoved(source);
break; break;
case OBS_SOURCE_TYPE_SCENE: case OBS_SOURCE_TYPE_SCENE:
// Only emit removed if the scene has not already been removed. // Only emit removed if the scene has not already been removed.
if (!obs_source_removed(source)) if (!obs_source_removed(source))
eventHandler->HandleSceneRemoved(source); eventHandler->HandleSceneRemoved(source);
break; break;
default: default:
break; break;
} }
} }
@ -549,7 +547,7 @@ void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
// For example, if an input is "removed" but there is a dangling ref, you still want to know that it shouldn't exist, but it's not guaranteed to be destroyed. // For example, if an input is "removed" but there is a dangling ref, you still want to know that it shouldn't exist, but it's not guaranteed to be destroyed.
void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data) void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
if (!eventHandler->_obsLoaded.load()) if (!eventHandler->_obsLoaded.load())
return; return;
@ -559,20 +557,20 @@ void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data)
return; return;
switch (obs_source_get_type(source)) { switch (obs_source_get_type(source)) {
case OBS_SOURCE_TYPE_INPUT: case OBS_SOURCE_TYPE_INPUT:
eventHandler->HandleInputRemoved(source); eventHandler->HandleInputRemoved(source);
break; break;
case OBS_SOURCE_TYPE_SCENE: case OBS_SOURCE_TYPE_SCENE:
eventHandler->HandleSceneRemoved(source); eventHandler->HandleSceneRemoved(source);
break; break;
default: default:
break; break;
} }
} }
void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data) void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
if (!eventHandler->_obsLoaded.load()) if (!eventHandler->_obsLoaded.load())
return; return;
@ -587,15 +585,15 @@ void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data)
return; return;
switch (obs_source_get_type(source)) { switch (obs_source_get_type(source)) {
case OBS_SOURCE_TYPE_INPUT: case OBS_SOURCE_TYPE_INPUT:
eventHandler->HandleInputNameChanged(source, oldSourceName, sourceName); eventHandler->HandleInputNameChanged(source, oldSourceName, sourceName);
break; break;
case OBS_SOURCE_TYPE_TRANSITION: case OBS_SOURCE_TYPE_TRANSITION:
break; break;
case OBS_SOURCE_TYPE_SCENE: case OBS_SOURCE_TYPE_SCENE:
eventHandler->HandleSceneNameChanged(source, oldSourceName, sourceName); eventHandler->HandleSceneNameChanged(source, oldSourceName, sourceName);
break; break;
default: default:
break; break;
} }
} }

View File

@ -29,123 +29,121 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "../utils/Obs_VolumeMeter.h" #include "../utils/Obs_VolumeMeter.h"
#include "../plugin-macros.generated.h" #include "../plugin-macros.generated.h"
class EventHandler class EventHandler {
{ public:
public: EventHandler();
EventHandler(); ~EventHandler();
~EventHandler();
typedef std::function<void(uint64_t, std::string, json, uint8_t)> BroadcastCallback; typedef std::function<void(uint64_t, std::string, json, uint8_t)> BroadcastCallback;
void SetBroadcastCallback(BroadcastCallback cb); void SetBroadcastCallback(BroadcastCallback cb);
typedef std::function<void()> ObsLoadedCallback; typedef std::function<void()> ObsLoadedCallback;
void SetObsLoadedCallback(ObsLoadedCallback cb); void SetObsLoadedCallback(ObsLoadedCallback cb);
void ProcessSubscription(uint64_t eventSubscriptions); void ProcessSubscription(uint64_t eventSubscriptions);
void ProcessUnsubscription(uint64_t eventSubscriptions); void ProcessUnsubscription(uint64_t eventSubscriptions);
private: private:
BroadcastCallback _broadcastCallback; BroadcastCallback _broadcastCallback;
ObsLoadedCallback _obsLoadedCallback; ObsLoadedCallback _obsLoadedCallback;
std::atomic<bool> _obsLoaded; std::atomic<bool> _obsLoaded;
std::unique_ptr<Utils::Obs::VolumeMeter::Handler> _inputVolumeMetersHandler; std::unique_ptr<Utils::Obs::VolumeMeter::Handler> _inputVolumeMetersHandler;
std::atomic<uint64_t> _inputVolumeMetersRef; std::atomic<uint64_t> _inputVolumeMetersRef;
std::atomic<uint64_t> _inputActiveStateChangedRef; std::atomic<uint64_t> _inputActiveStateChangedRef;
std::atomic<uint64_t> _inputShowStateChangedRef; std::atomic<uint64_t> _inputShowStateChangedRef;
std::atomic<uint64_t> _sceneItemTransformChangedRef; std::atomic<uint64_t> _sceneItemTransformChangedRef;
void ConnectSourceSignals(obs_source_t *source); void ConnectSourceSignals(obs_source_t *source);
void DisconnectSourceSignals(obs_source_t *source); void DisconnectSourceSignals(obs_source_t *source);
void BroadcastEvent(uint64_t requiredIntent, std::string eventType, json eventData = nullptr, uint8_t rpcVersion = 0); void BroadcastEvent(uint64_t requiredIntent, std::string eventType, json eventData = nullptr, uint8_t rpcVersion = 0);
// Signal handler: frontend // Signal handler: frontend
static void OnFrontendEvent(enum obs_frontend_event event, void *private_data); static void OnFrontendEvent(enum obs_frontend_event event, void *private_data);
// Signal handler: libobs // Signal handler: libobs
static void SourceCreatedMultiHandler(void *param, calldata_t *data); static void SourceCreatedMultiHandler(void *param, calldata_t *data);
static void SourceDestroyedMultiHandler(void *param, calldata_t *data); static void SourceDestroyedMultiHandler(void *param, calldata_t *data);
static void SourceRemovedMultiHandler(void *param, calldata_t *data); static void SourceRemovedMultiHandler(void *param, calldata_t *data);
// Signal handler: source // Signal handler: source
static void SourceRenamedMultiHandler(void *param, calldata_t *data); static void SourceRenamedMultiHandler(void *param, calldata_t *data);
static void SourceMediaPauseMultiHandler(void *param, calldata_t *data); static void SourceMediaPauseMultiHandler(void *param, calldata_t *data);
static void SourceMediaPlayMultiHandler(void *param, calldata_t *data); static void SourceMediaPlayMultiHandler(void *param, calldata_t *data);
static void SourceMediaRestartMultiHandler(void *param, calldata_t *data); static void SourceMediaRestartMultiHandler(void *param, calldata_t *data);
static void SourceMediaStopMultiHandler(void *param, calldata_t *data); static void SourceMediaStopMultiHandler(void *param, calldata_t *data);
static void SourceMediaNextMultiHandler(void *param, calldata_t *data); static void SourceMediaNextMultiHandler(void *param, calldata_t *data);
static void SourceMediaPreviousMultiHandler(void *param, calldata_t *data); static void SourceMediaPreviousMultiHandler(void *param, calldata_t *data);
// General
void HandleExitStarted();
void HandleStudioModeStateChanged(bool enabled);
// General // Config
void HandleExitStarted(); void HandleCurrentSceneCollectionChanging();
void HandleStudioModeStateChanged(bool enabled); void HandleCurrentSceneCollectionChanged();
void HandleSceneCollectionListChanged();
void HandleCurrentProfileChanging();
void HandleCurrentProfileChanged();
void HandleProfileListChanged();
// Config // Scenes
void HandleCurrentSceneCollectionChanging(); void HandleSceneCreated(obs_source_t *source);
void HandleCurrentSceneCollectionChanged(); void HandleSceneRemoved(obs_source_t *source);
void HandleSceneCollectionListChanged(); void HandleSceneNameChanged(obs_source_t *source, std::string oldSceneName, std::string sceneName);
void HandleCurrentProfileChanging(); void HandleCurrentProgramSceneChanged();
void HandleCurrentProfileChanged(); void HandleCurrentPreviewSceneChanged();
void HandleProfileListChanged(); void HandleSceneListChanged();
// Scenes // Inputs
void HandleSceneCreated(obs_source_t *source); void HandleInputCreated(obs_source_t *source);
void HandleSceneRemoved(obs_source_t *source); void HandleInputRemoved(obs_source_t *source);
void HandleSceneNameChanged(obs_source_t *source, std::string oldSceneName, std::string sceneName); void HandleInputNameChanged(obs_source_t *source, std::string oldInputName, std::string inputName);
void HandleCurrentProgramSceneChanged(); void HandleInputVolumeMeters(std::vector<json> inputs); // AudioMeter::Handler callback
void HandleCurrentPreviewSceneChanged(); static void HandleInputActiveStateChanged(void *param, calldata_t *data); // Direct callback
void HandleSceneListChanged(); static void HandleInputShowStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputMuteStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputVolumeChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioBalanceChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioTracksChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data); // Direct callback
// Inputs // Transitions
void HandleInputCreated(obs_source_t *source); void HandleCurrentSceneTransitionChanged();
void HandleInputRemoved(obs_source_t *source); void HandleCurrentSceneTransitionDurationChanged();
void HandleInputNameChanged(obs_source_t *source, std::string oldInputName, std::string inputName); static void HandleSceneTransitionStarted(void *param, calldata_t *data); // Direct callback
void HandleInputVolumeMeters(std::vector<json> inputs); // AudioMeter::Handler callback static void HandleSceneTransitionEnded(void *param, calldata_t *data); // Direct callback
static void HandleInputActiveStateChanged(void *param, calldata_t *data); // Direct callback static void HandleSceneTransitionVideoEnded(void *param, calldata_t *data); // Direct callback
static void HandleInputShowStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputMuteStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputVolumeChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioBalanceChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioTracksChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data); // Direct callback
// Transitions // Filters
void HandleCurrentSceneTransitionChanged(); static void FilterAddMultiHandler(void *param, calldata_t *data); // Direct callback
void HandleCurrentSceneTransitionDurationChanged(); static void FilterRemoveMultiHandler(void *param, calldata_t *data); // Direct callback
static void HandleSceneTransitionStarted(void *param, calldata_t *data); // Direct callback static void HandleSourceFilterListReindexed(void *param, calldata_t *data); // Direct callback
static void HandleSceneTransitionEnded(void *param, calldata_t *data); // Direct callback void HandleSourceFilterCreated(obs_source_t *source, obs_source_t *filter);
static void HandleSceneTransitionVideoEnded(void *param, calldata_t *data); // Direct callback void HandleSourceFilterRemoved(obs_source_t *source, obs_source_t *filter);
static void HandleSourceFilterNameChanged(void *param, calldata_t *data); // Direct callback
static void HandleSourceFilterEnableStateChanged(void *param, calldata_t *data); // Direct callback
// Filters // Outputs
static void FilterAddMultiHandler(void *param, calldata_t *data); // Direct callback void HandleStreamStateChanged(ObsOutputState state);
static void FilterRemoveMultiHandler(void *param, calldata_t *data); // Direct callback void HandleRecordStateChanged(ObsOutputState state);
static void HandleSourceFilterListReindexed(void *param, calldata_t *data); // Direct callback void HandleReplayBufferStateChanged(ObsOutputState state);
void HandleSourceFilterCreated(obs_source_t *source, obs_source_t *filter); void HandleVirtualcamStateChanged(ObsOutputState state);
void HandleSourceFilterRemoved(obs_source_t *source, obs_source_t *filter); void HandleReplayBufferSaved();
static void HandleSourceFilterNameChanged(void *param, calldata_t *data); // Direct callback
static void HandleSourceFilterEnableStateChanged(void *param, calldata_t *data); // Direct callback
// Outputs // Scene Items
void HandleStreamStateChanged(ObsOutputState state); static void HandleSceneItemCreated(void *param, calldata_t *data); // Direct callback
void HandleRecordStateChanged(ObsOutputState state); static void HandleSceneItemRemoved(void *param, calldata_t *data); // Direct callback
void HandleReplayBufferStateChanged(ObsOutputState state); static void HandleSceneItemListReindexed(void *param, calldata_t *data); // Direct callback
void HandleVirtualcamStateChanged(ObsOutputState state); static void HandleSceneItemEnableStateChanged(void *param, calldata_t *data); // Direct callback
void HandleReplayBufferSaved(); static void HandleSceneItemLockStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemSelected(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemTransformChanged(void *param, calldata_t *data); // Direct callback
// Scene Items // Media Inputs
static void HandleSceneItemCreated(void *param, calldata_t *data); // Direct callback static void HandleMediaInputPlaybackStarted(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemRemoved(void *param, calldata_t *data); // Direct callback static void HandleMediaInputPlaybackEnded(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemListReindexed(void *param, calldata_t *data); // Direct callback void HandleMediaInputActionTriggered(obs_source_t *source, ObsMediaInputAction action);
static void HandleSceneItemEnableStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemLockStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemSelected(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemTransformChanged(void *param, calldata_t *data); // Direct callback
// Media Inputs
static void HandleMediaInputPlaybackStarted(void *param, calldata_t *data); // Direct callback
static void HandleMediaInputPlaybackEnded(void *param, calldata_t *data); // Direct callback
void HandleMediaInputActionTriggered(obs_source_t *source, ObsMediaInputAction action);
}; };

View File

@ -21,7 +21,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
void EventHandler::FilterAddMultiHandler(void *param, calldata_t *data) void EventHandler::FilterAddMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter"); obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter");
@ -36,7 +36,7 @@ void EventHandler::FilterAddMultiHandler(void *param, calldata_t *data)
void EventHandler::FilterRemoveMultiHandler(void *param, calldata_t *data) void EventHandler::FilterRemoveMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter"); obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter");
@ -65,7 +65,7 @@ void EventHandler::FilterRemoveMultiHandler(void *param, calldata_t *data)
*/ */
void EventHandler::HandleSourceFilterListReindexed(void *param, calldata_t *data) void EventHandler::HandleSourceFilterListReindexed(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -150,7 +150,7 @@ void EventHandler::HandleSourceFilterRemoved(obs_source_t *source, obs_source_t
*/ */
void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data) void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source");
if (!filter) if (!filter)
@ -180,7 +180,7 @@ void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleSourceFilterEnableStateChanged(void *param, calldata_t *data) void EventHandler::HandleSourceFilterEnableStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source");
if (!filter) if (!filter)

View File

@ -111,7 +111,7 @@ void EventHandler::HandleInputNameChanged(obs_source_t *, std::string oldInputNa
*/ */
void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data) void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
if (!eventHandler->_inputActiveStateChangedRef.load()) if (!eventHandler->_inputActiveStateChangedRef.load())
return; return;
@ -147,7 +147,7 @@ void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data) void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
if (!eventHandler->_inputShowStateChangedRef.load()) if (!eventHandler->_inputShowStateChangedRef.load())
return; return;
@ -181,7 +181,7 @@ void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data) void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -213,7 +213,7 @@ void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data) void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -252,7 +252,7 @@ void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data) void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -285,7 +285,7 @@ void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data) void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -318,7 +318,7 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
*/ */
void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data) void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -362,7 +362,7 @@ void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data) void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -373,11 +373,9 @@ void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *d
enum obs_monitoring_type monitorType = (obs_monitoring_type)calldata_int(data, "type"); enum obs_monitoring_type monitorType = (obs_monitoring_type)calldata_int(data, "type");
std::string monitorTypeString = Utils::Obs::StringHelper::GetInputMonitorType(monitorType);
json eventData; json eventData;
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventData["monitorType"] = monitorTypeString; eventData["monitorType"] = monitorType;
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioMonitorTypeChanged", eventData); eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioMonitorTypeChanged", eventData);
} }

View File

@ -19,11 +19,14 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "EventHandler.h" #include "EventHandler.h"
#define CASE(x) case x: return #x; #define CASE(x) \
case x: \
return #x;
std::string GetMediaInputActionString(ObsMediaInputAction action) { std::string GetMediaInputActionString(ObsMediaInputAction action)
{
switch (action) { switch (action) {
default: default:
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE) CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE)
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY) CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY)
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART) CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART)
@ -35,7 +38,7 @@ std::string GetMediaInputActionString(ObsMediaInputAction action) {
void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -49,7 +52,7 @@ void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -63,7 +66,7 @@ void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -77,7 +80,7 @@ void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -91,7 +94,7 @@ void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -105,7 +108,7 @@ void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -132,7 +135,7 @@ void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data
*/ */
void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data) void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -161,7 +164,7 @@ void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data
*/ */
void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data) void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)

View File

@ -19,18 +19,19 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "EventHandler.h" #include "EventHandler.h"
static bool GetOutputStateActive(ObsOutputState state) { static bool GetOutputStateActive(ObsOutputState state)
switch(state) { {
case OBS_WEBSOCKET_OUTPUT_STARTED: switch (state) {
case OBS_WEBSOCKET_OUTPUT_RESUMED: case OBS_WEBSOCKET_OUTPUT_STARTED:
return true; case OBS_WEBSOCKET_OUTPUT_RESUMED:
case OBS_WEBSOCKET_OUTPUT_STARTING: return true;
case OBS_WEBSOCKET_OUTPUT_STOPPING: case OBS_WEBSOCKET_OUTPUT_STARTING:
case OBS_WEBSOCKET_OUTPUT_STOPPED: case OBS_WEBSOCKET_OUTPUT_STOPPING:
case OBS_WEBSOCKET_OUTPUT_PAUSED: case OBS_WEBSOCKET_OUTPUT_STOPPED:
return false; case OBS_WEBSOCKET_OUTPUT_PAUSED:
default: return false;
return false; default:
return false;
} }
} }
@ -52,7 +53,7 @@ void EventHandler::HandleStreamStateChanged(ObsOutputState state)
{ {
json eventData; json eventData;
eventData["outputActive"] = GetOutputStateActive(state); eventData["outputActive"] = GetOutputStateActive(state);
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state); eventData["outputState"] = state;
BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged", eventData); BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged", eventData);
} }
@ -61,6 +62,7 @@ void EventHandler::HandleStreamStateChanged(ObsOutputState state)
* *
* @dataField outputActive | Boolean | Whether the output is active * @dataField outputActive | Boolean | Whether the output is active
* @dataField outputState | String | The specific state of the output * @dataField outputState | String | The specific state of the output
* @dataField outputPath | String | File name for the saved recording, if record stopped. `null` otherwise
* *
* @eventType RecordStateChanged * @eventType RecordStateChanged
* @eventSubscription Outputs * @eventSubscription Outputs
@ -74,7 +76,12 @@ void EventHandler::HandleRecordStateChanged(ObsOutputState state)
{ {
json eventData; json eventData;
eventData["outputActive"] = GetOutputStateActive(state); eventData["outputActive"] = GetOutputStateActive(state);
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state); eventData["outputState"] = state;
if (state == OBS_WEBSOCKET_OUTPUT_STOPPED) {
eventData["outputPath"] = Utils::Obs::StringHelper::GetLastRecordFileName();
} else {
eventData["outputPath"] = nullptr;
}
BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged", eventData); BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged", eventData);
} }
@ -96,7 +103,7 @@ void EventHandler::HandleReplayBufferStateChanged(ObsOutputState state)
{ {
json eventData; json eventData;
eventData["outputActive"] = GetOutputStateActive(state); eventData["outputActive"] = GetOutputStateActive(state);
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state); eventData["outputState"] = state;
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged", eventData); BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged", eventData);
} }
@ -118,7 +125,7 @@ void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
{ {
json eventData; json eventData;
eventData["outputActive"] = GetOutputStateActive(state); eventData["outputActive"] = GetOutputStateActive(state);
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state); eventData["outputState"] = state;
BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged", eventData); BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged", eventData);
} }
@ -138,6 +145,6 @@ void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
void EventHandler::HandleReplayBufferSaved() void EventHandler::HandleReplayBufferSaved()
{ {
json eventData; json eventData;
eventData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFilePath(); eventData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFileName();
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferSaved", eventData); BroadcastEvent(EventSubscription::Outputs, "ReplayBufferSaved", eventData);
} }

View File

@ -37,7 +37,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data) void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene"); obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene) if (!scene)
@ -74,7 +74,7 @@ void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
*/ */
void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data) void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene"); obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene) if (!scene)
@ -107,7 +107,7 @@ void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
*/ */
void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data) void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene"); obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene) if (!scene)
@ -136,7 +136,7 @@ void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
*/ */
void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *data) void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene"); obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene) if (!scene)
@ -172,7 +172,7 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
*/ */
void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data) void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene"); obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene) if (!scene)
@ -207,7 +207,7 @@ void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data
*/ */
void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data) void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene"); obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene) if (!scene)
@ -240,7 +240,7 @@ void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
*/ */
void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data) void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
if (!eventHandler->_sceneItemTransformChangedRef.load()) if (!eventHandler->_sceneItemTransformChangedRef.load())
return; return;

View File

@ -76,7 +76,7 @@ void EventHandler::HandleCurrentSceneTransitionDurationChanged()
*/ */
void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data) void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -104,7 +104,7 @@ void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data)
*/ */
void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data) void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -135,7 +135,7 @@ void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data)
*/ */
void EventHandler::HandleSceneTransitionVideoEnded(void *param, calldata_t *data) void EventHandler::HandleSceneTransitionVideoEnded(void *param, calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)

View File

@ -157,13 +157,15 @@ namespace EventSubscription {
* Helper to receive all non-high-volume events. * Helper to receive all non-high-volume events.
* *
* @enumIdentifier All * @enumIdentifier All
* @enumValue (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Vendors) * @enumValue (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Vendors | Ui)
* @enumType EventSubscription * @enumType EventSubscription
* @rpcVersion -1 * @rpcVersion -1
* @initialVersion 5.0.0 * @initialVersion 5.0.0
* @api enums * @api enums
*/ */
All = (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Ui | Vendors), All = (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Vendors |
Ui),
/** /**
* Subscription value to receive the `InputVolumeMeters` high-volume event. * Subscription value to receive the `InputVolumeMeters` high-volume event.
* *

View File

@ -28,18 +28,13 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "../Config.h" #include "../Config.h"
#include "../utils/Platform.h" #include "../utils/Platform.h"
ConnectInfo::ConnectInfo(QWidget* parent) : ConnectInfo::ConnectInfo(QWidget *parent) : QDialog(parent, Qt::Dialog), ui(new Ui::ConnectInfo)
QDialog(parent, Qt::Dialog),
ui(new Ui::ConnectInfo)
{ {
ui->setupUi(this); ui->setupUi(this);
connect(ui->copyServerIpButton, &QPushButton::clicked, connect(ui->copyServerIpButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerIpButtonClicked);
this, &ConnectInfo::CopyServerIpButtonClicked); connect(ui->copyServerPortButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerPortButtonClicked);
connect(ui->copyServerPortButton, &QPushButton::clicked, connect(ui->copyServerPasswordButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerPasswordButtonClicked);
this, &ConnectInfo::CopyServerPortButtonClicked);
connect(ui->copyServerPasswordButton, &QPushButton::clicked,
this, &ConnectInfo::CopyServerPasswordButtonClicked);
} }
ConnectInfo::~ConnectInfo() ConnectInfo::~ConnectInfo()
@ -118,9 +113,9 @@ void ConnectInfo::DrawQr(QString qrText)
const int s = qr.getSize() > 0 ? qr.getSize() : 1; const int s = qr.getSize() > 0 ? qr.getSize() : 1;
const double w = map.width(); const double w = map.width();
const double h = map.height(); const double h = map.height();
const double aspect = w/h; const double aspect = w / h;
const double size = ((aspect > 1.0) ? h : w); const double size = ((aspect > 1.0) ? h : w);
const double scale = size / (s+2); const double scale = size / (s + 2);
painter.setPen(Qt::NoPen); painter.setPen(Qt::NoPen);
painter.setBrush(Qt::black); painter.setBrush(Qt::black);

View File

@ -25,12 +25,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "ui_ConnectInfo.h" #include "ui_ConnectInfo.h"
class ConnectInfo : public QDialog class ConnectInfo : public QDialog {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConnectInfo(QWidget* parent = 0); explicit ConnectInfo(QWidget *parent = 0);
~ConnectInfo(); ~ConnectInfo();
void showEvent(QShowEvent *event); void showEvent(QShowEvent *event);
void RefreshData(); void RefreshData();

View File

@ -37,12 +37,12 @@ QString GetToolTipIconHtml()
return iconTemplate.arg(iconFile); return iconTemplate.arg(iconFile);
} }
SettingsDialog::SettingsDialog(QWidget* parent) : SettingsDialog::SettingsDialog(QWidget *parent)
QDialog(parent, Qt::Dialog), : QDialog(parent, Qt::Dialog),
ui(new Ui::SettingsDialog), ui(new Ui::SettingsDialog),
connectInfo(new ConnectInfo), connectInfo(new ConnectInfo),
sessionTableTimer(new QTimer), sessionTableTimer(new QTimer),
passwordManuallyEdited(false) passwordManuallyEdited(false)
{ {
ui->setupUi(this); ui->setupUi(this);
ui->websocketSessionTable->horizontalHeader()->resizeSection(3, 100); // Resize Session Table column widths ui->websocketSessionTable->horizontalHeader()->resizeSection(3, 100); // Resize Session Table column widths
@ -52,22 +52,15 @@ SettingsDialog::SettingsDialog(QWidget* parent) :
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
// Set the appropriate tooltip icon for the theme // Set the appropriate tooltip icon for the theme
QString toolTipHtml = GetToolTipIconHtml(); ui->enableDebugLoggingToolTipLabel->setText(GetToolTipIconHtml());
ui->enableDebugLoggingToolTipLabel->setText(toolTipHtml);
ui->allowExternalToolTipLabel->setText(toolTipHtml);
connect(sessionTableTimer, &QTimer::timeout, connect(sessionTableTimer, &QTimer::timeout, this, &SettingsDialog::FillSessionTable);
this, &SettingsDialog::FillSessionTable); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &SettingsDialog::DialogButtonClicked);
connect(ui->buttonBox, &QDialogButtonBox::clicked, connect(ui->enableAuthenticationCheckBox, &QCheckBox::stateChanged, this,
this, &SettingsDialog::DialogButtonClicked); &SettingsDialog::EnableAuthenticationCheckBoxChanged);
connect(ui->enableAuthenticationCheckBox, &QCheckBox::stateChanged, connect(ui->generatePasswordButton, &QPushButton::clicked, this, &SettingsDialog::GeneratePasswordButtonClicked);
this, &SettingsDialog::EnableAuthenticationCheckBoxChanged); connect(ui->showConnectInfoButton, &QPushButton::clicked, this, &SettingsDialog::ShowConnectInfoButtonClicked);
connect(ui->generatePasswordButton, &QPushButton::clicked, connect(ui->serverPasswordLineEdit, &QLineEdit::textEdited, this, &SettingsDialog::PasswordEdited);
this, &SettingsDialog::GeneratePasswordButtonClicked);
connect(ui->showConnectInfoButton, &QPushButton::clicked,
this, &SettingsDialog::ShowConnectInfoButtonClicked);
connect(ui->serverPasswordLineEdit, &QLineEdit::textEdited,
this, &SettingsDialog::PasswordEdited);
} }
SettingsDialog::~SettingsDialog() SettingsDialog::~SettingsDialog()
@ -129,16 +122,12 @@ void SettingsDialog::RefreshData()
ui->enableSystemTrayAlertsCheckBox->setChecked(conf->AlertsEnabled); ui->enableSystemTrayAlertsCheckBox->setChecked(conf->AlertsEnabled);
ui->enableDebugLoggingCheckBox->setChecked(conf->DebugEnabled); ui->enableDebugLoggingCheckBox->setChecked(conf->DebugEnabled);
ui->serverPortSpinBox->setValue(conf->ServerPort); ui->serverPortSpinBox->setValue(conf->ServerPort);
ui->allowExternalCheckBox->setChecked(!conf->BindLoopback);
ui->enableAuthenticationCheckBox->setChecked(conf->AuthRequired); ui->enableAuthenticationCheckBox->setChecked(conf->AuthRequired);
ui->serverPasswordLineEdit->setText(conf->ServerPassword); ui->serverPasswordLineEdit->setText(conf->ServerPassword);
ui->showConnectInfoButton->setEnabled(!conf->BindLoopback);
ui->serverPasswordLineEdit->setEnabled(conf->AuthRequired); ui->serverPasswordLineEdit->setEnabled(conf->AuthRequired);
ui->generatePasswordButton->setEnabled(conf->AuthRequired); ui->generatePasswordButton->setEnabled(conf->AuthRequired);
ui->showConnectInfoButton->setToolTip(ui->allowExternalCheckBox->isChecked() ? "" : obs_module_text("OBSWebSocket.Settings.ShowConnectInfoHoverText"));
FillSessionTable(); FillSessionTable();
} }
@ -179,25 +168,24 @@ void SettingsDialog::SaveFormData()
int ret = msgBox.exec(); int ret = msgBox.exec();
switch (ret) { switch (ret) {
case QMessageBox::Yes: case QMessageBox::Yes:
break; break;
case QMessageBox::No: case QMessageBox::No:
default: default:
ui->serverPasswordLineEdit->setText(conf->ServerPassword); ui->serverPasswordLineEdit->setText(conf->ServerPassword);
return; return;
} }
} }
bool needsRestart = (conf->ServerEnabled != ui->enableWebSocketServerCheckBox->isChecked()) || bool needsRestart =
(ui->enableAuthenticationCheckBox->isChecked() && conf->ServerPassword != ui->serverPasswordLineEdit->text()) || (conf->ServerEnabled != ui->enableWebSocketServerCheckBox->isChecked()) ||
(conf->BindLoopback == ui->allowExternalCheckBox->isChecked()) || (conf->ServerPort != ui->serverPortSpinBox->value()) ||
(conf->ServerPort != ui->serverPortSpinBox->value()); (ui->enableAuthenticationCheckBox->isChecked() && conf->ServerPassword != ui->serverPasswordLineEdit->text());
conf->ServerEnabled = ui->enableWebSocketServerCheckBox->isChecked(); conf->ServerEnabled = ui->enableWebSocketServerCheckBox->isChecked();
conf->AlertsEnabled = ui->enableSystemTrayAlertsCheckBox->isChecked(); conf->AlertsEnabled = ui->enableSystemTrayAlertsCheckBox->isChecked();
conf->DebugEnabled = ui->enableDebugLoggingCheckBox->isChecked(); conf->DebugEnabled = ui->enableDebugLoggingCheckBox->isChecked();
conf->ServerPort = ui->serverPortSpinBox->value(); conf->ServerPort = ui->serverPortSpinBox->value();
conf->BindLoopback = !ui->allowExternalCheckBox->isChecked();
conf->AuthRequired = ui->enableAuthenticationCheckBox->isChecked(); conf->AuthRequired = ui->enableAuthenticationCheckBox->isChecked();
conf->ServerPassword = ui->serverPasswordLineEdit->text(); conf->ServerPassword = ui->serverPasswordLineEdit->text();
@ -246,7 +234,8 @@ void SettingsDialog::FillSessionTable()
QTableWidgetItem *durationItem = new QTableWidgetItem(QTime(0, 0, sessionDuration).toString("hh:mm:ss")); QTableWidgetItem *durationItem = new QTableWidgetItem(QTime(0, 0, sessionDuration).toString("hh:mm:ss"));
ui->websocketSessionTable->setItem(i, 1, durationItem); ui->websocketSessionTable->setItem(i, 1, durationItem);
QTableWidgetItem *statsItem = new QTableWidgetItem(QString("%1/%2").arg(session.incomingMessages).arg(session.outgoingMessages)); QTableWidgetItem *statsItem =
new QTableWidgetItem(QString("%1/%2").arg(session.incomingMessages).arg(session.outgoingMessages));
ui->websocketSessionTable->setItem(i, 2, statsItem); ui->websocketSessionTable->setItem(i, 2, statsItem);
QLabel *identifiedLabel = new QLabel(); QLabel *identifiedLabel = new QLabel();
@ -266,9 +255,7 @@ void SettingsDialog::FillSessionTable()
invalidateButtonLayout->setContentsMargins(0, 0, 0, 0); invalidateButtonLayout->setContentsMargins(0, 0, 0, 0);
invalidateButtonWidget->setLayout(invalidateButtonLayout); invalidateButtonWidget->setLayout(invalidateButtonLayout);
ui->websocketSessionTable->setCellWidget(i, 4, invalidateButtonWidget); ui->websocketSessionTable->setCellWidget(i, 4, invalidateButtonWidget);
connect(invalidateButton, &QPushButton::clicked, [=]() { connect(invalidateButton, &QPushButton::clicked, [=]() { webSocketServer->InvalidateSession(session.hdl); });
webSocketServer->InvalidateSession(session.hdl);
});
i++; i++;
} }
@ -305,11 +292,11 @@ void SettingsDialog::ShowConnectInfoButtonClicked()
int ret = msgBox.exec(); int ret = msgBox.exec();
switch (ret) { switch (ret) {
case QMessageBox::Yes: case QMessageBox::Yes:
break; break;
case QMessageBox::No: case QMessageBox::No:
default: default:
return; return;
} }
} }

View File

@ -27,12 +27,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "ui_SettingsDialog.h" #include "ui_SettingsDialog.h"
class SettingsDialog : public QDialog class SettingsDialog : public QDialog {
{
Q_OBJECT Q_OBJECT
public: public:
explicit SettingsDialog(QWidget* parent = 0); explicit SettingsDialog(QWidget *parent = 0);
~SettingsDialog(); ~SettingsDialog();
void showEvent(QShowEvent *event); void showEvent(QShowEvent *event);
void hideEvent(QHideEvent *event); void hideEvent(QHideEvent *event);

View File

@ -155,21 +155,21 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="1" column="1">
<widget class="QCheckBox" name="enableAuthenticationCheckBox"> <widget class="QCheckBox" name="enableAuthenticationCheckBox">
<property name="text"> <property name="text">
<string>OBSWebSocket.Settings.AuthRequired</string> <string>OBSWebSocket.Settings.AuthRequired</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="2" column="0">
<widget class="QLabel" name="serverPasswordLabel"> <widget class="QLabel" name="serverPasswordLabel">
<property name="text"> <property name="text">
<string>OBSWebSocket.Settings.Password</string> <string>OBSWebSocket.Settings.Password</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QLineEdit" name="serverPasswordLineEdit"> <widget class="QLineEdit" name="serverPasswordLineEdit">
@ -187,7 +187,7 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="5" column="0"> <item row="3" column="0">
<spacer name="horizontalSpacer_2"> <spacer name="horizontalSpacer_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -203,47 +203,13 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="5" column="1"> <item row="3" column="1">
<widget class="QPushButton" name="showConnectInfoButton"> <widget class="QPushButton" name="showConnectInfoButton">
<property name="text"> <property name="text">
<string>OBSWebSocket.Settings.ShowConnectInfo</string> <string>OBSWebSocket.Settings.ShowConnectInfo</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="allowExternalCheckBox">
<property name="text">
<string>OBSWebSocket.Settings.AllowExternal</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="allowExternalToolTipLabel">
<property name="toolTip">
<string>OBSWebSocket.Settings.AllowExternalHoverText</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -32,10 +32,16 @@ with this program. If not, see <https://www.gnu.org/licenses/>
OBS_DECLARE_MODULE() OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US") OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
OBS_MODULE_AUTHOR("OBSProject") OBS_MODULE_AUTHOR("OBSProject")
const char *obs_module_name(void) { return "obs-websocket"; } const char *obs_module_name(void)
const char *obs_module_description(void) { return obs_module_text("OBSWebSocket.Plugin.Description"); } {
return "obs-websocket";
}
const char *obs_module_description(void)
{
return obs_module_text("OBSWebSocket.Plugin.Description");
}
os_cpu_usage_info_t* _cpuUsageInfo; os_cpu_usage_info_t *_cpuUsageInfo;
ConfigPtr _config; ConfigPtr _config;
EventHandlerPtr _eventHandler; EventHandlerPtr _eventHandler;
WebSocketApiPtr _webSocketApi; WebSocketApiPtr _webSocketApi;
@ -46,7 +52,8 @@ void WebSocketApiEventCallback(std::string vendorName, std::string eventType, ob
bool obs_module_load(void) bool obs_module_load(void)
{ {
blog(LOG_INFO, "[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_VERSION, OBS_WEBSOCKET_RPC_VERSION); blog(LOG_INFO, "[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_VERSION,
OBS_WEBSOCKET_RPC_VERSION);
blog(LOG_INFO, "[obs_module_load] Qt version (compile-time): %s | Qt version (run-time): %s", QT_VERSION_STR, qVersion()); blog(LOG_INFO, "[obs_module_load] Qt version (compile-time): %s | Qt version (run-time): %s", QT_VERSION_STR, qVersion());
blog(LOG_INFO, "[obs_module_load] Linked ASIO Version: %d", ASIO_VERSION); blog(LOG_INFO, "[obs_module_load] Linked ASIO Version: %d", ASIO_VERSION);
@ -69,13 +76,13 @@ bool obs_module_load(void)
// Initialize the settings dialog // Initialize the settings dialog
obs_frontend_push_ui_translation(obs_module_get_string); obs_frontend_push_ui_translation(obs_module_get_string);
QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window()); QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
_settingsDialog = new SettingsDialog(mainWindow); _settingsDialog = new SettingsDialog(mainWindow);
obs_frontend_pop_ui_translation(); obs_frontend_pop_ui_translation();
// Add the settings dialog to the tools menu // Add the settings dialog to the tools menu
const char* menuActionText = obs_module_text("OBSWebSocket.Settings.DialogTitle"); const char *menuActionText = obs_module_text("OBSWebSocket.Settings.DialogTitle");
QAction* menuAction = (QAction*)obs_frontend_add_tools_menu_qaction(menuActionText); QAction *menuAction = (QAction *)obs_frontend_add_tools_menu_qaction(menuActionText);
QObject::connect(menuAction, &QAction::triggered, [] { _settingsDialog->ToggleShowHide(); }); QObject::connect(menuAction, &QAction::triggered, [] { _settingsDialog->ToggleShowHide(); });
blog(LOG_INFO, "[obs_module_load] Module loaded."); blog(LOG_INFO, "[obs_module_load] Module loaded.");
@ -111,7 +118,7 @@ void obs_module_unload()
blog(LOG_INFO, "[obs_module_unload] Finished shutting down."); blog(LOG_INFO, "[obs_module_unload] Finished shutting down.");
} }
os_cpu_usage_info_t* GetCpuUsageInfo() os_cpu_usage_info_t *GetCpuUsageInfo()
{ {
return _cpuUsageInfo; return _cpuUsageInfo;
} }
@ -197,7 +204,8 @@ void obs_module_post_load()
// Test calling obs-websocket requests // Test calling obs-websocket requests
struct obs_websocket_request_response *response = obs_websocket_call_request("GetVersion"); struct obs_websocket_request_response *response = obs_websocket_call_request("GetVersion");
if (response) { if (response) {
blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s", response->status_code, response->comment, response->response_data); blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
response->status_code, response->comment, response->response_data);
obs_websocket_request_response_free(response); obs_websocket_request_response_free(response);
} }

View File

@ -38,7 +38,7 @@ typedef std::shared_ptr<WebSocketApi> WebSocketApiPtr;
class WebSocketServer; class WebSocketServer;
typedef std::shared_ptr<WebSocketServer> WebSocketServerPtr; typedef std::shared_ptr<WebSocketServer> WebSocketServerPtr;
os_cpu_usage_info_t* GetCpuUsageInfo(); os_cpu_usage_info_t *GetCpuUsageInfo();
ConfigPtr GetConfig(); ConfigPtr GetConfig();

View File

@ -24,8 +24,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "../utils/Compat.h" #include "../utils/Compat.h"
#include "../obs-websocket.h" #include "../obs-websocket.h"
struct SerialFrameBatch struct SerialFrameBatch {
{
RequestHandler &requestHandler; RequestHandler &requestHandler;
std::queue<RequestBatchRequest> requests; std::queue<RequestBatchRequest> requests;
std::vector<RequestResult> results; std::vector<RequestResult> results;
@ -37,43 +36,46 @@ struct SerialFrameBatch
std::mutex conditionMutex; std::mutex conditionMutex;
std::condition_variable condition; std::condition_variable condition;
SerialFrameBatch(RequestHandler &requestHandler, json &variables, bool haltOnFailure) : SerialFrameBatch(RequestHandler &requestHandler, json &variables, bool haltOnFailure)
requestHandler(requestHandler), : requestHandler(requestHandler),
variables(variables), variables(variables),
haltOnFailure(haltOnFailure), haltOnFailure(haltOnFailure),
frameCount(0), frameCount(0),
sleepUntilFrame(0) sleepUntilFrame(0)
{} {
}
}; };
struct ParallelBatchResults struct ParallelBatchResults {
{
RequestHandler &requestHandler; RequestHandler &requestHandler;
std::vector<RequestResult> results; std::vector<RequestResult> results;
std::mutex conditionMutex; std::mutex conditionMutex;
std::condition_variable condition; std::condition_variable condition;
ParallelBatchResults(RequestHandler &requestHandler) : ParallelBatchResults(RequestHandler &requestHandler) : requestHandler(requestHandler) {}
requestHandler(requestHandler)
{}
}; };
// `{"inputName": "inputNameVariable"}` is essentially `inputName = inputNameVariable` // `{"inputName": "inputNameVariable"}` is essentially `inputName = inputNameVariable`
static void PreProcessVariables(const json &variables, RequestBatchRequest &request) static void PreProcessVariables(const json &variables, RequestBatchRequest &request)
{ {
if (variables.empty() || !request.InputVariables.is_object() || request.InputVariables.empty() || !request.RequestData.is_object()) if (variables.empty() || !request.InputVariables.is_object() || request.InputVariables.empty() ||
!request.RequestData.is_object())
return; return;
for (auto& [key, value] : request.InputVariables.items()) { for (auto &[key, value] : request.InputVariables.items()) {
if (!value.is_string()) { if (!value.is_string()) {
blog_debug("[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `inputVariables `is not a string. Skipping!", key.c_str()); blog_debug(
"[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `inputVariables `is not a string. Skipping!",
key.c_str());
continue; continue;
} }
std::string valueString = value; std::string valueString = value;
if (!variables.contains(valueString)) { if (!variables.contains(valueString)) {
blog_debug("[WebSocketServer::ProcessRequestBatch] `inputVariables` requested variable `%s`, but it does not exist. Skipping!", valueString.c_str()); blog_debug(
"[WebSocketServer::ProcessRequestBatch] `inputVariables` requested variable `%s`, but it does not exist. Skipping!",
valueString.c_str());
continue; continue;
} }
@ -89,15 +91,19 @@ static void PostProcessVariables(json &variables, const RequestBatchRequest &req
if (!request.OutputVariables.is_object() || request.OutputVariables.empty() || requestResult.ResponseData.empty()) if (!request.OutputVariables.is_object() || request.OutputVariables.empty() || requestResult.ResponseData.empty())
return; return;
for (auto& [key, value] : request.OutputVariables.items()) { for (auto &[key, value] : request.OutputVariables.items()) {
if (!value.is_string()) { if (!value.is_string()) {
blog_debug("[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `outputVariables` is not a string. Skipping!", key.c_str()); blog_debug(
"[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `outputVariables` is not a string. Skipping!",
key.c_str());
continue; continue;
} }
std::string valueString = value; std::string valueString = value;
if (!requestResult.ResponseData.contains(valueString)) { if (!requestResult.ResponseData.contains(valueString)) {
blog_debug("[WebSocketServer::ProcessRequestBatch] `outputVariables` requested responseData field `%s`, but it does not exist. Skipping!", valueString.c_str()); blog_debug(
"[WebSocketServer::ProcessRequestBatch] `outputVariables` requested responseData field `%s`, but it does not exist. Skipping!",
valueString.c_str());
continue; continue;
} }
@ -109,7 +115,7 @@ static void ObsTickCallback(void *param, float)
{ {
ScopeProfiler prof{"obs_websocket_request_batch_frame_tick"}; ScopeProfiler prof{"obs_websocket_request_batch_frame_tick"};
auto serialFrameBatch = static_cast<SerialFrameBatch*>(param); auto serialFrameBatch = static_cast<SerialFrameBatch *>(param);
// Increment frame count // Increment frame count
serialFrameBatch->frameCount++; serialFrameBatch->frameCount++;
@ -156,7 +162,10 @@ static void ObsTickCallback(void *param, float)
serialFrameBatch->condition.notify_one(); serialFrameBatch->condition.notify_one();
} }
std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session, RequestBatchExecutionType::RequestBatchExecutionType executionType, std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure) std::vector<RequestResult>
RequestBatchHandler::ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session,
RequestBatchExecutionType::RequestBatchExecutionType executionType,
std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure)
{ {
RequestHandler requestHandler(session); RequestHandler requestHandler(session);
if (executionType == RequestBatchExecutionType::SerialRealtime) { if (executionType == RequestBatchExecutionType::SerialRealtime) {
@ -189,7 +198,7 @@ std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(QThreadPool
// Wait until the graphics thread processes the last request in the queue // Wait until the graphics thread processes the last request in the queue
std::unique_lock<std::mutex> lock(serialFrameBatch.conditionMutex); std::unique_lock<std::mutex> lock(serialFrameBatch.conditionMutex);
serialFrameBatch.condition.wait(lock, [&serialFrameBatch]{return serialFrameBatch.requests.empty();}); serialFrameBatch.condition.wait(lock, [&serialFrameBatch] { return serialFrameBatch.requests.empty(); });
// Remove the created callback entry since we don't need it anymore // Remove the created callback entry since we don't need it anymore
obs_remove_tick_callback(ObsTickCallback, &serialFrameBatch); obs_remove_tick_callback(ObsTickCallback, &serialFrameBatch);
@ -215,7 +224,9 @@ std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(QThreadPool
// Wait for the last request to finish processing // Wait for the last request to finish processing
size_t requestCount = requests.size(); size_t requestCount = requests.size();
parallelResults.condition.wait(lock, [&parallelResults, requestCount]{return parallelResults.results.size() == requestCount;}); parallelResults.condition.wait(lock, [&parallelResults, requestCount] {
return parallelResults.results.size() == requestCount;
});
return parallelResults.results; return parallelResults.results;
} }

View File

@ -24,5 +24,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "rpc/RequestBatchRequest.h" #include "rpc/RequestBatchRequest.h"
namespace RequestBatchHandler { namespace RequestBatchHandler {
std::vector<RequestResult> ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session, RequestBatchExecutionType::RequestBatchExecutionType executionType, std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure); std::vector<RequestResult> ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session,
RequestBatchExecutionType::RequestBatchExecutionType executionType,
std::vector<RequestBatchRequest> &requests, json &variables,
bool haltOnFailure);
} }

View File

@ -23,8 +23,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "RequestHandler.h" #include "RequestHandler.h"
const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_handlerMap const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_handlerMap{
{
// General // General
{"GetVersion", &RequestHandler::GetVersion}, {"GetVersion", &RequestHandler::GetVersion},
{"GetStats", &RequestHandler::GetStats}, {"GetStats", &RequestHandler::GetStats},
@ -152,6 +151,13 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"StopReplayBuffer", &RequestHandler::StopReplayBuffer}, {"StopReplayBuffer", &RequestHandler::StopReplayBuffer},
{"SaveReplayBuffer", &RequestHandler::SaveReplayBuffer}, {"SaveReplayBuffer", &RequestHandler::SaveReplayBuffer},
{"GetLastReplayBufferReplay", &RequestHandler::GetLastReplayBufferReplay}, {"GetLastReplayBufferReplay", &RequestHandler::GetLastReplayBufferReplay},
{"GetOutputList", &RequestHandler::GetOutputList},
{"GetOutputStatus", &RequestHandler::GetOutputStatus},
{"ToggleOutput", &RequestHandler::ToggleOutput},
{"StartOutput", &RequestHandler::StartOutput},
{"StopOutput", &RequestHandler::StopOutput},
{"GetOutputSettings", &RequestHandler::GetOutputSettings},
{"SetOutputSettings", &RequestHandler::SetOutputSettings},
// Stream // Stream
{"GetStreamStatus", &RequestHandler::GetStreamStatus}, {"GetStreamStatus", &RequestHandler::GetStreamStatus},
@ -182,14 +188,13 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog}, {"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog},
{"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog}, {"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog},
{"GetMonitorList", &RequestHandler::GetMonitorList}, {"GetMonitorList", &RequestHandler::GetMonitorList},
{"OpenVideoMixProjector", &RequestHandler::OpenVideoMixProjector},
{"OpenSourceProjector", &RequestHandler::OpenSourceProjector},
}; };
RequestHandler::RequestHandler(SessionPtr session) : RequestHandler::RequestHandler(SessionPtr session) : _session(session) {}
_session(session)
{
}
RequestResult RequestHandler::ProcessRequest(const Request& request) RequestResult RequestHandler::ProcessRequest(const Request &request)
{ {
#ifdef PLUGIN_TESTS #ifdef PLUGIN_TESTS
ScopeProfiler prof{"obs_websocket_request_processing"}; ScopeProfiler prof{"obs_websocket_request_processing"};
@ -199,12 +204,12 @@ RequestResult RequestHandler::ProcessRequest(const Request& request)
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object."); return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object.");
if (request.RequestType.empty()) if (request.RequestType.empty())
return RequestResult::Error(RequestStatus::MissingRequestType, "Your request is missing a `requestType`"); return RequestResult::Error(RequestStatus::MissingRequestType, "Your request's `requestType` may not be empty.");
RequestMethodHandler handler; RequestMethodHandler handler;
try { try {
handler = _handlerMap.at(request.RequestType); handler = _handlerMap.at(request.RequestType);
} catch (const std::out_of_range& oor) { } catch (const std::out_of_range &oor) {
return RequestResult::Error(RequestStatus::UnknownRequestType, "Your request type is not valid."); return RequestResult::Error(RequestStatus::UnknownRequestType, "Your request type is not valid.");
} }
@ -214,7 +219,7 @@ RequestResult RequestHandler::ProcessRequest(const Request& request)
std::vector<std::string> RequestHandler::GetRequestList() std::vector<std::string> RequestHandler::GetRequestList()
{ {
std::vector<std::string> ret; std::vector<std::string> ret;
for (auto const& [key, val] : _handlerMap) { for (auto const &[key, val] : _handlerMap) {
ret.push_back(key); ret.push_back(key);
} }

View File

@ -33,174 +33,183 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "../plugin-macros.generated.h" #include "../plugin-macros.generated.h"
class RequestHandler; class RequestHandler;
typedef RequestResult(RequestHandler::*RequestMethodHandler)(const Request&); typedef RequestResult (RequestHandler::*RequestMethodHandler)(const Request &);
class RequestHandler { class RequestHandler {
public: public:
RequestHandler(SessionPtr session = nullptr); RequestHandler(SessionPtr session = nullptr);
RequestResult ProcessRequest(const Request& request); RequestResult ProcessRequest(const Request &request);
std::vector<std::string> GetRequestList(); std::vector<std::string> GetRequestList();
private: private:
// General // General
RequestResult GetVersion(const Request&); RequestResult GetVersion(const Request &);
RequestResult GetStats(const Request&); RequestResult GetStats(const Request &);
RequestResult BroadcastCustomEvent(const Request&); RequestResult BroadcastCustomEvent(const Request &);
RequestResult CallVendorRequest(const Request&); RequestResult CallVendorRequest(const Request &);
RequestResult GetHotkeyList(const Request&); RequestResult GetHotkeyList(const Request &);
RequestResult TriggerHotkeyByName(const Request&); RequestResult TriggerHotkeyByName(const Request &);
RequestResult TriggerHotkeyByKeySequence(const Request&); RequestResult TriggerHotkeyByKeySequence(const Request &);
RequestResult Sleep(const Request&); RequestResult Sleep(const Request &);
// Config // Config
RequestResult GetPersistentData(const Request&); RequestResult GetPersistentData(const Request &);
RequestResult SetPersistentData(const Request&); RequestResult SetPersistentData(const Request &);
RequestResult GetSceneCollectionList(const Request&); RequestResult GetSceneCollectionList(const Request &);
RequestResult SetCurrentSceneCollection(const Request&); RequestResult SetCurrentSceneCollection(const Request &);
RequestResult CreateSceneCollection(const Request&); RequestResult CreateSceneCollection(const Request &);
RequestResult GetProfileList(const Request&); RequestResult GetProfileList(const Request &);
RequestResult SetCurrentProfile(const Request&); RequestResult SetCurrentProfile(const Request &);
RequestResult CreateProfile(const Request&); RequestResult CreateProfile(const Request &);
RequestResult RemoveProfile(const Request&); RequestResult RemoveProfile(const Request &);
RequestResult GetProfileParameter(const Request&); RequestResult GetProfileParameter(const Request &);
RequestResult SetProfileParameter(const Request&); RequestResult SetProfileParameter(const Request &);
RequestResult GetVideoSettings(const Request&); RequestResult GetVideoSettings(const Request &);
RequestResult SetVideoSettings(const Request&); RequestResult SetVideoSettings(const Request &);
RequestResult GetStreamServiceSettings(const Request&); RequestResult GetStreamServiceSettings(const Request &);
RequestResult SetStreamServiceSettings(const Request&); RequestResult SetStreamServiceSettings(const Request &);
RequestResult GetRecordDirectory(const Request&); RequestResult GetRecordDirectory(const Request &);
// Sources // Sources
RequestResult GetSourceActive(const Request&); RequestResult GetSourceActive(const Request &);
RequestResult GetSourceScreenshot(const Request&); RequestResult GetSourceScreenshot(const Request &);
RequestResult SaveSourceScreenshot(const Request&); RequestResult SaveSourceScreenshot(const Request &);
RequestResult GetSourcePrivateSettings(const Request&); RequestResult GetSourcePrivateSettings(const Request &);
RequestResult SetSourcePrivateSettings(const Request&); RequestResult SetSourcePrivateSettings(const Request &);
// Scenes // Scenes
RequestResult GetSceneList(const Request&); RequestResult GetSceneList(const Request &);
RequestResult GetGroupList(const Request&); RequestResult GetGroupList(const Request &);
RequestResult GetCurrentProgramScene(const Request&); RequestResult GetCurrentProgramScene(const Request &);
RequestResult SetCurrentProgramScene(const Request&); RequestResult SetCurrentProgramScene(const Request &);
RequestResult GetCurrentPreviewScene(const Request&); RequestResult GetCurrentPreviewScene(const Request &);
RequestResult SetCurrentPreviewScene(const Request&); RequestResult SetCurrentPreviewScene(const Request &);
RequestResult CreateScene(const Request&); RequestResult CreateScene(const Request &);
RequestResult RemoveScene(const Request&); RequestResult RemoveScene(const Request &);
RequestResult SetSceneName(const Request&); RequestResult SetSceneName(const Request &);
RequestResult GetSceneSceneTransitionOverride(const Request&); RequestResult GetSceneSceneTransitionOverride(const Request &);
RequestResult SetSceneSceneTransitionOverride(const Request&); RequestResult SetSceneSceneTransitionOverride(const Request &);
// Inputs // Inputs
RequestResult GetInputList(const Request&); RequestResult GetInputList(const Request &);
RequestResult GetInputKindList(const Request&); RequestResult GetInputKindList(const Request &);
RequestResult GetSpecialInputs(const Request&); RequestResult GetSpecialInputs(const Request &);
RequestResult CreateInput(const Request&); RequestResult CreateInput(const Request &);
RequestResult RemoveInput(const Request&); RequestResult RemoveInput(const Request &);
RequestResult SetInputName(const Request&); RequestResult SetInputName(const Request &);
RequestResult GetInputDefaultSettings(const Request&); RequestResult GetInputDefaultSettings(const Request &);
RequestResult GetInputSettings(const Request&); RequestResult GetInputSettings(const Request &);
RequestResult SetInputSettings(const Request&); RequestResult SetInputSettings(const Request &);
RequestResult GetInputMute(const Request&); RequestResult GetInputMute(const Request &);
RequestResult SetInputMute(const Request&); RequestResult SetInputMute(const Request &);
RequestResult ToggleInputMute(const Request&); RequestResult ToggleInputMute(const Request &);
RequestResult GetInputVolume(const Request&); RequestResult GetInputVolume(const Request &);
RequestResult SetInputVolume(const Request&); RequestResult SetInputVolume(const Request &);
RequestResult GetInputAudioBalance(const Request&); RequestResult GetInputAudioBalance(const Request &);
RequestResult SetInputAudioBalance(const Request&); RequestResult SetInputAudioBalance(const Request &);
RequestResult GetInputAudioSyncOffset(const Request&); RequestResult GetInputAudioSyncOffset(const Request &);
RequestResult SetInputAudioSyncOffset(const Request&); RequestResult SetInputAudioSyncOffset(const Request &);
RequestResult GetInputAudioMonitorType(const Request&); RequestResult GetInputAudioMonitorType(const Request &);
RequestResult SetInputAudioMonitorType(const Request&); RequestResult SetInputAudioMonitorType(const Request &);
RequestResult GetInputAudioTracks(const Request&); RequestResult GetInputAudioTracks(const Request &);
RequestResult SetInputAudioTracks(const Request&); RequestResult SetInputAudioTracks(const Request &);
RequestResult GetInputPropertiesListPropertyItems(const Request&); RequestResult GetInputPropertiesListPropertyItems(const Request &);
RequestResult PressInputPropertiesButton(const Request&); RequestResult PressInputPropertiesButton(const Request &);
// Transitions // Transitions
RequestResult GetTransitionKindList(const Request&); RequestResult GetTransitionKindList(const Request &);
RequestResult GetSceneTransitionList(const Request&); RequestResult GetSceneTransitionList(const Request &);
RequestResult GetCurrentSceneTransition(const Request&); RequestResult GetCurrentSceneTransition(const Request &);
RequestResult SetCurrentSceneTransition(const Request&); RequestResult SetCurrentSceneTransition(const Request &);
RequestResult SetCurrentSceneTransitionDuration(const Request&); RequestResult SetCurrentSceneTransitionDuration(const Request &);
RequestResult SetCurrentSceneTransitionSettings(const Request&); RequestResult SetCurrentSceneTransitionSettings(const Request &);
RequestResult GetCurrentSceneTransitionCursor(const Request&); RequestResult GetCurrentSceneTransitionCursor(const Request &);
RequestResult TriggerStudioModeTransition(const Request&); RequestResult TriggerStudioModeTransition(const Request &);
RequestResult SetTBarPosition(const Request&); RequestResult SetTBarPosition(const Request &);
// Filters // Filters
RequestResult GetSourceFilterList(const Request&); RequestResult GetSourceFilterList(const Request &);
RequestResult GetSourceFilterDefaultSettings(const Request&); RequestResult GetSourceFilterDefaultSettings(const Request &);
RequestResult CreateSourceFilter(const Request&); RequestResult CreateSourceFilter(const Request &);
RequestResult RemoveSourceFilter(const Request&); RequestResult RemoveSourceFilter(const Request &);
RequestResult SetSourceFilterName(const Request&); RequestResult SetSourceFilterName(const Request &);
RequestResult GetSourceFilter(const Request&); RequestResult GetSourceFilter(const Request &);
RequestResult SetSourceFilterIndex(const Request&); RequestResult SetSourceFilterIndex(const Request &);
RequestResult SetSourceFilterSettings(const Request&); RequestResult SetSourceFilterSettings(const Request &);
RequestResult SetSourceFilterEnabled(const Request&); RequestResult SetSourceFilterEnabled(const Request &);
// Scene Items // Scene Items
RequestResult GetSceneItemList(const Request&); RequestResult GetSceneItemList(const Request &);
RequestResult GetGroupSceneItemList(const Request&); RequestResult GetGroupSceneItemList(const Request &);
RequestResult GetSceneItemId(const Request&); RequestResult GetSceneItemId(const Request &);
RequestResult CreateSceneItem(const Request&); RequestResult CreateSceneItem(const Request &);
RequestResult RemoveSceneItem(const Request&); RequestResult RemoveSceneItem(const Request &);
RequestResult DuplicateSceneItem(const Request&); RequestResult DuplicateSceneItem(const Request &);
RequestResult GetSceneItemTransform(const Request&); RequestResult GetSceneItemTransform(const Request &);
RequestResult SetSceneItemTransform(const Request&); RequestResult SetSceneItemTransform(const Request &);
RequestResult GetSceneItemEnabled(const Request&); RequestResult GetSceneItemEnabled(const Request &);
RequestResult SetSceneItemEnabled(const Request&); RequestResult SetSceneItemEnabled(const Request &);
RequestResult GetSceneItemLocked(const Request&); RequestResult GetSceneItemLocked(const Request &);
RequestResult SetSceneItemLocked(const Request&); RequestResult SetSceneItemLocked(const Request &);
RequestResult GetSceneItemIndex(const Request&); RequestResult GetSceneItemIndex(const Request &);
RequestResult SetSceneItemIndex(const Request&); RequestResult SetSceneItemIndex(const Request &);
RequestResult GetSceneItemBlendMode(const Request&); RequestResult GetSceneItemBlendMode(const Request &);
RequestResult SetSceneItemBlendMode(const Request&); RequestResult SetSceneItemBlendMode(const Request &);
RequestResult GetSceneItemPrivateSettings(const Request&); RequestResult GetSceneItemPrivateSettings(const Request &);
RequestResult SetSceneItemPrivateSettings(const Request&); RequestResult SetSceneItemPrivateSettings(const Request &);
// Outputs // Outputs
RequestResult GetVirtualCamStatus(const Request&); RequestResult GetVirtualCamStatus(const Request &);
RequestResult ToggleVirtualCam(const Request&); RequestResult ToggleVirtualCam(const Request &);
RequestResult StartVirtualCam(const Request&); RequestResult StartVirtualCam(const Request &);
RequestResult StopVirtualCam(const Request&); RequestResult StopVirtualCam(const Request &);
RequestResult GetReplayBufferStatus(const Request&); RequestResult GetReplayBufferStatus(const Request &);
RequestResult ToggleReplayBuffer(const Request&); RequestResult ToggleReplayBuffer(const Request &);
RequestResult StartReplayBuffer(const Request&); RequestResult StartReplayBuffer(const Request &);
RequestResult StopReplayBuffer(const Request&); RequestResult StopReplayBuffer(const Request &);
RequestResult SaveReplayBuffer(const Request&); RequestResult SaveReplayBuffer(const Request &);
RequestResult GetLastReplayBufferReplay(const Request&); RequestResult GetLastReplayBufferReplay(const Request &);
RequestResult GetOutputList(const Request &);
RequestResult GetOutputStatus(const Request &);
RequestResult ToggleOutput(const Request &);
RequestResult StartOutput(const Request &);
RequestResult StopOutput(const Request &);
RequestResult GetOutputSettings(const Request &);
RequestResult SetOutputSettings(const Request &);
// Stream // Stream
RequestResult GetStreamStatus(const Request&); RequestResult GetStreamStatus(const Request &);
RequestResult ToggleStream(const Request&); RequestResult ToggleStream(const Request &);
RequestResult StartStream(const Request&); RequestResult StartStream(const Request &);
RequestResult StopStream(const Request&); RequestResult StopStream(const Request &);
RequestResult SendStreamCaption(const Request&); RequestResult SendStreamCaption(const Request &);
// Record // Record
RequestResult GetRecordStatus(const Request&); RequestResult GetRecordStatus(const Request &);
RequestResult ToggleRecord(const Request&); RequestResult ToggleRecord(const Request &);
RequestResult StartRecord(const Request&); RequestResult StartRecord(const Request &);
RequestResult StopRecord(const Request&); RequestResult StopRecord(const Request &);
RequestResult ToggleRecordPause(const Request&); RequestResult ToggleRecordPause(const Request &);
RequestResult PauseRecord(const Request&); RequestResult PauseRecord(const Request &);
RequestResult ResumeRecord(const Request&); RequestResult ResumeRecord(const Request &);
// Media Inputs // Media Inputs
RequestResult GetMediaInputStatus(const Request&); RequestResult GetMediaInputStatus(const Request &);
RequestResult SetMediaInputCursor(const Request&); RequestResult SetMediaInputCursor(const Request &);
RequestResult OffsetMediaInputCursor(const Request&); RequestResult OffsetMediaInputCursor(const Request &);
RequestResult TriggerMediaInputAction(const Request&); RequestResult TriggerMediaInputAction(const Request &);
// Ui // Ui
RequestResult GetStudioModeEnabled(const Request&); RequestResult GetStudioModeEnabled(const Request &);
RequestResult SetStudioModeEnabled(const Request&); RequestResult SetStudioModeEnabled(const Request &);
RequestResult OpenInputPropertiesDialog(const Request&); RequestResult OpenInputPropertiesDialog(const Request &);
RequestResult OpenInputFiltersDialog(const Request&); RequestResult OpenInputFiltersDialog(const Request &);
RequestResult OpenInputInteractDialog(const Request&); RequestResult OpenInputInteractDialog(const Request &);
RequestResult GetMonitorList(const Request&); RequestResult GetMonitorList(const Request &);
RequestResult OpenVideoMixProjector(const Request &);
RequestResult OpenSourceProjector(const Request &);
SessionPtr _session; SessionPtr _session;
static const std::unordered_map<std::string, RequestMethodHandler> _handlerMap; static const std::unordered_map<std::string, RequestMethodHandler> _handlerMap;
}; };

View File

@ -37,7 +37,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetPersistentData(const Request& request) RequestResult RequestHandler::GetPersistentData(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -53,7 +53,8 @@ RequestResult RequestHandler::GetPersistentData(const Request& request)
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE") else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
persistentDataPath += "/obsWebSocketPersistentData.json"; persistentDataPath += "/obsWebSocketPersistentData.json";
else else
return RequestResult::Error(RequestStatus::ResourceNotFound, "You have specified an invalid persistent data realm."); return RequestResult::Error(RequestStatus::ResourceNotFound,
"You have specified an invalid persistent data realm.");
json responseData; json responseData;
json persistentData; json persistentData;
@ -79,11 +80,12 @@ RequestResult RequestHandler::GetPersistentData(const Request& request)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::SetPersistentData(const Request& request) RequestResult RequestHandler::SetPersistentData(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!(request.ValidateString("realm", statusCode, comment) && request.ValidateString("slotName", statusCode, comment) && request.ValidateBasic("slotValue", statusCode, comment))) if (!(request.ValidateString("realm", statusCode, comment) && request.ValidateString("slotName", statusCode, comment) &&
request.ValidateBasic("slotValue", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string realm = request.RequestData["realm"]; std::string realm = request.RequestData["realm"];
@ -96,13 +98,15 @@ RequestResult RequestHandler::SetPersistentData(const Request& request)
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE") else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
persistentDataPath += "/obsWebSocketPersistentData.json"; persistentDataPath += "/obsWebSocketPersistentData.json";
else else
return RequestResult::Error(RequestStatus::ResourceNotFound, "You have specified an invalid persistent data realm."); return RequestResult::Error(RequestStatus::ResourceNotFound,
"You have specified an invalid persistent data realm.");
json persistentData = json::object(); json persistentData = json::object();
Utils::Json::GetJsonFileContent(persistentDataPath, persistentData); Utils::Json::GetJsonFileContent(persistentDataPath, persistentData);
persistentData[slotName] = slotValue; persistentData[slotName] = slotValue;
if (!Utils::Json::SetJsonFileContent(persistentDataPath, persistentData)) if (!Utils::Json::SetJsonFileContent(persistentDataPath, persistentData))
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to write persistent data. No permissions?"); return RequestResult::Error(RequestStatus::RequestProcessingFailed,
"Unable to write persistent data. No permissions?");
return RequestResult::Success(); return RequestResult::Success();
} }
@ -120,7 +124,7 @@ RequestResult RequestHandler::SetPersistentData(const Request& request)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetSceneCollectionList(const Request&) RequestResult RequestHandler::GetSceneCollectionList(const Request &)
{ {
json responseData; json responseData;
responseData["currentSceneCollectionName"] = Utils::Obs::StringHelper::GetCurrentSceneCollection(); responseData["currentSceneCollectionName"] = Utils::Obs::StringHelper::GetCurrentSceneCollection();
@ -142,7 +146,7 @@ RequestResult RequestHandler::GetSceneCollectionList(const Request&)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::SetCurrentSceneCollection(const Request& request) RequestResult RequestHandler::SetCurrentSceneCollection(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -158,9 +162,10 @@ RequestResult RequestHandler::SetCurrentSceneCollection(const Request& request)
std::string currentSceneCollectionName = Utils::Obs::StringHelper::GetCurrentSceneCollection(); std::string currentSceneCollectionName = Utils::Obs::StringHelper::GetCurrentSceneCollection();
// Avoid queueing tasks if nothing will change // Avoid queueing tasks if nothing will change
if (currentSceneCollectionName != sceneCollectionName) { if (currentSceneCollectionName != sceneCollectionName) {
obs_queue_task(OBS_TASK_UI, [](void* param) { obs_queue_task(
obs_frontend_set_current_scene_collection(static_cast<const char*>(param)); OBS_TASK_UI,
}, (void*)sceneCollectionName.c_str(), true); [](void *param) { obs_frontend_set_current_scene_collection(static_cast<const char *>(param)); },
(void *)sceneCollectionName.c_str(), true);
} }
return RequestResult::Success(); return RequestResult::Success();
@ -180,7 +185,7 @@ RequestResult RequestHandler::SetCurrentSceneCollection(const Request& request)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::CreateSceneCollection(const Request& request) RequestResult RequestHandler::CreateSceneCollection(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -193,9 +198,10 @@ RequestResult RequestHandler::CreateSceneCollection(const Request& request)
if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) != sceneCollections.end()) if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) != sceneCollections.end())
return RequestResult::Error(RequestStatus::ResourceAlreadyExists); return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window()); QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
bool success = false; bool success = false;
QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName))); QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success),
Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
if (!success) if (!success)
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene collection."); return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene collection.");
@ -215,7 +221,7 @@ RequestResult RequestHandler::CreateSceneCollection(const Request& request)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetProfileList(const Request&) RequestResult RequestHandler::GetProfileList(const Request &)
{ {
json responseData; json responseData;
responseData["currentProfileName"] = Utils::Obs::StringHelper::GetCurrentProfile(); responseData["currentProfileName"] = Utils::Obs::StringHelper::GetCurrentProfile();
@ -235,7 +241,7 @@ RequestResult RequestHandler::GetProfileList(const Request&)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::SetCurrentProfile(const Request& request) RequestResult RequestHandler::SetCurrentProfile(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -251,9 +257,9 @@ RequestResult RequestHandler::SetCurrentProfile(const Request& request)
std::string currentProfileName = Utils::Obs::StringHelper::GetCurrentProfile(); std::string currentProfileName = Utils::Obs::StringHelper::GetCurrentProfile();
// Avoid queueing tasks if nothing will change // Avoid queueing tasks if nothing will change
if (currentProfileName != profileName) { if (currentProfileName != profileName) {
obs_queue_task(OBS_TASK_UI, [](void* param) { obs_queue_task(
obs_frontend_set_current_profile(static_cast<const char*>(param)); OBS_TASK_UI, [](void *param) { obs_frontend_set_current_profile(static_cast<const char *>(param)); },
}, (void*)profileName.c_str(), true); (void *)profileName.c_str(), true);
} }
return RequestResult::Success(); return RequestResult::Success();
@ -271,7 +277,7 @@ RequestResult RequestHandler::SetCurrentProfile(const Request& request)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::CreateProfile(const Request& request) RequestResult RequestHandler::CreateProfile(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -284,8 +290,9 @@ RequestResult RequestHandler::CreateProfile(const Request& request)
if (std::find(profiles.begin(), profiles.end(), profileName) != profiles.end()) if (std::find(profiles.begin(), profiles.end(), profileName) != profiles.end())
return RequestResult::Error(RequestStatus::ResourceAlreadyExists); return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window()); QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName))); QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection,
Q_ARG(QString, QString::fromStdString(profileName)));
return RequestResult::Success(); return RequestResult::Success();
} }
@ -302,7 +309,7 @@ RequestResult RequestHandler::CreateProfile(const Request& request)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::RemoveProfile(const Request& request) RequestResult RequestHandler::RemoveProfile(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -318,8 +325,9 @@ RequestResult RequestHandler::RemoveProfile(const Request& request)
if (profiles.size() < 2) if (profiles.size() < 2)
return RequestResult::Error(RequestStatus::NotEnoughResources); return RequestResult::Error(RequestStatus::NotEnoughResources);
QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window()); QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName))); QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection,
Q_ARG(QString, QString::fromStdString(profileName)));
return RequestResult::Success(); return RequestResult::Success();
} }
@ -340,17 +348,18 @@ RequestResult RequestHandler::RemoveProfile(const Request& request)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetProfileParameter(const Request& request) RequestResult RequestHandler::GetProfileParameter(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!(request.ValidateString("parameterCategory", statusCode, comment) && request.ValidateString("parameterName", statusCode, comment))) if (!(request.ValidateString("parameterCategory", statusCode, comment) &&
request.ValidateString("parameterName", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string parameterCategory = request.RequestData["parameterCategory"]; std::string parameterCategory = request.RequestData["parameterCategory"];
std::string parameterName = request.RequestData["parameterName"]; std::string parameterName = request.RequestData["parameterName"];
config_t* profile = obs_frontend_get_profile_config(); config_t *profile = obs_frontend_get_profile_config();
if (!profile) if (!profile)
blog(LOG_ERROR, "[RequestHandler::GetProfileParameter] Profile is invalid."); blog(LOG_ERROR, "[RequestHandler::GetProfileParameter] Profile is invalid.");
@ -358,7 +367,8 @@ RequestResult RequestHandler::GetProfileParameter(const Request& request)
json responseData; json responseData;
if (config_has_default_value(profile, parameterCategory.c_str(), parameterName.c_str())) { if (config_has_default_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str()); responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
responseData["defaultParameterValue"] = config_get_default_string(profile, parameterCategory.c_str(), parameterName.c_str()); responseData["defaultParameterValue"] =
config_get_default_string(profile, parameterCategory.c_str(), parameterName.c_str());
} else if (config_has_user_value(profile, parameterCategory.c_str(), parameterName.c_str())) { } else if (config_has_user_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str()); responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
responseData["defaultParameterValue"] = nullptr; responseData["defaultParameterValue"] = nullptr;
@ -384,23 +394,24 @@ RequestResult RequestHandler::GetProfileParameter(const Request& request)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::SetProfileParameter(const Request& request) RequestResult RequestHandler::SetProfileParameter(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!(request.ValidateString("parameterCategory", statusCode, comment) && if (!(request.ValidateString("parameterCategory", statusCode, comment) &&
request.ValidateString("parameterName", statusCode, comment))) request.ValidateString("parameterName", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string parameterCategory = request.RequestData["parameterCategory"]; std::string parameterCategory = request.RequestData["parameterCategory"];
std::string parameterName = request.RequestData["parameterName"]; std::string parameterName = request.RequestData["parameterName"];
config_t* profile = obs_frontend_get_profile_config(); config_t *profile = obs_frontend_get_profile_config();
// Using check helpers here would just make the logic more complicated // Using check helpers here would just make the logic more complicated
if (!request.RequestData.contains("parameterValue") || request.RequestData["parameterValue"].is_null()) { if (!request.RequestData.contains("parameterValue") || request.RequestData["parameterValue"].is_null()) {
if (!config_remove_value(profile, parameterCategory.c_str(), parameterName.c_str())) if (!config_remove_value(profile, parameterCategory.c_str(), parameterName.c_str()))
return RequestResult::Error(RequestStatus::ResourceNotFound, "There are no existing instances of that profile parameter."); return RequestResult::Error(RequestStatus::ResourceNotFound,
"There are no existing instances of that profile parameter.");
} else if (request.RequestData["parameterValue"].is_string()) { } else if (request.RequestData["parameterValue"].is_string()) {
std::string parameterValue = request.RequestData["parameterValue"]; std::string parameterValue = request.RequestData["parameterValue"];
config_set_string(profile, parameterCategory.c_str(), parameterName.c_str(), parameterValue.c_str()); config_set_string(profile, parameterCategory.c_str(), parameterName.c_str(), parameterValue.c_str());
@ -432,7 +443,7 @@ RequestResult RequestHandler::SetProfileParameter(const Request& request)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetVideoSettings(const Request&) RequestResult RequestHandler::GetVideoSettings(const Request &)
{ {
struct obs_video_info ovi; struct obs_video_info ovi;
if (!obs_get_video_info(&ovi)) if (!obs_get_video_info(&ovi))
@ -468,23 +479,27 @@ RequestResult RequestHandler::GetVideoSettings(const Request&)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::SetVideoSettings(const Request& request) RequestResult RequestHandler::SetVideoSettings(const Request &request)
{ {
if (obs_video_active()) if (obs_video_active())
return RequestResult::Error(RequestStatus::OutputRunning, "Video settings cannot be changed while an output is active."); return RequestResult::Error(RequestStatus::OutputRunning,
"Video settings cannot be changed while an output is active.");
RequestStatus::RequestStatus statusCode = RequestStatus::NoError; RequestStatus::RequestStatus statusCode = RequestStatus::NoError;
std::string comment; std::string comment;
bool changeFps = (request.Contains("fpsNumerator") && request.Contains("fpsDenominator")); bool changeFps = (request.Contains("fpsNumerator") && request.Contains("fpsDenominator"));
if (changeFps && !(request.ValidateOptionalNumber("fpsNumerator", statusCode, comment, 1) && request.ValidateOptionalNumber("fpsDenominator", statusCode, comment, 1))) if (changeFps && !(request.ValidateOptionalNumber("fpsNumerator", statusCode, comment, 1) &&
request.ValidateOptionalNumber("fpsDenominator", statusCode, comment, 1)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
bool changeBaseRes = (request.Contains("baseWidth") && request.Contains("baseHeight")); bool changeBaseRes = (request.Contains("baseWidth") && request.Contains("baseHeight"));
if (changeBaseRes && !(request.ValidateOptionalNumber("baseWidth", statusCode, comment, 8, 4096) && request.ValidateOptionalNumber("baseHeight", statusCode, comment, 8, 4096))) if (changeBaseRes && !(request.ValidateOptionalNumber("baseWidth", statusCode, comment, 8, 4096) &&
request.ValidateOptionalNumber("baseHeight", statusCode, comment, 8, 4096)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
bool changeOutputRes = (request.Contains("outputWidth") && request.Contains("outputHeight")); bool changeOutputRes = (request.Contains("outputWidth") && request.Contains("outputHeight"));
if (changeOutputRes && !(request.ValidateOptionalNumber("outputWidth", statusCode, comment, 8, 4096) && request.ValidateOptionalNumber("outputHeight", statusCode, comment, 8, 4096))) if (changeOutputRes && !(request.ValidateOptionalNumber("outputWidth", statusCode, comment, 8, 4096) &&
request.ValidateOptionalNumber("outputHeight", statusCode, comment, 8, 4096)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
config_t *config = obs_frontend_get_profile_config(); config_t *config = obs_frontend_get_profile_config();
@ -527,7 +542,7 @@ RequestResult RequestHandler::SetVideoSettings(const Request& request)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetStreamServiceSettings(const Request&) RequestResult RequestHandler::GetStreamServiceSettings(const Request &)
{ {
json responseData; json responseData;
@ -554,21 +569,24 @@ RequestResult RequestHandler::GetStreamServiceSettings(const Request&)
* @category config * @category config
* @api requests * @api requests
*/ */
RequestResult RequestHandler::SetStreamServiceSettings(const Request& request) RequestResult RequestHandler::SetStreamServiceSettings(const Request &request)
{ {
if (obs_frontend_streaming_active()) if (obs_frontend_streaming_active())
return RequestResult::Error(RequestStatus::OutputRunning, "You cannot change stream service settings while streaming."); return RequestResult::Error(RequestStatus::OutputRunning,
"You cannot change stream service settings while streaming.");
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!(request.ValidateString("streamServiceType", statusCode, comment) && request.ValidateObject("streamServiceSettings", statusCode, comment))) if (!(request.ValidateString("streamServiceType", statusCode, comment) &&
request.ValidateObject("streamServiceSettings", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
OBSService currentStreamService = obs_frontend_get_streaming_service(); OBSService currentStreamService = obs_frontend_get_streaming_service();
std::string streamServiceType = obs_service_get_type(currentStreamService); std::string streamServiceType = obs_service_get_type(currentStreamService);
std::string requestedStreamServiceType = request.RequestData["streamServiceType"]; std::string requestedStreamServiceType = request.RequestData["streamServiceType"];
OBSDataAutoRelease requestedStreamServiceSettings = Utils::Json::JsonToObsData(request.RequestData["streamServiceSettings"]); OBSDataAutoRelease requestedStreamServiceSettings =
Utils::Json::JsonToObsData(request.RequestData["streamServiceSettings"]);
// Don't create a new service if the current service is the same type. // Don't create a new service if the current service is the same type.
if (streamServiceType == requestedStreamServiceType) { if (streamServiceType == requestedStreamServiceType) {
@ -582,10 +600,13 @@ RequestResult RequestHandler::SetStreamServiceSettings(const Request& request)
obs_service_update(currentStreamService, newStreamServiceSettings); obs_service_update(currentStreamService, newStreamServiceSettings);
} else { } else {
// TODO: This leaks memory. I have no idea why. // TODO: This leaks memory. I have no idea why.
OBSService newStreamService = obs_service_create(requestedStreamServiceType.c_str(), "obs_websocket_custom_service", requestedStreamServiceSettings, nullptr); OBSService newStreamService = obs_service_create(requestedStreamServiceType.c_str(), "obs_websocket_custom_service",
requestedStreamServiceSettings, nullptr);
// TODO: Check service type here, instead of relying on service creation to fail. // TODO: Check service type here, instead of relying on service creation to fail.
if (!newStreamService) if (!newStreamService)
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the stream service with the requested streamServiceType. It may be an invalid type."); return RequestResult::Error(
RequestStatus::ResourceCreationFailed,
"Failed to create the stream service with the requested streamServiceType. It may be an invalid type.");
obs_frontend_set_streaming_service(newStreamService); obs_frontend_set_streaming_service(newStreamService);
} }
@ -607,7 +628,7 @@ RequestResult RequestHandler::SetStreamServiceSettings(const Request& request)
* @api requests * @api requests
* @category rconfig * @category rconfig
*/ */
RequestResult RequestHandler::GetRecordDirectory(const Request&) RequestResult RequestHandler::GetRecordDirectory(const Request &)
{ {
json responseData; json responseData;
responseData["recordDirectory"] = Utils::Obs::StringHelper::GetCurrentRecordOutputPath(); responseData["recordDirectory"] = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();

View File

@ -33,12 +33,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @api requests * @api requests
* @category filters * @category filters
*/ */
RequestResult RequestHandler::GetSourceFilterList(const Request& request) RequestResult RequestHandler::GetSourceFilterList(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment); OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
if(!source) if (!source)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
json responseData; json responseData;
@ -61,7 +61,7 @@ RequestResult RequestHandler::GetSourceFilterList(const Request& request)
* @api requests * @api requests
* @category filters * @category filters
*/ */
RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request& request) RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -97,13 +97,14 @@ RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request& requ
* @api requests * @api requests
* @category filters * @category filters
*/ */
RequestResult RequestHandler::CreateSourceFilter(const Request& request) RequestResult RequestHandler::CreateSourceFilter(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment); OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
if (!(source && request.ValidateString("filterName", statusCode, comment) && request.ValidateString("filterKind", statusCode, comment))) if (!(source && request.ValidateString("filterName", statusCode, comment) &&
request.ValidateString("filterKind", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string filterName = request.RequestData["filterName"]; std::string filterName = request.RequestData["filterName"];
@ -114,7 +115,9 @@ RequestResult RequestHandler::CreateSourceFilter(const Request& request)
std::string filterKind = request.RequestData["filterKind"]; std::string filterKind = request.RequestData["filterKind"];
auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList(); auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList();
if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end()) if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end())
return RequestResult::Error(RequestStatus::InvalidFilterKind, "Your specified filter kind is not supported by OBS. Check that any necessary plugins are loaded."); return RequestResult::Error(
RequestStatus::InvalidFilterKind,
"Your specified filter kind is not supported by OBS. Check that any necessary plugins are loaded.");
OBSDataAutoRelease filterSettings = nullptr; OBSDataAutoRelease filterSettings = nullptr;
if (request.Contains("filterSettings")) { if (request.Contains("filterSettings")) {
@ -126,7 +129,7 @@ RequestResult RequestHandler::CreateSourceFilter(const Request& request)
OBSSourceAutoRelease filter = Utils::Obs::ActionHelper::CreateSourceFilter(source, filterName, filterKind, filterSettings); OBSSourceAutoRelease filter = Utils::Obs::ActionHelper::CreateSourceFilter(source, filterName, filterKind, filterSettings);
if(!filter) if (!filter)
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the filter failed."); return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the filter failed.");
return RequestResult::Success(); return RequestResult::Success();
@ -145,7 +148,7 @@ RequestResult RequestHandler::CreateSourceFilter(const Request& request)
* @api requests * @api requests
* @category filters * @category filters
*/ */
RequestResult RequestHandler::RemoveSourceFilter(const Request& request) RequestResult RequestHandler::RemoveSourceFilter(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -172,7 +175,7 @@ RequestResult RequestHandler::RemoveSourceFilter(const Request& request)
* @api requests * @api requests
* @category filters * @category filters
*/ */
RequestResult RequestHandler::SetSourceFilterName(const Request& request) RequestResult RequestHandler::SetSourceFilterName(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -209,7 +212,7 @@ RequestResult RequestHandler::SetSourceFilterName(const Request& request)
* @api requests * @api requests
* @category filters * @category filters
*/ */
RequestResult RequestHandler::GetSourceFilter(const Request& request) RequestResult RequestHandler::GetSourceFilter(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -219,7 +222,8 @@ RequestResult RequestHandler::GetSourceFilter(const Request& request)
json responseData; json responseData;
responseData["filterEnabled"] = obs_source_enabled(pair.filter); responseData["filterEnabled"] = obs_source_enabled(pair.filter);
responseData["filterIndex"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(pair.source, pair.filter); // Todo: Use `GetSourceFilterlist` to select this filter maybe responseData["filterIndex"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(
pair.source, pair.filter); // Todo: Use `GetSourceFilterlist` to select this filter maybe
responseData["filterKind"] = obs_source_get_id(pair.filter); responseData["filterKind"] = obs_source_get_id(pair.filter);
OBSDataAutoRelease filterSettings = obs_source_get_settings(pair.filter); OBSDataAutoRelease filterSettings = obs_source_get_settings(pair.filter);
@ -242,7 +246,7 @@ RequestResult RequestHandler::GetSourceFilter(const Request& request)
* @api requests * @api requests
* @category filters * @category filters
*/ */
RequestResult RequestHandler::SetSourceFilterIndex(const Request& request) RequestResult RequestHandler::SetSourceFilterIndex(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -272,7 +276,7 @@ RequestResult RequestHandler::SetSourceFilterIndex(const Request& request)
* @api requests * @api requests
* @category filters * @category filters
*/ */
RequestResult RequestHandler::SetSourceFilterSettings(const Request& request) RequestResult RequestHandler::SetSourceFilterSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -292,7 +296,8 @@ RequestResult RequestHandler::SetSourceFilterSettings(const Request& request)
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]); OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
if (!newSettings) if (!newSettings)
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!"); return RequestResult::Error(RequestStatus::RequestProcessingFailed,
"An internal data conversion operation failed. Please report this!");
if (overlay) if (overlay)
obs_source_update(pair.filter, newSettings); obs_source_update(pair.filter, newSettings);
@ -318,7 +323,7 @@ RequestResult RequestHandler::SetSourceFilterSettings(const Request& request)
* @api requests * @api requests
* @category filters * @category filters
*/ */
RequestResult RequestHandler::SetSourceFilterEnabled(const Request& request) RequestResult RequestHandler::SetSourceFilterEnabled(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;

View File

@ -26,7 +26,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "../WebSocketApi.h" #include "../WebSocketApi.h"
#include "../obs-websocket.h" #include "../obs-websocket.h"
/** /**
* Gets data about the current plugin and RPC version. * Gets data about the current plugin and RPC version.
* *
@ -45,7 +44,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @category general * @category general
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetVersion(const Request&) RequestResult RequestHandler::GetVersion(const Request &)
{ {
json responseData; json responseData;
responseData["obsVersion"] = Utils::Obs::StringHelper::GetObsVersion(); responseData["obsVersion"] = Utils::Obs::StringHelper::GetObsVersion();
@ -55,7 +54,7 @@ RequestResult RequestHandler::GetVersion(const Request&)
QList<QByteArray> imageWriterFormats = QImageWriter::supportedImageFormats(); QList<QByteArray> imageWriterFormats = QImageWriter::supportedImageFormats();
std::vector<std::string> supportedImageFormats; std::vector<std::string> supportedImageFormats;
for (const QByteArray& format : imageWriterFormats) { for (const QByteArray &format : imageWriterFormats) {
supportedImageFormats.push_back(format.toStdString()); supportedImageFormats.push_back(format.toStdString());
} }
responseData["supportedImageFormats"] = supportedImageFormats; responseData["supportedImageFormats"] = supportedImageFormats;
@ -88,7 +87,7 @@ RequestResult RequestHandler::GetVersion(const Request&)
* @category general * @category general
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetStats(const Request&) RequestResult RequestHandler::GetStats(const Request &)
{ {
json responseData = Utils::Obs::ObjectHelper::GetStats(); json responseData = Utils::Obs::ObjectHelper::GetStats();
@ -115,7 +114,7 @@ RequestResult RequestHandler::GetStats(const Request&)
* @category general * @category general
* @api requests * @api requests
*/ */
RequestResult RequestHandler::BroadcastCustomEvent(const Request& request) RequestResult RequestHandler::BroadcastCustomEvent(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -141,6 +140,8 @@ RequestResult RequestHandler::BroadcastCustomEvent(const Request& request)
* @requestField requestType | String | The request type to call * @requestField requestType | String | The request type to call
* @requestField ?requestData | Object | Object containing appropriate request data | {} * @requestField ?requestData | Object | Object containing appropriate request data | {}
* *
* @responseField vendorName | String | Echoed of `vendorName`
* @responseField requestType | String | Echoed of `requestType`
* @responseField responseData | Object | Object containing appropriate response data. {} if request does not provide any response data * @responseField responseData | Object | Object containing appropriate response data. {} if request does not provide any response data
* *
* @requestType CallVendorRequest * @requestType CallVendorRequest
@ -150,11 +151,12 @@ RequestResult RequestHandler::BroadcastCustomEvent(const Request& request)
* @category general * @category general
* @api requests * @api requests
*/ */
RequestResult RequestHandler::CallVendorRequest(const Request& request) RequestResult RequestHandler::CallVendorRequest(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!request.ValidateString("vendorName", statusCode, comment) || !request.ValidateString("requestType", statusCode, comment)) if (!request.ValidateString("vendorName", statusCode, comment) ||
!request.ValidateString("requestType", statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string vendorName = request.RequestData["vendorName"]; std::string vendorName = request.RequestData["vendorName"];
@ -172,20 +174,23 @@ RequestResult RequestHandler::CallVendorRequest(const Request& request)
auto webSocketApi = GetWebSocketApi(); auto webSocketApi = GetWebSocketApi();
if (!webSocketApi) if (!webSocketApi)
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to call request due to internal error."); return RequestResult::Error(RequestStatus::RequestProcessingFailed,
"Unable to call request due to internal error.");
auto ret = webSocketApi->PerformVendorRequest(vendorName, requestType, requestData, obsResponseData); auto ret = webSocketApi->PerformVendorRequest(vendorName, requestType, requestData, obsResponseData);
switch (ret) { switch (ret) {
default: default:
case WebSocketApi::RequestReturnCode::Normal: case WebSocketApi::RequestReturnCode::Normal:
break; break;
case WebSocketApi::RequestReturnCode::NoVendor: case WebSocketApi::RequestReturnCode::NoVendor:
return RequestResult::Error(RequestStatus::ResourceNotFound, "No vendor was found by that name."); return RequestResult::Error(RequestStatus::ResourceNotFound, "No vendor was found by that name.");
case WebSocketApi::RequestReturnCode::NoVendorRequest: case WebSocketApi::RequestReturnCode::NoVendorRequest:
return RequestResult::Error(RequestStatus::ResourceNotFound, "No request was found by that name."); return RequestResult::Error(RequestStatus::ResourceNotFound, "No request was found by that name.");
} }
json responseData; json responseData;
responseData["vendorName"] = vendorName;
responseData["requestType"] = requestType;
responseData["responseData"] = Utils::Json::ObsDataToJson(obsResponseData); responseData["responseData"] = Utils::Json::ObsDataToJson(obsResponseData);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
@ -203,7 +208,7 @@ RequestResult RequestHandler::CallVendorRequest(const Request& request)
* @category general * @category general
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetHotkeyList(const Request&) RequestResult RequestHandler::GetHotkeyList(const Request &)
{ {
json responseData; json responseData;
responseData["hotkeys"] = Utils::Obs::ArrayHelper::GetHotkeyNameList(); responseData["hotkeys"] = Utils::Obs::ArrayHelper::GetHotkeyNameList();
@ -222,7 +227,7 @@ RequestResult RequestHandler::GetHotkeyList(const Request&)
* @category general * @category general
* @api requests * @api requests
*/ */
RequestResult RequestHandler::TriggerHotkeyByName(const Request& request) RequestResult RequestHandler::TriggerHotkeyByName(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -255,7 +260,7 @@ RequestResult RequestHandler::TriggerHotkeyByName(const Request& request)
* @category general * @category general
* @api requests * @api requests
*/ */
RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request& request) RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request &request)
{ {
obs_key_combination_t combo = {0}; obs_key_combination_t combo = {0};
@ -277,19 +282,23 @@ RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request& request)
const json keyModifiersJson = request.RequestData["keyModifiers"]; const json keyModifiersJson = request.RequestData["keyModifiers"];
uint32_t keyModifiers = 0; uint32_t keyModifiers = 0;
if (keyModifiersJson.contains("shift") && keyModifiersJson["shift"].is_boolean() && keyModifiersJson["shift"].get<bool>()) if (keyModifiersJson.contains("shift") && keyModifiersJson["shift"].is_boolean() &&
keyModifiersJson["shift"].get<bool>())
keyModifiers |= INTERACT_SHIFT_KEY; keyModifiers |= INTERACT_SHIFT_KEY;
if (keyModifiersJson.contains("control") && keyModifiersJson["control"].is_boolean() && keyModifiersJson["control"].get<bool>()) if (keyModifiersJson.contains("control") && keyModifiersJson["control"].is_boolean() &&
keyModifiersJson["control"].get<bool>())
keyModifiers |= INTERACT_CONTROL_KEY; keyModifiers |= INTERACT_CONTROL_KEY;
if (keyModifiersJson.contains("alt") && keyModifiersJson["alt"].is_boolean() && keyModifiersJson["alt"].get<bool>()) if (keyModifiersJson.contains("alt") && keyModifiersJson["alt"].is_boolean() && keyModifiersJson["alt"].get<bool>())
keyModifiers |= INTERACT_ALT_KEY; keyModifiers |= INTERACT_ALT_KEY;
if (keyModifiersJson.contains("command") && keyModifiersJson["command"].is_boolean() && keyModifiersJson["command"].get<bool>()) if (keyModifiersJson.contains("command") && keyModifiersJson["command"].is_boolean() &&
keyModifiersJson["command"].get<bool>())
keyModifiers |= INTERACT_COMMAND_KEY; keyModifiers |= INTERACT_COMMAND_KEY;
combo.modifiers = keyModifiers; combo.modifiers = keyModifiers;
} }
if (!combo.modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) if (!combo.modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE))
return RequestResult::Error(RequestStatus::CannotAct, "Your provided request fields cannot be used to trigger a hotkey."); return RequestResult::Error(RequestStatus::CannotAct,
"Your provided request fields cannot be used to trigger a hotkey.");
// Apparently things break when you don't start by setting the combo to false // Apparently things break when you don't start by setting the combo to false
obs_hotkey_inject_event(combo, false); obs_hotkey_inject_event(combo, false);
@ -312,7 +321,7 @@ RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request& request)
* @category general * @category general
* @api requests * @api requests
*/ */
RequestResult RequestHandler::Sleep(const Request& request) RequestResult RequestHandler::Sleep(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;

View File

@ -33,7 +33,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetInputList(const Request& request) RequestResult RequestHandler::GetInputList(const Request &request)
{ {
std::string inputKind; std::string inputKind;
@ -65,7 +65,7 @@ RequestResult RequestHandler::GetInputList(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetInputKindList(const Request& request) RequestResult RequestHandler::GetInputKindList(const Request &request)
{ {
bool unversioned = false; bool unversioned = false;
@ -100,7 +100,7 @@ RequestResult RequestHandler::GetInputKindList(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetSpecialInputs(const Request&) RequestResult RequestHandler::GetSpecialInputs(const Request &)
{ {
json responseData; json responseData;
@ -138,12 +138,13 @@ RequestResult RequestHandler::GetSpecialInputs(const Request&)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::CreateInput(const Request& request) RequestResult RequestHandler::CreateInput(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment); OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment);
if (!(sceneSource && request.ValidateString("inputName", statusCode, comment) && request.ValidateString("inputKind", statusCode, comment))) if (!(sceneSource && request.ValidateString("inputName", statusCode, comment) &&
request.ValidateString("inputKind", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string inputName = request.RequestData["inputName"]; std::string inputName = request.RequestData["inputName"];
@ -154,7 +155,9 @@ RequestResult RequestHandler::CreateInput(const Request& request)
std::string inputKind = request.RequestData["inputKind"]; std::string inputKind = request.RequestData["inputKind"];
auto kinds = Utils::Obs::ArrayHelper::GetInputKindList(); auto kinds = Utils::Obs::ArrayHelper::GetInputKindList();
if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end()) if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end())
return RequestResult::Error(RequestStatus::InvalidInputKind, "Your specified input kind is not supported by OBS. Check that your specified kind is properly versioned and that any necessary plugins are loaded."); return RequestResult::Error(
RequestStatus::InvalidInputKind,
"Your specified input kind is not supported by OBS. Check that your specified kind is properly versioned and that any necessary plugins are loaded.");
OBSDataAutoRelease inputSettings = nullptr; OBSDataAutoRelease inputSettings = nullptr;
if (request.Contains("inputSettings")) { if (request.Contains("inputSettings")) {
@ -175,7 +178,8 @@ RequestResult RequestHandler::CreateInput(const Request& request)
} }
// Create the input and add it as a scene item to the destination scene // Create the input and add it as a scene item to the destination scene
OBSSceneItemAutoRelease sceneItem = Utils::Obs::ActionHelper::CreateInput(inputName, inputKind, inputSettings, scene, sceneItemEnabled); OBSSceneItemAutoRelease sceneItem =
Utils::Obs::ActionHelper::CreateInput(inputName, inputKind, inputSettings, scene, sceneItemEnabled);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the input or scene item failed."); return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the input or scene item failed.");
@ -199,7 +203,7 @@ RequestResult RequestHandler::CreateInput(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::RemoveInput(const Request& request) RequestResult RequestHandler::RemoveInput(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -228,7 +232,7 @@ RequestResult RequestHandler::RemoveInput(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::SetInputName(const Request& request) RequestResult RequestHandler::SetInputName(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -240,7 +244,8 @@ RequestResult RequestHandler::SetInputName(const Request& request)
OBSSourceAutoRelease existingSource = obs_get_source_by_name(newInputName.c_str()); OBSSourceAutoRelease existingSource = obs_get_source_by_name(newInputName.c_str());
if (existingSource) if (existingSource)
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that new input name."); return RequestResult::Error(RequestStatus::ResourceAlreadyExists,
"A source already exists by that new input name.");
obs_source_set_name(input, newInputName.c_str()); obs_source_set_name(input, newInputName.c_str());
@ -261,7 +266,7 @@ RequestResult RequestHandler::SetInputName(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetInputDefaultSettings(const Request& request) RequestResult RequestHandler::GetInputDefaultSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -299,7 +304,7 @@ RequestResult RequestHandler::GetInputDefaultSettings(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetInputSettings(const Request& request) RequestResult RequestHandler::GetInputSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -329,7 +334,7 @@ RequestResult RequestHandler::GetInputSettings(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::SetInputSettings(const Request& request) RequestResult RequestHandler::SetInputSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -349,7 +354,8 @@ RequestResult RequestHandler::SetInputSettings(const Request& request)
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["inputSettings"]); OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["inputSettings"]);
if (!newSettings) if (!newSettings)
// This should never happen // This should never happen
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!"); return RequestResult::Error(RequestStatus::RequestProcessingFailed,
"An internal data conversion operation failed. Please report this!");
if (overlay) if (overlay)
// Applies the new settings on top of the existing user settings // Applies the new settings on top of the existing user settings
@ -378,7 +384,7 @@ RequestResult RequestHandler::SetInputSettings(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetInputMute(const Request& request) RequestResult RequestHandler::GetInputMute(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -407,7 +413,7 @@ RequestResult RequestHandler::GetInputMute(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::SetInputMute(const Request& request) RequestResult RequestHandler::SetInputMute(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -437,7 +443,7 @@ RequestResult RequestHandler::SetInputMute(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::ToggleInputMute(const Request& request) RequestResult RequestHandler::ToggleInputMute(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -471,7 +477,7 @@ RequestResult RequestHandler::ToggleInputMute(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetInputVolume(const Request& request) RequestResult RequestHandler::GetInputVolume(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -507,7 +513,7 @@ RequestResult RequestHandler::GetInputVolume(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::SetInputVolume(const Request& request) RequestResult RequestHandler::SetInputVolume(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -557,7 +563,7 @@ RequestResult RequestHandler::SetInputVolume(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetInputAudioBalance(const Request& request) RequestResult RequestHandler::GetInputAudioBalance(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -587,7 +593,7 @@ RequestResult RequestHandler::GetInputAudioBalance(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::SetInputAudioBalance(const Request& request) RequestResult RequestHandler::SetInputAudioBalance(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -620,7 +626,7 @@ RequestResult RequestHandler::SetInputAudioBalance(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetInputAudioSyncOffset(const Request& request) RequestResult RequestHandler::GetInputAudioSyncOffset(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -651,7 +657,7 @@ RequestResult RequestHandler::GetInputAudioSyncOffset(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::SetInputAudioSyncOffset(const Request& request) RequestResult RequestHandler::SetInputAudioSyncOffset(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -688,7 +694,7 @@ RequestResult RequestHandler::SetInputAudioSyncOffset(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetInputAudioMonitorType(const Request& request) RequestResult RequestHandler::GetInputAudioMonitorType(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -700,7 +706,7 @@ RequestResult RequestHandler::GetInputAudioMonitorType(const Request& request)
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
json responseData; json responseData;
responseData["monitorType"] = Utils::Obs::StringHelper::GetInputMonitorType(input); responseData["monitorType"] = obs_source_get_monitoring_type(input);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -718,7 +724,7 @@ RequestResult RequestHandler::GetInputAudioMonitorType(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request) RequestResult RequestHandler::SetInputAudioMonitorType(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -730,7 +736,8 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
if (!obs_audio_monitoring_available()) if (!obs_audio_monitoring_available())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Audio monitoring is not available on this platform."); return RequestResult::Error(RequestStatus::InvalidResourceState,
"Audio monitoring is not available on this platform.");
enum obs_monitoring_type monitorType; enum obs_monitoring_type monitorType;
std::string monitorTypeString = request.RequestData["monitorType"]; std::string monitorTypeString = request.RequestData["monitorType"];
@ -741,7 +748,8 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
else if (monitorTypeString == "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT") else if (monitorTypeString == "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT")
monitorType = OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT; monitorType = OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT;
else else
return RequestResult::Error(RequestStatus::InvalidRequestField, std::string("Unknown monitor type: ") + monitorTypeString); return RequestResult::Error(RequestStatus::InvalidRequestField,
std::string("Unknown monitor type: ") + monitorTypeString);
obs_source_set_monitoring_type(input, monitorType); obs_source_set_monitoring_type(input, monitorType);
@ -762,7 +770,7 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetInputAudioTracks(const Request& request) RequestResult RequestHandler::GetInputAudioTracks(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -799,7 +807,7 @@ RequestResult RequestHandler::GetInputAudioTracks(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::SetInputAudioTracks(const Request& request) RequestResult RequestHandler::SetInputAudioTracks(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -821,7 +829,8 @@ RequestResult RequestHandler::SetInputAudioTracks(const Request& request)
continue; continue;
if (!inputAudioTracks[track].is_boolean()) if (!inputAudioTracks[track].is_boolean())
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The value of one of your tracks is not a boolean."); return RequestResult::Error(RequestStatus::InvalidRequestFieldType,
"The value of one of your tracks is not a boolean.");
bool enabled = inputAudioTracks[track]; bool enabled = inputAudioTracks[track];
@ -854,7 +863,7 @@ RequestResult RequestHandler::SetInputAudioTracks(const Request& request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::GetInputPropertiesListPropertyItems(const Request& request) RequestResult RequestHandler::GetInputPropertiesListPropertyItems(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -892,7 +901,7 @@ RequestResult RequestHandler::GetInputPropertiesListPropertyItems(const Request&
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestHandler::PressInputPropertiesButton(const Request& request) RequestResult RequestHandler::PressInputPropertiesButton(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;

View File

@ -52,7 +52,7 @@ bool IsMediaTimeValid(obs_source_t *input)
* @api requests * @api requests
* @category media inputs * @category media inputs
*/ */
RequestResult RequestHandler::GetMediaInputStatus(const Request& request) RequestResult RequestHandler::GetMediaInputStatus(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -61,7 +61,8 @@ RequestResult RequestHandler::GetMediaInputStatus(const Request& request)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
json responseData; json responseData;
responseData["mediaState"] = Utils::Obs::StringHelper::GetMediaInputState(input); responseData["mediaState"] = obs_source_media_get_state(input);
;
if (IsMediaTimeValid(input)) { if (IsMediaTimeValid(input)) {
responseData["mediaDuration"] = obs_source_media_get_duration(input); responseData["mediaDuration"] = obs_source_media_get_duration(input);
@ -89,7 +90,7 @@ RequestResult RequestHandler::GetMediaInputStatus(const Request& request)
* @api requests * @api requests
* @category media inputs * @category media inputs
*/ */
RequestResult RequestHandler::SetMediaInputCursor(const Request& request) RequestResult RequestHandler::SetMediaInputCursor(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -98,7 +99,8 @@ RequestResult RequestHandler::SetMediaInputCursor(const Request& request)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!IsMediaTimeValid(input)) if (!IsMediaTimeValid(input))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The media input must be playing or paused in order to set the cursor position."); return RequestResult::Error(RequestStatus::InvalidResourceState,
"The media input must be playing or paused in order to set the cursor position.");
int64_t mediaCursor = request.RequestData["mediaCursor"]; int64_t mediaCursor = request.RequestData["mediaCursor"];
@ -123,7 +125,7 @@ RequestResult RequestHandler::SetMediaInputCursor(const Request& request)
* @api requests * @api requests
* @category media inputs * @category media inputs
*/ */
RequestResult RequestHandler::OffsetMediaInputCursor(const Request& request) RequestResult RequestHandler::OffsetMediaInputCursor(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -132,7 +134,8 @@ RequestResult RequestHandler::OffsetMediaInputCursor(const Request& request)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!IsMediaTimeValid(input)) if (!IsMediaTimeValid(input))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The media input must be playing or paused in order to set the cursor position."); return RequestResult::Error(RequestStatus::InvalidResourceState,
"The media input must be playing or paused in order to set the cursor position.");
int64_t mediaCursorOffset = request.RequestData["mediaCursorOffset"]; int64_t mediaCursorOffset = request.RequestData["mediaCursorOffset"];
int64_t mediaCursor = obs_source_media_get_time(input) + mediaCursorOffset; int64_t mediaCursor = obs_source_media_get_time(input) + mediaCursorOffset;
@ -158,7 +161,7 @@ RequestResult RequestHandler::OffsetMediaInputCursor(const Request& request)
* @api requests * @api requests
* @category media inputs * @category media inputs
*/ */
RequestResult RequestHandler::TriggerMediaInputAction(const Request& request) RequestResult RequestHandler::TriggerMediaInputAction(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -166,33 +169,33 @@ RequestResult RequestHandler::TriggerMediaInputAction(const Request& request)
if (!(input && request.ValidateString("mediaAction", statusCode, comment))) if (!(input && request.ValidateString("mediaAction", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string mediaActionString = request.RequestData["mediaAction"]; enum ObsMediaInputAction mediaAction = request.RequestData["mediaAction"];
auto mediaAction = Utils::Obs::EnumHelper::GetMediaInputAction(mediaActionString);
switch (mediaAction) { switch (mediaAction) {
default: default:
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE: case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE:
return RequestResult::Error(RequestStatus::InvalidRequestField, "You have specified an invalid media input action."); return RequestResult::Error(RequestStatus::InvalidRequestField,
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY: "You have specified an invalid media input action.");
// Shoutout to whoever implemented this API call like this case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY:
obs_source_media_play_pause(input, false); // Shoutout to whoever implemented this API call like this
break; obs_source_media_play_pause(input, false);
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE: break;
obs_source_media_play_pause(input, true); case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE:
break; obs_source_media_play_pause(input, true);
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP: break;
obs_source_media_stop(input); case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP:
break; obs_source_media_stop(input);
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART: break;
// I'm only implementing this because I'm nice. I think its a really dumb action. case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART:
obs_source_media_restart(input); // I'm only implementing this because I'm nice. I think its a really dumb action.
break; obs_source_media_restart(input);
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT: break;
obs_source_media_next(input); case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT:
break; obs_source_media_next(input);
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS: break;
obs_source_media_previous(input); case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS:
break; obs_source_media_previous(input);
break;
} }
return RequestResult::Success(); return RequestResult::Success();

View File

@ -46,7 +46,7 @@ static bool ReplayBufferAvailable()
* @category outputs * @category outputs
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetVirtualCamStatus(const Request&) RequestResult RequestHandler::GetVirtualCamStatus(const Request &)
{ {
if (!VirtualCamAvailable()) if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available."); return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
@ -68,7 +68,7 @@ RequestResult RequestHandler::GetVirtualCamStatus(const Request&)
* @category outputs * @category outputs
* @api requests * @api requests
*/ */
RequestResult RequestHandler::ToggleVirtualCam(const Request&) RequestResult RequestHandler::ToggleVirtualCam(const Request &)
{ {
if (!VirtualCamAvailable()) if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available."); return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
@ -95,7 +95,7 @@ RequestResult RequestHandler::ToggleVirtualCam(const Request&)
* @api requests * @api requests
* @category outputs * @category outputs
*/ */
RequestResult RequestHandler::StartVirtualCam(const Request&) RequestResult RequestHandler::StartVirtualCam(const Request &)
{ {
if (!VirtualCamAvailable()) if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available."); return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
@ -118,7 +118,7 @@ RequestResult RequestHandler::StartVirtualCam(const Request&)
* @api requests * @api requests
* @category outputs * @category outputs
*/ */
RequestResult RequestHandler::StopVirtualCam(const Request&) RequestResult RequestHandler::StopVirtualCam(const Request &)
{ {
if (!VirtualCamAvailable()) if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available."); return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
@ -143,7 +143,7 @@ RequestResult RequestHandler::StopVirtualCam(const Request&)
* @category outputs * @category outputs
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetReplayBufferStatus(const Request&) RequestResult RequestHandler::GetReplayBufferStatus(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available."); return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
@ -165,7 +165,7 @@ RequestResult RequestHandler::GetReplayBufferStatus(const Request&)
* @category outputs * @category outputs
* @api requests * @api requests
*/ */
RequestResult RequestHandler::ToggleReplayBuffer(const Request&) RequestResult RequestHandler::ToggleReplayBuffer(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available."); return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
@ -192,7 +192,7 @@ RequestResult RequestHandler::ToggleReplayBuffer(const Request&)
* @api requests * @api requests
* @category outputs * @category outputs
*/ */
RequestResult RequestHandler::StartReplayBuffer(const Request&) RequestResult RequestHandler::StartReplayBuffer(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available."); return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
@ -215,7 +215,7 @@ RequestResult RequestHandler::StartReplayBuffer(const Request&)
* @api requests * @api requests
* @category outputs * @category outputs
*/ */
RequestResult RequestHandler::StopReplayBuffer(const Request&) RequestResult RequestHandler::StopReplayBuffer(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available."); return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
@ -238,7 +238,7 @@ RequestResult RequestHandler::StopReplayBuffer(const Request&)
* @api requests * @api requests
* @category outputs * @category outputs
*/ */
RequestResult RequestHandler::SaveReplayBuffer(const Request&) RequestResult RequestHandler::SaveReplayBuffer(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available."); return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
@ -263,7 +263,7 @@ RequestResult RequestHandler::SaveReplayBuffer(const Request&)
* @api requests * @api requests
* @category outputs * @category outputs
*/ */
RequestResult RequestHandler::GetLastReplayBufferReplay(const Request&) RequestResult RequestHandler::GetLastReplayBufferReplay(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available."); return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
@ -272,6 +272,217 @@ RequestResult RequestHandler::GetLastReplayBufferReplay(const Request&)
return RequestResult::Error(RequestStatus::OutputNotRunning); return RequestResult::Error(RequestStatus::OutputNotRunning);
json responseData; json responseData;
responseData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFilePath(); responseData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFileName();
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
/**
* Gets the list of available outputs.
*
* @requestType GetOutputList
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::GetOutputList(const Request &)
{
json responseData;
responseData["outputs"] = Utils::Obs::ArrayHelper::GetOutputList();
return RequestResult::Success(responseData);
}
/**
* Gets the status of an output.
*
* @requestField outputName | String | Output name
*
* @responseField outputActive | Boolean | Whether the output is active
* @responseField outputReconnecting | Boolean | Whether the output is reconnecting
* @responseField outputTimecode | String | Current formatted timecode string for the output
* @responseField outputDuration | Number | Current duration in milliseconds for the output
* @responseField outputCongestion | Number | Congestion of the output
* @responseField outputBytes | Number | Number of bytes sent by the output
* @responseField outputSkippedFrames | Number | Number of frames skipped by the output's process
* @responseField outputTotalFrames | Number | Total number of frames delivered by the output's process
*
* @requestType GetOutputStatus
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::GetOutputStatus(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!output)
return RequestResult::Error(statusCode, comment);
uint64_t outputDuration = Utils::Obs::NumberHelper::GetOutputDuration(output);
json responseData;
responseData["outputActive"] = obs_output_active(output);
responseData["outputReconnecting"] = obs_output_reconnecting(output);
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
responseData["outputDuration"] = outputDuration;
responseData["outputCongestion"] = obs_output_get_congestion(output);
responseData["outputBytes"] = obs_output_get_total_bytes(output);
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(output);
responseData["outputTotalFrames"] = obs_output_get_total_frames(output);
return RequestResult::Success(responseData);
}
/**
* Toggles the status of an output.
*
* @requestField outputName | String | Output name
*
* @responseField outputActive | Boolean | Whether the output is active
*
* @requestType ToggleOutput
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::ToggleOutput(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!output)
return RequestResult::Error(statusCode, comment);
bool outputActive = obs_output_active(output);
if (outputActive)
obs_output_stop(output);
else
obs_output_start(output);
json responseData;
responseData["outputActive"] = !outputActive;
return RequestResult::Success(responseData);
}
/**
* Starts an output.
*
* @requestField outputName | String | Output name
*
* @requestType StartOutput
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::StartOutput(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!output)
return RequestResult::Error(statusCode, comment);
if (obs_output_active(output))
return RequestResult::Error(RequestStatus::OutputRunning);
obs_output_start(output);
return RequestResult::Success();
}
/**
* Stops an output.
*
* @requestField outputName | String | Output name
*
* @requestType StopOutput
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::StopOutput(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!output)
return RequestResult::Error(statusCode, comment);
if (!obs_output_active(output))
return RequestResult::Error(RequestStatus::OutputNotRunning);
obs_output_stop(output);
return RequestResult::Success();
}
/**
* Gets the settings of an output.
*
* @requestField outputName | String | Output name
*
* @responseField outputSettings | Object | Output settings
*
* @requestType GetOutputSettings
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::GetOutputSettings(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!output)
return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease outputSettings = obs_output_get_settings(output);
json responseData;
responseData["outputSettings"] = Utils::Json::ObsDataToJson(outputSettings);
return RequestResult::Success(responseData);
}
/**
* Sets the settings of an output.
*
* @requestField outputName | String | Output name
* @requestField outputSettings | Object | Output settings
*
* @requestType SetOutputSettings
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::SetOutputSettings(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!(output && request.ValidateObject("outputSettings", statusCode, comment, true)))
return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["outputSettings"]);
if (!newSettings)
// This should never happen
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
"An internal data conversion operation failed. Please report this!");
obs_output_update(output, newSettings);
return RequestResult::Success();
}

View File

@ -35,7 +35,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @api requests * @api requests
* @category record * @category record
*/ */
RequestResult RequestHandler::GetRecordStatus(const Request&) RequestResult RequestHandler::GetRecordStatus(const Request &)
{ {
OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output(); OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output();
@ -61,7 +61,7 @@ RequestResult RequestHandler::GetRecordStatus(const Request&)
* @api requests * @api requests
* @category record * @category record
*/ */
RequestResult RequestHandler::ToggleRecord(const Request&) RequestResult RequestHandler::ToggleRecord(const Request &)
{ {
json responseData; json responseData;
if (obs_frontend_recording_active()) { if (obs_frontend_recording_active()) {
@ -85,7 +85,7 @@ RequestResult RequestHandler::ToggleRecord(const Request&)
* @api requests * @api requests
* @category record * @category record
*/ */
RequestResult RequestHandler::StartRecord(const Request&) RequestResult RequestHandler::StartRecord(const Request &)
{ {
if (obs_frontend_recording_active()) if (obs_frontend_recording_active())
return RequestResult::Error(RequestStatus::OutputRunning); return RequestResult::Error(RequestStatus::OutputRunning);
@ -99,6 +99,8 @@ RequestResult RequestHandler::StartRecord(const Request&)
/** /**
* Stops the record output. * Stops the record output.
* *
* @responseField outputPath | String | File name for the saved recording
*
* @requestType StopRecord * @requestType StopRecord
* @complexity 1 * @complexity 1
* @rpcVersion -1 * @rpcVersion -1
@ -106,7 +108,7 @@ RequestResult RequestHandler::StartRecord(const Request&)
* @api requests * @api requests
* @category record * @category record
*/ */
RequestResult RequestHandler::StopRecord(const Request&) RequestResult RequestHandler::StopRecord(const Request &)
{ {
if (!obs_frontend_recording_active()) if (!obs_frontend_recording_active())
return RequestResult::Error(RequestStatus::OutputNotRunning); return RequestResult::Error(RequestStatus::OutputNotRunning);
@ -114,7 +116,10 @@ RequestResult RequestHandler::StopRecord(const Request&)
// TODO: Call signal directly to perform blocking wait // TODO: Call signal directly to perform blocking wait
obs_frontend_recording_stop(); obs_frontend_recording_stop();
return RequestResult::Success(); json responseData;
responseData["outputPath"] = Utils::Obs::StringHelper::GetLastRecordFileName();
return RequestResult::Success(responseData);
} }
/** /**
@ -127,7 +132,7 @@ RequestResult RequestHandler::StopRecord(const Request&)
* @api requests * @api requests
* @category record * @category record
*/ */
RequestResult RequestHandler::ToggleRecordPause(const Request&) RequestResult RequestHandler::ToggleRecordPause(const Request &)
{ {
json responseData; json responseData;
if (obs_frontend_recording_paused()) { if (obs_frontend_recording_paused()) {
@ -151,7 +156,7 @@ RequestResult RequestHandler::ToggleRecordPause(const Request&)
* @api requests * @api requests
* @category record * @category record
*/ */
RequestResult RequestHandler::PauseRecord(const Request&) RequestResult RequestHandler::PauseRecord(const Request &)
{ {
if (obs_frontend_recording_paused()) if (obs_frontend_recording_paused())
return RequestResult::Error(RequestStatus::OutputPaused); return RequestResult::Error(RequestStatus::OutputPaused);
@ -172,7 +177,7 @@ RequestResult RequestHandler::PauseRecord(const Request&)
* @api requests * @api requests
* @category record * @category record
*/ */
RequestResult RequestHandler::ResumeRecord(const Request&) RequestResult RequestHandler::ResumeRecord(const Request &)
{ {
if (!obs_frontend_recording_paused()) if (!obs_frontend_recording_paused())
return RequestResult::Error(RequestStatus::OutputNotPaused); return RequestResult::Error(RequestStatus::OutputNotPaused);

View File

@ -35,7 +35,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::GetSceneItemList(const Request& request) RequestResult RequestHandler::GetSceneItemList(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -67,7 +67,7 @@ RequestResult RequestHandler::GetSceneItemList(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::GetGroupSceneItemList(const Request& request) RequestResult RequestHandler::GetGroupSceneItemList(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -99,11 +99,12 @@ RequestResult RequestHandler::GetGroupSceneItemList(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::GetSceneItemId(const Request& request) RequestResult RequestHandler::GetSceneItemId(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneAutoRelease scene = request.ValidateScene2("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneAutoRelease scene =
request.ValidateScene2("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(scene && request.ValidateString("sourceName", statusCode, comment))) if (!(scene && request.ValidateString("sourceName", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -118,7 +119,8 @@ RequestResult RequestHandler::GetSceneItemId(const Request& request)
OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName, offset); OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName, offset);
if (!item) if (!item)
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene items were found in the specified scene by that name or offset."); return RequestResult::Error(RequestStatus::ResourceNotFound,
"No scene items were found in the specified scene by that name or offset.");
json responseData; json responseData;
responseData["sceneItemId"] = obs_sceneitem_get_id(item); responseData["sceneItemId"] = obs_sceneitem_get_id(item);
@ -144,7 +146,7 @@ RequestResult RequestHandler::GetSceneItemId(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::CreateSceneItem(const Request& request) RequestResult RequestHandler::CreateSceneItem(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -193,7 +195,7 @@ RequestResult RequestHandler::CreateSceneItem(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::RemoveSceneItem(const Request& request) RequestResult RequestHandler::RemoveSceneItem(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -225,7 +227,7 @@ RequestResult RequestHandler::RemoveSceneItem(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::DuplicateSceneItem(const Request& request) RequestResult RequestHandler::DuplicateSceneItem(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -242,7 +244,8 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
} else { } else {
destinationScene = obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem)); destinationScene = obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem));
if (!destinationScene) if (!destinationScene)
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Internal error: Failed to get ref for scene of scene item."); return RequestResult::Error(RequestStatus::RequestProcessingFailed,
"Internal error: Failed to get ref for scene of scene item.");
} }
if (obs_sceneitem_is_group(sceneItem) && obs_sceneitem_get_scene(sceneItem) == destinationScene) { if (obs_sceneitem_is_group(sceneItem) && obs_sceneitem_get_scene(sceneItem) == destinationScene) {
@ -259,7 +262,8 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
obs_sceneitem_get_crop(sceneItem, &sceneItemCrop); obs_sceneitem_get_crop(sceneItem, &sceneItemCrop);
// Create the new item // Create the new item
OBSSceneItemAutoRelease newSceneItem = Utils::Obs::ActionHelper::CreateSceneItem(sceneItemSource, destinationScene, sceneItemEnabled, &sceneItemTransform, &sceneItemCrop); OBSSceneItemAutoRelease newSceneItem = Utils::Obs::ActionHelper::CreateSceneItem(
sceneItemSource, destinationScene, sceneItemEnabled, &sceneItemTransform, &sceneItemCrop);
obs_scene_release(destinationScene); obs_scene_release(destinationScene);
if (!newSceneItem) if (!newSceneItem)
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene item."); return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene item.");
@ -287,11 +291,12 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::GetSceneItemTransform(const Request& request) RequestResult RequestHandler::GetSceneItemTransform(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -315,11 +320,12 @@ RequestResult RequestHandler::GetSceneItemTransform(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::SetSceneItemTransform(const Request& request) RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(sceneItem && request.ValidateObject("sceneItemTransform", statusCode, comment))) if (!(sceneItem && request.ValidateObject("sceneItemTransform", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -363,7 +369,8 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
float scaleX = r.RequestData["scaleX"]; float scaleX = r.RequestData["scaleX"];
float finalWidth = scaleX * sourceWidth; float finalWidth = scaleX * sourceWidth;
if (!(finalWidth > -90001.0 && finalWidth < 90001.0)) if (!(finalWidth > -90001.0 && finalWidth < 90001.0))
return RequestResult::Error(RequestStatus::RequestFieldOutOfRange, "The field scaleX is too small or large for the current source resolution."); return RequestResult::Error(RequestStatus::RequestFieldOutOfRange,
"The field `scaleX` is too small or large for the current source resolution.");
sceneItemTransform.scale.x = scaleX; sceneItemTransform.scale.x = scaleX;
transformChanged = true; transformChanged = true;
} }
@ -373,7 +380,8 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
float scaleY = r.RequestData["scaleY"]; float scaleY = r.RequestData["scaleY"];
float finalHeight = scaleY * sourceHeight; float finalHeight = scaleY * sourceHeight;
if (!(finalHeight > -90001.0 && finalHeight < 90001.0)) if (!(finalHeight > -90001.0 && finalHeight < 90001.0))
return RequestResult::Error(RequestStatus::RequestFieldOutOfRange, "The field scaleY is too small or large for the current source resolution."); return RequestResult::Error(RequestStatus::RequestFieldOutOfRange,
"The field `scaleY` is too small or large for the current source resolution.");
sceneItemTransform.scale.y = scaleY; sceneItemTransform.scale.y = scaleY;
transformChanged = true; transformChanged = true;
} }
@ -388,10 +396,10 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
if (r.Contains("boundsType")) { if (r.Contains("boundsType")) {
if (!r.ValidateOptionalString("boundsType", statusCode, comment)) if (!r.ValidateOptionalString("boundsType", statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string boundsTypeString = r.RequestData["boundsType"]; enum obs_bounds_type boundsType = r.RequestData["boundsType"];
enum obs_bounds_type boundsType = Utils::Obs::EnumHelper::GetSceneItemBoundsType(boundsTypeString); if (boundsType == OBS_BOUNDS_NONE && r.RequestData["boundsType"] != "OBS_BOUNDS_NONE")
if (boundsType == OBS_BOUNDS_NONE && boundsTypeString != "OBS_BOUNDS_NONE") return RequestResult::Error(RequestStatus::InvalidRequestField,
return RequestResult::Error(RequestStatus::InvalidRequestField, "The field boundsType has an invalid value."); "The field `boundsType` has an invalid value.");
sceneItemTransform.bounds_type = boundsType; sceneItemTransform.bounds_type = boundsType;
transformChanged = true; transformChanged = true;
} }
@ -470,11 +478,12 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::GetSceneItemEnabled(const Request& request) RequestResult RequestHandler::GetSceneItemEnabled(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -500,11 +509,12 @@ RequestResult RequestHandler::GetSceneItemEnabled(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::SetSceneItemEnabled(const Request& request) RequestResult RequestHandler::SetSceneItemEnabled(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(sceneItem && request.ValidateBoolean("sceneItemEnabled", statusCode, comment))) if (!(sceneItem && request.ValidateBoolean("sceneItemEnabled", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -532,11 +542,12 @@ RequestResult RequestHandler::SetSceneItemEnabled(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::GetSceneItemLocked(const Request& request) RequestResult RequestHandler::GetSceneItemLocked(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -562,11 +573,12 @@ RequestResult RequestHandler::GetSceneItemLocked(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::SetSceneItemLocked(const Request& request) RequestResult RequestHandler::SetSceneItemLocked(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(sceneItem && request.ValidateBoolean("sceneItemLocked", statusCode, comment))) if (!(sceneItem && request.ValidateBoolean("sceneItemLocked", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -596,11 +608,12 @@ RequestResult RequestHandler::SetSceneItemLocked(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::GetSceneItemIndex(const Request& request) RequestResult RequestHandler::GetSceneItemIndex(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -626,11 +639,12 @@ RequestResult RequestHandler::GetSceneItemIndex(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::SetSceneItemIndex(const Request& request) RequestResult RequestHandler::SetSceneItemIndex(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(sceneItem && request.ValidateNumber("sceneItemIndex", statusCode, comment, 0, 8192))) if (!(sceneItem && request.ValidateNumber("sceneItemIndex", statusCode, comment, 0, 8192)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -668,18 +682,19 @@ RequestResult RequestHandler::SetSceneItemIndex(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::GetSceneItemBlendMode(const Request& request) RequestResult RequestHandler::GetSceneItemBlendMode(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
auto blendMode = obs_sceneitem_get_blending_mode(sceneItem); auto blendMode = obs_sceneitem_get_blending_mode(sceneItem);
json responseData; json responseData;
responseData["sceneItemBlendMode"] = Utils::Obs::StringHelper::GetSceneItemBlendMode(blendMode); responseData["sceneItemBlendMode"] = blendMode;
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -700,19 +715,19 @@ RequestResult RequestHandler::GetSceneItemBlendMode(const Request& request)
* @api requests * @api requests
* @category scene items * @category scene items
*/ */
RequestResult RequestHandler::SetSceneItemBlendMode(const Request& request) RequestResult RequestHandler::SetSceneItemBlendMode(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(sceneItem && request.ValidateString("sceneItemBlendMode", statusCode, comment))) if (!(sceneItem && request.ValidateString("sceneItemBlendMode", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string blendModeString = request.RequestData["sceneItemBlendMode"]; enum obs_blending_type blendMode = request.RequestData["sceneItemBlendMode"];
if (blendMode == OBS_BLEND_NORMAL && request.RequestData["sceneItemBlendMode"] != "OBS_BLEND_NORMAL")
auto blendMode = Utils::Obs::EnumHelper::GetSceneItemBlendMode(blendModeString); return RequestResult::Error(RequestStatus::InvalidRequestField,
if (blendMode == OBS_BLEND_NORMAL && blendModeString != "OBS_BLEND_NORMAL") "The field sceneItemBlendMode has an invalid value.");
return RequestResult::Error(RequestStatus::InvalidRequestField, "The field sceneItemBlendMode has an invalid value.");
obs_sceneitem_set_blending_mode(sceneItem, blendMode); obs_sceneitem_set_blending_mode(sceneItem, blendMode);
@ -720,11 +735,12 @@ RequestResult RequestHandler::SetSceneItemBlendMode(const Request& request)
} }
// Intentionally undocumented // Intentionally undocumented
RequestResult RequestHandler::GetSceneItemPrivateSettings(const Request& request) RequestResult RequestHandler::GetSceneItemPrivateSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -737,11 +753,12 @@ RequestResult RequestHandler::GetSceneItemPrivateSettings(const Request& request
} }
// Intentionally undocumented // Intentionally undocumented
RequestResult RequestHandler::SetSceneItemPrivateSettings(const Request& request) RequestResult RequestHandler::SetSceneItemPrivateSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem || !request.ValidateObject("sceneItemSettings", statusCode, comment)) if (!sceneItem || !request.ValidateObject("sceneItemSettings", statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);

View File

@ -33,7 +33,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestHandler::GetSceneList(const Request&) RequestResult RequestHandler::GetSceneList(const Request &)
{ {
json responseData; json responseData;
@ -68,7 +68,7 @@ RequestResult RequestHandler::GetSceneList(const Request&)
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestHandler::GetGroupList(const Request&) RequestResult RequestHandler::GetGroupList(const Request &)
{ {
json responseData; json responseData;
@ -89,7 +89,7 @@ RequestResult RequestHandler::GetGroupList(const Request&)
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestHandler::GetCurrentProgramScene(const Request&) RequestResult RequestHandler::GetCurrentProgramScene(const Request &)
{ {
json responseData; json responseData;
OBSSourceAutoRelease currentProgramScene = obs_frontend_get_current_scene(); OBSSourceAutoRelease currentProgramScene = obs_frontend_get_current_scene();
@ -110,7 +110,7 @@ RequestResult RequestHandler::GetCurrentProgramScene(const Request&)
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestHandler::SetCurrentProgramScene(const Request& request) RequestResult RequestHandler::SetCurrentProgramScene(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -137,7 +137,7 @@ RequestResult RequestHandler::SetCurrentProgramScene(const Request& request)
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestHandler::GetCurrentPreviewScene(const Request&) RequestResult RequestHandler::GetCurrentPreviewScene(const Request &)
{ {
if (!obs_frontend_preview_program_mode_active()) if (!obs_frontend_preview_program_mode_active())
return RequestResult::Error(RequestStatus::StudioModeNotActive); return RequestResult::Error(RequestStatus::StudioModeNotActive);
@ -164,7 +164,7 @@ RequestResult RequestHandler::GetCurrentPreviewScene(const Request&)
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestHandler::SetCurrentPreviewScene(const Request& request) RequestResult RequestHandler::SetCurrentPreviewScene(const Request &request)
{ {
if (!obs_frontend_preview_program_mode_active()) if (!obs_frontend_preview_program_mode_active())
return RequestResult::Error(RequestStatus::StudioModeNotActive); return RequestResult::Error(RequestStatus::StudioModeNotActive);
@ -192,7 +192,7 @@ RequestResult RequestHandler::SetCurrentPreviewScene(const Request& request)
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestHandler::CreateScene(const Request& request) RequestResult RequestHandler::CreateScene(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -226,7 +226,7 @@ RequestResult RequestHandler::CreateScene(const Request& request)
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestHandler::RemoveScene(const Request& request) RequestResult RequestHandler::RemoveScene(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -235,7 +235,8 @@ RequestResult RequestHandler::RemoveScene(const Request& request)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (Utils::Obs::NumberHelper::GetSceneCount() < 2) if (Utils::Obs::NumberHelper::GetSceneCount() < 2)
return RequestResult::Error(RequestStatus::NotEnoughResources, "You cannot remove the last scene in the collection."); return RequestResult::Error(RequestStatus::NotEnoughResources,
"You cannot remove the last scene in the collection.");
obs_source_remove(scene); obs_source_remove(scene);
@ -255,7 +256,7 @@ RequestResult RequestHandler::RemoveScene(const Request& request)
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestHandler::SetSceneName(const Request& request) RequestResult RequestHandler::SetSceneName(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -267,7 +268,8 @@ RequestResult RequestHandler::SetSceneName(const Request& request)
OBSSourceAutoRelease existingSource = obs_get_source_by_name(newSceneName.c_str()); OBSSourceAutoRelease existingSource = obs_get_source_by_name(newSceneName.c_str());
if (existingSource) if (existingSource)
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that new scene name."); return RequestResult::Error(RequestStatus::ResourceAlreadyExists,
"A source already exists by that new scene name.");
obs_source_set_name(scene, newSceneName.c_str()); obs_source_set_name(scene, newSceneName.c_str());
@ -289,7 +291,7 @@ RequestResult RequestHandler::SetSceneName(const Request& request)
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestHandler::GetSceneSceneTransitionOverride(const Request& request) RequestResult RequestHandler::GetSceneSceneTransitionOverride(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -328,7 +330,7 @@ RequestResult RequestHandler::GetSceneSceneTransitionOverride(const Request& req
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request& request) RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -342,7 +344,8 @@ RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request& req
if (hasName && !request.RequestData["transitionName"].is_null()) { if (hasName && !request.RequestData["transitionName"].is_null()) {
if (!request.ValidateOptionalString("transitionName", statusCode, comment)) if (!request.ValidateOptionalString("transitionName", statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
OBSSourceAutoRelease transition = Utils::Obs::SearchHelper::GetSceneTransitionByName(request.RequestData["transitionName"]); OBSSourceAutoRelease transition =
Utils::Obs::SearchHelper::GetSceneTransitionByName(request.RequestData["transitionName"]);
if (!transition) if (!transition)
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene transition was found by that name."); return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene transition was found by that name.");
} }
@ -354,7 +357,8 @@ RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request& req
} }
if (!hasName && !hasDuration) if (!hasName && !hasDuration)
return RequestResult::Error(RequestStatus::MissingRequestField, "Your request data must include either `transitionName` or `transitionDuration`."); return RequestResult::Error(RequestStatus::MissingRequestField,
"Your request data must include either `transitionName` or `transitionDuration`.");
if (hasName) { if (hasName) {
if (request.RequestData["transitionName"].is_null()) { if (request.RequestData["transitionName"].is_null()) {

View File

@ -56,14 +56,14 @@ QImage TakeSourceScreenshot(obs_source_t *source, bool &success, uint32_t reques
ret.fill(0); ret.fill(0);
// Video image buffer // Video image buffer
uint8_t* videoData = nullptr; uint8_t *videoData = nullptr;
uint32_t videoLinesize = 0; uint32_t videoLinesize = 0;
// Enter graphics context // Enter graphics context
obs_enter_graphics(); obs_enter_graphics();
gs_texrender_t* texRender = gs_texrender_create(GS_RGBA, GS_ZS_NONE); gs_texrender_t *texRender = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
gs_stagesurf_t* stageSurface = gs_stagesurface_create(imgWidth, imgHeight, GS_RGBA); gs_stagesurf_t *stageSurface = gs_stagesurface_create(imgWidth, imgHeight, GS_RGBA);
success = false; success = false;
gs_texrender_reset(texRender); gs_texrender_reset(texRender);
@ -88,7 +88,7 @@ QImage TakeSourceScreenshot(obs_source_t *source, bool &success, uint32_t reques
if (gs_stagesurface_map(stageSurface, &videoData, &videoLinesize)) { if (gs_stagesurface_map(stageSurface, &videoData, &videoLinesize)) {
int lineSize = ret.bytesPerLine(); int lineSize = ret.bytesPerLine();
for (uint y = 0; y < imgHeight; y++) { for (uint y = 0; y < imgHeight; y++) {
memcpy(ret.scanLine(y), videoData + (y * videoLinesize), lineSize); memcpy(ret.scanLine(y), videoData + (y * videoLinesize), lineSize);
} }
gs_stagesurface_unmap(stageSurface); gs_stagesurface_unmap(stageSurface);
success = true; success = true;
@ -126,7 +126,7 @@ bool IsImageFormatValid(std::string format)
* @api requests * @api requests
* @category sources * @category sources
*/ */
RequestResult RequestHandler::GetSourceActive(const Request& request) RequestResult RequestHandler::GetSourceActive(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -166,7 +166,7 @@ RequestResult RequestHandler::GetSourceActive(const Request& request)
* @api requests * @api requests
* @category sources * @category sources
*/ */
RequestResult RequestHandler::GetSourceScreenshot(const Request& request) RequestResult RequestHandler::GetSourceScreenshot(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -180,7 +180,8 @@ RequestResult RequestHandler::GetSourceScreenshot(const Request& request)
std::string imageFormat = request.RequestData["imageFormat"]; std::string imageFormat = request.RequestData["imageFormat"];
if (!IsImageFormatValid(imageFormat)) if (!IsImageFormatValid(imageFormat))
return RequestResult::Error(RequestStatus::InvalidRequestField, "Your specified image format is invalid or not supported by this system."); return RequestResult::Error(RequestStatus::InvalidRequestField,
"Your specified image format is invalid or not supported by this system.");
uint32_t requestedWidth{0}; uint32_t requestedWidth{0};
uint32_t requestedHeight{0}; uint32_t requestedHeight{0};
@ -253,12 +254,13 @@ RequestResult RequestHandler::GetSourceScreenshot(const Request& request)
* @api requests * @api requests
* @category sources * @category sources
*/ */
RequestResult RequestHandler::SaveSourceScreenshot(const Request& request) RequestResult RequestHandler::SaveSourceScreenshot(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment); OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
if (!(source && request.ValidateString("imageFormat", statusCode, comment) && request.ValidateString("imageFilePath", statusCode, comment))) if (!(source && request.ValidateString("imageFormat", statusCode, comment) &&
request.ValidateString("imageFilePath", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE) if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE)
@ -268,7 +270,8 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request& request)
std::string imageFilePath = request.RequestData["imageFilePath"]; std::string imageFilePath = request.RequestData["imageFilePath"];
if (!IsImageFormatValid(imageFormat)) if (!IsImageFormatValid(imageFormat))
return RequestResult::Error(RequestStatus::InvalidRequestField, "Your specified image format is invalid or not supported by this system."); return RequestResult::Error(RequestStatus::InvalidRequestField,
"Your specified image format is invalid or not supported by this system.");
QFileInfo filePathInfo(QString::fromStdString(imageFilePath)); QFileInfo filePathInfo(QString::fromStdString(imageFilePath));
if (!filePathInfo.absoluteDir().exists()) if (!filePathInfo.absoluteDir().exists())
@ -314,7 +317,7 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request& request)
} }
// Intentionally undocumented // Intentionally undocumented
RequestResult RequestHandler::GetSourcePrivateSettings(const Request& request) RequestResult RequestHandler::GetSourcePrivateSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -331,7 +334,7 @@ RequestResult RequestHandler::GetSourcePrivateSettings(const Request& request)
} }
// Intentionally undocumented // Intentionally undocumented
RequestResult RequestHandler::SetSourcePrivateSettings(const Request& request) RequestResult RequestHandler::SetSourcePrivateSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;

View File

@ -26,6 +26,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @responseField outputReconnecting | Boolean | Whether the output is currently reconnecting * @responseField outputReconnecting | Boolean | Whether the output is currently reconnecting
* @responseField outputTimecode | String | Current formatted timecode string for the output * @responseField outputTimecode | String | Current formatted timecode string for the output
* @responseField outputDuration | Number | Current duration in milliseconds for the output * @responseField outputDuration | Number | Current duration in milliseconds for the output
* @responseField outputCongestion | Number | Congestion of the output
* @responseField outputBytes | Number | Number of bytes sent by the output * @responseField outputBytes | Number | Number of bytes sent by the output
* @responseField outputSkippedFrames | Number | Number of frames skipped by the output's process * @responseField outputSkippedFrames | Number | Number of frames skipped by the output's process
* @responseField outputTotalFrames | Number | Total number of frames delivered by the output's process * @responseField outputTotalFrames | Number | Total number of frames delivered by the output's process
@ -37,7 +38,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @api requests * @api requests
* @category stream * @category stream
*/ */
RequestResult RequestHandler::GetStreamStatus(const Request&) RequestResult RequestHandler::GetStreamStatus(const Request &)
{ {
OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output(); OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output();
@ -48,6 +49,7 @@ RequestResult RequestHandler::GetStreamStatus(const Request&)
responseData["outputReconnecting"] = obs_output_reconnecting(streamOutput); responseData["outputReconnecting"] = obs_output_reconnecting(streamOutput);
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration); responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
responseData["outputDuration"] = outputDuration; responseData["outputDuration"] = outputDuration;
responseData["outputCongestion"] = obs_output_get_congestion(streamOutput);
responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(streamOutput); responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(streamOutput);
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(streamOutput); responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(streamOutput);
responseData["outputTotalFrames"] = obs_output_get_total_frames(streamOutput); responseData["outputTotalFrames"] = obs_output_get_total_frames(streamOutput);
@ -67,7 +69,7 @@ RequestResult RequestHandler::GetStreamStatus(const Request&)
* @api requests * @api requests
* @category stream * @category stream
*/ */
RequestResult RequestHandler::ToggleStream(const Request&) RequestResult RequestHandler::ToggleStream(const Request &)
{ {
json responseData; json responseData;
if (obs_frontend_streaming_active()) { if (obs_frontend_streaming_active()) {
@ -91,7 +93,7 @@ RequestResult RequestHandler::ToggleStream(const Request&)
* @api requests * @api requests
* @category stream * @category stream
*/ */
RequestResult RequestHandler::StartStream(const Request&) RequestResult RequestHandler::StartStream(const Request &)
{ {
if (obs_frontend_streaming_active()) if (obs_frontend_streaming_active())
return RequestResult::Error(RequestStatus::OutputRunning); return RequestResult::Error(RequestStatus::OutputRunning);
@ -112,7 +114,7 @@ RequestResult RequestHandler::StartStream(const Request&)
* @api requests * @api requests
* @category stream * @category stream
*/ */
RequestResult RequestHandler::StopStream(const Request&) RequestResult RequestHandler::StopStream(const Request &)
{ {
if (!obs_frontend_streaming_active()) if (!obs_frontend_streaming_active())
return RequestResult::Error(RequestStatus::OutputNotRunning); return RequestResult::Error(RequestStatus::OutputNotRunning);
@ -135,7 +137,7 @@ RequestResult RequestHandler::StopStream(const Request&)
* @category stream * @category stream
* @api requests * @api requests
*/ */
RequestResult RequestHandler::SendStreamCaption(const Request& request) RequestResult RequestHandler::SendStreamCaption(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;

View File

@ -35,7 +35,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @api requests * @api requests
* @category transitions * @category transitions
*/ */
RequestResult RequestHandler::GetTransitionKindList(const Request&) RequestResult RequestHandler::GetTransitionKindList(const Request &)
{ {
json responseData; json responseData;
responseData["transitionKinds"] = Utils::Obs::ArrayHelper::GetTransitionKindList(); responseData["transitionKinds"] = Utils::Obs::ArrayHelper::GetTransitionKindList();
@ -56,7 +56,7 @@ RequestResult RequestHandler::GetTransitionKindList(const Request&)
* @api requests * @api requests
* @category transitions * @category transitions
*/ */
RequestResult RequestHandler::GetSceneTransitionList(const Request&) RequestResult RequestHandler::GetSceneTransitionList(const Request &)
{ {
json responseData; json responseData;
@ -91,11 +91,12 @@ RequestResult RequestHandler::GetSceneTransitionList(const Request&)
* @api requests * @api requests
* @category transitions * @category transitions
*/ */
RequestResult RequestHandler::GetCurrentSceneTransition(const Request&) RequestResult RequestHandler::GetCurrentSceneTransition(const Request &)
{ {
OBSSourceAutoRelease transition = obs_frontend_get_current_transition(); OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
if (!transition) if (!transition)
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen! return RequestResult::Error(RequestStatus::InvalidResourceState,
"OBS does not currently have a scene transition set."); // This should not happen!
json responseData; json responseData;
responseData["transitionName"] = obs_source_get_name(transition); responseData["transitionName"] = obs_source_get_name(transition);
@ -135,7 +136,7 @@ RequestResult RequestHandler::GetCurrentSceneTransition(const Request&)
* @api requests * @api requests
* @category transitions * @category transitions
*/ */
RequestResult RequestHandler::SetCurrentSceneTransition(const Request& request) RequestResult RequestHandler::SetCurrentSceneTransition(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -165,7 +166,7 @@ RequestResult RequestHandler::SetCurrentSceneTransition(const Request& request)
* @api requests * @api requests
* @category transitions * @category transitions
*/ */
RequestResult RequestHandler::SetCurrentSceneTransitionDuration(const Request& request) RequestResult RequestHandler::SetCurrentSceneTransitionDuration(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -192,7 +193,7 @@ RequestResult RequestHandler::SetCurrentSceneTransitionDuration(const Request& r
* @api requests * @api requests
* @category transitions * @category transitions
*/ */
RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request& request) RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -201,10 +202,12 @@ RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request& r
OBSSourceAutoRelease transition = obs_frontend_get_current_transition(); OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
if (!transition) if (!transition)
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen! return RequestResult::Error(RequestStatus::InvalidResourceState,
"OBS does not currently have a scene transition set."); // This should not happen!
if (!obs_source_configurable(transition)) if (!obs_source_configurable(transition))
return RequestResult::Error(RequestStatus::ResourceNotConfigurable, "The current transition does not support custom settings."); return RequestResult::Error(RequestStatus::ResourceNotConfigurable,
"The current transition does not support custom settings.");
bool overlay = true; bool overlay = true;
if (request.Contains("overlay")) { if (request.Contains("overlay")) {
@ -216,7 +219,8 @@ RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request& r
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["transitionSettings"]); OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["transitionSettings"]);
if (!newSettings) if (!newSettings)
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!"); return RequestResult::Error(RequestStatus::RequestProcessingFailed,
"An internal data conversion operation failed. Please report this!");
if (overlay) if (overlay)
obs_source_update(transition, newSettings); obs_source_update(transition, newSettings);
@ -242,11 +246,12 @@ RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request& r
* @api requests * @api requests
* @category transitions * @category transitions
*/ */
RequestResult RequestHandler::GetCurrentSceneTransitionCursor(const Request&) RequestResult RequestHandler::GetCurrentSceneTransitionCursor(const Request &)
{ {
OBSSourceAutoRelease transition = obs_frontend_get_current_transition(); OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
if (!transition) if (!transition)
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen! return RequestResult::Error(RequestStatus::InvalidResourceState,
"OBS does not currently have a scene transition set."); // This should not happen!
json responseData; json responseData;
responseData["transitionCursor"] = obs_transition_get_time(transition); responseData["transitionCursor"] = obs_transition_get_time(transition);
@ -264,7 +269,7 @@ RequestResult RequestHandler::GetCurrentSceneTransitionCursor(const Request&)
* @api requests * @api requests
* @category transitions * @category transitions
*/ */
RequestResult RequestHandler::TriggerStudioModeTransition(const Request&) RequestResult RequestHandler::TriggerStudioModeTransition(const Request &)
{ {
if (!obs_frontend_preview_program_mode_active()) if (!obs_frontend_preview_program_mode_active())
return RequestResult::Error(RequestStatus::StudioModeNotActive); return RequestResult::Error(RequestStatus::StudioModeNotActive);
@ -291,7 +296,7 @@ RequestResult RequestHandler::TriggerStudioModeTransition(const Request&)
* @api requests * @api requests
* @category transitions * @category transitions
*/ */
RequestResult RequestHandler::SetTBarPosition(const Request& request) RequestResult RequestHandler::SetTBarPosition(const Request &request)
{ {
if (!obs_frontend_preview_program_mode_active()) if (!obs_frontend_preview_program_mode_active())
return RequestResult::Error(RequestStatus::StudioModeNotActive); return RequestResult::Error(RequestStatus::StudioModeNotActive);
@ -309,7 +314,8 @@ RequestResult RequestHandler::SetTBarPosition(const Request& request)
OBSSourceAutoRelease transition = obs_frontend_get_current_transition(); OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
if (!transition) if (!transition)
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen! return RequestResult::Error(RequestStatus::InvalidResourceState,
"OBS does not currently have a scene transition set."); // This should not happen!
float position = request.RequestData["position"]; float position = request.RequestData["position"];

View File

@ -36,7 +36,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @category ui * @category ui
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetStudioModeEnabled(const Request&) RequestResult RequestHandler::GetStudioModeEnabled(const Request &)
{ {
json responseData; json responseData;
responseData["studioModeEnabled"] = obs_frontend_preview_program_mode_active(); responseData["studioModeEnabled"] = obs_frontend_preview_program_mode_active();
@ -55,7 +55,7 @@ RequestResult RequestHandler::GetStudioModeEnabled(const Request&)
* @category ui * @category ui
* @api requests * @api requests
*/ */
RequestResult RequestHandler::SetStudioModeEnabled(const Request& request) RequestResult RequestHandler::SetStudioModeEnabled(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -67,10 +67,13 @@ RequestResult RequestHandler::SetStudioModeEnabled(const Request& request)
// (Bad) Create a boolean then pass it as a reference to the task. Requires `wait` in obs_queue_task() to be true, else undefined behavior // (Bad) Create a boolean then pass it as a reference to the task. Requires `wait` in obs_queue_task() to be true, else undefined behavior
bool studioModeEnabled = request.RequestData["studioModeEnabled"]; bool studioModeEnabled = request.RequestData["studioModeEnabled"];
// Queue the task inside of the UI thread to prevent race conditions // Queue the task inside of the UI thread to prevent race conditions
obs_queue_task(OBS_TASK_UI, [](void* param) { obs_queue_task(
auto studioModeEnabled = (bool*)param; OBS_TASK_UI,
obs_frontend_set_preview_program_mode(*studioModeEnabled); [](void *param) {
}, &studioModeEnabled, true); auto studioModeEnabled = (bool *)param;
obs_frontend_set_preview_program_mode(*studioModeEnabled);
},
&studioModeEnabled, true);
} }
return RequestResult::Success(); return RequestResult::Success();
@ -88,7 +91,7 @@ RequestResult RequestHandler::SetStudioModeEnabled(const Request& request)
* @category ui * @category ui
* @api requests * @api requests
*/ */
RequestResult RequestHandler::OpenInputPropertiesDialog(const Request& request) RequestResult RequestHandler::OpenInputPropertiesDialog(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -113,7 +116,7 @@ RequestResult RequestHandler::OpenInputPropertiesDialog(const Request& request)
* @category ui * @category ui
* @api requests * @api requests
*/ */
RequestResult RequestHandler::OpenInputFiltersDialog(const Request& request) RequestResult RequestHandler::OpenInputFiltersDialog(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -138,7 +141,7 @@ RequestResult RequestHandler::OpenInputFiltersDialog(const Request& request)
* @category ui * @category ui
* @api requests * @api requests
*/ */
RequestResult RequestHandler::OpenInputInteractDialog(const Request& request) RequestResult RequestHandler::OpenInputInteractDialog(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -147,7 +150,8 @@ RequestResult RequestHandler::OpenInputInteractDialog(const Request& request)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_INTERACTION)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_INTERACTION))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support interaction."); return RequestResult::Error(RequestStatus::InvalidResourceState,
"The specified input does not support interaction.");
obs_frontend_open_source_interaction(input); obs_frontend_open_source_interaction(input);
@ -166,19 +170,19 @@ RequestResult RequestHandler::OpenInputInteractDialog(const Request& request)
* @category ui * @category ui
* @api requests * @api requests
*/ */
RequestResult RequestHandler::GetMonitorList(const Request&) RequestResult RequestHandler::GetMonitorList(const Request &)
{ {
json responseData; json responseData;
std::vector<json> monitorsData; std::vector<json> monitorsData;
QList<QScreen *> screensList = QGuiApplication::screens(); QList<QScreen *> screensList = QGuiApplication::screens();
for (int screenIndex = 0; screenIndex < screensList.size(); screenIndex++) for (int screenIndex = 0; screenIndex < screensList.size(); screenIndex++) {
{
json screenData; json screenData;
QScreen const* screen = screensList[screenIndex]; QScreen const *screen = screensList[screenIndex];
std::stringstream nameAndIndex; std::stringstream nameAndIndex;
nameAndIndex << screen->name().toStdString(); nameAndIndex << screen->name().toStdString();
nameAndIndex << '(' << screenIndex << ')'; nameAndIndex << '(' << screenIndex << ')';
screenData["monitorName"] = nameAndIndex.str(); screenData["monitorName"] = nameAndIndex.str();
screenData["monitorIndex"] = screenIndex;
const QRect screenGeometry = screen->geometry(); const QRect screenGeometry = screen->geometry();
screenData["monitorWidth"] = screenGeometry.width(); screenData["monitorWidth"] = screenGeometry.width();
screenData["monitorHeight"] = screenGeometry.height(); screenData["monitorHeight"] = screenGeometry.height();
@ -189,3 +193,112 @@ RequestResult RequestHandler::GetMonitorList(const Request&)
responseData["monitors"] = monitorsData; responseData["monitors"] = monitorsData;
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
/**
* Opens a projector for a specific output video mix.
*
* Mix types:
*
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_PREVIEW`
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_PROGRAM`
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW`
*
* Note: This request serves to provide feature parity with 4.x. It is very likely to be changed/deprecated in a future release.
*
* @requestField videoMixType | String | Type of mix to open
* @requestField ?monitorIndex | Number | Monitor index, use `GetMonitorList` to obtain index | None | -1: Opens projector in windowed mode
* @requestField ?projectorGeometry | String | Size/Position data for a windowed projector, in Qt Base64 encoded format. Mutually exclusive with `monitorIndex` | N/A
*
* @requestType OpenVideoMixProjector
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @category ui
* @api requests
*/
RequestResult RequestHandler::OpenVideoMixProjector(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
if (!request.ValidateString("videoMixType", statusCode, comment))
return RequestResult::Error(statusCode, comment);
std::string videoMixType = request.RequestData["videoMixType"];
const char *projectorType;
if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PREVIEW")
projectorType = "Preview";
else if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PROGRAM")
projectorType = "Program";
else if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW")
projectorType = "Multiview";
else
return RequestResult::Error(RequestStatus::InvalidRequestField,
"The field `videoMixType` has an invalid enum value.");
int monitorIndex = -1;
if (request.Contains("monitorIndex")) {
if (!request.ValidateOptionalNumber("monitorIndex", statusCode, comment, -1, 9))
return RequestResult::Error(statusCode, comment);
monitorIndex = request.RequestData["monitorIndex"];
}
std::string projectorGeometry;
if (request.Contains("projectorGeometry")) {
if (!request.ValidateOptionalString("projectorGeometry", statusCode, comment))
return RequestResult::Error(statusCode, comment);
if (monitorIndex != -1)
return RequestResult::Error(RequestStatus::TooManyRequestFields,
"`monitorIndex` and `projectorGeometry` are mutually exclusive.");
projectorGeometry = request.RequestData["projectorGeometry"];
}
obs_frontend_open_projector(projectorType, monitorIndex, projectorGeometry.c_str(), nullptr);
return RequestResult::Success();
}
/**
* Opens a projector for a source.
*
* Note: This request serves to provide feature parity with 4.x. It is very likely to be changed/deprecated in a future release.
*
* @requestField sourceName | String | Name of the source to open a projector for
* @requestField ?monitorIndex | Number | Monitor index, use `GetMonitorList` to obtain index | None | -1: Opens projector in windowed mode
* @requestField ?projectorGeometry | String | Size/Position data for a windowed projector, in Qt Base64 encoded format. Mutually exclusive with `monitorIndex` | N/A
*
* @requestType OpenSourceProjector
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @category ui
* @api requests
*/
RequestResult RequestHandler::OpenSourceProjector(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
if (!source)
return RequestResult::Error(statusCode, comment);
int monitorIndex = -1;
if (request.Contains("monitorIndex")) {
if (!request.ValidateOptionalNumber("monitorIndex", statusCode, comment, -1, 9))
return RequestResult::Error(statusCode, comment);
monitorIndex = request.RequestData["monitorIndex"];
}
std::string projectorGeometry;
if (request.Contains("projectorGeometry")) {
if (!request.ValidateOptionalString("projectorGeometry", statusCode, comment))
return RequestResult::Error(statusCode, comment);
if (monitorIndex != -1)
return RequestResult::Error(RequestStatus::TooManyRequestFields,
"`monitorIndex` and `projectorGeometry` are mutually exclusive.");
projectorGeometry = request.RequestData["projectorGeometry"];
}
obs_frontend_open_projector("Source", monitorIndex, projectorGeometry.c_str(), obs_source_get_name(source));
return RequestResult::Success();
}

View File

@ -29,11 +29,12 @@ json GetDefaultJsonObject(const json &requestData)
return requestData; return requestData;
} }
Request::Request(const std::string &requestType, const json &requestData, const RequestBatchExecutionType::RequestBatchExecutionType executionType) : Request::Request(const std::string &requestType, const json &requestData,
RequestType(requestType), const RequestBatchExecutionType::RequestBatchExecutionType executionType)
HasRequestData(requestData.is_object()), : RequestType(requestType),
RequestData(GetDefaultJsonObject(requestData)), HasRequestData(requestData.is_object()),
ExecutionType(executionType) RequestData(GetDefaultJsonObject(requestData)),
ExecutionType(executionType)
{ {
} }
@ -59,7 +60,8 @@ bool Request::ValidateBasic(const std::string &keyName, RequestStatus::RequestSt
return true; return true;
} }
bool Request::ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue, const double maxValue) const bool Request::ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const double minValue, const double maxValue) const
{ {
if (!RequestData[keyName].is_number()) { if (!RequestData[keyName].is_number()) {
statusCode = RequestStatus::InvalidRequestFieldType; statusCode = RequestStatus::InvalidRequestFieldType;
@ -70,19 +72,22 @@ bool Request::ValidateOptionalNumber(const std::string &keyName, RequestStatus::
double value = RequestData[keyName]; double value = RequestData[keyName];
if (value < minValue) { if (value < minValue) {
statusCode = RequestStatus::RequestFieldOutOfRange; statusCode = RequestStatus::RequestFieldOutOfRange;
comment = std::string("The field value of `") + keyName + "` is below the minimum of `" + std::to_string(minValue) + "`"; comment = std::string("The field value of `") + keyName + "` is below the minimum of `" + std::to_string(minValue) +
"`";
return false; return false;
} }
if (value > maxValue) { if (value > maxValue) {
statusCode = RequestStatus::RequestFieldOutOfRange; statusCode = RequestStatus::RequestFieldOutOfRange;
comment = std::string("The field value of `") + keyName + "` is above the maximum of `" + std::to_string(maxValue) + "`"; comment = std::string("The field value of `") + keyName + "` is above the maximum of `" + std::to_string(maxValue) +
"`";
return false; return false;
} }
return true; return true;
} }
bool Request::ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue, const double maxValue) const bool Request::ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const double minValue, const double maxValue) const
{ {
if (!ValidateBasic(keyName, statusCode, comment)) if (!ValidateBasic(keyName, statusCode, comment))
return false; return false;
@ -93,7 +98,8 @@ bool Request::ValidateNumber(const std::string &keyName, RequestStatus::RequestS
return true; return true;
} }
bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const bool allowEmpty) const
{ {
if (!RequestData[keyName].is_string()) { if (!RequestData[keyName].is_string()) {
statusCode = RequestStatus::InvalidRequestFieldType; statusCode = RequestStatus::InvalidRequestFieldType;
@ -110,7 +116,8 @@ bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::
return true; return true;
} }
bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const bool allowEmpty) const
{ {
if (!ValidateBasic(keyName, statusCode, comment)) if (!ValidateBasic(keyName, statusCode, comment))
return false; return false;
@ -121,7 +128,8 @@ bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestS
return true; return true;
} }
bool Request::ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const bool Request::ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
std::string &comment) const
{ {
if (!RequestData[keyName].is_boolean()) { if (!RequestData[keyName].is_boolean()) {
statusCode = RequestStatus::InvalidRequestFieldType; statusCode = RequestStatus::InvalidRequestFieldType;
@ -143,7 +151,8 @@ bool Request::ValidateBoolean(const std::string &keyName, RequestStatus::Request
return true; return true;
} }
bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const bool allowEmpty) const
{ {
if (!RequestData[keyName].is_object()) { if (!RequestData[keyName].is_object()) {
statusCode = RequestStatus::InvalidRequestFieldType; statusCode = RequestStatus::InvalidRequestFieldType;
@ -160,7 +169,8 @@ bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::
return true; return true;
} }
bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const bool allowEmpty) const
{ {
if (!ValidateBasic(keyName, statusCode, comment)) if (!ValidateBasic(keyName, statusCode, comment))
return false; return false;
@ -171,7 +181,8 @@ bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestS
return true; return true;
} }
bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const bool allowEmpty) const
{ {
if (!RequestData[keyName].is_array()) { if (!RequestData[keyName].is_array()) {
statusCode = RequestStatus::InvalidRequestFieldType; statusCode = RequestStatus::InvalidRequestFieldType;
@ -188,7 +199,8 @@ bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::R
return true; return true;
} }
bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const bool allowEmpty) const
{ {
if (!ValidateBasic(keyName, statusCode, comment)) if (!ValidateBasic(keyName, statusCode, comment))
return false; return false;
@ -199,7 +211,8 @@ bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestSt
return true; return true;
} }
obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
std::string &comment) const
{ {
if (!ValidateString(keyName, statusCode, comment)) if (!ValidateString(keyName, statusCode, comment))
return nullptr; return nullptr;
@ -216,7 +229,8 @@ obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus:
return ret; return ret;
} }
obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const ObsWebSocketSceneFilter filter) const
{ {
obs_source_t *ret = ValidateSource(keyName, statusCode, comment); obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
if (!ret) if (!ret)
@ -245,7 +259,8 @@ obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::
return ret; return ret;
} }
obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const ObsWebSocketSceneFilter filter) const
{ {
OBSSourceAutoRelease sceneSource = ValidateSource(keyName, statusCode, comment); OBSSourceAutoRelease sceneSource = ValidateSource(keyName, statusCode, comment);
if (!sceneSource) if (!sceneSource)
@ -275,7 +290,8 @@ obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::
} }
} }
obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
std::string &comment) const
{ {
obs_source_t *ret = ValidateSource(keyName, statusCode, comment); obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
if (!ret) if (!ret)
@ -291,7 +307,8 @@ obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::
return ret; return ret;
} }
FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName,
RequestStatus::RequestStatus &statusCode, std::string &comment) const
{ {
obs_source_t *source = ValidateSource(sourceKeyName, statusCode, comment); obs_source_t *source = ValidateSource(sourceKeyName, statusCode, comment);
if (!source) if (!source)
@ -305,14 +322,17 @@ FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::
obs_source_t *filter = obs_source_get_filter_by_name(source, filterName.c_str()); obs_source_t *filter = obs_source_get_filter_by_name(source, filterName.c_str());
if (!filter) { if (!filter) {
statusCode = RequestStatus::ResourceNotFound; statusCode = RequestStatus::ResourceNotFound;
comment = std::string("No filter was found in the source `") + RequestData[sourceKeyName].get<std::string>() + "` with the name `" + filterName + "`."; comment = std::string("No filter was found in the source `") + RequestData[sourceKeyName].get<std::string>() +
"` with the name `" + filterName + "`.";
return FilterPair{source, nullptr}; return FilterPair{source, nullptr};
} }
return FilterPair{source, filter}; return FilterPair{source, filter};
} }
obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName,
RequestStatus::RequestStatus &statusCode, std::string &comment,
const ObsWebSocketSceneFilter filter) const
{ {
OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter); OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter);
if (!scene) if (!scene)
@ -326,10 +346,29 @@ obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, con
OBSSceneItem sceneItem = obs_scene_find_sceneitem_by_id(scene, sceneItemId); OBSSceneItem sceneItem = obs_scene_find_sceneitem_by_id(scene, sceneItemId);
if (!sceneItem) { if (!sceneItem) {
statusCode = RequestStatus::ResourceNotFound; statusCode = RequestStatus::ResourceNotFound;
comment = std::string("No scene items were found in scene `") + RequestData[sceneKeyName].get<std::string>() + "` with the ID `" + std::to_string(sceneItemId) + "`."; comment = std::string("No scene items were found in scene `") + RequestData[sceneKeyName].get<std::string>() +
"` with the ID `" + std::to_string(sceneItemId) + "`.";
return nullptr; return nullptr;
} }
obs_sceneitem_addref(sceneItem); obs_sceneitem_addref(sceneItem);
return sceneItem; return sceneItem;
} }
obs_output_t *Request::ValidateOutput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
std::string &comment) const
{
if (!ValidateString(keyName, statusCode, comment))
return nullptr;
std::string outputName = RequestData[keyName];
obs_output_t *ret = obs_get_output_by_name(outputName.c_str());
if (!ret) {
statusCode = RequestStatus::ResourceNotFound;
comment = std::string("No output was found with the name `") + outputName + "`.";
return nullptr;
}
return ret;
}

View File

@ -35,32 +35,50 @@ struct FilterPair {
OBSSourceAutoRelease filter; OBSSourceAutoRelease filter;
}; };
struct Request struct Request {
{ Request(const std::string &requestType, const json &requestData = nullptr,
Request(const std::string &requestType, const json &requestData = nullptr, const RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::None); const RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::None);
// Contains the key and is not null // Contains the key and is not null
bool Contains(const std::string &keyName) const; bool Contains(const std::string &keyName) const;
bool ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const; bool ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
bool ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const; bool ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
bool ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const; const double minValue = -INFINITY, const double maxValue = INFINITY) const;
bool ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const; bool ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
bool ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const; const double minValue = -INFINITY, const double maxValue = INFINITY) const;
bool ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const; bool ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const bool allowEmpty = false) const;
bool ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const bool allowEmpty = false) const;
bool ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
std::string &comment) const;
bool ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const; bool ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
bool ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const; bool ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
bool ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const; const bool allowEmpty = false) const;
bool ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const; bool ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
bool ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const; const bool allowEmpty = false) const;
bool ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const bool allowEmpty = false) const;
bool ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const bool allowEmpty = false) const;
// All return values have incremented refcounts // All return values have incremented refcounts
obs_source_t *ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const; obs_source_t *ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
obs_source_t *ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const; std::string &comment) const;
obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const; obs_source_t *ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
obs_source_t *ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const; const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
FilterPair ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const; obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const; const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
obs_source_t *ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
std::string &comment) const;
FilterPair ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName,
RequestStatus::RequestStatus &statusCode, std::string &comment) const;
obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName,
RequestStatus::RequestStatus &statusCode, std::string &comment,
const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
obs_output_t *ValidateOutput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
std::string &comment) const;
std::string RequestType; std::string RequestType;
bool HasRequestData; bool HasRequestData;

View File

@ -18,9 +18,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "RequestBatchRequest.h" #include "RequestBatchRequest.h"
RequestBatchRequest::RequestBatchRequest(const std::string &requestType, const json &requestData, RequestBatchExecutionType::RequestBatchExecutionType executionType, const json &inputVariables, const json &outputVariables) : RequestBatchRequest::RequestBatchRequest(const std::string &requestType, const json &requestData,
Request(requestType, requestData, executionType), RequestBatchExecutionType::RequestBatchExecutionType executionType,
InputVariables(inputVariables), const json &inputVariables, const json &outputVariables)
OutputVariables(outputVariables) : Request(requestType, requestData, executionType), InputVariables(inputVariables), OutputVariables(outputVariables)
{ {
} }

View File

@ -21,7 +21,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "Request.h" #include "Request.h"
struct RequestBatchRequest : Request { struct RequestBatchRequest : Request {
RequestBatchRequest(const std::string &requestType, const json &requestData, RequestBatchExecutionType::RequestBatchExecutionType executionType, const json &inputVariables = nullptr, const json &outputVariables = nullptr); RequestBatchRequest(const std::string &requestType, const json &requestData,
RequestBatchExecutionType::RequestBatchExecutionType executionType,
const json &inputVariables = nullptr, const json &outputVariables = nullptr);
json InputVariables; json InputVariables;
json OutputVariables; json OutputVariables;

View File

@ -19,11 +19,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "RequestResult.h" #include "RequestResult.h"
RequestResult::RequestResult(RequestStatus::RequestStatus statusCode, json responseData, std::string comment) : RequestResult::RequestResult(RequestStatus::RequestStatus statusCode, json responseData, std::string comment)
StatusCode(statusCode), : StatusCode(statusCode), ResponseData(responseData), Comment(comment), SleepFrames(0)
ResponseData(responseData),
Comment(comment),
SleepFrames(0)
{ {
} }

View File

@ -22,9 +22,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "../types/RequestStatus.h" #include "../types/RequestStatus.h"
#include "../../utils/Json.h" #include "../../utils/Json.h"
struct RequestResult struct RequestResult {
{ RequestResult(RequestStatus::RequestStatus statusCode = RequestStatus::Success, json responseData = nullptr,
RequestResult(RequestStatus::RequestStatus statusCode = RequestStatus::Success, json responseData = nullptr, std::string comment = ""); std::string comment = "");
static RequestResult Success(json responseData = nullptr); static RequestResult Success(json responseData = nullptr);
static RequestResult Error(RequestStatus::RequestStatus statusCode, std::string comment = ""); static RequestResult Error(RequestStatus::RequestStatus statusCode, std::string comment = "");
RequestStatus::RequestStatus StatusCode; RequestStatus::RequestStatus StatusCode;

View File

@ -22,7 +22,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <stdint.h> #include <stdint.h>
namespace RequestBatchExecutionType { namespace RequestBatchExecutionType {
enum RequestBatchExecutionType: int8_t { enum RequestBatchExecutionType : int8_t {
/** /**
* Not a request batch. * Not a request batch.
* *
@ -77,8 +77,5 @@ namespace RequestBatchExecutionType {
Parallel = 2, Parallel = 2,
}; };
inline bool IsValid(int8_t executionType) inline bool IsValid(int8_t executionType) { return executionType >= None && executionType <= Parallel; }
{
return executionType >= None && executionType <= Parallel;
}
} }

View File

@ -263,7 +263,6 @@ namespace RequestStatus {
*/ */
StudioModeNotActive = 506, StudioModeNotActive = 506,
/** /**
* The resource was not found. * The resource was not found.
* *

View File

@ -19,10 +19,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "Compat.h" #include "Compat.h"
Utils::Compat::StdFunctionRunnable::StdFunctionRunnable(std::function<void()> func) : Utils::Compat::StdFunctionRunnable::StdFunctionRunnable(std::function<void()> func) : cb(std::move(func)) {}
cb(std::move(func))
{
}
void Utils::Compat::StdFunctionRunnable::run() void Utils::Compat::StdFunctionRunnable::run()
{ {

View File

@ -27,9 +27,10 @@ namespace Utils {
// Reimplement QRunnable for std::function. Retrocompatability for Qt < 5.15 // Reimplement QRunnable for std::function. Retrocompatability for Qt < 5.15
class StdFunctionRunnable : public QRunnable { class StdFunctionRunnable : public QRunnable {
std::function<void()> cb; std::function<void()> cb;
public:
StdFunctionRunnable(std::function<void()> func); public:
void run() override; StdFunctionRunnable(std::function<void()> func);
void run() override;
}; };
QRunnable *CreateFunctionRunnable(std::function<void()> func); QRunnable *CreateFunctionRunnable(std::function<void()> func);

View File

@ -63,10 +63,7 @@ bool Utils::Crypto::CheckAuthenticationString(std::string secret, std::string ch
secretAndChallenge += QString::fromStdString(challenge); secretAndChallenge += QString::fromStdString(challenge);
// Generate a SHA256 hash of secretAndChallenge // Generate a SHA256 hash of secretAndChallenge
auto hash = QCryptographicHash::hash( auto hash = QCryptographicHash::hash(secretAndChallenge.toUtf8(), QCryptographicHash::Algorithm::Sha256);
secretAndChallenge.toUtf8(),
QCryptographicHash::Algorithm::Sha256
);
// Encode the SHA256 hash to Base64 // Encode the SHA256 hash to Base64
std::string expectedAuthenticationString = hash.toBase64().toStdString(); std::string expectedAuthenticationString = hash.toBase64().toStdString();

View File

@ -45,7 +45,7 @@ void obs_data_set_json_array(obs_data_t *d, const char *key, json j)
{ {
obs_data_array_t *array = obs_data_array_create(); obs_data_array_t *array = obs_data_array_create();
for (auto& [key, value] : j.items()) { for (auto &[key, value] : j.items()) {
if (!value.is_object()) if (!value.is_object())
continue; continue;
@ -61,7 +61,7 @@ void obs_data_set_json_array(obs_data_t *d, const char *key, json j)
void obs_data_set_json_object_item(obs_data_t *d, json j) void obs_data_set_json_object_item(obs_data_t *d, json j)
{ {
for (auto& [key, value] : j.items()) { for (auto &[key, value] : j.items()) {
if (value.is_object()) { if (value.is_object()) {
obs_data_set_json_object(d, key.c_str(), value); obs_data_set_json_object(d, key.c_str(), value);
} else if (value.is_array()) { } else if (value.is_array()) {
@ -153,23 +153,22 @@ json Utils::Json::ObsDataToJson(obs_data_t *d, bool includeDefault)
continue; continue;
switch (type) { switch (type) {
case OBS_DATA_STRING: case OBS_DATA_STRING:
set_json_string(&j, name, item); set_json_string(&j, name, item);
break; break;
case OBS_DATA_NUMBER: case OBS_DATA_NUMBER:
set_json_number(&j, name, item); set_json_number(&j, name, item);
break; break;
case OBS_DATA_BOOLEAN: case OBS_DATA_BOOLEAN:
set_json_bool(&j, name, item); set_json_bool(&j, name, item);
break; break;
case OBS_DATA_OBJECT: case OBS_DATA_OBJECT:
set_json_object(&j, name, item, includeDefault); set_json_object(&j, name, item, includeDefault);
break; break;
case OBS_DATA_ARRAY: case OBS_DATA_ARRAY:
set_json_array(&j, name, item, includeDefault); set_json_array(&j, name, item, includeDefault);
break; break;
default: default:;
;
} }
} }
@ -184,7 +183,7 @@ bool Utils::Json::GetJsonFileContent(std::string fileName, json &content)
try { try {
content = json::parse(textContent); content = json::parse(textContent);
} catch (json::parse_error& e) { } catch (json::parse_error &e) {
blog(LOG_WARNING, "Failed to decode content of JSON file `%s`. Error: %s", fileName.c_str(), e.what()); blog(LOG_WARNING, "Failed to decode content of JSON file `%s`. Error: %s", fileName.c_str(), e.what());
return false; return false;
} }

View File

@ -25,6 +25,51 @@ with this program. If not, see <https://www.gnu.org/licenses/>
using json = nlohmann::json; using json = nlohmann::json;
NLOHMANN_JSON_SERIALIZE_ENUM(obs_source_type, {
{OBS_SOURCE_TYPE_INPUT, "OBS_SOURCE_TYPE_INPUT"},
{OBS_SOURCE_TYPE_FILTER, "OBS_SOURCE_TYPE_FILTER"},
{OBS_SOURCE_TYPE_TRANSITION, "OBS_SOURCE_TYPE_TRANSITION"},
{OBS_SOURCE_TYPE_SCENE, "OBS_SOURCE_TYPE_SCENE"},
})
NLOHMANN_JSON_SERIALIZE_ENUM(obs_monitoring_type,
{
{OBS_MONITORING_TYPE_NONE, "OBS_MONITORING_TYPE_NONE"},
{OBS_MONITORING_TYPE_MONITOR_ONLY, "OBS_MONITORING_TYPE_MONITOR_ONLY"},
{OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT, "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT"},
})
NLOHMANN_JSON_SERIALIZE_ENUM(obs_media_state, {
{OBS_MEDIA_STATE_NONE, "OBS_MEDIA_STATE_NONE"},
{OBS_MEDIA_STATE_PLAYING, "OBS_MEDIA_STATE_PLAYING"},
{OBS_MEDIA_STATE_OPENING, "OBS_MEDIA_STATE_OPENING"},
{OBS_MEDIA_STATE_BUFFERING, "OBS_MEDIA_STATE_BUFFERING"},
{OBS_MEDIA_STATE_PAUSED, "OBS_MEDIA_STATE_PAUSED"},
{OBS_MEDIA_STATE_STOPPED, "OBS_MEDIA_STATE_STOPPED"},
{OBS_MEDIA_STATE_ENDED, "OBS_MEDIA_STATE_ENDED"},
{OBS_MEDIA_STATE_ERROR, "OBS_MEDIA_STATE_ERROR"},
})
NLOHMANN_JSON_SERIALIZE_ENUM(obs_bounds_type, {
{OBS_BOUNDS_NONE, "OBS_BOUNDS_NONE"},
{OBS_BOUNDS_STRETCH, "OBS_BOUNDS_STRETCH"},
{OBS_BOUNDS_SCALE_INNER, "OBS_BOUNDS_SCALE_INNER"},
{OBS_BOUNDS_SCALE_OUTER, "OBS_BOUNDS_SCALE_OUTER"},
{OBS_BOUNDS_SCALE_TO_WIDTH, "OBS_BOUNDS_SCALE_TO_WIDTH"},
{OBS_BOUNDS_SCALE_TO_HEIGHT, "OBS_BOUNDS_SCALE_TO_HEIGHT"},
{OBS_BOUNDS_MAX_ONLY, "OBS_BOUNDS_MAX_ONLY"},
})
NLOHMANN_JSON_SERIALIZE_ENUM(obs_blending_type, {
{OBS_BLEND_NORMAL, "OBS_BLEND_NORMAL"},
{OBS_BLEND_ADDITIVE, "OBS_BLEND_ADDITIVE"},
{OBS_BLEND_SUBTRACT, "OBS_BLEND_SUBTRACT"},
{OBS_BLEND_SCREEN, "OBS_BLEND_SCREEN"},
{OBS_BLEND_MULTIPLY, "OBS_BLEND_MULTIPLY"},
{OBS_BLEND_LIGHTEN, "OBS_BLEND_LIGHTEN"},
{OBS_BLEND_DARKEN, "OBS_BLEND_DARKEN"},
})
namespace Utils { namespace Utils {
namespace Json { namespace Json {
bool JsonArrayIsValidObsArray(const json &j); bool JsonArrayIsValidObsArray(const json &j);

View File

@ -26,43 +26,44 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "Json.h" #include "Json.h"
// Autorelease object definitions // Autorelease object definitions
inline void ___properties_dummy_addref(obs_properties_t*){} inline void ___properties_dummy_addref(obs_properties_t *) {}
using OBSPropertiesAutoDestroy = OBSRef<obs_properties_t*, ___properties_dummy_addref, obs_properties_destroy>; using OBSPropertiesAutoDestroy = OBSRef<obs_properties_t *, ___properties_dummy_addref, obs_properties_destroy>;
#if !defined(OBS_AUTORELEASE) #if !defined(OBS_AUTORELEASE)
inline void ___source_dummy_addref(obs_source_t*){} inline void ___source_dummy_addref(obs_source_t *) {}
inline void ___scene_dummy_addref(obs_scene_t*){} inline void ___scene_dummy_addref(obs_scene_t *) {}
inline void ___sceneitem_dummy_addref(obs_sceneitem_t*){} inline void ___sceneitem_dummy_addref(obs_sceneitem_t *) {}
inline void ___data_dummy_addref(obs_data_t*){} inline void ___data_dummy_addref(obs_data_t *) {}
inline void ___data_array_dummy_addref(obs_data_array_t*){} inline void ___data_array_dummy_addref(obs_data_array_t *) {}
inline void ___output_dummy_addref(obs_output_t*){} inline void ___output_dummy_addref(obs_output_t *) {}
inline void ___encoder_dummy_addref(obs_encoder_t *){} inline void ___encoder_dummy_addref(obs_encoder_t *) {}
inline void ___service_dummy_addref(obs_service_t *){} inline void ___service_dummy_addref(obs_service_t *) {}
inline void ___weak_source_dummy_addref(obs_weak_source_t*){} inline void ___weak_source_dummy_addref(obs_weak_source_t *) {}
inline void ___weak_output_dummy_addref(obs_weak_output_t *){} inline void ___weak_output_dummy_addref(obs_weak_output_t *) {}
inline void ___weak_encoder_dummy_addref(obs_weak_encoder_t *){} inline void ___weak_encoder_dummy_addref(obs_weak_encoder_t *) {}
inline void ___weak_service_dummy_addref(obs_weak_service_t *){} inline void ___weak_service_dummy_addref(obs_weak_service_t *) {}
using OBSSourceAutoRelease = OBSRef<obs_source_t*, ___source_dummy_addref, obs_source_release>; using OBSSourceAutoRelease = OBSRef<obs_source_t *, ___source_dummy_addref, obs_source_release>;
using OBSSceneAutoRelease = OBSRef<obs_scene_t*, ___scene_dummy_addref, obs_scene_release>; using OBSSceneAutoRelease = OBSRef<obs_scene_t *, ___scene_dummy_addref, obs_scene_release>;
using OBSSceneItemAutoRelease = OBSRef<obs_sceneitem_t*, ___sceneitem_dummy_addref, obs_sceneitem_release>; using OBSSceneItemAutoRelease = OBSRef<obs_sceneitem_t *, ___sceneitem_dummy_addref, obs_sceneitem_release>;
using OBSDataAutoRelease = OBSRef<obs_data_t*, ___data_dummy_addref, obs_data_release>; using OBSDataAutoRelease = OBSRef<obs_data_t *, ___data_dummy_addref, obs_data_release>;
using OBSDataArrayAutoRelease = OBSRef<obs_data_array_t*, ___data_array_dummy_addref, obs_data_array_release>; using OBSDataArrayAutoRelease = OBSRef<obs_data_array_t *, ___data_array_dummy_addref, obs_data_array_release>;
using OBSOutputAutoRelease = OBSRef<obs_output_t*, ___output_dummy_addref, obs_output_release>; using OBSOutputAutoRelease = OBSRef<obs_output_t *, ___output_dummy_addref, obs_output_release>;
using OBSEncoderAutoRelease = OBSRef<obs_encoder_t *, ___encoder_dummy_addref, obs_encoder_release>; using OBSEncoderAutoRelease = OBSRef<obs_encoder_t *, ___encoder_dummy_addref, obs_encoder_release>;
using OBSServiceAutoRelease = OBSRef<obs_service_t *, ___service_dummy_addref, obs_service_release>; using OBSServiceAutoRelease = OBSRef<obs_service_t *, ___service_dummy_addref, obs_service_release>;
using OBSWeakSourceAutoRelease = OBSRef<obs_weak_source_t*, ___weak_source_dummy_addref, obs_weak_source_release>; using OBSWeakSourceAutoRelease = OBSRef<obs_weak_source_t *, ___weak_source_dummy_addref, obs_weak_source_release>;
using OBSWeakOutputAutoRelease = OBSRef<obs_weak_output_t *, ___weak_output_dummy_addref, obs_weak_output_release>; using OBSWeakOutputAutoRelease = OBSRef<obs_weak_output_t *, ___weak_output_dummy_addref, obs_weak_output_release>;
using OBSWeakEncoderAutoRelease = OBSRef<obs_weak_encoder_t *, ___weak_encoder_dummy_addref, obs_weak_encoder_release>; using OBSWeakEncoderAutoRelease = OBSRef<obs_weak_encoder_t *, ___weak_encoder_dummy_addref, obs_weak_encoder_release>;
using OBSWeakServiceAutoRelease = OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref, obs_weak_service_release>; using OBSWeakServiceAutoRelease = OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref, obs_weak_service_release>;
#endif #endif
template <typename T> T* GetCalldataPointer(const calldata_t *data, const char* name) { template<typename T> T *GetCalldataPointer(const calldata_t *data, const char *name)
{
void *ptr = nullptr; void *ptr = nullptr;
calldata_get_ptr(data, name, &ptr); calldata_get_ptr(data, name, &ptr);
return static_cast<T*>(ptr); return static_cast<T *>(ptr);
} }
enum ObsOutputState { enum ObsOutputState {
@ -76,6 +77,16 @@ enum ObsOutputState {
OBS_WEBSOCKET_OUTPUT_RESUMED, OBS_WEBSOCKET_OUTPUT_RESUMED,
}; };
NLOHMANN_JSON_SERIALIZE_ENUM(ObsOutputState, {
{OBS_WEBSOCKET_OUTPUT_UNKNOWN, "OBS_WEBSOCKET_OUTPUT_UNKNOWN"},
{OBS_WEBSOCKET_OUTPUT_STARTING, "OBS_WEBSOCKET_OUTPUT_STARTING"},
{OBS_WEBSOCKET_OUTPUT_STARTED, "OBS_WEBSOCKET_OUTPUT_STARTED"},
{OBS_WEBSOCKET_OUTPUT_STOPPING, "OBS_WEBSOCKET_OUTPUT_STOPPING"},
{OBS_WEBSOCKET_OUTPUT_STOPPED, "OBS_WEBSOCKET_OUTPUT_STOPPED"},
{OBS_WEBSOCKET_OUTPUT_PAUSED, "OBS_WEBSOCKET_OUTPUT_PAUSED"},
{OBS_WEBSOCKET_OUTPUT_RESUMED, "OBS_WEBSOCKET_OUTPUT_RESUMED"},
})
enum ObsMediaInputAction { enum ObsMediaInputAction {
/** /**
* No action. * No action.
@ -149,6 +160,17 @@ enum ObsMediaInputAction {
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS,
}; };
NLOHMANN_JSON_SERIALIZE_ENUM(ObsMediaInputAction,
{
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE"},
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY"},
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE"},
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP"},
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART"},
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT"},
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS"},
})
namespace Utils { namespace Utils {
namespace Obs { namespace Obs {
namespace StringHelper { namespace StringHelper {
@ -157,21 +179,9 @@ namespace Utils {
std::string GetCurrentProfile(); std::string GetCurrentProfile();
std::string GetCurrentProfilePath(); std::string GetCurrentProfilePath();
std::string GetCurrentRecordOutputPath(); std::string GetCurrentRecordOutputPath();
std::string GetSourceType(obs_source_t *source); std::string GetLastRecordFileName();
std::string GetInputMonitorType(enum obs_monitoring_type monitorType); std::string GetLastReplayBufferFileName();
std::string GetInputMonitorType(obs_source_t *input);
std::string GetMediaInputState(obs_source_t *input);
std::string GetLastReplayBufferFilePath();
std::string GetSceneItemBoundsType(enum obs_bounds_type type);
std::string GetSceneItemBlendMode(enum obs_blending_type mode);
std::string DurationToTimecode(uint64_t); std::string DurationToTimecode(uint64_t);
std::string GetOutputState(ObsOutputState state);
}
namespace EnumHelper {
enum obs_bounds_type GetSceneItemBoundsType(std::string boundsType);
enum ObsMediaInputAction GetMediaInputAction(std::string mediaAction);
enum obs_blending_type GetSceneItemBlendMode(std::string mode);
} }
namespace NumberHelper { namespace NumberHelper {
@ -195,6 +205,7 @@ namespace Utils {
std::vector<json> GetSceneTransitionList(); std::vector<json> GetSceneTransitionList();
std::vector<json> GetSourceFilterList(obs_source_t *source); std::vector<json> GetSourceFilterList(obs_source_t *source);
std::vector<std::string> GetFilterKindList(); std::vector<std::string> GetFilterKindList();
std::vector<json> GetOutputList();
} }
namespace ObjectHelper { namespace ObjectHelper {
@ -205,13 +216,21 @@ namespace Utils {
namespace SearchHelper { namespace SearchHelper {
obs_hotkey_t *GetHotkeyByName(std::string name); obs_hotkey_t *GetHotkeyByName(std::string name);
obs_source_t *GetSceneTransitionByName(std::string name); // Increments source ref. Use OBSSourceAutoRelease obs_source_t *GetSceneTransitionByName(std::string name); // Increments source ref. Use OBSSourceAutoRelease
obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name, int offset = 0); // Increments ref. Use OBSSceneItemAutoRelease obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name,
int offset = 0); // Increments ref. Use OBSSceneItemAutoRelease
} }
namespace ActionHelper { namespace ActionHelper {
obs_sceneitem_t *CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled = true, obs_transform_info *sceneItemTransform = nullptr, obs_sceneitem_crop *sceneItemCrop = nullptr); // Increments ref. Use OBSSceneItemAutoRelease obs_sceneitem_t *
obs_sceneitem_t *CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled = true); // Increments ref. Use OBSSceneItemAutoRelease CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled = true,
obs_source_t *CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind, obs_data_t *filterSettings); // Increments source ref. Use OBSSourceAutoRelease obs_transform_info *sceneItemTransform = nullptr,
obs_sceneitem_crop *sceneItemCrop = nullptr); // Increments ref. Use OBSSceneItemAutoRelease
obs_sceneitem_t *CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings,
obs_scene_t *scene,
bool sceneItemEnabled = true); // Increments ref. Use OBSSceneItemAutoRelease
obs_source_t *
CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind,
obs_data_t *filterSettings); // Increments source ref. Use OBSSourceAutoRelease
void SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index); void SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index);
} }
} }

View File

@ -20,16 +20,16 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "../plugin-macros.generated.h" #include "../plugin-macros.generated.h"
struct CreateSceneItemData { struct CreateSceneItemData {
obs_source_t *source; // In obs_source_t *source; // In
bool sceneItemEnabled; // In bool sceneItemEnabled; // In
obs_transform_info *sceneItemTransform = nullptr; // In obs_transform_info *sceneItemTransform = nullptr; // In
obs_sceneitem_crop *sceneItemCrop = nullptr; // In obs_sceneitem_crop *sceneItemCrop = nullptr; // In
OBSSceneItem sceneItem; // Out OBSSceneItem sceneItem; // Out
}; };
void CreateSceneItemHelper(void *_data, obs_scene_t *scene) static void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
{ {
auto *data = static_cast<CreateSceneItemData*>(_data); auto *data = static_cast<CreateSceneItemData *>(_data);
data->sceneItem = obs_scene_add(scene, data->source); data->sceneItem = obs_scene_add(scene, data->source);
if (data->sceneItemTransform) if (data->sceneItemTransform)
@ -41,7 +41,9 @@ void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
obs_sceneitem_set_visible(data->sceneItem, data->sceneItemEnabled); obs_sceneitem_set_visible(data->sceneItem, data->sceneItemEnabled);
} }
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled, obs_transform_info *sceneItemTransform, obs_sceneitem_crop *sceneItemCrop) obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled,
obs_transform_info *sceneItemTransform,
obs_sceneitem_crop *sceneItemCrop)
{ {
// Sanity check for valid scene // Sanity check for valid scene
if (!(source && scene)) if (!(source && scene))
@ -64,7 +66,8 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source,
return data.sceneItem; return data.sceneItem;
} }
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled) obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings,
obs_scene_t *scene, bool sceneItemEnabled)
{ {
// Create the input // Create the input
OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr); OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
@ -88,7 +91,8 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, st
return ret; return ret;
} }
obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind, obs_data_t *filterSettings) obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind,
obs_data_t *filterSettings)
{ {
obs_source_t *filter = obs_source_create_private(filterKind.c_str(), filterName.c_str(), filterSettings); obs_source_t *filter = obs_source_create_private(filterKind.c_str(), filterName.c_str(), filterSettings);
@ -105,7 +109,7 @@ void Utils::Obs::ActionHelper::SetSourceFilterIndex(obs_source_t *source, obs_so
size_t currentIndex = Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter); size_t currentIndex = Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter);
obs_order_movement direction = index > currentIndex ? OBS_ORDER_MOVE_DOWN : OBS_ORDER_MOVE_UP; obs_order_movement direction = index > currentIndex ? OBS_ORDER_MOVE_DOWN : OBS_ORDER_MOVE_UP;
while(currentIndex != index) { while (currentIndex != index) {
obs_source_filter_set_order(source, filter, direction); obs_source_filter_set_order(source, filter, direction);
if (direction == OBS_ORDER_MOVE_DOWN) if (direction == OBS_ORDER_MOVE_DOWN)

View File

@ -29,7 +29,7 @@ static std::vector<std::string> ConvertStringArray(char **array)
return ret; return ret;
size_t index = 0; size_t index = 0;
char* value = nullptr; char *value = nullptr;
do { do {
value = array[index]; value = array[index];
if (value) if (value)
@ -42,7 +42,7 @@ static std::vector<std::string> ConvertStringArray(char **array)
std::vector<std::string> Utils::Obs::ArrayHelper::GetSceneCollectionList() std::vector<std::string> Utils::Obs::ArrayHelper::GetSceneCollectionList()
{ {
char** sceneCollections = obs_frontend_get_scene_collections(); char **sceneCollections = obs_frontend_get_scene_collections();
auto ret = ConvertStringArray(sceneCollections); auto ret = ConvertStringArray(sceneCollections);
bfree(sceneCollections); bfree(sceneCollections);
return ret; return ret;
@ -50,7 +50,7 @@ std::vector<std::string> Utils::Obs::ArrayHelper::GetSceneCollectionList()
std::vector<std::string> Utils::Obs::ArrayHelper::GetProfileList() std::vector<std::string> Utils::Obs::ArrayHelper::GetProfileList()
{ {
char** profiles = obs_frontend_get_profiles(); char **profiles = obs_frontend_get_profiles();
auto ret = ConvertStringArray(profiles); auto ret = ConvertStringArray(profiles);
bfree(profiles); bfree(profiles);
return ret; return ret;
@ -60,13 +60,15 @@ std::vector<obs_hotkey_t *> Utils::Obs::ArrayHelper::GetHotkeyList()
{ {
std::vector<obs_hotkey_t *> ret; std::vector<obs_hotkey_t *> ret;
obs_enum_hotkeys([](void* data, obs_hotkey_id, obs_hotkey_t* hotkey) { auto cb = [](void *data, obs_hotkey_id, obs_hotkey_t *hotkey) {
auto ret = static_cast<std::vector<obs_hotkey_t *> *>(data); auto ret = static_cast<std::vector<obs_hotkey_t *> *>(data);
ret->push_back(hotkey); ret->push_back(hotkey);
return true; return true;
}, &ret); };
obs_enum_hotkeys(cb, &ret);
return ret; return ret;
} }
@ -112,7 +114,7 @@ std::vector<std::string> Utils::Obs::ArrayHelper::GetGroupList()
std::vector<std::string> ret; std::vector<std::string> ret;
auto cb = [](void *priv_data, obs_source_t *scene) { auto cb = [](void *priv_data, obs_source_t *scene) {
auto ret = static_cast<std::vector<std::string>*>(priv_data); auto ret = static_cast<std::vector<std::string> *>(priv_data);
if (!obs_source_is_group(scene)) if (!obs_source_is_group(scene))
return true; return true;
@ -132,17 +134,23 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene,
std::pair<std::vector<json>, bool> enumData; std::pair<std::vector<json>, bool> enumData;
enumData.second = basic; enumData.second = basic;
obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) { auto cb = [](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
auto enumData = static_cast<std::pair<std::vector<json>, bool>*>(param); auto enumData = static_cast<std::pair<std::vector<json>, bool> *>(param);
// TODO: Make ObjectHelper util for scene items
json item; json item;
item["sceneItemId"] = obs_sceneitem_get_id(sceneItem); item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
// Should be slightly faster than calling obs_sceneitem_get_order_position() item["sceneItemIndex"] =
item["sceneItemIndex"] = enumData->first.size(); enumData->first.size(); // Should be slightly faster than calling obs_sceneitem_get_order_position()
if (!enumData->second) { if (!enumData->second) {
item["sceneItemEnabled"] = obs_sceneitem_visible(sceneItem);
item["sceneItemLocked"] = obs_sceneitem_locked(sceneItem);
item["sceneItemTransform"] = ObjectHelper::GetSceneItemTransform(sceneItem);
item["sceneItemBlendMode"] = obs_sceneitem_get_blending_mode(sceneItem);
OBSSource itemSource = obs_sceneitem_get_source(sceneItem); OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
item["sourceName"] = obs_source_get_name(itemSource); item["sourceName"] = obs_source_get_name(itemSource);
item["sourceType"] = StringHelper::GetSourceType(itemSource); item["sourceType"] = obs_source_get_type(itemSource);
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT) if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
item["inputKind"] = obs_source_get_id(itemSource); item["inputKind"] = obs_source_get_id(itemSource);
else else
@ -156,7 +164,9 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene,
enumData->first.push_back(item); enumData->first.push_back(item);
return true; return true;
}, &enumData); };
obs_scene_enum_items(scene, cb, &enumData);
return enumData.first; return enumData.first;
} }
@ -171,12 +181,12 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
EnumInputInfo inputInfo; EnumInputInfo inputInfo;
inputInfo.inputKind = inputKind; inputInfo.inputKind = inputKind;
auto inputEnumProc = [](void *param, obs_source_t *input) { auto cb = [](void *param, obs_source_t *input) {
// Sanity check in case the API changes // Sanity check in case the API changes
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT) if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
return true; return true;
auto inputInfo = static_cast<EnumInputInfo*>(param); auto inputInfo = static_cast<EnumInputInfo *>(param);
std::string inputKind = obs_source_get_id(input); std::string inputKind = obs_source_get_id(input);
@ -191,8 +201,9 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
inputInfo->inputs.push_back(inputJson); inputInfo->inputs.push_back(inputJson);
return true; return true;
}; };
// Actually enumerates only public inputs, despite the name // Actually enumerates only public inputs, despite the name
obs_enum_sources(inputEnumProc, &inputInfo); obs_enum_sources(cb, &inputInfo);
return inputInfo.inputs; return inputInfo.inputs;
} }
@ -287,7 +298,7 @@ std::vector<std::string> Utils::Obs::ArrayHelper::GetFilterKindList()
size_t idx = 0; size_t idx = 0;
const char *kind; const char *kind;
while(obs_enum_filter_types(idx++, &kind)) while (obs_enum_filter_types(idx++, &kind))
ret.push_back(kind); ret.push_back(kind);
return ret; return ret;
@ -297,8 +308,8 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *sou
{ {
std::vector<json> filters; std::vector<json> filters;
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param) { auto cb = [](obs_source_t *, obs_source_t *filter, void *param) {
auto filters = reinterpret_cast<std::vector<json>*>(param); auto filters = reinterpret_cast<std::vector<json> *>(param);
json filterJson; json filterJson;
filterJson["filterEnabled"] = obs_source_enabled(filter); filterJson["filterEnabled"] = obs_source_enabled(filter);
@ -311,7 +322,40 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *sou
filters->push_back(filterJson); filters->push_back(filterJson);
}; };
obs_source_enum_filters(source, enumFilters, &filters);
obs_source_enum_filters(source, cb, &filters);
return filters; return filters;
} }
std::vector<json> Utils::Obs::ArrayHelper::GetOutputList()
{
std::vector<json> outputs;
auto cb = [](void *param, obs_output_t *output) {
auto outputs = reinterpret_cast<std::vector<json> *>(param);
auto rawFlags = obs_output_get_flags(output);
json flags;
flags["OBS_OUTPUT_AUDIO"] = !!(rawFlags & OBS_OUTPUT_AUDIO);
flags["OBS_OUTPUT_VIDEO"] = !!(rawFlags & OBS_OUTPUT_VIDEO);
flags["OBS_OUTPUT_ENCODED"] = !!(rawFlags & OBS_OUTPUT_ENCODED);
flags["OBS_OUTPUT_MULTI_TRACK"] = !!(rawFlags & OBS_OUTPUT_MULTI_TRACK);
flags["OBS_OUTPUT_SERVICE"] = !!(rawFlags & OBS_OUTPUT_SERVICE);
json outputJson;
outputJson["outputName"] = obs_output_get_name(output);
outputJson["outputKind"] = obs_output_get_id(output);
outputJson["outputWidth"] = obs_output_get_width(output);
outputJson["outputHeight"] = obs_output_get_height(output);
outputJson["outputActive"] = obs_output_active(output);
outputJson["outputFlags"] = flags;
outputs->push_back(outputJson);
return true;
};
obs_enum_outputs(cb, &outputs);
return outputs;
}

View File

@ -1,60 +0,0 @@
/*
obs-websocket
Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#include "Obs.h"
#include "../plugin-macros.generated.h"
#define RET_COMPARE(str, x) if (str == #x) return x;
enum obs_bounds_type Utils::Obs::EnumHelper::GetSceneItemBoundsType(std::string boundsType)
{
RET_COMPARE(boundsType, OBS_BOUNDS_NONE)
RET_COMPARE(boundsType, OBS_BOUNDS_STRETCH)
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_INNER)
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_OUTER)
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_WIDTH)
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_HEIGHT)
RET_COMPARE(boundsType, OBS_BOUNDS_MAX_ONLY)
return OBS_BOUNDS_NONE;
}
enum ObsMediaInputAction Utils::Obs::EnumHelper::GetMediaInputAction(std::string mediaAction)
{
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY)
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE)
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP)
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART)
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT)
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS)
return OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE;
}
enum obs_blending_type Utils::Obs::EnumHelper::GetSceneItemBlendMode(std::string mode)
{
RET_COMPARE(mode, OBS_BLEND_NORMAL)
RET_COMPARE(mode, OBS_BLEND_ADDITIVE)
RET_COMPARE(mode, OBS_BLEND_SUBTRACT)
RET_COMPARE(mode, OBS_BLEND_SCREEN)
RET_COMPARE(mode, OBS_BLEND_MULTIPLY)
RET_COMPARE(mode, OBS_BLEND_LIGHTEN)
RET_COMPARE(mode, OBS_BLEND_DARKEN)
return OBS_BLEND_NORMAL;
}

View File

@ -28,7 +28,7 @@ uint64_t Utils::Obs::NumberHelper::GetOutputDuration(obs_output_t *output)
if (!output || !obs_output_active(output)) if (!output || !obs_output_active(output))
return 0; return 0;
video_t* video = obs_output_video(output); video_t *video = obs_output_video(output);
uint64_t frameTimeNs = video_output_get_frame_time(video); uint64_t frameTimeNs = video_output_get_frame_time(video);
int totalFrames = obs_output_get_total_frames(output); int totalFrames = obs_output_get_total_frames(output);
@ -39,7 +39,7 @@ size_t Utils::Obs::NumberHelper::GetSceneCount()
{ {
size_t ret; size_t ret;
auto sceneEnumProc = [](void *param, obs_source_t *scene) { auto sceneEnumProc = [](void *param, obs_source_t *scene) {
auto ret = static_cast<size_t*>(param); auto ret = static_cast<size_t *>(param);
if (obs_source_is_group(scene)) if (obs_source_is_group(scene))
return true; return true;
@ -62,7 +62,7 @@ size_t Utils::Obs::NumberHelper::GetSourceFilterIndex(obs_source_t *source, obs_
}; };
auto search = [](obs_source_t *, obs_source_t *filter, void *priv_data) { auto search = [](obs_source_t *, obs_source_t *filter, void *priv_data) {
auto filterSearch = static_cast<FilterSearch*>(priv_data); auto filterSearch = static_cast<FilterSearch *>(priv_data);
if (filter == filterSearch->filter) if (filter == filterSearch->filter)
filterSearch->found = true; filterSearch->found = true;

View File

@ -29,7 +29,7 @@ json Utils::Obs::ObjectHelper::GetStats()
std::string outputPath = Utils::Obs::StringHelper::GetCurrentRecordOutputPath(); std::string outputPath = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
video_t* video = obs_get_video(); video_t *video = obs_get_video();
ret["cpuUsage"] = os_cpu_usage_info_query(GetCpuUsageInfo()); ret["cpuUsage"] = os_cpu_usage_info_query(GetCpuUsageInfo());
ret["memoryUsage"] = (double)os_get_proc_resident_size() / (1024.0 * 1024.0); ret["memoryUsage"] = (double)os_get_proc_resident_size() / (1024.0 * 1024.0);
@ -54,8 +54,8 @@ json Utils::Obs::ObjectHelper::GetSceneItemTransform(obs_sceneitem_t *item)
obs_sceneitem_get_crop(item, &crop); obs_sceneitem_get_crop(item, &crop);
OBSSource source = obs_sceneitem_get_source(item); OBSSource source = obs_sceneitem_get_source(item);
float sourceWidth = float(obs_source_get_width(source)); float sourceWidth = (float)obs_source_get_width(source);
float sourceHeight = float(obs_source_get_height(source)); float sourceHeight = (float)obs_source_get_height(source);
ret["sourceWidth"] = sourceWidth; ret["sourceWidth"] = sourceWidth;
ret["sourceHeight"] = sourceHeight; ret["sourceHeight"] = sourceHeight;
@ -73,15 +73,15 @@ json Utils::Obs::ObjectHelper::GetSceneItemTransform(obs_sceneitem_t *item)
ret["alignment"] = osi.alignment; ret["alignment"] = osi.alignment;
ret["boundsType"] = StringHelper::GetSceneItemBoundsType(osi.bounds_type); ret["boundsType"] = osi.bounds_type;
ret["boundsAlignment"] = osi.bounds_alignment; ret["boundsAlignment"] = osi.bounds_alignment;
ret["boundsWidth"] = osi.bounds.x; ret["boundsWidth"] = osi.bounds.x;
ret["boundsHeight"] = osi.bounds.y; ret["boundsHeight"] = osi.bounds.y;
ret["cropLeft"] = int(crop.left); ret["cropLeft"] = (int)crop.left;
ret["cropRight"] = int(crop.right); ret["cropRight"] = (int)crop.right;
ret["cropTop"] = int(crop.top); ret["cropTop"] = (int)crop.top;
ret["cropBottom"] = int(crop.bottom); ret["cropBottom"] = (int)crop.bottom;
return ret; return ret;
} }

View File

@ -70,8 +70,8 @@ obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene
enumData.name = name; enumData.name = name;
enumData.offset = offset; enumData.offset = offset;
obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) { auto cb = [](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
auto enumData = static_cast<SceneItemSearchData*>(param); auto enumData = static_cast<SceneItemSearchData *>(param);
OBSSource itemSource = obs_sceneitem_get_source(sceneItem); OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
std::string sourceName = obs_source_get_name(itemSource); std::string sourceName = obs_source_get_name(itemSource);
@ -89,7 +89,9 @@ obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene
} }
return true; return true;
}, &enumData); };
obs_scene_enum_items(scene, cb, &enumData);
return enumData.ret; return enumData.ret;
} }

View File

@ -23,7 +23,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "Obs.h" #include "Obs.h"
#include "../plugin-macros.generated.h" #include "../plugin-macros.generated.h"
#define CASE(x) case x: return #x; #define CASE(x) \
case x: \
return #x;
std::string Utils::Obs::StringHelper::GetObsVersion() std::string Utils::Obs::StringHelper::GetObsVersion()
{ {
@ -70,54 +72,27 @@ std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath()
return ret; return ret;
} }
std::string Utils::Obs::StringHelper::GetSourceType(obs_source_t *source) std::string Utils::Obs::StringHelper::GetLastRecordFileName()
{ {
obs_source_type sourceType = obs_source_get_type(source); OBSOutputAutoRelease output = obs_frontend_get_recording_output();
if (!output)
return "";
switch (sourceType) { OBSDataAutoRelease outputSettings = obs_output_get_settings(output);
default:
CASE(OBS_SOURCE_TYPE_INPUT) obs_data_item_t *item = obs_data_item_byname(outputSettings, "url");
CASE(OBS_SOURCE_TYPE_FILTER) if (!item) {
CASE(OBS_SOURCE_TYPE_TRANSITION) item = obs_data_item_byname(outputSettings, "path");
CASE(OBS_SOURCE_TYPE_SCENE) if (!item)
return "";
} }
std::string ret = obs_data_item_get_string(item);
obs_data_item_release(&item);
return ret;
} }
std::string Utils::Obs::StringHelper::GetInputMonitorType(enum obs_monitoring_type monitorType) std::string Utils::Obs::StringHelper::GetLastReplayBufferFileName()
{
switch (monitorType) {
default:
CASE(OBS_MONITORING_TYPE_NONE)
CASE(OBS_MONITORING_TYPE_MONITOR_ONLY)
CASE(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT)
}
}
std::string Utils::Obs::StringHelper::GetInputMonitorType(obs_source_t *input)
{
obs_monitoring_type monitorType = obs_source_get_monitoring_type(input);
return GetInputMonitorType(monitorType);
}
std::string Utils::Obs::StringHelper::GetMediaInputState(obs_source_t *input)
{
obs_media_state mediaState = obs_source_media_get_state(input);
switch (mediaState) {
default:
CASE(OBS_MEDIA_STATE_NONE)
CASE(OBS_MEDIA_STATE_PLAYING)
CASE(OBS_MEDIA_STATE_OPENING)
CASE(OBS_MEDIA_STATE_BUFFERING)
CASE(OBS_MEDIA_STATE_PAUSED)
CASE(OBS_MEDIA_STATE_STOPPED)
CASE(OBS_MEDIA_STATE_ENDED)
CASE(OBS_MEDIA_STATE_ERROR)
}
}
std::string Utils::Obs::StringHelper::GetLastReplayBufferFilePath()
{ {
OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output(); OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output();
if (!output) if (!output)
@ -135,34 +110,6 @@ std::string Utils::Obs::StringHelper::GetLastReplayBufferFilePath()
return savedReplayPath; return savedReplayPath;
} }
std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
{
switch (type) {
default:
CASE(OBS_BOUNDS_NONE)
CASE(OBS_BOUNDS_STRETCH)
CASE(OBS_BOUNDS_SCALE_INNER)
CASE(OBS_BOUNDS_SCALE_OUTER)
CASE(OBS_BOUNDS_SCALE_TO_WIDTH)
CASE(OBS_BOUNDS_SCALE_TO_HEIGHT)
CASE(OBS_BOUNDS_MAX_ONLY)
}
}
std::string Utils::Obs::StringHelper::GetSceneItemBlendMode(enum obs_blending_type mode)
{
switch (mode) {
default:
CASE(OBS_BLEND_NORMAL)
CASE(OBS_BLEND_ADDITIVE)
CASE(OBS_BLEND_SUBTRACT)
CASE(OBS_BLEND_SCREEN)
CASE(OBS_BLEND_MULTIPLY)
CASE(OBS_BLEND_LIGHTEN)
CASE(OBS_BLEND_DARKEN)
}
}
std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms) std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
{ {
uint64_t secs = ms / 1000ULL; uint64_t secs = ms / 1000ULL;
@ -173,20 +120,7 @@ std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
uint64_t secsPart = secs % 60ULL; uint64_t secsPart = secs % 60ULL;
uint64_t msPart = ms % 1000ULL; uint64_t msPart = ms % 1000ULL;
QString formatted = QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart); QString formatted =
QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
return formatted.toStdString(); return formatted.toStdString();
} }
std::string Utils::Obs::StringHelper::GetOutputState(ObsOutputState state)
{
switch (state) {
default:
CASE(OBS_WEBSOCKET_OUTPUT_UNKNOWN)
CASE(OBS_WEBSOCKET_OUTPUT_STARTING)
CASE(OBS_WEBSOCKET_OUTPUT_STARTED)
CASE(OBS_WEBSOCKET_OUTPUT_STOPPING)
CASE(OBS_WEBSOCKET_OUTPUT_STOPPED)
CASE(OBS_WEBSOCKET_OUTPUT_PAUSED)
CASE(OBS_WEBSOCKET_OUTPUT_RESUMED)
}
}

View File

@ -26,12 +26,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "Obs_VolumeMeter_Helpers.h" #include "Obs_VolumeMeter_Helpers.h"
#include "../obs-websocket.h" #include "../obs-websocket.h"
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) : Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input)
PeakMeterType(SAMPLE_PEAK_METER), : PeakMeterType(SAMPLE_PEAK_METER),
_input(obs_source_get_weak_source(input)), _input(obs_source_get_weak_source(input)),
_channels(0), _channels(0),
_lastUpdate(0), _lastUpdate(0),
_volume(obs_source_get_volume(input)) _volume(obs_source_get_volume(input))
{ {
signal_handler_t *sh = obs_source_get_signal_handler(input); signal_handler_t *sh = obs_source_get_signal_handler(input);
signal_handler_connect(sh, "volume", Meter::InputVolumeCallback, this); signal_handler_connect(sh, "volume", Meter::InputVolumeCallback, this);
@ -45,7 +45,8 @@ Utils::Obs::VolumeMeter::Meter::~Meter()
{ {
OBSSourceAutoRelease input = obs_weak_source_get_source(_input); OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
if (!input) { if (!input) {
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?"); blog(LOG_WARNING,
"[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?");
return; return;
} }
@ -68,7 +69,8 @@ json Utils::Obs::VolumeMeter::Meter::GetMeterData()
OBSSourceAutoRelease input = obs_weak_source_get_source(_input); OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
if (!input) { if (!input) {
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?"); blog(LOG_WARNING,
"[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
return ret; return ret;
} }
@ -129,7 +131,7 @@ void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
int channelNumber = 0; int channelNumber = 0;
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) { for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
float *samples = (float*)data->data[planeNumber]; float *samples = (float *)data->data[planeNumber];
if (!samples) if (!samples)
continue; continue;
@ -143,41 +145,41 @@ void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
float peak; float peak;
switch (PeakMeterType) { switch (PeakMeterType) {
default: default:
case SAMPLE_PEAK_METER: case SAMPLE_PEAK_METER:
peak = GetSamplePeak(previousSamples, samples, sampleCount); peak = GetSamplePeak(previousSamples, samples, sampleCount);
break; break;
case TRUE_PEAK_METER: case TRUE_PEAK_METER:
peak = GetTruePeak(previousSamples, samples, sampleCount); peak = GetTruePeak(previousSamples, samples, sampleCount);
break; break;
} }
switch (sampleCount) { switch (sampleCount) {
case 0: case 0:
break; break;
case 1: case 1:
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][1]; _previousSamples[channelNumber][0] = _previousSamples[channelNumber][1];
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][2]; _previousSamples[channelNumber][1] = _previousSamples[channelNumber][2];
_previousSamples[channelNumber][2] = _previousSamples[channelNumber][3]; _previousSamples[channelNumber][2] = _previousSamples[channelNumber][3];
_previousSamples[channelNumber][3] = samples[sampleCount - 1]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
break; break;
case 2: case 2:
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][2]; _previousSamples[channelNumber][0] = _previousSamples[channelNumber][2];
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][3]; _previousSamples[channelNumber][1] = _previousSamples[channelNumber][3];
_previousSamples[channelNumber][2] = samples[sampleCount - 2]; _previousSamples[channelNumber][2] = samples[sampleCount - 2];
_previousSamples[channelNumber][3] = samples[sampleCount - 1]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
break; break;
case 3: case 3:
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][3]; _previousSamples[channelNumber][0] = _previousSamples[channelNumber][3];
_previousSamples[channelNumber][1] = samples[sampleCount - 3]; _previousSamples[channelNumber][1] = samples[sampleCount - 3];
_previousSamples[channelNumber][2] = samples[sampleCount - 2]; _previousSamples[channelNumber][2] = samples[sampleCount - 2];
_previousSamples[channelNumber][3] = samples[sampleCount - 1]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
break; break;
default: default:
_previousSamples[channelNumber][0] = samples[sampleCount - 4]; _previousSamples[channelNumber][0] = samples[sampleCount - 4];
_previousSamples[channelNumber][1] = samples[sampleCount - 3]; _previousSamples[channelNumber][1] = samples[sampleCount - 3];
_previousSamples[channelNumber][2] = samples[sampleCount - 2]; _previousSamples[channelNumber][2] = samples[sampleCount - 2];
_previousSamples[channelNumber][3] = samples[sampleCount - 1]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
} }
_peak[channelNumber] = peak; _peak[channelNumber] = peak;
@ -196,7 +198,7 @@ void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *d
int channelNumber = 0; int channelNumber = 0;
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) { for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
float *samples = (float*)data->data[planeNumber]; float *samples = (float *)data->data[planeNumber];
if (!samples) if (!samples)
continue; continue;
@ -212,9 +214,10 @@ void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *d
} }
} }
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data, bool muted) void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data,
bool muted)
{ {
auto c = static_cast<Meter*>(priv_data); auto c = static_cast<Meter *>(priv_data);
std::unique_lock<std::mutex> l(c->_mutex); std::unique_lock<std::mutex> l(c->_mutex);
@ -228,22 +231,20 @@ void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data,
void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd) void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd)
{ {
auto c = static_cast<Meter*>(priv_data); auto c = static_cast<Meter *>(priv_data);
c->_volume = (float)calldata_float(cd, "volume"); c->_volume = (float)calldata_float(cd, "volume");
} }
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod) : Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod)
_updateCallback(cb), : _updateCallback(cb), _updatePeriod(updatePeriod), _running(false)
_updatePeriod(updatePeriod),
_running(false)
{ {
signal_handler_t *sh = obs_get_signal_handler(); signal_handler_t *sh = obs_get_signal_handler();
if (!sh) if (!sh)
return; return;
auto enumProc = [](void *priv_data, obs_source_t *input) { auto enumProc = [](void *priv_data, obs_source_t *input) {
auto c = static_cast<Handler*>(priv_data); auto c = static_cast<Handler *>(priv_data);
if (!obs_source_active(input)) if (!obs_source_active(input))
return true; return true;
@ -293,7 +294,7 @@ void Utils::Obs::VolumeMeter::Handler::UpdateThread()
while (_running) { while (_running) {
{ {
std::unique_lock<std::mutex> l(_mutex); std::unique_lock<std::mutex> l(_mutex);
if (_cond.wait_for(l, std::chrono::milliseconds(_updatePeriod), [this]{ return !_running; })) if (_cond.wait_for(l, std::chrono::milliseconds(_updatePeriod), [this] { return !_running; }))
break; break;
} }
@ -313,7 +314,7 @@ void Utils::Obs::VolumeMeter::Handler::UpdateThread()
void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd) void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd)
{ {
auto c = static_cast<Handler*>(priv_data); auto c = static_cast<Handler *>(priv_data);
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source"); obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
if (!input) if (!input)
@ -332,7 +333,7 @@ void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, ca
void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data, calldata_t *cd) void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data, calldata_t *cd)
{ {
auto c = static_cast<Handler*>(priv_data); auto c = static_cast<Handler *>(priv_data);
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source"); obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
if (!input) if (!input)

View File

@ -36,37 +36,38 @@ namespace Utils {
// Some code copied from https://github.com/obsproject/obs-studio/blob/master/libobs/obs-audio-controls.c // Some code copied from https://github.com/obsproject/obs-studio/blob/master/libobs/obs-audio-controls.c
// Keeps a running tally of the current audio levels, for a specific input // Keeps a running tally of the current audio levels, for a specific input
class Meter { class Meter {
public: public:
Meter(obs_source_t *input); Meter(obs_source_t *input);
~Meter(); ~Meter();
bool InputValid(); bool InputValid();
obs_weak_source_t *GetWeakInput() { return _input; } obs_weak_source_t *GetWeakInput() { return _input; }
json GetMeterData(); json GetMeterData();
std::atomic<enum obs_peak_meter_type> PeakMeterType; std::atomic<enum obs_peak_meter_type> PeakMeterType;
private: private:
OBSWeakSourceAutoRelease _input; OBSWeakSourceAutoRelease _input;
// All values in mul // All values in mul
std::mutex _mutex; std::mutex _mutex;
bool _muted; bool _muted;
int _channels; int _channels;
float _magnitude[MAX_AUDIO_CHANNELS]; float _magnitude[MAX_AUDIO_CHANNELS];
float _peak[MAX_AUDIO_CHANNELS]; float _peak[MAX_AUDIO_CHANNELS];
float _previousSamples[MAX_AUDIO_CHANNELS][4]; float _previousSamples[MAX_AUDIO_CHANNELS][4];
std::atomic<uint64_t> _lastUpdate; std::atomic<uint64_t> _lastUpdate;
std::atomic<float> _volume; std::atomic<float> _volume;
void ResetAudioLevels(); void ResetAudioLevels();
void ProcessAudioChannels(const struct audio_data *data); void ProcessAudioChannels(const struct audio_data *data);
void ProcessPeak(const struct audio_data *data); void ProcessPeak(const struct audio_data *data);
void ProcessMagnitude(const struct audio_data *data); void ProcessMagnitude(const struct audio_data *data);
static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source, const struct audio_data *data, bool muted); static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source,
static void InputVolumeCallback(void *priv_data, calldata_t *cd); const struct audio_data *data, bool muted);
static void InputVolumeCallback(void *priv_data, calldata_t *cd);
}; };
// Maintains an array of active inputs // Maintains an array of active inputs
@ -74,25 +75,25 @@ namespace Utils {
typedef std::function<void(std::vector<json>)> UpdateCallback; typedef std::function<void(std::vector<json>)> UpdateCallback;
typedef std::unique_ptr<Meter> MeterPtr; typedef std::unique_ptr<Meter> MeterPtr;
public: public:
Handler(UpdateCallback cb, uint64_t updatePeriod = 50); Handler(UpdateCallback cb, uint64_t updatePeriod = 50);
~Handler(); ~Handler();
private: private:
UpdateCallback _updateCallback; UpdateCallback _updateCallback;
std::mutex _meterMutex; std::mutex _meterMutex;
std::vector<MeterPtr> _meters; std::vector<MeterPtr> _meters;
uint64_t _updatePeriod; uint64_t _updatePeriod;
std::mutex _mutex; std::mutex _mutex;
std::condition_variable _cond; std::condition_variable _cond;
std::atomic<bool> _running; std::atomic<bool> _running;
std::thread _updateThread; std::thread _updateThread;
void UpdateThread(); void UpdateThread();
static void InputActivateCallback(void *priv_data, calldata_t *cd); static void InputActivateCallback(void *priv_data, calldata_t *cd);
static void InputDeactivateCallback(void *priv_data, calldata_t *cd); static void InputDeactivateCallback(void *priv_data, calldata_t *cd);
}; };
} }
} }

View File

@ -22,12 +22,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
// It should probably be imported as a libobs util someday though. // It should probably be imported as a libobs util someday though.
#define SHIFT_RIGHT_2PS(msb, lsb) \ #define SHIFT_RIGHT_2PS(msb, lsb) \
{ \ { \
__m128 tmp = \ __m128 tmp = _mm_shuffle_ps(lsb, msb, _MM_SHUFFLE(0, 0, 3, 3)); \
_mm_shuffle_ps(lsb, msb, _MM_SHUFFLE(0, 0, 3, 3)); \ lsb = _mm_shuffle_ps(lsb, tmp, _MM_SHUFFLE(2, 1, 2, 1)); \
lsb = _mm_shuffle_ps(lsb, tmp, _MM_SHUFFLE(2, 1, 2, 1)); \ msb = _mm_shuffle_ps(msb, msb, _MM_SHUFFLE(3, 3, 2, 1)); \
msb = _mm_shuffle_ps(msb, msb, _MM_SHUFFLE(3, 3, 2, 1)); \
} }
#define abs_ps(v) _mm_andnot_ps(_mm_set1_ps(-0.f), v) #define abs_ps(v) _mm_andnot_ps(_mm_set1_ps(-0.f), v)

View File

@ -53,7 +53,8 @@ std::string Utils::Platform::GetLocalAddress()
std::vector<std::pair<QString, uint8_t>> preferredAddresses; std::vector<std::pair<QString, uint8_t>> preferredAddresses;
for (auto address : validAddresses) { for (auto address : validAddresses) {
// Attribute a priority (0 is best) to the address to choose the best picks // Attribute a priority (0 is best) to the address to choose the best picks
if (address.startsWith("192.168.1.") || address.startsWith("192.168.0.")) { // Prefer common consumer router network prefixes if (address.startsWith("192.168.1.") ||
address.startsWith("192.168.0.")) { // Prefer common consumer router network prefixes
if (address.startsWith("192.168.56.")) if (address.startsWith("192.168.56."))
preferredAddresses.push_back(std::make_pair(address, 255)); // Ignore virtualbox default preferredAddresses.push_back(std::make_pair(address, 255)); // Ignore virtualbox default
else else
@ -68,29 +69,13 @@ std::string Utils::Platform::GetLocalAddress()
} }
// Sort by priority // Sort by priority
std::sort(preferredAddresses.begin(), preferredAddresses.end(), [=](std::pair<QString, uint8_t> a, std::pair<QString, uint8_t> b) { std::sort(preferredAddresses.begin(), preferredAddresses.end(),
return a.second < b.second; [=](std::pair<QString, uint8_t> a, std::pair<QString, uint8_t> b) { return a.second < b.second; });
});
// Return highest priority address // Return highest priority address
return preferredAddresses[0].first.toStdString(); return preferredAddresses[0].first.toStdString();
} }
std::string Utils::Platform::GetLoopbackAddress(bool allowIpv6)
{
std::vector<QString> validAddresses;
for (auto address : QNetworkInterface::allAddresses()) {
if (address == QHostAddress::LocalHost)
return address.toString().toStdString();
else if (address == QHostAddress::LocalHostIPv6 && allowIpv6)
return address.toString().toStdString();
else if (address.isLoopback())
return address.toString().toStdString();
}
return "";
}
QString Utils::Platform::GetCommandLineArgument(QString arg) QString Utils::Platform::GetCommandLineArgument(QString arg)
{ {
QCommandLineParser parser; QCommandLineParser parser;
@ -127,14 +112,17 @@ void Utils::Platform::SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QS
SystemTrayNotification *notification = new SystemTrayNotification{icon, title, body}; SystemTrayNotification *notification = new SystemTrayNotification{icon, title, body};
obs_queue_task(OBS_TASK_UI, [](void* param) { obs_queue_task(
void *systemTrayPtr = obs_frontend_get_system_tray(); OBS_TASK_UI,
auto systemTray = static_cast<QSystemTrayIcon*>(systemTrayPtr); [](void *param) {
void *systemTrayPtr = obs_frontend_get_system_tray();
auto systemTray = static_cast<QSystemTrayIcon *>(systemTrayPtr);
auto notification = static_cast<SystemTrayNotification*>(param); auto notification = static_cast<SystemTrayNotification *>(param);
systemTray->showMessage(notification->title, notification->body, notification->icon); systemTray->showMessage(notification->title, notification->body, notification->icon);
delete notification; delete notification;
}, (void*)notification, false); },
(void *)notification, false);
} }
bool Utils::Platform::GetTextFileContent(std::string fileName, std::string &content) bool Utils::Platform::GetTextFileContent(std::string fileName, std::string &content)

View File

@ -26,7 +26,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
namespace Utils { namespace Utils {
namespace Platform { namespace Platform {
std::string GetLocalAddress(); std::string GetLocalAddress();
std::string GetLoopbackAddress(bool allowIpv6 = true);
QString GetCommandLineArgument(QString arg); QString GetCommandLineArgument(QString arg);
bool GetCommandLineFlagSet(QString arg); bool GetCommandLineFlagSet(QString arg);
void SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QString title, QString body); void SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QString title, QString body);

View File

@ -31,9 +31,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "../utils/Platform.h" #include "../utils/Platform.h"
#include "../utils/Compat.h" #include "../utils/Compat.h"
WebSocketServer::WebSocketServer() : WebSocketServer::WebSocketServer() : QObject(nullptr), _sessions()
QObject(nullptr),
_sessions()
{ {
_server.get_alog().clear_channels(websocketpp::log::alevel::all); _server.get_alog().clear_channels(websocketpp::log::alevel::all);
_server.get_elog().clear_channels(websocketpp::log::elevel::all); _server.get_elog().clear_channels(websocketpp::log::elevel::all);
@ -44,38 +42,17 @@ WebSocketServer::WebSocketServer() :
#endif #endif
_server.set_validate_handler( _server.set_validate_handler(
websocketpp::lib::bind( websocketpp::lib::bind(&WebSocketServer::onValidate, this, websocketpp::lib::placeholders::_1));
&WebSocketServer::onValidate, this, websocketpp::lib::placeholders::_1 _server.set_open_handler(websocketpp::lib::bind(&WebSocketServer::onOpen, this, websocketpp::lib::placeholders::_1));
) _server.set_close_handler(websocketpp::lib::bind(&WebSocketServer::onClose, this, websocketpp::lib::placeholders::_1));
); _server.set_message_handler(websocketpp::lib::bind(&WebSocketServer::onMessage, this, websocketpp::lib::placeholders::_1,
_server.set_open_handler( websocketpp::lib::placeholders::_2));
websocketpp::lib::bind(
&WebSocketServer::onOpen, this, websocketpp::lib::placeholders::_1
)
);
_server.set_close_handler(
websocketpp::lib::bind(
&WebSocketServer::onClose, this, websocketpp::lib::placeholders::_1
)
);
_server.set_message_handler(
websocketpp::lib::bind(
&WebSocketServer::onMessage, this, websocketpp::lib::placeholders::_1, websocketpp::lib::placeholders::_2
)
);
auto eventHandler = GetEventHandler(); auto eventHandler = GetEventHandler();
eventHandler->SetBroadcastCallback( eventHandler->SetBroadcastCallback(std::bind(&WebSocketServer::BroadcastEvent, this, std::placeholders::_1,
std::bind( std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
&WebSocketServer::BroadcastEvent, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4
)
);
eventHandler->SetObsLoadedCallback( eventHandler->SetObsLoadedCallback(std::bind(&WebSocketServer::onObsLoaded, this));
std::bind(
&WebSocketServer::onObsLoaded, this
)
);
} }
WebSocketServer::~WebSocketServer() WebSocketServer::~WebSocketServer()
@ -89,9 +66,9 @@ void WebSocketServer::ServerRunner()
blog(LOG_INFO, "[WebSocketServer::ServerRunner] IO thread started."); blog(LOG_INFO, "[WebSocketServer::ServerRunner] IO thread started.");
try { try {
_server.run(); _server.run();
} catch (websocketpp::exception const & e) { } catch (websocketpp::exception const &e) {
blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s", e.what()); blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s", e.what());
} catch (const std::exception & e) { } catch (const std::exception &e) {
blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s", e.what()); blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s", e.what());
} catch (...) { } catch (...) {
blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error"); blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error");
@ -118,7 +95,8 @@ void WebSocketServer::Start()
// Set log levels if debug is enabled // Set log levels if debug is enabled
if (IsDebugEnabled()) { if (IsDebugEnabled()) {
_server.get_alog().set_channels(websocketpp::log::alevel::all); _server.get_alog().set_channels(websocketpp::log::alevel::all);
_server.get_alog().clear_channels(websocketpp::log::alevel::frame_header | websocketpp::log::alevel::frame_payload | websocketpp::log::alevel::control); _server.get_alog().clear_channels(websocketpp::log::alevel::frame_header | websocketpp::log::alevel::frame_payload |
websocketpp::log::alevel::control);
_server.get_elog().set_channels(websocketpp::log::elevel::all); _server.get_elog().set_channels(websocketpp::log::elevel::all);
_server.get_alog().clear_channels(websocketpp::log::elevel::devel | websocketpp::log::elevel::library); _server.get_alog().clear_channels(websocketpp::log::elevel::devel | websocketpp::log::elevel::library);
} else { } else {
@ -129,25 +107,17 @@ void WebSocketServer::Start()
_server.reset(); _server.reset();
websocketpp::lib::error_code errorCode; websocketpp::lib::error_code errorCode;
if (conf->BindLoopback) { if (conf->Ipv4Only) {
std::string addr = Utils::Platform::GetLoopbackAddress(!conf->Ipv4Only); blog(LOG_INFO, "[WebSocketServer::Start] Locked to IPv4 bindings");
if (addr.empty()) {
blog(LOG_ERROR, "[WebSocketServer::Start] Failed to find loopback interface. Server not started.");
return;
}
_server.listen(addr, std::to_string(conf->ServerPort), errorCode);
blog(LOG_INFO, "[WebSocketServer::Start] Locked to loopback interface.");
} else if (conf->Ipv4Only) {
_server.listen(websocketpp::lib::asio::ip::tcp::v4(), conf->ServerPort, errorCode); _server.listen(websocketpp::lib::asio::ip::tcp::v4(), conf->ServerPort, errorCode);
blog(LOG_INFO, "[WebSocketServer::Start] Locked to IPv4 bindings.");
} else { } else {
blog(LOG_INFO, "[WebSocketServer::Start] Not locked to IPv4 bindings");
_server.listen(conf->ServerPort, errorCode); _server.listen(conf->ServerPort, errorCode);
blog(LOG_INFO, "[WebSocketServer::Start] Bound to all interfaces.");
} }
if (errorCode) { if (errorCode) {
std::string errorCodeMessage = errorCode.message(); std::string errorCodeMessage = errorCode.message();
blog(LOG_ERROR, "[WebSocketServer::Start] Listen failed: %s", errorCodeMessage.c_str()); blog(LOG_INFO, "[WebSocketServer::Start] Listen failed: %s", errorCodeMessage.c_str());
return; return;
} }
@ -155,7 +125,8 @@ void WebSocketServer::Start()
_serverThread = std::thread(&WebSocketServer::ServerRunner, this); _serverThread = std::thread(&WebSocketServer::ServerRunner, this);
blog(LOG_INFO, "[WebSocketServer::Start] Server started successfully on port %d. Possible connect address: %s", conf->ServerPort.load(), Utils::Platform::GetLocalAddress().c_str()); blog(LOG_INFO, "[WebSocketServer::Start] Server started successfully on port %d. Possible connect address: %s",
conf->ServerPort.load(), Utils::Platform::GetLocalAddress().c_str());
} }
void WebSocketServer::Stop() void WebSocketServer::Stop()
@ -168,7 +139,7 @@ void WebSocketServer::Stop()
_server.stop_listening(); _server.stop_listening();
std::unique_lock<std::mutex> lock(_sessionMutex); std::unique_lock<std::mutex> lock(_sessionMutex);
for (auto const& [hdl, session] : _sessions) { for (auto const &[hdl, session] : _sessions) {
websocketpp::lib::error_code errorCode; websocketpp::lib::error_code errorCode;
_server.pause_reading(hdl, errorCode); _server.pause_reading(hdl, errorCode);
if (errorCode) { if (errorCode) {
@ -219,14 +190,15 @@ std::vector<WebSocketServer::WebSocketSessionState> WebSocketServer::GetWebSocke
std::vector<WebSocketServer::WebSocketSessionState> webSocketSessions; std::vector<WebSocketServer::WebSocketSessionState> webSocketSessions;
std::unique_lock<std::mutex> lock(_sessionMutex); std::unique_lock<std::mutex> lock(_sessionMutex);
for (auto & [hdl, session] : _sessions) { for (auto &[hdl, session] : _sessions) {
uint64_t connectedAt = session->ConnectedAt(); uint64_t connectedAt = session->ConnectedAt();
uint64_t incomingMessages = session->IncomingMessages(); uint64_t incomingMessages = session->IncomingMessages();
uint64_t outgoingMessages = session->OutgoingMessages(); uint64_t outgoingMessages = session->OutgoingMessages();
std::string remoteAddress = session->RemoteAddress(); std::string remoteAddress = session->RemoteAddress();
bool isIdentified = session->IsIdentified(); bool isIdentified = session->IsIdentified();
webSocketSessions.emplace_back(WebSocketSessionState{hdl, remoteAddress, connectedAt, incomingMessages, outgoingMessages, isIdentified}); webSocketSessions.emplace_back(
WebSocketSessionState{hdl, remoteAddress, connectedAt, incomingMessages, outgoingMessages, isIdentified});
} }
lock.unlock(); lock.unlock();
@ -372,7 +344,8 @@ void WebSocketServer::onClose(websocketpp::connection_hdl hdl)
emit ClientDisconnected(state, conn->get_local_close_code()); emit ClientDisconnected(state, conn->get_local_close_code());
// Log disconnection // Log disconnection
blog(LOG_INFO, "[WebSocketServer::onClose] WebSocket client %s has disconnected", remoteAddress.c_str()); blog(LOG_INFO, "[WebSocketServer::onClose] WebSocket client `%s` has disconnected with code `%d` and reason: %s",
remoteAddress.c_str(), conn->get_local_close_code(), conn->get_local_close_reason().c_str());
// Get config for tray notification // Get config for tray notification
auto conf = GetConfig(); auto conf = GetConfig();
@ -384,12 +357,14 @@ void WebSocketServer::onClose(websocketpp::connection_hdl hdl)
// If previously identified, not going away, and notifications enabled, send a tray notification // If previously identified, not going away, and notifications enabled, send a tray notification
if (isIdentified && (conn->get_local_close_code() != websocketpp::close::status::going_away) && conf->AlertsEnabled) { if (isIdentified && (conn->get_local_close_code() != websocketpp::close::status::going_away) && conf->AlertsEnabled) {
QString title = obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Title"); QString title = obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Title");
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Body")).arg(QString::fromStdString(remoteAddress)); QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Body"))
.arg(QString::fromStdString(remoteAddress));
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body); Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
} }
} }
void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::server<websocketpp::config::asio>::message_ptr message) void WebSocketServer::onMessage(websocketpp::connection_hdl hdl,
websocketpp::server<websocketpp::config::asio>::message_ptr message)
{ {
auto opCode = message->get_opcode(); auto opCode = message->get_opcode();
std::string payload = message->get_payload(); std::string payload = message->get_payload();
@ -398,7 +373,7 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se
SessionPtr session; SessionPtr session;
try { try {
session = _sessions.at(hdl); session = _sessions.at(hdl);
} catch (const std::out_of_range& oor) { } catch (const std::out_of_range &oor) {
return; return;
} }
lock.unlock(); lock.unlock();
@ -412,26 +387,32 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se
uint8_t sessionEncoding = session->Encoding(); uint8_t sessionEncoding = session->Encoding();
if (sessionEncoding == WebSocketEncoding::Json) { if (sessionEncoding == WebSocketEncoding::Json) {
if (opCode != websocketpp::frame::opcode::text) { if (opCode != websocketpp::frame::opcode::text) {
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, "Your session encoding is set to Json, but a binary message was received.", errorCode); _server.close(hdl, WebSocketCloseCode::MessageDecodeError,
"Your session encoding is set to Json, but a binary message was received.",
errorCode);
return; return;
} }
try { try {
incomingMessage = json::parse(payload); incomingMessage = json::parse(payload);
} catch (json::parse_error& e) { } catch (json::parse_error &e) {
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, std::string("Unable to decode Json: ") + e.what(), errorCode); _server.close(hdl, WebSocketCloseCode::MessageDecodeError,
std::string("Unable to decode Json: ") + e.what(), errorCode);
return; return;
} }
} else if (sessionEncoding == WebSocketEncoding::MsgPack) { } else if (sessionEncoding == WebSocketEncoding::MsgPack) {
if (opCode != websocketpp::frame::opcode::binary) { if (opCode != websocketpp::frame::opcode::binary) {
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, "Your session encoding is set to MsgPack, but a text message was received.", errorCode); _server.close(hdl, WebSocketCloseCode::MessageDecodeError,
"Your session encoding is set to MsgPack, but a text message was received.",
errorCode);
return; return;
} }
try { try {
incomingMessage = json::from_msgpack(payload); incomingMessage = json::from_msgpack(payload);
} catch (json::parse_error& e) { } catch (json::parse_error &e) {
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, std::string("Unable to decode MsgPack: ") + e.what(), errorCode); _server.close(hdl, WebSocketCloseCode::MessageDecodeError,
std::string("Unable to decode MsgPack: ") + e.what(), errorCode);
return; return;
} }
} }
@ -449,9 +430,11 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se
// Disconnect client if 4.x protocol is detected // Disconnect client if 4.x protocol is detected
if (!session->IsIdentified() && incomingMessage.contains("request-type")) { if (!session->IsIdentified() && incomingMessage.contains("request-type")) {
blog(LOG_WARNING, "[WebSocketServer::onMessage] Client %s appears to be running a pre-5.0.0 protocol.", session->RemoteAddress().c_str()); blog(LOG_WARNING, "[WebSocketServer::onMessage] Client %s appears to be running a pre-5.0.0 protocol.",
session->RemoteAddress().c_str());
ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion; ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion;
ret.closeReason = "You appear to be attempting to connect with the pre-5.0.0 plugin protocol. Check to make sure your client is updated."; ret.closeReason =
"You appear to be attempting to connect with the pre-5.0.0 plugin protocol. Check to make sure your client is updated.";
goto skipProcessing; goto skipProcessing;
} }
@ -464,7 +447,7 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se
ProcessMessage(session, ret, incomingMessage["op"], incomingMessage["d"]); ProcessMessage(session, ret, incomingMessage["op"], incomingMessage["d"]);
skipProcessing: skipProcessing:
if (ret.closeCode != WebSocketCloseCode::DontClose) { if (ret.closeCode != WebSocketCloseCode::DontClose) {
websocketpp::lib::error_code errorCode; websocketpp::lib::error_code errorCode;
_server.close(hdl, ret.closeCode, ret.closeReason, errorCode); _server.close(hdl, ret.closeCode, ret.closeReason, errorCode);
@ -486,7 +469,8 @@ skipProcessing:
blog_debug("[WebSocketServer::onMessage] Outgoing message:\n%s", ret.result.dump(2).c_str()); blog_debug("[WebSocketServer::onMessage] Outgoing message:\n%s", ret.result.dump(2).c_str());
if (errorCode) if (errorCode)
blog(LOG_WARNING, "[WebSocketServer::onMessage] Sending message to client failed: %s", errorCode.message().c_str()); blog(LOG_WARNING, "[WebSocketServer::onMessage] Sending message to client failed: %s",
errorCode.message().c_str());
} }
})); }));
} }

View File

@ -34,73 +34,66 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "../requesthandler/rpc/Request.h" #include "../requesthandler/rpc/Request.h"
#include "../plugin-macros.generated.h" #include "../plugin-macros.generated.h"
class WebSocketServer : QObject class WebSocketServer : QObject {
{
Q_OBJECT Q_OBJECT
public: public:
enum WebSocketEncoding { enum WebSocketEncoding { Json, MsgPack };
Json,
MsgPack
};
struct WebSocketSessionState { struct WebSocketSessionState {
websocketpp::connection_hdl hdl; websocketpp::connection_hdl hdl;
std::string remoteAddress; std::string remoteAddress;
uint64_t connectedAt; uint64_t connectedAt;
uint64_t incomingMessages; uint64_t incomingMessages;
uint64_t outgoingMessages; uint64_t outgoingMessages;
bool isIdentified; bool isIdentified;
}; };
WebSocketServer(); WebSocketServer();
~WebSocketServer(); ~WebSocketServer();
void Start(); void Start();
void Stop(); void Stop();
void InvalidateSession(websocketpp::connection_hdl hdl); void InvalidateSession(websocketpp::connection_hdl hdl);
void BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData = nullptr, uint8_t rpcVersion = 0); void BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData = nullptr,
uint8_t rpcVersion = 0);
bool IsListening() { bool IsListening() { return _server.is_listening(); }
return _server.is_listening();
}
std::vector<WebSocketSessionState> GetWebSocketSessions(); std::vector<WebSocketSessionState> GetWebSocketSessions();
QThreadPool *GetThreadPool() { QThreadPool *GetThreadPool() { return &_threadPool; }
return &_threadPool;
}
signals: signals:
void ClientConnected(WebSocketSessionState state); void ClientConnected(WebSocketSessionState state);
void ClientDisconnected(WebSocketSessionState state, uint16_t closeCode); void ClientDisconnected(WebSocketSessionState state, uint16_t closeCode);
private: private:
struct ProcessResult { struct ProcessResult {
WebSocketCloseCode::WebSocketCloseCode closeCode = WebSocketCloseCode::DontClose; WebSocketCloseCode::WebSocketCloseCode closeCode = WebSocketCloseCode::DontClose;
std::string closeReason; std::string closeReason;
json result; json result;
}; };
void ServerRunner(); void ServerRunner();
void onObsLoaded(); void onObsLoaded();
bool onValidate(websocketpp::connection_hdl hdl); bool onValidate(websocketpp::connection_hdl hdl);
void onOpen(websocketpp::connection_hdl hdl); void onOpen(websocketpp::connection_hdl hdl);
void onClose(websocketpp::connection_hdl hdl); void onClose(websocketpp::connection_hdl hdl);
void onMessage(websocketpp::connection_hdl hdl, websocketpp::server<websocketpp::config::asio>::message_ptr message); void onMessage(websocketpp::connection_hdl hdl, websocketpp::server<websocketpp::config::asio>::message_ptr message);
static void SetSessionParameters(SessionPtr session, WebSocketServer::ProcessResult &ret, const json &payloadData); static void SetSessionParameters(SessionPtr session, WebSocketServer::ProcessResult &ret, const json &payloadData);
void ProcessMessage(SessionPtr session, ProcessResult &ret, WebSocketOpCode::WebSocketOpCode opCode, json &payloadData); void ProcessMessage(SessionPtr session, ProcessResult &ret, WebSocketOpCode::WebSocketOpCode opCode, json &payloadData);
QThreadPool _threadPool; QThreadPool _threadPool;
std::thread _serverThread; std::thread _serverThread;
websocketpp::server<websocketpp::config::asio> _server; websocketpp::server<websocketpp::config::asio> _server;
std::string _authenticationSecret; std::string _authenticationSecret;
std::string _authenticationSalt; std::string _authenticationSalt;
std::mutex _sessionMutex; std::mutex _sessionMutex;
std::map<websocketpp::connection_hdl, SessionPtr, std::owner_less<websocketpp::connection_hdl>> _sessions; std::map<websocketpp::connection_hdl, SessionPtr, std::owner_less<websocketpp::connection_hdl>> _sessions;
}; };

View File

@ -44,10 +44,7 @@ static json ConstructRequestResult(RequestResult requestResult, const json &requ
if (requestJson.contains("requestId") && !requestJson["requestId"].is_null()) if (requestJson.contains("requestId") && !requestJson["requestId"].is_null())
ret["requestId"] = requestJson["requestId"]; ret["requestId"] = requestJson["requestId"];
ret["requestStatus"] = { ret["requestStatus"] = {{"result", requestResult.StatusCode == RequestStatus::Success}, {"code", requestResult.StatusCode}};
{"result", requestResult.StatusCode == RequestStatus::Success},
{"code", requestResult.StatusCode}
};
if (!requestResult.Comment.empty()) if (!requestResult.Comment.empty())
ret["requestStatus"]["comment"] = requestResult.Comment; ret["requestStatus"]["comment"] = requestResult.Comment;
@ -70,7 +67,8 @@ void WebSocketServer::SetSessionParameters(SessionPtr session, ProcessResult &re
} }
} }
void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::ProcessResult &ret, WebSocketOpCode::WebSocketOpCode opCode, json &payloadData) void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::ProcessResult &ret,
WebSocketOpCode::WebSocketOpCode opCode, json &payloadData)
{ {
if (!payloadData.is_object()) { if (!payloadData.is_object()) {
if (payloadData.is_null()) { if (payloadData.is_null()) {
@ -91,218 +89,253 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
} }
switch (opCode) { switch (opCode) {
case WebSocketOpCode::Identify: { // Identify case WebSocketOpCode::Identify: { // Identify
std::unique_lock<std::mutex> sessionLock(session->OperationMutex); std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
if (session->IsIdentified()) { if (session->IsIdentified()) {
ret.closeCode = WebSocketCloseCode::AlreadyIdentified; ret.closeCode = WebSocketCloseCode::AlreadyIdentified;
ret.closeReason = "You are already Identified with the obs-websocket server."; ret.closeReason = "You are already Identified with the obs-websocket server.";
return;
}
if (session->AuthenticationRequired()) {
if (!payloadData.contains("authentication")) {
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
ret.closeReason = "Your payload's data is missing an `authentication` string, however authentication is required.";
return;
}
if (!Utils::Crypto::CheckAuthenticationString(session->Secret(), session->Challenge(), payloadData["authentication"])) {
auto conf = GetConfig();
if (conf && conf->AlertsEnabled) {
QString title = obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Title");
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Body")).arg(QString::fromStdString(session->RemoteAddress()));
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Warning, title, body);
}
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
ret.closeReason = "Authentication failed.";
return;
}
}
if (!payloadData.contains("rpcVersion")) {
ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = "Your payload's data is missing an `rpcVersion`.";
return;
}
if (!payloadData["rpcVersion"].is_number_unsigned()) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `rpcVersion` is not an unsigned number.";
}
uint8_t requestedRpcVersion = payloadData["rpcVersion"];
if (!IsSupportedRpcVersion(requestedRpcVersion)) {
ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion;
ret.closeReason = "Your requested RPC version is not supported by this server.";
return;
}
session->SetRpcVersion(requestedRpcVersion);
SetSessionParameters(session, ret, payloadData);
if (ret.closeCode != WebSocketCloseCode::DontClose) {
return;
}
// Increment refs for event subscriptions
auto eventHandler = GetEventHandler();
eventHandler->ProcessSubscription(session->EventSubscriptions());
// Mark session as identified
session->SetIsIdentified(true);
// Send desktop notification. TODO: Move to UI code
auto conf = GetConfig();
if (conf && conf->AlertsEnabled) {
QString title = obs_module_text("OBSWebSocket.TrayNotification.Identified.Title");
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Identified.Body")).arg(QString::fromStdString(session->RemoteAddress()));
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
}
ret.result["op"] = WebSocketOpCode::Identified;
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
} return;
case WebSocketOpCode::Reidentify: { // Reidentify
std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
// Decrement refs for current subscriptions
auto eventHandler = GetEventHandler();
eventHandler->ProcessUnsubscription(session->EventSubscriptions());
SetSessionParameters(session, ret, payloadData);
if (ret.closeCode != WebSocketCloseCode::DontClose) {
return;
}
// Increment refs for new subscriptions
eventHandler->ProcessSubscription(session->EventSubscriptions());
ret.result["op"] = WebSocketOpCode::Identified;
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
} return;
case WebSocketOpCode::Request: { // Request
// RequestID checking has to be done here where we are able to close the connection.
if (!payloadData.contains("requestId")) {
ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = "Your payload data is missing a `requestId`.";
return;
}
RequestHandler requestHandler(session);
Request request(payloadData["requestType"], payloadData["requestData"]);
RequestResult requestResult = requestHandler.ProcessRequest(request);
json resultPayloadData;
resultPayloadData["requestType"] = payloadData["requestType"];
resultPayloadData["requestId"] = payloadData["requestId"];
resultPayloadData["requestStatus"] = {
{"result", requestResult.StatusCode == RequestStatus::Success},
{"code", requestResult.StatusCode}
};
if (!requestResult.Comment.empty())
resultPayloadData["requestStatus"]["comment"] = requestResult.Comment;
if (requestResult.ResponseData.is_object())
resultPayloadData["responseData"] = requestResult.ResponseData;
ret.result["op"] = WebSocketOpCode::RequestResponse;
ret.result["d"] = resultPayloadData;
} return;
case WebSocketOpCode::RequestBatch: { // RequestBatch
// RequestID checking has to be done here where we are able to close the connection.
if (!payloadData.contains("requestId")) {
ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = "Your payload data is missing a `requestId`.";
return;
}
if (!payloadData.contains("requests")) {
ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = "Your payload data is missing a `requests`.";
return;
}
if (!payloadData["requests"].is_array()) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `requests` is not an array.";
return;
}
RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::SerialRealtime;
if (payloadData.contains("executionType") && !payloadData["executionType"].is_null()) {
if (!payloadData["executionType"].is_number_unsigned()) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `executionType` is not a number.";
return;
}
int8_t requestedExecutionType = payloadData["executionType"];
if (!RequestBatchExecutionType::IsValid(requestedExecutionType) || requestedExecutionType == RequestBatchExecutionType::None) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldValue;
ret.closeReason = "Your `executionType` has an invalid value.";
return;
}
// The thread pool must support 2 or more threads else parallel requests will deadlock.
if (requestedExecutionType == RequestBatchExecutionType::Parallel && _threadPool.maxThreadCount() < 2) {
ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
ret.closeReason = "Parallel request batch processing is not available on this system due to limited core count.";
return;
}
executionType = (RequestBatchExecutionType::RequestBatchExecutionType)requestedExecutionType;
}
if (payloadData.contains("variables") && !payloadData["variables"].is_null()) {
if (!payloadData.is_object()) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `variables` is not an object.";
return;
}
if (executionType == RequestBatchExecutionType::Parallel) {
ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
ret.closeReason = "Variables are not supported in Parallel mode.";
return;
}
}
bool haltOnFailure = false;
if (payloadData.contains("haltOnFailure") && !payloadData["haltOnFailure"].is_null()) {
if (!payloadData["haltOnFailure"].is_boolean()) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `haltOnFailure` is not a boolean.";
return;
}
haltOnFailure = payloadData["haltOnFailure"];
}
std::vector<json> requests = payloadData["requests"];
std::vector<RequestBatchRequest> requestsVector;
for (auto &requestJson : requests)
requestsVector.emplace_back(requestJson["requestType"], requestJson["requestData"], executionType, requestJson["inputVariables"], requestJson["outputVariables"]);
auto resultsVector = RequestBatchHandler::ProcessRequestBatch(_threadPool, session, executionType, requestsVector, payloadData["variables"], haltOnFailure);
size_t i = 0;
std::vector<json> results;
for (auto &requestResult : resultsVector) {
results.push_back(ConstructRequestResult(requestResult, requests[i]));
i++;
}
ret.result["op"] = WebSocketOpCode::RequestBatchResponse;
ret.result["d"]["requestId"] = payloadData["requestId"];
ret.result["d"]["results"] = results;
} return;
default:
ret.closeCode = WebSocketCloseCode::UnknownOpCode;
ret.closeReason = std::string("Unknown OpCode: ") + std::to_string(opCode);
return; return;
}
if (session->AuthenticationRequired()) {
if (!payloadData.contains("authentication")) {
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
ret.closeReason =
"Your payload's data is missing an `authentication` string, however authentication is required.";
return;
}
if (!Utils::Crypto::CheckAuthenticationString(session->Secret(), session->Challenge(),
payloadData["authentication"])) {
auto conf = GetConfig();
if (conf && conf->AlertsEnabled) {
QString title = obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Title");
QString body =
QString(obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Body"))
.arg(QString::fromStdString(session->RemoteAddress()));
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Warning, title, body);
}
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
ret.closeReason = "Authentication failed.";
return;
}
}
if (!payloadData.contains("rpcVersion")) {
ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = "Your payload's data is missing an `rpcVersion`.";
return;
}
if (!payloadData["rpcVersion"].is_number_unsigned()) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `rpcVersion` is not an unsigned number.";
return;
}
uint8_t requestedRpcVersion = payloadData["rpcVersion"];
if (!IsSupportedRpcVersion(requestedRpcVersion)) {
ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion;
ret.closeReason = "Your requested RPC version is not supported by this server.";
return;
}
session->SetRpcVersion(requestedRpcVersion);
SetSessionParameters(session, ret, payloadData);
if (ret.closeCode != WebSocketCloseCode::DontClose) {
return;
}
// Increment refs for event subscriptions
auto eventHandler = GetEventHandler();
eventHandler->ProcessSubscription(session->EventSubscriptions());
// Mark session as identified
session->SetIsIdentified(true);
// Send desktop notification. TODO: Move to UI code
auto conf = GetConfig();
if (conf && conf->AlertsEnabled) {
QString title = obs_module_text("OBSWebSocket.TrayNotification.Identified.Title");
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Identified.Body"))
.arg(QString::fromStdString(session->RemoteAddress()));
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
}
ret.result["op"] = WebSocketOpCode::Identified;
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
}
return;
case WebSocketOpCode::Reidentify: { // Reidentify
std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
// Decrement refs for current subscriptions
auto eventHandler = GetEventHandler();
eventHandler->ProcessUnsubscription(session->EventSubscriptions());
SetSessionParameters(session, ret, payloadData);
if (ret.closeCode != WebSocketCloseCode::DontClose) {
return;
}
// Increment refs for new subscriptions
eventHandler->ProcessSubscription(session->EventSubscriptions());
ret.result["op"] = WebSocketOpCode::Identified;
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
}
return;
case WebSocketOpCode::Request: { // Request
// RequestID checking has to be done here where we are able to close the connection.
if (!payloadData.contains("requestId")) {
ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = "Your payload data is missing a `requestId`.";
return;
}
if (!payloadData.contains("requestType")) {
ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = "Your payload's data is missing an `requestType`.";
return;
}
if (!payloadData["requestType"].is_string()) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `requestType` is not a string.";
return;
}
RequestHandler requestHandler(session);
std::string requestType = payloadData["requestType"];
json requestData = payloadData["requestData"];
Request request(requestType, requestData);
RequestResult requestResult = requestHandler.ProcessRequest(request);
json resultPayloadData;
resultPayloadData["requestType"] = requestType;
resultPayloadData["requestId"] = payloadData["requestId"];
resultPayloadData["requestStatus"] = {{"result", requestResult.StatusCode == RequestStatus::Success},
{"code", requestResult.StatusCode}};
if (!requestResult.Comment.empty())
resultPayloadData["requestStatus"]["comment"] = requestResult.Comment;
if (requestResult.ResponseData.is_object())
resultPayloadData["responseData"] = requestResult.ResponseData;
ret.result["op"] = WebSocketOpCode::RequestResponse;
ret.result["d"] = resultPayloadData;
}
return;
case WebSocketOpCode::RequestBatch: { // RequestBatch
// RequestID checking has to be done here where we are able to close the connection.
if (!payloadData.contains("requestId")) {
ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = "Your payload data is missing a `requestId`.";
return;
}
RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::SerialRealtime;
if (payloadData.contains("executionType") && !payloadData["executionType"].is_null()) {
if (!payloadData["executionType"].is_number_unsigned()) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `executionType` is not a number.";
return;
}
int8_t requestedExecutionType = payloadData["executionType"];
if (!RequestBatchExecutionType::IsValid(requestedExecutionType) ||
requestedExecutionType == RequestBatchExecutionType::None) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldValue;
ret.closeReason = "Your `executionType` has an invalid value.";
return;
}
// The thread pool must support 2 or more threads else parallel requests will deadlock.
if (requestedExecutionType == RequestBatchExecutionType::Parallel && _threadPool.maxThreadCount() < 2) {
ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
ret.closeReason =
"Parallel request batch processing is not available on this system due to limited core count.";
return;
}
executionType = (RequestBatchExecutionType::RequestBatchExecutionType)requestedExecutionType;
}
if (payloadData.contains("variables") && !payloadData["variables"].is_null()) {
if (!payloadData.is_object()) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `variables` is not an object.";
return;
}
if (executionType == RequestBatchExecutionType::Parallel) {
ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
ret.closeReason = "Variables are not supported in Parallel mode.";
return;
}
}
bool haltOnFailure = false;
if (payloadData.contains("haltOnFailure") && !payloadData["haltOnFailure"].is_null()) {
if (!payloadData["haltOnFailure"].is_boolean()) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `haltOnFailure` is not a boolean.";
return;
}
haltOnFailure = payloadData["haltOnFailure"];
}
if (!payloadData.contains("requests")) {
ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = "Your payload data is missing a `requests`.";
return;
}
if (!payloadData["requests"].is_array()) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `requests` is not an array.";
return;
}
std::vector<json> requests = payloadData["requests"];
std::vector<RequestBatchRequest> requestsVector;
for (auto &requestJson : requests) {
if (!requestJson["requestType"].is_string())
requestJson["requestType"] =
""; // Workaround for what would otherwise be extensive additional logic for a rare edge case
std::string requestType = requestJson["requestType"];
json requestData = requestJson["requestData"];
json inputVariables = requestJson["inputVariables"];
json outputVariables = requestJson["outputVariables"];
requestsVector.emplace_back(requestType, requestData, executionType, inputVariables, outputVariables);
}
auto resultsVector = RequestBatchHandler::ProcessRequestBatch(_threadPool, session, executionType, requestsVector,
payloadData["variables"], haltOnFailure);
size_t i = 0;
std::vector<json> results;
for (auto &requestResult : resultsVector) {
results.push_back(ConstructRequestResult(requestResult, requests[i]));
i++;
}
ret.result["op"] = WebSocketOpCode::RequestBatchResponse;
ret.result["d"]["requestId"] = payloadData["requestId"];
ret.result["d"]["results"] = results;
}
return;
default:
ret.closeCode = WebSocketCloseCode::UnknownOpCode;
ret.closeReason = std::string("Unknown OpCode: ") + std::to_string(opCode);
return;
} }
} }
// It isn't consistent to directly call the WebSocketServer from the events system, but it would also be dumb to make it unnecessarily complicated. // It isn't consistent to directly call the WebSocketServer from the events system, but it would also be dumb to make it unnecessarily complicated.
void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData, uint8_t rpcVersion) void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData,
uint8_t rpcVersion)
{ {
if (!_server.is_listening()) if (!_server.is_listening())
return; return;
@ -322,7 +355,7 @@ void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string
// Recurse connected sessions and send the event to suitable sessions. // Recurse connected sessions and send the event to suitable sessions.
std::unique_lock<std::mutex> lock(_sessionMutex); std::unique_lock<std::mutex> lock(_sessionMutex);
for (auto & it : _sessions) { for (auto &it : _sessions) {
if (!it.second->IsIdentified()) { if (!it.second->IsIdentified()) {
continue; continue;
} }
@ -332,24 +365,27 @@ void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string
if ((it.second->EventSubscriptions() & requiredIntent) != 0) { if ((it.second->EventSubscriptions() & requiredIntent) != 0) {
websocketpp::lib::error_code errorCode; websocketpp::lib::error_code errorCode;
switch (it.second->Encoding()) { switch (it.second->Encoding()) {
case WebSocketEncoding::Json: case WebSocketEncoding::Json:
if (messageJson.empty()) { if (messageJson.empty()) {
messageJson = eventMessage.dump(); messageJson = eventMessage.dump();
} }
_server.send((websocketpp::connection_hdl)it.first, messageJson, websocketpp::frame::opcode::text, errorCode); _server.send((websocketpp::connection_hdl)it.first, messageJson,
it.second->IncrementOutgoingMessages(); websocketpp::frame::opcode::text, errorCode);
break; it.second->IncrementOutgoingMessages();
case WebSocketEncoding::MsgPack: break;
if (messageMsgPack.empty()) { case WebSocketEncoding::MsgPack:
auto msgPackData = json::to_msgpack(eventMessage); if (messageMsgPack.empty()) {
messageMsgPack = std::string(msgPackData.begin(), msgPackData.end()); auto msgPackData = json::to_msgpack(eventMessage);
} messageMsgPack = std::string(msgPackData.begin(), msgPackData.end());
_server.send((websocketpp::connection_hdl)it.first, messageMsgPack, websocketpp::frame::opcode::binary, errorCode); }
it.second->IncrementOutgoingMessages(); _server.send((websocketpp::connection_hdl)it.first, messageMsgPack,
break; websocketpp::frame::opcode::binary, errorCode);
it.second->IncrementOutgoingMessages();
break;
} }
if (errorCode) if (errorCode)
blog(LOG_ERROR, "[WebSocketServer::BroadcastEvent] Error sending event message: %s", errorCode.message().c_str()); blog(LOG_ERROR, "[WebSocketServer::BroadcastEvent] Error sending event message: %s",
errorCode.message().c_str());
} }
} }
lock.unlock(); lock.unlock();

View File

@ -20,16 +20,16 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "WebSocketSession.h" #include "WebSocketSession.h"
#include "../../eventhandler/types/EventSubscription.h" #include "../../eventhandler/types/EventSubscription.h"
WebSocketSession::WebSocketSession() : WebSocketSession::WebSocketSession()
_remoteAddress(""), : _remoteAddress(""),
_connectedAt(0), _connectedAt(0),
_incomingMessages(0), _incomingMessages(0),
_outgoingMessages(0), _outgoingMessages(0),
_encoding(0), _encoding(0),
_challenge(""), _challenge(""),
_rpcVersion(OBS_WEBSOCKET_RPC_VERSION), _rpcVersion(OBS_WEBSOCKET_RPC_VERSION),
_isIdentified(false), _isIdentified(false),
_eventSubscriptions(EventSubscription::All) _eventSubscriptions(EventSubscription::All)
{ {
} }

View File

@ -29,59 +29,58 @@ with this program. If not, see <https://www.gnu.org/licenses/>
class WebSocketSession; class WebSocketSession;
typedef std::shared_ptr<WebSocketSession> SessionPtr; typedef std::shared_ptr<WebSocketSession> SessionPtr;
class WebSocketSession class WebSocketSession {
{ public:
public: WebSocketSession();
WebSocketSession();
std::string RemoteAddress(); std::string RemoteAddress();
void SetRemoteAddress(std::string address); void SetRemoteAddress(std::string address);
uint64_t ConnectedAt(); uint64_t ConnectedAt();
void SetConnectedAt(uint64_t at); void SetConnectedAt(uint64_t at);
uint64_t IncomingMessages(); uint64_t IncomingMessages();
void IncrementIncomingMessages(); void IncrementIncomingMessages();
uint64_t OutgoingMessages(); uint64_t OutgoingMessages();
void IncrementOutgoingMessages(); void IncrementOutgoingMessages();
uint8_t Encoding(); uint8_t Encoding();
void SetEncoding(uint8_t encoding); void SetEncoding(uint8_t encoding);
bool AuthenticationRequired(); bool AuthenticationRequired();
void SetAuthenticationRequired(bool required); void SetAuthenticationRequired(bool required);
std::string Secret(); std::string Secret();
void SetSecret(std::string secret); void SetSecret(std::string secret);
std::string Challenge(); std::string Challenge();
void SetChallenge(std::string challenge); void SetChallenge(std::string challenge);
uint8_t RpcVersion(); uint8_t RpcVersion();
void SetRpcVersion(uint8_t version); void SetRpcVersion(uint8_t version);
bool IsIdentified(); bool IsIdentified();
void SetIsIdentified(bool identified); void SetIsIdentified(bool identified);
uint64_t EventSubscriptions(); uint64_t EventSubscriptions();
void SetEventSubscriptions(uint64_t subscriptions); void SetEventSubscriptions(uint64_t subscriptions);
std::mutex OperationMutex; std::mutex OperationMutex;
private: private:
std::mutex _remoteAddressMutex; std::mutex _remoteAddressMutex;
std::string _remoteAddress; std::string _remoteAddress;
std::atomic<uint64_t> _connectedAt; std::atomic<uint64_t> _connectedAt;
std::atomic<uint64_t> _incomingMessages; std::atomic<uint64_t> _incomingMessages;
std::atomic<uint64_t> _outgoingMessages; std::atomic<uint64_t> _outgoingMessages;
std::atomic<uint8_t> _encoding; std::atomic<uint8_t> _encoding;
std::atomic<bool> _authenticationRequired; std::atomic<bool> _authenticationRequired;
std::mutex _secretMutex; std::mutex _secretMutex;
std::string _secret; std::string _secret;
std::mutex _challengeMutex; std::mutex _challengeMutex;
std::string _challenge; std::string _challenge;
std::atomic<uint8_t> _rpcVersion; std::atomic<uint8_t> _rpcVersion;
std::atomic<bool> _isIdentified; std::atomic<bool> _isIdentified;
std::atomic<uint64_t> _eventSubscriptions; std::atomic<uint64_t> _eventSubscriptions;
}; };

View File

@ -20,7 +20,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#pragma once #pragma once
namespace WebSocketOpCode { namespace WebSocketOpCode {
enum WebSocketOpCode: uint8_t { enum WebSocketOpCode : uint8_t {
/** /**
* The initial message sent by obs-websocket to newly connected clients. * The initial message sent by obs-websocket to newly connected clients.
* *
@ -122,8 +122,5 @@ namespace WebSocketOpCode {
RequestBatchResponse = 9, RequestBatchResponse = 9,
}; };
inline bool IsValid(uint8_t opCode) inline bool IsValid(uint8_t opCode) { return opCode >= Hello && opCode <= RequestBatchResponse; }
{
return opCode >= Hello && opCode <= RequestBatchResponse;
}
} }