base: Change column limit to 132

A column limit of 80 is very restrictive and code readability was
suffering. A limit of 132 is more realistic for this plugin.
This commit is contained in:
tt2468 2022-06-07 21:35:10 -07:00
parent f73e78582b
commit 6deadc25ac
65 changed files with 1697 additions and 3265 deletions

View File

@ -44,7 +44,7 @@ BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon BreakConstructorInitializers: BeforeColon
BreakStringLiterals: false # apparently unpredictable BreakStringLiterals: false # apparently unpredictable
ColumnLimit: 80 ColumnLimit: 132
CompactNamespaces: false CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8 ConstructorInitializerIndentWidth: 8

View File

@ -34,42 +34,33 @@ bool obs_module_load(void)
return true; return true;
} }
void example_request_cb(obs_data_t *request_data, obs_data_t *response_data, void example_request_cb(obs_data_t *request_data, obs_data_t *response_data, void *priv_data);
void *priv_data);
void obs_module_post_load(void) void obs_module_post_load(void)
{ {
vendor = obs_websocket_register_vendor("api_example_plugin"); vendor = obs_websocket_register_vendor("api_example_plugin");
if (!vendor) { if (!vendor) {
blog(LOG_ERROR, blog(LOG_ERROR, "Vendor registration failed! (obs-websocket should have logged something if installed properly.)");
"Vendor registration failed! (obs-websocket should have logged something if installed properly.)");
return; return;
} }
if (!obs_websocket_vendor_register_request(vendor, "example_request", if (!obs_websocket_vendor_register_request(vendor, "example_request", example_request_cb, NULL))
example_request_cb, NULL)) blog(LOG_ERROR, "Failed to register `example_request` request with obs-websocket.");
blog(LOG_ERROR,
"Failed to register `example_request` request with obs-websocket.");
uint api_version = obs_websocket_get_api_version(); uint api_version = obs_websocket_get_api_version();
if (api_version == 0) { if (api_version == 0) {
blog(LOG_ERROR, blog(LOG_ERROR, "Unable to fetch obs-websocket plugin API version.");
"Unable to fetch obs-websocket plugin API version.");
return; return;
} else if (api_version == 1) { } else if (api_version == 1) {
blog(LOG_WARNING, blog(LOG_WARNING, "Unsupported obs-websocket plugin API version for calling requests.");
"Unsupported obs-websocket plugin API version for calling requests.");
return; return;
} }
struct obs_websocket_request_response *response = struct obs_websocket_request_response *response = obs_websocket_call_request("GetVersion");
obs_websocket_call_request("GetVersion");
if (!response) { if (!response) {
blog(LOG_ERROR, blog(LOG_ERROR, "Failed to call GetVersion due to obs-websocket not being installed.");
"Failed to call GetVersion due to obs-websocket not being installed.");
return; return;
} }
blog(LOG_INFO, blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
"[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
response->status_code, response->comment, response->response_data); response->status_code, response->comment, response->response_data);
obs_websocket_request_response_free(response); obs_websocket_request_response_free(response);
} }
@ -79,8 +70,7 @@ void obs_module_unload(void)
blog(LOG_INFO, "Example obs-websocket-api plugin unloaded!"); blog(LOG_INFO, "Example obs-websocket-api plugin unloaded!");
} }
void example_request_cb(obs_data_t *request_data, obs_data_t *response_data, void example_request_cb(obs_data_t *request_data, obs_data_t *response_data, void *priv_data)
void *priv_data)
{ {
if (obs_data_has_user_value(request_data, "ping")) if (obs_data_has_user_value(request_data, "ping"))
obs_data_set_bool(response_data, "pong", true); obs_data_set_bool(response_data, "pong", true);

View File

@ -1,234 +1,216 @@
/* /*
obs-websocket obs-websocket
Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com> Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com>
Copyright (C) 2020-2022 Kyle Manning <tt2468@gmail.com> Copyright (C) 2020-2022 Kyle Manning <tt2468@gmail.com>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License along You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/> with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#ifndef _OBS_WEBSOCKET_API_H #ifndef _OBS_WEBSOCKET_API_H
#define _OBS_WEBSOCKET_API_H #define _OBS_WEBSOCKET_API_H
#include <obs.h> #include <obs.h>
#define OBS_WEBSOCKET_API_VERSION 2 #define OBS_WEBSOCKET_API_VERSION 2
#ifdef __cplusplus #ifdef __cplusplus
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 *, typedef void (*obs_websocket_request_callback_function)(obs_data_t *, obs_data_t *, void *);
obs_data_t *, void *);
struct obs_websocket_request_response {
struct obs_websocket_request_response { unsigned int status_code;
unsigned int status_code; char *comment;
char *comment; char *response_data; // JSON string, because obs_data_t* only supports array<object>, so conversions would break API.
char *response_data; // JSON string, because obs_data_t* only supports array<object>, so conversions would break API. };
};
/* ==================== INTERNAL DEFINITIONS ==================== */
/* ==================== INTERNAL DEFINITIONS ==================== */
struct obs_websocket_request_callback {
struct obs_websocket_request_callback { obs_websocket_request_callback_function callback;
obs_websocket_request_callback_function callback; void *priv_data;
void *priv_data; };
};
inline proc_handler_t *_ph;
inline proc_handler_t *_ph;
/* ==================== INTERNAL API FUNCTIONS ==================== */
/* ==================== INTERNAL API FUNCTIONS ==================== */
static inline proc_handler_t *obs_websocket_get_ph(void)
static inline proc_handler_t *obs_websocket_get_ph(void) {
{ proc_handler_t *global_ph = obs_get_proc_handler();
proc_handler_t *global_ph = obs_get_proc_handler(); assert(global_ph != NULL);
assert(global_ph != NULL);
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, proc_handler_t *ret = (proc_handler_t *)calldata_ptr(&cd, "ph");
"Unable to fetch obs-websocket proc handler object. obs-websocket not installed?"); calldata_free(&cd);
proc_handler_t *ret = (proc_handler_t *)calldata_ptr(&cd, "ph");
calldata_free(&cd); return ret;
}
return ret;
} static inline bool obs_websocket_ensure_ph(void)
{
static inline bool obs_websocket_ensure_ph(void) if (!_ph)
{ _ph = obs_websocket_get_ph();
if (!_ph) return _ph != NULL;
_ph = obs_websocket_get_ph(); }
return _ph != NULL;
} static inline bool obs_websocket_vendor_run_simple_proc(obs_websocket_vendor vendor, const char *proc_name, calldata_t *cd)
{
static inline bool if (!obs_websocket_ensure_ph())
obs_websocket_vendor_run_simple_proc(obs_websocket_vendor vendor, return false;
const char *proc_name, calldata_t *cd)
{ if (!vendor || !proc_name || !strlen(proc_name) || !cd)
if (!obs_websocket_ensure_ph()) return false;
return false;
calldata_set_ptr(cd, "vendor", vendor);
if (!vendor || !proc_name || !strlen(proc_name) || !cd)
return false; proc_handler_call(_ph, proc_name, cd);
return calldata_bool(cd, "success");
calldata_set_ptr(cd, "vendor", vendor); }
proc_handler_call(_ph, proc_name, cd); /* ==================== GENERAL API FUNCTIONS ==================== */
return calldata_bool(cd, "success");
} // Gets the API version built with the obs-websocket plugin
static inline unsigned int obs_websocket_get_api_version(void)
/* ==================== GENERAL API FUNCTIONS ==================== */ {
if (!obs_websocket_ensure_ph())
// Gets the API version built with the obs-websocket plugin return 0;
static inline unsigned int obs_websocket_get_api_version(void)
{ calldata_t cd = {0};
if (!obs_websocket_ensure_ph())
return 0; if (!proc_handler_call(_ph, "get_api_version", &cd))
return 1; // API v1 does not include get_api_version
calldata_t cd = {0};
unsigned int ret = calldata_int(&cd, "version");
if (!proc_handler_call(_ph, "get_api_version", &cd))
return 1; // API v1 does not include get_api_version calldata_free(&cd);
unsigned int ret = calldata_int(&cd, "version"); return ret;
}
calldata_free(&cd);
// Calls an obs-websocket request. Free response with `obs_websocket_request_response_free()`
return ret; static inline obs_websocket_request_response *obs_websocket_call_request(const char *request_type, obs_data_t *request_data = NULL)
} {
if (!obs_websocket_ensure_ph())
// Calls an obs-websocket request. Free response with `obs_websocket_request_response_free()` return NULL;
static inline obs_websocket_request_response *
obs_websocket_call_request(const char *request_type, const char *request_data_string = NULL;
obs_data_t *request_data = NULL) if (request_data)
{ request_data_string = obs_data_get_json(request_data);
if (!obs_websocket_ensure_ph())
return NULL; calldata_t cd = {0};
const char *request_data_string = NULL; calldata_set_string(&cd, "request_type", request_type);
if (request_data) calldata_set_string(&cd, "request_data", request_data_string);
request_data_string = obs_data_get_json(request_data);
proc_handler_call(_ph, "call_request", &cd);
calldata_t cd = {0};
auto ret = (struct obs_websocket_request_response *)calldata_ptr(&cd, "response");
calldata_set_string(&cd, "request_type", request_type);
calldata_set_string(&cd, "request_data", request_data_string); calldata_free(&cd);
proc_handler_call(_ph, "call_request", &cd); return ret;
}
auto ret = (struct obs_websocket_request_response *)calldata_ptr(
&cd, "response"); // Free a request response object returned by `obs_websocket_call_request()`
static inline void obs_websocket_request_response_free(struct obs_websocket_request_response *response)
calldata_free(&cd); {
if (!response)
return ret; return;
}
if (response->comment)
// Free a request response object returned by `obs_websocket_call_request()` bfree(response->comment);
static inline void obs_websocket_request_response_free( if (response->response_data)
struct obs_websocket_request_response *response) bfree(response->response_data);
{ bfree(response);
if (!response) }
return;
/* ==================== VENDOR API FUNCTIONS ==================== */
if (response->comment)
bfree(response->comment); // ALWAYS CALL ONLY VIA `obs_module_post_load()` CALLBACK!
if (response->response_data) // Registers a new "vendor" (Example: obs-ndi)
bfree(response->response_data); static inline obs_websocket_vendor obs_websocket_register_vendor(const char *vendor_name)
bfree(response); {
} if (!obs_websocket_ensure_ph())
return NULL;
/* ==================== VENDOR API FUNCTIONS ==================== */
calldata_t cd = {0};
// ALWAYS CALL ONLY VIA `obs_module_post_load()` CALLBACK!
// Registers a new "vendor" (Example: obs-ndi) calldata_set_string(&cd, "name", vendor_name);
static inline obs_websocket_vendor
obs_websocket_register_vendor(const char *vendor_name) proc_handler_call(_ph, "vendor_register", &cd);
{ obs_websocket_vendor ret = calldata_ptr(&cd, "vendor");
if (!obs_websocket_ensure_ph()) calldata_free(&cd);
return NULL;
return ret;
calldata_t cd = {0}; }
calldata_set_string(&cd, "name", vendor_name); // Registers a new request for a vendor
static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor vendor, const char *request_type,
proc_handler_call(_ph, "vendor_register", &cd); obs_websocket_request_callback_function request_callback, void *priv_data)
obs_websocket_vendor ret = calldata_ptr(&cd, "vendor"); {
calldata_free(&cd); calldata_t cd = {0};
return ret; struct obs_websocket_request_callback cb = {};
} cb.callback = request_callback;
cb.priv_data = priv_data;
// Registers a new request for a vendor
static inline bool obs_websocket_vendor_register_request( calldata_set_string(&cd, "type", request_type);
obs_websocket_vendor vendor, const char *request_type, calldata_set_ptr(&cd, "callback", &cb);
obs_websocket_request_callback_function request_callback,
void *priv_data) bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_request_register", &cd);
{ calldata_free(&cd);
calldata_t cd = {0};
return success;
struct obs_websocket_request_callback cb = {}; }
cb.callback = request_callback;
cb.priv_data = priv_data; // Unregisters an existing vendor request
static inline bool obs_websocket_vendor_unregister_request(obs_websocket_vendor vendor, const char *request_type)
calldata_set_string(&cd, "type", request_type); {
calldata_set_ptr(&cd, "callback", &cb); calldata_t cd = {0};
bool success = obs_websocket_vendor_run_simple_proc( calldata_set_string(&cd, "type", request_type);
vendor, "vendor_request_register", &cd);
calldata_free(&cd); bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_request_unregister", &cd);
calldata_free(&cd);
return success;
} return success;
}
// Unregisters an existing vendor request
static inline bool // Does not affect event_data refcount.
obs_websocket_vendor_unregister_request(obs_websocket_vendor vendor, // Emits an event under the vendor's name
const char *request_type) static inline bool obs_websocket_vendor_emit_event(obs_websocket_vendor vendor, const char *event_name, obs_data_t *event_data)
{ {
calldata_t cd = {0}; calldata_t cd = {0};
calldata_set_string(&cd, "type", request_type); calldata_set_string(&cd, "type", event_name);
calldata_set_ptr(&cd, "data", (void *)event_data);
bool success = obs_websocket_vendor_run_simple_proc(
vendor, "vendor_request_unregister", &cd); bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_event_emit", &cd);
calldata_free(&cd); calldata_free(&cd);
return success; return success;
} }
// Does not affect event_data refcount. /* ==================== END API FUNCTIONS ==================== */
// Emits an event under the vendor's name
static inline bool obs_websocket_vendor_emit_event(obs_websocket_vendor vendor, #ifdef __cplusplus
const char *event_name, }
obs_data_t *event_data) #endif
{
calldata_t cd = {0}; #endif
calldata_set_string(&cd, "type", event_name);
calldata_set_ptr(&cd, "data", (void *)event_data);
bool success = obs_websocket_vendor_run_simple_proc(
vendor, "vendor_event_emit", &cd);
calldata_free(&cd);
return success;
}
/* ==================== END API FUNCTIONS ==================== */
#ifdef __cplusplus
}
#endif
#endif

View File

@ -62,20 +62,13 @@ void Config::Load()
return; return;
} }
FirstLoad = config_get_bool(obsConfig, CONFIG_SECTION_NAME, FirstLoad = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD);
PARAM_FIRSTLOAD); ServerEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED);
ServerEnabled = AlertsEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS);
config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED); ServerPort = config_get_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT);
AlertsEnabled = BindLoopback = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK);
config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS); AuthRequired = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED);
ServerPort = ServerPassword = config_get_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD);
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);
ServerPassword = config_get_string(obsConfig, CONFIG_SECTION_NAME,
PARAM_PASSWORD);
// Set server password and save it to the config before processing overrides, // Set server password and save it to the config before processing overrides,
// so that there is always a true configured password regardless of if // so that there is always a true configured password regardless of if
@ -83,49 +76,38 @@ void Config::Load()
if (FirstLoad) { if (FirstLoad) {
FirstLoad = false; FirstLoad = false;
if (ServerPassword.isEmpty()) { if (ServerPassword.isEmpty()) {
blog(LOG_INFO, blog(LOG_INFO, "[Config::Load] (FirstLoad) Generating new server password.");
"[Config::Load] (FirstLoad) Generating new server password."); ServerPassword = QString::fromStdString(Utils::Crypto::GeneratePassword());
ServerPassword = QString::fromStdString(
Utils::Crypto::GeneratePassword());
} else { } else {
blog(LOG_INFO, blog(LOG_INFO, "[Config::Load] (FirstLoad) Not generating new password since one is already configured.");
"[Config::Load] (FirstLoad) Not generating new password since one is already configured.");
} }
Save(); Save();
} }
// Process `--websocket_port` override // Process `--websocket_port` override
QString portArgument = QString portArgument = Utils::Platform::GetCommandLineArgument(CMDLINE_WEBSOCKET_PORT);
Utils::Platform::GetCommandLineArgument(CMDLINE_WEBSOCKET_PORT);
if (portArgument != "") { if (portArgument != "") {
bool ok; bool ok;
uint16_t serverPort = portArgument.toUShort(&ok); uint16_t serverPort = portArgument.toUShort(&ok);
if (ok) { if (ok) {
blog(LOG_INFO, blog(LOG_INFO, "[Config::Load] --websocket_port passed. Overriding WebSocket port with: %d", serverPort);
"[Config::Load] --websocket_port passed. Overriding WebSocket port with: %d",
serverPort);
PortOverridden = true; PortOverridden = true;
ServerPort = serverPort; ServerPort = serverPort;
} else { } else {
blog(LOG_WARNING, blog(LOG_WARNING, "[Config::Load] Not overriding WebSocket port since integer conversion failed.");
"[Config::Load] Not overriding WebSocket port since integer conversion failed.");
} }
} }
// Process `--websocket_ipv4_only` override // Process `--websocket_ipv4_only` override
if (Utils::Platform::GetCommandLineFlagSet( if (Utils::Platform::GetCommandLineFlagSet(CMDLINE_WEBSOCKET_IPV4_ONLY)) {
CMDLINE_WEBSOCKET_IPV4_ONLY)) { blog(LOG_INFO, "[Config::Load] --websocket_ipv4_only passed. Binding only to IPv4 interfaces.");
blog(LOG_INFO,
"[Config::Load] --websocket_ipv4_only passed. Binding only to IPv4 interfaces.");
Ipv4Only = true; Ipv4Only = true;
} }
// Process `--websocket_password` override // Process `--websocket_password` override
QString passwordArgument = Utils::Platform::GetCommandLineArgument( QString passwordArgument = Utils::Platform::GetCommandLineArgument(CMDLINE_WEBSOCKET_PASSWORD);
CMDLINE_WEBSOCKET_PASSWORD);
if (passwordArgument != "") { if (passwordArgument != "") {
blog(LOG_INFO, blog(LOG_INFO, "[Config::Load] --websocket_password passed. Overriding WebSocket password.");
"[Config::Load] --websocket_password passed. Overriding WebSocket password.");
PasswordOverridden = true; PasswordOverridden = true;
AuthRequired = true; AuthRequired = true;
ServerPassword = passwordArgument; ServerPassword = passwordArgument;
@ -134,8 +116,7 @@ void Config::Load()
// Process `--websocket_debug` override // Process `--websocket_debug` override
if (Utils::Platform::GetCommandLineFlagSet(CMDLINE_WEBSOCKET_DEBUG)) { if (Utils::Platform::GetCommandLineFlagSet(CMDLINE_WEBSOCKET_DEBUG)) {
// Debug does not persist on reload, so we let people override it with a flag. // Debug does not persist on reload, so we let people override it with a flag.
blog(LOG_INFO, blog(LOG_INFO, "[Config::Load] --websocket_debug passed. Enabling debug logging.");
"[Config::Load] --websocket_debug passed. Enabling debug logging.");
DebugEnabled = true; DebugEnabled = true;
} }
} }
@ -148,23 +129,16 @@ void Config::Save()
return; return;
} }
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, FirstLoad);
FirstLoad); config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, ServerEnabled);
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED,
ServerEnabled);
if (!PortOverridden) { if (!PortOverridden) {
config_set_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, config_set_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
ServerPort);
} }
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK, config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK, 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, config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
PARAM_AUTHREQUIRED, AuthRequired); config_set_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
config_set_string(obsConfig, CONFIG_SECTION_NAME,
PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
} }
config_save(obsConfig); config_save(obsConfig);
@ -174,25 +148,17 @@ void Config::SetDefaultsToGlobalStore()
{ {
config_t *obsConfig = GetConfigStore(); config_t *obsConfig = GetConfigStore();
if (!obsConfig) { if (!obsConfig) {
blog(LOG_ERROR, blog(LOG_ERROR, "[Config::SetDefaultsToGlobalStore] Unable to fetch OBS config!");
"[Config::SetDefaultsToGlobalStore] Unable to fetch OBS config!");
return; return;
} }
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, FirstLoad);
FirstLoad); config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, ServerEnabled);
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, config_set_default_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
ServerEnabled); config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK, BindLoopback);
config_set_default_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
ServerPort); config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, config_set_default_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
PARAM_BINDLOOPBACK, BindLoopback);
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_string(obsConfig, CONFIG_SECTION_NAME,
PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
} }
config_t *Config::GetConfigStore() config_t *Config::GetConfigStore()

View File

@ -15,8 +15,7 @@ WebSocketApi::Vendor *get_vendor(calldata_t *cd)
{ {
void *voidVendor; void *voidVendor;
if (!calldata_get_ptr(cd, "vendor", &voidVendor)) { if (!calldata_get_ptr(cd, "vendor", &voidVendor)) {
blog(LOG_WARNING, blog(LOG_WARNING, "[WebSocketApi: get_vendor] Failed due to missing `vendor` pointer.");
"[WebSocketApi: get_vendor] Failed due to missing `vendor` pointer.");
return nullptr; return nullptr;
} }
@ -29,33 +28,21 @@ WebSocketApi::WebSocketApi()
_procHandler = proc_handler_create(); _procHandler = proc_handler_create();
proc_handler_add(_procHandler, "bool get_api_version(out int version)", proc_handler_add(_procHandler, "bool get_api_version(out int version)", &get_api_version, nullptr);
&get_api_version, nullptr); proc_handler_add(_procHandler, "bool call_request(in string request_type, in string request_data, out ptr response)",
proc_handler_add( &call_request, nullptr);
_procHandler, proc_handler_add(_procHandler, "bool vendor_register(in string name, out ptr vendor)", &vendor_register_cb, this);
"bool call_request(in string request_type, in string request_data, out ptr response)", proc_handler_add(_procHandler, "bool vendor_request_register(in ptr vendor, in string type, in ptr callback)",
&call_request, nullptr); &vendor_request_register_cb, this);
proc_handler_add(_procHandler, proc_handler_add(_procHandler, "bool vendor_request_unregister(in ptr vendor, in string type)",
"bool vendor_register(in string name, out ptr vendor)", &vendor_request_unregister_cb, this);
&vendor_register_cb, this); proc_handler_add(_procHandler, "bool vendor_event_emit(in ptr vendor, in string type, in ptr data)", &vendor_event_emit_cb,
proc_handler_add( this);
_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_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);
proc_handler_add(ph, "bool obs_websocket_api_get_ph(out ptr ph)", proc_handler_add(ph, "bool obs_websocket_api_get_ph(out ptr ph)", &get_ph_cb, this);
&get_ph_cb, this);
blog_debug("[WebSocketApi::WebSocketApi] Finished."); blog_debug("[WebSocketApi::WebSocketApi] Finished.");
} }
@ -67,8 +54,7 @@ WebSocketApi::~WebSocketApi()
proc_handler_destroy(_procHandler); proc_handler_destroy(_procHandler);
for (auto vendor : _vendors) { for (auto vendor : _vendors) {
blog_debug("[WebSocketApi::~WebSocketApi] Deleting vendor: %s", blog_debug("[WebSocketApi::~WebSocketApi] Deleting vendor: %s", vendor.first.c_str());
vendor.first.c_str());
delete vendor.second; delete vendor.second;
} }
@ -80,9 +66,8 @@ void WebSocketApi::SetEventCallback(EventCallback cb)
_eventCallback = cb; _eventCallback = cb;
} }
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest( enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType,
std::string vendorName, std::string requestType, obs_data_t *requestData, obs_data_t *responseData)
obs_data_t *requestData, obs_data_t *responseData)
{ {
std::shared_lock l(_mutex); std::shared_lock l(_mutex);
@ -131,8 +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 *>( auto response = static_cast<obs_websocket_request_response *>(bzalloc(sizeof(struct obs_websocket_request_response)));
bzalloc(sizeof(struct obs_websocket_request_response)));
if (!response) if (!response)
RETURN_FAILURE(); RETURN_FAILURE();
@ -154,9 +138,8 @@ void WebSocketApi::call_request(void *, calldata_t *cd)
calldata_set_ptr(cd, "response", response); calldata_set_ptr(cd, "response", response);
blog_debug( blog_debug("[WebSocketApi::call_request] Request %s called, response status code is %u", request_type,
"[WebSocketApi::call_request] Request %s called, response status code is %u", response->status_code);
request_type, response->status_code);
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
@ -166,10 +149,8 @@ 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) || if (!calldata_get_string(cd, "name", &vendorName) || strlen(vendorName) == 0) {
strlen(vendorName) == 0) { blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed due to missing `name` string.");
blog(LOG_WARNING,
"[WebSocketApi::vendor_register_cb] Failed due to missing `name` string.");
RETURN_FAILURE(); RETURN_FAILURE();
} }
@ -177,8 +158,7 @@ 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, blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed because `%s` is already a registered vendor.",
"[WebSocketApi::vendor_register_cb] Failed because `%s` is already a registered vendor.",
vendorName); vendorName);
RETURN_FAILURE(); RETURN_FAILURE();
} }
@ -188,9 +168,7 @@ void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
c->_vendors[vendorName] = v; c->_vendors[vendorName] = v;
blog_debug( blog_debug("[WebSocketApi::vendor_register_cb] [vendorName: %s] Registered new vendor.", v->_name.c_str());
"[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));
@ -204,8 +182,7 @@ void WebSocketApi::vendor_request_register_cb(void *, calldata_t *cd)
RETURN_FAILURE(); RETURN_FAILURE();
const char *requestType; const char *requestType;
if (!calldata_get_string(cd, "type", &requestType) || if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
strlen(requestType) == 0) {
blog(LOG_WARNING, blog(LOG_WARNING,
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing or empty `type` string.", "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing or empty `type` string.",
v->_name.c_str()); v->_name.c_str());
@ -233,9 +210,8 @@ void WebSocketApi::vendor_request_register_cb(void *, calldata_t *cd)
v->_requests[requestType] = *cb; v->_requests[requestType] = *cb;
blog_debug( blog_debug("[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Registered new vendor request: %s",
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Registered new vendor request: %s", v->_name.c_str(), requestType);
v->_name.c_str(), requestType);
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
@ -247,8 +223,7 @@ void WebSocketApi::vendor_request_unregister_cb(void *, calldata_t *cd)
RETURN_FAILURE(); RETURN_FAILURE();
const char *requestType; const char *requestType;
if (!calldata_get_string(cd, "type", &requestType) || if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
strlen(requestType) == 0) {
blog(LOG_WARNING, blog(LOG_WARNING,
"[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Failed due to missing `type` string.", "[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Failed due to missing `type` string.",
v->_name.c_str()); v->_name.c_str());
@ -266,9 +241,8 @@ void WebSocketApi::vendor_request_unregister_cb(void *, calldata_t *cd)
v->_requests.erase(requestType); v->_requests.erase(requestType);
blog_debug( blog_debug("[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Unregistered vendor request: %s",
"[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Unregistered vendor request: %s", v->_name.c_str(), requestType);
v->_name.c_str(), requestType);
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
@ -282,18 +256,15 @@ void WebSocketApi::vendor_event_emit_cb(void *priv_data, calldata_t *cd)
RETURN_FAILURE(); RETURN_FAILURE();
const char *eventType; const char *eventType;
if (!calldata_get_string(cd, "type", &eventType) || if (!calldata_get_string(cd, "type", &eventType) || strlen(eventType) == 0) {
strlen(eventType) == 0) { blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `type` string.",
blog(LOG_WARNING,
"[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `type` string.",
v->_name.c_str()); 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, blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `data` pointer.",
"[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `data` pointer.",
v->_name.c_str()); v->_name.c_str());
RETURN_FAILURE(); RETURN_FAILURE();
} }

View File

@ -17,8 +17,7 @@ public:
NoVendorRequest, NoVendorRequest,
}; };
typedef std::function<void(std::string, std::string, obs_data_t *)> typedef std::function<void(std::string, std::string, obs_data_t *)> EventCallback;
EventCallback;
struct Vendor { struct Vendor {
std::shared_mutex _mutex; std::shared_mutex _mutex;
@ -31,9 +30,7 @@ public:
void SetEventCallback(EventCallback cb); void SetEventCallback(EventCallback cb);
enum RequestReturnCode PerformVendorRequest(std::string vendorName, enum RequestReturnCode PerformVendorRequest(std::string vendorName, std::string requestName, obs_data_t *requestData,
std::string requestName,
obs_data_t *requestData,
obs_data_t *responseData); obs_data_t *responseData);
static void get_ph_cb(void *priv_data, calldata_t *cd); static void get_ph_cb(void *priv_data, calldata_t *cd);
@ -41,8 +38,7 @@ public:
static void call_request(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_register_cb(void *priv_data, calldata_t *cd);
static void vendor_request_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, static void vendor_request_unregister_cb(void *priv_data, calldata_t *cd);
calldata_t *cd);
static void vendor_event_emit_cb(void *priv_data, calldata_t *cd); static void vendor_event_emit_cb(void *priv_data, calldata_t *cd);
private: private:

View File

@ -32,17 +32,12 @@ EventHandler::EventHandler()
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", signal_handler_connect(coreSignalHandler, "source_create", SourceCreatedMultiHandler, this);
SourceCreatedMultiHandler, this); signal_handler_connect(coreSignalHandler, "source_destroy", SourceDestroyedMultiHandler, this);
signal_handler_connect(coreSignalHandler, "source_destroy", signal_handler_connect(coreSignalHandler, "source_remove", SourceRemovedMultiHandler, this);
SourceDestroyedMultiHandler, this); signal_handler_connect(coreSignalHandler, "source_rename", SourceRenamedMultiHandler, this);
signal_handler_connect(coreSignalHandler, "source_remove",
SourceRemovedMultiHandler, this);
signal_handler_connect(coreSignalHandler, "source_rename",
SourceRenamedMultiHandler, this);
} else { } else {
blog(LOG_ERROR, blog(LOG_ERROR, "[EventHandler::EventHandler] Unable to get libobs signal handler!");
"[EventHandler::EventHandler] Unable to get libobs signal handler!");
} }
blog_debug("[EventHandler::EventHandler] Finished."); blog_debug("[EventHandler::EventHandler] Finished.");
@ -56,17 +51,12 @@ EventHandler::~EventHandler()
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", signal_handler_disconnect(coreSignalHandler, "source_create", SourceCreatedMultiHandler, this);
SourceCreatedMultiHandler, this); signal_handler_disconnect(coreSignalHandler, "source_destroy", SourceDestroyedMultiHandler, this);
signal_handler_disconnect(coreSignalHandler, "source_destroy", signal_handler_disconnect(coreSignalHandler, "source_remove", SourceRemovedMultiHandler, this);
SourceDestroyedMultiHandler, this); signal_handler_disconnect(coreSignalHandler, "source_rename", SourceRenamedMultiHandler, this);
signal_handler_disconnect(coreSignalHandler, "source_remove",
SourceRemovedMultiHandler, this);
signal_handler_disconnect(coreSignalHandler, "source_rename",
SourceRenamedMultiHandler, this);
} else { } else {
blog(LOG_ERROR, blog(LOG_ERROR, "[EventHandler::~EventHandler] Unable to get libobs signal handler!");
"[EventHandler::~EventHandler] Unable to get libobs signal handler!");
} }
blog_debug("[EventHandler::~EventHandler] Finished."); blog_debug("[EventHandler::~EventHandler] Finished.");
@ -88,25 +78,17 @@ void EventHandler::ProcessSubscription(uint64_t eventSubscriptions)
if ((eventSubscriptions & EventSubscription::InputVolumeMeters) != 0) { if ((eventSubscriptions & EventSubscription::InputVolumeMeters) != 0) {
if (_inputVolumeMetersRef.fetch_add(1) == 0) { if (_inputVolumeMetersRef.fetch_add(1) == 0) {
if (_inputVolumeMetersHandler) if (_inputVolumeMetersHandler)
blog(LOG_WARNING, blog(LOG_WARNING, "[EventHandler::ProcessSubscription] Input volume meter handler already exists!");
"[EventHandler::ProcessSubscription] Input volume meter handler already exists!");
else else
_inputVolumeMetersHandler = std::make_unique< _inputVolumeMetersHandler = std::make_unique<Utils::Obs::VolumeMeter::Handler>(
Utils::Obs::VolumeMeter::Handler>( std::bind(&EventHandler::HandleInputVolumeMeters, this, std::placeholders::_1));
std::bind(
&EventHandler::
HandleInputVolumeMeters,
this, std::placeholders::_1));
} }
} }
if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0)
0)
_inputActiveStateChangedRef++; _inputActiveStateChangedRef++;
if ((eventSubscriptions & EventSubscription::InputShowStateChanged) != if ((eventSubscriptions & EventSubscription::InputShowStateChanged) != 0)
0)
_inputShowStateChangedRef++; _inputShowStateChangedRef++;
if ((eventSubscriptions & if ((eventSubscriptions & EventSubscription::SceneItemTransformChanged) != 0)
EventSubscription::SceneItemTransformChanged) != 0)
_sceneItemTransformChangedRef++; _sceneItemTransformChangedRef++;
} }
@ -117,21 +99,16 @@ void EventHandler::ProcessUnsubscription(uint64_t eventSubscriptions)
if (_inputVolumeMetersRef.fetch_sub(1) == 1) if (_inputVolumeMetersRef.fetch_sub(1) == 1)
_inputVolumeMetersHandler.reset(); _inputVolumeMetersHandler.reset();
} }
if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0)
0)
_inputActiveStateChangedRef--; _inputActiveStateChangedRef--;
if ((eventSubscriptions & EventSubscription::InputShowStateChanged) != if ((eventSubscriptions & EventSubscription::InputShowStateChanged) != 0)
0)
_inputShowStateChangedRef--; _inputShowStateChangedRef--;
if ((eventSubscriptions & if ((eventSubscriptions & EventSubscription::SceneItemTransformChanged) != 0)
EventSubscription::SceneItemTransformChanged) != 0)
_sceneItemTransformChangedRef--; _sceneItemTransformChangedRef--;
} }
// Function required in order to use default arguments // Function required in order to use default arguments
void EventHandler::BroadcastEvent(uint64_t requiredIntent, void EventHandler::BroadcastEvent(uint64_t requiredIntent, std::string eventType, json eventData, uint8_t rpcVersion)
std::string eventType, json eventData,
uint8_t rpcVersion)
{ {
if (!_broadcastCallback) if (!_broadcastCallback)
return; return;
@ -140,8 +117,7 @@ void EventHandler::BroadcastEvent(uint64_t requiredIntent,
} }
// Connect source signals for Inputs, Scenes, and Transitions. Filters are automatically connected. // Connect source signals for Inputs, Scenes, and Transitions. Filters are automatically connected.
void EventHandler::ConnectSourceSignals( void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inputs and scenes
obs_source_t *source) // Applies to inputs and scenes
{ {
if (!source || obs_source_removed(source)) if (!source || obs_source_removed(source))
return; return;
@ -155,74 +131,43 @@ void EventHandler::ConnectSourceSignals(
// Inputs // Inputs
if (sourceType == OBS_SOURCE_TYPE_INPUT) { if (sourceType == OBS_SOURCE_TYPE_INPUT) {
signal_handler_connect(sh, "activate", signal_handler_connect(sh, "activate", HandleInputActiveStateChanged, this);
HandleInputActiveStateChanged, this); signal_handler_connect(sh, "deactivate", HandleInputActiveStateChanged, this);
signal_handler_connect(sh, "deactivate", signal_handler_connect(sh, "show", HandleInputShowStateChanged, this);
HandleInputActiveStateChanged, this); signal_handler_connect(sh, "hide", HandleInputShowStateChanged, this);
signal_handler_connect(sh, "show", HandleInputShowStateChanged, signal_handler_connect(sh, "mute", HandleInputMuteStateChanged, this);
this); signal_handler_connect(sh, "volume", HandleInputVolumeChanged, this);
signal_handler_connect(sh, "hide", HandleInputShowStateChanged, signal_handler_connect(sh, "audio_balance", HandleInputAudioBalanceChanged, this);
this); signal_handler_connect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
signal_handler_connect(sh, "mute", HandleInputMuteStateChanged, signal_handler_connect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
this); signal_handler_connect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
signal_handler_connect(sh, "volume", HandleInputVolumeChanged, signal_handler_connect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
this); signal_handler_connect(sh, "media_ended", HandleMediaInputPlaybackEnded, this);
signal_handler_connect(sh, "audio_balance", signal_handler_connect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
HandleInputAudioBalanceChanged, this); signal_handler_connect(sh, "media_play", SourceMediaPlayMultiHandler, this);
signal_handler_connect(sh, "audio_sync", signal_handler_connect(sh, "media_restart", SourceMediaRestartMultiHandler, this);
HandleInputAudioSyncOffsetChanged, this); signal_handler_connect(sh, "media_stopped", SourceMediaStopMultiHandler, this);
signal_handler_connect(sh, "audio_mixers", signal_handler_connect(sh, "media_next", SourceMediaNextMultiHandler, this);
HandleInputAudioTracksChanged, this); signal_handler_connect(sh, "media_previous", SourceMediaPreviousMultiHandler, this);
signal_handler_connect(sh, "audio_monitoring",
HandleInputAudioMonitorTypeChanged,
this);
signal_handler_connect(sh, "media_started",
HandleMediaInputPlaybackStarted, this);
signal_handler_connect(sh, "media_ended",
HandleMediaInputPlaybackEnded, this);
signal_handler_connect(sh, "media_pause",
SourceMediaPauseMultiHandler, this);
signal_handler_connect(sh, "media_play",
SourceMediaPlayMultiHandler, this);
signal_handler_connect(sh, "media_restart",
SourceMediaRestartMultiHandler, this);
signal_handler_connect(sh, "media_stopped",
SourceMediaStopMultiHandler, this);
signal_handler_connect(sh, "media_next",
SourceMediaNextMultiHandler, this);
signal_handler_connect(sh, "media_previous",
SourceMediaPreviousMultiHandler, this);
} }
// Scenes // Scenes
if (sourceType == OBS_SOURCE_TYPE_SCENE) { if (sourceType == OBS_SOURCE_TYPE_SCENE) {
signal_handler_connect(sh, "item_add", HandleSceneItemCreated, signal_handler_connect(sh, "item_add", HandleSceneItemCreated, this);
this); signal_handler_connect(sh, "item_remove", HandleSceneItemRemoved, this);
signal_handler_connect(sh, "item_remove", signal_handler_connect(sh, "reorder", HandleSceneItemListReindexed, this);
HandleSceneItemRemoved, this); signal_handler_connect(sh, "item_visible", HandleSceneItemEnableStateChanged, this);
signal_handler_connect(sh, "reorder", signal_handler_connect(sh, "item_locked", HandleSceneItemLockStateChanged, this);
HandleSceneItemListReindexed, this); signal_handler_connect(sh, "item_select", HandleSceneItemSelected, this);
signal_handler_connect(sh, "item_visible", signal_handler_connect(sh, "item_transform", HandleSceneItemTransformChanged, this);
HandleSceneItemEnableStateChanged, this);
signal_handler_connect(sh, "item_locked",
HandleSceneItemLockStateChanged, this);
signal_handler_connect(sh, "item_select",
HandleSceneItemSelected, this);
signal_handler_connect(sh, "item_transform",
HandleSceneItemTransformChanged, this);
} }
// Scenes and Inputs // Scenes and Inputs
if (sourceType == OBS_SOURCE_TYPE_INPUT || if (sourceType == OBS_SOURCE_TYPE_INPUT || sourceType == OBS_SOURCE_TYPE_SCENE) {
sourceType == OBS_SOURCE_TYPE_SCENE) { signal_handler_connect(sh, "reorder_filters", HandleSourceFilterListReindexed, this);
signal_handler_connect(sh, "reorder_filters", signal_handler_connect(sh, "filter_add", FilterAddMultiHandler, this);
HandleSourceFilterListReindexed, this); signal_handler_connect(sh, "filter_remove", FilterRemoveMultiHandler, this);
signal_handler_connect(sh, "filter_add", FilterAddMultiHandler, auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param) {
this);
signal_handler_connect(sh, "filter_remove",
FilterRemoveMultiHandler, this);
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);
}; };
@ -231,21 +176,15 @@ void EventHandler::ConnectSourceSignals(
// Transitions // Transitions
if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { if (sourceType == OBS_SOURCE_TYPE_TRANSITION) {
signal_handler_connect(sh, "transition_start", signal_handler_connect(sh, "transition_start", HandleSceneTransitionStarted, this);
HandleSceneTransitionStarted, this); signal_handler_connect(sh, "transition_stop", HandleSceneTransitionEnded, this);
signal_handler_connect(sh, "transition_stop", signal_handler_connect(sh, "transition_video_stop", HandleSceneTransitionVideoEnded, this);
HandleSceneTransitionEnded, this);
signal_handler_connect(sh, "transition_video_stop",
HandleSceneTransitionVideoEnded, this);
} }
// Filters // Filters
if (sourceType == OBS_SOURCE_TYPE_FILTER) { if (sourceType == OBS_SOURCE_TYPE_FILTER) {
signal_handler_connect(sh, "enable", signal_handler_connect(sh, "enable", HandleSourceFilterEnableStateChanged, this);
HandleSourceFilterEnableStateChanged, signal_handler_connect(sh, "rename", HandleSourceFilterNameChanged, this);
this);
signal_handler_connect(sh, "rename",
HandleSourceFilterNameChanged, this);
} }
} }
@ -261,81 +200,43 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
// Inputs // Inputs
if (sourceType == OBS_SOURCE_TYPE_INPUT) { if (sourceType == OBS_SOURCE_TYPE_INPUT) {
signal_handler_disconnect(sh, "activate", signal_handler_disconnect(sh, "activate", HandleInputActiveStateChanged, this);
HandleInputActiveStateChanged, this); signal_handler_disconnect(sh, "deactivate", HandleInputActiveStateChanged, this);
signal_handler_disconnect(sh, "deactivate", signal_handler_disconnect(sh, "show", HandleInputShowStateChanged, this);
HandleInputActiveStateChanged, this); signal_handler_disconnect(sh, "hide", HandleInputShowStateChanged, this);
signal_handler_disconnect(sh, "show", signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, this);
HandleInputShowStateChanged, this); signal_handler_disconnect(sh, "volume", HandleInputVolumeChanged, this);
signal_handler_disconnect(sh, "hide", signal_handler_disconnect(sh, "audio_balance", HandleInputAudioBalanceChanged, this);
HandleInputShowStateChanged, this); signal_handler_disconnect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
signal_handler_disconnect(sh, "mute", signal_handler_disconnect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
HandleInputMuteStateChanged, this); signal_handler_disconnect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
signal_handler_disconnect(sh, "volume", signal_handler_disconnect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
HandleInputVolumeChanged, this); signal_handler_disconnect(sh, "media_ended", HandleMediaInputPlaybackEnded, this);
signal_handler_disconnect(sh, "audio_balance", signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
HandleInputAudioBalanceChanged, this); signal_handler_disconnect(sh, "media_play", SourceMediaPlayMultiHandler, this);
signal_handler_disconnect(sh, "audio_sync", signal_handler_disconnect(sh, "media_restart", SourceMediaRestartMultiHandler, this);
HandleInputAudioSyncOffsetChanged, signal_handler_disconnect(sh, "media_stopped", SourceMediaStopMultiHandler, this);
this); signal_handler_disconnect(sh, "media_next", SourceMediaNextMultiHandler, this);
signal_handler_disconnect(sh, "audio_mixers", signal_handler_disconnect(sh, "media_previous", SourceMediaPreviousMultiHandler, this);
HandleInputAudioTracksChanged, this);
signal_handler_disconnect(sh, "audio_monitoring",
HandleInputAudioMonitorTypeChanged,
this);
signal_handler_disconnect(sh, "media_started",
HandleMediaInputPlaybackStarted,
this);
signal_handler_disconnect(sh, "media_ended",
HandleMediaInputPlaybackEnded, this);
signal_handler_disconnect(sh, "media_pause",
SourceMediaPauseMultiHandler, this);
signal_handler_disconnect(sh, "media_play",
SourceMediaPlayMultiHandler, this);
signal_handler_disconnect(sh, "media_restart",
SourceMediaRestartMultiHandler, this);
signal_handler_disconnect(sh, "media_stopped",
SourceMediaStopMultiHandler, this);
signal_handler_disconnect(sh, "media_next",
SourceMediaNextMultiHandler, this);
signal_handler_disconnect(sh, "media_previous",
SourceMediaPreviousMultiHandler,
this);
} }
// Scenes // Scenes
if (sourceType == OBS_SOURCE_TYPE_SCENE) { if (sourceType == OBS_SOURCE_TYPE_SCENE) {
signal_handler_disconnect(sh, "item_add", signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this);
HandleSceneItemCreated, this); signal_handler_disconnect(sh, "item_remove", HandleSceneItemRemoved, this);
signal_handler_disconnect(sh, "item_remove", signal_handler_disconnect(sh, "reorder", HandleSceneItemListReindexed, this);
HandleSceneItemRemoved, this); signal_handler_disconnect(sh, "item_visible", HandleSceneItemEnableStateChanged, this);
signal_handler_disconnect(sh, "reorder", signal_handler_disconnect(sh, "item_locked", HandleSceneItemLockStateChanged, this);
HandleSceneItemListReindexed, this); signal_handler_disconnect(sh, "item_select", HandleSceneItemSelected, this);
signal_handler_disconnect(sh, "item_visible", signal_handler_disconnect(sh, "item_transform", HandleSceneItemTransformChanged, this);
HandleSceneItemEnableStateChanged,
this);
signal_handler_disconnect(sh, "item_locked",
HandleSceneItemLockStateChanged,
this);
signal_handler_disconnect(sh, "item_select",
HandleSceneItemSelected, this);
signal_handler_disconnect(sh, "item_transform",
HandleSceneItemTransformChanged,
this);
} }
// Inputs and Scenes // Inputs and Scenes
if (sourceType == OBS_SOURCE_TYPE_INPUT || if (sourceType == OBS_SOURCE_TYPE_INPUT || sourceType == OBS_SOURCE_TYPE_SCENE) {
sourceType == OBS_SOURCE_TYPE_SCENE) { signal_handler_disconnect(sh, "reorder_filters", HandleSourceFilterListReindexed, this);
signal_handler_disconnect(sh, "reorder_filters", signal_handler_disconnect(sh, "filter_add", FilterAddMultiHandler, this);
HandleSourceFilterListReindexed, signal_handler_disconnect(sh, "filter_remove", FilterRemoveMultiHandler, this);
this); auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param) {
signal_handler_disconnect(sh, "filter_add",
FilterAddMultiHandler, this);
signal_handler_disconnect(sh, "filter_remove",
FilterRemoveMultiHandler, this);
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);
}; };
@ -344,32 +245,23 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
// Transitions // Transitions
if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { if (sourceType == OBS_SOURCE_TYPE_TRANSITION) {
signal_handler_disconnect(sh, "transition_start", signal_handler_disconnect(sh, "transition_start", HandleSceneTransitionStarted, this);
HandleSceneTransitionStarted, this); signal_handler_disconnect(sh, "transition_stop", HandleSceneTransitionEnded, this);
signal_handler_disconnect(sh, "transition_stop", signal_handler_disconnect(sh, "transition_video_stop", HandleSceneTransitionVideoEnded, this);
HandleSceneTransitionEnded, this);
signal_handler_disconnect(sh, "transition_video_stop",
HandleSceneTransitionVideoEnded,
this);
} }
// Filters // Filters
if (sourceType == OBS_SOURCE_TYPE_FILTER) { if (sourceType == OBS_SOURCE_TYPE_FILTER) {
signal_handler_disconnect(sh, "enable", signal_handler_disconnect(sh, "enable", HandleSourceFilterEnableStateChanged, this);
HandleSourceFilterEnableStateChanged, signal_handler_disconnect(sh, "rename", HandleSourceFilterNameChanged, this);
this);
signal_handler_disconnect(sh, "rename",
HandleSourceFilterNameChanged, this);
} }
} }
void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_data)
void *private_data)
{ {
auto eventHandler = static_cast<EventHandler *>(private_data); auto eventHandler = static_cast<EventHandler *>(private_data);
if (!eventHandler->_obsLoaded.load() && if (!eventHandler->_obsLoaded.load() && event != OBS_FRONTEND_EVENT_FINISHED_LOADING)
event != OBS_FRONTEND_EVENT_FINISHED_LOADING)
return; return;
switch (event) { switch (event) {
@ -384,10 +276,8 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event,
// 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, auto enumInputs = [](void *param, obs_source_t *source) {
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;
}; };
@ -396,10 +286,8 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event,
// Enumerate scenes and connect each one // Enumerate scenes and connect each one
{ {
auto enumScenes = [](void *param, auto enumScenes = [](void *param, obs_source_t *source) {
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;
}; };
@ -411,8 +299,7 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event,
obs_frontend_source_list transitions = {}; obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions); obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) { for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t *transition = obs_source_t *transition = transitions.sources.array[i];
transitions.sources.array[i];
eventHandler->ConnectSourceSignals(transition); eventHandler->ConnectSourceSignals(transition);
} }
obs_frontend_source_list_free(&transitions); obs_frontend_source_list_free(&transitions);
@ -427,18 +314,15 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event,
case OBS_FRONTEND_EVENT_EXIT: case OBS_FRONTEND_EVENT_EXIT:
eventHandler->HandleExitStarted(); eventHandler->HandleExitStarted();
blog_debug( blog_debug("[EventHandler::OnFrontendEvent] OBS is unloading. Disabling events...");
"[EventHandler::OnFrontendEvent] OBS is unloading. Disabling events...");
// Disconnect source signals and disable events when OBS starts unloading (to reduce extra logging). // Disconnect source signals and disable events when OBS starts unloading (to reduce extra logging).
eventHandler->_obsLoaded.store(false); eventHandler->_obsLoaded.store(false);
// 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 disconnect each one // Enumerate inputs and disconnect each one
{ {
auto enumInputs = [](void *param, auto enumInputs = [](void *param, obs_source_t *source) {
obs_source_t *source) { auto eventHandler = static_cast<EventHandler *>(param);
auto eventHandler =
static_cast<EventHandler *>(param);
eventHandler->DisconnectSourceSignals(source); eventHandler->DisconnectSourceSignals(source);
return true; return true;
}; };
@ -447,10 +331,8 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event,
// Enumerate scenes and disconnect each one // Enumerate scenes and disconnect each one
{ {
auto enumScenes = [](void *param, auto enumScenes = [](void *param, obs_source_t *source) {
obs_source_t *source) { auto eventHandler = static_cast<EventHandler *>(param);
auto eventHandler =
static_cast<EventHandler *>(param);
eventHandler->DisconnectSourceSignals(source); eventHandler->DisconnectSourceSignals(source);
return true; return true;
}; };
@ -462,10 +344,8 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event,
obs_frontend_source_list transitions = {}; obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions); obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) { for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t *transition = obs_source_t *transition = transitions.sources.array[i];
transitions.sources.array[i]; eventHandler->DisconnectSourceSignals(transition);
eventHandler->DisconnectSourceSignals(
transition);
} }
obs_frontend_source_list_free(&transitions); obs_frontend_source_list_free(&transitions);
} }
@ -546,68 +426,52 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event,
// Outputs // Outputs
case OBS_FRONTEND_EVENT_STREAMING_STARTING: case OBS_FRONTEND_EVENT_STREAMING_STARTING:
eventHandler->HandleStreamStateChanged( eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STARTING);
OBS_WEBSOCKET_OUTPUT_STARTING);
break; break;
case OBS_FRONTEND_EVENT_STREAMING_STARTED: case OBS_FRONTEND_EVENT_STREAMING_STARTED:
eventHandler->HandleStreamStateChanged( eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
OBS_WEBSOCKET_OUTPUT_STARTED);
break; break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPING: case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
eventHandler->HandleStreamStateChanged( eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPING);
OBS_WEBSOCKET_OUTPUT_STOPPING);
break; break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPED: case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
eventHandler->HandleStreamStateChanged( eventHandler->HandleStreamStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
OBS_WEBSOCKET_OUTPUT_STOPPED);
break; break;
case OBS_FRONTEND_EVENT_RECORDING_STARTING: case OBS_FRONTEND_EVENT_RECORDING_STARTING:
eventHandler->HandleRecordStateChanged( eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STARTING);
OBS_WEBSOCKET_OUTPUT_STARTING);
break; break;
case OBS_FRONTEND_EVENT_RECORDING_STARTED: case OBS_FRONTEND_EVENT_RECORDING_STARTED:
eventHandler->HandleRecordStateChanged( eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
OBS_WEBSOCKET_OUTPUT_STARTED);
break; break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPING: case OBS_FRONTEND_EVENT_RECORDING_STOPPING:
eventHandler->HandleRecordStateChanged( eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPING);
OBS_WEBSOCKET_OUTPUT_STOPPING);
break; break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPED: case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
eventHandler->HandleRecordStateChanged( eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
OBS_WEBSOCKET_OUTPUT_STOPPED);
break; break;
case OBS_FRONTEND_EVENT_RECORDING_PAUSED: case OBS_FRONTEND_EVENT_RECORDING_PAUSED:
eventHandler->HandleRecordStateChanged( eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_PAUSED);
OBS_WEBSOCKET_OUTPUT_PAUSED);
break; break;
case OBS_FRONTEND_EVENT_RECORDING_UNPAUSED: case OBS_FRONTEND_EVENT_RECORDING_UNPAUSED:
eventHandler->HandleRecordStateChanged( eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_RESUMED);
OBS_WEBSOCKET_OUTPUT_RESUMED);
break; break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING: case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING:
eventHandler->HandleReplayBufferStateChanged( eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STARTING);
OBS_WEBSOCKET_OUTPUT_STARTING);
break; break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED: case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED:
eventHandler->HandleReplayBufferStateChanged( eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
OBS_WEBSOCKET_OUTPUT_STARTED);
break; break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING: case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING:
eventHandler->HandleReplayBufferStateChanged( eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPING);
OBS_WEBSOCKET_OUTPUT_STOPPING);
break; break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED: case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED:
eventHandler->HandleReplayBufferStateChanged( eventHandler->HandleReplayBufferStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
OBS_WEBSOCKET_OUTPUT_STOPPED);
break; break;
case OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED: case OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED:
eventHandler->HandleVirtualcamStateChanged( eventHandler->HandleVirtualcamStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
OBS_WEBSOCKET_OUTPUT_STARTED);
break; break;
case OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED: case OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED:
eventHandler->HandleVirtualcamStateChanged( eventHandler->HandleVirtualcamStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
OBS_WEBSOCKET_OUTPUT_STOPPED);
break; break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED: case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
eventHandler->HandleReplayBufferSaved(); eventHandler->HandleReplayBufferSaved();
@ -722,14 +586,12 @@ void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data)
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, eventHandler->HandleInputNameChanged(source, oldSourceName, sourceName);
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, eventHandler->HandleSceneNameChanged(source, oldSourceName, sourceName);
sourceName);
break; break;
default: default:
break; break;

View File

@ -34,8 +34,7 @@ public:
EventHandler(); EventHandler();
~EventHandler(); ~EventHandler();
typedef std::function<void(uint64_t, std::string, json, uint8_t)> typedef std::function<void(uint64_t, std::string, json, uint8_t)> BroadcastCallback;
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);
@ -49,8 +48,7 @@ private:
std::atomic<bool> _obsLoaded; std::atomic<bool> _obsLoaded;
std::unique_ptr<Utils::Obs::VolumeMeter::Handler> std::unique_ptr<Utils::Obs::VolumeMeter::Handler> _inputVolumeMetersHandler;
_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;
@ -59,12 +57,10 @@ private:
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, void BroadcastEvent(uint64_t requiredIntent, std::string eventType, json eventData = nullptr, uint8_t rpcVersion = 0);
json eventData = nullptr, uint8_t rpcVersion = 0);
// Signal handler: frontend // Signal handler: frontend
static void OnFrontendEvent(enum obs_frontend_event event, static void OnFrontendEvent(enum obs_frontend_event event, void *private_data);
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);
@ -75,12 +71,10 @@ private:
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, static void SourceMediaRestartMultiHandler(void *param, calldata_t *data);
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, static void SourceMediaPreviousMultiHandler(void *param, calldata_t *data);
calldata_t *data);
// General // General
void HandleExitStarted(); void HandleExitStarted();
@ -97,9 +91,7 @@ private:
// Scenes // Scenes
void HandleSceneCreated(obs_source_t *source); void HandleSceneCreated(obs_source_t *source);
void HandleSceneRemoved(obs_source_t *source); void HandleSceneRemoved(obs_source_t *source);
void HandleSceneNameChanged(obs_source_t *source, void HandleSceneNameChanged(obs_source_t *source, std::string oldSceneName, std::string sceneName);
std::string oldSceneName,
std::string sceneName);
void HandleCurrentProgramSceneChanged(); void HandleCurrentProgramSceneChanged();
void HandleCurrentPreviewSceneChanged(); void HandleCurrentPreviewSceneChanged();
void HandleSceneListChanged(); void HandleSceneListChanged();
@ -107,67 +99,47 @@ private:
// Inputs // Inputs
void HandleInputCreated(obs_source_t *source); void HandleInputCreated(obs_source_t *source);
void HandleInputRemoved(obs_source_t *source); void HandleInputRemoved(obs_source_t *source);
void HandleInputNameChanged(obs_source_t *source, void HandleInputNameChanged(obs_source_t *source, std::string oldInputName, std::string inputName);
std::string oldInputName, void HandleInputVolumeMeters(std::vector<json> inputs); // AudioMeter::Handler callback
std::string inputName); static void HandleInputActiveStateChanged(void *param,
void HandleInputVolumeMeters( calldata_t *data); // Direct callback
std::vector<json> inputs); // AudioMeter::Handler callback static void HandleInputShowStateChanged(void *param,
static void calldata_t *data); // Direct callback
HandleInputActiveStateChanged(void *param, static void HandleInputMuteStateChanged(void *param,
calldata_t *data); // Direct callback calldata_t *data); // Direct callback
static void static void HandleInputVolumeChanged(void *param,
HandleInputShowStateChanged(void *param, calldata_t *data); // Direct callback
calldata_t *data); // Direct callback static void HandleInputAudioBalanceChanged(void *param,
static void calldata_t *data); // Direct callback
HandleInputMuteStateChanged(void *param, static void HandleInputAudioSyncOffsetChanged(void *param,
calldata_t *data); // Direct callback calldata_t *data); // Direct callback
static void static void HandleInputAudioTracksChanged(void *param,
HandleInputVolumeChanged(void *param, calldata_t *data); // Direct callback
calldata_t *data); // Direct callback static void HandleInputAudioMonitorTypeChanged(void *param,
static void calldata_t *data); // Direct callback
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 // Transitions
void HandleCurrentSceneTransitionChanged(); void HandleCurrentSceneTransitionChanged();
void HandleCurrentSceneTransitionDurationChanged(); void HandleCurrentSceneTransitionDurationChanged();
static void static void HandleSceneTransitionStarted(void *param,
HandleSceneTransitionStarted(void *param, calldata_t *data); // Direct callback
calldata_t *data); // Direct callback static void HandleSceneTransitionEnded(void *param,
static void calldata_t *data); // Direct callback
HandleSceneTransitionEnded(void *param, static void HandleSceneTransitionVideoEnded(void *param,
calldata_t *data); // Direct callback calldata_t *data); // Direct callback
static void
HandleSceneTransitionVideoEnded(void *param,
calldata_t *data); // Direct callback
// Filters // Filters
static void FilterAddMultiHandler(void *param, static void FilterAddMultiHandler(void *param,
calldata_t *data); // Direct callback calldata_t *data); // Direct callback
static void static void FilterRemoveMultiHandler(void *param,
FilterRemoveMultiHandler(void *param, calldata_t *data); // Direct callback
calldata_t *data); // Direct callback static void HandleSourceFilterListReindexed(void *param,
static void calldata_t *data); // Direct callback
HandleSourceFilterListReindexed(void *param, void HandleSourceFilterCreated(obs_source_t *source, obs_source_t *filter);
calldata_t *data); // Direct callback void HandleSourceFilterRemoved(obs_source_t *source, obs_source_t *filter);
void HandleSourceFilterCreated(obs_source_t *source, static void HandleSourceFilterNameChanged(void *param,
obs_source_t *filter); calldata_t *data); // Direct callback
void HandleSourceFilterRemoved(obs_source_t *source, static void HandleSourceFilterEnableStateChanged(void *param, calldata_t *data); // Direct callback
obs_source_t *filter);
static void
HandleSourceFilterNameChanged(void *param,
calldata_t *data); // Direct callback
static void HandleSourceFilterEnableStateChanged(
void *param, calldata_t *data); // Direct callback
// Outputs // Outputs
void HandleStreamStateChanged(ObsOutputState state); void HandleStreamStateChanged(ObsOutputState state);
@ -181,29 +153,21 @@ private:
calldata_t *data); // Direct callback calldata_t *data); // Direct callback
static void HandleSceneItemRemoved(void *param, static void HandleSceneItemRemoved(void *param,
calldata_t *data); // Direct callback calldata_t *data); // Direct callback
static void static void HandleSceneItemListReindexed(void *param,
HandleSceneItemListReindexed(void *param, calldata_t *data); // Direct callback
calldata_t *data); // Direct callback static void HandleSceneItemEnableStateChanged(void *param,
static void calldata_t *data); // Direct callback
HandleSceneItemEnableStateChanged(void *param, static void HandleSceneItemLockStateChanged(void *param,
calldata_t *data); // Direct callback calldata_t *data); // Direct callback
static void static void HandleSceneItemSelected(void *param,
HandleSceneItemLockStateChanged(void *param, calldata_t *data); // Direct callback
calldata_t *data); // Direct callback static void HandleSceneItemTransformChanged(void *param,
static void calldata_t *data); // Direct callback
HandleSceneItemSelected(void *param,
calldata_t *data); // Direct callback
static void
HandleSceneItemTransformChanged(void *param,
calldata_t *data); // Direct callback
// Media Inputs // Media Inputs
static void static void HandleMediaInputPlaybackStarted(void *param,
HandleMediaInputPlaybackStarted(void *param, calldata_t *data); // Direct callback
calldata_t *data); // Direct callback static void HandleMediaInputPlaybackEnded(void *param,
static void calldata_t *data); // Direct callback
HandleMediaInputPlaybackEnded(void *param, void HandleMediaInputActionTriggered(obs_source_t *source, ObsMediaInputAction action);
calldata_t *data); // Direct callback
void HandleMediaInputActionTriggered(obs_source_t *source,
ObsMediaInputAction action);
}; };

View File

@ -38,10 +38,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
void EventHandler::HandleCurrentSceneCollectionChanging() void EventHandler::HandleCurrentSceneCollectionChanging()
{ {
json eventData; json eventData;
eventData["sceneCollectionName"] = eventData["sceneCollectionName"] = Utils::Obs::StringHelper::GetCurrentSceneCollection();
Utils::Obs::StringHelper::GetCurrentSceneCollection(); BroadcastEvent(EventSubscription::Config, "CurrentSceneCollectionChanging", eventData);
BroadcastEvent(EventSubscription::Config,
"CurrentSceneCollectionChanging", eventData);
} }
/** /**
@ -62,10 +60,8 @@ void EventHandler::HandleCurrentSceneCollectionChanging()
void EventHandler::HandleCurrentSceneCollectionChanged() void EventHandler::HandleCurrentSceneCollectionChanged()
{ {
json eventData; json eventData;
eventData["sceneCollectionName"] = eventData["sceneCollectionName"] = Utils::Obs::StringHelper::GetCurrentSceneCollection();
Utils::Obs::StringHelper::GetCurrentSceneCollection(); BroadcastEvent(EventSubscription::Config, "CurrentSceneCollectionChanged", eventData);
BroadcastEvent(EventSubscription::Config,
"CurrentSceneCollectionChanged", eventData);
} }
/** /**
@ -84,10 +80,8 @@ void EventHandler::HandleCurrentSceneCollectionChanged()
void EventHandler::HandleSceneCollectionListChanged() void EventHandler::HandleSceneCollectionListChanged()
{ {
json eventData; json eventData;
eventData["sceneCollections"] = eventData["sceneCollections"] = Utils::Obs::ArrayHelper::GetSceneCollectionList();
Utils::Obs::ArrayHelper::GetSceneCollectionList(); BroadcastEvent(EventSubscription::Config, "SceneCollectionListChanged", eventData);
BroadcastEvent(EventSubscription::Config, "SceneCollectionListChanged",
eventData);
} }
/** /**
@ -106,10 +100,8 @@ void EventHandler::HandleSceneCollectionListChanged()
void EventHandler::HandleCurrentProfileChanging() void EventHandler::HandleCurrentProfileChanging()
{ {
json eventData; json eventData;
eventData["profileName"] = eventData["profileName"] = Utils::Obs::StringHelper::GetCurrentProfile();
Utils::Obs::StringHelper::GetCurrentProfile(); BroadcastEvent(EventSubscription::Config, "CurrentProfileChanging", eventData);
BroadcastEvent(EventSubscription::Config, "CurrentProfileChanging",
eventData);
} }
/** /**
@ -128,10 +120,8 @@ void EventHandler::HandleCurrentProfileChanging()
void EventHandler::HandleCurrentProfileChanged() void EventHandler::HandleCurrentProfileChanged()
{ {
json eventData; json eventData;
eventData["profileName"] = eventData["profileName"] = Utils::Obs::StringHelper::GetCurrentProfile();
Utils::Obs::StringHelper::GetCurrentProfile(); BroadcastEvent(EventSubscription::Config, "CurrentProfileChanged", eventData);
BroadcastEvent(EventSubscription::Config, "CurrentProfileChanged",
eventData);
} }
/** /**
@ -151,6 +141,5 @@ void EventHandler::HandleProfileListChanged()
{ {
json eventData; json eventData;
eventData["profiles"] = Utils::Obs::ArrayHelper::GetProfileList(); eventData["profiles"] = Utils::Obs::ArrayHelper::GetProfileList();
BroadcastEvent(EventSubscription::Config, "ProfileListChanged", BroadcastEvent(EventSubscription::Config, "ProfileListChanged", eventData);
eventData);
} }

View File

@ -63,8 +63,7 @@ void EventHandler::FilterRemoveMultiHandler(void *param, calldata_t *data)
* @api events * @api events
* @category filters * @category filters
*/ */
void EventHandler::HandleSourceFilterListReindexed(void *param, void EventHandler::HandleSourceFilterListReindexed(void *param, calldata_t *data)
calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler *>(param); auto eventHandler = static_cast<EventHandler *>(param);
@ -74,10 +73,8 @@ void EventHandler::HandleSourceFilterListReindexed(void *param,
json eventData; json eventData;
eventData["sourceName"] = obs_source_get_name(source); eventData["sourceName"] = obs_source_get_name(source);
eventData["filters"] = eventData["filters"] = Utils::Obs::ArrayHelper::GetSourceFilterList(source);
Utils::Obs::ArrayHelper::GetSourceFilterList(source); eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterListReindexed", eventData);
eventHandler->BroadcastEvent(EventSubscription::Filters,
"SourceFilterListReindexed", eventData);
} }
/** /**
@ -98,26 +95,20 @@ void EventHandler::HandleSourceFilterListReindexed(void *param,
* @api events * @api events
* @category filters * @category filters
*/ */
void EventHandler::HandleSourceFilterCreated(obs_source_t *source, void EventHandler::HandleSourceFilterCreated(obs_source_t *source, obs_source_t *filter)
obs_source_t *filter)
{ {
std::string filterKind = obs_source_get_id(filter); std::string filterKind = obs_source_get_id(filter);
OBSDataAutoRelease filterSettings = obs_source_get_settings(filter); OBSDataAutoRelease filterSettings = obs_source_get_settings(filter);
OBSDataAutoRelease defaultFilterSettings = OBSDataAutoRelease defaultFilterSettings = obs_get_source_defaults(filterKind.c_str());
obs_get_source_defaults(filterKind.c_str());
json eventData; json eventData;
eventData["sourceName"] = obs_source_get_name(source); eventData["sourceName"] = obs_source_get_name(source);
eventData["filterName"] = obs_source_get_name(filter); eventData["filterName"] = obs_source_get_name(filter);
eventData["filterKind"] = filterKind; eventData["filterKind"] = filterKind;
eventData["filterIndex"] = eventData["filterIndex"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter);
Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter); eventData["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
eventData["filterSettings"] = eventData["defaultFilterSettings"] = Utils::Json::ObsDataToJson(defaultFilterSettings, true);
Utils::Json::ObsDataToJson(filterSettings); BroadcastEvent(EventSubscription::Filters, "SourceFilterCreated", eventData);
eventData["defaultFilterSettings"] =
Utils::Json::ObsDataToJson(defaultFilterSettings, true);
BroadcastEvent(EventSubscription::Filters, "SourceFilterCreated",
eventData);
} }
/** /**
@ -134,14 +125,12 @@ void EventHandler::HandleSourceFilterCreated(obs_source_t *source,
* @api events * @api events
* @category filters * @category filters
*/ */
void EventHandler::HandleSourceFilterRemoved(obs_source_t *source, void EventHandler::HandleSourceFilterRemoved(obs_source_t *source, obs_source_t *filter)
obs_source_t *filter)
{ {
json eventData; json eventData;
eventData["sourceName"] = obs_source_get_name(source); eventData["sourceName"] = obs_source_get_name(source);
eventData["filterName"] = obs_source_get_name(filter); eventData["filterName"] = obs_source_get_name(filter);
BroadcastEvent(EventSubscription::Filters, "SourceFilterRemoved", BroadcastEvent(EventSubscription::Filters, "SourceFilterRemoved", eventData);
eventData);
} }
/** /**
@ -168,12 +157,10 @@ void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data)
return; return;
json eventData; json eventData;
eventData["sourceName"] = eventData["sourceName"] = obs_source_get_name(obs_filter_get_parent(filter));
obs_source_get_name(obs_filter_get_parent(filter));
eventData["oldFilterName"] = calldata_string(data, "prev_name"); eventData["oldFilterName"] = calldata_string(data, "prev_name");
eventData["filterName"] = calldata_string(data, "new_name"); eventData["filterName"] = calldata_string(data, "new_name");
eventHandler->BroadcastEvent(EventSubscription::Filters, eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterNameChanged", eventData);
"SourceFilterNameChanged", eventData);
} }
/** /**
@ -191,8 +178,7 @@ void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data)
* @api events * @api events
* @category filters * @category filters
*/ */
void EventHandler::HandleSourceFilterEnableStateChanged(void *param, void EventHandler::HandleSourceFilterEnableStateChanged(void *param, calldata_t *data)
calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler *>(param); auto eventHandler = static_cast<EventHandler *>(param);
@ -211,7 +197,5 @@ void EventHandler::HandleSourceFilterEnableStateChanged(void *param,
eventData["sourceName"] = obs_source_get_name(source); eventData["sourceName"] = obs_source_get_name(source);
eventData["filterName"] = obs_source_get_name(filter); eventData["filterName"] = obs_source_get_name(filter);
eventData["filterEnabled"] = filterEnabled; eventData["filterEnabled"] = filterEnabled;
eventHandler->BroadcastEvent(EventSubscription::Filters, eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterEnableStateChanged", eventData);
"SourceFilterEnableStateChanged",
eventData);
} }

View File

@ -40,17 +40,14 @@ void EventHandler::HandleInputCreated(obs_source_t *source)
{ {
std::string inputKind = obs_source_get_id(source); std::string inputKind = obs_source_get_id(source);
OBSDataAutoRelease inputSettings = obs_source_get_settings(source); OBSDataAutoRelease inputSettings = obs_source_get_settings(source);
OBSDataAutoRelease defaultInputSettings = OBSDataAutoRelease defaultInputSettings = obs_get_source_defaults(inputKind.c_str());
obs_get_source_defaults(inputKind.c_str());
json eventData; json eventData;
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventData["inputKind"] = inputKind; eventData["inputKind"] = inputKind;
eventData["unversionedInputKind"] = eventData["unversionedInputKind"] = obs_source_get_unversioned_id(source);
obs_source_get_unversioned_id(source);
eventData["inputSettings"] = Utils::Json::ObsDataToJson(inputSettings); eventData["inputSettings"] = Utils::Json::ObsDataToJson(inputSettings);
eventData["defaultInputSettings"] = eventData["defaultInputSettings"] = Utils::Json::ObsDataToJson(defaultInputSettings, true);
Utils::Json::ObsDataToJson(defaultInputSettings, true);
BroadcastEvent(EventSubscription::Inputs, "InputCreated", eventData); BroadcastEvent(EventSubscription::Inputs, "InputCreated", eventData);
} }
@ -88,15 +85,12 @@ void EventHandler::HandleInputRemoved(obs_source_t *source)
* @api events * @api events
* @category inputs * @category inputs
*/ */
void EventHandler::HandleInputNameChanged(obs_source_t *, void EventHandler::HandleInputNameChanged(obs_source_t *, std::string oldInputName, std::string inputName)
std::string oldInputName,
std::string inputName)
{ {
json eventData; json eventData;
eventData["oldInputName"] = oldInputName; eventData["oldInputName"] = oldInputName;
eventData["inputName"] = inputName; eventData["inputName"] = inputName;
BroadcastEvent(EventSubscription::Inputs, "InputNameChanged", BroadcastEvent(EventSubscription::Inputs, "InputNameChanged", eventData);
eventData);
} }
/** /**
@ -132,8 +126,7 @@ void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
json eventData; json eventData;
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventData["videoActive"] = obs_source_active(source); eventData["videoActive"] = obs_source_active(source);
eventHandler->BroadcastEvent(EventSubscription::InputActiveStateChanged, eventHandler->BroadcastEvent(EventSubscription::InputActiveStateChanged, "InputActiveStateChanged", eventData);
"InputActiveStateChanged", eventData);
} }
/** /**
@ -169,8 +162,7 @@ void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
json eventData; json eventData;
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventData["videoShowing"] = obs_source_showing(source); eventData["videoShowing"] = obs_source_showing(source);
eventHandler->BroadcastEvent(EventSubscription::InputShowStateChanged, eventHandler->BroadcastEvent(EventSubscription::InputShowStateChanged, "InputShowStateChanged", eventData);
"InputShowStateChanged", eventData);
} }
/** /**
@ -201,8 +193,7 @@ void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
json eventData; json eventData;
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventData["inputMuted"] = obs_source_muted(source); eventData["inputMuted"] = obs_source_muted(source);
eventHandler->BroadcastEvent(EventSubscription::Inputs, eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputMuteStateChanged", eventData);
"InputMuteStateChanged", eventData);
} }
/** /**
@ -242,8 +233,7 @@ void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventData["inputVolumeMul"] = inputVolumeMul; eventData["inputVolumeMul"] = inputVolumeMul;
eventData["inputVolumeDb"] = inputVolumeDb; eventData["inputVolumeDb"] = inputVolumeDb;
eventHandler->BroadcastEvent(EventSubscription::Inputs, eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputVolumeChanged", eventData);
"InputVolumeChanged", eventData);
} }
/** /**
@ -276,8 +266,7 @@ void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
json eventData; json eventData;
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventData["inputAudioBalance"] = inputAudioBalance; eventData["inputAudioBalance"] = inputAudioBalance;
eventHandler->BroadcastEvent(EventSubscription::Inputs, eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioBalanceChanged", eventData);
"InputAudioBalanceChanged", eventData);
} }
/** /**
@ -294,8 +283,7 @@ void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
* @api events * @api events
* @category inputs * @category inputs
*/ */
void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data)
calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler *>(param); auto eventHandler = static_cast<EventHandler *>(param);
@ -311,8 +299,7 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param,
json eventData; json eventData;
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventData["inputAudioSyncOffset"] = inputAudioSyncOffset / 1000000; eventData["inputAudioSyncOffset"] = inputAudioSyncOffset / 1000000;
eventHandler->BroadcastEvent(EventSubscription::Inputs, eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioSyncOffsetChanged", eventData);
"InputAudioSyncOffsetChanged", eventData);
} }
/** /**
@ -344,15 +331,13 @@ void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
json inputAudioTracks; json inputAudioTracks;
for (long long i = 0; i < MAX_AUDIO_MIXES; i++) { for (long long i = 0; i < MAX_AUDIO_MIXES; i++) {
inputAudioTracks[std::to_string(i + 1)] = inputAudioTracks[std::to_string(i + 1)] = (bool)((tracks >> i) & 1);
(bool)((tracks >> i) & 1);
} }
json eventData; json eventData;
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventData["inputAudioTracks"] = inputAudioTracks; eventData["inputAudioTracks"] = inputAudioTracks;
eventHandler->BroadcastEvent(EventSubscription::Inputs, eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioTracksChanged", eventData);
"InputAudioTracksChanged", eventData);
} }
/** /**
@ -375,8 +360,7 @@ void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
* @api events * @api events
* @category inputs * @category inputs
*/ */
void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data)
calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler *>(param); auto eventHandler = static_cast<EventHandler *>(param);
@ -387,17 +371,14 @@ void EventHandler::HandleInputAudioMonitorTypeChanged(void *param,
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT) if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT)
return; return;
enum obs_monitoring_type monitorType = enum obs_monitoring_type monitorType = (obs_monitoring_type)calldata_int(data, "type");
(obs_monitoring_type)calldata_int(data, "type");
std::string monitorTypeString = std::string monitorTypeString = Utils::Obs::StringHelper::GetInputMonitorType(monitorType);
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"] = monitorTypeString;
eventHandler->BroadcastEvent(EventSubscription::Inputs, eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioMonitorTypeChanged", eventData);
"InputAudioMonitorTypeChanged", eventData);
} }
/** /**
@ -417,6 +398,5 @@ void EventHandler::HandleInputVolumeMeters(std::vector<json> inputs)
{ {
json eventData; json eventData;
eventData["inputs"] = inputs; eventData["inputs"] = inputs;
BroadcastEvent(EventSubscription::InputVolumeMeters, BroadcastEvent(EventSubscription::InputVolumeMeters, "InputVolumeMeters", eventData);
"InputVolumeMeters", eventData);
} }

View File

@ -47,8 +47,7 @@ void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data)
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT) if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT)
return; return;
eventHandler->HandleMediaInputActionTriggered( eventHandler->HandleMediaInputActionTriggered(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE);
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE);
} }
void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data)
@ -62,8 +61,7 @@ void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data)
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT) if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT)
return; return;
eventHandler->HandleMediaInputActionTriggered( eventHandler->HandleMediaInputActionTriggered(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY);
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY);
} }
void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data)
@ -77,8 +75,7 @@ void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data)
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT) if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT)
return; return;
eventHandler->HandleMediaInputActionTriggered( eventHandler->HandleMediaInputActionTriggered(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART);
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART);
} }
void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data)
@ -92,8 +89,7 @@ void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data)
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT) if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT)
return; return;
eventHandler->HandleMediaInputActionTriggered( eventHandler->HandleMediaInputActionTriggered(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP);
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP);
} }
void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data)
@ -107,12 +103,10 @@ void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data)
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT) if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT)
return; return;
eventHandler->HandleMediaInputActionTriggered( eventHandler->HandleMediaInputActionTriggered(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT);
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT);
} }
void EventHandler::SourceMediaPreviousMultiHandler(void *param, void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data)
calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler *>(param); auto eventHandler = static_cast<EventHandler *>(param);
@ -123,8 +117,7 @@ void EventHandler::SourceMediaPreviousMultiHandler(void *param,
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT) if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT)
return; return;
eventHandler->HandleMediaInputActionTriggered( eventHandler->HandleMediaInputActionTriggered(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS);
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS);
} }
/** /**
@ -140,8 +133,7 @@ void EventHandler::SourceMediaPreviousMultiHandler(void *param,
* @api events * @api events
* @category media inputs * @category media inputs
*/ */
void EventHandler::HandleMediaInputPlaybackStarted(void *param, void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data)
calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler *>(param); auto eventHandler = static_cast<EventHandler *>(param);
@ -154,8 +146,7 @@ void EventHandler::HandleMediaInputPlaybackStarted(void *param,
json eventData; json eventData;
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventHandler->BroadcastEvent(EventSubscription::MediaInputs, eventHandler->BroadcastEvent(EventSubscription::MediaInputs, "MediaInputPlaybackStarted", eventData);
"MediaInputPlaybackStarted", eventData);
} }
/** /**
@ -184,8 +175,7 @@ void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
json eventData; json eventData;
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventHandler->BroadcastEvent(EventSubscription::MediaInputs, eventHandler->BroadcastEvent(EventSubscription::MediaInputs, "MediaInputPlaybackEnded", eventData);
"MediaInputPlaybackEnded", eventData);
} }
/** /**
@ -202,12 +192,10 @@ void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
* @api events * @api events
* @category media inputs * @category media inputs
*/ */
void EventHandler::HandleMediaInputActionTriggered(obs_source_t *source, void EventHandler::HandleMediaInputActionTriggered(obs_source_t *source, ObsMediaInputAction action)
ObsMediaInputAction action)
{ {
json eventData; json eventData;
eventData["inputName"] = obs_source_get_name(source); eventData["inputName"] = obs_source_get_name(source);
eventData["mediaAction"] = GetMediaInputActionString(action); eventData["mediaAction"] = GetMediaInputActionString(action);
BroadcastEvent(EventSubscription::MediaInputs, BroadcastEvent(EventSubscription::MediaInputs, "MediaInputActionTriggered", eventData);
"MediaInputActionTriggered", eventData);
} }

View File

@ -53,10 +53,8 @@ void EventHandler::HandleStreamStateChanged(ObsOutputState state)
{ {
json eventData; json eventData;
eventData["outputActive"] = GetOutputStateActive(state); eventData["outputActive"] = GetOutputStateActive(state);
eventData["outputState"] = eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
Utils::Obs::StringHelper::GetOutputState(state); BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged", eventData);
BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged",
eventData);
} }
/** /**
@ -77,10 +75,8 @@ void EventHandler::HandleRecordStateChanged(ObsOutputState state)
{ {
json eventData; json eventData;
eventData["outputActive"] = GetOutputStateActive(state); eventData["outputActive"] = GetOutputStateActive(state);
eventData["outputState"] = eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
Utils::Obs::StringHelper::GetOutputState(state); BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged", eventData);
BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged",
eventData);
} }
/** /**
@ -101,10 +97,8 @@ void EventHandler::HandleReplayBufferStateChanged(ObsOutputState state)
{ {
json eventData; json eventData;
eventData["outputActive"] = GetOutputStateActive(state); eventData["outputActive"] = GetOutputStateActive(state);
eventData["outputState"] = eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
Utils::Obs::StringHelper::GetOutputState(state); BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged", eventData);
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged",
eventData);
} }
/** /**
@ -125,10 +119,8 @@ void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
{ {
json eventData; json eventData;
eventData["outputActive"] = GetOutputStateActive(state); eventData["outputActive"] = GetOutputStateActive(state);
eventData["outputState"] = eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
Utils::Obs::StringHelper::GetOutputState(state); BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged", eventData);
BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged",
eventData);
} }
/** /**
@ -147,8 +139,6 @@ void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
void EventHandler::HandleReplayBufferSaved() void EventHandler::HandleReplayBufferSaved()
{ {
json eventData; json eventData;
eventData["savedReplayPath"] = eventData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFilePath();
Utils::Obs::StringHelper::GetLastReplayBufferFilePath(); BroadcastEvent(EventSubscription::Outputs, "ReplayBufferSaved", eventData);
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferSaved",
eventData);
} }

View File

@ -43,21 +43,16 @@ void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
if (!scene) if (!scene)
return; return;
obs_sceneitem_t *sceneItem = obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
GetCalldataPointer<obs_sceneitem_t>(data, "item");
if (!sceneItem) if (!sceneItem)
return; return;
json eventData; json eventData;
eventData["sceneName"] = eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
obs_source_get_name(obs_scene_get_source(scene)); eventData["sourceName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
eventData["sourceName"] =
obs_source_get_name(obs_sceneitem_get_source(sceneItem));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem); eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemIndex"] = eventData["sceneItemIndex"] = obs_sceneitem_get_order_position(sceneItem);
obs_sceneitem_get_order_position(sceneItem); eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemCreated", eventData);
eventHandler->BroadcastEvent(EventSubscription::SceneItems,
"SceneItemCreated", eventData);
} }
/** /**
@ -85,19 +80,15 @@ void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
if (!scene) if (!scene)
return; return;
obs_sceneitem_t *sceneItem = obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
GetCalldataPointer<obs_sceneitem_t>(data, "item");
if (!sceneItem) if (!sceneItem)
return; return;
json eventData; json eventData;
eventData["sceneName"] = eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
obs_source_get_name(obs_scene_get_source(scene)); eventData["sourceName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
eventData["sourceName"] =
obs_source_get_name(obs_sceneitem_get_source(sceneItem));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem); eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventHandler->BroadcastEvent(EventSubscription::SceneItems, eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemRemoved", eventData);
"SceneItemRemoved", eventData);
} }
/** /**
@ -123,12 +114,9 @@ void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
return; return;
json eventData; json eventData;
eventData["sceneName"] = eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
obs_source_get_name(obs_scene_get_source(scene)); eventData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList(scene, true);
eventData["sceneItems"] = eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemListReindexed", eventData);
Utils::Obs::ArrayHelper::GetSceneItemList(scene, true);
eventHandler->BroadcastEvent(EventSubscription::SceneItems,
"SceneItemListReindexed", eventData);
} }
/** /**
@ -146,8 +134,7 @@ void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
* @api events * @api events
* @category scene items * @category scene items
*/ */
void EventHandler::HandleSceneItemEnableStateChanged(void *param, void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *data)
calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler *>(param); auto eventHandler = static_cast<EventHandler *>(param);
@ -155,20 +142,17 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param,
if (!scene) if (!scene)
return; return;
obs_sceneitem_t *sceneItem = obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
GetCalldataPointer<obs_sceneitem_t>(data, "item");
if (!sceneItem) if (!sceneItem)
return; return;
bool sceneItemEnabled = calldata_bool(data, "visible"); bool sceneItemEnabled = calldata_bool(data, "visible");
json eventData; json eventData;
eventData["sceneName"] = eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem); eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemEnabled"] = sceneItemEnabled; eventData["sceneItemEnabled"] = sceneItemEnabled;
eventHandler->BroadcastEvent(EventSubscription::SceneItems, eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemEnableStateChanged", eventData);
"SceneItemEnableStateChanged", eventData);
} }
/** /**
@ -186,8 +170,7 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param,
* @api events * @api events
* @category scene items * @category scene items
*/ */
void EventHandler::HandleSceneItemLockStateChanged(void *param, void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data)
calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler *>(param); auto eventHandler = static_cast<EventHandler *>(param);
@ -195,20 +178,17 @@ void EventHandler::HandleSceneItemLockStateChanged(void *param,
if (!scene) if (!scene)
return; return;
obs_sceneitem_t *sceneItem = obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
GetCalldataPointer<obs_sceneitem_t>(data, "item");
if (!sceneItem) if (!sceneItem)
return; return;
bool sceneItemLocked = calldata_bool(data, "locked"); bool sceneItemLocked = calldata_bool(data, "locked");
json eventData; json eventData;
eventData["sceneName"] = eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem); eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemLocked"] = sceneItemLocked; eventData["sceneItemLocked"] = sceneItemLocked;
eventHandler->BroadcastEvent(EventSubscription::SceneItems, eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemLockStateChanged", eventData);
"SceneItemLockStateChanged", eventData);
} }
/** /**
@ -233,17 +213,14 @@ void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
if (!scene) if (!scene)
return; return;
obs_sceneitem_t *sceneItem = obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
GetCalldataPointer<obs_sceneitem_t>(data, "item");
if (!sceneItem) if (!sceneItem)
return; return;
json eventData; json eventData;
eventData["sceneName"] = eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem); eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventHandler->BroadcastEvent(EventSubscription::SceneItems, eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemSelected", eventData);
"SceneItemSelected", eventData);
} }
/** /**
@ -261,8 +238,7 @@ void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
* @api events * @api events
* @category scene items * @category scene items
*/ */
void EventHandler::HandleSceneItemTransformChanged(void *param, void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data)
calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler *>(param); auto eventHandler = static_cast<EventHandler *>(param);
@ -273,18 +249,13 @@ void EventHandler::HandleSceneItemTransformChanged(void *param,
if (!scene) if (!scene)
return; return;
obs_sceneitem_t *sceneItem = obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
GetCalldataPointer<obs_sceneitem_t>(data, "item");
if (!sceneItem) if (!sceneItem)
return; return;
json eventData; json eventData;
eventData["sceneName"] = eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem); eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemTransform"] = eventData["sceneItemTransform"] = Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem);
Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem); eventHandler->BroadcastEvent(EventSubscription::SceneItemTransformChanged, "SceneItemTransformChanged", eventData);
eventHandler->BroadcastEvent(
EventSubscription::SceneItemTransformChanged,
"SceneItemTransformChanged", eventData);
} }

View File

@ -77,15 +77,12 @@ void EventHandler::HandleSceneRemoved(obs_source_t *source)
* @api events * @api events
* @category scenes * @category scenes
*/ */
void EventHandler::HandleSceneNameChanged(obs_source_t *, void EventHandler::HandleSceneNameChanged(obs_source_t *, std::string oldSceneName, std::string sceneName)
std::string oldSceneName,
std::string sceneName)
{ {
json eventData; json eventData;
eventData["oldSceneName"] = oldSceneName; eventData["oldSceneName"] = oldSceneName;
eventData["sceneName"] = sceneName; eventData["sceneName"] = sceneName;
BroadcastEvent(EventSubscription::Scenes, "SceneNameChanged", BroadcastEvent(EventSubscription::Scenes, "SceneNameChanged", eventData);
eventData);
} }
/** /**
@ -107,8 +104,7 @@ void EventHandler::HandleCurrentProgramSceneChanged()
json eventData; json eventData;
eventData["sceneName"] = obs_source_get_name(currentScene); eventData["sceneName"] = obs_source_get_name(currentScene);
BroadcastEvent(EventSubscription::Scenes, "CurrentProgramSceneChanged", BroadcastEvent(EventSubscription::Scenes, "CurrentProgramSceneChanged", eventData);
eventData);
} }
/** /**
@ -126,8 +122,7 @@ void EventHandler::HandleCurrentProgramSceneChanged()
*/ */
void EventHandler::HandleCurrentPreviewSceneChanged() void EventHandler::HandleCurrentPreviewSceneChanged()
{ {
OBSSourceAutoRelease currentPreviewScene = OBSSourceAutoRelease currentPreviewScene = obs_frontend_get_current_preview_scene();
obs_frontend_get_current_preview_scene();
// This event may be called when OBS is not in studio mode, however retreiving the source while not in studio mode will return null. // This event may be called when OBS is not in studio mode, however retreiving the source while not in studio mode will return null.
if (!currentPreviewScene) if (!currentPreviewScene)
@ -135,8 +130,7 @@ void EventHandler::HandleCurrentPreviewSceneChanged()
json eventData; json eventData;
eventData["sceneName"] = obs_source_get_name(currentPreviewScene); eventData["sceneName"] = obs_source_get_name(currentPreviewScene);
BroadcastEvent(EventSubscription::Scenes, "CurrentPreviewSceneChanged", BroadcastEvent(EventSubscription::Scenes, "CurrentPreviewSceneChanged", eventData);
eventData);
} }
/** /**
@ -158,6 +152,5 @@ void EventHandler::HandleSceneListChanged()
{ {
json eventData; json eventData;
eventData["scenes"] = Utils::Obs::ArrayHelper::GetSceneList(); eventData["scenes"] = Utils::Obs::ArrayHelper::GetSceneList();
BroadcastEvent(EventSubscription::Scenes, "SceneListChanged", BroadcastEvent(EventSubscription::Scenes, "SceneListChanged", eventData);
eventData);
} }

View File

@ -38,8 +38,7 @@ void EventHandler::HandleCurrentSceneTransitionChanged()
json eventData; json eventData;
eventData["transitionName"] = obs_source_get_name(transition); eventData["transitionName"] = obs_source_get_name(transition);
BroadcastEvent(EventSubscription::Transitions, BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionChanged", eventData);
"CurrentSceneTransitionChanged", eventData);
} }
/** /**
@ -58,10 +57,8 @@ void EventHandler::HandleCurrentSceneTransitionChanged()
void EventHandler::HandleCurrentSceneTransitionDurationChanged() void EventHandler::HandleCurrentSceneTransitionDurationChanged()
{ {
json eventData; json eventData;
eventData["transitionDuration"] = eventData["transitionDuration"] = obs_frontend_get_transition_duration();
obs_frontend_get_transition_duration(); BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionDurationChanged", eventData);
BroadcastEvent(EventSubscription::Transitions,
"CurrentSceneTransitionDurationChanged", eventData);
} }
/** /**
@ -87,8 +84,7 @@ void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data)
json eventData; json eventData;
eventData["transitionName"] = obs_source_get_name(source); eventData["transitionName"] = obs_source_get_name(source);
eventHandler->BroadcastEvent(EventSubscription::Transitions, eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionStarted", eventData);
"SceneTransitionStarted", eventData);
} }
/** /**
@ -116,8 +112,7 @@ void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data)
json eventData; json eventData;
eventData["transitionName"] = obs_source_get_name(source); eventData["transitionName"] = obs_source_get_name(source);
eventHandler->BroadcastEvent(EventSubscription::Transitions, eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionEnded", eventData);
"SceneTransitionEnded", eventData);
} }
/** /**
@ -138,8 +133,7 @@ void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data)
* @api events * @api events
* @category transitions * @category transitions
*/ */
void EventHandler::HandleSceneTransitionVideoEnded(void *param, void EventHandler::HandleSceneTransitionVideoEnded(void *param, calldata_t *data)
calldata_t *data)
{ {
auto eventHandler = static_cast<EventHandler *>(param); auto eventHandler = static_cast<EventHandler *>(param);
@ -149,6 +143,5 @@ void EventHandler::HandleSceneTransitionVideoEnded(void *param,
json eventData; json eventData;
eventData["transitionName"] = obs_source_get_name(source); eventData["transitionName"] = obs_source_get_name(source);
eventHandler->BroadcastEvent(EventSubscription::Transitions, eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionVideoEnded", eventData);
"SceneTransitionVideoEnded", eventData);
} }

View File

@ -36,6 +36,5 @@ void EventHandler::HandleStudioModeStateChanged(bool enabled)
{ {
json eventData; json eventData;
eventData["studioModeEnabled"] = enabled; eventData["studioModeEnabled"] = enabled;
BroadcastEvent(EventSubscription::Ui, "StudioModeStateChanged", BroadcastEvent(EventSubscription::Ui, "StudioModeStateChanged", eventData);
eventData);
} }

View File

@ -163,8 +163,7 @@ enum EventSubscription {
* @initialVersion 5.0.0 * @initialVersion 5.0.0
* @api enums * @api enums
*/ */
All = (General | Config | Scenes | Inputs | Transitions | Filters | All = (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Ui | Vendors),
Outputs | SceneItems | MediaInputs | Ui | Vendors),
/** /**
* Subscription value to receive the `InputVolumeMeters` high-volume event. * Subscription value to receive the `InputVolumeMeters` high-volume event.
* *

View File

@ -28,17 +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, this, connect(ui->copyServerIpButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerIpButtonClicked);
&ConnectInfo::CopyServerIpButtonClicked); connect(ui->copyServerPortButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerPortButtonClicked);
connect(ui->copyServerPortButton, &QPushButton::clicked, this, connect(ui->copyServerPasswordButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerPasswordButtonClicked);
&ConnectInfo::CopyServerPortButtonClicked);
connect(ui->copyServerPasswordButton, &QPushButton::clicked, this,
&ConnectInfo::CopyServerPasswordButtonClicked);
} }
ConnectInfo::~ConnectInfo() ConnectInfo::~ConnectInfo()
@ -55,13 +51,11 @@ void ConnectInfo::RefreshData()
{ {
auto conf = GetConfig(); auto conf = GetConfig();
if (!conf) { if (!conf) {
blog(LOG_ERROR, blog(LOG_ERROR, "[ConnectInfo::showEvent] Unable to retreive config!");
"[ConnectInfo::showEvent] Unable to retreive config!");
return; return;
} }
QString serverIp = QString serverIp = QString::fromStdString(Utils::Platform::GetLocalAddress());
QString::fromStdString(Utils::Platform::GetLocalAddress());
ui->serverIpLineEdit->setText(serverIp); ui->serverIpLineEdit->setText(serverIp);
QString serverPort = QString::number(conf->ServerPort); QString serverPort = QString::number(conf->ServerPort);
@ -73,20 +67,15 @@ void ConnectInfo::RefreshData()
serverPassword = QUrl::toPercentEncoding(conf->ServerPassword); serverPassword = QUrl::toPercentEncoding(conf->ServerPassword);
} else { } else {
ui->copyServerPasswordButton->setEnabled(false); ui->copyServerPasswordButton->setEnabled(false);
serverPassword = obs_module_text( serverPassword = obs_module_text("OBSWebSocket.ConnectInfo.ServerPasswordPlaceholderText");
"OBSWebSocket.ConnectInfo.ServerPasswordPlaceholderText");
} }
ui->serverPasswordLineEdit->setText(serverPassword); ui->serverPasswordLineEdit->setText(serverPassword);
QString connectString; QString connectString;
if (conf->AuthRequired) if (conf->AuthRequired)
connectString = QString("obsws://%1:%2/%3") connectString = QString("obsws://%1:%2/%3").arg(serverIp).arg(serverPort).arg(serverPassword);
.arg(serverIp)
.arg(serverPort)
.arg(serverPassword);
else else
connectString = connectString = QString("obsws://%1:%2").arg(serverIp).arg(serverPort);
QString("obsws://%1:%2").arg(serverIp).arg(serverPort);
DrawQr(connectString); DrawQr(connectString);
} }
@ -120,8 +109,7 @@ void ConnectInfo::DrawQr(QString qrText)
map.fill(Qt::white); map.fill(Qt::white);
QPainter painter(&map); QPainter painter(&map);
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText( qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(QT_TO_UTF8(qrText), qrcodegen::QrCode::Ecc::MEDIUM);
QT_TO_UTF8(qrText), qrcodegen::QrCode::Ecc::MEDIUM);
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();

View File

@ -32,10 +32,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
QString GetToolTipIconHtml() QString GetToolTipIconHtml()
{ {
bool lightTheme = QApplication::palette().text().color().redF() < 0.5; bool lightTheme = QApplication::palette().text().color().redF() < 0.5;
QString iconFile = lightTheme ? ":toolTip/images/help.svg" QString iconFile = lightTheme ? ":toolTip/images/help.svg" : ":toolTip/images/help_light.svg";
: ":toolTip/images/help_light.svg"; QString iconTemplate = "<html> <img src='%1' style=' vertical-align: bottom; ' /></html>";
QString iconTemplate =
"<html> <img src='%1' style=' vertical-align: bottom; ' /></html>";
return iconTemplate.arg(iconFile); return iconTemplate.arg(iconFile);
} }
@ -47,8 +45,7 @@ SettingsDialog::SettingsDialog(QWidget *parent)
passwordManuallyEdited(false) passwordManuallyEdited(false)
{ {
ui->setupUi(this); ui->setupUi(this);
ui->websocketSessionTable->horizontalHeader()->resizeSection( ui->websocketSessionTable->horizontalHeader()->resizeSection(3, 100); // Resize Session Table column widths
3, 100); // Resize Session Table column widths
ui->websocketSessionTable->horizontalHeader()->resizeSection(4, 100); ui->websocketSessionTable->horizontalHeader()->resizeSection(4, 100);
// Remove the ? button on dialogs on Windows // Remove the ? button on dialogs on Windows
@ -59,18 +56,13 @@ SettingsDialog::SettingsDialog(QWidget *parent)
ui->enableDebugLoggingToolTipLabel->setText(toolTipHtml); ui->enableDebugLoggingToolTipLabel->setText(toolTipHtml);
ui->allowExternalToolTipLabel->setText(toolTipHtml); ui->allowExternalToolTipLabel->setText(toolTipHtml);
connect(sessionTableTimer, &QTimer::timeout, this, connect(sessionTableTimer, &QTimer::timeout, this, &SettingsDialog::FillSessionTable);
&SettingsDialog::FillSessionTable); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &SettingsDialog::DialogButtonClicked);
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, connect(ui->enableAuthenticationCheckBox, &QCheckBox::stateChanged, 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, this, connect(ui->serverPasswordLineEdit, &QLineEdit::textEdited, this, &SettingsDialog::PasswordEdited);
&SettingsDialog::GeneratePasswordButtonClicked);
connect(ui->showConnectInfoButton, &QPushButton::clicked, this,
&SettingsDialog::ShowConnectInfoButtonClicked);
connect(ui->serverPasswordLineEdit, &QLineEdit::textEdited, this,
&SettingsDialog::PasswordEdited);
} }
SettingsDialog::~SettingsDialog() SettingsDialog::~SettingsDialog()
@ -84,8 +76,7 @@ void SettingsDialog::showEvent(QShowEvent *)
{ {
auto conf = GetConfig(); auto conf = GetConfig();
if (!conf) { if (!conf) {
blog(LOG_ERROR, blog(LOG_ERROR, "[SettingsDialog::showEvent] Unable to retreive config!");
"[SettingsDialog::showEvent] Unable to retreive config!");
return; return;
} }
@ -125,8 +116,7 @@ void SettingsDialog::RefreshData()
{ {
auto conf = GetConfig(); auto conf = GetConfig();
if (!conf) { if (!conf) {
blog(LOG_ERROR, blog(LOG_ERROR, "[SettingsDialog::RefreshData] Unable to retreive config!");
"[SettingsDialog::RefreshData] Unable to retreive config!");
return; return;
} }
@ -143,10 +133,7 @@ void SettingsDialog::RefreshData()
ui->generatePasswordButton->setEnabled(conf->AuthRequired); ui->generatePasswordButton->setEnabled(conf->AuthRequired);
ui->showConnectInfoButton->setToolTip( ui->showConnectInfoButton->setToolTip(
ui->allowExternalCheckBox->isChecked() ui->allowExternalCheckBox->isChecked() ? "" : obs_module_text("OBSWebSocket.Settings.ShowConnectInfoHoverText"));
? ""
: obs_module_text(
"OBSWebSocket.Settings.ShowConnectInfoHoverText"));
FillSessionTable(); FillSessionTable();
} }
@ -164,32 +151,25 @@ void SettingsDialog::SaveFormData()
{ {
auto conf = GetConfig(); auto conf = GetConfig();
if (!conf) { if (!conf) {
blog(LOG_ERROR, blog(LOG_ERROR, "[SettingsDialog::SaveFormData] Unable to retreive config!");
"[SettingsDialog::SaveFormData] Unable to retreive config!");
return; return;
} }
if (ui->serverPasswordLineEdit->text().length() < 6) { if (ui->serverPasswordLineEdit->text().length() < 6) {
QMessageBox msgBox; QMessageBox msgBox;
msgBox.setWindowTitle(obs_module_text( msgBox.setWindowTitle(obs_module_text("OBSWebSocket.Settings.Save.PasswordInvalidErrorTitle"));
"OBSWebSocket.Settings.Save.PasswordInvalidErrorTitle")); msgBox.setText(obs_module_text("OBSWebSocket.Settings.Save.PasswordInvalidErrorMessage"));
msgBox.setText(obs_module_text(
"OBSWebSocket.Settings.Save.PasswordInvalidErrorMessage"));
msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec(); msgBox.exec();
return; return;
} }
// Show a confirmation box to the user if they attempt to provide their own password // Show a confirmation box to the user if they attempt to provide their own password
if (passwordManuallyEdited && if (passwordManuallyEdited && (conf->ServerPassword != ui->serverPasswordLineEdit->text())) {
(conf->ServerPassword != ui->serverPasswordLineEdit->text())) {
QMessageBox msgBox; QMessageBox msgBox;
msgBox.setWindowTitle(obs_module_text( msgBox.setWindowTitle(obs_module_text("OBSWebSocket.Settings.Save.UserPasswordWarningTitle"));
"OBSWebSocket.Settings.Save.UserPasswordWarningTitle")); msgBox.setText(obs_module_text("OBSWebSocket.Settings.Save.UserPasswordWarningMessage"));
msgBox.setText(obs_module_text( msgBox.setInformativeText(obs_module_text("OBSWebSocket.Settings.Save.UserPasswordWarningInfoText"));
"OBSWebSocket.Settings.Save.UserPasswordWarningMessage"));
msgBox.setInformativeText(obs_module_text(
"OBSWebSocket.Settings.Save.UserPasswordWarningInfoText"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No);
int ret = msgBox.exec(); int ret = msgBox.exec();
@ -199,19 +179,15 @@ void SettingsDialog::SaveFormData()
break; break;
case QMessageBox::No: case QMessageBox::No:
default: default:
ui->serverPasswordLineEdit->setText( ui->serverPasswordLineEdit->setText(conf->ServerPassword);
conf->ServerPassword);
return; return;
} }
} }
bool needsRestart = bool needsRestart =
(conf->ServerEnabled != (conf->ServerEnabled != ui->enableWebSocketServerCheckBox->isChecked()) ||
ui->enableWebSocketServerCheckBox->isChecked()) || (ui->enableAuthenticationCheckBox->isChecked() && conf->ServerPassword != ui->serverPasswordLineEdit->text()) ||
(ui->enableAuthenticationCheckBox->isChecked() && (conf->BindLoopback == ui->allowExternalCheckBox->isChecked()) ||
conf->ServerPassword != ui->serverPasswordLineEdit->text()) ||
(conf->BindLoopback ==
ui->allowExternalCheckBox->isChecked()) ||
(conf->ServerPort != ui->serverPortSpinBox->value()); (conf->ServerPort != ui->serverPortSpinBox->value());
conf->ServerEnabled = ui->enableWebSocketServerCheckBox->isChecked(); conf->ServerEnabled = ui->enableWebSocketServerCheckBox->isChecked();
@ -228,8 +204,7 @@ void SettingsDialog::SaveFormData()
connectInfo->RefreshData(); connectInfo->RefreshData();
if (needsRestart) { if (needsRestart) {
blog(LOG_INFO, blog(LOG_INFO, "[SettingsDialog::SaveFormData] A setting was changed which requires a server restart.");
"[SettingsDialog::SaveFormData] A setting was changed which requires a server restart.");
auto server = GetWebSocketServer(); auto server = GetWebSocketServer();
server->Stop(); server->Stop();
if (conf->ServerEnabled) { if (conf->ServerEnabled) {
@ -242,8 +217,7 @@ void SettingsDialog::FillSessionTable()
{ {
auto webSocketServer = GetWebSocketServer(); auto webSocketServer = GetWebSocketServer();
if (!webSocketServer) { if (!webSocketServer) {
blog(LOG_ERROR, blog(LOG_ERROR, "[SettingsDialog::FillSessionTable] Unable to fetch websocket server instance!");
"[SettingsDialog::FillSessionTable] Unable to fetch websocket server instance!");
return; return;
} }
@ -257,26 +231,20 @@ void SettingsDialog::FillSessionTable()
QPixmap crossIconPixmap = crossIcon.pixmap(QSize(25, 25)); QPixmap crossIconPixmap = crossIcon.pixmap(QSize(25, 25));
// Todo: Make a util for translations so that we don't need to import a bunch of obs libraries in order to use them. // Todo: Make a util for translations so that we don't need to import a bunch of obs libraries in order to use them.
QString kickButtonText = QString kickButtonText = obs_module_text("OBSWebSocket.SessionTable.KickButtonText");
obs_module_text("OBSWebSocket.SessionTable.KickButtonText");
ui->websocketSessionTable->setRowCount(rowCount); ui->websocketSessionTable->setRowCount(rowCount);
size_t i = 0; size_t i = 0;
for (auto session : webSocketSessions) { for (auto session : webSocketSessions) {
QTableWidgetItem *addressItem = new QTableWidgetItem( QTableWidgetItem *addressItem = new QTableWidgetItem(QString::fromStdString(session.remoteAddress));
QString::fromStdString(session.remoteAddress));
ui->websocketSessionTable->setItem(i, 0, addressItem); ui->websocketSessionTable->setItem(i, 0, addressItem);
uint64_t sessionDuration = QDateTime::currentSecsSinceEpoch() - uint64_t sessionDuration = QDateTime::currentSecsSinceEpoch() - session.connectedAt;
session.connectedAt; 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( QTableWidgetItem *statsItem =
QString("%1/%2") new QTableWidgetItem(QString("%1/%2").arg(session.incomingMessages).arg(session.outgoingMessages));
.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();
@ -288,20 +256,15 @@ void SettingsDialog::FillSessionTable()
} }
ui->websocketSessionTable->setCellWidget(i, 3, identifiedLabel); ui->websocketSessionTable->setCellWidget(i, 3, identifiedLabel);
QPushButton *invalidateButton = QPushButton *invalidateButton = new QPushButton(kickButtonText, this);
new QPushButton(kickButtonText, this);
QWidget *invalidateButtonWidget = new QWidget(); QWidget *invalidateButtonWidget = new QWidget();
QHBoxLayout *invalidateButtonLayout = QHBoxLayout *invalidateButtonLayout = new QHBoxLayout(invalidateButtonWidget);
new QHBoxLayout(invalidateButtonWidget);
invalidateButtonLayout->addWidget(invalidateButton); invalidateButtonLayout->addWidget(invalidateButton);
invalidateButtonLayout->setAlignment(Qt::AlignCenter); invalidateButtonLayout->setAlignment(Qt::AlignCenter);
invalidateButtonLayout->setContentsMargins(0, 0, 0, 0); invalidateButtonLayout->setContentsMargins(0, 0, 0, 0);
invalidateButtonWidget->setLayout(invalidateButtonLayout); invalidateButtonWidget->setLayout(invalidateButtonLayout);
ui->websocketSessionTable->setCellWidget( ui->websocketSessionTable->setCellWidget(i, 4, invalidateButtonWidget);
i, 4, invalidateButtonWidget); connect(invalidateButton, &QPushButton::clicked, [=]() { webSocketServer->InvalidateSession(session.hdl); });
connect(invalidateButton, &QPushButton::clicked, [=]() {
webSocketServer->InvalidateSession(session.hdl);
});
i++; i++;
} }
@ -320,8 +283,7 @@ void SettingsDialog::EnableAuthenticationCheckBoxChanged()
void SettingsDialog::GeneratePasswordButtonClicked() void SettingsDialog::GeneratePasswordButtonClicked()
{ {
QString newPassword = QString newPassword = QString::fromStdString(Utils::Crypto::GeneratePassword());
QString::fromStdString(Utils::Crypto::GeneratePassword());
ui->serverPasswordLineEdit->setText(newPassword); ui->serverPasswordLineEdit->setText(newPassword);
ui->serverPasswordLineEdit->selectAll(); ui->serverPasswordLineEdit->selectAll();
passwordManuallyEdited = false; passwordManuallyEdited = false;
@ -331,12 +293,9 @@ void SettingsDialog::ShowConnectInfoButtonClicked()
{ {
if (obs_video_active()) { if (obs_video_active()) {
QMessageBox msgBox; QMessageBox msgBox;
msgBox.setWindowTitle(obs_module_text( msgBox.setWindowTitle(obs_module_text("OBSWebSocket.Settings.ShowConnectInfoWarningTitle"));
"OBSWebSocket.Settings.ShowConnectInfoWarningTitle")); msgBox.setText(obs_module_text("OBSWebSocket.Settings.ShowConnectInfoWarningMessage"));
msgBox.setText(obs_module_text( msgBox.setInformativeText(obs_module_text("OBSWebSocket.Settings.ShowConnectInfoWarningInfoText"));
"OBSWebSocket.Settings.ShowConnectInfoWarningMessage"));
msgBox.setInformativeText(obs_module_text(
"OBSWebSocket.Settings.ShowConnectInfoWarningInfoText"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No);
int ret = msgBox.exec(); int ret = msgBox.exec();

View File

@ -48,19 +48,14 @@ WebSocketApiPtr _webSocketApi;
WebSocketServerPtr _webSocketServer; WebSocketServerPtr _webSocketServer;
SettingsDialog *_settingsDialog = nullptr; SettingsDialog *_settingsDialog = nullptr;
void WebSocketApiEventCallback(std::string vendorName, std::string eventType, void WebSocketApiEventCallback(std::string vendorName, std::string eventType, obs_data_t *obsEventData);
obs_data_t *obsEventData);
bool obs_module_load(void) bool obs_module_load(void)
{ {
blog(LOG_INFO, blog(LOG_INFO, "[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_VERSION,
"[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_RPC_VERSION);
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, blog(LOG_INFO, "[obs_module_load] Linked ASIO Version: %d", ASIO_VERSION);
"[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);
// Initialize the cpu stats // Initialize the cpu stats
_cpuUsageInfo = os_cpu_usage_info_start(); _cpuUsageInfo = os_cpu_usage_info_start();
@ -81,18 +76,14 @@ 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 = QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
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 = const char *menuActionText = obs_module_text("OBSWebSocket.Settings.DialogTitle");
obs_module_text("OBSWebSocket.Settings.DialogTitle"); QAction *menuAction = (QAction *)obs_frontend_add_tools_menu_qaction(menuActionText);
QAction *menuAction = QObject::connect(menuAction, &QAction::triggered, [] { _settingsDialog->ToggleShowHide(); });
(QAction *)obs_frontend_add_tools_menu_qaction(menuActionText);
QObject::connect(menuAction, &QAction::triggered,
[] { _settingsDialog->ToggleShowHide(); });
blog(LOG_INFO, "[obs_module_load] Module loaded."); blog(LOG_INFO, "[obs_module_load] Module loaded.");
return true; return true;
@ -104,8 +95,7 @@ void obs_module_unload()
// Shutdown the WebSocket server if it is running // Shutdown the WebSocket server if it is running
if (_webSocketServer->IsListening()) { if (_webSocketServer->IsListening()) {
blog_debug( blog_debug("[obs_module_unload] WebSocket server is running. Stopping...");
"[obs_module_unload] WebSocket server is running. Stopping...");
_webSocketServer->Stop(); _webSocketServer->Stop();
} }
@ -176,8 +166,7 @@ bool IsDebugEnabled()
* @api events * @api events
* @category general * @category general
*/ */
void WebSocketApiEventCallback(std::string vendorName, std::string eventType, void WebSocketApiEventCallback(std::string vendorName, std::string eventType, obs_data_t *obsEventData)
obs_data_t *obsEventData)
{ {
json eventData = Utils::Json::ObsDataToJson(obsEventData); json eventData = Utils::Json::ObsDataToJson(obsEventData);
@ -186,19 +175,16 @@ void WebSocketApiEventCallback(std::string vendorName, std::string eventType,
broadcastEventData["eventType"] = eventType; broadcastEventData["eventType"] = eventType;
broadcastEventData["eventData"] = eventData; broadcastEventData["eventData"] = eventData;
_webSocketServer->BroadcastEvent(EventSubscription::Vendors, _webSocketServer->BroadcastEvent(EventSubscription::Vendors, "VendorEvent", broadcastEventData);
"VendorEvent", broadcastEventData);
} }
#ifdef PLUGIN_TESTS #ifdef PLUGIN_TESTS
static void test_vendor_request_cb(obs_data_t *requestData, static void test_vendor_request_cb(obs_data_t *requestData, obs_data_t *responseData, void *priv_data)
obs_data_t *responseData, void *priv_data)
{ {
blog(LOG_INFO, "[test_vendor_request_cb] Request called!"); blog(LOG_INFO, "[test_vendor_request_cb] Request called!");
blog(LOG_INFO, "[test_vendor_request_cb] Request data: %s", blog(LOG_INFO, "[test_vendor_request_cb] Request data: %s", obs_data_get_json(requestData));
obs_data_get_json(requestData));
// Set an item to the response data // Set an item to the response data
obs_data_set_string(responseData, "test", "pp"); obs_data_set_string(responseData, "test", "pp");
@ -213,34 +199,26 @@ void obs_module_post_load()
// Test plugin API version fetch // Test plugin API version fetch
uint apiVersion = obs_websocket_get_api_version(); uint apiVersion = obs_websocket_get_api_version();
blog(LOG_INFO, blog(LOG_INFO, "[obs_module_post_load] obs-websocket plugin API version: %u", apiVersion);
"[obs_module_post_load] obs-websocket plugin API version: %u",
apiVersion);
// Test calling obs-websocket requests // Test calling obs-websocket requests
struct obs_websocket_request_response *response = struct obs_websocket_request_response *response = obs_websocket_call_request("GetVersion");
obs_websocket_call_request("GetVersion");
if (response) { if (response) {
blog(LOG_INFO, blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
"[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s", response->status_code, response->comment, response->response_data);
response->status_code, response->comment,
response->response_data);
obs_websocket_request_response_free(response); obs_websocket_request_response_free(response);
} }
// Test vendor creation // Test vendor creation
auto vendor = obs_websocket_register_vendor("obs-websocket-test"); auto vendor = obs_websocket_register_vendor("obs-websocket-test");
if (!vendor) { if (!vendor) {
blog(LOG_WARNING, blog(LOG_WARNING, "[obs_module_post_load] Failed to create vendor!");
"[obs_module_post_load] Failed to create vendor!");
return; return;
} }
// Test vendor request registration // Test vendor request registration
if (!obs_websocket_vendor_register_request( if (!obs_websocket_vendor_register_request(vendor, "TestRequest", test_vendor_request_cb, vendor)) {
vendor, "TestRequest", test_vendor_request_cb, vendor)) { blog(LOG_WARNING, "[obs_module_post_load] Failed to register vendor request!");
blog(LOG_WARNING,
"[obs_module_post_load] Failed to register vendor request!");
return; return;
} }

View File

@ -36,8 +36,7 @@ struct SerialFrameBatch {
std::mutex conditionMutex; std::mutex conditionMutex;
std::condition_variable condition; std::condition_variable condition;
SerialFrameBatch(RequestHandler &requestHandler, json &variables, SerialFrameBatch(RequestHandler &requestHandler, json &variables, bool haltOnFailure)
bool haltOnFailure)
: requestHandler(requestHandler), : requestHandler(requestHandler),
variables(variables), variables(variables),
haltOnFailure(haltOnFailure), haltOnFailure(haltOnFailure),
@ -54,18 +53,14 @@ struct ParallelBatchResults {
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, static void PreProcessVariables(const json &variables, RequestBatchRequest &request)
RequestBatchRequest &request)
{ {
if (variables.empty() || !request.InputVariables.is_object() || if (variables.empty() || !request.InputVariables.is_object() || request.InputVariables.empty() ||
request.InputVariables.empty() || !request.RequestData.is_object()) !request.RequestData.is_object())
return; return;
for (auto &[key, value] : request.InputVariables.items()) { for (auto &[key, value] : request.InputVariables.items()) {
@ -91,13 +86,9 @@ static void PreProcessVariables(const json &variables,
} }
// `{"sceneItemIdVariable": "sceneItemId"}` is essentially `sceneItemIdVariable = sceneItemId` // `{"sceneItemIdVariable": "sceneItemId"}` is essentially `sceneItemIdVariable = sceneItemId`
static void PostProcessVariables(json &variables, static void PostProcessVariables(json &variables, const RequestBatchRequest &request, const RequestResult &requestResult)
const RequestBatchRequest &request,
const RequestResult &requestResult)
{ {
if (!request.OutputVariables.is_object() || if (!request.OutputVariables.is_object() || request.OutputVariables.empty() || requestResult.ResponseData.empty())
request.OutputVariables.empty() ||
requestResult.ResponseData.empty())
return; return;
for (auto &[key, value] : request.OutputVariables.items()) { for (auto &[key, value] : request.OutputVariables.items()) {
@ -130,8 +121,7 @@ static void ObsTickCallback(void *param, float)
serialFrameBatch->frameCount++; serialFrameBatch->frameCount++;
if (serialFrameBatch->sleepUntilFrame) { if (serialFrameBatch->sleepUntilFrame) {
if (serialFrameBatch->frameCount < if (serialFrameBatch->frameCount < serialFrameBatch->sleepUntilFrame)
serialFrameBatch->sleepUntilFrame)
// Do not process any requests if in "sleep mode" // Do not process any requests if in "sleep mode"
return; return;
else else
@ -142,35 +132,27 @@ static void ObsTickCallback(void *param, float)
// Begin recursing any unprocessed requests // Begin recursing any unprocessed requests
while (!serialFrameBatch->requests.empty()) { while (!serialFrameBatch->requests.empty()) {
// Fetch first in queue // Fetch first in queue
RequestBatchRequest request = RequestBatchRequest request = serialFrameBatch->requests.front();
serialFrameBatch->requests.front();
// Pre-process batch variables // Pre-process batch variables
PreProcessVariables(serialFrameBatch->variables, request); PreProcessVariables(serialFrameBatch->variables, request);
// Process request and get result // Process request and get result
RequestResult requestResult = RequestResult requestResult = serialFrameBatch->requestHandler.ProcessRequest(request);
serialFrameBatch->requestHandler.ProcessRequest(
request);
// Post-process batch variables // Post-process batch variables
PostProcessVariables(serialFrameBatch->variables, request, PostProcessVariables(serialFrameBatch->variables, request, requestResult);
requestResult);
// Add to results vector // Add to results vector
serialFrameBatch->results.push_back(requestResult); serialFrameBatch->results.push_back(requestResult);
// Remove from front of queue // Remove from front of queue
serialFrameBatch->requests.pop(); serialFrameBatch->requests.pop();
// If haltOnFailure and the request failed, clear the queue to make the batch return early. // If haltOnFailure and the request failed, clear the queue to make the batch return early.
if (serialFrameBatch->haltOnFailure && if (serialFrameBatch->haltOnFailure && requestResult.StatusCode != RequestStatus::Success) {
requestResult.StatusCode != RequestStatus::Success) { serialFrameBatch->requests = std::queue<RequestBatchRequest>();
serialFrameBatch->requests =
std::queue<RequestBatchRequest>();
break; break;
} }
// If the processed request tells us to sleep, do so accordingly // If the processed request tells us to sleep, do so accordingly
if (requestResult.SleepFrames) { if (requestResult.SleepFrames) {
serialFrameBatch->sleepUntilFrame = serialFrameBatch->sleepUntilFrame = serialFrameBatch->frameCount + requestResult.SleepFrames;
serialFrameBatch->frameCount +
requestResult.SleepFrames;
break; break;
} }
} }
@ -180,11 +162,10 @@ static void ObsTickCallback(void *param, float)
serialFrameBatch->condition.notify_one(); serialFrameBatch->condition.notify_one();
} }
std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch( std::vector<RequestResult>
QThreadPool &threadPool, SessionPtr session, RequestBatchHandler::ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session,
RequestBatchExecutionType::RequestBatchExecutionType executionType, RequestBatchExecutionType::RequestBatchExecutionType executionType,
std::vector<RequestBatchRequest> &requests, json &variables, std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure)
bool haltOnFailure)
{ {
RequestHandler requestHandler(session); RequestHandler requestHandler(session);
if (executionType == RequestBatchExecutionType::SerialRealtime) { if (executionType == RequestBatchExecutionType::SerialRealtime) {
@ -194,22 +175,19 @@ std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(
for (auto &request : requests) { for (auto &request : requests) {
PreProcessVariables(variables, request); PreProcessVariables(variables, request);
RequestResult requestResult = RequestResult requestResult = requestHandler.ProcessRequest(request);
requestHandler.ProcessRequest(request);
PostProcessVariables(variables, request, requestResult); PostProcessVariables(variables, request, requestResult);
ret.push_back(requestResult); ret.push_back(requestResult);
if (haltOnFailure && if (haltOnFailure && requestResult.StatusCode != RequestStatus::Success)
requestResult.StatusCode != RequestStatus::Success)
break; break;
} }
return ret; return ret;
} else if (executionType == RequestBatchExecutionType::SerialFrame) { } else if (executionType == RequestBatchExecutionType::SerialFrame) {
SerialFrameBatch serialFrameBatch(requestHandler, variables, SerialFrameBatch serialFrameBatch(requestHandler, variables, haltOnFailure);
haltOnFailure);
// Create Request objects in the worker thread (avoid unnecessary processing in graphics thread) // Create Request objects in the worker thread (avoid unnecessary processing in graphics thread)
for (auto &request : requests) for (auto &request : requests)
@ -219,11 +197,8 @@ std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(
obs_add_tick_callback(ObsTickCallback, &serialFrameBatch); obs_add_tick_callback(ObsTickCallback, &serialFrameBatch);
// 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( std::unique_lock<std::mutex> lock(serialFrameBatch.conditionMutex);
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);
@ -233,31 +208,23 @@ std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(
ParallelBatchResults parallelResults(requestHandler); ParallelBatchResults parallelResults(requestHandler);
// Acquire the lock early to prevent the batch from finishing before we're ready // Acquire the lock early to prevent the batch from finishing before we're ready
std::unique_lock<std::mutex> lock( std::unique_lock<std::mutex> lock(parallelResults.conditionMutex);
parallelResults.conditionMutex);
// Submit each request as a task to the thread pool to be processed ASAP // Submit each request as a task to the thread pool to be processed ASAP
for (auto &request : requests) { for (auto &request : requests) {
threadPool.start(Utils::Compat::CreateFunctionRunnable( threadPool.start(Utils::Compat::CreateFunctionRunnable([&parallelResults, &request]() {
[&parallelResults, &request]() { RequestResult requestResult = parallelResults.requestHandler.ProcessRequest(request);
RequestResult requestResult =
parallelResults.requestHandler
.ProcessRequest(
request);
std::unique_lock<std::mutex> lock( std::unique_lock<std::mutex> lock(parallelResults.conditionMutex);
parallelResults.conditionMutex); parallelResults.results.push_back(requestResult);
parallelResults.results.push_back( lock.unlock();
requestResult); parallelResults.condition.notify_one();
lock.unlock(); }));
parallelResults.condition.notify_one();
}));
} }
// 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, parallelResults.condition.wait(lock, [&parallelResults, requestCount] {
requestCount] {
return parallelResults.results.size() == requestCount; return parallelResults.results.size() == requestCount;
}); });

View File

@ -24,9 +24,7 @@ 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( std::vector<RequestResult> ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session,
QThreadPool &threadPool, SessionPtr session, RequestBatchExecutionType::RequestBatchExecutionType executionType,
RequestBatchExecutionType::RequestBatchExecutionType executionType, std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure);
std::vector<RequestBatchRequest> &requests, json &variables,
bool haltOnFailure);
} }

View File

@ -31,16 +31,14 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"CallVendorRequest", &RequestHandler::CallVendorRequest}, {"CallVendorRequest", &RequestHandler::CallVendorRequest},
{"GetHotkeyList", &RequestHandler::GetHotkeyList}, {"GetHotkeyList", &RequestHandler::GetHotkeyList},
{"TriggerHotkeyByName", &RequestHandler::TriggerHotkeyByName}, {"TriggerHotkeyByName", &RequestHandler::TriggerHotkeyByName},
{"TriggerHotkeyByKeySequence", {"TriggerHotkeyByKeySequence", &RequestHandler::TriggerHotkeyByKeySequence},
&RequestHandler::TriggerHotkeyByKeySequence},
{"Sleep", &RequestHandler::Sleep}, {"Sleep", &RequestHandler::Sleep},
// Config // Config
{"GetPersistentData", &RequestHandler::GetPersistentData}, {"GetPersistentData", &RequestHandler::GetPersistentData},
{"SetPersistentData", &RequestHandler::SetPersistentData}, {"SetPersistentData", &RequestHandler::SetPersistentData},
{"GetSceneCollectionList", &RequestHandler::GetSceneCollectionList}, {"GetSceneCollectionList", &RequestHandler::GetSceneCollectionList},
{"SetCurrentSceneCollection", {"SetCurrentSceneCollection", &RequestHandler::SetCurrentSceneCollection},
&RequestHandler::SetCurrentSceneCollection},
{"CreateSceneCollection", &RequestHandler::CreateSceneCollection}, {"CreateSceneCollection", &RequestHandler::CreateSceneCollection},
{"GetProfileList", &RequestHandler::GetProfileList}, {"GetProfileList", &RequestHandler::GetProfileList},
{"SetCurrentProfile", &RequestHandler::SetCurrentProfile}, {"SetCurrentProfile", &RequestHandler::SetCurrentProfile},
@ -71,10 +69,8 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"CreateScene", &RequestHandler::CreateScene}, {"CreateScene", &RequestHandler::CreateScene},
{"RemoveScene", &RequestHandler::RemoveScene}, {"RemoveScene", &RequestHandler::RemoveScene},
{"SetSceneName", &RequestHandler::SetSceneName}, {"SetSceneName", &RequestHandler::SetSceneName},
{"GetSceneSceneTransitionOverride", {"GetSceneSceneTransitionOverride", &RequestHandler::GetSceneSceneTransitionOverride},
&RequestHandler::GetSceneSceneTransitionOverride}, {"SetSceneSceneTransitionOverride", &RequestHandler::SetSceneSceneTransitionOverride},
{"SetSceneSceneTransitionOverride",
&RequestHandler::SetSceneSceneTransitionOverride},
// Inputs // Inputs
{"GetInputList", &RequestHandler::GetInputList}, {"GetInputList", &RequestHandler::GetInputList},
@ -99,32 +95,23 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"SetInputAudioMonitorType", &RequestHandler::SetInputAudioMonitorType}, {"SetInputAudioMonitorType", &RequestHandler::SetInputAudioMonitorType},
{"GetInputAudioTracks", &RequestHandler::GetInputAudioTracks}, {"GetInputAudioTracks", &RequestHandler::GetInputAudioTracks},
{"SetInputAudioTracks", &RequestHandler::SetInputAudioTracks}, {"SetInputAudioTracks", &RequestHandler::SetInputAudioTracks},
{"GetInputPropertiesListPropertyItems", {"GetInputPropertiesListPropertyItems", &RequestHandler::GetInputPropertiesListPropertyItems},
&RequestHandler::GetInputPropertiesListPropertyItems}, {"PressInputPropertiesButton", &RequestHandler::PressInputPropertiesButton},
{"PressInputPropertiesButton",
&RequestHandler::PressInputPropertiesButton},
// Transitions // Transitions
{"GetTransitionKindList", &RequestHandler::GetTransitionKindList}, {"GetTransitionKindList", &RequestHandler::GetTransitionKindList},
{"GetSceneTransitionList", &RequestHandler::GetSceneTransitionList}, {"GetSceneTransitionList", &RequestHandler::GetSceneTransitionList},
{"GetCurrentSceneTransition", {"GetCurrentSceneTransition", &RequestHandler::GetCurrentSceneTransition},
&RequestHandler::GetCurrentSceneTransition}, {"SetCurrentSceneTransition", &RequestHandler::SetCurrentSceneTransition},
{"SetCurrentSceneTransition", {"SetCurrentSceneTransitionDuration", &RequestHandler::SetCurrentSceneTransitionDuration},
&RequestHandler::SetCurrentSceneTransition}, {"SetCurrentSceneTransitionSettings", &RequestHandler::SetCurrentSceneTransitionSettings},
{"SetCurrentSceneTransitionDuration", {"GetCurrentSceneTransitionCursor", &RequestHandler::GetCurrentSceneTransitionCursor},
&RequestHandler::SetCurrentSceneTransitionDuration}, {"TriggerStudioModeTransition", &RequestHandler::TriggerStudioModeTransition},
{"SetCurrentSceneTransitionSettings",
&RequestHandler::SetCurrentSceneTransitionSettings},
{"GetCurrentSceneTransitionCursor",
&RequestHandler::GetCurrentSceneTransitionCursor},
{"TriggerStudioModeTransition",
&RequestHandler::TriggerStudioModeTransition},
{"SetTBarPosition", &RequestHandler::SetTBarPosition}, {"SetTBarPosition", &RequestHandler::SetTBarPosition},
// Filters // Filters
{"GetSourceFilterList", &RequestHandler::GetSourceFilterList}, {"GetSourceFilterList", &RequestHandler::GetSourceFilterList},
{"GetSourceFilterDefaultSettings", {"GetSourceFilterDefaultSettings", &RequestHandler::GetSourceFilterDefaultSettings},
&RequestHandler::GetSourceFilterDefaultSettings},
{"CreateSourceFilter", &RequestHandler::CreateSourceFilter}, {"CreateSourceFilter", &RequestHandler::CreateSourceFilter},
{"RemoveSourceFilter", &RequestHandler::RemoveSourceFilter}, {"RemoveSourceFilter", &RequestHandler::RemoveSourceFilter},
{"SetSourceFilterName", &RequestHandler::SetSourceFilterName}, {"SetSourceFilterName", &RequestHandler::SetSourceFilterName},
@ -150,10 +137,8 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex}, {"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex},
{"GetSceneItemBlendMode", &RequestHandler::GetSceneItemBlendMode}, {"GetSceneItemBlendMode", &RequestHandler::GetSceneItemBlendMode},
{"SetSceneItemBlendMode", &RequestHandler::SetSceneItemBlendMode}, {"SetSceneItemBlendMode", &RequestHandler::SetSceneItemBlendMode},
{"GetSceneItemPrivateSettings", {"GetSceneItemPrivateSettings", &RequestHandler::GetSceneItemPrivateSettings},
&RequestHandler::GetSceneItemPrivateSettings}, {"SetSceneItemPrivateSettings", &RequestHandler::SetSceneItemPrivateSettings},
{"SetSceneItemPrivateSettings",
&RequestHandler::SetSceneItemPrivateSettings},
// Outputs // Outputs
{"GetVirtualCamStatus", &RequestHandler::GetVirtualCamStatus}, {"GetVirtualCamStatus", &RequestHandler::GetVirtualCamStatus},
@ -165,8 +150,7 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"StartReplayBuffer", &RequestHandler::StartReplayBuffer}, {"StartReplayBuffer", &RequestHandler::StartReplayBuffer},
{"StopReplayBuffer", &RequestHandler::StopReplayBuffer}, {"StopReplayBuffer", &RequestHandler::StopReplayBuffer},
{"SaveReplayBuffer", &RequestHandler::SaveReplayBuffer}, {"SaveReplayBuffer", &RequestHandler::SaveReplayBuffer},
{"GetLastReplayBufferReplay", {"GetLastReplayBufferReplay", &RequestHandler::GetLastReplayBufferReplay},
&RequestHandler::GetLastReplayBufferReplay},
// Stream // Stream
{"GetStreamStatus", &RequestHandler::GetStreamStatus}, {"GetStreamStatus", &RequestHandler::GetStreamStatus},
@ -193,8 +177,7 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
// Ui // Ui
{"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled}, {"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled},
{"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled}, {"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled},
{"OpenInputPropertiesDialog", {"OpenInputPropertiesDialog", &RequestHandler::OpenInputPropertiesDialog},
&RequestHandler::OpenInputPropertiesDialog},
{"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog}, {"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog},
{"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog}, {"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog},
{"GetMonitorList", &RequestHandler::GetMonitorList}, {"GetMonitorList", &RequestHandler::GetMonitorList},
@ -209,21 +192,16 @@ RequestResult RequestHandler::ProcessRequest(const Request &request)
#endif #endif
if (!request.RequestData.is_object() && !request.RequestData.is_null()) if (!request.RequestData.is_object() && !request.RequestData.is_null())
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object.");
RequestStatus::InvalidRequestFieldType,
"Your request data is not an object.");
if (request.RequestType.empty()) if (request.RequestType.empty())
return RequestResult::Error( return RequestResult::Error(RequestStatus::MissingRequestType, "Your request is missing a `requestType`");
RequestStatus::MissingRequestType,
"Your request is missing a `requestType`");
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, return RequestResult::Error(RequestStatus::UnknownRequestType, "Your request type is not valid.");
"Your request type is not valid.");
} }
return std::bind(handler, this, std::placeholders::_1)(request); return std::bind(handler, this, std::placeholders::_1)(request);

View File

@ -202,6 +202,5 @@ private:
RequestResult GetMonitorList(const Request &); RequestResult GetMonitorList(const Request &);
SessionPtr _session; SessionPtr _session;
static const std::unordered_map<std::string, RequestMethodHandler> static const std::unordered_map<std::string, RequestMethodHandler> _handlerMap;
_handlerMap;
}; };

View File

@ -41,30 +41,24 @@ RequestResult RequestHandler::GetPersistentData(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!(request.ValidateString("realm", statusCode, comment) && if (!(request.ValidateString("realm", statusCode, comment) && request.ValidateString("slotName", statusCode, comment)))
request.ValidateString("slotName", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string realm = request.RequestData["realm"]; std::string realm = request.RequestData["realm"];
std::string slotName = request.RequestData["slotName"]; std::string slotName = request.RequestData["slotName"];
std::string persistentDataPath = std::string persistentDataPath = Utils::Obs::StringHelper::GetCurrentProfilePath();
Utils::Obs::StringHelper::GetCurrentProfilePath();
if (realm == "OBS_WEBSOCKET_DATA_REALM_GLOBAL") if (realm == "OBS_WEBSOCKET_DATA_REALM_GLOBAL")
persistentDataPath += persistentDataPath += "/../../../obsWebSocketPersistentData.json";
"/../../../obsWebSocketPersistentData.json";
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( return RequestResult::Error(RequestStatus::ResourceNotFound,
RequestStatus::ResourceNotFound, "You have specified an invalid persistent data realm.");
"You have specified an invalid persistent data realm.");
json responseData; json responseData;
json persistentData; json persistentData;
if (Utils::Json::GetJsonFileContent(persistentDataPath, if (Utils::Json::GetJsonFileContent(persistentDataPath, persistentData) && persistentData.contains(slotName))
persistentData) &&
persistentData.contains(slotName))
responseData["slotValue"] = persistentData[slotName]; responseData["slotValue"] = persistentData[slotName];
else else
responseData["slotValue"] = nullptr; responseData["slotValue"] = nullptr;
@ -90,8 +84,7 @@ RequestResult RequestHandler::SetPersistentData(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!(request.ValidateString("realm", statusCode, comment) && if (!(request.ValidateString("realm", statusCode, comment) && request.ValidateString("slotName", statusCode, comment) &&
request.ValidateString("slotName", statusCode, comment) &&
request.ValidateBasic("slotValue", statusCode, comment))) request.ValidateBasic("slotValue", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -99,26 +92,21 @@ RequestResult RequestHandler::SetPersistentData(const Request &request)
std::string slotName = request.RequestData["slotName"]; std::string slotName = request.RequestData["slotName"];
json slotValue = request.RequestData["slotValue"]; json slotValue = request.RequestData["slotValue"];
std::string persistentDataPath = std::string persistentDataPath = Utils::Obs::StringHelper::GetCurrentProfilePath();
Utils::Obs::StringHelper::GetCurrentProfilePath();
if (realm == "OBS_WEBSOCKET_DATA_REALM_GLOBAL") if (realm == "OBS_WEBSOCKET_DATA_REALM_GLOBAL")
persistentDataPath += persistentDataPath += "/../../../obsWebSocketPersistentData.json";
"/../../../obsWebSocketPersistentData.json";
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( return RequestResult::Error(RequestStatus::ResourceNotFound,
RequestStatus::ResourceNotFound, "You have specified an invalid persistent data realm.");
"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, if (!Utils::Json::SetJsonFileContent(persistentDataPath, persistentData))
persistentData)) return RequestResult::Error(RequestStatus::RequestProcessingFailed,
return RequestResult::Error( "Unable to write persistent data. No permissions?");
RequestStatus::RequestProcessingFailed,
"Unable to write persistent data. No permissions?");
return RequestResult::Success(); return RequestResult::Success();
} }
@ -139,10 +127,8 @@ RequestResult RequestHandler::SetPersistentData(const Request &request)
RequestResult RequestHandler::GetSceneCollectionList(const Request &) RequestResult RequestHandler::GetSceneCollectionList(const Request &)
{ {
json responseData; json responseData;
responseData["currentSceneCollectionName"] = responseData["currentSceneCollectionName"] = Utils::Obs::StringHelper::GetCurrentSceneCollection();
Utils::Obs::StringHelper::GetCurrentSceneCollection(); responseData["sceneCollections"] = Utils::Obs::ArrayHelper::GetSceneCollectionList();
responseData["sceneCollections"] =
Utils::Obs::ArrayHelper::GetSceneCollectionList();
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -167,25 +153,18 @@ RequestResult RequestHandler::SetCurrentSceneCollection(const Request &request)
if (!request.ValidateString("sceneCollectionName", statusCode, comment)) if (!request.ValidateString("sceneCollectionName", statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string sceneCollectionName = std::string sceneCollectionName = request.RequestData["sceneCollectionName"];
request.RequestData["sceneCollectionName"];
auto sceneCollections = auto sceneCollections = Utils::Obs::ArrayHelper::GetSceneCollectionList();
Utils::Obs::ArrayHelper::GetSceneCollectionList(); if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) == sceneCollections.end())
if (std::find(sceneCollections.begin(), sceneCollections.end(),
sceneCollectionName) == sceneCollections.end())
return RequestResult::Error(RequestStatus::ResourceNotFound); return RequestResult::Error(RequestStatus::ResourceNotFound);
std::string currentSceneCollectionName = std::string currentSceneCollectionName = Utils::Obs::StringHelper::GetCurrentSceneCollection();
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_queue_task(
OBS_TASK_UI, OBS_TASK_UI,
[](void *param) { [](void *param) { obs_frontend_set_current_scene_collection(static_cast<const char *>(param)); },
obs_frontend_set_current_scene_collection(
static_cast<const char *>(param));
},
(void *)sceneCollectionName.c_str(), true); (void *)sceneCollectionName.c_str(), true);
} }
@ -213,27 +192,18 @@ RequestResult RequestHandler::CreateSceneCollection(const Request &request)
if (!request.ValidateString("sceneCollectionName", statusCode, comment)) if (!request.ValidateString("sceneCollectionName", statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string sceneCollectionName = std::string sceneCollectionName = request.RequestData["sceneCollectionName"];
request.RequestData["sceneCollectionName"];
auto sceneCollections = auto sceneCollections = Utils::Obs::ArrayHelper::GetSceneCollectionList();
Utils::Obs::ArrayHelper::GetSceneCollectionList(); if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) != sceneCollections.end())
if (std::find(sceneCollections.begin(), sceneCollections.end(), return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
sceneCollectionName) != sceneCollections.end())
return RequestResult::Error(
RequestStatus::ResourceAlreadyExists);
QMainWindow *mainWindow = QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
static_cast<QMainWindow *>(obs_frontend_get_main_window());
bool success = false; bool success = false;
QMetaObject::invokeMethod( QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success),
mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
Q_RETURN_ARG(bool, success), Q_ARG(bool, true),
Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
if (!success) if (!success)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene collection.");
RequestStatus::ResourceCreationFailed,
"Failed to create the scene collection.");
return RequestResult::Success(); return RequestResult::Success();
} }
@ -254,8 +224,7 @@ RequestResult RequestHandler::CreateSceneCollection(const Request &request)
RequestResult RequestHandler::GetProfileList(const Request &) RequestResult RequestHandler::GetProfileList(const Request &)
{ {
json responseData; json responseData;
responseData["currentProfileName"] = responseData["currentProfileName"] = Utils::Obs::StringHelper::GetCurrentProfile();
Utils::Obs::StringHelper::GetCurrentProfile();
responseData["profiles"] = Utils::Obs::ArrayHelper::GetProfileList(); responseData["profiles"] = Utils::Obs::ArrayHelper::GetProfileList();
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -282,20 +251,14 @@ RequestResult RequestHandler::SetCurrentProfile(const Request &request)
std::string profileName = request.RequestData["profileName"]; std::string profileName = request.RequestData["profileName"];
auto profiles = Utils::Obs::ArrayHelper::GetProfileList(); auto profiles = Utils::Obs::ArrayHelper::GetProfileList();
if (std::find(profiles.begin(), profiles.end(), profileName) == if (std::find(profiles.begin(), profiles.end(), profileName) == profiles.end())
profiles.end())
return RequestResult::Error(RequestStatus::ResourceNotFound); return RequestResult::Error(RequestStatus::ResourceNotFound);
std::string currentProfileName = std::string currentProfileName = Utils::Obs::StringHelper::GetCurrentProfile();
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_queue_task(
OBS_TASK_UI, OBS_TASK_UI, [](void *param) { obs_frontend_set_current_profile(static_cast<const char *>(param)); },
[](void *param) {
obs_frontend_set_current_profile(
static_cast<const char *>(param));
},
(void *)profileName.c_str(), true); (void *)profileName.c_str(), true);
} }
@ -324,16 +287,12 @@ RequestResult RequestHandler::CreateProfile(const Request &request)
std::string profileName = request.RequestData["profileName"]; std::string profileName = request.RequestData["profileName"];
auto profiles = Utils::Obs::ArrayHelper::GetProfileList(); auto profiles = Utils::Obs::ArrayHelper::GetProfileList();
if (std::find(profiles.begin(), profiles.end(), profileName) != if (std::find(profiles.begin(), profiles.end(), profileName) != profiles.end())
profiles.end()) return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
return RequestResult::Error(
RequestStatus::ResourceAlreadyExists);
QMainWindow *mainWindow = QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
static_cast<QMainWindow *>(obs_frontend_get_main_window()); QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection,
QMetaObject::invokeMethod( Q_ARG(QString, QString::fromStdString(profileName)));
mainWindow, "NewProfile", Qt::BlockingQueuedConnection,
Q_ARG(QString, QString::fromStdString(profileName)));
return RequestResult::Success(); return RequestResult::Success();
} }
@ -360,18 +319,15 @@ RequestResult RequestHandler::RemoveProfile(const Request &request)
std::string profileName = request.RequestData["profileName"]; std::string profileName = request.RequestData["profileName"];
auto profiles = Utils::Obs::ArrayHelper::GetProfileList(); auto profiles = Utils::Obs::ArrayHelper::GetProfileList();
if (std::find(profiles.begin(), profiles.end(), profileName) == if (std::find(profiles.begin(), profiles.end(), profileName) == profiles.end())
profiles.end())
return RequestResult::Error(RequestStatus::ResourceNotFound); return RequestResult::Error(RequestStatus::ResourceNotFound);
if (profiles.size() < 2) if (profiles.size() < 2)
return RequestResult::Error(RequestStatus::NotEnoughResources); return RequestResult::Error(RequestStatus::NotEnoughResources);
QMainWindow *mainWindow = QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
static_cast<QMainWindow *>(obs_frontend_get_main_window()); QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection,
QMetaObject::invokeMethod( Q_ARG(QString, QString::fromStdString(profileName)));
mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection,
Q_ARG(QString, QString::fromStdString(profileName)));
return RequestResult::Success(); return RequestResult::Success();
} }
@ -396,36 +352,25 @@ RequestResult RequestHandler::GetProfileParameter(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!(request.ValidateString("parameterCategory", statusCode, if (!(request.ValidateString("parameterCategory", statusCode, comment) &&
comment) &&
request.ValidateString("parameterName", statusCode, comment))) request.ValidateString("parameterName", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string parameterCategory = std::string parameterCategory = request.RequestData["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, blog(LOG_ERROR, "[RequestHandler::GetProfileParameter] Profile is invalid.");
"[RequestHandler::GetProfileParameter] Profile is invalid.");
json responseData; json responseData;
if (config_has_default_value(profile, parameterCategory.c_str(), if (config_has_default_value(profile, parameterCategory.c_str(), parameterName.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"] = responseData["defaultParameterValue"] =
config_get_default_string(profile, config_get_default_string(profile, parameterCategory.c_str(), parameterName.c_str());
parameterCategory.c_str(), } else if (config_has_user_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
parameterName.c_str()); responseData["parameterValue"] = config_get_string(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["defaultParameterValue"] = nullptr; responseData["defaultParameterValue"] = nullptr;
} else { } else {
responseData["parameterValue"] = nullptr; responseData["parameterValue"] = nullptr;
@ -453,35 +398,25 @@ RequestResult RequestHandler::SetProfileParameter(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!(request.ValidateString("parameterCategory", statusCode, if (!(request.ValidateString("parameterCategory", statusCode, comment) &&
comment) &&
request.ValidateString("parameterName", statusCode, comment))) request.ValidateString("parameterName", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string parameterCategory = std::string parameterCategory = request.RequestData["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") || if (!request.RequestData.contains("parameterValue") || request.RequestData["parameterValue"].is_null()) {
request.RequestData["parameterValue"].is_null()) { if (!config_remove_value(profile, parameterCategory.c_str(), parameterName.c_str()))
if (!config_remove_value(profile, parameterCategory.c_str(), return RequestResult::Error(RequestStatus::ResourceNotFound,
parameterName.c_str())) "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 = std::string parameterValue = request.RequestData["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());
} else { } else {
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The field `parameterValue` must be a string.");
RequestStatus::InvalidRequestFieldType,
"The field `parameterValue` must be a string.");
} }
config_save(profile); config_save(profile);
@ -512,9 +447,7 @@ 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))
return RequestResult::Error( return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to get internal OBS video info.");
RequestStatus::RequestProcessingFailed,
"Unable to get internal OBS video info.");
json responseData; json responseData;
responseData["fpsNumerator"] = ovi.fps_num; responseData["fpsNumerator"] = ovi.fps_num;
@ -549,60 +482,42 @@ RequestResult RequestHandler::GetVideoSettings(const Request &)
RequestResult RequestHandler::SetVideoSettings(const Request &request) RequestResult RequestHandler::SetVideoSettings(const Request &request)
{ {
if (obs_video_active()) if (obs_video_active())
return RequestResult::Error( return RequestResult::Error(RequestStatus::OutputRunning,
RequestStatus::OutputRunning, "Video settings cannot be changed while an output is active.");
"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") && bool changeFps = (request.Contains("fpsNumerator") && request.Contains("fpsDenominator"));
request.Contains("fpsDenominator")); if (changeFps && !(request.ValidateOptionalNumber("fpsNumerator", statusCode, comment, 1) &&
if (changeFps && !(request.ValidateOptionalNumber( request.ValidateOptionalNumber("fpsDenominator", statusCode, comment, 1)))
"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") && bool changeBaseRes = (request.Contains("baseWidth") && request.Contains("baseHeight"));
request.Contains("baseHeight")); if (changeBaseRes && !(request.ValidateOptionalNumber("baseWidth", statusCode, comment, 8, 4096) &&
if (changeBaseRes && request.ValidateOptionalNumber("baseHeight", statusCode, comment, 8, 4096)))
!(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") && bool changeOutputRes = (request.Contains("outputWidth") && request.Contains("outputHeight"));
request.Contains("outputHeight")); if (changeOutputRes && !(request.ValidateOptionalNumber("outputWidth", statusCode, comment, 8, 4096) &&
if (changeOutputRes && request.ValidateOptionalNumber("outputHeight", statusCode, comment, 8, 4096)))
!(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();
if (changeFps) { if (changeFps) {
config_set_uint(config, "Video", "FPSType", 2); config_set_uint(config, "Video", "FPSType", 2);
config_set_uint(config, "Video", "FPSNum", config_set_uint(config, "Video", "FPSNum", request.RequestData["fpsNumerator"]);
request.RequestData["fpsNumerator"]); config_set_uint(config, "Video", "FPSDen", request.RequestData["fpsDenominator"]);
config_set_uint(config, "Video", "FPSDen",
request.RequestData["fpsDenominator"]);
} }
if (changeBaseRes) { if (changeBaseRes) {
config_set_uint(config, "Video", "BaseCX", config_set_uint(config, "Video", "BaseCX", request.RequestData["baseWidth"]);
request.RequestData["baseWidth"]); config_set_uint(config, "Video", "BaseCY", request.RequestData["baseHeight"]);
config_set_uint(config, "Video", "BaseCY",
request.RequestData["baseHeight"]);
} }
if (changeOutputRes) { if (changeOutputRes) {
config_set_uint(config, "Video", "OutputCX", config_set_uint(config, "Video", "OutputCX", request.RequestData["outputWidth"]);
request.RequestData["outputWidth"]); config_set_uint(config, "Video", "OutputCY", request.RequestData["outputHeight"]);
config_set_uint(config, "Video", "OutputCY",
request.RequestData["outputHeight"]);
} }
if (changeFps || changeBaseRes || changeOutputRes) { if (changeFps || changeBaseRes || changeOutputRes) {
@ -611,9 +526,7 @@ RequestResult RequestHandler::SetVideoSettings(const Request &request)
return RequestResult::Success(); return RequestResult::Success();
} }
return RequestResult::Error( return RequestResult::Error(RequestStatus::MissingRequestField, "You must specify at least one video-changing pair.");
RequestStatus::MissingRequestField,
"You must specify at least one video-changing pair.");
} }
/** /**
@ -636,8 +549,7 @@ RequestResult RequestHandler::GetStreamServiceSettings(const Request &)
OBSService service = obs_frontend_get_streaming_service(); OBSService service = obs_frontend_get_streaming_service();
responseData["streamServiceType"] = obs_service_get_type(service); responseData["streamServiceType"] = obs_service_get_type(service);
OBSDataAutoRelease serviceSettings = obs_service_get_settings(service); OBSDataAutoRelease serviceSettings = obs_service_get_settings(service);
responseData["streamServiceSettings"] = responseData["streamServiceSettings"] = Utils::Json::ObsDataToJson(serviceSettings, true);
Utils::Json::ObsDataToJson(serviceSettings, true);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -660,48 +572,36 @@ RequestResult RequestHandler::GetStreamServiceSettings(const Request &)
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( return RequestResult::Error(RequestStatus::OutputRunning,
RequestStatus::OutputRunning, "You cannot change stream service settings while streaming.");
"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, if (!(request.ValidateString("streamServiceType", statusCode, comment) &&
comment) && request.ValidateObject("streamServiceSettings", 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 = std::string streamServiceType = obs_service_get_type(currentStreamService);
obs_service_get_type(currentStreamService); std::string requestedStreamServiceType = request.RequestData["streamServiceType"];
std::string requestedStreamServiceType =
request.RequestData["streamServiceType"];
OBSDataAutoRelease requestedStreamServiceSettings = OBSDataAutoRelease requestedStreamServiceSettings =
Utils::Json::JsonToObsData( Utils::Json::JsonToObsData(request.RequestData["streamServiceSettings"]);
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) {
OBSDataAutoRelease currentStreamServiceSettings = OBSDataAutoRelease currentStreamServiceSettings = obs_service_get_settings(currentStreamService);
obs_service_get_settings(currentStreamService);
// TODO: Add `overlay` field // TODO: Add `overlay` field
OBSDataAutoRelease newStreamServiceSettings = obs_data_create(); OBSDataAutoRelease newStreamServiceSettings = obs_data_create();
obs_data_apply(newStreamServiceSettings, obs_data_apply(newStreamServiceSettings, currentStreamServiceSettings);
currentStreamServiceSettings); obs_data_apply(newStreamServiceSettings, requestedStreamServiceSettings);
obs_data_apply(newStreamServiceSettings,
requestedStreamServiceSettings);
obs_service_update(currentStreamService, obs_service_update(currentStreamService, newStreamServiceSettings);
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( OBSService newStreamService = obs_service_create(requestedStreamServiceType.c_str(), "obs_websocket_custom_service",
requestedStreamServiceType.c_str(), requestedStreamServiceSettings, nullptr);
"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( return RequestResult::Error(
@ -731,8 +631,7 @@ RequestResult RequestHandler::SetStreamServiceSettings(const Request &request)
RequestResult RequestHandler::GetRecordDirectory(const Request &) RequestResult RequestHandler::GetRecordDirectory(const Request &)
{ {
json responseData; json responseData;
responseData["recordDirectory"] = responseData["recordDirectory"] = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }

View File

@ -37,14 +37,12 @@ RequestResult RequestHandler::GetSourceFilterList(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease source = OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
request.ValidateSource("sourceName", statusCode, comment);
if (!source) if (!source)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
json responseData; json responseData;
responseData["filters"] = responseData["filters"] = Utils::Obs::ArrayHelper::GetSourceFilterList(source);
Utils::Obs::ArrayHelper::GetSourceFilterList(source);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -63,8 +61,7 @@ RequestResult RequestHandler::GetSourceFilterList(const Request &request)
* @api requests * @api requests
* @category filters * @category filters
*/ */
RequestResult RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request &request)
RequestHandler::GetSourceFilterDefaultSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
@ -76,14 +73,12 @@ RequestHandler::GetSourceFilterDefaultSettings(const Request &request)
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); return RequestResult::Error(RequestStatus::InvalidFilterKind);
OBSDataAutoRelease defaultSettings = OBSDataAutoRelease defaultSettings = obs_get_source_defaults(filterKind.c_str());
obs_get_source_defaults(filterKind.c_str());
if (!defaultSettings) if (!defaultSettings)
return RequestResult::Error(RequestStatus::InvalidFilterKind); return RequestResult::Error(RequestStatus::InvalidFilterKind);
json responseData; json responseData;
responseData["defaultFilterSettings"] = responseData["defaultFilterSettings"] = Utils::Json::ObsDataToJson(defaultSettings, true);
Utils::Json::ObsDataToJson(defaultSettings, true);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -107,20 +102,15 @@ RequestResult RequestHandler::CreateSourceFilter(const Request &request)
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease source = OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
request.ValidateSource("sourceName", statusCode, comment); if (!(source && request.ValidateString("filterName", statusCode, comment) &&
if (!(source &&
request.ValidateString("filterName", statusCode, comment) &&
request.ValidateString("filterKind", 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"];
OBSSourceAutoRelease existingFilter = OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name(source, filterName.c_str());
obs_source_get_filter_by_name(source, filterName.c_str());
if (existingFilter) if (existingFilter)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A filter already exists by that name.");
RequestStatus::ResourceAlreadyExists,
"A filter already exists by that name.");
std::string filterKind = request.RequestData["filterKind"]; std::string filterKind = request.RequestData["filterKind"];
auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList(); auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList();
@ -131,22 +121,16 @@ RequestResult RequestHandler::CreateSourceFilter(const Request &request)
OBSDataAutoRelease filterSettings = nullptr; OBSDataAutoRelease filterSettings = nullptr;
if (request.Contains("filterSettings")) { if (request.Contains("filterSettings")) {
if (!request.ValidateOptionalObject("filterSettings", if (!request.ValidateOptionalObject("filterSettings", statusCode, comment, true))
statusCode, comment, true))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
filterSettings = Utils::Json::JsonToObsData( filterSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
request.RequestData["filterSettings"]);
} }
OBSSourceAutoRelease filter = OBSSourceAutoRelease filter = Utils::Obs::ActionHelper::CreateSourceFilter(source, filterName, filterKind, filterSettings);
Utils::Obs::ActionHelper::CreateSourceFilter(
source, filterName, filterKind, filterSettings);
if (!filter) if (!filter)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the filter failed.");
RequestStatus::ResourceCreationFailed,
"Creation of the filter failed.");
return RequestResult::Success(); return RequestResult::Success();
} }
@ -168,8 +152,7 @@ RequestResult RequestHandler::RemoveSourceFilter(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
statusCode, comment);
if (!pair.filter) if (!pair.filter)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -196,20 +179,15 @@ RequestResult RequestHandler::SetSourceFilterName(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
statusCode, comment); if (!pair.filter || !request.ValidateString("newFilterName", statusCode, comment))
if (!pair.filter ||
!request.ValidateString("newFilterName", statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string newFilterName = request.RequestData["newFilterName"]; std::string newFilterName = request.RequestData["newFilterName"];
OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name( OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name(pair.source, newFilterName.c_str());
pair.source, newFilterName.c_str());
if (existingFilter) if (existingFilter)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A filter already exists by that new name.");
RequestStatus::ResourceAlreadyExists,
"A filter already exists by that new name.");
obs_source_set_name(pair.filter, newFilterName.c_str()); obs_source_set_name(pair.filter, newFilterName.c_str());
@ -238,23 +216,19 @@ RequestResult RequestHandler::GetSourceFilter(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
statusCode, comment);
if (!pair.filter) if (!pair.filter)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
json responseData; json responseData;
responseData["filterEnabled"] = obs_source_enabled(pair.filter); responseData["filterEnabled"] = obs_source_enabled(pair.filter);
responseData["filterIndex"] = responseData["filterIndex"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(
Utils::Obs::NumberHelper::GetSourceFilterIndex( pair.source,
pair.source, pair.filter); // Todo: Use `GetSourceFilterlist` to select this filter maybe
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 = OBSDataAutoRelease filterSettings = obs_source_get_settings(pair.filter);
obs_source_get_settings(pair.filter); responseData["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
responseData["filterSettings"] =
Utils::Json::ObsDataToJson(filterSettings);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -277,16 +251,13 @@ RequestResult RequestHandler::SetSourceFilterIndex(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
statusCode, comment); if (!(pair.filter && request.ValidateNumber("filterIndex", statusCode, comment, 0, 8192)))
if (!(pair.filter && request.ValidateNumber("filterIndex", statusCode,
comment, 0, 8192)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
int filterIndex = request.RequestData["filterIndex"]; int filterIndex = request.RequestData["filterIndex"];
Utils::Obs::ActionHelper::SetSourceFilterIndex(pair.source, pair.filter, Utils::Obs::ActionHelper::SetSourceFilterIndex(pair.source, pair.filter, filterIndex);
filterIndex);
return RequestResult::Success(); return RequestResult::Success();
} }
@ -310,29 +281,24 @@ RequestResult RequestHandler::SetSourceFilterSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
statusCode, comment); if (!(pair.filter && request.ValidateObject("filterSettings", statusCode, comment, true)))
if (!(pair.filter && request.ValidateObject("filterSettings",
statusCode, comment, true)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
// Almost identical to SetInputSettings // Almost identical to SetInputSettings
bool overlay = true; bool overlay = true;
if (request.Contains("overlay")) { if (request.Contains("overlay")) {
if (!request.ValidateOptionalBoolean("overlay", statusCode, if (!request.ValidateOptionalBoolean("overlay", statusCode, comment))
comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
overlay = request.RequestData["overlay"]; overlay = request.RequestData["overlay"];
} }
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData( OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
request.RequestData["filterSettings"]);
if (!newSettings) if (!newSettings)
return RequestResult::Error( return RequestResult::Error(RequestStatus::RequestProcessingFailed,
RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
"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);
@ -362,10 +328,8 @@ RequestResult RequestHandler::SetSourceFilterEnabled(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
statusCode, comment); if (!(pair.filter && request.ValidateBoolean("filterEnabled", statusCode, comment)))
if (!(pair.filter &&
request.ValidateBoolean("filterEnabled", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
bool filterEnabled = request.RequestData["filterEnabled"]; bool filterEnabled = request.RequestData["filterEnabled"];

View File

@ -52,8 +52,7 @@ RequestResult RequestHandler::GetVersion(const Request &)
responseData["rpcVersion"] = OBS_WEBSOCKET_RPC_VERSION; responseData["rpcVersion"] = OBS_WEBSOCKET_RPC_VERSION;
responseData["availableRequests"] = GetRequestList(); responseData["availableRequests"] = GetRequestList();
QList<QByteArray> imageWriterFormats = QList<QByteArray> imageWriterFormats = QImageWriter::supportedImageFormats();
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());
@ -61,8 +60,7 @@ RequestResult RequestHandler::GetVersion(const Request &)
responseData["supportedImageFormats"] = supportedImageFormats; responseData["supportedImageFormats"] = supportedImageFormats;
responseData["platform"] = QSysInfo::productType().toStdString(); responseData["platform"] = QSysInfo::productType().toStdString();
responseData["platformDescription"] = responseData["platformDescription"] = QSysInfo::prettyProductName().toStdString();
QSysInfo::prettyProductName().toStdString();
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -94,10 +92,8 @@ RequestResult RequestHandler::GetStats(const Request &)
json responseData = Utils::Obs::ObjectHelper::GetStats(); json responseData = Utils::Obs::ObjectHelper::GetStats();
if (_session) { if (_session) {
responseData["webSocketSessionIncomingMessages"] = responseData["webSocketSessionIncomingMessages"] = _session->IncomingMessages();
_session->IncomingMessages(); responseData["webSocketSessionOutgoingMessages"] = _session->OutgoingMessages();
responseData["webSocketSessionOutgoingMessages"] =
_session->OutgoingMessages();
} else { } else {
responseData["webSocketSessionIncomingMessages"] = nullptr; responseData["webSocketSessionIncomingMessages"] = nullptr;
responseData["webSocketSessionOutgoingMessages"] = nullptr; responseData["webSocketSessionOutgoingMessages"] = nullptr;
@ -127,13 +123,9 @@ RequestResult RequestHandler::BroadcastCustomEvent(const Request &request)
auto webSocketServer = GetWebSocketServer(); auto webSocketServer = GetWebSocketServer();
if (!webSocketServer) if (!webSocketServer)
return RequestResult::Error( return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to send event due to internal error.");
RequestStatus::RequestProcessingFailed,
"Unable to send event due to internal error.");
webSocketServer->BroadcastEvent(EventSubscription::General, webSocketServer->BroadcastEvent(EventSubscription::General, "CustomEvent", request.RequestData["eventData"]);
"CustomEvent",
request.RequestData["eventData"]);
return RequestResult::Success(); return RequestResult::Success();
} }
@ -170,41 +162,32 @@ RequestResult RequestHandler::CallVendorRequest(const Request &request)
OBSDataAutoRelease requestData = obs_data_create(); OBSDataAutoRelease requestData = obs_data_create();
if (request.Contains("requestData")) { if (request.Contains("requestData")) {
if (!request.ValidateOptionalObject("requestData", statusCode, if (!request.ValidateOptionalObject("requestData", statusCode, comment))
comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
requestData = Utils::Json::JsonToObsData( requestData = Utils::Json::JsonToObsData(request.RequestData["requestData"]);
request.RequestData["requestData"]);
} }
OBSDataAutoRelease obsResponseData = obs_data_create(); OBSDataAutoRelease obsResponseData = obs_data_create();
auto webSocketApi = GetWebSocketApi(); auto webSocketApi = GetWebSocketApi();
if (!webSocketApi) if (!webSocketApi)
return RequestResult::Error( return RequestResult::Error(RequestStatus::RequestProcessingFailed,
RequestStatus::RequestProcessingFailed, "Unable to call request due to internal error.");
"Unable to call request due to internal error.");
auto ret = webSocketApi->PerformVendorRequest( auto ret = webSocketApi->PerformVendorRequest(vendorName, requestType, requestData, obsResponseData);
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( return RequestResult::Error(RequestStatus::ResourceNotFound, "No vendor was found by that name.");
RequestStatus::ResourceNotFound,
"No vendor was found by that name.");
case WebSocketApi::RequestReturnCode::NoVendorRequest: case WebSocketApi::RequestReturnCode::NoVendorRequest:
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceNotFound, "No request was found by that name.");
RequestStatus::ResourceNotFound,
"No request was found by that name.");
} }
json responseData; json responseData;
responseData["responseData"] = responseData["responseData"] = Utils::Json::ObsDataToJson(obsResponseData);
Utils::Json::ObsDataToJson(obsResponseData);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -247,12 +230,9 @@ RequestResult RequestHandler::TriggerHotkeyByName(const Request &request)
if (!request.ValidateString("hotkeyName", statusCode, comment)) if (!request.ValidateString("hotkeyName", statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
obs_hotkey_t *hotkey = Utils::Obs::SearchHelper::GetHotkeyByName( obs_hotkey_t *hotkey = Utils::Obs::SearchHelper::GetHotkeyByName(request.RequestData["hotkeyName"]);
request.RequestData["hotkeyName"]);
if (!hotkey) if (!hotkey)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceNotFound, "No hotkeys were found by that name.");
RequestStatus::ResourceNotFound,
"No hotkeys were found by that name.");
obs_hotkey_trigger_routed_callback(obs_hotkey_get_id(hotkey), true); obs_hotkey_trigger_routed_callback(obs_hotkey_get_id(hotkey), true);
@ -284,8 +264,7 @@ RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request &request)
std::string comment; std::string comment;
if (request.Contains("keyId")) { if (request.Contains("keyId")) {
if (!request.ValidateOptionalString("keyId", statusCode, if (!request.ValidateOptionalString("keyId", statusCode, comment))
comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string keyId = request.RequestData["keyId"]; std::string keyId = request.RequestData["keyId"];
@ -294,37 +273,28 @@ RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request &request)
statusCode = RequestStatus::NoError; statusCode = RequestStatus::NoError;
if (request.Contains("keyModifiers")) { if (request.Contains("keyModifiers")) {
if (!request.ValidateOptionalObject("keyModifiers", statusCode, if (!request.ValidateOptionalObject("keyModifiers", statusCode, comment, true))
comment, true))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
const json keyModifiersJson = const json keyModifiersJson = request.RequestData["keyModifiers"];
request.RequestData["keyModifiers"];
uint32_t keyModifiers = 0; uint32_t keyModifiers = 0;
if (keyModifiersJson.contains("shift") && if (keyModifiersJson.contains("shift") && keyModifiersJson["shift"].is_boolean() &&
keyModifiersJson["shift"].is_boolean() &&
keyModifiersJson["shift"].get<bool>()) keyModifiersJson["shift"].get<bool>())
keyModifiers |= INTERACT_SHIFT_KEY; keyModifiers |= INTERACT_SHIFT_KEY;
if (keyModifiersJson.contains("control") && if (keyModifiersJson.contains("control") && keyModifiersJson["control"].is_boolean() &&
keyModifiersJson["control"].is_boolean() &&
keyModifiersJson["control"].get<bool>()) keyModifiersJson["control"].get<bool>())
keyModifiers |= INTERACT_CONTROL_KEY; keyModifiers |= INTERACT_CONTROL_KEY;
if (keyModifiersJson.contains("alt") && if (keyModifiersJson.contains("alt") && keyModifiersJson["alt"].is_boolean() && keyModifiersJson["alt"].get<bool>())
keyModifiersJson["alt"].is_boolean() &&
keyModifiersJson["alt"].get<bool>())
keyModifiers |= INTERACT_ALT_KEY; keyModifiers |= INTERACT_ALT_KEY;
if (keyModifiersJson.contains("command") && if (keyModifiersJson.contains("command") && keyModifiersJson["command"].is_boolean() &&
keyModifiersJson["command"].is_boolean() &&
keyModifiersJson["command"].get<bool>()) keyModifiersJson["command"].get<bool>())
keyModifiers |= INTERACT_COMMAND_KEY; keyModifiers |= INTERACT_COMMAND_KEY;
combo.modifiers = keyModifiers; combo.modifiers = keyModifiers;
} }
if (!combo.modifiers && if (!combo.modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE))
(combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) return RequestResult::Error(RequestStatus::CannotAct,
return RequestResult::Error( "Your provided request fields cannot be used to trigger a hotkey.");
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);
@ -352,25 +322,19 @@ RequestResult RequestHandler::Sleep(const Request &request)
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (request.ExecutionType == if (request.ExecutionType == RequestBatchExecutionType::SerialRealtime) {
RequestBatchExecutionType::SerialRealtime) { if (!request.ValidateNumber("sleepMillis", statusCode, comment, 0, 50000))
if (!request.ValidateNumber("sleepMillis", statusCode, comment,
0, 50000))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
int64_t sleepMillis = request.RequestData["sleepMillis"]; int64_t sleepMillis = request.RequestData["sleepMillis"];
std::this_thread::sleep_for( std::this_thread::sleep_for(std::chrono::milliseconds(sleepMillis));
std::chrono::milliseconds(sleepMillis));
return RequestResult::Success(); return RequestResult::Success();
} else if (request.ExecutionType == } else if (request.ExecutionType == RequestBatchExecutionType::SerialFrame) {
RequestBatchExecutionType::SerialFrame) { if (!request.ValidateNumber("sleepFrames", statusCode, comment, 0, 10000))
if (!request.ValidateNumber("sleepFrames", statusCode, comment,
0, 10000))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
RequestResult ret = RequestResult::Success(); RequestResult ret = RequestResult::Success();
ret.SleepFrames = request.RequestData["sleepFrames"]; ret.SleepFrames = request.RequestData["sleepFrames"];
return ret; return ret;
} else { } else {
return RequestResult::Error( return RequestResult::Error(RequestStatus::UnsupportedRequestBatchExecutionType);
RequestStatus::UnsupportedRequestBatchExecutionType);
} }
} }

View File

@ -40,16 +40,14 @@ RequestResult RequestHandler::GetInputList(const Request &request)
if (request.Contains("inputKind")) { if (request.Contains("inputKind")) {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!request.ValidateOptionalString("inputKind", statusCode, if (!request.ValidateOptionalString("inputKind", statusCode, comment))
comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
inputKind = request.RequestData["inputKind"]; inputKind = request.RequestData["inputKind"];
} }
json responseData; json responseData;
responseData["inputs"] = responseData["inputs"] = Utils::Obs::ArrayHelper::GetInputList(inputKind);
Utils::Obs::ArrayHelper::GetInputList(inputKind);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -74,16 +72,14 @@ RequestResult RequestHandler::GetInputKindList(const Request &request)
if (request.Contains("unversioned")) { if (request.Contains("unversioned")) {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!request.ValidateOptionalBoolean("unversioned", statusCode, if (!request.ValidateOptionalBoolean("unversioned", statusCode, comment))
comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
unversioned = request.RequestData["unversioned"]; unversioned = request.RequestData["unversioned"];
} }
json responseData; json responseData;
responseData["inputKinds"] = responseData["inputKinds"] = Utils::Obs::ArrayHelper::GetInputKindList(unversioned);
Utils::Obs::ArrayHelper::GetInputKindList(unversioned);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -108,8 +104,7 @@ RequestResult RequestHandler::GetSpecialInputs(const Request &)
{ {
json responseData; json responseData;
std::vector<std::string> channels = {"desktop1", "desktop2", "mic1", std::vector<std::string> channels = {"desktop1", "desktop2", "mic1", "mic2", "mic3", "mic4"};
"mic2", "mic3", "mic4"};
size_t channelId = 1; size_t channelId = 1;
for (auto &channel : channels) { for (auto &channel : channels) {
@ -147,20 +142,15 @@ RequestResult RequestHandler::CreateInput(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease sceneSource = OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment);
request.ValidateScene("sceneName", statusCode, comment); if (!(sceneSource && request.ValidateString("inputName", statusCode, comment) &&
if (!(sceneSource &&
request.ValidateString("inputName", statusCode, comment) &&
request.ValidateString("inputKind", 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"];
OBSSourceAutoRelease existingInput = OBSSourceAutoRelease existingInput = obs_get_source_by_name(inputName.c_str());
obs_get_source_by_name(inputName.c_str());
if (existingInput) if (existingInput)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that input name.");
RequestStatus::ResourceAlreadyExists,
"A source already exists by that input name.");
std::string inputKind = request.RequestData["inputKind"]; std::string inputKind = request.RequestData["inputKind"];
auto kinds = Utils::Obs::ArrayHelper::GetInputKindList(); auto kinds = Utils::Obs::ArrayHelper::GetInputKindList();
@ -171,20 +161,17 @@ RequestResult RequestHandler::CreateInput(const Request &request)
OBSDataAutoRelease inputSettings = nullptr; OBSDataAutoRelease inputSettings = nullptr;
if (request.Contains("inputSettings")) { if (request.Contains("inputSettings")) {
if (!request.ValidateOptionalObject("inputSettings", statusCode, if (!request.ValidateOptionalObject("inputSettings", statusCode, comment, true))
comment, true))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
inputSettings = Utils::Json::JsonToObsData( inputSettings = Utils::Json::JsonToObsData(request.RequestData["inputSettings"]);
request.RequestData["inputSettings"]);
} }
OBSScene scene = obs_scene_from_source(sceneSource); OBSScene scene = obs_scene_from_source(sceneSource);
bool sceneItemEnabled = true; bool sceneItemEnabled = true;
if (request.Contains("sceneItemEnabled")) { if (request.Contains("sceneItemEnabled")) {
if (!request.ValidateOptionalBoolean("sceneItemEnabled", if (!request.ValidateOptionalBoolean("sceneItemEnabled", statusCode, comment))
statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemEnabled = request.RequestData["sceneItemEnabled"]; sceneItemEnabled = request.RequestData["sceneItemEnabled"];
@ -192,14 +179,10 @@ 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 = OBSSceneItemAutoRelease sceneItem =
Utils::Obs::ActionHelper::CreateInput(inputName, inputKind, Utils::Obs::ActionHelper::CreateInput(inputName, inputKind, inputSettings, scene, sceneItemEnabled);
inputSettings, scene,
sceneItemEnabled);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the input or scene item failed.");
RequestStatus::ResourceCreationFailed,
"Creation of the input or scene item failed.");
json responseData; json responseData;
responseData["sceneItemId"] = obs_sceneitem_get_id(sceneItem); responseData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
@ -224,8 +207,7 @@ RequestResult RequestHandler::RemoveInput(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -254,20 +236,16 @@ RequestResult RequestHandler::SetInputName(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateString("newInputName", statusCode, comment)))
if (!(input &&
request.ValidateString("newInputName", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string newInputName = request.RequestData["newInputName"]; std::string newInputName = request.RequestData["newInputName"];
OBSSourceAutoRelease existingSource = OBSSourceAutoRelease existingSource = obs_get_source_by_name(newInputName.c_str());
obs_get_source_by_name(newInputName.c_str());
if (existingSource) if (existingSource)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceAlreadyExists,
RequestStatus::ResourceAlreadyExists, "A source already exists by that new input name.");
"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());
@ -300,14 +278,12 @@ RequestResult RequestHandler::GetInputDefaultSettings(const Request &request)
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); return RequestResult::Error(RequestStatus::InvalidInputKind);
OBSDataAutoRelease defaultSettings = OBSDataAutoRelease defaultSettings = obs_get_source_defaults(inputKind.c_str());
obs_get_source_defaults(inputKind.c_str());
if (!defaultSettings) if (!defaultSettings)
return RequestResult::Error(RequestStatus::InvalidInputKind); return RequestResult::Error(RequestStatus::InvalidInputKind);
json responseData; json responseData;
responseData["defaultInputSettings"] = responseData["defaultInputSettings"] = Utils::Json::ObsDataToJson(defaultSettings, true);
Utils::Json::ObsDataToJson(defaultSettings, true);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -332,16 +308,14 @@ RequestResult RequestHandler::GetInputSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease inputSettings = obs_source_get_settings(input); OBSDataAutoRelease inputSettings = obs_source_get_settings(input);
json responseData; json responseData;
responseData["inputSettings"] = responseData["inputSettings"] = Utils::Json::ObsDataToJson(inputSettings);
Utils::Json::ObsDataToJson(inputSettings);
responseData["inputKind"] = obs_source_get_id(input); responseData["inputKind"] = obs_source_get_id(input);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -364,29 +338,24 @@ RequestResult RequestHandler::SetInputSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateObject("inputSettings", statusCode, comment, true)))
if (!(input && request.ValidateObject("inputSettings", statusCode,
comment, true)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
bool overlay = true; bool overlay = true;
if (request.Contains("overlay")) { if (request.Contains("overlay")) {
if (!request.ValidateOptionalBoolean("overlay", statusCode, if (!request.ValidateOptionalBoolean("overlay", statusCode, comment))
comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
overlay = request.RequestData["overlay"]; overlay = request.RequestData["overlay"];
} }
// Get the new settings and convert it to obs_data_t* // Get the new settings and convert it to obs_data_t*
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData( OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["inputSettings"]);
request.RequestData["inputSettings"]);
if (!newSettings) if (!newSettings)
// This should never happen // This should never happen
return RequestResult::Error( return RequestResult::Error(RequestStatus::RequestProcessingFailed,
RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
"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
@ -419,15 +388,12 @@ RequestResult RequestHandler::GetInputMute(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
json responseData; json responseData;
responseData["inputMuted"] = obs_source_muted(input); responseData["inputMuted"] = obs_source_muted(input);
@ -451,16 +417,12 @@ RequestResult RequestHandler::SetInputMute(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateBoolean("inputMuted", statusCode, comment)))
if (!(input &&
request.ValidateBoolean("inputMuted", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
obs_source_set_muted(input, request.RequestData["inputMuted"]); obs_source_set_muted(input, request.RequestData["inputMuted"]);
@ -485,15 +447,12 @@ RequestResult RequestHandler::ToggleInputMute(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
bool inputMuted = !obs_source_muted(input); bool inputMuted = !obs_source_muted(input);
obs_source_set_muted(input, inputMuted); obs_source_set_muted(input, inputMuted);
@ -522,15 +481,12 @@ RequestResult RequestHandler::GetInputVolume(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
float inputVolumeMul = obs_source_get_volume(input); float inputVolumeMul = obs_source_get_volume(input);
float inputVolumeDb = obs_mul_to_db(inputVolumeMul); float inputVolumeDb = obs_mul_to_db(inputVolumeMul);
@ -561,42 +517,32 @@ RequestResult RequestHandler::SetInputVolume(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
bool hasMul = request.Contains("inputVolumeMul"); bool hasMul = request.Contains("inputVolumeMul");
if (hasMul && !request.ValidateOptionalNumber( if (hasMul && !request.ValidateOptionalNumber("inputVolumeMul", statusCode, comment, 0, 20))
"inputVolumeMul", statusCode, comment, 0, 20))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
bool hasDb = request.Contains("inputVolumeDb"); bool hasDb = request.Contains("inputVolumeDb");
if (hasDb && !request.ValidateOptionalNumber( if (hasDb && !request.ValidateOptionalNumber("inputVolumeDb", statusCode, comment, -100, 26))
"inputVolumeDb", statusCode, comment, -100, 26))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (hasMul && hasDb) if (hasMul && hasDb)
return RequestResult::Error( return RequestResult::Error(RequestStatus::TooManyRequestFields, "You may only specify one volume field.");
RequestStatus::TooManyRequestFields,
"You may only specify one volume field.");
if (!hasMul && !hasDb) if (!hasMul && !hasDb)
return RequestResult::Error( return RequestResult::Error(RequestStatus::MissingRequestField, "You must specify one volume field.");
RequestStatus::MissingRequestField,
"You must specify one volume field.");
float inputVolumeMul; float inputVolumeMul;
if (hasMul) if (hasMul)
inputVolumeMul = request.RequestData["inputVolumeMul"]; inputVolumeMul = request.RequestData["inputVolumeMul"];
else else
inputVolumeMul = inputVolumeMul = obs_db_to_mul(request.RequestData["inputVolumeDb"]);
obs_db_to_mul(request.RequestData["inputVolumeDb"]);
obs_source_set_volume(input, inputVolumeMul); obs_source_set_volume(input, inputVolumeMul);
@ -621,15 +567,12 @@ RequestResult RequestHandler::GetInputAudioBalance(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
json responseData; json responseData;
responseData["inputAudioBalance"] = obs_source_get_balance_value(input); responseData["inputAudioBalance"] = obs_source_get_balance_value(input);
@ -654,16 +597,12 @@ RequestResult RequestHandler::SetInputAudioBalance(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateNumber("inputAudioBalance", statusCode, comment, 0.0, 1.0)))
if (!(input && request.ValidateNumber("inputAudioBalance", statusCode,
comment, 0.0, 1.0)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
float inputAudioBalance = request.RequestData["inputAudioBalance"]; float inputAudioBalance = request.RequestData["inputAudioBalance"];
obs_source_set_balance_value(input, inputAudioBalance); obs_source_set_balance_value(input, inputAudioBalance);
@ -691,20 +630,16 @@ RequestResult RequestHandler::GetInputAudioSyncOffset(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
json responseData; json responseData;
// Offset is stored in nanoseconds in OBS. // Offset is stored in nanoseconds in OBS.
responseData["inputAudioSyncOffset"] = responseData["inputAudioSyncOffset"] = obs_source_get_sync_offset(input) / 1000000;
obs_source_get_sync_offset(input) / 1000000;
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -726,17 +661,12 @@ RequestResult RequestHandler::SetInputAudioSyncOffset(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateNumber("inputAudioSyncOffset", statusCode, comment, -950, 20000)))
if (!(input &&
request.ValidateNumber("inputAudioSyncOffset", statusCode,
comment, -950, 20000)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
int64_t syncOffset = request.RequestData["inputAudioSyncOffset"]; int64_t syncOffset = request.RequestData["inputAudioSyncOffset"];
obs_source_set_sync_offset(input, syncOffset * 1000000); obs_source_set_sync_offset(input, syncOffset * 1000000);
@ -768,19 +698,15 @@ RequestResult RequestHandler::GetInputAudioMonitorType(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
json responseData; json responseData;
responseData["monitorType"] = responseData["monitorType"] = Utils::Obs::StringHelper::GetInputMonitorType(input);
Utils::Obs::StringHelper::GetInputMonitorType(input);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -802,21 +728,16 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateString("monitorType", statusCode, comment)))
if (!(input &&
request.ValidateString("monitorType", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
if (!obs_audio_monitoring_available()) if (!obs_audio_monitoring_available())
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState,
RequestStatus::InvalidResourceState, "Audio monitoring is not available on this platform.");
"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"];
@ -827,10 +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( return RequestResult::Error(RequestStatus::InvalidRequestField,
RequestStatus::InvalidRequestField, std::string("Unknown monitor type: ") + monitorTypeString);
std::string("Unknown monitor type: ") +
monitorTypeString);
obs_source_set_monitoring_type(input, monitorType); obs_source_set_monitoring_type(input, monitorType);
@ -855,22 +774,18 @@ RequestResult RequestHandler::GetInputAudioTracks(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
long long tracks = obs_source_get_audio_mixers(input); long long tracks = obs_source_get_audio_mixers(input);
json inputAudioTracks; json inputAudioTracks;
for (long long i = 0; i < MAX_AUDIO_MIXES; i++) { for (long long i = 0; i < MAX_AUDIO_MIXES; i++) {
inputAudioTracks[std::to_string(i + 1)] = inputAudioTracks[std::to_string(i + 1)] = (bool)((tracks >> i) & 1);
(bool)((tracks >> i) & 1);
} }
json responseData; json responseData;
@ -896,16 +811,12 @@ RequestResult RequestHandler::SetInputAudioTracks(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); if (!input || !request.ValidateObject("inputAudioTracks", statusCode, comment))
if (!input ||
!request.ValidateObject("inputAudioTracks", statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
RequestStatus::InvalidResourceState,
"The specified input does not support audio.");
json inputAudioTracks = request.RequestData["inputAudioTracks"]; json inputAudioTracks = request.RequestData["inputAudioTracks"];
@ -918,9 +829,8 @@ RequestResult RequestHandler::SetInputAudioTracks(const Request &request)
continue; continue;
if (!inputAudioTracks[track].is_boolean()) if (!inputAudioTracks[track].is_boolean())
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidRequestFieldType,
RequestStatus::InvalidRequestFieldType, "The value of one of your tracks is not a boolean.");
"The value of one of your tracks is not a boolean.");
bool enabled = inputAudioTracks[track]; bool enabled = inputAudioTracks[track];
@ -953,34 +863,25 @@ RequestResult RequestHandler::SetInputAudioTracks(const Request &request)
* @api requests * @api requests
* @category inputs * @category inputs
*/ */
RequestResult RequestResult RequestHandler::GetInputPropertiesListPropertyItems(const Request &request)
RequestHandler::GetInputPropertiesListPropertyItems(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateString("propertyName", statusCode, comment)))
if (!(input &&
request.ValidateString("propertyName", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string propertyName = request.RequestData["propertyName"]; std::string propertyName = request.RequestData["propertyName"];
OBSPropertiesAutoDestroy inputProperties = obs_source_properties(input); OBSPropertiesAutoDestroy inputProperties = obs_source_properties(input);
obs_property_t *property = obs_property_t *property = obs_properties_get(inputProperties, propertyName.c_str());
obs_properties_get(inputProperties, propertyName.c_str());
if (!property) if (!property)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceNotFound, "Unable to find a property by that name.");
RequestStatus::ResourceNotFound,
"Unable to find a property by that name.");
if (obs_property_get_type(property) != OBS_PROPERTY_LIST) if (obs_property_get_type(property) != OBS_PROPERTY_LIST)
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceType, "The property found is not a list.");
RequestStatus::InvalidResourceType,
"The property found is not a list.");
json responseData; json responseData;
responseData["propertyItems"] = responseData["propertyItems"] = Utils::Obs::ArrayHelper::GetListPropertyItems(property);
Utils::Obs::ArrayHelper::GetListPropertyItems(property);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -1004,29 +905,20 @@ RequestResult RequestHandler::PressInputPropertiesButton(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateString("propertyName", statusCode, comment)))
if (!(input &&
request.ValidateString("propertyName", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string propertyName = request.RequestData["propertyName"]; std::string propertyName = request.RequestData["propertyName"];
OBSPropertiesAutoDestroy inputProperties = obs_source_properties(input); OBSPropertiesAutoDestroy inputProperties = obs_source_properties(input);
obs_property_t *property = obs_property_t *property = obs_properties_get(inputProperties, propertyName.c_str());
obs_properties_get(inputProperties, propertyName.c_str());
if (!property) if (!property)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceNotFound, "Unable to find a property by that name.");
RequestStatus::ResourceNotFound,
"Unable to find a property by that name.");
if (obs_property_get_type(property) != OBS_PROPERTY_BUTTON) if (obs_property_get_type(property) != OBS_PROPERTY_BUTTON)
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceType, "The property found is not a button.");
RequestStatus::InvalidResourceType,
"The property found is not a button.");
if (!obs_property_enabled(property)) if (!obs_property_enabled(property))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState, "The property item found is not enabled.");
RequestStatus::InvalidResourceState,
"The property item found is not enabled.");
obs_property_button_clicked(property, input); obs_property_button_clicked(property, input);

View File

@ -22,8 +22,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
bool IsMediaTimeValid(obs_source_t *input) bool IsMediaTimeValid(obs_source_t *input)
{ {
auto mediaState = obs_source_media_get_state(input); auto mediaState = obs_source_media_get_state(input);
return mediaState == OBS_MEDIA_STATE_PLAYING || return mediaState == OBS_MEDIA_STATE_PLAYING || mediaState == OBS_MEDIA_STATE_PAUSED;
mediaState == OBS_MEDIA_STATE_PAUSED;
} }
/** /**
@ -57,18 +56,15 @@ RequestResult RequestHandler::GetMediaInputStatus(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
json responseData; json responseData;
responseData["mediaState"] = responseData["mediaState"] = Utils::Obs::StringHelper::GetMediaInputState(input);
Utils::Obs::StringHelper::GetMediaInputState(input);
if (IsMediaTimeValid(input)) { if (IsMediaTimeValid(input)) {
responseData["mediaDuration"] = responseData["mediaDuration"] = obs_source_media_get_duration(input);
obs_source_media_get_duration(input);
responseData["mediaCursor"] = obs_source_media_get_time(input); responseData["mediaCursor"] = obs_source_media_get_time(input);
} else { } else {
responseData["mediaDuration"] = nullptr; responseData["mediaDuration"] = nullptr;
@ -97,16 +93,13 @@ RequestResult RequestHandler::SetMediaInputCursor(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateNumber("mediaCursor", statusCode, comment, 0)))
if (!(input &&
request.ValidateNumber("mediaCursor", statusCode, comment, 0)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!IsMediaTimeValid(input)) if (!IsMediaTimeValid(input))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState,
RequestStatus::InvalidResourceState, "The media input must be playing or paused in order to set the cursor position.");
"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"];
@ -135,20 +128,16 @@ RequestResult RequestHandler::OffsetMediaInputCursor(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateNumber("mediaCursorOffset", statusCode, comment)))
if (!(input &&
request.ValidateNumber("mediaCursorOffset", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!IsMediaTimeValid(input)) if (!IsMediaTimeValid(input))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState,
RequestStatus::InvalidResourceState, "The media input must be playing or paused in order to set the cursor position.");
"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 = int64_t mediaCursor = obs_source_media_get_time(input) + mediaCursorOffset;
obs_source_media_get_time(input) + mediaCursorOffset;
if (mediaCursor < 0) if (mediaCursor < 0)
mediaCursor = 0; mediaCursor = 0;
@ -175,22 +164,18 @@ RequestResult RequestHandler::TriggerMediaInputAction(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment); 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"]; std::string mediaActionString = request.RequestData["mediaAction"];
auto mediaAction = auto mediaAction = Utils::Obs::EnumHelper::GetMediaInputAction(mediaActionString);
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( return RequestResult::Error(RequestStatus::InvalidRequestField,
RequestStatus::InvalidRequestField, "You have specified an invalid media input action.");
"You have specified an invalid media input action.");
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY: case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY:
// Shoutout to whoever implemented this API call like this // Shoutout to whoever implemented this API call like this
obs_source_media_play_pause(input, false); obs_source_media_play_pause(input, false);

View File

@ -49,8 +49,7 @@ static bool ReplayBufferAvailable()
RequestResult RequestHandler::GetVirtualCamStatus(const Request &) RequestResult RequestHandler::GetVirtualCamStatus(const Request &)
{ {
if (!VirtualCamAvailable()) if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
"VirtualCam is not available.");
json responseData; json responseData;
responseData["outputActive"] = obs_frontend_virtualcam_active(); responseData["outputActive"] = obs_frontend_virtualcam_active();
@ -72,8 +71,7 @@ RequestResult RequestHandler::GetVirtualCamStatus(const Request &)
RequestResult RequestHandler::ToggleVirtualCam(const Request &) RequestResult RequestHandler::ToggleVirtualCam(const Request &)
{ {
if (!VirtualCamAvailable()) if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
"VirtualCam is not available.");
bool outputActive = obs_frontend_virtualcam_active(); bool outputActive = obs_frontend_virtualcam_active();
@ -100,8 +98,7 @@ RequestResult RequestHandler::ToggleVirtualCam(const Request &)
RequestResult RequestHandler::StartVirtualCam(const Request &) RequestResult RequestHandler::StartVirtualCam(const Request &)
{ {
if (!VirtualCamAvailable()) if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
"VirtualCam is not available.");
if (obs_frontend_virtualcam_active()) if (obs_frontend_virtualcam_active())
return RequestResult::Error(RequestStatus::OutputRunning); return RequestResult::Error(RequestStatus::OutputRunning);
@ -124,8 +121,7 @@ RequestResult RequestHandler::StartVirtualCam(const Request &)
RequestResult RequestHandler::StopVirtualCam(const Request &) RequestResult RequestHandler::StopVirtualCam(const Request &)
{ {
if (!VirtualCamAvailable()) if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
"VirtualCam is not available.");
if (!obs_frontend_virtualcam_active()) if (!obs_frontend_virtualcam_active())
return RequestResult::Error(RequestStatus::OutputNotRunning); return RequestResult::Error(RequestStatus::OutputNotRunning);
@ -150,8 +146,7 @@ RequestResult RequestHandler::StopVirtualCam(const Request &)
RequestResult RequestHandler::GetReplayBufferStatus(const Request &) RequestResult RequestHandler::GetReplayBufferStatus(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
"Replay buffer is not available.");
json responseData; json responseData;
responseData["outputActive"] = obs_frontend_replay_buffer_active(); responseData["outputActive"] = obs_frontend_replay_buffer_active();
@ -173,8 +168,7 @@ RequestResult RequestHandler::GetReplayBufferStatus(const Request &)
RequestResult RequestHandler::ToggleReplayBuffer(const Request &) RequestResult RequestHandler::ToggleReplayBuffer(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
"Replay buffer is not available.");
bool outputActive = obs_frontend_replay_buffer_active(); bool outputActive = obs_frontend_replay_buffer_active();
@ -201,8 +195,7 @@ RequestResult RequestHandler::ToggleReplayBuffer(const Request &)
RequestResult RequestHandler::StartReplayBuffer(const Request &) RequestResult RequestHandler::StartReplayBuffer(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
"Replay buffer is not available.");
if (obs_frontend_replay_buffer_active()) if (obs_frontend_replay_buffer_active())
return RequestResult::Error(RequestStatus::OutputRunning); return RequestResult::Error(RequestStatus::OutputRunning);
@ -225,8 +218,7 @@ RequestResult RequestHandler::StartReplayBuffer(const Request &)
RequestResult RequestHandler::StopReplayBuffer(const Request &) RequestResult RequestHandler::StopReplayBuffer(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
"Replay buffer is not available.");
if (!obs_frontend_replay_buffer_active()) if (!obs_frontend_replay_buffer_active())
return RequestResult::Error(RequestStatus::OutputNotRunning); return RequestResult::Error(RequestStatus::OutputNotRunning);
@ -249,8 +241,7 @@ RequestResult RequestHandler::StopReplayBuffer(const Request &)
RequestResult RequestHandler::SaveReplayBuffer(const Request &) RequestResult RequestHandler::SaveReplayBuffer(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
"Replay buffer is not available.");
if (!obs_frontend_replay_buffer_active()) if (!obs_frontend_replay_buffer_active())
return RequestResult::Error(RequestStatus::OutputNotRunning); return RequestResult::Error(RequestStatus::OutputNotRunning);
@ -275,14 +266,12 @@ RequestResult RequestHandler::SaveReplayBuffer(const Request &)
RequestResult RequestHandler::GetLastReplayBufferReplay(const Request &) RequestResult RequestHandler::GetLastReplayBufferReplay(const Request &)
{ {
if (!ReplayBufferAvailable()) if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
"Replay buffer is not available.");
if (!obs_frontend_replay_buffer_active()) if (!obs_frontend_replay_buffer_active())
return RequestResult::Error(RequestStatus::OutputNotRunning); return RequestResult::Error(RequestStatus::OutputNotRunning);
json responseData; json responseData;
responseData["savedReplayPath"] = responseData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFilePath();
Utils::Obs::StringHelper::GetLastReplayBufferFilePath();
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }

View File

@ -39,17 +39,14 @@ RequestResult RequestHandler::GetRecordStatus(const Request &)
{ {
OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output(); OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output();
uint64_t outputDuration = uint64_t outputDuration = Utils::Obs::NumberHelper::GetOutputDuration(recordOutput);
Utils::Obs::NumberHelper::GetOutputDuration(recordOutput);
json responseData; json responseData;
responseData["outputActive"] = obs_output_active(recordOutput); responseData["outputActive"] = obs_output_active(recordOutput);
responseData["outputPaused"] = obs_output_paused(recordOutput); responseData["outputPaused"] = obs_output_paused(recordOutput);
responseData["outputTimecode"] = responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
responseData["outputDuration"] = outputDuration; responseData["outputDuration"] = outputDuration;
responseData["outputBytes"] = responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(recordOutput);
(uint64_t)obs_output_get_total_bytes(recordOutput);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }

View File

@ -39,14 +39,12 @@ RequestResult RequestHandler::GetSceneItemList(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease scene = OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
request.ValidateScene("sceneName", statusCode, comment);
if (!scene) if (!scene)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
json responseData; json responseData;
responseData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList( responseData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_from_source(scene));
obs_scene_from_source(scene));
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -73,15 +71,12 @@ RequestResult RequestHandler::GetGroupSceneItemList(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease scene = OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY);
request.ValidateScene("sceneName", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY);
if (!scene) if (!scene)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
json responseData; json responseData;
responseData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList( responseData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList(obs_group_from_source(scene));
obs_group_from_source(scene));
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -108,30 +103,24 @@ RequestResult RequestHandler::GetSceneItemId(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneAutoRelease scene = request.ValidateScene2( OBSSceneAutoRelease scene =
"sceneName", statusCode, comment, request.ValidateScene2("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
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);
std::string sourceName = request.RequestData["sourceName"]; std::string sourceName = request.RequestData["sourceName"];
int offset = 0; int offset = 0;
if (request.Contains("searchOffset")) { if (request.Contains("searchOffset")) {
if (!request.ValidateOptionalNumber("searchOffset", statusCode, if (!request.ValidateOptionalNumber("searchOffset", statusCode, comment, -1))
comment, -1))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
offset = request.RequestData["searchOffset"]; offset = request.RequestData["searchOffset"];
} }
OBSSceneItemAutoRelease item = OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName, offset);
Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName,
offset);
if (!item) if (!item)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceNotFound,
RequestStatus::ResourceNotFound, "No scene items were found in the specified scene by that name or offset.");
"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);
@ -161,39 +150,29 @@ RequestResult RequestHandler::CreateSceneItem(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease sceneSource = OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment);
request.ValidateScene("sceneName", statusCode, comment);
if (!sceneSource) if (!sceneSource)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
OBSScene scene = obs_scene_from_source(sceneSource); OBSScene scene = obs_scene_from_source(sceneSource);
OBSSourceAutoRelease source = OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
request.ValidateSource("sourceName", statusCode, comment);
if (!source) if (!source)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (request.RequestData["sceneName"] == if (request.RequestData["sceneName"] == request.RequestData["sourceName"])
request.RequestData["sourceName"]) return RequestResult::Error(RequestStatus::CannotAct, "You cannot create scene item of a scene within itself.");
return RequestResult::Error(
RequestStatus::CannotAct,
"You cannot create scene item of a scene within itself.");
bool sceneItemEnabled = true; bool sceneItemEnabled = true;
if (request.Contains("sceneItemEnabled")) { if (request.Contains("sceneItemEnabled")) {
if (!request.ValidateOptionalBoolean("sceneItemEnabled", if (!request.ValidateOptionalBoolean("sceneItemEnabled", statusCode, comment))
statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemEnabled = request.RequestData["sceneItemEnabled"]; sceneItemEnabled = request.RequestData["sceneItemEnabled"];
} }
OBSSceneItemAutoRelease sceneItem = OBSSceneItemAutoRelease sceneItem = Utils::Obs::ActionHelper::CreateSceneItem(source, scene, sceneItemEnabled);
Utils::Obs::ActionHelper::CreateSceneItem(source, scene,
sceneItemEnabled);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene item.");
RequestStatus::ResourceCreationFailed,
"Failed to create the scene item.");
json responseData; json responseData;
responseData["sceneItemId"] = obs_sceneitem_get_id(sceneItem); responseData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
@ -220,8 +199,7 @@ RequestResult RequestHandler::RemoveSceneItem(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment);
"sceneName", "sceneItemId", statusCode, comment);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -253,33 +231,26 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment);
"sceneName", "sceneItemId", statusCode, comment);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
// Get destination scene // Get destination scene
obs_scene_t *destinationScene; obs_scene_t *destinationScene;
if (request.Contains("destinationSceneName")) { if (request.Contains("destinationSceneName")) {
destinationScene = request.ValidateScene2( destinationScene = request.ValidateScene2("destinationSceneName", statusCode, comment);
"destinationSceneName", statusCode, comment);
if (!destinationScene) if (!destinationScene)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
} else { } else {
destinationScene = destinationScene = obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem));
obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem));
if (!destinationScene) if (!destinationScene)
return RequestResult::Error( return RequestResult::Error(RequestStatus::RequestProcessingFailed,
RequestStatus::RequestProcessingFailed, "Internal error: Failed to get ref for scene of scene item.");
"Internal error: Failed to get ref for scene of scene item.");
} }
if (obs_sceneitem_is_group(sceneItem) && if (obs_sceneitem_is_group(sceneItem) && obs_sceneitem_get_scene(sceneItem) == destinationScene) {
obs_sceneitem_get_scene(sceneItem) == destinationScene) {
obs_scene_release(destinationScene); obs_scene_release(destinationScene);
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Scenes may only have one instance of a group.");
RequestStatus::ResourceCreationFailed,
"Scenes may only have one instance of a group.");
} }
// Get scene item details // Get scene item details
@ -291,15 +262,11 @@ 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 = OBSSceneItemAutoRelease newSceneItem = Utils::Obs::ActionHelper::CreateSceneItem(
Utils::Obs::ActionHelper::CreateSceneItem( sceneItemSource, destinationScene, sceneItemEnabled, &sceneItemTransform, &sceneItemCrop);
sceneItemSource, destinationScene, sceneItemEnabled,
&sceneItemTransform, &sceneItemCrop);
obs_scene_release(destinationScene); obs_scene_release(destinationScene);
if (!newSceneItem) if (!newSceneItem)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene item.");
RequestStatus::ResourceCreationFailed,
"Failed to create the scene item.");
json responseData; json responseData;
responseData["sceneItemId"] = obs_sceneitem_get_id(newSceneItem); responseData["sceneItemId"] = obs_sceneitem_get_id(newSceneItem);
@ -328,15 +295,13 @@ RequestResult RequestHandler::GetSceneItemTransform(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
json responseData; json responseData;
responseData["sceneItemTransform"] = responseData["sceneItemTransform"] = Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem);
Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -359,11 +324,9 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
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);
// Create a fake request to use checks on the sub object // Create a fake request to use checks on the sub object
@ -381,23 +344,20 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
float sourceHeight = float(obs_source_get_height(source)); float sourceHeight = float(obs_source_get_height(source));
if (r.Contains("positionX")) { if (r.Contains("positionX")) {
if (!r.ValidateOptionalNumber("positionX", statusCode, comment, if (!r.ValidateOptionalNumber("positionX", statusCode, comment, -90001.0, 90001.0))
-90001.0, 90001.0))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemTransform.pos.x = r.RequestData["positionX"]; sceneItemTransform.pos.x = r.RequestData["positionX"];
transformChanged = true; transformChanged = true;
} }
if (r.Contains("positionY")) { if (r.Contains("positionY")) {
if (!r.ValidateOptionalNumber("positionY", statusCode, comment, if (!r.ValidateOptionalNumber("positionY", statusCode, comment, -90001.0, 90001.0))
-90001.0, 90001.0))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemTransform.pos.y = r.RequestData["positionY"]; sceneItemTransform.pos.y = r.RequestData["positionY"];
transformChanged = true; transformChanged = true;
} }
if (r.Contains("rotation")) { if (r.Contains("rotation")) {
if (!r.ValidateOptionalNumber("rotation", statusCode, comment, if (!r.ValidateOptionalNumber("rotation", statusCode, comment, -360.0, 360.0))
-360.0, 360.0))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemTransform.rot = r.RequestData["rotation"]; sceneItemTransform.rot = r.RequestData["rotation"];
transformChanged = true; transformChanged = true;
@ -409,110 +369,89 @@ 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( return RequestResult::Error(RequestStatus::RequestFieldOutOfRange,
RequestStatus::RequestFieldOutOfRange, "The field scaleX is too small or large for the current source resolution.");
"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;
} }
if (r.Contains("scaleY")) { if (r.Contains("scaleY")) {
if (!r.ValidateOptionalNumber("scaleY", statusCode, comment, if (!r.ValidateOptionalNumber("scaleY", statusCode, comment, -90001.0, 90001.0))
-90001.0, 90001.0))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
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( return RequestResult::Error(RequestStatus::RequestFieldOutOfRange,
RequestStatus::RequestFieldOutOfRange, "The field scaleY is too small or large for the current source resolution.");
"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;
} }
if (r.Contains("alignment")) { if (r.Contains("alignment")) {
if (!r.ValidateOptionalNumber( if (!r.ValidateOptionalNumber("alignment", statusCode, comment, 0, std::numeric_limits<uint32_t>::max()))
"alignment", statusCode, comment, 0,
std::numeric_limits<uint32_t>::max()))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemTransform.alignment = r.RequestData["alignment"]; sceneItemTransform.alignment = r.RequestData["alignment"];
transformChanged = true; transformChanged = true;
} }
if (r.Contains("boundsType")) { if (r.Contains("boundsType")) {
if (!r.ValidateOptionalString("boundsType", statusCode, if (!r.ValidateOptionalString("boundsType", statusCode, comment))
comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string boundsTypeString = r.RequestData["boundsType"]; std::string boundsTypeString = r.RequestData["boundsType"];
enum obs_bounds_type boundsType = enum obs_bounds_type boundsType = Utils::Obs::EnumHelper::GetSceneItemBoundsType(boundsTypeString);
Utils::Obs::EnumHelper::GetSceneItemBoundsType( if (boundsType == OBS_BOUNDS_NONE && boundsTypeString != "OBS_BOUNDS_NONE")
boundsTypeString); return RequestResult::Error(RequestStatus::InvalidRequestField,
if (boundsType == OBS_BOUNDS_NONE && "The field boundsType has an invalid value.");
boundsTypeString != "OBS_BOUNDS_NONE")
return RequestResult::Error(
RequestStatus::InvalidRequestField,
"The field boundsType has an invalid value.");
sceneItemTransform.bounds_type = boundsType; sceneItemTransform.bounds_type = boundsType;
transformChanged = true; transformChanged = true;
} }
if (r.Contains("boundsAlignment")) { if (r.Contains("boundsAlignment")) {
if (!r.ValidateOptionalNumber( if (!r.ValidateOptionalNumber("boundsAlignment", statusCode, comment, 0, std::numeric_limits<uint32_t>::max()))
"boundsAlignment", statusCode, comment, 0,
std::numeric_limits<uint32_t>::max()))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemTransform.bounds_alignment = sceneItemTransform.bounds_alignment = r.RequestData["boundsAlignment"];
r.RequestData["boundsAlignment"];
transformChanged = true; transformChanged = true;
} }
if (r.Contains("boundsWidth")) { if (r.Contains("boundsWidth")) {
if (!r.ValidateOptionalNumber("boundsWidth", statusCode, if (!r.ValidateOptionalNumber("boundsWidth", statusCode, comment, 1.0, 90001.0))
comment, 1.0, 90001.0))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemTransform.bounds.x = r.RequestData["boundsWidth"]; sceneItemTransform.bounds.x = r.RequestData["boundsWidth"];
transformChanged = true; transformChanged = true;
} }
if (r.Contains("boundsHeight")) { if (r.Contains("boundsHeight")) {
if (!r.ValidateOptionalNumber("boundsHeight", statusCode, if (!r.ValidateOptionalNumber("boundsHeight", statusCode, comment, 1.0, 90001.0))
comment, 1.0, 90001.0))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemTransform.bounds.y = r.RequestData["boundsHeight"]; sceneItemTransform.bounds.y = r.RequestData["boundsHeight"];
transformChanged = true; transformChanged = true;
} }
if (r.Contains("cropLeft")) { if (r.Contains("cropLeft")) {
if (!r.ValidateOptionalNumber("cropLeft", statusCode, comment, if (!r.ValidateOptionalNumber("cropLeft", statusCode, comment, 0.0, 100000.0))
0.0, 100000.0))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemCrop.left = r.RequestData["cropLeft"]; sceneItemCrop.left = r.RequestData["cropLeft"];
cropChanged = true; cropChanged = true;
} }
if (r.Contains("cropRight")) { if (r.Contains("cropRight")) {
if (!r.ValidateOptionalNumber("cropRight", statusCode, comment, if (!r.ValidateOptionalNumber("cropRight", statusCode, comment, 0.0, 100000.0))
0.0, 100000.0))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemCrop.right = r.RequestData["cropRight"]; sceneItemCrop.right = r.RequestData["cropRight"];
cropChanged = true; cropChanged = true;
} }
if (r.Contains("cropTop")) { if (r.Contains("cropTop")) {
if (!r.ValidateOptionalNumber("cropTop", statusCode, comment, if (!r.ValidateOptionalNumber("cropTop", statusCode, comment, 0.0, 100000.0))
0.0, 100000.0))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemCrop.top = r.RequestData["cropTop"]; sceneItemCrop.top = r.RequestData["cropTop"];
cropChanged = true; cropChanged = true;
} }
if (r.Contains("cropBottom")) { if (r.Contains("cropBottom")) {
if (!r.ValidateOptionalNumber("cropBottom", statusCode, comment, if (!r.ValidateOptionalNumber("cropBottom", statusCode, comment, 0.0, 100000.0))
0.0, 100000.0))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
sceneItemCrop.bottom = r.RequestData["cropBottom"]; sceneItemCrop.bottom = r.RequestData["cropBottom"];
cropChanged = true; cropChanged = true;
} }
if (!transformChanged && !cropChanged) if (!transformChanged && !cropChanged)
return RequestResult::Error( return RequestResult::Error(RequestStatus::CannotAct, "You have not provided any valid transform changes.");
RequestStatus::CannotAct,
"You have not provided any valid transform changes.");
if (transformChanged) if (transformChanged)
obs_sceneitem_set_info(sceneItem, &sceneItemTransform); obs_sceneitem_set_info(sceneItem, &sceneItemTransform);
@ -544,9 +483,8 @@ RequestResult RequestHandler::GetSceneItemEnabled(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -576,11 +514,9 @@ RequestResult RequestHandler::SetSceneItemEnabled(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
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);
bool sceneItemEnabled = request.RequestData["sceneItemEnabled"]; bool sceneItemEnabled = request.RequestData["sceneItemEnabled"];
@ -611,9 +547,8 @@ RequestResult RequestHandler::GetSceneItemLocked(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -643,11 +578,9 @@ RequestResult RequestHandler::SetSceneItemLocked(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
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);
bool sceneItemLocked = request.RequestData["sceneItemLocked"]; bool sceneItemLocked = request.RequestData["sceneItemLocked"];
@ -680,15 +613,13 @@ RequestResult RequestHandler::GetSceneItemIndex(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
json responseData; json responseData;
responseData["sceneItemIndex"] = responseData["sceneItemIndex"] = obs_sceneitem_get_order_position(sceneItem);
obs_sceneitem_get_order_position(sceneItem);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -713,11 +644,9 @@ RequestResult RequestHandler::SetSceneItemIndex(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
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);
int sceneItemIndex = request.RequestData["sceneItemIndex"]; int sceneItemIndex = request.RequestData["sceneItemIndex"];
@ -758,17 +687,15 @@ RequestResult RequestHandler::GetSceneItemBlendMode(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
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"] = responseData["sceneItemBlendMode"] = Utils::Obs::StringHelper::GetSceneItemBlendMode(blendMode);
Utils::Obs::StringHelper::GetSceneItemBlendMode(blendMode);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -793,22 +720,17 @@ RequestResult RequestHandler::SetSceneItemBlendMode(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
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"]; std::string blendModeString = request.RequestData["sceneItemBlendMode"];
auto blendMode = auto blendMode = Utils::Obs::EnumHelper::GetSceneItemBlendMode(blendModeString);
Utils::Obs::EnumHelper::GetSceneItemBlendMode(blendModeString); if (blendMode == OBS_BLEND_NORMAL && blendModeString != "OBS_BLEND_NORMAL")
if (blendMode == OBS_BLEND_NORMAL && return RequestResult::Error(RequestStatus::InvalidRequestField,
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);
@ -816,45 +738,36 @@ RequestResult RequestHandler::SetSceneItemBlendMode(const Request &request)
} }
// Intentionally undocumented // Intentionally undocumented
RequestResult RequestResult RequestHandler::GetSceneItemPrivateSettings(const Request &request)
RequestHandler::GetSceneItemPrivateSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem) if (!sceneItem)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease privateSettings = OBSDataAutoRelease privateSettings = obs_sceneitem_get_private_settings(sceneItem);
obs_sceneitem_get_private_settings(sceneItem);
json responseData; json responseData;
responseData["sceneItemSettings"] = responseData["sceneItemSettings"] = Utils::Json::ObsDataToJson(privateSettings);
Utils::Json::ObsDataToJson(privateSettings);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
// Intentionally undocumented // Intentionally undocumented
RequestResult RequestResult RequestHandler::SetSceneItemPrivateSettings(const Request &request)
RequestHandler::SetSceneItemPrivateSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem( OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
"sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
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);
OBSDataAutoRelease privateSettings = OBSDataAutoRelease privateSettings = obs_sceneitem_get_private_settings(sceneItem);
obs_sceneitem_get_private_settings(sceneItem);
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData( OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["sceneItemSettings"]);
request.RequestData["sceneItemSettings"]);
// Always overlays to prevent destroying internal source unintentionally // Always overlays to prevent destroying internal source unintentionally
obs_data_apply(privateSettings, newSettings); obs_data_apply(privateSettings, newSettings);

View File

@ -37,19 +37,15 @@ RequestResult RequestHandler::GetSceneList(const Request &)
{ {
json responseData; json responseData;
OBSSourceAutoRelease currentProgramScene = OBSSourceAutoRelease currentProgramScene = obs_frontend_get_current_scene();
obs_frontend_get_current_scene();
if (currentProgramScene) if (currentProgramScene)
responseData["currentProgramSceneName"] = responseData["currentProgramSceneName"] = obs_source_get_name(currentProgramScene);
obs_source_get_name(currentProgramScene);
else else
responseData["currentProgramSceneName"] = nullptr; responseData["currentProgramSceneName"] = nullptr;
OBSSourceAutoRelease currentPreviewScene = OBSSourceAutoRelease currentPreviewScene = obs_frontend_get_current_preview_scene();
obs_frontend_get_current_preview_scene();
if (currentPreviewScene) if (currentPreviewScene)
responseData["currentPreviewSceneName"] = responseData["currentPreviewSceneName"] = obs_source_get_name(currentPreviewScene);
obs_source_get_name(currentPreviewScene);
else else
responseData["currentPreviewSceneName"] = nullptr; responseData["currentPreviewSceneName"] = nullptr;
@ -96,10 +92,8 @@ RequestResult RequestHandler::GetGroupList(const Request &)
RequestResult RequestHandler::GetCurrentProgramScene(const Request &) RequestResult RequestHandler::GetCurrentProgramScene(const Request &)
{ {
json responseData; json responseData;
OBSSourceAutoRelease currentProgramScene = OBSSourceAutoRelease currentProgramScene = obs_frontend_get_current_scene();
obs_frontend_get_current_scene(); responseData["currentProgramSceneName"] = obs_source_get_name(currentProgramScene);
responseData["currentProgramSceneName"] =
obs_source_get_name(currentProgramScene);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -120,8 +114,7 @@ RequestResult RequestHandler::SetCurrentProgramScene(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease scene = OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
request.ValidateScene("sceneName", statusCode, comment);
if (!scene) if (!scene)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -149,12 +142,10 @@ 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);
OBSSourceAutoRelease currentPreviewScene = OBSSourceAutoRelease currentPreviewScene = obs_frontend_get_current_preview_scene();
obs_frontend_get_current_preview_scene();
json responseData; json responseData;
responseData["currentPreviewSceneName"] = responseData["currentPreviewSceneName"] = obs_source_get_name(currentPreviewScene);
obs_source_get_name(currentPreviewScene);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -180,8 +171,7 @@ RequestResult RequestHandler::SetCurrentPreviewScene(const Request &request)
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease scene = OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
request.ValidateScene("sceneName", statusCode, comment);
if (!scene) if (!scene)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -213,15 +203,11 @@ RequestResult RequestHandler::CreateScene(const Request &request)
OBSSourceAutoRelease scene = obs_get_source_by_name(sceneName.c_str()); OBSSourceAutoRelease scene = obs_get_source_by_name(sceneName.c_str());
if (scene) if (scene)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that scene name.");
RequestStatus::ResourceAlreadyExists,
"A source already exists by that scene name.");
obs_scene_t *createdScene = obs_scene_create(sceneName.c_str()); obs_scene_t *createdScene = obs_scene_create(sceneName.c_str());
if (!createdScene) if (!createdScene)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene.");
RequestStatus::ResourceCreationFailed,
"Failed to create the scene.");
obs_scene_release(createdScene); obs_scene_release(createdScene);
@ -244,15 +230,13 @@ RequestResult RequestHandler::RemoveScene(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease scene = OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
request.ValidateScene("sceneName", statusCode, comment);
if (!scene) if (!scene)
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( return RequestResult::Error(RequestStatus::NotEnoughResources,
RequestStatus::NotEnoughResources, "You cannot remove the last scene in the collection.");
"You cannot remove the last scene in the collection.");
obs_source_remove(scene); obs_source_remove(scene);
@ -276,20 +260,16 @@ RequestResult RequestHandler::SetSceneName(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease scene = OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
request.ValidateScene("sceneName", statusCode, comment); if (!(scene && request.ValidateString("newSceneName", statusCode, comment)))
if (!(scene &&
request.ValidateString("newSceneName", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string newSceneName = request.RequestData["newSceneName"]; std::string newSceneName = request.RequestData["newSceneName"];
OBSSourceAutoRelease existingSource = OBSSourceAutoRelease existingSource = obs_get_source_by_name(newSceneName.c_str());
obs_get_source_by_name(newSceneName.c_str());
if (existingSource) if (existingSource)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceAlreadyExists,
RequestStatus::ResourceAlreadyExists, "A source already exists by that new scene name.");
"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());
@ -311,30 +291,25 @@ RequestResult RequestHandler::SetSceneName(const Request &request)
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestResult RequestHandler::GetSceneSceneTransitionOverride(const Request &request)
RequestHandler::GetSceneSceneTransitionOverride(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease scene = OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
request.ValidateScene("sceneName", statusCode, comment);
if (!scene) if (!scene)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease privateSettings = OBSDataAutoRelease privateSettings = obs_source_get_private_settings(scene);
obs_source_get_private_settings(scene);
json responseData; json responseData;
const char *transitionName = const char *transitionName = obs_data_get_string(privateSettings, "transition");
obs_data_get_string(privateSettings, "transition");
if (transitionName && strlen(transitionName)) if (transitionName && strlen(transitionName))
responseData["transitionName"] = transitionName; responseData["transitionName"] = transitionName;
else else
responseData["transitionName"] = nullptr; responseData["transitionName"] = nullptr;
if (obs_data_has_user_value(privateSettings, "transition_duration")) if (obs_data_has_user_value(privateSettings, "transition_duration"))
responseData["transitionDuration"] = obs_data_get_int( responseData["transitionDuration"] = obs_data_get_int(privateSettings, "transition_duration");
privateSettings, "transition_duration");
else else
responseData["transitionDuration"] = nullptr; responseData["transitionDuration"] = nullptr;
@ -355,55 +330,42 @@ RequestHandler::GetSceneSceneTransitionOverride(const Request &request)
* @api requests * @api requests
* @category scenes * @category scenes
*/ */
RequestResult RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request &request)
RequestHandler::SetSceneSceneTransitionOverride(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease scene = OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
request.ValidateScene("sceneName", statusCode, comment);
if (!scene) if (!scene)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease privateSettings = OBSDataAutoRelease privateSettings = obs_source_get_private_settings(scene);
obs_source_get_private_settings(scene);
bool hasName = request.RequestData.contains("transitionName"); bool hasName = request.RequestData.contains("transitionName");
if (hasName && !request.RequestData["transitionName"].is_null()) { if (hasName && !request.RequestData["transitionName"].is_null()) {
if (!request.ValidateOptionalString("transitionName", if (!request.ValidateOptionalString("transitionName", statusCode, comment))
statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
OBSSourceAutoRelease transition = OBSSourceAutoRelease transition =
Utils::Obs::SearchHelper::GetSceneTransitionByName( Utils::Obs::SearchHelper::GetSceneTransitionByName(request.RequestData["transitionName"]);
request.RequestData["transitionName"]);
if (!transition) if (!transition)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene transition was found by that name.");
RequestStatus::ResourceNotFound,
"No scene transition was found by that name.");
} }
bool hasDuration = request.RequestData.contains("transitionDuration"); bool hasDuration = request.RequestData.contains("transitionDuration");
if (hasDuration && if (hasDuration && !request.RequestData["transitionDuration"].is_null()) {
!request.RequestData["transitionDuration"].is_null()) { if (!request.ValidateOptionalNumber("transitionDuration", statusCode, comment, 50, 20000))
if (!request.ValidateOptionalNumber("transitionDuration",
statusCode, comment, 50,
20000))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
} }
if (!hasName && !hasDuration) if (!hasName && !hasDuration)
return RequestResult::Error( return RequestResult::Error(RequestStatus::MissingRequestField,
RequestStatus::MissingRequestField, "Your request data must include either `transitionName` or `transitionDuration`.");
"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()) {
obs_data_erase(privateSettings, "transition"); obs_data_erase(privateSettings, "transition");
} else { } else {
std::string transitionName = std::string transitionName = request.RequestData["transitionName"];
request.RequestData["transitionName"]; obs_data_set_string(privateSettings, "transition", transitionName.c_str());
obs_data_set_string(privateSettings, "transition",
transitionName.c_str());
} }
} }
@ -411,9 +373,7 @@ RequestHandler::SetSceneSceneTransitionOverride(const Request &request)
if (request.RequestData["transitionDuration"].is_null()) { if (request.RequestData["transitionDuration"].is_null()) {
obs_data_erase(privateSettings, "transition_duration"); obs_data_erase(privateSettings, "transition_duration");
} else { } else {
obs_data_set_int( obs_data_set_int(privateSettings, "transition_duration", request.RequestData["transitionDuration"]);
privateSettings, "transition_duration",
request.RequestData["transitionDuration"]);
} }
} }

View File

@ -25,15 +25,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "RequestHandler.h" #include "RequestHandler.h"
QImage TakeSourceScreenshot(obs_source_t *source, bool &success, QImage TakeSourceScreenshot(obs_source_t *source, bool &success, uint32_t requestedWidth = 0, uint32_t requestedHeight = 0)
uint32_t requestedWidth = 0,
uint32_t requestedHeight = 0)
{ {
// Get info about the requested source // Get info about the requested source
const uint32_t sourceWidth = obs_source_get_base_width(source); const uint32_t sourceWidth = obs_source_get_base_width(source);
const uint32_t sourceHeight = obs_source_get_base_height(source); const uint32_t sourceHeight = obs_source_get_base_height(source);
const double sourceAspectRatio = const double sourceAspectRatio = ((double)sourceWidth / (double)sourceHeight);
((double)sourceWidth / (double)sourceHeight);
uint32_t imgWidth = sourceWidth; uint32_t imgWidth = sourceWidth;
uint32_t imgHeight = sourceHeight; uint32_t imgHeight = sourceHeight;
@ -66,8 +63,7 @@ QImage TakeSourceScreenshot(obs_source_t *source, bool &success,
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_stagesurf_t *stageSurface = gs_stagesurface_create(imgWidth, imgHeight, GS_RGBA);
gs_stagesurface_create(imgWidth, imgHeight, GS_RGBA);
success = false; success = false;
gs_texrender_reset(texRender); gs_texrender_reset(texRender);
@ -76,8 +72,7 @@ QImage TakeSourceScreenshot(obs_source_t *source, bool &success,
vec4_zero(&background); vec4_zero(&background);
gs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0); gs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0);
gs_ortho(0.0f, (float)sourceWidth, 0.0f, (float)sourceHeight, gs_ortho(0.0f, (float)sourceWidth, 0.0f, (float)sourceHeight, -100.0f, 100.0f);
-100.0f, 100.0f);
gs_blend_state_push(); gs_blend_state_push();
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
@ -89,15 +84,11 @@ QImage TakeSourceScreenshot(obs_source_t *source, bool &success,
gs_blend_state_pop(); gs_blend_state_pop();
gs_texrender_end(texRender); gs_texrender_end(texRender);
gs_stage_texture(stageSurface, gs_stage_texture(stageSurface, gs_texrender_get_texture(texRender));
gs_texrender_get_texture(texRender)); 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), memcpy(ret.scanLine(y), videoData + (y * videoLinesize), lineSize);
videoData + (y * videoLinesize),
lineSize);
} }
gs_stagesurface_unmap(stageSurface); gs_stagesurface_unmap(stageSurface);
success = true; success = true;
@ -139,16 +130,12 @@ RequestResult RequestHandler::GetSourceActive(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease source = OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
request.ValidateSource("sourceName", statusCode, comment);
if (!source) if (!source)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE)
obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE) return RequestResult::Error(RequestStatus::InvalidResourceType, "The specified source is not an input or a scene.");
return RequestResult::Error(
RequestStatus::InvalidResourceType,
"The specified source is not an input or a scene.");
json responseData; json responseData;
responseData["videoActive"] = obs_source_active(source); responseData["videoActive"] = obs_source_active(source);
@ -183,79 +170,60 @@ RequestResult RequestHandler::GetSourceScreenshot(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease source = OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
request.ValidateSource("sourceName", statusCode, comment); if (!(source && request.ValidateString("imageFormat", statusCode, comment)))
if (!(source &&
request.ValidateString("imageFormat", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE)
obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE) return RequestResult::Error(RequestStatus::InvalidResourceType, "The specified source is not an input or a scene.");
return RequestResult::Error(
RequestStatus::InvalidResourceType,
"The specified source is not an input or a scene.");
std::string imageFormat = request.RequestData["imageFormat"]; std::string imageFormat = request.RequestData["imageFormat"];
if (!IsImageFormatValid(imageFormat)) if (!IsImageFormatValid(imageFormat))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidRequestField,
RequestStatus::InvalidRequestField, "Your specified image format is invalid or not supported by this system.");
"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};
int compressionQuality{-1}; int compressionQuality{-1};
if (request.Contains("imageWidth")) { if (request.Contains("imageWidth")) {
if (!request.ValidateOptionalNumber("imageWidth", statusCode, if (!request.ValidateOptionalNumber("imageWidth", statusCode, comment, 8, 4096))
comment, 8, 4096))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
requestedWidth = request.RequestData["imageWidth"]; requestedWidth = request.RequestData["imageWidth"];
} }
if (request.Contains("imageHeight")) { if (request.Contains("imageHeight")) {
if (!request.ValidateOptionalNumber("imageHeight", statusCode, if (!request.ValidateOptionalNumber("imageHeight", statusCode, comment, 8, 4096))
comment, 8, 4096))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
requestedHeight = request.RequestData["imageHeight"]; requestedHeight = request.RequestData["imageHeight"];
} }
if (request.Contains("imageCompressionQuality")) { if (request.Contains("imageCompressionQuality")) {
if (!request.ValidateOptionalNumber("imageCompressionQuality", if (!request.ValidateOptionalNumber("imageCompressionQuality", statusCode, comment, -1, 100))
statusCode, comment, -1,
100))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
compressionQuality = compressionQuality = request.RequestData["imageCompressionQuality"];
request.RequestData["imageCompressionQuality"];
} }
bool success; bool success;
QImage renderedImage = TakeSourceScreenshot( QImage renderedImage = TakeSourceScreenshot(source, success, requestedWidth, requestedHeight);
source, success, requestedWidth, requestedHeight);
if (!success) if (!success)
return RequestResult::Error( return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Failed to render screenshot.");
RequestStatus::RequestProcessingFailed,
"Failed to render screenshot.");
QByteArray encodedImgBytes; QByteArray encodedImgBytes;
QBuffer buffer(&encodedImgBytes); QBuffer buffer(&encodedImgBytes);
buffer.open(QBuffer::WriteOnly); buffer.open(QBuffer::WriteOnly);
if (!renderedImage.save(&buffer, imageFormat.c_str(), if (!renderedImage.save(&buffer, imageFormat.c_str(), compressionQuality))
compressionQuality)) return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Failed to encode screenshot.");
return RequestResult::Error(
RequestStatus::RequestProcessingFailed,
"Failed to encode screenshot.");
buffer.close(); buffer.close();
QString encodedPicture = QString("data:image/%1;base64,") QString encodedPicture = QString("data:image/%1;base64,").arg(imageFormat.c_str()).append(encodedImgBytes.toBase64());
.arg(imageFormat.c_str())
.append(encodedImgBytes.toBase64());
json responseData; json responseData;
responseData["imageData"] = encodedPicture.toStdString(); responseData["imageData"] = encodedPicture.toStdString();
@ -290,79 +258,60 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease source = OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
request.ValidateSource("sourceName", statusCode, comment); if (!(source && request.ValidateString("imageFormat", statusCode, comment) &&
if (!(source &&
request.ValidateString("imageFormat", statusCode, comment) &&
request.ValidateString("imageFilePath", 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 && if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE)
obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE) return RequestResult::Error(RequestStatus::InvalidResourceType, "The specified source is not an input or a scene.");
return RequestResult::Error(
RequestStatus::InvalidResourceType,
"The specified source is not an input or a scene.");
std::string imageFormat = request.RequestData["imageFormat"]; std::string imageFormat = request.RequestData["imageFormat"];
std::string imageFilePath = request.RequestData["imageFilePath"]; std::string imageFilePath = request.RequestData["imageFilePath"];
if (!IsImageFormatValid(imageFormat)) if (!IsImageFormatValid(imageFormat))
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidRequestField,
RequestStatus::InvalidRequestField, "Your specified image format is invalid or not supported by this system.");
"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())
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceNotFound, "The directory for your file path does not exist.");
RequestStatus::ResourceNotFound,
"The directory for your file path does not exist.");
uint32_t requestedWidth{0}; uint32_t requestedWidth{0};
uint32_t requestedHeight{0}; uint32_t requestedHeight{0};
int compressionQuality{-1}; int compressionQuality{-1};
if (request.Contains("imageWidth")) { if (request.Contains("imageWidth")) {
if (!request.ValidateOptionalNumber("imageWidth", statusCode, if (!request.ValidateOptionalNumber("imageWidth", statusCode, comment, 8, 4096))
comment, 8, 4096))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
requestedWidth = request.RequestData["imageWidth"]; requestedWidth = request.RequestData["imageWidth"];
} }
if (request.Contains("imageHeight")) { if (request.Contains("imageHeight")) {
if (!request.ValidateOptionalNumber("imageHeight", statusCode, if (!request.ValidateOptionalNumber("imageHeight", statusCode, comment, 8, 4096))
comment, 8, 4096))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
requestedHeight = request.RequestData["imageHeight"]; requestedHeight = request.RequestData["imageHeight"];
} }
if (request.Contains("imageCompressionQuality")) { if (request.Contains("imageCompressionQuality")) {
if (!request.ValidateOptionalNumber("imageCompressionQuality", if (!request.ValidateOptionalNumber("imageCompressionQuality", statusCode, comment, -1, 100))
statusCode, comment, -1,
100))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
compressionQuality = compressionQuality = request.RequestData["imageCompressionQuality"];
request.RequestData["imageCompressionQuality"];
} }
bool success; bool success;
QImage renderedImage = TakeSourceScreenshot( QImage renderedImage = TakeSourceScreenshot(source, success, requestedWidth, requestedHeight);
source, success, requestedWidth, requestedHeight);
if (!success) if (!success)
return RequestResult::Error( return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Failed to render screenshot.");
RequestStatus::RequestProcessingFailed,
"Failed to render screenshot.");
QString absoluteFilePath = filePathInfo.absoluteFilePath(); QString absoluteFilePath = filePathInfo.absoluteFilePath();
if (!renderedImage.save(absoluteFilePath, imageFormat.c_str(), if (!renderedImage.save(absoluteFilePath, imageFormat.c_str(), compressionQuality))
compressionQuality)) return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Failed to save screenshot.");
return RequestResult::Error(
RequestStatus::RequestProcessingFailed,
"Failed to save screenshot.");
return RequestResult::Success(); return RequestResult::Success();
} }
@ -372,17 +321,14 @@ RequestResult RequestHandler::GetSourcePrivateSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease source = OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
request.ValidateSource("sourceName", statusCode, comment);
if (!source) if (!source)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease privateSettings = OBSDataAutoRelease privateSettings = obs_source_get_private_settings(source);
obs_source_get_private_settings(source);
json responseData; json responseData;
responseData["sourceSettings"] = responseData["sourceSettings"] = Utils::Json::ObsDataToJson(privateSettings);
Utils::Json::ObsDataToJson(privateSettings);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -392,17 +338,13 @@ RequestResult RequestHandler::SetSourcePrivateSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease source = OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
request.ValidateSource("sourceName", statusCode, comment); if (!source || !request.ValidateObject("sourceSettings", statusCode, comment))
if (!source ||
!request.ValidateObject("sourceSettings", statusCode, comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease privateSettings = OBSDataAutoRelease privateSettings = obs_source_get_private_settings(source);
obs_source_get_private_settings(source);
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData( OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["sourceSettings"]);
request.RequestData["sourceSettings"]);
// Always overlays to prevent destroying internal source data unintentionally // Always overlays to prevent destroying internal source data unintentionally
obs_data_apply(privateSettings, newSettings); obs_data_apply(privateSettings, newSettings);

View File

@ -41,22 +41,16 @@ RequestResult RequestHandler::GetStreamStatus(const Request &)
{ {
OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output(); OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output();
uint64_t outputDuration = uint64_t outputDuration = Utils::Obs::NumberHelper::GetOutputDuration(streamOutput);
Utils::Obs::NumberHelper::GetOutputDuration(streamOutput);
json responseData; json responseData;
responseData["outputActive"] = obs_output_active(streamOutput); responseData["outputActive"] = obs_output_active(streamOutput);
responseData["outputReconnecting"] = responseData["outputReconnecting"] = obs_output_reconnecting(streamOutput);
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["outputBytes"] = responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(streamOutput);
(uint64_t)obs_output_get_total_bytes(streamOutput); responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(streamOutput);
responseData["outputSkippedFrames"] = responseData["outputTotalFrames"] = obs_output_get_total_frames(streamOutput);
obs_output_get_frames_dropped(streamOutput);
responseData["outputTotalFrames"] =
obs_output_get_total_frames(streamOutput);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }

View File

@ -38,8 +38,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
RequestResult RequestHandler::GetTransitionKindList(const Request &) RequestResult RequestHandler::GetTransitionKindList(const Request &)
{ {
json responseData; json responseData;
responseData["transitionKinds"] = responseData["transitionKinds"] = Utils::Obs::ArrayHelper::GetTransitionKindList();
Utils::Obs::ArrayHelper::GetTransitionKindList();
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -63,17 +62,14 @@ RequestResult RequestHandler::GetSceneTransitionList(const Request &)
OBSSourceAutoRelease transition = obs_frontend_get_current_transition(); OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
if (transition) { if (transition) {
responseData["currentSceneTransitionName"] = responseData["currentSceneTransitionName"] = obs_source_get_name(transition);
obs_source_get_name(transition); responseData["currentSceneTransitionKind"] = obs_source_get_id(transition);
responseData["currentSceneTransitionKind"] =
obs_source_get_id(transition);
} else { } else {
responseData["currentSceneTransitionName"] = nullptr; responseData["currentSceneTransitionName"] = nullptr;
responseData["currentSceneTransitionKind"] = nullptr; responseData["currentSceneTransitionKind"] = nullptr;
} }
responseData["transitions"] = responseData["transitions"] = Utils::Obs::ArrayHelper::GetSceneTransitionList();
Utils::Obs::ArrayHelper::GetSceneTransitionList();
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -99,9 +95,8 @@ 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( return RequestResult::Error(RequestStatus::InvalidResourceState,
RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
"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);
@ -112,16 +107,13 @@ RequestResult RequestHandler::GetCurrentSceneTransition(const Request &)
responseData["transitionDuration"] = nullptr; responseData["transitionDuration"] = nullptr;
} else { } else {
responseData["transitionFixed"] = false; responseData["transitionFixed"] = false;
responseData["transitionDuration"] = responseData["transitionDuration"] = obs_frontend_get_transition_duration();
obs_frontend_get_transition_duration();
} }
if (obs_source_configurable(transition)) { if (obs_source_configurable(transition)) {
responseData["transitionConfigurable"] = true; responseData["transitionConfigurable"] = true;
OBSDataAutoRelease transitionSettings = OBSDataAutoRelease transitionSettings = obs_source_get_settings(transition);
obs_source_get_settings(transition); responseData["transitionSettings"] = Utils::Json::ObsDataToJson(transitionSettings);
responseData["transitionSettings"] =
Utils::Json::ObsDataToJson(transitionSettings);
} else { } else {
responseData["transitionConfigurable"] = false; responseData["transitionConfigurable"] = false;
responseData["transitionSettings"] = nullptr; responseData["transitionSettings"] = nullptr;
@ -153,13 +145,9 @@ RequestResult RequestHandler::SetCurrentSceneTransition(const Request &request)
std::string transitionName = request.RequestData["transitionName"]; std::string transitionName = request.RequestData["transitionName"];
OBSSourceAutoRelease transition = OBSSourceAutoRelease transition = Utils::Obs::SearchHelper::GetSceneTransitionByName(transitionName);
Utils::Obs::SearchHelper::GetSceneTransitionByName(
transitionName);
if (!transition) if (!transition)
return RequestResult::Error( return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene transition was found by that name.");
RequestStatus::ResourceNotFound,
"No scene transition was found by that name.");
obs_frontend_set_current_transition(transition); obs_frontend_set_current_transition(transition);
@ -178,13 +166,11 @@ RequestResult RequestHandler::SetCurrentSceneTransition(const Request &request)
* @api requests * @api requests
* @category transitions * @category transitions
*/ */
RequestResult RequestResult RequestHandler::SetCurrentSceneTransitionDuration(const Request &request)
RequestHandler::SetCurrentSceneTransitionDuration(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!request.ValidateNumber("transitionDuration", statusCode, comment, if (!request.ValidateNumber("transitionDuration", statusCode, comment, 50, 20000))
50, 20000))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
int transitionDuration = request.RequestData["transitionDuration"]; int transitionDuration = request.RequestData["transitionDuration"];
@ -207,41 +193,34 @@ RequestHandler::SetCurrentSceneTransitionDuration(const Request &request)
* @api requests * @api requests
* @category transitions * @category transitions
*/ */
RequestResult RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request &request)
RequestHandler::SetCurrentSceneTransitionSettings(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
if (!request.ValidateObject("transitionSettings", statusCode, comment, if (!request.ValidateObject("transitionSettings", statusCode, comment, true))
true))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
OBSSourceAutoRelease transition = obs_frontend_get_current_transition(); OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
if (!transition) if (!transition)
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState,
RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
"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( return RequestResult::Error(RequestStatus::ResourceNotConfigurable,
RequestStatus::ResourceNotConfigurable, "The current transition does not support custom settings.");
"The current transition does not support custom settings.");
bool overlay = true; bool overlay = true;
if (request.Contains("overlay")) { if (request.Contains("overlay")) {
if (!request.ValidateOptionalBoolean("overlay", statusCode, if (!request.ValidateOptionalBoolean("overlay", statusCode, comment))
comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
overlay = request.RequestData["overlay"]; overlay = request.RequestData["overlay"];
} }
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData( OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["transitionSettings"]);
request.RequestData["transitionSettings"]);
if (!newSettings) if (!newSettings)
return RequestResult::Error( return RequestResult::Error(RequestStatus::RequestProcessingFailed,
RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
"An internal data conversion operation failed. Please report this!");
if (overlay) if (overlay)
obs_source_update(transition, newSettings); obs_source_update(transition, newSettings);
@ -271,9 +250,8 @@ 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( return RequestResult::Error(RequestStatus::InvalidResourceState,
RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
"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);
@ -296,8 +274,7 @@ 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);
OBSSourceAutoRelease previewScene = OBSSourceAutoRelease previewScene = obs_frontend_get_current_preview_scene();
obs_frontend_get_current_preview_scene();
obs_frontend_set_current_scene(previewScene); obs_frontend_set_current_scene(previewScene);
@ -331,16 +308,14 @@ RequestResult RequestHandler::SetTBarPosition(const Request &request)
bool release = true; bool release = true;
if (request.Contains("release")) { if (request.Contains("release")) {
if (!request.ValidateOptionalBoolean("release", statusCode, if (!request.ValidateOptionalBoolean("release", statusCode, comment))
comment))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
} }
OBSSourceAutoRelease transition = obs_frontend_get_current_transition(); OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
if (!transition) if (!transition)
return RequestResult::Error( return RequestResult::Error(RequestStatus::InvalidResourceState,
RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
"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

@ -39,8 +39,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
RequestResult RequestHandler::GetStudioModeEnabled(const Request &) RequestResult RequestHandler::GetStudioModeEnabled(const Request &)
{ {
json responseData; json responseData;
responseData["studioModeEnabled"] = responseData["studioModeEnabled"] = obs_frontend_preview_program_mode_active();
obs_frontend_preview_program_mode_active();
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
@ -64,18 +63,15 @@ RequestResult RequestHandler::SetStudioModeEnabled(const Request &request)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
// Avoid queueing tasks if nothing will change // Avoid queueing tasks if nothing will change
if (obs_frontend_preview_program_mode_active() != if (obs_frontend_preview_program_mode_active() != request.RequestData["studioModeEnabled"]) {
request.RequestData["studioModeEnabled"]) {
// (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 = bool studioModeEnabled = request.RequestData["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_queue_task(
OBS_TASK_UI, OBS_TASK_UI,
[](void *param) { [](void *param) {
auto studioModeEnabled = (bool *)param; auto studioModeEnabled = (bool *)param;
obs_frontend_set_preview_program_mode( obs_frontend_set_preview_program_mode(*studioModeEnabled);
*studioModeEnabled);
}, },
&studioModeEnabled, true); &studioModeEnabled, true);
} }
@ -99,8 +95,7 @@ RequestResult RequestHandler::OpenInputPropertiesDialog(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -125,8 +120,7 @@ RequestResult RequestHandler::OpenInputFiltersDialog(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -151,15 +145,13 @@ RequestResult RequestHandler::OpenInputInteractDialog(const Request &request)
{ {
RequestStatus::RequestStatus statusCode; RequestStatus::RequestStatus statusCode;
std::string comment; std::string comment;
OBSSourceAutoRelease input = OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
request.ValidateInput("inputName", statusCode, comment);
if (!input) if (!input)
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( return RequestResult::Error(RequestStatus::InvalidResourceState,
RequestStatus::InvalidResourceState, "The specified input does not support interaction.");
"The specified input does not support interaction.");
obs_frontend_open_source_interaction(input); obs_frontend_open_source_interaction(input);
@ -183,8 +175,7 @@ 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(); for (int screenIndex = 0; screenIndex < screensList.size(); screenIndex++) {
screenIndex++) {
json screenData; json screenData;
QScreen const *screen = screensList[screenIndex]; QScreen const *screen = screensList[screenIndex];
std::stringstream nameAndIndex; std::stringstream nameAndIndex;

View File

@ -30,8 +30,7 @@ json GetDefaultJsonObject(const json &requestData)
} }
Request::Request(const std::string &requestType, const json &requestData, Request::Request(const std::string &requestType, const json &requestData,
const RequestBatchExecutionType::RequestBatchExecutionType const RequestBatchExecutionType::RequestBatchExecutionType executionType)
executionType)
: RequestType(requestType), : RequestType(requestType),
HasRequestData(requestData.is_object()), HasRequestData(requestData.is_object()),
RequestData(GetDefaultJsonObject(requestData)), RequestData(GetDefaultJsonObject(requestData)),
@ -41,103 +40,84 @@ Request::Request(const std::string &requestType, const json &requestData,
bool Request::Contains(const std::string &keyName) const bool Request::Contains(const std::string &keyName) const
{ {
return (RequestData.contains(keyName) && return (RequestData.contains(keyName) && !RequestData[keyName].is_null());
!RequestData[keyName].is_null());
} }
bool Request::ValidateBasic(const std::string &keyName, bool Request::ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
RequestStatus::RequestStatus &statusCode,
std::string &comment) const
{ {
if (!HasRequestData) { if (!HasRequestData) {
statusCode = RequestStatus::MissingRequestData; statusCode = RequestStatus::MissingRequestData;
comment = comment = "Your request data is missing or invalid (non-object)";
"Your request data is missing or invalid (non-object)";
return false; return false;
} }
if (!RequestData.contains(keyName) || RequestData[keyName].is_null()) { if (!RequestData.contains(keyName) || RequestData[keyName].is_null()) {
statusCode = RequestStatus::MissingRequestField; statusCode = RequestStatus::MissingRequestField;
comment = std::string("Your request is missing the `") + comment = std::string("Your request is missing the `") + keyName + "` field.";
keyName + "` field.";
return false; return false;
} }
return true; return true;
} }
bool Request::ValidateOptionalNumber(const std::string &keyName, bool Request::ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode, const double minValue, const double maxValue) const
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;
comment = std::string("The field value of `") + keyName + comment = std::string("The field value of `") + keyName + "` must be a number.";
"` must be a number.";
return false; return false;
} }
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 + comment = std::string("The field value of `") + keyName + "` is below the minimum of `" + std::to_string(minValue) +
"` 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 + comment = std::string("The field value of `") + keyName + "` is above the maximum of `" + std::to_string(maxValue) +
"` is above the maximum of `" + "`";
std::to_string(maxValue) + "`";
return false; return false;
} }
return true; return true;
} }
bool Request::ValidateNumber(const std::string &keyName, bool Request::ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode, const double minValue, const double maxValue) const
std::string &comment, const double minValue,
const double maxValue) const
{ {
if (!ValidateBasic(keyName, statusCode, comment)) if (!ValidateBasic(keyName, statusCode, comment))
return false; return false;
if (!ValidateOptionalNumber(keyName, statusCode, comment, minValue, if (!ValidateOptionalNumber(keyName, statusCode, comment, minValue, maxValue))
maxValue))
return false; return false;
return true; return true;
} }
bool Request::ValidateOptionalString(const std::string &keyName, bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode,
std::string &comment,
const bool allowEmpty) const const bool allowEmpty) const
{ {
if (!RequestData[keyName].is_string()) { if (!RequestData[keyName].is_string()) {
statusCode = RequestStatus::InvalidRequestFieldType; statusCode = RequestStatus::InvalidRequestFieldType;
comment = std::string("The field value of `") + keyName + comment = std::string("The field value of `") + keyName + "` must be a string.";
"` must be a string.";
return false; return false;
} }
if (RequestData[keyName].get<std::string>().empty() && !allowEmpty) { if (RequestData[keyName].get<std::string>().empty() && !allowEmpty) {
statusCode = RequestStatus::RequestFieldEmpty; statusCode = RequestStatus::RequestFieldEmpty;
comment = std::string("The field value of `") + keyName + comment = std::string("The field value of `") + keyName + "` must not be empty.";
"` must not be empty.";
return false; return false;
} }
return true; return true;
} }
bool Request::ValidateString(const std::string &keyName, bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode, const bool allowEmpty) const
std::string &comment, const bool allowEmpty) const
{ {
if (!ValidateBasic(keyName, statusCode, comment)) if (!ValidateBasic(keyName, statusCode, comment))
return false; return false;
@ -148,23 +128,19 @@ bool Request::ValidateString(const std::string &keyName,
return true; return true;
} }
bool Request::ValidateOptionalBoolean(const std::string &keyName, bool Request::ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
RequestStatus::RequestStatus &statusCode,
std::string &comment) const std::string &comment) const
{ {
if (!RequestData[keyName].is_boolean()) { if (!RequestData[keyName].is_boolean()) {
statusCode = RequestStatus::InvalidRequestFieldType; statusCode = RequestStatus::InvalidRequestFieldType;
comment = std::string("The field value of `") + keyName + comment = std::string("The field value of `") + keyName + "` must be boolean.";
"` must be boolean.";
return false; return false;
} }
return true; return true;
} }
bool Request::ValidateBoolean(const std::string &keyName, bool Request::ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
RequestStatus::RequestStatus &statusCode,
std::string &comment) const
{ {
if (!ValidateBasic(keyName, statusCode, comment)) if (!ValidateBasic(keyName, statusCode, comment))
return false; return false;
@ -175,31 +151,26 @@ bool Request::ValidateBoolean(const std::string &keyName,
return true; return true;
} }
bool Request::ValidateOptionalObject(const std::string &keyName, bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode,
std::string &comment,
const bool allowEmpty) const const bool allowEmpty) const
{ {
if (!RequestData[keyName].is_object()) { if (!RequestData[keyName].is_object()) {
statusCode = RequestStatus::InvalidRequestFieldType; statusCode = RequestStatus::InvalidRequestFieldType;
comment = std::string("The field value of `") + keyName + comment = std::string("The field value of `") + keyName + "` must be an object.";
"` must be an object.";
return false; return false;
} }
if (RequestData[keyName].empty() && !allowEmpty) { if (RequestData[keyName].empty() && !allowEmpty) {
statusCode = RequestStatus::RequestFieldEmpty; statusCode = RequestStatus::RequestFieldEmpty;
comment = std::string("The field value of `") + keyName + comment = std::string("The field value of `") + keyName + "` must not be empty.";
"` must not be empty.";
return false; return false;
} }
return true; return true;
} }
bool Request::ValidateObject(const std::string &keyName, bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode, const bool allowEmpty) const
std::string &comment, const bool allowEmpty) const
{ {
if (!ValidateBasic(keyName, statusCode, comment)) if (!ValidateBasic(keyName, statusCode, comment))
return false; return false;
@ -210,31 +181,26 @@ bool Request::ValidateObject(const std::string &keyName,
return true; return true;
} }
bool Request::ValidateOptionalArray(const std::string &keyName, bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode,
std::string &comment,
const bool allowEmpty) const const bool allowEmpty) const
{ {
if (!RequestData[keyName].is_array()) { if (!RequestData[keyName].is_array()) {
statusCode = RequestStatus::InvalidRequestFieldType; statusCode = RequestStatus::InvalidRequestFieldType;
comment = std::string("The field value of `") + keyName + comment = std::string("The field value of `") + keyName + "` must be an array.";
"` must be an array.";
return false; return false;
} }
if (RequestData[keyName].empty() && !allowEmpty) { if (RequestData[keyName].empty() && !allowEmpty) {
statusCode = RequestStatus::RequestFieldEmpty; statusCode = RequestStatus::RequestFieldEmpty;
comment = std::string("The field value of `") + keyName + comment = std::string("The field value of `") + keyName + "` must not be empty.";
"` must not be empty.";
return false; return false;
} }
return true; return true;
} }
bool Request::ValidateArray(const std::string &keyName, bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode, const bool allowEmpty) const
std::string &comment, const bool allowEmpty) const
{ {
if (!ValidateBasic(keyName, statusCode, comment)) if (!ValidateBasic(keyName, statusCode, comment))
return false; return false;
@ -245,8 +211,7 @@ bool Request::ValidateArray(const std::string &keyName,
return true; return true;
} }
obs_source_t *Request::ValidateSource(const std::string &keyName, obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
RequestStatus::RequestStatus &statusCode,
std::string &comment) const std::string &comment) const
{ {
if (!ValidateString(keyName, statusCode, comment)) if (!ValidateString(keyName, statusCode, comment))
@ -257,17 +222,14 @@ obs_source_t *Request::ValidateSource(const std::string &keyName,
obs_source_t *ret = obs_get_source_by_name(sourceName.c_str()); obs_source_t *ret = obs_get_source_by_name(sourceName.c_str());
if (!ret) { if (!ret) {
statusCode = RequestStatus::ResourceNotFound; statusCode = RequestStatus::ResourceNotFound;
comment = std::string("No source was found by the name of `") + comment = std::string("No source was found by the name of `") + sourceName + "`.";
sourceName + "`.";
return nullptr; return nullptr;
} }
return ret; return ret;
} }
obs_source_t *Request::ValidateScene(const std::string &keyName, obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode,
std::string &comment,
const ObsWebSocketSceneFilter filter) const const ObsWebSocketSceneFilter filter) const
{ {
obs_source_t *ret = ValidateSource(keyName, statusCode, comment); obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
@ -287,8 +249,7 @@ obs_source_t *Request::ValidateScene(const std::string &keyName,
statusCode = RequestStatus::InvalidResourceType; statusCode = RequestStatus::InvalidResourceType;
comment = "The specified source is not a scene. (Is group)"; comment = "The specified source is not a scene. (Is group)";
return nullptr; return nullptr;
} else if (filter == OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY && } else if (filter == OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY && !isGroup) {
!isGroup) {
obs_source_release(ret); obs_source_release(ret);
statusCode = RequestStatus::InvalidResourceType; statusCode = RequestStatus::InvalidResourceType;
comment = "The specified source is not a group. (Is scene)"; comment = "The specified source is not a group. (Is scene)";
@ -298,13 +259,10 @@ obs_source_t *Request::ValidateScene(const std::string &keyName,
return ret; return ret;
} }
obs_scene_t *Request::ValidateScene2(const std::string &keyName, obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode,
std::string &comment,
const ObsWebSocketSceneFilter filter) const const ObsWebSocketSceneFilter filter) const
{ {
OBSSourceAutoRelease sceneSource = OBSSourceAutoRelease sceneSource = ValidateSource(keyName, statusCode, comment);
ValidateSource(keyName, statusCode, comment);
if (!sceneSource) if (!sceneSource)
return nullptr; return nullptr;
@ -318,24 +276,21 @@ obs_scene_t *Request::ValidateScene2(const std::string &keyName,
if (isGroup) { if (isGroup) {
if (filter == OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) { if (filter == OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) {
statusCode = RequestStatus::InvalidResourceType; statusCode = RequestStatus::InvalidResourceType;
comment = comment = "The specified source is not a scene. (Is group)";
"The specified source is not a scene. (Is group)";
return nullptr; return nullptr;
} }
return obs_scene_get_ref(obs_group_from_source(sceneSource)); return obs_scene_get_ref(obs_group_from_source(sceneSource));
} else { } else {
if (filter == OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY) { if (filter == OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY) {
statusCode = RequestStatus::InvalidResourceType; statusCode = RequestStatus::InvalidResourceType;
comment = comment = "The specified source is not a group. (Is scene)";
"The specified source is not a group. (Is scene)";
return nullptr; return nullptr;
} }
return obs_scene_get_ref(obs_scene_from_source(sceneSource)); return obs_scene_get_ref(obs_scene_from_source(sceneSource));
} }
} }
obs_source_t *Request::ValidateInput(const std::string &keyName, obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
RequestStatus::RequestStatus &statusCode,
std::string &comment) const std::string &comment) const
{ {
obs_source_t *ret = ValidateSource(keyName, statusCode, comment); obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
@ -352,13 +307,10 @@ obs_source_t *Request::ValidateInput(const std::string &keyName,
return ret; return ret;
} }
FilterPair Request::ValidateFilter(const std::string &sourceKeyName, FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName,
const std::string &filterKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
RequestStatus::RequestStatus &statusCode,
std::string &comment) const
{ {
obs_source_t *source = obs_source_t *source = ValidateSource(sourceKeyName, statusCode, comment);
ValidateSource(sourceKeyName, statusCode, comment);
if (!source) if (!source)
return FilterPair{source, nullptr}; return FilterPair{source, nullptr};
@ -367,12 +319,10 @@ FilterPair Request::ValidateFilter(const std::string &sourceKeyName,
std::string filterName = RequestData[filterKeyName]; std::string filterName = RequestData[filterKeyName];
obs_source_t *filter = obs_source_t *filter = obs_source_get_filter_by_name(source, filterName.c_str());
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 `") + comment = std::string("No filter was found in the source `") + RequestData[sourceKeyName].get<std::string>() +
RequestData[sourceKeyName].get<std::string>() +
"` with the name `" + filterName + "`."; "` with the name `" + filterName + "`.";
return FilterPair{source, nullptr}; return FilterPair{source, nullptr};
} }
@ -380,13 +330,11 @@ FilterPair Request::ValidateFilter(const std::string &sourceKeyName,
return FilterPair{source, filter}; return FilterPair{source, filter};
} }
obs_sceneitem_t *Request::ValidateSceneItem( obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName,
const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
const ObsWebSocketSceneFilter filter) const
{ {
OBSSceneAutoRelease scene = OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter);
ValidateScene2(sceneKeyName, statusCode, comment, filter);
if (!scene) if (!scene)
return nullptr; return nullptr;
@ -395,14 +343,11 @@ obs_sceneitem_t *Request::ValidateSceneItem(
int64_t sceneItemId = RequestData[sceneItemIdKeyName]; int64_t sceneItemId = RequestData[sceneItemIdKeyName];
OBSSceneItem sceneItem = OBSSceneItem sceneItem = obs_scene_find_sceneitem_by_id(scene, sceneItemId);
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 `") + comment = std::string("No scene items were found in scene `") + RequestData[sceneKeyName].get<std::string>() +
RequestData[sceneKeyName].get<std::string>() + "` with the ID `" + std::to_string(sceneItemId) + "`.";
"` with the ID `" + std::to_string(sceneItemId) +
"`.";
return nullptr; return nullptr;
} }

View File

@ -36,88 +36,47 @@ struct FilterPair {
}; };
struct Request { struct Request {
Request(const std::string &requestType, Request(const std::string &requestType, const json &requestData = nullptr,
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, bool ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
RequestStatus::RequestStatus &statusCode, bool ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
std::string &comment) const; const double minValue = -INFINITY, const double maxValue = INFINITY) const;
bool ValidateOptionalNumber(const std::string &keyName, bool ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode, const double minValue = -INFINITY, const double maxValue = INFINITY) const;
std::string &comment, bool ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
const double minValue = -INFINITY,
const double maxValue = INFINITY) const;
bool ValidateNumber(const std::string &keyName,
RequestStatus::RequestStatus &statusCode,
std::string &comment,
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; const bool allowEmpty = false) const;
bool ValidateString(const std::string &keyName, bool ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode,
std::string &comment,
const bool allowEmpty = false) const; const bool allowEmpty = false) const;
bool ValidateOptionalBoolean(const std::string &keyName, bool ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
RequestStatus::RequestStatus &statusCode,
std::string &comment) const; std::string &comment) const;
bool ValidateBoolean(const std::string &keyName, bool ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
RequestStatus::RequestStatus &statusCode, bool ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
std::string &comment) const;
bool ValidateOptionalObject(const std::string &keyName,
RequestStatus::RequestStatus &statusCode,
std::string &comment,
const bool allowEmpty = false) const; const bool allowEmpty = false) const;
bool ValidateObject(const std::string &keyName, bool ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode,
std::string &comment,
const bool allowEmpty = false) const; const bool allowEmpty = false) const;
bool ValidateOptionalArray(const std::string &keyName, bool ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode,
std::string &comment,
const bool allowEmpty = false) const; const bool allowEmpty = false) const;
bool ValidateArray(const std::string &keyName, bool ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
RequestStatus::RequestStatus &statusCode,
std::string &comment,
const bool allowEmpty = false) const; 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, obs_source_t *ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
RequestStatus::RequestStatus &statusCode,
std::string &comment) const; std::string &comment) const;
obs_source_t * obs_source_t *ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
ValidateScene(const std::string &keyName, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
RequestStatus::RequestStatus &statusCode, obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
const ObsWebSocketSceneFilter filter = obs_source_t *ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) 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 *ValidateInput(const std::string &keyName,
RequestStatus::RequestStatus &statusCode,
std::string &comment) const; std::string &comment) const;
FilterPair ValidateFilter(const std::string &sourceKeyName, FilterPair ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName,
const std::string &filterKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
RequestStatus::RequestStatus &statusCode, obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName,
std::string &comment) const; RequestStatus::RequestStatus &statusCode, std::string &comment,
obs_sceneitem_t * const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
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;
std::string RequestType; std::string RequestType;
bool HasRequestData; bool HasRequestData;

View File

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

View File

@ -21,12 +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, RequestBatchRequest(const std::string &requestType, const json &requestData,
const json &requestData, RequestBatchExecutionType::RequestBatchExecutionType executionType,
RequestBatchExecutionType::RequestBatchExecutionType const json &inputVariables = nullptr, const json &outputVariables = nullptr);
executionType,
const json &inputVariables = nullptr,
const json &outputVariables = nullptr);
json InputVariables; json InputVariables;
json OutputVariables; json OutputVariables;

View File

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

View File

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

View File

@ -1,36 +1,32 @@
/* /*
obs-websocket obs-websocket
Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com> Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com>
Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com> Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License along You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/> with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include "Compat.h" #include "Compat.h"
Utils::Compat::StdFunctionRunnable::StdFunctionRunnable( Utils::Compat::StdFunctionRunnable::StdFunctionRunnable(std::function<void()> func) : cb(std::move(func)) {}
std::function<void()> func)
: cb(std::move(func)) void Utils::Compat::StdFunctionRunnable::run()
{ {
} cb();
}
void Utils::Compat::StdFunctionRunnable::run()
{ QRunnable *Utils::Compat::CreateFunctionRunnable(std::function<void()> func)
cb(); {
} return new Utils::Compat::StdFunctionRunnable(std::move(func));
}
QRunnable *Utils::Compat::CreateFunctionRunnable(std::function<void()> func)
{
return new Utils::Compat::StdFunctionRunnable(std::move(func));
}

View File

@ -24,8 +24,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "Crypto.h" #include "Crypto.h"
#include "../plugin-macros.generated.h" #include "../plugin-macros.generated.h"
static const char allowedChars[] = static const char allowedChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
static const int allowedCharsCount = static_cast<int>(sizeof(allowedChars) - 1); static const int allowedCharsCount = static_cast<int>(sizeof(allowedChars) - 1);
std::string Utils::Crypto::GenerateSalt() std::string Utils::Crypto::GenerateSalt()
@ -43,12 +42,10 @@ std::string Utils::Crypto::GenerateSalt()
return randomChars.toBase64().toStdString(); return randomChars.toBase64().toStdString();
} }
std::string Utils::Crypto::GenerateSecret(std::string password, std::string Utils::Crypto::GenerateSecret(std::string password, std::string salt)
std::string salt)
{ {
// Create challenge hash // Create challenge hash
auto challengeHash = auto challengeHash = QCryptographicHash(QCryptographicHash::Algorithm::Sha256);
QCryptographicHash(QCryptographicHash::Algorithm::Sha256);
// Add password bytes to hash // Add password bytes to hash
challengeHash.addData(QByteArray::fromStdString(password)); challengeHash.addData(QByteArray::fromStdString(password));
// Add salt bytes to hash // Add salt bytes to hash
@ -58,9 +55,7 @@ std::string Utils::Crypto::GenerateSecret(std::string password,
return challengeHash.result().toBase64().toStdString(); return challengeHash.result().toBase64().toStdString();
} }
bool Utils::Crypto::CheckAuthenticationString(std::string secret, bool Utils::Crypto::CheckAuthenticationString(std::string secret, std::string challenge, std::string authenticationString)
std::string challenge,
std::string authenticationString)
{ {
// Concatenate auth secret with the challenge sent to the user // Concatenate auth secret with the challenge sent to the user
QString secretAndChallenge = ""; QString secretAndChallenge = "";
@ -68,13 +63,10 @@ bool Utils::Crypto::CheckAuthenticationString(std::string secret,
secretAndChallenge += QString::fromStdString(challenge); secretAndChallenge += QString::fromStdString(challenge);
// Generate a SHA256 hash of secretAndChallenge // Generate a SHA256 hash of secretAndChallenge
auto hash = auto hash = QCryptographicHash::hash(secretAndChallenge.toUtf8(), QCryptographicHash::Algorithm::Sha256);
QCryptographicHash::hash(secretAndChallenge.toUtf8(),
QCryptographicHash::Algorithm::Sha256);
// Encode the SHA256 hash to Base64 // Encode the SHA256 hash to Base64
std::string expectedAuthenticationString = std::string expectedAuthenticationString = hash.toBase64().toStdString();
hash.toBase64().toStdString();
return (authenticationString == expectedAuthenticationString); return (authenticationString == expectedAuthenticationString);
} }

View File

@ -26,8 +26,7 @@ namespace Utils {
namespace Crypto { namespace Crypto {
std::string GenerateSalt(); std::string GenerateSalt();
std::string GenerateSecret(std::string password, std::string salt); std::string GenerateSecret(std::string password, std::string salt);
bool CheckAuthenticationString(std::string secret, std::string challenge, bool CheckAuthenticationString(std::string secret, std::string challenge, std::string authenticationString);
std::string authenticationString);
std::string GeneratePassword(size_t length = 16); std::string GeneratePassword(size_t length = 16);
} }
} }

View File

@ -67,13 +67,11 @@ void obs_data_set_json_object_item(obs_data_t *d, json j)
} else if (value.is_array()) { } else if (value.is_array()) {
obs_data_set_json_array(d, key.c_str(), value); obs_data_set_json_array(d, key.c_str(), value);
} else if (value.is_string()) { } else if (value.is_string()) {
obs_data_set_string(d, key.c_str(), obs_data_set_string(d, key.c_str(), value.get<std::string>().c_str());
value.get<std::string>().c_str());
} else if (value.is_number_integer()) { } else if (value.is_number_integer()) {
obs_data_set_int(d, key.c_str(), value.get<int64_t>()); obs_data_set_int(d, key.c_str(), value.get<int64_t>());
} else if (value.is_number_float()) { } else if (value.is_number_float()) {
obs_data_set_double(d, key.c_str(), obs_data_set_double(d, key.c_str(), value.get<double>());
value.get<double>());
} else if (value.is_boolean()) { } else if (value.is_boolean()) {
obs_data_set_bool(d, key.c_str(), value.get<bool>()); obs_data_set_bool(d, key.c_str(), value.get<bool>());
} }
@ -116,15 +114,13 @@ void set_json_bool(json *j, const char *name, obs_data_item_t *item)
bool val = obs_data_item_get_bool(item); bool val = obs_data_item_get_bool(item);
j->emplace(name, val); j->emplace(name, val);
} }
void set_json_object(json *j, const char *name, obs_data_item_t *item, void set_json_object(json *j, const char *name, obs_data_item_t *item, bool includeDefault)
bool includeDefault)
{ {
obs_data_t *obj = obs_data_item_get_obj(item); obs_data_t *obj = obs_data_item_get_obj(item);
j->emplace(name, Utils::Json::ObsDataToJson(obj, includeDefault)); j->emplace(name, Utils::Json::ObsDataToJson(obj, includeDefault));
obs_data_release(obj); obs_data_release(obj);
} }
void set_json_array(json *j, const char *name, obs_data_item_t *item, void set_json_array(json *j, const char *name, obs_data_item_t *item, bool includeDefault)
bool includeDefault)
{ {
json jArray = json::array(); json jArray = json::array();
obs_data_array_t *array = obs_data_item_get_array(item); obs_data_array_t *array = obs_data_item_get_array(item);
@ -132,8 +128,7 @@ void set_json_array(json *j, const char *name, obs_data_item_t *item,
for (size_t idx = 0; idx < count; idx++) { for (size_t idx = 0; idx < count; idx++) {
obs_data_t *subItem = obs_data_array_item(array, idx); obs_data_t *subItem = obs_data_array_item(array, idx);
json jItem = json jItem = Utils::Json::ObsDataToJson(subItem, includeDefault);
Utils::Json::ObsDataToJson(subItem, includeDefault);
obs_data_release(subItem); obs_data_release(subItem);
jArray.push_back(jItem); jArray.push_back(jItem);
} }
@ -189,18 +184,14 @@ 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, blog(LOG_WARNING, "Failed to decode content of JSON file `%s`. Error: %s", fileName.c_str(), e.what());
"Failed to decode content of JSON file `%s`. Error: %s",
fileName.c_str(), e.what());
return false; return false;
} }
return true; return true;
} }
bool Utils::Json::SetJsonFileContent(std::string fileName, const json &content, bool Utils::Json::SetJsonFileContent(std::string fileName, const json &content, bool createNew)
bool createNew)
{ {
std::string textContent = content.dump(2); std::string textContent = content.dump(2);
return Utils::Platform::SetTextFileContent(fileName, textContent, return Utils::Platform::SetTextFileContent(fileName, textContent, createNew);
createNew);
} }

View File

@ -31,8 +31,7 @@ bool JsonArrayIsValidObsArray(const json &j);
obs_data_t *JsonToObsData(json j); obs_data_t *JsonToObsData(json j);
json ObsDataToJson(obs_data_t *d, bool includeDefault = false); json ObsDataToJson(obs_data_t *d, bool includeDefault = false);
bool GetJsonFileContent(std::string fileName, json &content); bool GetJsonFileContent(std::string fileName, json &content);
bool SetJsonFileContent(std::string fileName, const json &content, bool SetJsonFileContent(std::string fileName, const json &content, bool createNew = true);
bool createNew = true);
static inline bool Contains(const json &j, std::string key) static inline bool Contains(const json &j, std::string key)
{ {
return j.contains(key) && !j[key].is_null(); return j.contains(key) && !j[key].is_null();

View File

@ -27,9 +27,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
// Autorelease object definitions // Autorelease object definitions
inline void ___properties_dummy_addref(obs_properties_t *) {} inline void ___properties_dummy_addref(obs_properties_t *) {}
using OBSPropertiesAutoDestroy = using OBSPropertiesAutoDestroy = OBSRef<obs_properties_t *, ___properties_dummy_addref, obs_properties_destroy>;
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 *) {}
@ -46,41 +44,22 @@ 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 = using OBSSourceAutoRelease = OBSRef<obs_source_t *, ___source_dummy_addref, obs_source_release>;
OBSRef<obs_source_t *, ___source_dummy_addref, obs_source_release>; using OBSSceneAutoRelease = OBSRef<obs_scene_t *, ___scene_dummy_addref, obs_scene_release>;
using OBSSceneAutoRelease = using OBSSceneItemAutoRelease = OBSRef<obs_sceneitem_t *, ___sceneitem_dummy_addref, obs_sceneitem_release>;
OBSRef<obs_scene_t *, ___scene_dummy_addref, obs_scene_release>; using OBSDataAutoRelease = OBSRef<obs_data_t *, ___data_dummy_addref, obs_data_release>;
using OBSSceneItemAutoRelease = using OBSDataArrayAutoRelease = OBSRef<obs_data_array_t *, ___data_array_dummy_addref, obs_data_array_release>;
OBSRef<obs_sceneitem_t *, ___sceneitem_dummy_addref, using OBSOutputAutoRelease = OBSRef<obs_output_t *, ___output_dummy_addref, obs_output_release>;
obs_sceneitem_release>; using OBSEncoderAutoRelease = OBSRef<obs_encoder_t *, ___encoder_dummy_addref, obs_encoder_release>;
using OBSDataAutoRelease = using OBSServiceAutoRelease = OBSRef<obs_service_t *, ___service_dummy_addref, obs_service_release>;
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 OBSOutputAutoRelease =
OBSRef<obs_output_t *, ___output_dummy_addref, obs_output_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 OBSWeakSourceAutoRelease = using OBSWeakSourceAutoRelease = OBSRef<obs_weak_source_t *, ___weak_source_dummy_addref, obs_weak_source_release>;
OBSRef<obs_weak_source_t *, ___weak_source_dummy_addref, using OBSWeakOutputAutoRelease = OBSRef<obs_weak_output_t *, ___weak_output_dummy_addref, obs_weak_output_release>;
obs_weak_source_release>; using OBSWeakEncoderAutoRelease = OBSRef<obs_weak_encoder_t *, ___weak_encoder_dummy_addref, obs_weak_encoder_release>;
using OBSWeakOutputAutoRelease = using OBSWeakServiceAutoRelease = OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref, obs_weak_service_release>;
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 OBSWeakServiceAutoRelease =
OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref,
obs_weak_service_release>;
#endif #endif
template<typename T> template<typename T> T *GetCalldataPointer(const calldata_t *data, const char *name)
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);
@ -211,8 +190,7 @@ std::vector<json> GetSceneList();
std::vector<std::string> GetGroupList(); std::vector<std::string> GetGroupList();
std::vector<json> GetSceneItemList(obs_scene_t *scene, bool basic = false); std::vector<json> GetSceneItemList(obs_scene_t *scene, bool basic = false);
std::vector<json> GetInputList(std::string inputKind = ""); std::vector<json> GetInputList(std::string inputKind = "");
std::vector<std::string> GetInputKindList(bool unversioned = false, std::vector<std::string> GetInputKindList(bool unversioned = false, bool includeDisabled = false);
bool includeDisabled = false);
std::vector<json> GetListPropertyItems(obs_property_t *property); std::vector<json> GetListPropertyItems(obs_property_t *property);
std::vector<std::string> GetTransitionKindList(); std::vector<std::string> GetTransitionKindList();
std::vector<json> GetSceneTransitionList(); std::vector<json> GetSceneTransitionList();
@ -227,31 +205,20 @@ json GetSceneItemTransform(obs_sceneitem_t *item);
namespace SearchHelper { namespace SearchHelper {
obs_hotkey_t *GetHotkeyByName(std::string name); obs_hotkey_t *GetHotkeyByName(std::string name);
obs_source_t *GetSceneTransitionByName( obs_source_t *GetSceneTransitionByName(std::string name); // Increments source ref. Use OBSSourceAutoRelease
std::string name); // Increments source ref. Use OBSSourceAutoRelease obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name,
obs_sceneitem_t *GetSceneItemByName( int offset = 0); // Increments ref. Use OBSSceneItemAutoRelease
obs_scene_t *scene, std::string name,
int offset = 0); // Increments ref. Use OBSSceneItemAutoRelease
} }
namespace ActionHelper { namespace ActionHelper {
obs_sceneitem_t * obs_sceneitem_t *CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled = true,
CreateSceneItem(obs_source_t *source, obs_scene_t *scene, obs_transform_info *sceneItemTransform = nullptr,
bool sceneItemEnabled = true, obs_sceneitem_crop *sceneItemCrop = nullptr); // Increments ref. Use OBSSceneItemAutoRelease
obs_transform_info *sceneItemTransform = nullptr, obs_sceneitem_t *CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene,
obs_sceneitem_crop *sceneItemCrop = bool sceneItemEnabled = true); // Increments ref. Use OBSSceneItemAutoRelease
nullptr); // Increments ref. Use OBSSceneItemAutoRelease obs_source_t *CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind,
obs_sceneitem_t * obs_data_t *filterSettings); // Increments source ref. Use OBSSourceAutoRelease
CreateInput(std::string inputName, std::string inputKind, void SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index);
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);
} }
} }
} }

View File

@ -33,8 +33,7 @@ void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
data->sceneItem = obs_scene_add(scene, data->source); data->sceneItem = obs_scene_add(scene, data->source);
if (data->sceneItemTransform) if (data->sceneItemTransform)
obs_sceneitem_set_info(data->sceneItem, obs_sceneitem_set_info(data->sceneItem, data->sceneItemTransform);
data->sceneItemTransform);
if (data->sceneItemCrop) if (data->sceneItemCrop)
obs_sceneitem_set_crop(data->sceneItem, data->sceneItemCrop); obs_sceneitem_set_crop(data->sceneItem, data->sceneItemCrop);
@ -42,10 +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_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled,
obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled, obs_transform_info *sceneItemTransform,
obs_transform_info *sceneItemTransform, obs_sceneitem_crop *sceneItemCrop)
obs_sceneitem_crop *sceneItemCrop)
{ {
// Sanity check for valid scene // Sanity check for valid scene
if (!(source && scene)) if (!(source && scene))
@ -68,13 +66,11 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(
return data.sceneItem; return data.sceneItem;
} }
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput( obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings,
std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled)
obs_scene_t *scene, bool sceneItemEnabled)
{ {
// Create the input // Create the input
OBSSourceAutoRelease input = obs_source_create( OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
// Check that everything was created properly // Check that everything was created properly
if (!input) if (!input)
@ -83,8 +79,7 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(
// Apparently not all default input properties actually get applied on creation (smh) // Apparently not all default input properties actually get applied on creation (smh)
uint32_t flags = obs_source_get_output_flags(input); uint32_t flags = obs_source_get_output_flags(input);
if ((flags & OBS_SOURCE_MONITOR_BY_DEFAULT) != 0) if ((flags & OBS_SOURCE_MONITOR_BY_DEFAULT) != 0)
obs_source_set_monitoring_type( obs_source_set_monitoring_type(input, OBS_MONITORING_TYPE_MONITOR_ONLY);
input, OBS_MONITORING_TYPE_MONITOR_ONLY);
// Create a scene item for the input // Create a scene item for the input
obs_sceneitem_t *ret = CreateSceneItem(input, scene, sceneItemEnabled); obs_sceneitem_t *ret = CreateSceneItem(input, scene, sceneItemEnabled);
@ -96,12 +91,10 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(
return ret; return ret;
} }
obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter( obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind,
obs_source_t *source, std::string filterName, std::string filterKind, obs_data_t *filterSettings)
obs_data_t *filterSettings)
{ {
obs_source_t *filter = obs_source_create_private( obs_source_t *filter = obs_source_create_private(filterKind.c_str(), filterName.c_str(), filterSettings);
filterKind.c_str(), filterName.c_str(), filterSettings);
if (!filter) if (!filter)
return nullptr; return nullptr;
@ -111,14 +104,10 @@ obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter(
return filter; return filter;
} }
void Utils::Obs::ActionHelper::SetSourceFilterIndex(obs_source_t *source, void Utils::Obs::ActionHelper::SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index)
obs_source_t *filter,
size_t index)
{ {
size_t currentIndex = size_t currentIndex = Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter);
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);

View File

@ -62,8 +62,7 @@ std::vector<obs_hotkey_t *> Utils::Obs::ArrayHelper::GetHotkeyList()
obs_enum_hotkeys( obs_enum_hotkeys(
[](void *data, obs_hotkey_id, obs_hotkey_t *hotkey) { [](void *data, obs_hotkey_id, obs_hotkey_t *hotkey) {
auto ret = static_cast<std::vector<obs_hotkey_t *> *>( auto ret = static_cast<std::vector<obs_hotkey_t *> *>(data);
data);
ret->push_back(hotkey); ret->push_back(hotkey);
@ -130,8 +129,7 @@ std::vector<std::string> Utils::Obs::ArrayHelper::GetGroupList()
return ret; return ret;
} }
std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene, std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene, bool basic)
bool basic)
{ {
std::pair<std::vector<json>, bool> enumData; std::pair<std::vector<json>, bool> enumData;
enumData.second = basic; enumData.second = basic;
@ -139,30 +137,22 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene,
obs_scene_enum_items( obs_scene_enum_items(
scene, scene,
[](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) { [](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
auto enumData = static_cast< auto enumData = static_cast<std::pair<std::vector<json>, bool> *>(param);
std::pair<std::vector<json>, bool> *>(param);
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() // Should be slightly faster than calling obs_sceneitem_get_order_position()
item["sceneItemIndex"] = enumData->first.size(); item["sceneItemIndex"] = enumData->first.size();
if (!enumData->second) { if (!enumData->second) {
OBSSource itemSource = OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
obs_sceneitem_get_source(sceneItem); item["sourceName"] = obs_source_get_name(itemSource);
item["sourceName"] = item["sourceType"] = StringHelper::GetSourceType(itemSource);
obs_source_get_name(itemSource); if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
item["sourceType"] = item["inputKind"] = obs_source_get_id(itemSource);
StringHelper::GetSourceType(itemSource);
if (obs_source_get_type(itemSource) ==
OBS_SOURCE_TYPE_INPUT)
item["inputKind"] =
obs_source_get_id(itemSource);
else else
item["inputKind"] = nullptr; item["inputKind"] = nullptr;
if (obs_source_get_type(itemSource) == if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_SCENE)
OBS_SOURCE_TYPE_SCENE) item["isGroup"] = obs_source_is_group(itemSource);
item["isGroup"] =
obs_source_is_group(itemSource);
else else
item["isGroup"] = nullptr; item["isGroup"] = nullptr;
} }
@ -195,15 +185,13 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
std::string inputKind = obs_source_get_id(input); std::string inputKind = obs_source_get_id(input);
if (!inputInfo->inputKind.empty() && if (!inputInfo->inputKind.empty() && inputInfo->inputKind != inputKind)
inputInfo->inputKind != inputKind)
return true; return true;
json inputJson; json inputJson;
inputJson["inputName"] = obs_source_get_name(input); inputJson["inputName"] = obs_source_get_name(input);
inputJson["inputKind"] = inputKind; inputJson["inputKind"] = inputKind;
inputJson["unversionedInputKind"] = inputJson["unversionedInputKind"] = obs_source_get_unversioned_id(input);
obs_source_get_unversioned_id(input);
inputInfo->inputs.push_back(inputJson); inputInfo->inputs.push_back(inputJson);
return true; return true;
@ -214,9 +202,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
return inputInfo.inputs; return inputInfo.inputs;
} }
std::vector<std::string> std::vector<std::string> Utils::Obs::ArrayHelper::GetInputKindList(bool unversioned, bool includeDisabled)
Utils::Obs::ArrayHelper::GetInputKindList(bool unversioned,
bool includeDisabled)
{ {
std::vector<std::string> ret; std::vector<std::string> ret;
@ -238,8 +224,7 @@ Utils::Obs::ArrayHelper::GetInputKindList(bool unversioned,
return ret; return ret;
} }
std::vector<json> std::vector<json> Utils::Obs::ArrayHelper::GetListPropertyItems(obs_property_t *property)
Utils::Obs::ArrayHelper::GetListPropertyItems(obs_property_t *property)
{ {
std::vector<json> ret; std::vector<json> ret;
@ -251,17 +236,13 @@ Utils::Obs::ArrayHelper::GetListPropertyItems(obs_property_t *property)
for (size_t i = 0; i < itemCount; i++) { for (size_t i = 0; i < itemCount; i++) {
json itemData; json itemData;
itemData["itemName"] = obs_property_list_item_name(property, i); itemData["itemName"] = obs_property_list_item_name(property, i);
itemData["itemEnabled"] = itemData["itemEnabled"] = !obs_property_list_item_disabled(property, i);
!obs_property_list_item_disabled(property, i);
if (itemFormat == OBS_COMBO_FORMAT_INT) { if (itemFormat == OBS_COMBO_FORMAT_INT) {
itemData["itemValue"] = itemData["itemValue"] = obs_property_list_item_int(property, i);
obs_property_list_item_int(property, i);
} else if (itemFormat == OBS_COMBO_FORMAT_FLOAT) { } else if (itemFormat == OBS_COMBO_FORMAT_FLOAT) {
itemData["itemValue"] = itemData["itemValue"] = obs_property_list_item_float(property, i);
obs_property_list_item_float(property, i);
} else if (itemFormat == OBS_COMBO_FORMAT_STRING) { } else if (itemFormat == OBS_COMBO_FORMAT_STRING) {
itemData["itemValue"] = itemData["itemValue"] = obs_property_list_item_string(property, i);
obs_property_list_item_string(property, i);
} else { } else {
itemData["itemValue"] = nullptr; itemData["itemValue"] = nullptr;
} }
@ -293,14 +274,10 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneTransitionList()
for (size_t i = 0; i < transitionList.sources.num; i++) { for (size_t i = 0; i < transitionList.sources.num; i++) {
obs_source_t *transition = transitionList.sources.array[i]; obs_source_t *transition = transitionList.sources.array[i];
json transitionJson; json transitionJson;
transitionJson["transitionName"] = transitionJson["transitionName"] = obs_source_get_name(transition);
obs_source_get_name(transition); transitionJson["transitionKind"] = obs_source_get_id(transition);
transitionJson["transitionKind"] = transitionJson["transitionFixed"] = obs_transition_fixed(transition);
obs_source_get_id(transition); transitionJson["transitionConfigurable"] = obs_source_configurable(transition);
transitionJson["transitionFixed"] =
obs_transition_fixed(transition);
transitionJson["transitionConfigurable"] =
obs_source_configurable(transition);
ret.push_back(transitionJson); ret.push_back(transitionJson);
} }
@ -321,13 +298,11 @@ std::vector<std::string> Utils::Obs::ArrayHelper::GetFilterKindList()
return ret; return ret;
} }
std::vector<json> std::vector<json> Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *source)
Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *source)
{ {
std::vector<json> filters; std::vector<json> filters;
auto enumFilters = [](obs_source_t *, obs_source_t *filter, auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param) {
void *param) {
auto filters = reinterpret_cast<std::vector<json> *>(param); auto filters = reinterpret_cast<std::vector<json> *>(param);
json filterJson; json filterJson;
@ -336,10 +311,8 @@ Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *source)
filterJson["filterKind"] = obs_source_get_id(filter); filterJson["filterKind"] = obs_source_get_id(filter);
filterJson["filterName"] = obs_source_get_name(filter); filterJson["filterName"] = obs_source_get_name(filter);
OBSDataAutoRelease filterSettings = OBSDataAutoRelease filterSettings = obs_source_get_settings(filter);
obs_source_get_settings(filter); filterJson["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
filterJson["filterSettings"] =
Utils::Json::ObsDataToJson(filterSettings);
filters->push_back(filterJson); filters->push_back(filterJson);
}; };

View File

@ -23,8 +23,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
if (str == #x) \ if (str == #x) \
return x; return x;
enum obs_bounds_type enum obs_bounds_type Utils::Obs::EnumHelper::GetSceneItemBoundsType(std::string boundsType)
Utils::Obs::EnumHelper::GetSceneItemBoundsType(std::string boundsType)
{ {
RET_COMPARE(boundsType, OBS_BOUNDS_NONE) RET_COMPARE(boundsType, OBS_BOUNDS_NONE)
RET_COMPARE(boundsType, OBS_BOUNDS_STRETCH) RET_COMPARE(boundsType, OBS_BOUNDS_STRETCH)
@ -37,8 +36,7 @@ Utils::Obs::EnumHelper::GetSceneItemBoundsType(std::string boundsType)
return OBS_BOUNDS_NONE; return OBS_BOUNDS_NONE;
} }
enum ObsMediaInputAction enum ObsMediaInputAction Utils::Obs::EnumHelper::GetMediaInputAction(std::string mediaAction)
Utils::Obs::EnumHelper::GetMediaInputAction(std::string mediaAction)
{ {
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY) 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_PAUSE)
@ -50,8 +48,7 @@ Utils::Obs::EnumHelper::GetMediaInputAction(std::string mediaAction)
return OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE; return OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE;
} }
enum obs_blending_type enum obs_blending_type Utils::Obs::EnumHelper::GetSceneItemBlendMode(std::string mode)
Utils::Obs::EnumHelper::GetSceneItemBlendMode(std::string mode)
{ {
RET_COMPARE(mode, OBS_BLEND_NORMAL) RET_COMPARE(mode, OBS_BLEND_NORMAL)
RET_COMPARE(mode, OBS_BLEND_ADDITIVE) RET_COMPARE(mode, OBS_BLEND_ADDITIVE)

View File

@ -53,8 +53,7 @@ size_t Utils::Obs::NumberHelper::GetSceneCount()
return ret; return ret;
} }
size_t Utils::Obs::NumberHelper::GetSourceFilterIndex(obs_source_t *source, size_t Utils::Obs::NumberHelper::GetSourceFilterIndex(obs_source_t *source, obs_source_t *filter)
obs_source_t *filter)
{ {
struct FilterSearch { struct FilterSearch {
obs_source_t *filter; obs_source_t *filter;
@ -62,8 +61,7 @@ size_t Utils::Obs::NumberHelper::GetSourceFilterIndex(obs_source_t *source,
size_t index; size_t index;
}; };
auto search = [](obs_source_t *, obs_source_t *filter, auto search = [](obs_source_t *, obs_source_t *filter, void *priv_data) {
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)

View File

@ -27,20 +27,15 @@ json Utils::Obs::ObjectHelper::GetStats()
{ {
json ret; json ret;
std::string outputPath = std::string outputPath = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
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"] = ret["memoryUsage"] = (double)os_get_proc_resident_size() / (1024.0 * 1024.0);
(double)os_get_proc_resident_size() / (1024.0 * 1024.0); ret["availableDiskSpace"] = (double)os_get_free_disk_space(outputPath.c_str()) / (1024.0 * 1024.0);
ret["availableDiskSpace"] =
(double)os_get_free_disk_space(outputPath.c_str()) /
(1024.0 * 1024.0);
ret["activeFps"] = obs_get_active_fps(); ret["activeFps"] = obs_get_active_fps();
ret["averageFrameRenderTime"] = ret["averageFrameRenderTime"] = (double)obs_get_average_frame_time_ns() / 1000000.0;
(double)obs_get_average_frame_time_ns() / 1000000.0;
ret["renderSkippedFrames"] = obs_get_lagged_frames(); ret["renderSkippedFrames"] = obs_get_lagged_frames();
ret["renderTotalFrames"] = obs_get_total_frames(); ret["renderTotalFrames"] = obs_get_total_frames();
ret["outputSkippedFrames"] = video_output_get_skipped_frames(video); ret["outputSkippedFrames"] = video_output_get_skipped_frames(video);
@ -78,8 +73,7 @@ json Utils::Obs::ObjectHelper::GetSceneItemTransform(obs_sceneitem_t *item)
ret["alignment"] = osi.alignment; ret["alignment"] = osi.alignment;
ret["boundsType"] = ret["boundsType"] = StringHelper::GetSceneItemBoundsType(osi.bounds_type);
StringHelper::GetSceneItemBoundsType(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;

View File

@ -35,8 +35,7 @@ obs_hotkey_t *Utils::Obs::SearchHelper::GetHotkeyByName(std::string name)
} }
// Increments source ref. Use OBSSourceAutoRelease // Increments source ref. Use OBSSourceAutoRelease
obs_source_t * obs_source_t *Utils::Obs::SearchHelper::GetSceneTransitionByName(std::string name)
Utils::Obs::SearchHelper::GetSceneTransitionByName(std::string name)
{ {
obs_frontend_source_list transitionList = {}; obs_frontend_source_list transitionList = {};
obs_frontend_get_transitions(&transitionList); obs_frontend_get_transitions(&transitionList);
@ -62,9 +61,7 @@ struct SceneItemSearchData {
}; };
// Increments item ref. Use OBSSceneItemAutoRelease // Increments item ref. Use OBSSceneItemAutoRelease
obs_sceneitem_t * obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name, int offset)
Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene,
std::string name, int offset)
{ {
if (name.empty()) if (name.empty())
return nullptr; return nullptr;
@ -76,24 +73,19 @@ Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene,
obs_scene_enum_items( obs_scene_enum_items(
scene, scene,
[](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) { [](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
auto enumData = auto enumData = static_cast<SceneItemSearchData *>(param);
static_cast<SceneItemSearchData *>(param);
OBSSource itemSource = OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
obs_sceneitem_get_source(sceneItem); std::string sourceName = obs_source_get_name(itemSource);
std::string sourceName =
obs_source_get_name(itemSource);
if (sourceName == enumData->name) { if (sourceName == enumData->name) {
if (enumData->offset > 0) { if (enumData->offset > 0) {
enumData->offset--; enumData->offset--;
} else { } else {
if (enumData->ret) // Release existing selection in the case of last match selection if (enumData->ret) // Release existing selection in the case of last match selection
obs_sceneitem_release( obs_sceneitem_release(enumData->ret);
enumData->ret);
obs_sceneitem_addref(sceneItem); obs_sceneitem_addref(sceneItem);
enumData->ret = sceneItem; enumData->ret = sceneItem;
if (enumData->offset == if (enumData->offset == 0) // Only break if in normal selection mode (not offset == -1)
0) // Only break if in normal selection mode (not offset == -1)
return false; return false;
} }
} }

View File

@ -85,8 +85,7 @@ std::string Utils::Obs::StringHelper::GetSourceType(obs_source_t *source)
} }
} }
std::string Utils::Obs::StringHelper::GetInputMonitorType( std::string Utils::Obs::StringHelper::GetInputMonitorType(enum obs_monitoring_type monitorType)
enum obs_monitoring_type monitorType)
{ {
switch (monitorType) { switch (monitorType) {
default: default:
@ -138,8 +137,7 @@ std::string Utils::Obs::StringHelper::GetLastReplayBufferFilePath()
return savedReplayPath; return savedReplayPath;
} }
std::string std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
{ {
switch (type) { switch (type) {
default: default:
@ -153,8 +151,7 @@ Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
} }
} }
std::string std::string Utils::Obs::StringHelper::GetSceneItemBlendMode(enum obs_blending_type mode)
Utils::Obs::StringHelper::GetSceneItemBlendMode(enum obs_blending_type mode)
{ {
switch (mode) { switch (mode) {
default: default:
@ -178,10 +175,8 @@ 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 QString formatted =
":%02" PRIu64 ".%03" PRIu64, QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
hoursPart, minutesPart, secsPart,
msPart);
return formatted.toStdString(); return formatted.toStdString();
} }

View File

@ -36,12 +36,9 @@ Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *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);
obs_source_add_audio_capture_callback( obs_source_add_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this);
input, Meter::InputAudioCaptureCallback, this);
blog_debug( blog_debug("[Utils::Obs::VolumeMeter::Meter::Meter] Meter created for input: %s", obs_source_get_name(input));
"[Utils::Obs::VolumeMeter::Meter::Meter] Meter created for input: %s",
obs_source_get_name(input));
} }
Utils::Obs::VolumeMeter::Meter::~Meter() Utils::Obs::VolumeMeter::Meter::~Meter()
@ -54,15 +51,11 @@ Utils::Obs::VolumeMeter::Meter::~Meter()
} }
signal_handler_t *sh = obs_source_get_signal_handler(input); signal_handler_t *sh = obs_source_get_signal_handler(input);
signal_handler_disconnect(sh, "volume", Meter::InputVolumeCallback, signal_handler_disconnect(sh, "volume", Meter::InputVolumeCallback, this);
this);
obs_source_remove_audio_capture_callback( obs_source_remove_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this);
input, Meter::InputAudioCaptureCallback, this);
blog_debug( blog_debug("[Utils::Obs::VolumeMeter::Meter::~Meter] Meter destroyed for input: %s", obs_source_get_name(input));
"[Utils::Obs::VolumeMeter::Meter::~Meter] Meter destroyed for input: %s",
obs_source_get_name(input));
} }
bool Utils::Obs::VolumeMeter::Meter::InputValid() bool Utils::Obs::VolumeMeter::Meter::InputValid()
@ -86,8 +79,7 @@ json Utils::Obs::VolumeMeter::Meter::GetMeterData()
std::unique_lock<std::mutex> l(_mutex); std::unique_lock<std::mutex> l(_mutex);
if (_lastUpdate != 0 && if (_lastUpdate != 0 && (os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3)
(os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3)
ResetAudioLevels(); ResetAudioLevels();
for (int channel = 0; channel < _channels; channel++) { for (int channel = 0; channel < _channels; channel++) {
@ -110,16 +102,14 @@ json Utils::Obs::VolumeMeter::Meter::GetMeterData()
void Utils::Obs::VolumeMeter::Meter::ResetAudioLevels() void Utils::Obs::VolumeMeter::Meter::ResetAudioLevels()
{ {
_lastUpdate = 0; _lastUpdate = 0;
for (int channelNumber = 0; channelNumber < MAX_AUDIO_CHANNELS; for (int channelNumber = 0; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) {
channelNumber++) {
_magnitude[channelNumber] = 0; _magnitude[channelNumber] = 0;
_peak[channelNumber] = 0; _peak[channelNumber] = 0;
} }
} }
// MUST HOLD LOCK // MUST HOLD LOCK
void Utils::Obs::VolumeMeter::Meter::ProcessAudioChannels( void Utils::Obs::VolumeMeter::Meter::ProcessAudioChannels(const struct audio_data *data)
const struct audio_data *data)
{ {
int channels = 0; int channels = 0;
for (int i = 0; i < MAX_AV_PLANES; i++) { for (int i = 0; i < MAX_AV_PLANES; i++) {
@ -151,19 +141,16 @@ void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
continue; continue;
} }
__m128 previousSamples = __m128 previousSamples = _mm_loadu_ps(_previousSamples[channelNumber]);
_mm_loadu_ps(_previousSamples[channelNumber]);
float peak; float peak;
switch (PeakMeterType) { switch (PeakMeterType) {
default: default:
case SAMPLE_PEAK_METER: case SAMPLE_PEAK_METER:
peak = GetSamplePeak(previousSamples, samples, peak = GetSamplePeak(previousSamples, samples, sampleCount);
sampleCount);
break; break;
case TRUE_PEAK_METER: case TRUE_PEAK_METER:
peak = GetTruePeak(previousSamples, samples, peak = GetTruePeak(previousSamples, samples, sampleCount);
sampleCount);
break; break;
} }
@ -171,44 +158,28 @@ void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
case 0: case 0:
break; break;
case 1: case 1:
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][0] = _previousSamples[channelNumber][1];
_previousSamples[channelNumber][1]; _previousSamples[channelNumber][1] = _previousSamples[channelNumber][2];
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][2] = _previousSamples[channelNumber][3];
_previousSamples[channelNumber][2]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
_previousSamples[channelNumber][2] =
_previousSamples[channelNumber][3];
_previousSamples[channelNumber][3] =
samples[sampleCount - 1];
break; break;
case 2: case 2:
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][0] = _previousSamples[channelNumber][2];
_previousSamples[channelNumber][2]; _previousSamples[channelNumber][1] = _previousSamples[channelNumber][3];
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][2] = samples[sampleCount - 2];
_previousSamples[channelNumber][3]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
_previousSamples[channelNumber][2] =
samples[sampleCount - 2];
_previousSamples[channelNumber][3] =
samples[sampleCount - 1];
break; break;
case 3: case 3:
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][0] = _previousSamples[channelNumber][3];
_previousSamples[channelNumber][3]; _previousSamples[channelNumber][1] = samples[sampleCount - 3];
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][2] = samples[sampleCount - 2];
samples[sampleCount - 3]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
_previousSamples[channelNumber][2] =
samples[sampleCount - 2];
_previousSamples[channelNumber][3] =
samples[sampleCount - 1];
break; break;
default: default:
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][0] = samples[sampleCount - 4];
samples[sampleCount - 4]; _previousSamples[channelNumber][1] = samples[sampleCount - 3];
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][2] = samples[sampleCount - 2];
samples[sampleCount - 3]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
_previousSamples[channelNumber][2] =
samples[sampleCount - 2];
_previousSamples[channelNumber][3] =
samples[sampleCount - 1];
} }
_peak[channelNumber] = peak; _peak[channelNumber] = peak;
@ -221,8 +192,7 @@ void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
} }
// MUST HOLD LOCK // MUST HOLD LOCK
void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude( void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *data)
const struct audio_data *data)
{ {
size_t sampleCount = data->frames; size_t sampleCount = data->frames;
@ -244,9 +214,8 @@ void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(
} }
} }
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback( void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data,
void *priv_data, obs_source_t *, const struct audio_data *data, bool muted)
bool muted)
{ {
auto c = static_cast<Meter *>(priv_data); auto c = static_cast<Meter *>(priv_data);
@ -260,16 +229,14 @@ void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(
c->_lastUpdate = os_gettime_ns(); c->_lastUpdate = os_gettime_ns();
} }
void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd)
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, Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod)
uint64_t updatePeriod)
: _updateCallback(cb), _updatePeriod(updatePeriod), _running(false) : _updateCallback(cb), _updatePeriod(updatePeriod), _running(false)
{ {
signal_handler_t *sh = obs_get_signal_handler(); signal_handler_t *sh = obs_get_signal_handler();
@ -292,16 +259,13 @@ Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb,
}; };
obs_enum_sources(enumProc, this); obs_enum_sources(enumProc, this);
signal_handler_connect(sh, "source_activate", signal_handler_connect(sh, "source_activate", Handler::InputActivateCallback, this);
Handler::InputActivateCallback, this); signal_handler_connect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
signal_handler_connect(sh, "source_deactivate",
Handler::InputDeactivateCallback, this);
_running = true; _running = true;
_updateThread = std::thread(&Handler::UpdateThread, this); _updateThread = std::thread(&Handler::UpdateThread, this);
blog_debug( blog_debug("[Utils::Obs::VolumeMeter::Handler::Handler] Handler created.");
"[Utils::Obs::VolumeMeter::Handler::Handler] Handler created.");
} }
Utils::Obs::VolumeMeter::Handler::~Handler() Utils::Obs::VolumeMeter::Handler::~Handler()
@ -310,10 +274,8 @@ Utils::Obs::VolumeMeter::Handler::~Handler()
if (!sh) if (!sh)
return; return;
signal_handler_disconnect(sh, "source_activate", signal_handler_disconnect(sh, "source_activate", Handler::InputActivateCallback, this);
Handler::InputActivateCallback, this); signal_handler_disconnect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
signal_handler_disconnect(sh, "source_deactivate",
Handler::InputDeactivateCallback, this);
if (_running) { if (_running) {
_running = false; _running = false;
@ -323,20 +285,16 @@ Utils::Obs::VolumeMeter::Handler::~Handler()
if (_updateThread.joinable()) if (_updateThread.joinable())
_updateThread.join(); _updateThread.join();
blog_debug( blog_debug("[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed.");
"[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed.");
} }
void Utils::Obs::VolumeMeter::Handler::UpdateThread() void Utils::Obs::VolumeMeter::Handler::UpdateThread()
{ {
blog_debug( blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread started.");
"[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread started.");
while (_running) { while (_running) {
{ {
std::unique_lock<std::mutex> l(_mutex); std::unique_lock<std::mutex> l(_mutex);
if (_cond.wait_for( if (_cond.wait_for(l, std::chrono::milliseconds(_updatePeriod), [this] { return !_running; }))
l, std::chrono::milliseconds(_updatePeriod),
[this] { return !_running; }))
break; break;
} }
@ -351,12 +309,10 @@ void Utils::Obs::VolumeMeter::Handler::UpdateThread()
if (_updateCallback) if (_updateCallback)
_updateCallback(inputs); _updateCallback(inputs);
} }
blog_debug( blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped.");
"[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped.");
} }
void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd)
calldata_t *cd)
{ {
auto c = static_cast<Handler *>(priv_data); auto c = static_cast<Handler *>(priv_data);
@ -375,8 +331,7 @@ void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data,
c->_meters.emplace_back(std::move(new Meter(input))); c->_meters.emplace_back(std::move(new Meter(input)));
} }
void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data, void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data, calldata_t *cd)
calldata_t *cd)
{ {
auto c = static_cast<Handler *>(priv_data); auto c = static_cast<Handler *>(priv_data);
@ -391,8 +346,7 @@ void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data,
std::unique_lock<std::mutex> l(c->_meterMutex); std::unique_lock<std::mutex> l(c->_meterMutex);
std::vector<MeterPtr>::iterator iter; std::vector<MeterPtr>::iterator iter;
for (iter = c->_meters.begin(); iter != c->_meters.end();) { for (iter = c->_meters.begin(); iter != c->_meters.end();) {
if (obs_weak_source_references_source( if (obs_weak_source_references_source(iter->get()->GetWeakInput(), input))
iter->get()->GetWeakInput(), input))
iter = c->_meters.erase(iter); iter = c->_meters.erase(iter);
else else
++iter; ++iter;

View File

@ -65,10 +65,7 @@ private:
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, static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source, const struct audio_data *data, bool muted);
obs_source_t *source,
const struct audio_data *data,
bool muted);
static void InputVolumeCallback(void *priv_data, calldata_t *cd); static void InputVolumeCallback(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)
@ -56,8 +55,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
r = fmaxf(r, x4_mem[3]); \ r = fmaxf(r, x4_mem[3]); \
} while (false) } while (false)
static float GetSamplePeak(__m128 previousSamples, const float *samples, static float GetSamplePeak(__m128 previousSamples, const float *samples, size_t sampleCount)
size_t sampleCount)
{ {
__m128 peak = previousSamples; __m128 peak = previousSamples;
for (size_t i = 0; (i + 3) < sampleCount; i += 4) { for (size_t i = 0; (i + 3) < sampleCount; i += 4) {
@ -70,17 +68,12 @@ static float GetSamplePeak(__m128 previousSamples, const float *samples,
return ret; return ret;
} }
static float GetTruePeak(__m128 previousSamples, const float *samples, static float GetTruePeak(__m128 previousSamples, const float *samples, size_t sampleCount)
size_t sampleCount)
{ {
const __m128 m3 = const __m128 m3 = _mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f);
_mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f); const __m128 m1 = _mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f);
const __m128 m1 = const __m128 p1 = _mm_set_ps(-0.189207f, 0.504551f, 0.756827f, -0.216236f);
_mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f); const __m128 p3 = _mm_set_ps(-0.103943f, 0.233872f, 0.935489f, -0.155915f);
const __m128 p1 =
_mm_set_ps(-0.189207f, 0.504551f, 0.756827f, -0.216236f);
const __m128 p3 =
_mm_set_ps(-0.103943f, 0.233872f, 0.935489f, -0.155915f);
__m128 work = previousSamples; __m128 work = previousSamples;
__m128 peak = previousSamples; __m128 peak = previousSamples;

View File

@ -54,35 +54,24 @@ std::string Utils::Platform::GetLocalAddress()
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.") || if (address.startsWith("192.168.1.") ||
address.startsWith( address.startsWith("192.168.0.")) { // Prefer common consumer router network prefixes
"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( preferredAddresses.push_back(std::make_pair(address,
address, 255)); // Ignore virtualbox default
255)); // Ignore virtualbox default
else else
preferredAddresses.push_back( preferredAddresses.push_back(std::make_pair(address, 0));
std::make_pair(address, 0)); } else if (address.startsWith("172.16.")) { // Slightly less common consumer router network prefixes
} else if (address.startsWith( preferredAddresses.push_back(std::make_pair(address, 1));
"172.16.")) { // Slightly less common consumer router network prefixes } else if (address.startsWith("10.")) { // Even less common consumer router network prefixes
preferredAddresses.push_back( preferredAddresses.push_back(std::make_pair(address, 2));
std::make_pair(address, 1));
} else if (address.startsWith(
"10.")) { // Even less common consumer router network prefixes
preferredAddresses.push_back(
std::make_pair(address, 2));
} else { // Set all other addresses to equal priority } else { // Set all other addresses to equal priority
preferredAddresses.push_back( preferredAddresses.push_back(std::make_pair(address, 255));
std::make_pair(address, 255));
} }
} }
// Sort by priority // Sort by priority
std::sort(preferredAddresses.begin(), preferredAddresses.end(), std::sort(preferredAddresses.begin(), preferredAddresses.end(),
[=](std::pair<QString, uint8_t> a, [=](std::pair<QString, uint8_t> a, std::pair<QString, uint8_t> b) { return a.second < b.second; });
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();
@ -132,35 +121,27 @@ struct SystemTrayNotification {
QString body; QString body;
}; };
void Utils::Platform::SendTrayNotification(QSystemTrayIcon::MessageIcon icon, void Utils::Platform::SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QString title, QString body)
QString title, QString body)
{ {
if (!QSystemTrayIcon::isSystemTrayAvailable() || if (!QSystemTrayIcon::isSystemTrayAvailable() || !QSystemTrayIcon::supportsMessages())
!QSystemTrayIcon::supportsMessages())
return; return;
SystemTrayNotification *notification = SystemTrayNotification *notification = new SystemTrayNotification{icon, title, body};
new SystemTrayNotification{icon, title, body};
obs_queue_task( obs_queue_task(
OBS_TASK_UI, OBS_TASK_UI,
[](void *param) { [](void *param) {
void *systemTrayPtr = obs_frontend_get_system_tray(); void *systemTrayPtr = obs_frontend_get_system_tray();
auto systemTray = auto systemTray = static_cast<QSystemTrayIcon *>(systemTrayPtr);
static_cast<QSystemTrayIcon *>(systemTrayPtr);
auto notification = auto notification = static_cast<SystemTrayNotification *>(param);
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, bool Utils::Platform::GetTextFileContent(std::string fileName, std::string &content)
std::string &content)
{ {
QFile f(QString::fromStdString(fileName)); QFile f(QString::fromStdString(fileName));
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
@ -170,8 +151,7 @@ bool Utils::Platform::GetTextFileContent(std::string fileName,
return true; return true;
} }
bool Utils::Platform::SetTextFileContent(std::string fileName, bool Utils::Platform::SetTextFileContent(std::string fileName, std::string content, bool createNew)
std::string content, bool createNew)
{ {
if (!createNew && !QFile::exists(QString::fromStdString(fileName))) if (!createNew && !QFile::exists(QString::fromStdString(fileName)))
return false; return false;

View File

@ -29,10 +29,8 @@ std::string GetLocalAddress();
std::string GetLoopbackAddress(bool allowIpv6 = true); 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, void SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QString title, QString body);
QString body);
bool GetTextFileContent(std::string fileName, std::string &content); bool GetTextFileContent(std::string fileName, std::string &content);
bool SetTextFileContent(std::string filePath, std::string content, bool SetTextFileContent(std::string filePath, std::string content, bool createNew = true);
bool createNew = true);
} }
} }

View File

@ -42,27 +42,17 @@ WebSocketServer::WebSocketServer() : QObject(nullptr), _sessions()
#endif #endif
_server.set_validate_handler( _server.set_validate_handler(
websocketpp::lib::bind(&WebSocketServer::onValidate, this, websocketpp::lib::bind(&WebSocketServer::onValidate, this, websocketpp::lib::placeholders::_1));
websocketpp::lib::placeholders::_1)); _server.set_open_handler(websocketpp::lib::bind(&WebSocketServer::onOpen, this, websocketpp::lib::placeholders::_1));
_server.set_open_handler( _server.set_close_handler(websocketpp::lib::bind(&WebSocketServer::onClose, this, websocketpp::lib::placeholders::_1));
websocketpp::lib::bind(&WebSocketServer::onOpen, this, _server.set_message_handler(websocketpp::lib::bind(&WebSocketServer::onMessage, this, websocketpp::lib::placeholders::_1,
websocketpp::lib::placeholders::_1)); websocketpp::lib::placeholders::_2));
_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(&WebSocketServer::BroadcastEvent, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
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()
@ -77,16 +67,11 @@ void WebSocketServer::ServerRunner()
try { try {
_server.run(); _server.run();
} catch (websocketpp::exception const &e) { } catch (websocketpp::exception const &e) {
blog(LOG_ERROR, blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s", e.what());
"[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s",
e.what());
} catch (const std::exception &e) { } catch (const std::exception &e) {
blog(LOG_ERROR, blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s", e.what());
"[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s",
e.what());
} catch (...) { } catch (...) {
blog(LOG_ERROR, blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error");
"[WebSocketServer::ServerRunner] websocketpp instance returned an error");
} }
blog(LOG_INFO, "[WebSocketServer::ServerRunner] IO thread exited."); blog(LOG_INFO, "[WebSocketServer::ServerRunner] IO thread exited.");
} }
@ -94,70 +79,53 @@ void WebSocketServer::ServerRunner()
void WebSocketServer::Start() void WebSocketServer::Start()
{ {
if (_server.is_listening()) { if (_server.is_listening()) {
blog(LOG_WARNING, blog(LOG_WARNING, "[WebSocketServer::Start] Call to Start() but the server is already listening.");
"[WebSocketServer::Start] Call to Start() but the server is already listening.");
return; return;
} }
auto conf = GetConfig(); auto conf = GetConfig();
if (!conf) { if (!conf) {
blog(LOG_ERROR, blog(LOG_ERROR, "[WebSocketServer::Start] Unable to retreive config!");
"[WebSocketServer::Start] Unable to retreive config!");
return; return;
} }
_authenticationSalt = Utils::Crypto::GenerateSalt(); _authenticationSalt = Utils::Crypto::GenerateSalt();
_authenticationSecret = Utils::Crypto::GenerateSecret( _authenticationSecret = Utils::Crypto::GenerateSecret(conf->ServerPassword.toStdString(), _authenticationSalt);
conf->ServerPassword.toStdString(), _authenticationSalt);
// 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( _server.get_alog().clear_channels(websocketpp::log::alevel::frame_header | websocketpp::log::alevel::frame_payload |
websocketpp::log::alevel::frame_header | websocketpp::log::alevel::control);
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( _server.get_alog().clear_channels(websocketpp::log::elevel::devel | websocketpp::log::elevel::library);
websocketpp::log::elevel::devel |
websocketpp::log::elevel::library);
} else { } else {
_server.get_alog().clear_channels( _server.get_alog().clear_channels(websocketpp::log::alevel::all);
websocketpp::log::alevel::all); _server.get_elog().clear_channels(websocketpp::log::elevel::all);
_server.get_elog().clear_channels(
websocketpp::log::elevel::all);
} }
_server.reset(); _server.reset();
websocketpp::lib::error_code errorCode; websocketpp::lib::error_code errorCode;
if (conf->BindLoopback) { if (conf->BindLoopback) {
std::string addr = std::string addr = Utils::Platform::GetLoopbackAddress(!conf->Ipv4Only);
Utils::Platform::GetLoopbackAddress(!conf->Ipv4Only);
if (addr.empty()) { if (addr.empty()) {
blog(LOG_ERROR, blog(LOG_ERROR, "[WebSocketServer::Start] Failed to find loopback interface. Server not started.");
"[WebSocketServer::Start] Failed to find loopback interface. Server not started.");
return; return;
} }
_server.listen(addr, std::to_string(conf->ServerPort), _server.listen(addr, std::to_string(conf->ServerPort), errorCode);
errorCode); blog(LOG_INFO, "[WebSocketServer::Start] Locked to loopback interface.");
blog(LOG_INFO,
"[WebSocketServer::Start] Locked to loopback interface.");
} else if (conf->Ipv4Only) { } else if (conf->Ipv4Only) {
_server.listen(websocketpp::lib::asio::ip::tcp::v4(), _server.listen(websocketpp::lib::asio::ip::tcp::v4(), conf->ServerPort, errorCode);
conf->ServerPort, errorCode); blog(LOG_INFO, "[WebSocketServer::Start] Locked to IPv4 bindings.");
blog(LOG_INFO,
"[WebSocketServer::Start] Locked to IPv4 bindings.");
} else { } else {
_server.listen(conf->ServerPort, errorCode); _server.listen(conf->ServerPort, errorCode);
blog(LOG_INFO, blog(LOG_INFO, "[WebSocketServer::Start] Bound to all interfaces.");
"[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", blog(LOG_ERROR, "[WebSocketServer::Start] Listen failed: %s", errorCodeMessage.c_str());
errorCodeMessage.c_str());
return; return;
} }
@ -165,17 +133,14 @@ void WebSocketServer::Start()
_serverThread = std::thread(&WebSocketServer::ServerRunner, this); _serverThread = std::thread(&WebSocketServer::ServerRunner, this);
blog(LOG_INFO, blog(LOG_INFO, "[WebSocketServer::Start] Server started successfully on port %d. Possible connect address: %s",
"[WebSocketServer::Start] Server started successfully on port %d. Possible connect address: %s", conf->ServerPort.load(), Utils::Platform::GetLocalAddress().c_str());
conf->ServerPort.load(),
Utils::Platform::GetLocalAddress().c_str());
} }
void WebSocketServer::Stop() void WebSocketServer::Stop()
{ {
if (!_server.is_listening()) { if (!_server.is_listening()) {
blog(LOG_WARNING, blog(LOG_WARNING, "[WebSocketServer::Stop] Call to Stop() but the server is not listening.");
"[WebSocketServer::Stop] Call to Stop() but the server is not listening.");
return; return;
} }
@ -186,16 +151,13 @@ void WebSocketServer::Stop()
websocketpp::lib::error_code errorCode; websocketpp::lib::error_code errorCode;
_server.pause_reading(hdl, errorCode); _server.pause_reading(hdl, errorCode);
if (errorCode) { if (errorCode) {
blog(LOG_INFO, "[WebSocketServer::Stop] Error: %s", blog(LOG_INFO, "[WebSocketServer::Stop] Error: %s", errorCode.message().c_str());
errorCode.message().c_str());
continue; continue;
} }
_server.close(hdl, websocketpp::close::status::going_away, _server.close(hdl, websocketpp::close::status::going_away, "Server stopping.", errorCode);
"Server stopping.", errorCode);
if (errorCode) { if (errorCode) {
blog(LOG_INFO, "[WebSocketServer::Stop] Error: %s", blog(LOG_INFO, "[WebSocketServer::Stop] Error: %s", errorCode.message().c_str());
errorCode.message().c_str());
continue; continue;
} }
} }
@ -215,28 +177,23 @@ void WebSocketServer::Stop()
void WebSocketServer::InvalidateSession(websocketpp::connection_hdl hdl) void WebSocketServer::InvalidateSession(websocketpp::connection_hdl hdl)
{ {
blog(LOG_INFO, blog(LOG_INFO, "[WebSocketServer::InvalidateSession] Invalidating a session.");
"[WebSocketServer::InvalidateSession] Invalidating a session.");
websocketpp::lib::error_code errorCode; websocketpp::lib::error_code errorCode;
_server.pause_reading(hdl, errorCode); _server.pause_reading(hdl, errorCode);
if (errorCode) { if (errorCode) {
blog(LOG_INFO, "[WebSocketServer::InvalidateSession] Error: %s", blog(LOG_INFO, "[WebSocketServer::InvalidateSession] Error: %s", errorCode.message().c_str());
errorCode.message().c_str());
return; return;
} }
_server.close(hdl, WebSocketCloseCode::SessionInvalidated, _server.close(hdl, WebSocketCloseCode::SessionInvalidated, "Your session has been invalidated.", errorCode);
"Your session has been invalidated.", errorCode);
if (errorCode) { if (errorCode) {
blog(LOG_INFO, "[WebSocketServer::InvalidateSession] Error: %s", blog(LOG_INFO, "[WebSocketServer::InvalidateSession] Error: %s", errorCode.message().c_str());
errorCode.message().c_str());
return; return;
} }
} }
std::vector<WebSocketServer::WebSocketSessionState> std::vector<WebSocketServer::WebSocketSessionState> WebSocketServer::GetWebSocketSessions()
WebSocketServer::GetWebSocketSessions()
{ {
std::vector<WebSocketServer::WebSocketSessionState> webSocketSessions; std::vector<WebSocketServer::WebSocketSessionState> webSocketSessions;
@ -248,9 +205,8 @@ WebSocketServer::GetWebSocketSessions()
std::string remoteAddress = session->RemoteAddress(); std::string remoteAddress = session->RemoteAddress();
bool isIdentified = session->IsIdentified(); bool isIdentified = session->IsIdentified();
webSocketSessions.emplace_back(WebSocketSessionState{ webSocketSessions.emplace_back(
hdl, remoteAddress, connectedAt, incomingMessages, WebSocketSessionState{hdl, remoteAddress, connectedAt, incomingMessages, outgoingMessages, isIdentified});
outgoingMessages, isIdentified});
} }
lock.unlock(); lock.unlock();
@ -261,14 +217,12 @@ void WebSocketServer::onObsLoaded()
{ {
auto conf = GetConfig(); auto conf = GetConfig();
if (!conf) { if (!conf) {
blog(LOG_ERROR, blog(LOG_ERROR, "[WebSocketServer::onObsLoaded] Unable to retreive config!");
"[WebSocketServer::onObsLoaded] Unable to retreive config!");
return; return;
} }
if (conf->ServerEnabled) { if (conf->ServerEnabled) {
blog(LOG_INFO, blog(LOG_INFO, "[WebSocketServer::onObsLoaded] WebSocket server is enabled, starting...");
"[WebSocketServer::onObsLoaded] WebSocket server is enabled, starting...");
Start(); Start();
} }
} }
@ -277,11 +231,9 @@ bool WebSocketServer::onValidate(websocketpp::connection_hdl hdl)
{ {
auto conn = _server.get_con_from_hdl(hdl); auto conn = _server.get_con_from_hdl(hdl);
std::vector<std::string> requestedSubprotocols = std::vector<std::string> requestedSubprotocols = conn->get_requested_subprotocols();
conn->get_requested_subprotocols();
for (auto subprotocol : requestedSubprotocols) { for (auto subprotocol : requestedSubprotocols) {
if (subprotocol == "obswebsocket.json" || if (subprotocol == "obswebsocket.json" || subprotocol == "obswebsocket.msgpack") {
subprotocol == "obswebsocket.msgpack") {
conn->select_subprotocol(subprotocol); conn->select_subprotocol(subprotocol);
break; break;
} }
@ -296,15 +248,13 @@ void WebSocketServer::onOpen(websocketpp::connection_hdl hdl)
auto conf = GetConfig(); auto conf = GetConfig();
if (!conf) { if (!conf) {
blog(LOG_ERROR, blog(LOG_ERROR, "[WebSocketServer::onOpen] Unable to retreive config!");
"[WebSocketServer::onOpen] Unable to retreive config!");
return; return;
} }
// Build new session // Build new session
std::unique_lock<std::mutex> lock(_sessionMutex); std::unique_lock<std::mutex> lock(_sessionMutex);
SessionPtr session = _sessions[hdl] = SessionPtr session = _sessions[hdl] = std::make_shared<WebSocketSession>();
std::make_shared<WebSocketSession>();
std::unique_lock<std::mutex> sessionLock(session->OperationMutex); std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
lock.unlock(); lock.unlock();
@ -329,10 +279,8 @@ void WebSocketServer::onOpen(websocketpp::connection_hdl hdl)
std::string sessionChallenge = Utils::Crypto::GenerateSalt(); std::string sessionChallenge = Utils::Crypto::GenerateSalt();
session->SetChallenge(sessionChallenge); session->SetChallenge(sessionChallenge);
helloMessageData["authentication"] = json::object(); helloMessageData["authentication"] = json::object();
helloMessageData["authentication"]["challenge"] = helloMessageData["authentication"]["challenge"] = sessionChallenge;
sessionChallenge; helloMessageData["authentication"]["salt"] = _authenticationSalt;
helloMessageData["authentication"]["salt"] =
_authenticationSalt;
} }
json helloMessage; json helloMessage;
helloMessage["op"] = 0; helloMessage["op"] = 0;
@ -352,27 +300,20 @@ void WebSocketServer::onOpen(websocketpp::connection_hdl hdl)
emit ClientConnected(state); emit ClientConnected(state);
// Log connection // Log connection
blog(LOG_INFO, blog(LOG_INFO, "[WebSocketServer::onOpen] New WebSocket client has connected from %s", session->RemoteAddress().c_str());
"[WebSocketServer::onOpen] New WebSocket client has connected from %s",
session->RemoteAddress().c_str());
blog_debug( blog_debug("[WebSocketServer::onOpen] Sending Op 0 (Hello) message:\n%s", helloMessage.dump(2).c_str());
"[WebSocketServer::onOpen] Sending Op 0 (Hello) message:\n%s",
helloMessage.dump(2).c_str());
// Send object to client // Send object to client
websocketpp::lib::error_code errorCode; websocketpp::lib::error_code errorCode;
auto sessionEncoding = session->Encoding(); auto sessionEncoding = session->Encoding();
if (sessionEncoding == WebSocketEncoding::Json) { if (sessionEncoding == WebSocketEncoding::Json) {
std::string helloMessageJson = helloMessage.dump(); std::string helloMessageJson = helloMessage.dump();
_server.send(hdl, helloMessageJson, _server.send(hdl, helloMessageJson, websocketpp::frame::opcode::text, errorCode);
websocketpp::frame::opcode::text, errorCode);
} else if (sessionEncoding == WebSocketEncoding::MsgPack) { } else if (sessionEncoding == WebSocketEncoding::MsgPack) {
auto msgPackData = json::to_msgpack(helloMessage); auto msgPackData = json::to_msgpack(helloMessage);
std::string messageMsgPack(msgPackData.begin(), std::string messageMsgPack(msgPackData.begin(), msgPackData.end());
msgPackData.end()); _server.send(hdl, messageMsgPack, websocketpp::frame::opcode::binary, errorCode);
_server.send(hdl, messageMsgPack,
websocketpp::frame::opcode::binary, errorCode);
} }
session->IncrementOutgoingMessages(); session->IncrementOutgoingMessages();
} }
@ -411,37 +352,26 @@ 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, blog(LOG_INFO, "[WebSocketServer::onClose] WebSocket client %s has disconnected", remoteAddress.c_str());
"[WebSocketServer::onClose] WebSocket client %s has disconnected",
remoteAddress.c_str());
// Get config for tray notification // Get config for tray notification
auto conf = GetConfig(); auto conf = GetConfig();
if (!conf) { if (!conf) {
blog(LOG_ERROR, blog(LOG_ERROR, "[WebSocketServer::onClose] Unable to retreive config!");
"[WebSocketServer::onClose] Unable to retreive config!");
return; return;
} }
// 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 && if (isIdentified && (conn->get_local_close_code() != websocketpp::close::status::going_away) && conf->AlertsEnabled) {
(conn->get_local_close_code() != QString title = obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Title");
websocketpp::close::status::going_away) && QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Body"))
conf->AlertsEnabled) { .arg(QString::fromStdString(remoteAddress));
QString title = obs_module_text( Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
"OBSWebSocket.TrayNotification.Disconnected.Title");
QString body =
QString(obs_module_text(
"OBSWebSocket.TrayNotification.Disconnected.Body"))
.arg(QString::fromStdString(remoteAddress));
Utils::Platform::SendTrayNotification(
QSystemTrayIcon::Information, title, body);
} }
} }
void WebSocketServer::onMessage( void WebSocketServer::onMessage(websocketpp::connection_hdl hdl,
websocketpp::connection_hdl hdl, websocketpp::server<websocketpp::config::asio>::message_ptr message)
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();
@ -464,52 +394,37 @@ void WebSocketServer::onMessage(
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( _server.close(hdl, WebSocketCloseCode::MessageDecodeError,
hdl, "Your session encoding is set to Json, but a binary message was received.",
WebSocketCloseCode::MessageDecodeError, errorCode);
"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( _server.close(hdl, WebSocketCloseCode::MessageDecodeError,
hdl, std::string("Unable to decode Json: ") + e.what(), errorCode);
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( _server.close(hdl, WebSocketCloseCode::MessageDecodeError,
hdl, "Your session encoding is set to MsgPack, but a text message was received.",
WebSocketCloseCode::MessageDecodeError, errorCode);
"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( _server.close(hdl, WebSocketCloseCode::MessageDecodeError,
hdl, std::string("Unable to decode MsgPack: ") + e.what(), errorCode);
WebSocketCloseCode::MessageDecodeError,
std::string(
"Unable to decode MsgPack: ") +
e.what(),
errorCode);
return; return;
} }
} }
blog_debug( blog_debug("[WebSocketServer::onMessage] Incoming message (decoded):\n%s", incomingMessage.dump(2).c_str());
"[WebSocketServer::onMessage] Incoming message (decoded):\n%s",
incomingMessage.dump(2).c_str());
ProcessResult ret; ProcessResult ret;
@ -521,13 +436,10 @@ void WebSocketServer::onMessage(
} }
// Disconnect client if 4.x protocol is detected // Disconnect client if 4.x protocol is detected
if (!session->IsIdentified() && if (!session->IsIdentified() && incomingMessage.contains("request-type")) {
incomingMessage.contains("request-type")) { blog(LOG_WARNING, "[WebSocketServer::onMessage] Client %s appears to be running a pre-5.0.0 protocol.",
blog(LOG_WARNING,
"[WebSocketServer::onMessage] Client %s appears to be running a pre-5.0.0 protocol.",
session->RemoteAddress().c_str()); session->RemoteAddress().c_str());
ret.closeCode = ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion;
WebSocketCloseCode::UnsupportedRpcVersion;
ret.closeReason = 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."; "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;
@ -540,43 +452,31 @@ void WebSocketServer::onMessage(
goto skipProcessing; goto skipProcessing;
} }
ProcessMessage(session, ret, incomingMessage["op"], ProcessMessage(session, ret, incomingMessage["op"], incomingMessage["d"]);
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, _server.close(hdl, ret.closeCode, ret.closeReason, errorCode);
errorCode);
return; return;
} }
if (!ret.result.is_null()) { if (!ret.result.is_null()) {
websocketpp::lib::error_code errorCode; websocketpp::lib::error_code errorCode;
if (sessionEncoding == WebSocketEncoding::Json) { if (sessionEncoding == WebSocketEncoding::Json) {
std::string helloMessageJson = std::string helloMessageJson = ret.result.dump();
ret.result.dump(); _server.send(hdl, helloMessageJson, websocketpp::frame::opcode::text, errorCode);
_server.send(hdl, helloMessageJson, } else if (sessionEncoding == WebSocketEncoding::MsgPack) {
websocketpp::frame::opcode::text,
errorCode);
} else if (sessionEncoding ==
WebSocketEncoding::MsgPack) {
auto msgPackData = json::to_msgpack(ret.result); auto msgPackData = json::to_msgpack(ret.result);
std::string messageMsgPack(msgPackData.begin(), std::string messageMsgPack(msgPackData.begin(), msgPackData.end());
msgPackData.end()); _server.send(hdl, messageMsgPack, websocketpp::frame::opcode::binary, errorCode);
_server.send(hdl, messageMsgPack,
websocketpp::frame::opcode::binary,
errorCode);
} }
session->IncrementOutgoingMessages(); session->IncrementOutgoingMessages();
blog_debug( blog_debug("[WebSocketServer::onMessage] Outgoing message:\n%s", ret.result.dump(2).c_str());
"[WebSocketServer::onMessage] Outgoing message:\n%s",
ret.result.dump(2).c_str());
if (errorCode) if (errorCode)
blog(LOG_WARNING, blog(LOG_WARNING, "[WebSocketServer::onMessage] Sending message to client failed: %s",
"[WebSocketServer::onMessage] Sending message to client failed: %s",
errorCode.message().c_str()); errorCode.message().c_str());
} }
})); }));

View File

@ -55,9 +55,7 @@ public:
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, void BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData = nullptr,
const std::string &eventType,
const json &eventData = nullptr,
uint8_t rpcVersion = 0); uint8_t rpcVersion = 0);
bool IsListening() { return _server.is_listening(); } bool IsListening() { return _server.is_listening(); }
@ -68,13 +66,11 @@ public:
signals: signals:
void ClientConnected(WebSocketSessionState state); void ClientConnected(WebSocketSessionState state);
void ClientDisconnected(WebSocketSessionState state, void ClientDisconnected(WebSocketSessionState state, uint16_t closeCode);
uint16_t closeCode);
private: private:
struct ProcessResult { struct ProcessResult {
WebSocketCloseCode::WebSocketCloseCode closeCode = WebSocketCloseCode::WebSocketCloseCode closeCode = WebSocketCloseCode::DontClose;
WebSocketCloseCode::DontClose;
std::string closeReason; std::string closeReason;
json result; json result;
}; };
@ -85,17 +81,10 @@ private:
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 void onMessage(websocketpp::connection_hdl hdl, websocketpp::server<websocketpp::config::asio>::message_ptr message);
onMessage(websocketpp::connection_hdl hdl,
websocketpp::server<websocketpp::config::asio>::message_ptr
message);
static void SetSessionParameters(SessionPtr session, static void SetSessionParameters(SessionPtr session, WebSocketServer::ProcessResult &ret, const json &payloadData);
WebSocketServer::ProcessResult &ret, void ProcessMessage(SessionPtr session, ProcessResult &ret, WebSocketOpCode::WebSocketOpCode opCode, json &payloadData);
const json &payloadData);
void ProcessMessage(SessionPtr session, ProcessResult &ret,
WebSocketOpCode::WebSocketOpCode opCode,
json &payloadData);
QThreadPool _threadPool; QThreadPool _threadPool;
@ -106,7 +95,5 @@ private:
std::string _authenticationSalt; std::string _authenticationSalt;
std::mutex _sessionMutex; std::mutex _sessionMutex;
std::map<websocketpp::connection_hdl, SessionPtr, std::map<websocketpp::connection_hdl, SessionPtr, std::owner_less<websocketpp::connection_hdl>> _sessions;
std::owner_less<websocketpp::connection_hdl>>
_sessions;
}; };

View File

@ -35,20 +35,16 @@ static bool IsSupportedRpcVersion(uint8_t requestedVersion)
return (requestedVersion == 1); return (requestedVersion == 1);
} }
static json ConstructRequestResult(RequestResult requestResult, static json ConstructRequestResult(RequestResult requestResult, const json &requestJson)
const json &requestJson)
{ {
json ret; json ret;
ret["requestType"] = requestJson["requestType"]; ret["requestType"] = requestJson["requestType"];
if (requestJson.contains("requestId") && if (requestJson.contains("requestId") && !requestJson["requestId"].is_null())
!requestJson["requestId"].is_null())
ret["requestId"] = requestJson["requestId"]; ret["requestId"] = requestJson["requestId"];
ret["requestStatus"] = {{"result", requestResult.StatusCode == ret["requestStatus"] = {{"result", requestResult.StatusCode == RequestStatus::Success}, {"code", 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;
@ -59,37 +55,28 @@ static json ConstructRequestResult(RequestResult requestResult,
return ret; return ret;
} }
void WebSocketServer::SetSessionParameters(SessionPtr session, void WebSocketServer::SetSessionParameters(SessionPtr session, ProcessResult &ret, const json &payloadData)
ProcessResult &ret,
const json &payloadData)
{ {
if (payloadData.contains("eventSubscriptions")) { if (payloadData.contains("eventSubscriptions")) {
if (!payloadData["eventSubscriptions"].is_number_unsigned()) { if (!payloadData["eventSubscriptions"].is_number_unsigned()) {
ret.closeCode = ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
WebSocketCloseCode::InvalidDataFieldType; ret.closeReason = "Your `eventSubscriptions` is not an unsigned number.";
ret.closeReason =
"Your `eventSubscriptions` is not an unsigned number.";
return; return;
} }
session->SetEventSubscriptions( session->SetEventSubscriptions(payloadData["eventSubscriptions"]);
payloadData["eventSubscriptions"]);
} }
} }
void WebSocketServer::ProcessMessage(SessionPtr session, void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::ProcessResult &ret,
WebSocketServer::ProcessResult &ret, WebSocketOpCode::WebSocketOpCode opCode, json &payloadData)
WebSocketOpCode::WebSocketOpCode opCode,
json &payloadData)
{ {
if (!payloadData.is_object()) { if (!payloadData.is_object()) {
if (payloadData.is_null()) { if (payloadData.is_null()) {
ret.closeCode = WebSocketCloseCode::MissingDataField; ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = "Your payload is missing data (`d`)."; ret.closeReason = "Your payload is missing data (`d`).";
} else { } else {
ret.closeCode = ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
WebSocketCloseCode::InvalidDataFieldType; ret.closeReason = "Your payload's data (`d`) is not an object.";
ret.closeReason =
"Your payload's data (`d`) is not an object.";
} }
return; return;
} }
@ -97,48 +84,37 @@ void WebSocketServer::ProcessMessage(SessionPtr session,
// Only `Identify` is allowed when not identified // Only `Identify` is allowed when not identified
if (!session->IsIdentified() && opCode != 1) { if (!session->IsIdentified() && opCode != 1) {
ret.closeCode = WebSocketCloseCode::NotIdentified; ret.closeCode = WebSocketCloseCode::NotIdentified;
ret.closeReason = ret.closeReason = "You attempted to send a non-Identify message while not identified.";
"You attempted to send a non-Identify message while not identified.";
return; return;
} }
switch (opCode) { switch (opCode) {
case WebSocketOpCode::Identify: { // Identify case WebSocketOpCode::Identify: { // Identify
std::unique_lock<std::mutex> sessionLock( std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
session->OperationMutex);
if (session->IsIdentified()) { if (session->IsIdentified()) {
ret.closeCode = WebSocketCloseCode::AlreadyIdentified; ret.closeCode = WebSocketCloseCode::AlreadyIdentified;
ret.closeReason = ret.closeReason = "You are already Identified with the obs-websocket server.";
"You are already Identified with the obs-websocket server.";
return; return;
} }
if (session->AuthenticationRequired()) { if (session->AuthenticationRequired()) {
if (!payloadData.contains("authentication")) { if (!payloadData.contains("authentication")) {
ret.closeCode = ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
WebSocketCloseCode::AuthenticationFailed;
ret.closeReason = ret.closeReason =
"Your payload's data is missing an `authentication` string, however authentication is required."; "Your payload's data is missing an `authentication` string, however authentication is required.";
return; return;
} }
if (!Utils::Crypto::CheckAuthenticationString( if (!Utils::Crypto::CheckAuthenticationString(session->Secret(), session->Challenge(),
session->Secret(), session->Challenge(), payloadData["authentication"])) {
payloadData["authentication"])) {
auto conf = GetConfig(); auto conf = GetConfig();
if (conf && conf->AlertsEnabled) { if (conf && conf->AlertsEnabled) {
QString title = obs_module_text( QString title = obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Title");
"OBSWebSocket.TrayNotification.AuthenticationFailed.Title");
QString body = QString body =
QString(obs_module_text( QString(obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Body"))
"OBSWebSocket.TrayNotification.AuthenticationFailed.Body")) .arg(QString::fromStdString(session->RemoteAddress()));
.arg(QString::fromStdString( Utils::Platform::SendTrayNotification(QSystemTrayIcon::Warning, title, body);
session->RemoteAddress()));
Utils::Platform::SendTrayNotification(
QSystemTrayIcon::Warning, title,
body);
} }
ret.closeCode = ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
WebSocketCloseCode::AuthenticationFailed;
ret.closeReason = "Authentication failed."; ret.closeReason = "Authentication failed.";
return; return;
} }
@ -146,24 +122,19 @@ void WebSocketServer::ProcessMessage(SessionPtr session,
if (!payloadData.contains("rpcVersion")) { if (!payloadData.contains("rpcVersion")) {
ret.closeCode = WebSocketCloseCode::MissingDataField; ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = ret.closeReason = "Your payload's data is missing an `rpcVersion`.";
"Your payload's data is missing an `rpcVersion`.";
return; return;
} }
if (!payloadData["rpcVersion"].is_number_unsigned()) { if (!payloadData["rpcVersion"].is_number_unsigned()) {
ret.closeCode = ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
WebSocketCloseCode::InvalidDataFieldType; ret.closeReason = "Your `rpcVersion` is not an unsigned number.";
ret.closeReason =
"Your `rpcVersion` is not an unsigned number.";
} }
uint8_t requestedRpcVersion = payloadData["rpcVersion"]; uint8_t requestedRpcVersion = payloadData["rpcVersion"];
if (!IsSupportedRpcVersion(requestedRpcVersion)) { if (!IsSupportedRpcVersion(requestedRpcVersion)) {
ret.closeCode = ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion;
WebSocketCloseCode::UnsupportedRpcVersion; ret.closeReason = "Your requested RPC version is not supported by this server.";
ret.closeReason =
"Your requested RPC version is not supported by this server.";
return; return;
} }
session->SetRpcVersion(requestedRpcVersion); session->SetRpcVersion(requestedRpcVersion);
@ -175,8 +146,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session,
// Increment refs for event subscriptions // Increment refs for event subscriptions
auto eventHandler = GetEventHandler(); auto eventHandler = GetEventHandler();
eventHandler->ProcessSubscription( eventHandler->ProcessSubscription(session->EventSubscriptions());
session->EventSubscriptions());
// Mark session as identified // Mark session as identified
session->SetIsIdentified(true); session->SetIsIdentified(true);
@ -184,15 +154,10 @@ void WebSocketServer::ProcessMessage(SessionPtr session,
// Send desktop notification. TODO: Move to UI code // Send desktop notification. TODO: Move to UI code
auto conf = GetConfig(); auto conf = GetConfig();
if (conf && conf->AlertsEnabled) { if (conf && conf->AlertsEnabled) {
QString title = obs_module_text( QString title = obs_module_text("OBSWebSocket.TrayNotification.Identified.Title");
"OBSWebSocket.TrayNotification.Identified.Title"); QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Identified.Body"))
QString body = .arg(QString::fromStdString(session->RemoteAddress()));
QString(obs_module_text( Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
"OBSWebSocket.TrayNotification.Identified.Body"))
.arg(QString::fromStdString(
session->RemoteAddress()));
Utils::Platform::SendTrayNotification(
QSystemTrayIcon::Information, title, body);
} }
ret.result["op"] = WebSocketOpCode::Identified; ret.result["op"] = WebSocketOpCode::Identified;
@ -200,13 +165,11 @@ void WebSocketServer::ProcessMessage(SessionPtr session,
} }
return; return;
case WebSocketOpCode::Reidentify: { // Reidentify case WebSocketOpCode::Reidentify: { // Reidentify
std::unique_lock<std::mutex> sessionLock( std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
session->OperationMutex);
// Decrement refs for current subscriptions // Decrement refs for current subscriptions
auto eventHandler = GetEventHandler(); auto eventHandler = GetEventHandler();
eventHandler->ProcessUnsubscription( eventHandler->ProcessUnsubscription(session->EventSubscriptions());
session->EventSubscriptions());
SetSessionParameters(session, ret, payloadData); SetSessionParameters(session, ret, payloadData);
if (ret.closeCode != WebSocketCloseCode::DontClose) { if (ret.closeCode != WebSocketCloseCode::DontClose) {
@ -214,8 +177,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session,
} }
// Increment refs for new subscriptions // Increment refs for new subscriptions
eventHandler->ProcessSubscription( eventHandler->ProcessSubscription(session->EventSubscriptions());
session->EventSubscriptions());
ret.result["op"] = WebSocketOpCode::Identified; ret.result["op"] = WebSocketOpCode::Identified;
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion(); ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
@ -225,31 +187,24 @@ void WebSocketServer::ProcessMessage(SessionPtr session,
// RequestID checking has to be done here where we are able to close the connection. // RequestID checking has to be done here where we are able to close the connection.
if (!payloadData.contains("requestId")) { if (!payloadData.contains("requestId")) {
ret.closeCode = WebSocketCloseCode::MissingDataField; ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = ret.closeReason = "Your payload data is missing a `requestId`.";
"Your payload data is missing a `requestId`.";
return; return;
} }
RequestHandler requestHandler(session); RequestHandler requestHandler(session);
Request request(payloadData["requestType"], Request request(payloadData["requestType"], payloadData["requestData"]);
payloadData["requestData"]);
RequestResult requestResult = RequestResult requestResult = requestHandler.ProcessRequest(request);
requestHandler.ProcessRequest(request);
json resultPayloadData; json resultPayloadData;
resultPayloadData["requestType"] = payloadData["requestType"]; resultPayloadData["requestType"] = payloadData["requestType"];
resultPayloadData["requestId"] = payloadData["requestId"]; resultPayloadData["requestId"] = payloadData["requestId"];
resultPayloadData["requestStatus"] = { resultPayloadData["requestStatus"] = {{"result", requestResult.StatusCode == RequestStatus::Success},
{"result", {"code", requestResult.StatusCode}};
requestResult.StatusCode == RequestStatus::Success},
{"code", requestResult.StatusCode}};
if (!requestResult.Comment.empty()) if (!requestResult.Comment.empty())
resultPayloadData["requestStatus"]["comment"] = resultPayloadData["requestStatus"]["comment"] = requestResult.Comment;
requestResult.Comment;
if (requestResult.ResponseData.is_object()) if (requestResult.ResponseData.is_object())
resultPayloadData["responseData"] = resultPayloadData["responseData"] = requestResult.ResponseData;
requestResult.ResponseData;
ret.result["op"] = WebSocketOpCode::RequestResponse; ret.result["op"] = WebSocketOpCode::RequestResponse;
ret.result["d"] = resultPayloadData; ret.result["d"] = resultPayloadData;
} }
@ -258,95 +213,68 @@ void WebSocketServer::ProcessMessage(SessionPtr session,
// RequestID checking has to be done here where we are able to close the connection. // RequestID checking has to be done here where we are able to close the connection.
if (!payloadData.contains("requestId")) { if (!payloadData.contains("requestId")) {
ret.closeCode = WebSocketCloseCode::MissingDataField; ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = ret.closeReason = "Your payload data is missing a `requestId`.";
"Your payload data is missing a `requestId`.";
return; return;
} }
if (!payloadData.contains("requests")) { if (!payloadData.contains("requests")) {
ret.closeCode = WebSocketCloseCode::MissingDataField; ret.closeCode = WebSocketCloseCode::MissingDataField;
ret.closeReason = ret.closeReason = "Your payload data is missing a `requests`.";
"Your payload data is missing a `requests`.";
return; return;
} }
if (!payloadData["requests"].is_array()) { if (!payloadData["requests"].is_array()) {
ret.closeCode = ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
WebSocketCloseCode::InvalidDataFieldType;
ret.closeReason = "Your `requests` is not an array."; ret.closeReason = "Your `requests` is not an array.";
return; return;
} }
RequestBatchExecutionType::RequestBatchExecutionType RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::SerialRealtime;
executionType = if (payloadData.contains("executionType") && !payloadData["executionType"].is_null()) {
RequestBatchExecutionType::SerialRealtime;
if (payloadData.contains("executionType") &&
!payloadData["executionType"].is_null()) {
if (!payloadData["executionType"].is_number_unsigned()) { if (!payloadData["executionType"].is_number_unsigned()) {
ret.closeCode = ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
WebSocketCloseCode::InvalidDataFieldType; ret.closeReason = "Your `executionType` is not a number.";
ret.closeReason =
"Your `executionType` is not a number.";
return; return;
} }
int8_t requestedExecutionType = int8_t requestedExecutionType = payloadData["executionType"];
payloadData["executionType"]; if (!RequestBatchExecutionType::IsValid(requestedExecutionType) ||
if (!RequestBatchExecutionType::IsValid( requestedExecutionType == RequestBatchExecutionType::None) {
requestedExecutionType) || ret.closeCode = WebSocketCloseCode::InvalidDataFieldValue;
requestedExecutionType == ret.closeReason = "Your `executionType` has an invalid value.";
RequestBatchExecutionType::None) {
ret.closeCode =
WebSocketCloseCode::InvalidDataFieldValue;
ret.closeReason =
"Your `executionType` has an invalid value.";
return; return;
} }
// The thread pool must support 2 or more threads else parallel requests will deadlock. // The thread pool must support 2 or more threads else parallel requests will deadlock.
if (requestedExecutionType == if (requestedExecutionType == RequestBatchExecutionType::Parallel && _threadPool.maxThreadCount() < 2) {
RequestBatchExecutionType::Parallel && ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
_threadPool.maxThreadCount() < 2) {
ret.closeCode =
WebSocketCloseCode::UnsupportedFeature;
ret.closeReason = ret.closeReason =
"Parallel request batch processing is not available on this system due to limited core count."; "Parallel request batch processing is not available on this system due to limited core count.";
return; return;
} }
executionType = (RequestBatchExecutionType:: executionType = (RequestBatchExecutionType::RequestBatchExecutionType)requestedExecutionType;
RequestBatchExecutionType)
requestedExecutionType;
} }
if (payloadData.contains("variables") && if (payloadData.contains("variables") && !payloadData["variables"].is_null()) {
!payloadData["variables"].is_null()) {
if (!payloadData.is_object()) { if (!payloadData.is_object()) {
ret.closeCode = ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
WebSocketCloseCode::InvalidDataFieldType; ret.closeReason = "Your `variables` is not an object.";
ret.closeReason =
"Your `variables` is not an object.";
return; return;
} }
if (executionType == if (executionType == RequestBatchExecutionType::Parallel) {
RequestBatchExecutionType::Parallel) { ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
ret.closeCode = ret.closeReason = "Variables are not supported in Parallel mode.";
WebSocketCloseCode::UnsupportedFeature;
ret.closeReason =
"Variables are not supported in Parallel mode.";
return; return;
} }
} }
bool haltOnFailure = false; bool haltOnFailure = false;
if (payloadData.contains("haltOnFailure") && if (payloadData.contains("haltOnFailure") && !payloadData["haltOnFailure"].is_null()) {
!payloadData["haltOnFailure"].is_null()) {
if (!payloadData["haltOnFailure"].is_boolean()) { if (!payloadData["haltOnFailure"].is_boolean()) {
ret.closeCode = ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
WebSocketCloseCode::InvalidDataFieldType; ret.closeReason = "Your `haltOnFailure` is not a boolean.";
ret.closeReason =
"Your `haltOnFailure` is not a boolean.";
return; return;
} }
@ -357,21 +285,16 @@ void WebSocketServer::ProcessMessage(SessionPtr session,
std::vector<RequestBatchRequest> requestsVector; std::vector<RequestBatchRequest> requestsVector;
for (auto &requestJson : requests) for (auto &requestJson : requests)
requestsVector.emplace_back( requestsVector.emplace_back(requestJson["requestType"], requestJson["requestData"], executionType,
requestJson["requestType"], requestJson["inputVariables"], requestJson["outputVariables"]);
requestJson["requestData"], executionType,
requestJson["inputVariables"],
requestJson["outputVariables"]);
auto resultsVector = RequestBatchHandler::ProcessRequestBatch( auto resultsVector = RequestBatchHandler::ProcessRequestBatch(_threadPool, session, executionType, requestsVector,
_threadPool, session, executionType, requestsVector, payloadData["variables"], haltOnFailure);
payloadData["variables"], haltOnFailure);
size_t i = 0; size_t i = 0;
std::vector<json> results; std::vector<json> results;
for (auto &requestResult : resultsVector) { for (auto &requestResult : resultsVector) {
results.push_back(ConstructRequestResult(requestResult, results.push_back(ConstructRequestResult(requestResult, requests[i]));
requests[i]));
i++; i++;
} }
@ -382,16 +305,14 @@ void WebSocketServer::ProcessMessage(SessionPtr session,
return; return;
default: default:
ret.closeCode = WebSocketCloseCode::UnknownOpCode; ret.closeCode = WebSocketCloseCode::UnknownOpCode;
ret.closeReason = std::string("Unknown OpCode: ") + ret.closeReason = std::string("Unknown OpCode: ") + std::to_string(opCode);
std::to_string(opCode);
return; 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, void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData,
const std::string &eventType, uint8_t rpcVersion)
const json &eventData, uint8_t rpcVersion)
{ {
if (!_server.is_listening()) if (!_server.is_listening())
return; return;
@ -415,57 +336,37 @@ void WebSocketServer::BroadcastEvent(uint64_t requiredIntent,
if (!it.second->IsIdentified()) { if (!it.second->IsIdentified()) {
continue; continue;
} }
if (rpcVersion && if (rpcVersion && it.second->RpcVersion() != rpcVersion) {
it.second->RpcVersion() != rpcVersion) {
continue; continue;
} }
if ((it.second->EventSubscriptions() & if ((it.second->EventSubscriptions() & requiredIntent) != 0) {
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 = messageJson = eventMessage.dump();
eventMessage.dump();
} }
_server.send( _server.send((websocketpp::connection_hdl)it.first, messageJson,
(websocketpp::connection_hdl) websocketpp::frame::opcode::text, errorCode);
it.first,
messageJson,
websocketpp::frame::opcode::text,
errorCode);
it.second->IncrementOutgoingMessages(); it.second->IncrementOutgoingMessages();
break; break;
case WebSocketEncoding::MsgPack: case WebSocketEncoding::MsgPack:
if (messageMsgPack.empty()) { if (messageMsgPack.empty()) {
auto msgPackData = auto msgPackData = json::to_msgpack(eventMessage);
json::to_msgpack( messageMsgPack = std::string(msgPackData.begin(), msgPackData.end());
eventMessage);
messageMsgPack = std::string(
msgPackData.begin(),
msgPackData.end());
} }
_server.send( _server.send((websocketpp::connection_hdl)it.first, messageMsgPack,
(websocketpp::connection_hdl) websocketpp::frame::opcode::binary, errorCode);
it.first,
messageMsgPack,
websocketpp::frame::opcode::binary,
errorCode);
it.second->IncrementOutgoingMessages(); it.second->IncrementOutgoingMessages();
break; break;
} }
if (errorCode) if (errorCode)
blog(LOG_ERROR, blog(LOG_ERROR, "[WebSocketServer::BroadcastEvent] Error sending event message: %s",
"[WebSocketServer::BroadcastEvent] Error sending event message: %s",
errorCode.message().c_str()); errorCode.message().c_str());
} }
} }
lock.unlock(); lock.unlock();
if (IsDebugEnabled() && if (IsDebugEnabled() && (EventSubscription::All & requiredIntent) != 0) // Don't log high volume events
(EventSubscription::All & requiredIntent) != blog(LOG_INFO, "[WebSocketServer::BroadcastEvent] Outgoing event:\n%s", eventMessage.dump(2).c_str());
0) // Don't log high volume events
blog(LOG_INFO,
"[WebSocketServer::BroadcastEvent] Outgoing event:\n%s",
eventMessage.dump(2).c_str());
})); }));
} }