mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
base: Format code
This commit is contained in:
parent
7b238793d0
commit
f73e78582b
@ -1,78 +1,89 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
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/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Similar example code can be found in ../../src/obs-websocket.cpp
|
// Similar example code can be found in ../../src/obs-websocket.cpp
|
||||||
// You can test that sample code by specifying -DPLUGIN_TESTS=TRUE
|
// You can test that sample code by specifying -DPLUGIN_TESTS=TRUE
|
||||||
|
|
||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
|
|
||||||
#include "../obs-websocket-api.h"
|
#include "../obs-websocket-api.h"
|
||||||
|
|
||||||
OBS_DECLARE_MODULE()
|
OBS_DECLARE_MODULE()
|
||||||
OBS_MODULE_USE_DEFAULT_LOCALE(PLUGIN_NAME, "en-US")
|
OBS_MODULE_USE_DEFAULT_LOCALE(PLUGIN_NAME, "en-US")
|
||||||
|
|
||||||
obs_websocket_vendor vendor;
|
obs_websocket_vendor vendor;
|
||||||
|
|
||||||
bool obs_module_load(void)
|
bool obs_module_load(void)
|
||||||
{
|
{
|
||||||
blog(LOG_INFO, "Example obs-websocket-api plugin loaded!");
|
blog(LOG_INFO, "Example obs-websocket-api plugin loaded!");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void example_request_cb(obs_data_t *request_data, obs_data_t *response_data, void *priv_data);
|
void example_request_cb(obs_data_t *request_data, obs_data_t *response_data,
|
||||||
void obs_module_post_load(void)
|
void *priv_data);
|
||||||
{
|
void obs_module_post_load(void)
|
||||||
vendor = obs_websocket_register_vendor("api_example_plugin");
|
{
|
||||||
if (!vendor) {
|
vendor = obs_websocket_register_vendor("api_example_plugin");
|
||||||
blog(LOG_ERROR, "Vendor registration failed! (obs-websocket should have logged something if installed properly.)");
|
if (!vendor) {
|
||||||
return;
|
blog(LOG_ERROR,
|
||||||
}
|
"Vendor registration failed! (obs-websocket should have logged something if installed properly.)");
|
||||||
|
return;
|
||||||
if (!obs_websocket_vendor_register_request(vendor, "example_request", example_request_cb, NULL))
|
}
|
||||||
blog(LOG_ERROR, "Failed to register `example_request` request with obs-websocket.");
|
|
||||||
|
if (!obs_websocket_vendor_register_request(vendor, "example_request",
|
||||||
uint api_version = obs_websocket_get_api_version();
|
example_request_cb, NULL))
|
||||||
if (api_version == 0) {
|
blog(LOG_ERROR,
|
||||||
blog(LOG_ERROR, "Unable to fetch obs-websocket plugin API version.");
|
"Failed to register `example_request` request with obs-websocket.");
|
||||||
return;
|
|
||||||
} else if (api_version == 1) {
|
uint api_version = obs_websocket_get_api_version();
|
||||||
blog(LOG_WARNING, "Unsupported obs-websocket plugin API version for calling requests.");
|
if (api_version == 0) {
|
||||||
return;
|
blog(LOG_ERROR,
|
||||||
}
|
"Unable to fetch obs-websocket plugin API version.");
|
||||||
|
return;
|
||||||
struct obs_websocket_request_response *response = obs_websocket_call_request("GetVersion");
|
} else if (api_version == 1) {
|
||||||
if (!response) {
|
blog(LOG_WARNING,
|
||||||
blog(LOG_ERROR, "Failed to call GetVersion due to obs-websocket not being installed.");
|
"Unsupported obs-websocket plugin API version for calling requests.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s", response->status_code, response->comment, response->response_data);
|
|
||||||
obs_websocket_request_response_free(response);
|
struct obs_websocket_request_response *response =
|
||||||
}
|
obs_websocket_call_request("GetVersion");
|
||||||
|
if (!response) {
|
||||||
void obs_module_unload(void)
|
blog(LOG_ERROR,
|
||||||
{
|
"Failed to call GetVersion due to obs-websocket not being installed.");
|
||||||
blog(LOG_INFO, "Example obs-websocket-api plugin unloaded!");
|
return;
|
||||||
}
|
}
|
||||||
|
blog(LOG_INFO,
|
||||||
void example_request_cb(obs_data_t *request_data, obs_data_t *response_data, void *priv_data)
|
"[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
|
||||||
{
|
response->status_code, response->comment, response->response_data);
|
||||||
if (obs_data_has_user_value(request_data, "ping"))
|
obs_websocket_request_response_free(response);
|
||||||
obs_data_set_bool(response_data, "pong", true);
|
}
|
||||||
|
|
||||||
UNUSED_PARAMETER(priv_data);
|
void obs_module_unload(void)
|
||||||
}
|
{
|
||||||
|
blog(LOG_INFO, "Example obs-websocket-api plugin unloaded!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void example_request_cb(obs_data_t *request_data, obs_data_t *response_data,
|
||||||
|
void *priv_data)
|
||||||
|
{
|
||||||
|
if (obs_data_has_user_value(request_data, "ping"))
|
||||||
|
obs_data_set_bool(response_data, "pong", true);
|
||||||
|
|
||||||
|
UNUSED_PARAMETER(priv_data);
|
||||||
|
}
|
||||||
|
@ -28,8 +28,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef void* obs_websocket_vendor;
|
typedef void *obs_websocket_vendor;
|
||||||
typedef void (*obs_websocket_request_callback_function)(obs_data_t*, obs_data_t*, void*);
|
typedef void (*obs_websocket_request_callback_function)(obs_data_t *,
|
||||||
|
obs_data_t *, void *);
|
||||||
|
|
||||||
struct obs_websocket_request_response {
|
struct obs_websocket_request_response {
|
||||||
unsigned int status_code;
|
unsigned int status_code;
|
||||||
@ -55,8 +56,9 @@ static inline proc_handler_t *obs_websocket_get_ph(void)
|
|||||||
|
|
||||||
calldata_t cd = {0};
|
calldata_t cd = {0};
|
||||||
if (!proc_handler_call(global_ph, "obs_websocket_api_get_ph", &cd))
|
if (!proc_handler_call(global_ph, "obs_websocket_api_get_ph", &cd))
|
||||||
blog(LOG_DEBUG, "Unable to fetch obs-websocket proc handler object. obs-websocket not installed?");
|
blog(LOG_DEBUG,
|
||||||
proc_handler_t *ret = (proc_handler_t*)calldata_ptr(&cd, "ph");
|
"Unable to fetch obs-websocket proc handler object. obs-websocket not installed?");
|
||||||
|
proc_handler_t *ret = (proc_handler_t *)calldata_ptr(&cd, "ph");
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -69,7 +71,9 @@ static inline bool obs_websocket_ensure_ph(void)
|
|||||||
return _ph != NULL;
|
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
|
||||||
|
obs_websocket_vendor_run_simple_proc(obs_websocket_vendor vendor,
|
||||||
|
const char *proc_name, calldata_t *cd)
|
||||||
{
|
{
|
||||||
if (!obs_websocket_ensure_ph())
|
if (!obs_websocket_ensure_ph())
|
||||||
return false;
|
return false;
|
||||||
@ -104,7 +108,9 @@ static inline unsigned int obs_websocket_get_api_version(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calls an obs-websocket request. Free response with `obs_websocket_request_response_free()`
|
// Calls an obs-websocket request. Free response with `obs_websocket_request_response_free()`
|
||||||
static inline obs_websocket_request_response *obs_websocket_call_request(const char *request_type, obs_data_t *request_data = NULL)
|
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())
|
if (!obs_websocket_ensure_ph())
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -120,7 +126,8 @@ static inline obs_websocket_request_response *obs_websocket_call_request(const c
|
|||||||
|
|
||||||
proc_handler_call(_ph, "call_request", &cd);
|
proc_handler_call(_ph, "call_request", &cd);
|
||||||
|
|
||||||
auto ret = (struct obs_websocket_request_response*)calldata_ptr(&cd, "response");
|
auto ret = (struct obs_websocket_request_response *)calldata_ptr(
|
||||||
|
&cd, "response");
|
||||||
|
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
|
||||||
@ -128,7 +135,8 @@ static inline obs_websocket_request_response *obs_websocket_call_request(const c
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Free a request response object returned by `obs_websocket_call_request()`
|
// 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)
|
static inline void obs_websocket_request_response_free(
|
||||||
|
struct obs_websocket_request_response *response)
|
||||||
{
|
{
|
||||||
if (!response)
|
if (!response)
|
||||||
return;
|
return;
|
||||||
@ -144,7 +152,8 @@ static inline void obs_websocket_request_response_free(struct obs_websocket_requ
|
|||||||
|
|
||||||
// ALWAYS CALL ONLY VIA `obs_module_post_load()` CALLBACK!
|
// ALWAYS CALL ONLY VIA `obs_module_post_load()` CALLBACK!
|
||||||
// Registers a new "vendor" (Example: obs-ndi)
|
// Registers a new "vendor" (Example: obs-ndi)
|
||||||
static inline obs_websocket_vendor obs_websocket_register_vendor(const char *vendor_name)
|
static inline obs_websocket_vendor
|
||||||
|
obs_websocket_register_vendor(const char *vendor_name)
|
||||||
{
|
{
|
||||||
if (!obs_websocket_ensure_ph())
|
if (!obs_websocket_ensure_ph())
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -161,7 +170,10 @@ static inline obs_websocket_vendor obs_websocket_register_vendor(const char *ven
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Registers a new request for a vendor
|
// Registers a new request for a vendor
|
||||||
static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor vendor, const char *request_type, obs_websocket_request_callback_function request_callback, void* priv_data)
|
static inline bool obs_websocket_vendor_register_request(
|
||||||
|
obs_websocket_vendor vendor, const char *request_type,
|
||||||
|
obs_websocket_request_callback_function request_callback,
|
||||||
|
void *priv_data)
|
||||||
{
|
{
|
||||||
calldata_t cd = {0};
|
calldata_t cd = {0};
|
||||||
|
|
||||||
@ -172,20 +184,24 @@ static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor ve
|
|||||||
calldata_set_string(&cd, "type", request_type);
|
calldata_set_string(&cd, "type", request_type);
|
||||||
calldata_set_ptr(&cd, "callback", &cb);
|
calldata_set_ptr(&cd, "callback", &cb);
|
||||||
|
|
||||||
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_request_register", &cd);
|
bool success = obs_websocket_vendor_run_simple_proc(
|
||||||
|
vendor, "vendor_request_register", &cd);
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unregisters an existing vendor request
|
// Unregisters an existing vendor request
|
||||||
static inline bool obs_websocket_vendor_unregister_request(obs_websocket_vendor vendor, const char *request_type)
|
static inline bool
|
||||||
|
obs_websocket_vendor_unregister_request(obs_websocket_vendor vendor,
|
||||||
|
const char *request_type)
|
||||||
{
|
{
|
||||||
calldata_t cd = {0};
|
calldata_t cd = {0};
|
||||||
|
|
||||||
calldata_set_string(&cd, "type", request_type);
|
calldata_set_string(&cd, "type", request_type);
|
||||||
|
|
||||||
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_request_unregister", &cd);
|
bool success = obs_websocket_vendor_run_simple_proc(
|
||||||
|
vendor, "vendor_request_unregister", &cd);
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
@ -193,14 +209,17 @@ static inline bool obs_websocket_vendor_unregister_request(obs_websocket_vendor
|
|||||||
|
|
||||||
// Does not affect event_data refcount.
|
// Does not affect event_data refcount.
|
||||||
// Emits an event under the vendor's name
|
// Emits an event under the vendor's name
|
||||||
static inline bool obs_websocket_vendor_emit_event(obs_websocket_vendor vendor, const char *event_name, obs_data_t *event_data)
|
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", event_name);
|
calldata_set_string(&cd, "type", event_name);
|
||||||
calldata_set_ptr(&cd, "data", (void*)event_data);
|
calldata_set_ptr(&cd, "data", (void *)event_data);
|
||||||
|
|
||||||
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_event_emit", &cd);
|
bool success = obs_websocket_vendor_run_simple_proc(
|
||||||
|
vendor, "vendor_event_emit", &cd);
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
132
src/Config.cpp
132
src/Config.cpp
@ -38,37 +38,44 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#define CMDLINE_WEBSOCKET_PASSWORD "websocket_password"
|
#define CMDLINE_WEBSOCKET_PASSWORD "websocket_password"
|
||||||
#define CMDLINE_WEBSOCKET_DEBUG "websocket_debug"
|
#define CMDLINE_WEBSOCKET_DEBUG "websocket_debug"
|
||||||
|
|
||||||
Config::Config() :
|
Config::Config()
|
||||||
PortOverridden(false),
|
: PortOverridden(false),
|
||||||
PasswordOverridden(false),
|
PasswordOverridden(false),
|
||||||
FirstLoad(true),
|
FirstLoad(true),
|
||||||
ServerEnabled(true),
|
ServerEnabled(true),
|
||||||
ServerPort(4455),
|
ServerPort(4455),
|
||||||
BindLoopback(true),
|
BindLoopback(true),
|
||||||
Ipv4Only(false),
|
Ipv4Only(false),
|
||||||
DebugEnabled(false),
|
DebugEnabled(false),
|
||||||
AlertsEnabled(false),
|
AlertsEnabled(false),
|
||||||
AuthRequired(true),
|
AuthRequired(true),
|
||||||
ServerPassword("")
|
ServerPassword("")
|
||||||
{
|
{
|
||||||
SetDefaultsToGlobalStore();
|
SetDefaultsToGlobalStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Load()
|
void Config::Load()
|
||||||
{
|
{
|
||||||
config_t* obsConfig = GetConfigStore();
|
config_t *obsConfig = GetConfigStore();
|
||||||
if (!obsConfig) {
|
if (!obsConfig) {
|
||||||
blog(LOG_ERROR, "[Config::Load] Unable to fetch OBS config!");
|
blog(LOG_ERROR, "[Config::Load] Unable to fetch OBS config!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FirstLoad = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD);
|
FirstLoad = config_get_bool(obsConfig, CONFIG_SECTION_NAME,
|
||||||
ServerEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED);
|
PARAM_FIRSTLOAD);
|
||||||
AlertsEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS);
|
ServerEnabled =
|
||||||
ServerPort = config_get_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT);
|
config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED);
|
||||||
BindLoopback = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK);
|
AlertsEnabled =
|
||||||
AuthRequired = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED);
|
config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS);
|
||||||
ServerPassword = config_get_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD);
|
ServerPort =
|
||||||
|
config_get_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT);
|
||||||
|
BindLoopback = config_get_bool(obsConfig, CONFIG_SECTION_NAME,
|
||||||
|
PARAM_BINDLOOPBACK);
|
||||||
|
AuthRequired = config_get_bool(obsConfig, CONFIG_SECTION_NAME,
|
||||||
|
PARAM_AUTHREQUIRED);
|
||||||
|
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
|
||||||
@ -76,38 +83,49 @@ void Config::Load()
|
|||||||
if (FirstLoad) {
|
if (FirstLoad) {
|
||||||
FirstLoad = false;
|
FirstLoad = false;
|
||||||
if (ServerPassword.isEmpty()) {
|
if (ServerPassword.isEmpty()) {
|
||||||
blog(LOG_INFO, "[Config::Load] (FirstLoad) Generating new server password.");
|
blog(LOG_INFO,
|
||||||
ServerPassword = QString::fromStdString(Utils::Crypto::GeneratePassword());
|
"[Config::Load] (FirstLoad) Generating new server password.");
|
||||||
|
ServerPassword = QString::fromStdString(
|
||||||
|
Utils::Crypto::GeneratePassword());
|
||||||
} else {
|
} else {
|
||||||
blog(LOG_INFO, "[Config::Load] (FirstLoad) Not generating new password since one is already configured.");
|
blog(LOG_INFO,
|
||||||
|
"[Config::Load] (FirstLoad) Not generating new password since one is already configured.");
|
||||||
}
|
}
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process `--websocket_port` override
|
// Process `--websocket_port` override
|
||||||
QString portArgument = Utils::Platform::GetCommandLineArgument(CMDLINE_WEBSOCKET_PORT);
|
QString portArgument =
|
||||||
|
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, "[Config::Load] --websocket_port passed. Overriding WebSocket port with: %d", serverPort);
|
blog(LOG_INFO,
|
||||||
|
"[Config::Load] --websocket_port passed. Overriding WebSocket port with: %d",
|
||||||
|
serverPort);
|
||||||
PortOverridden = true;
|
PortOverridden = true;
|
||||||
ServerPort = serverPort;
|
ServerPort = serverPort;
|
||||||
} else {
|
} else {
|
||||||
blog(LOG_WARNING, "[Config::Load] Not overriding WebSocket port since integer conversion failed.");
|
blog(LOG_WARNING,
|
||||||
|
"[Config::Load] Not overriding WebSocket port since integer conversion failed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process `--websocket_ipv4_only` override
|
// Process `--websocket_ipv4_only` override
|
||||||
if (Utils::Platform::GetCommandLineFlagSet(CMDLINE_WEBSOCKET_IPV4_ONLY)) {
|
if (Utils::Platform::GetCommandLineFlagSet(
|
||||||
blog(LOG_INFO, "[Config::Load] --websocket_ipv4_only passed. Binding only to IPv4 interfaces.");
|
CMDLINE_WEBSOCKET_IPV4_ONLY)) {
|
||||||
|
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(CMDLINE_WEBSOCKET_PASSWORD);
|
QString passwordArgument = Utils::Platform::GetCommandLineArgument(
|
||||||
|
CMDLINE_WEBSOCKET_PASSWORD);
|
||||||
if (passwordArgument != "") {
|
if (passwordArgument != "") {
|
||||||
blog(LOG_INFO, "[Config::Load] --websocket_password passed. Overriding WebSocket password.");
|
blog(LOG_INFO,
|
||||||
|
"[Config::Load] --websocket_password passed. Overriding WebSocket password.");
|
||||||
PasswordOverridden = true;
|
PasswordOverridden = true;
|
||||||
AuthRequired = true;
|
AuthRequired = true;
|
||||||
ServerPassword = passwordArgument;
|
ServerPassword = passwordArgument;
|
||||||
@ -116,29 +134,37 @@ 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, "[Config::Load] --websocket_debug passed. Enabling debug logging.");
|
blog(LOG_INFO,
|
||||||
|
"[Config::Load] --websocket_debug passed. Enabling debug logging.");
|
||||||
DebugEnabled = true;
|
DebugEnabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Save()
|
void Config::Save()
|
||||||
{
|
{
|
||||||
config_t* obsConfig = GetConfigStore();
|
config_t *obsConfig = GetConfigStore();
|
||||||
if (!obsConfig) {
|
if (!obsConfig) {
|
||||||
blog(LOG_ERROR, "[Config::Save] Unable to fetch OBS config!");
|
blog(LOG_ERROR, "[Config::Save] Unable to fetch OBS config!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, FirstLoad);
|
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD,
|
||||||
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, ServerEnabled);
|
FirstLoad);
|
||||||
|
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED,
|
||||||
|
ServerEnabled);
|
||||||
if (!PortOverridden) {
|
if (!PortOverridden) {
|
||||||
config_set_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
|
config_set_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT,
|
||||||
|
ServerPort);
|
||||||
}
|
}
|
||||||
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK, BindLoopback);
|
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK,
|
||||||
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
|
BindLoopback);
|
||||||
|
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS,
|
||||||
|
AlertsEnabled);
|
||||||
if (!PasswordOverridden) {
|
if (!PasswordOverridden) {
|
||||||
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
config_set_bool(obsConfig, CONFIG_SECTION_NAME,
|
||||||
config_set_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
|
PARAM_AUTHREQUIRED, AuthRequired);
|
||||||
|
config_set_string(obsConfig, CONFIG_SECTION_NAME,
|
||||||
|
PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
|
||||||
}
|
}
|
||||||
|
|
||||||
config_save(obsConfig);
|
config_save(obsConfig);
|
||||||
@ -146,22 +172,30 @@ void Config::Save()
|
|||||||
|
|
||||||
void Config::SetDefaultsToGlobalStore()
|
void Config::SetDefaultsToGlobalStore()
|
||||||
{
|
{
|
||||||
config_t* obsConfig = GetConfigStore();
|
config_t *obsConfig = GetConfigStore();
|
||||||
if (!obsConfig) {
|
if (!obsConfig) {
|
||||||
blog(LOG_ERROR, "[Config::SetDefaultsToGlobalStore] Unable to fetch OBS config!");
|
blog(LOG_ERROR,
|
||||||
|
"[Config::SetDefaultsToGlobalStore] Unable to fetch OBS config!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, FirstLoad);
|
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD,
|
||||||
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, ServerEnabled);
|
FirstLoad);
|
||||||
config_set_default_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
|
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED,
|
||||||
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK, BindLoopback);
|
ServerEnabled);
|
||||||
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
|
config_set_default_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT,
|
||||||
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
ServerPort);
|
||||||
config_set_default_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
|
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME,
|
||||||
|
PARAM_BINDLOOPBACK, BindLoopback);
|
||||||
|
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS,
|
||||||
|
AlertsEnabled);
|
||||||
|
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME,
|
||||||
|
PARAM_AUTHREQUIRED, AuthRequired);
|
||||||
|
config_set_default_string(obsConfig, CONFIG_SECTION_NAME,
|
||||||
|
PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
|
||||||
}
|
}
|
||||||
|
|
||||||
config_t* Config::GetConfigStore()
|
config_t *Config::GetConfigStore()
|
||||||
{
|
{
|
||||||
return obs_frontend_get_global_config();
|
return obs_frontend_get_global_config();
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ struct Config {
|
|||||||
void Load();
|
void Load();
|
||||||
void Save();
|
void Save();
|
||||||
void SetDefaultsToGlobalStore();
|
void SetDefaultsToGlobalStore();
|
||||||
config_t* GetConfigStore();
|
config_t *GetConfigStore();
|
||||||
|
|
||||||
std::atomic<bool> PortOverridden;
|
std::atomic<bool> PortOverridden;
|
||||||
std::atomic<bool> PasswordOverridden;
|
std::atomic<bool> PasswordOverridden;
|
||||||
|
@ -3,7 +3,11 @@
|
|||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
#include "utils/Json.h"
|
#include "utils/Json.h"
|
||||||
|
|
||||||
#define RETURN_STATUS(status) { calldata_set_bool(cd, "success", status); return; }
|
#define RETURN_STATUS(status) \
|
||||||
|
{ \
|
||||||
|
calldata_set_bool(cd, "success", status); \
|
||||||
|
return; \
|
||||||
|
}
|
||||||
#define RETURN_SUCCESS() RETURN_STATUS(true);
|
#define RETURN_SUCCESS() RETURN_STATUS(true);
|
||||||
#define RETURN_FAILURE() RETURN_STATUS(false);
|
#define RETURN_FAILURE() RETURN_STATUS(false);
|
||||||
|
|
||||||
@ -11,11 +15,12 @@ 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, "[WebSocketApi: get_vendor] Failed due to missing `vendor` pointer.");
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi: get_vendor] Failed due to missing `vendor` pointer.");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_cast<WebSocketApi::Vendor*>(voidVendor);
|
return static_cast<WebSocketApi::Vendor *>(voidVendor);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketApi::WebSocketApi()
|
WebSocketApi::WebSocketApi()
|
||||||
@ -24,17 +29,33 @@ WebSocketApi::WebSocketApi()
|
|||||||
|
|
||||||
_procHandler = proc_handler_create();
|
_procHandler = proc_handler_create();
|
||||||
|
|
||||||
proc_handler_add(_procHandler, "bool get_api_version(out int version)", &get_api_version, nullptr);
|
proc_handler_add(_procHandler, "bool get_api_version(out int version)",
|
||||||
proc_handler_add(_procHandler, "bool call_request(in string request_type, in string request_data, out ptr response)", &call_request, nullptr);
|
&get_api_version, nullptr);
|
||||||
proc_handler_add(_procHandler, "bool vendor_register(in string name, out ptr vendor)", &vendor_register_cb, this);
|
proc_handler_add(
|
||||||
proc_handler_add(_procHandler, "bool vendor_request_register(in ptr vendor, in string type, in ptr callback)", &vendor_request_register_cb, this);
|
_procHandler,
|
||||||
proc_handler_add(_procHandler, "bool vendor_request_unregister(in ptr vendor, in string type)", &vendor_request_unregister_cb, this);
|
"bool call_request(in string request_type, in string request_data, out ptr response)",
|
||||||
proc_handler_add(_procHandler, "bool vendor_event_emit(in ptr vendor, in string type, in ptr data)", &vendor_event_emit_cb, this);
|
&call_request, nullptr);
|
||||||
|
proc_handler_add(_procHandler,
|
||||||
|
"bool vendor_register(in string name, out ptr vendor)",
|
||||||
|
&vendor_register_cb, this);
|
||||||
|
proc_handler_add(
|
||||||
|
_procHandler,
|
||||||
|
"bool vendor_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)", &get_ph_cb, this);
|
proc_handler_add(ph, "bool obs_websocket_api_get_ph(out ptr ph)",
|
||||||
|
&get_ph_cb, this);
|
||||||
|
|
||||||
blog_debug("[WebSocketApi::WebSocketApi] Finished.");
|
blog_debug("[WebSocketApi::WebSocketApi] Finished.");
|
||||||
}
|
}
|
||||||
@ -46,7 +67,8 @@ 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", vendor.first.c_str());
|
blog_debug("[WebSocketApi::~WebSocketApi] Deleting vendor: %s",
|
||||||
|
vendor.first.c_str());
|
||||||
delete vendor.second;
|
delete vendor.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +80,9 @@ void WebSocketApi::SetEventCallback(EventCallback cb)
|
|||||||
_eventCallback = cb;
|
_eventCallback = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType, obs_data_t *requestData, obs_data_t *responseData)
|
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(
|
||||||
|
std::string vendorName, std::string requestType,
|
||||||
|
obs_data_t *requestData, obs_data_t *responseData)
|
||||||
{
|
{
|
||||||
std::shared_lock l(_mutex);
|
std::shared_lock l(_mutex);
|
||||||
|
|
||||||
@ -85,9 +109,9 @@ enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::str
|
|||||||
|
|
||||||
void WebSocketApi::get_ph_cb(void *priv_data, calldata_t *cd)
|
void WebSocketApi::get_ph_cb(void *priv_data, calldata_t *cd)
|
||||||
{
|
{
|
||||||
auto c = static_cast<WebSocketApi*>(priv_data);
|
auto c = static_cast<WebSocketApi *>(priv_data);
|
||||||
|
|
||||||
calldata_set_ptr(cd, "ph", (void*)c->_procHandler);
|
calldata_set_ptr(cd, "ph", (void *)c->_procHandler);
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
@ -107,7 +131,8 @@ void WebSocketApi::call_request(void *, calldata_t *cd)
|
|||||||
if (!request_type)
|
if (!request_type)
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
|
|
||||||
auto response = static_cast<obs_websocket_request_response*>(bzalloc(sizeof(struct obs_websocket_request_response)));
|
auto response = static_cast<obs_websocket_request_response *>(
|
||||||
|
bzalloc(sizeof(struct obs_websocket_request_response)));
|
||||||
if (!response)
|
if (!response)
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
|
|
||||||
@ -129,18 +154,22 @@ void WebSocketApi::call_request(void *, calldata_t *cd)
|
|||||||
|
|
||||||
calldata_set_ptr(cd, "response", response);
|
calldata_set_ptr(cd, "response", response);
|
||||||
|
|
||||||
blog_debug("[WebSocketApi::call_request] Request %s called, response status code is %u", request_type, response->status_code);
|
blog_debug(
|
||||||
|
"[WebSocketApi::call_request] Request %s called, response status code is %u",
|
||||||
|
request_type, response->status_code);
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
|
void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
|
||||||
{
|
{
|
||||||
auto c = static_cast<WebSocketApi*>(priv_data);
|
auto c = static_cast<WebSocketApi *>(priv_data);
|
||||||
|
|
||||||
const char *vendorName;
|
const char *vendorName;
|
||||||
if (!calldata_get_string(cd, "name", &vendorName) || strlen(vendorName) == 0) {
|
if (!calldata_get_string(cd, "name", &vendorName) ||
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed due to missing `name` string.");
|
strlen(vendorName) == 0) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_register_cb] Failed due to missing `name` string.");
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,18 +177,22 @@ void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
|
|||||||
std::unique_lock l(c->_mutex);
|
std::unique_lock l(c->_mutex);
|
||||||
|
|
||||||
if (c->_vendors.count(vendorName)) {
|
if (c->_vendors.count(vendorName)) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed because `%s` is already a registered vendor.", vendorName);
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_register_cb] Failed because `%s` is already a registered vendor.",
|
||||||
|
vendorName);
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vendor* v = new Vendor();
|
Vendor *v = new Vendor();
|
||||||
v->_name = vendorName;
|
v->_name = vendorName;
|
||||||
|
|
||||||
c->_vendors[vendorName] = v;
|
c->_vendors[vendorName] = v;
|
||||||
|
|
||||||
blog_debug("[WebSocketApi::vendor_register_cb] [vendorName: %s] Registered new vendor.", v->_name.c_str());
|
blog_debug(
|
||||||
|
"[WebSocketApi::vendor_register_cb] [vendorName: %s] Registered new vendor.",
|
||||||
|
v->_name.c_str());
|
||||||
|
|
||||||
calldata_set_ptr(cd, "vendor", static_cast<void*>(v));
|
calldata_set_ptr(cd, "vendor", static_cast<void *>(v));
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
@ -171,29 +204,38 @@ 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) || strlen(requestType) == 0) {
|
if (!calldata_get_string(cd, "type", &requestType) ||
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing or empty `type` string.", v->_name.c_str());
|
strlen(requestType) == 0) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing or empty `type` string.",
|
||||||
|
v->_name.c_str());
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *voidCallback;
|
void *voidCallback;
|
||||||
if (!calldata_get_ptr(cd, "callback", &voidCallback) || !voidCallback) {
|
if (!calldata_get_ptr(cd, "callback", &voidCallback) || !voidCallback) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing `callback` pointer.", v->_name.c_str());
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing `callback` pointer.",
|
||||||
|
v->_name.c_str());
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cb = static_cast<obs_websocket_request_callback*>(voidCallback);
|
auto cb = static_cast<obs_websocket_request_callback *>(voidCallback);
|
||||||
|
|
||||||
std::unique_lock l(v->_mutex);
|
std::unique_lock l(v->_mutex);
|
||||||
|
|
||||||
if (v->_requests.count(requestType)) {
|
if (v->_requests.count(requestType)) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is already a registered request.", v->_name.c_str(), requestType);
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is already a registered request.",
|
||||||
|
v->_name.c_str(), requestType);
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
v->_requests[requestType] = *cb;
|
v->_requests[requestType] = *cb;
|
||||||
|
|
||||||
blog_debug("[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Registered new vendor request: %s", v->_name.c_str(), requestType);
|
blog_debug(
|
||||||
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Registered new vendor request: %s",
|
||||||
|
v->_name.c_str(), requestType);
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
@ -205,46 +247,58 @@ 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) || strlen(requestType) == 0) {
|
if (!calldata_get_string(cd, "type", &requestType) ||
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Failed due to missing `type` string.", v->_name.c_str());
|
strlen(requestType) == 0) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Failed due to missing `type` string.",
|
||||||
|
v->_name.c_str());
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_lock l(v->_mutex);
|
std::unique_lock l(v->_mutex);
|
||||||
|
|
||||||
if (!v->_requests.count(requestType)) {
|
if (!v->_requests.count(requestType)) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is not a registered request.", v->_name.c_str(), requestType);
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is not a registered request.",
|
||||||
|
v->_name.c_str(), requestType);
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
v->_requests.erase(requestType);
|
v->_requests.erase(requestType);
|
||||||
|
|
||||||
blog_debug("[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Unregistered vendor request: %s", v->_name.c_str(), requestType);
|
blog_debug(
|
||||||
|
"[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Unregistered vendor request: %s",
|
||||||
|
v->_name.c_str(), requestType);
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketApi::vendor_event_emit_cb(void *priv_data, calldata_t *cd)
|
void WebSocketApi::vendor_event_emit_cb(void *priv_data, calldata_t *cd)
|
||||||
{
|
{
|
||||||
auto c = static_cast<WebSocketApi*>(priv_data);
|
auto c = static_cast<WebSocketApi *>(priv_data);
|
||||||
|
|
||||||
Vendor *v = get_vendor(cd);
|
Vendor *v = get_vendor(cd);
|
||||||
if (!v)
|
if (!v)
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
|
|
||||||
const char *eventType;
|
const char *eventType;
|
||||||
if (!calldata_get_string(cd, "type", &eventType) || strlen(eventType) == 0) {
|
if (!calldata_get_string(cd, "type", &eventType) ||
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `type` string.", v->_name.c_str());
|
strlen(eventType) == 0) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `type` string.",
|
||||||
|
v->_name.c_str());
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *voidEventData;
|
void *voidEventData;
|
||||||
if (!calldata_get_ptr(cd, "data", &voidEventData)) {
|
if (!calldata_get_ptr(cd, "data", &voidEventData)) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `data` pointer.", v->_name.c_str());
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `data` pointer.",
|
||||||
|
v->_name.c_str());
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto eventData = static_cast<obs_data_t*>(voidEventData);
|
auto eventData = static_cast<obs_data_t *>(voidEventData);
|
||||||
|
|
||||||
if (!c->_eventCallback)
|
if (!c->_eventCallback)
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
|
@ -10,39 +10,44 @@
|
|||||||
#include "../lib/obs-websocket-api.h"
|
#include "../lib/obs-websocket-api.h"
|
||||||
|
|
||||||
class WebSocketApi {
|
class WebSocketApi {
|
||||||
public:
|
public:
|
||||||
enum RequestReturnCode {
|
enum RequestReturnCode {
|
||||||
Normal,
|
Normal,
|
||||||
NoVendor,
|
NoVendor,
|
||||||
NoVendorRequest,
|
NoVendorRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::function<void(std::string, std::string, obs_data_t*)> EventCallback;
|
typedef std::function<void(std::string, std::string, obs_data_t *)>
|
||||||
|
EventCallback;
|
||||||
|
|
||||||
struct Vendor {
|
struct Vendor {
|
||||||
std::shared_mutex _mutex;
|
|
||||||
std::string _name;
|
|
||||||
std::map<std::string, obs_websocket_request_callback> _requests;
|
|
||||||
};
|
|
||||||
|
|
||||||
WebSocketApi();
|
|
||||||
~WebSocketApi();
|
|
||||||
|
|
||||||
void SetEventCallback(EventCallback cb);
|
|
||||||
|
|
||||||
enum RequestReturnCode PerformVendorRequest(std::string vendorName, std::string requestName, obs_data_t *requestData, obs_data_t *responseData);
|
|
||||||
|
|
||||||
static void get_ph_cb(void *priv_data, calldata_t *cd);
|
|
||||||
static void get_api_version(void *, calldata_t *cd);
|
|
||||||
static void call_request(void *, calldata_t *cd);
|
|
||||||
static void vendor_register_cb(void *priv_data, calldata_t *cd);
|
|
||||||
static void vendor_request_register_cb(void *priv_data, calldata_t *cd);
|
|
||||||
static void vendor_request_unregister_cb(void *priv_data, calldata_t *cd);
|
|
||||||
static void vendor_event_emit_cb(void *priv_data, calldata_t *cd);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_mutex _mutex;
|
std::shared_mutex _mutex;
|
||||||
EventCallback _eventCallback;
|
std::string _name;
|
||||||
proc_handler_t *_procHandler;
|
std::map<std::string, obs_websocket_request_callback> _requests;
|
||||||
std::map<std::string, Vendor*> _vendors;
|
};
|
||||||
|
|
||||||
|
WebSocketApi();
|
||||||
|
~WebSocketApi();
|
||||||
|
|
||||||
|
void SetEventCallback(EventCallback cb);
|
||||||
|
|
||||||
|
enum RequestReturnCode PerformVendorRequest(std::string vendorName,
|
||||||
|
std::string requestName,
|
||||||
|
obs_data_t *requestData,
|
||||||
|
obs_data_t *responseData);
|
||||||
|
|
||||||
|
static void get_ph_cb(void *priv_data, calldata_t *cd);
|
||||||
|
static void get_api_version(void *, calldata_t *cd);
|
||||||
|
static void call_request(void *, calldata_t *cd);
|
||||||
|
static void vendor_register_cb(void *priv_data, calldata_t *cd);
|
||||||
|
static void vendor_request_register_cb(void *priv_data, calldata_t *cd);
|
||||||
|
static void vendor_request_unregister_cb(void *priv_data,
|
||||||
|
calldata_t *cd);
|
||||||
|
static void vendor_event_emit_cb(void *priv_data, calldata_t *cd);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_mutex _mutex;
|
||||||
|
EventCallback _eventCallback;
|
||||||
|
proc_handler_t *_procHandler;
|
||||||
|
std::map<std::string, Vendor *> _vendors;
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -29,123 +29,181 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../utils/Obs_VolumeMeter.h"
|
#include "../utils/Obs_VolumeMeter.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
class EventHandler
|
class EventHandler {
|
||||||
{
|
public:
|
||||||
public:
|
EventHandler();
|
||||||
EventHandler();
|
~EventHandler();
|
||||||
~EventHandler();
|
|
||||||
|
|
||||||
typedef std::function<void(uint64_t, std::string, json, uint8_t)> BroadcastCallback;
|
typedef std::function<void(uint64_t, std::string, json, uint8_t)>
|
||||||
void SetBroadcastCallback(BroadcastCallback cb);
|
BroadcastCallback;
|
||||||
typedef std::function<void()> ObsLoadedCallback;
|
void SetBroadcastCallback(BroadcastCallback cb);
|
||||||
void SetObsLoadedCallback(ObsLoadedCallback cb);
|
typedef std::function<void()> ObsLoadedCallback;
|
||||||
|
void SetObsLoadedCallback(ObsLoadedCallback cb);
|
||||||
|
|
||||||
void ProcessSubscription(uint64_t eventSubscriptions);
|
void ProcessSubscription(uint64_t eventSubscriptions);
|
||||||
void ProcessUnsubscription(uint64_t eventSubscriptions);
|
void ProcessUnsubscription(uint64_t eventSubscriptions);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BroadcastCallback _broadcastCallback;
|
BroadcastCallback _broadcastCallback;
|
||||||
ObsLoadedCallback _obsLoadedCallback;
|
ObsLoadedCallback _obsLoadedCallback;
|
||||||
|
|
||||||
std::atomic<bool> _obsLoaded;
|
std::atomic<bool> _obsLoaded;
|
||||||
|
|
||||||
std::unique_ptr<Utils::Obs::VolumeMeter::Handler> _inputVolumeMetersHandler;
|
std::unique_ptr<Utils::Obs::VolumeMeter::Handler>
|
||||||
std::atomic<uint64_t> _inputVolumeMetersRef;
|
_inputVolumeMetersHandler;
|
||||||
std::atomic<uint64_t> _inputActiveStateChangedRef;
|
std::atomic<uint64_t> _inputVolumeMetersRef;
|
||||||
std::atomic<uint64_t> _inputShowStateChangedRef;
|
std::atomic<uint64_t> _inputActiveStateChangedRef;
|
||||||
std::atomic<uint64_t> _sceneItemTransformChangedRef;
|
std::atomic<uint64_t> _inputShowStateChangedRef;
|
||||||
|
std::atomic<uint64_t> _sceneItemTransformChangedRef;
|
||||||
|
|
||||||
void ConnectSourceSignals(obs_source_t *source);
|
void ConnectSourceSignals(obs_source_t *source);
|
||||||
void DisconnectSourceSignals(obs_source_t *source);
|
void DisconnectSourceSignals(obs_source_t *source);
|
||||||
|
|
||||||
void BroadcastEvent(uint64_t requiredIntent, std::string eventType, json eventData = nullptr, uint8_t rpcVersion = 0);
|
void BroadcastEvent(uint64_t requiredIntent, std::string eventType,
|
||||||
|
json eventData = nullptr, uint8_t rpcVersion = 0);
|
||||||
|
|
||||||
// Signal handler: frontend
|
// Signal handler: frontend
|
||||||
static void OnFrontendEvent(enum obs_frontend_event event, void *private_data);
|
static void OnFrontendEvent(enum obs_frontend_event event,
|
||||||
|
void *private_data);
|
||||||
|
|
||||||
// Signal handler: libobs
|
// Signal handler: libobs
|
||||||
static void SourceCreatedMultiHandler(void *param, calldata_t *data);
|
static void SourceCreatedMultiHandler(void *param, calldata_t *data);
|
||||||
static void SourceDestroyedMultiHandler(void *param, calldata_t *data);
|
static void SourceDestroyedMultiHandler(void *param, calldata_t *data);
|
||||||
static void SourceRemovedMultiHandler(void *param, calldata_t *data);
|
static void SourceRemovedMultiHandler(void *param, calldata_t *data);
|
||||||
|
|
||||||
// Signal handler: source
|
// Signal handler: source
|
||||||
static void SourceRenamedMultiHandler(void *param, calldata_t *data);
|
static void SourceRenamedMultiHandler(void *param, calldata_t *data);
|
||||||
static void SourceMediaPauseMultiHandler(void *param, calldata_t *data);
|
static void SourceMediaPauseMultiHandler(void *param, calldata_t *data);
|
||||||
static void SourceMediaPlayMultiHandler(void *param, calldata_t *data);
|
static void SourceMediaPlayMultiHandler(void *param, calldata_t *data);
|
||||||
static void SourceMediaRestartMultiHandler(void *param, calldata_t *data);
|
static void SourceMediaRestartMultiHandler(void *param,
|
||||||
static void SourceMediaStopMultiHandler(void *param, calldata_t *data);
|
calldata_t *data);
|
||||||
static void SourceMediaNextMultiHandler(void *param, calldata_t *data);
|
static void SourceMediaStopMultiHandler(void *param, calldata_t *data);
|
||||||
static void SourceMediaPreviousMultiHandler(void *param, calldata_t *data);
|
static void SourceMediaNextMultiHandler(void *param, calldata_t *data);
|
||||||
|
static void SourceMediaPreviousMultiHandler(void *param,
|
||||||
|
calldata_t *data);
|
||||||
|
|
||||||
|
// General
|
||||||
|
void HandleExitStarted();
|
||||||
|
void HandleStudioModeStateChanged(bool enabled);
|
||||||
|
|
||||||
// General
|
// Config
|
||||||
void HandleExitStarted();
|
void HandleCurrentSceneCollectionChanging();
|
||||||
void HandleStudioModeStateChanged(bool enabled);
|
void HandleCurrentSceneCollectionChanged();
|
||||||
|
void HandleSceneCollectionListChanged();
|
||||||
|
void HandleCurrentProfileChanging();
|
||||||
|
void HandleCurrentProfileChanged();
|
||||||
|
void HandleProfileListChanged();
|
||||||
|
|
||||||
// Config
|
// Scenes
|
||||||
void HandleCurrentSceneCollectionChanging();
|
void HandleSceneCreated(obs_source_t *source);
|
||||||
void HandleCurrentSceneCollectionChanged();
|
void HandleSceneRemoved(obs_source_t *source);
|
||||||
void HandleSceneCollectionListChanged();
|
void HandleSceneNameChanged(obs_source_t *source,
|
||||||
void HandleCurrentProfileChanging();
|
std::string oldSceneName,
|
||||||
void HandleCurrentProfileChanged();
|
std::string sceneName);
|
||||||
void HandleProfileListChanged();
|
void HandleCurrentProgramSceneChanged();
|
||||||
|
void HandleCurrentPreviewSceneChanged();
|
||||||
|
void HandleSceneListChanged();
|
||||||
|
|
||||||
// Scenes
|
// Inputs
|
||||||
void HandleSceneCreated(obs_source_t *source);
|
void HandleInputCreated(obs_source_t *source);
|
||||||
void HandleSceneRemoved(obs_source_t *source);
|
void HandleInputRemoved(obs_source_t *source);
|
||||||
void HandleSceneNameChanged(obs_source_t *source, std::string oldSceneName, std::string sceneName);
|
void HandleInputNameChanged(obs_source_t *source,
|
||||||
void HandleCurrentProgramSceneChanged();
|
std::string oldInputName,
|
||||||
void HandleCurrentPreviewSceneChanged();
|
std::string inputName);
|
||||||
void HandleSceneListChanged();
|
void HandleInputVolumeMeters(
|
||||||
|
std::vector<json> inputs); // AudioMeter::Handler callback
|
||||||
|
static void
|
||||||
|
HandleInputActiveStateChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleInputShowStateChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleInputMuteStateChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleInputVolumeChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleInputAudioBalanceChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleInputAudioSyncOffsetChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleInputAudioTracksChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleInputAudioMonitorTypeChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
|
||||||
// Inputs
|
// Transitions
|
||||||
void HandleInputCreated(obs_source_t *source);
|
void HandleCurrentSceneTransitionChanged();
|
||||||
void HandleInputRemoved(obs_source_t *source);
|
void HandleCurrentSceneTransitionDurationChanged();
|
||||||
void HandleInputNameChanged(obs_source_t *source, std::string oldInputName, std::string inputName);
|
static void
|
||||||
void HandleInputVolumeMeters(std::vector<json> inputs); // AudioMeter::Handler callback
|
HandleSceneTransitionStarted(void *param,
|
||||||
static void HandleInputActiveStateChanged(void *param, calldata_t *data); // Direct callback
|
calldata_t *data); // Direct callback
|
||||||
static void HandleInputShowStateChanged(void *param, calldata_t *data); // Direct callback
|
static void
|
||||||
static void HandleInputMuteStateChanged(void *param, calldata_t *data); // Direct callback
|
HandleSceneTransitionEnded(void *param,
|
||||||
static void HandleInputVolumeChanged(void *param, calldata_t *data); // Direct callback
|
calldata_t *data); // Direct callback
|
||||||
static void HandleInputAudioBalanceChanged(void *param, calldata_t *data); // Direct callback
|
static void
|
||||||
static void HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data); // Direct callback
|
HandleSceneTransitionVideoEnded(void *param,
|
||||||
static void HandleInputAudioTracksChanged(void *param, calldata_t *data); // Direct callback
|
calldata_t *data); // Direct callback
|
||||||
static void HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data); // Direct callback
|
|
||||||
|
|
||||||
// Transitions
|
// Filters
|
||||||
void HandleCurrentSceneTransitionChanged();
|
static void FilterAddMultiHandler(void *param,
|
||||||
void HandleCurrentSceneTransitionDurationChanged();
|
calldata_t *data); // Direct callback
|
||||||
static void HandleSceneTransitionStarted(void *param, calldata_t *data); // Direct callback
|
static void
|
||||||
static void HandleSceneTransitionEnded(void *param, calldata_t *data); // Direct callback
|
FilterRemoveMultiHandler(void *param,
|
||||||
static void HandleSceneTransitionVideoEnded(void *param, calldata_t *data); // Direct callback
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleSourceFilterListReindexed(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
void HandleSourceFilterCreated(obs_source_t *source,
|
||||||
|
obs_source_t *filter);
|
||||||
|
void HandleSourceFilterRemoved(obs_source_t *source,
|
||||||
|
obs_source_t *filter);
|
||||||
|
static void
|
||||||
|
HandleSourceFilterNameChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void HandleSourceFilterEnableStateChanged(
|
||||||
|
void *param, calldata_t *data); // Direct callback
|
||||||
|
|
||||||
// Filters
|
// Outputs
|
||||||
static void FilterAddMultiHandler(void *param, calldata_t *data); // Direct callback
|
void HandleStreamStateChanged(ObsOutputState state);
|
||||||
static void FilterRemoveMultiHandler(void *param, calldata_t *data); // Direct callback
|
void HandleRecordStateChanged(ObsOutputState state);
|
||||||
static void HandleSourceFilterListReindexed(void *param, calldata_t *data); // Direct callback
|
void HandleReplayBufferStateChanged(ObsOutputState state);
|
||||||
void HandleSourceFilterCreated(obs_source_t *source, obs_source_t *filter);
|
void HandleVirtualcamStateChanged(ObsOutputState state);
|
||||||
void HandleSourceFilterRemoved(obs_source_t *source, obs_source_t *filter);
|
void HandleReplayBufferSaved();
|
||||||
static void HandleSourceFilterNameChanged(void *param, calldata_t *data); // Direct callback
|
|
||||||
static void HandleSourceFilterEnableStateChanged(void *param, calldata_t *data); // Direct callback
|
|
||||||
|
|
||||||
// Outputs
|
// Scene Items
|
||||||
void HandleStreamStateChanged(ObsOutputState state);
|
static void HandleSceneItemCreated(void *param,
|
||||||
void HandleRecordStateChanged(ObsOutputState state);
|
calldata_t *data); // Direct callback
|
||||||
void HandleReplayBufferStateChanged(ObsOutputState state);
|
static void HandleSceneItemRemoved(void *param,
|
||||||
void HandleVirtualcamStateChanged(ObsOutputState state);
|
calldata_t *data); // Direct callback
|
||||||
void HandleReplayBufferSaved();
|
static void
|
||||||
|
HandleSceneItemListReindexed(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleSceneItemEnableStateChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleSceneItemLockStateChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleSceneItemSelected(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
static void
|
||||||
|
HandleSceneItemTransformChanged(void *param,
|
||||||
|
calldata_t *data); // Direct callback
|
||||||
|
|
||||||
// Scene Items
|
// Media Inputs
|
||||||
static void HandleSceneItemCreated(void *param, calldata_t *data); // Direct callback
|
static void
|
||||||
static void HandleSceneItemRemoved(void *param, calldata_t *data); // Direct callback
|
HandleMediaInputPlaybackStarted(void *param,
|
||||||
static void HandleSceneItemListReindexed(void *param, calldata_t *data); // Direct callback
|
calldata_t *data); // Direct callback
|
||||||
static void HandleSceneItemEnableStateChanged(void *param, calldata_t *data); // Direct callback
|
static void
|
||||||
static void HandleSceneItemLockStateChanged(void *param, calldata_t *data); // Direct callback
|
HandleMediaInputPlaybackEnded(void *param,
|
||||||
static void HandleSceneItemSelected(void *param, calldata_t *data); // Direct callback
|
calldata_t *data); // Direct callback
|
||||||
static void HandleSceneItemTransformChanged(void *param, calldata_t *data); // Direct callback
|
void HandleMediaInputActionTriggered(obs_source_t *source,
|
||||||
|
ObsMediaInputAction action);
|
||||||
// Media Inputs
|
|
||||||
static void HandleMediaInputPlaybackStarted(void *param, calldata_t *data); // Direct callback
|
|
||||||
static void HandleMediaInputPlaybackEnded(void *param, calldata_t *data); // Direct callback
|
|
||||||
void HandleMediaInputActionTriggered(obs_source_t *source, ObsMediaInputAction action);
|
|
||||||
};
|
};
|
||||||
|
@ -38,8 +38,10 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
void EventHandler::HandleCurrentSceneCollectionChanging()
|
void EventHandler::HandleCurrentSceneCollectionChanging()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneCollectionName"] = Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
eventData["sceneCollectionName"] =
|
||||||
BroadcastEvent(EventSubscription::Config, "CurrentSceneCollectionChanging", eventData);
|
Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
||||||
|
BroadcastEvent(EventSubscription::Config,
|
||||||
|
"CurrentSceneCollectionChanging", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,8 +62,10 @@ void EventHandler::HandleCurrentSceneCollectionChanging()
|
|||||||
void EventHandler::HandleCurrentSceneCollectionChanged()
|
void EventHandler::HandleCurrentSceneCollectionChanged()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneCollectionName"] = Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
eventData["sceneCollectionName"] =
|
||||||
BroadcastEvent(EventSubscription::Config, "CurrentSceneCollectionChanged", eventData);
|
Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
||||||
|
BroadcastEvent(EventSubscription::Config,
|
||||||
|
"CurrentSceneCollectionChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,8 +84,10 @@ void EventHandler::HandleCurrentSceneCollectionChanged()
|
|||||||
void EventHandler::HandleSceneCollectionListChanged()
|
void EventHandler::HandleSceneCollectionListChanged()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneCollections"] = Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
eventData["sceneCollections"] =
|
||||||
BroadcastEvent(EventSubscription::Config, "SceneCollectionListChanged", eventData);
|
Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
||||||
|
BroadcastEvent(EventSubscription::Config, "SceneCollectionListChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,8 +106,10 @@ void EventHandler::HandleSceneCollectionListChanged()
|
|||||||
void EventHandler::HandleCurrentProfileChanging()
|
void EventHandler::HandleCurrentProfileChanging()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["profileName"] = Utils::Obs::StringHelper::GetCurrentProfile();
|
eventData["profileName"] =
|
||||||
BroadcastEvent(EventSubscription::Config, "CurrentProfileChanging", eventData);
|
Utils::Obs::StringHelper::GetCurrentProfile();
|
||||||
|
BroadcastEvent(EventSubscription::Config, "CurrentProfileChanging",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,8 +128,10 @@ void EventHandler::HandleCurrentProfileChanging()
|
|||||||
void EventHandler::HandleCurrentProfileChanged()
|
void EventHandler::HandleCurrentProfileChanged()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["profileName"] = Utils::Obs::StringHelper::GetCurrentProfile();
|
eventData["profileName"] =
|
||||||
BroadcastEvent(EventSubscription::Config, "CurrentProfileChanged", eventData);
|
Utils::Obs::StringHelper::GetCurrentProfile();
|
||||||
|
BroadcastEvent(EventSubscription::Config, "CurrentProfileChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,5 +151,6 @@ void EventHandler::HandleProfileListChanged()
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["profiles"] = Utils::Obs::ArrayHelper::GetProfileList();
|
eventData["profiles"] = Utils::Obs::ArrayHelper::GetProfileList();
|
||||||
BroadcastEvent(EventSubscription::Config, "ProfileListChanged", eventData);
|
BroadcastEvent(EventSubscription::Config, "ProfileListChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
void EventHandler::FilterAddMultiHandler(void *param, calldata_t *data)
|
void EventHandler::FilterAddMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter");
|
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter");
|
||||||
@ -36,7 +36,7 @@ void EventHandler::FilterAddMultiHandler(void *param, calldata_t *data)
|
|||||||
|
|
||||||
void EventHandler::FilterRemoveMultiHandler(void *param, calldata_t *data)
|
void EventHandler::FilterRemoveMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter");
|
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter");
|
||||||
@ -63,9 +63,10 @@ void EventHandler::FilterRemoveMultiHandler(void *param, calldata_t *data)
|
|||||||
* @api events
|
* @api events
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleSourceFilterListReindexed(void *param, calldata_t *data)
|
void EventHandler::HandleSourceFilterListReindexed(void *param,
|
||||||
|
calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -73,8 +74,10 @@ void EventHandler::HandleSourceFilterListReindexed(void *param, calldata_t *data
|
|||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sourceName"] = obs_source_get_name(source);
|
eventData["sourceName"] = obs_source_get_name(source);
|
||||||
eventData["filters"] = Utils::Obs::ArrayHelper::GetSourceFilterList(source);
|
eventData["filters"] =
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterListReindexed", eventData);
|
Utils::Obs::ArrayHelper::GetSourceFilterList(source);
|
||||||
|
eventHandler->BroadcastEvent(EventSubscription::Filters,
|
||||||
|
"SourceFilterListReindexed", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,20 +98,26 @@ void EventHandler::HandleSourceFilterListReindexed(void *param, calldata_t *data
|
|||||||
* @api events
|
* @api events
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleSourceFilterCreated(obs_source_t *source, obs_source_t *filter)
|
void EventHandler::HandleSourceFilterCreated(obs_source_t *source,
|
||||||
|
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 = obs_get_source_defaults(filterKind.c_str());
|
OBSDataAutoRelease defaultFilterSettings =
|
||||||
|
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"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter);
|
eventData["filterIndex"] =
|
||||||
eventData["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
|
Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter);
|
||||||
eventData["defaultFilterSettings"] = Utils::Json::ObsDataToJson(defaultFilterSettings, true);
|
eventData["filterSettings"] =
|
||||||
BroadcastEvent(EventSubscription::Filters, "SourceFilterCreated", eventData);
|
Utils::Json::ObsDataToJson(filterSettings);
|
||||||
|
eventData["defaultFilterSettings"] =
|
||||||
|
Utils::Json::ObsDataToJson(defaultFilterSettings, true);
|
||||||
|
BroadcastEvent(EventSubscription::Filters, "SourceFilterCreated",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -125,12 +134,14 @@ void EventHandler::HandleSourceFilterCreated(obs_source_t *source, obs_source_t
|
|||||||
* @api events
|
* @api events
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleSourceFilterRemoved(obs_source_t *source, obs_source_t *filter)
|
void EventHandler::HandleSourceFilterRemoved(obs_source_t *source,
|
||||||
|
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", eventData);
|
BroadcastEvent(EventSubscription::Filters, "SourceFilterRemoved",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -150,17 +161,19 @@ void EventHandler::HandleSourceFilterRemoved(obs_source_t *source, obs_source_t
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data)
|
void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!filter)
|
if (!filter)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sourceName"] = obs_source_get_name(obs_filter_get_parent(filter));
|
eventData["sourceName"] =
|
||||||
|
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, "SourceFilterNameChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Filters,
|
||||||
|
"SourceFilterNameChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,9 +191,10 @@ void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data)
|
|||||||
* @api events
|
* @api events
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleSourceFilterEnableStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleSourceFilterEnableStateChanged(void *param,
|
||||||
|
calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!filter)
|
if (!filter)
|
||||||
@ -197,5 +211,7 @@ void EventHandler::HandleSourceFilterEnableStateChanged(void *param, calldata_t
|
|||||||
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, "SourceFilterEnableStateChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Filters,
|
||||||
|
"SourceFilterEnableStateChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
@ -40,14 +40,17 @@ 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 = obs_get_source_defaults(inputKind.c_str());
|
OBSDataAutoRelease defaultInputSettings =
|
||||||
|
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"] = obs_source_get_unversioned_id(source);
|
eventData["unversionedInputKind"] =
|
||||||
|
obs_source_get_unversioned_id(source);
|
||||||
eventData["inputSettings"] = Utils::Json::ObsDataToJson(inputSettings);
|
eventData["inputSettings"] = Utils::Json::ObsDataToJson(inputSettings);
|
||||||
eventData["defaultInputSettings"] = Utils::Json::ObsDataToJson(defaultInputSettings, true);
|
eventData["defaultInputSettings"] =
|
||||||
|
Utils::Json::ObsDataToJson(defaultInputSettings, true);
|
||||||
BroadcastEvent(EventSubscription::Inputs, "InputCreated", eventData);
|
BroadcastEvent(EventSubscription::Inputs, "InputCreated", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,12 +88,15 @@ void EventHandler::HandleInputRemoved(obs_source_t *source)
|
|||||||
* @api events
|
* @api events
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputNameChanged(obs_source_t *, std::string oldInputName, std::string inputName)
|
void EventHandler::HandleInputNameChanged(obs_source_t *,
|
||||||
|
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", eventData);
|
BroadcastEvent(EventSubscription::Inputs, "InputNameChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,7 +117,7 @@ void EventHandler::HandleInputNameChanged(obs_source_t *, std::string oldInputNa
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
if (!eventHandler->_inputActiveStateChangedRef.load())
|
if (!eventHandler->_inputActiveStateChangedRef.load())
|
||||||
return;
|
return;
|
||||||
@ -126,7 +132,8 @@ 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, "InputActiveStateChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::InputActiveStateChanged,
|
||||||
|
"InputActiveStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,7 +154,7 @@ void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
if (!eventHandler->_inputShowStateChangedRef.load())
|
if (!eventHandler->_inputShowStateChangedRef.load())
|
||||||
return;
|
return;
|
||||||
@ -162,7 +169,8 @@ 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, "InputShowStateChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::InputShowStateChanged,
|
||||||
|
"InputShowStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,7 +189,7 @@ void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -193,7 +201,8 @@ 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, "InputMuteStateChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs,
|
||||||
|
"InputMuteStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -213,7 +222,7 @@ void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -233,7 +242,8 @@ 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, "InputVolumeChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs,
|
||||||
|
"InputVolumeChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -252,7 +262,7 @@ void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -266,7 +276,8 @@ 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, "InputAudioBalanceChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs,
|
||||||
|
"InputAudioBalanceChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -283,9 +294,10 @@ void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
|
|||||||
* @api events
|
* @api events
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputAudioSyncOffsetChanged(void *param,
|
||||||
|
calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -299,7 +311,8 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
|
|||||||
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, "InputAudioSyncOffsetChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs,
|
||||||
|
"InputAudioSyncOffsetChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -318,7 +331,7 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -331,13 +344,15 @@ 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)] = (bool)((tracks >> i) & 1);
|
inputAudioTracks[std::to_string(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, "InputAudioTracksChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs,
|
||||||
|
"InputAudioTracksChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -360,9 +375,10 @@ void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
|
|||||||
* @api events
|
* @api events
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputAudioMonitorTypeChanged(void *param,
|
||||||
|
calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -371,14 +387,17 @@ void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *d
|
|||||||
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 = (obs_monitoring_type)calldata_int(data, "type");
|
enum obs_monitoring_type monitorType =
|
||||||
|
(obs_monitoring_type)calldata_int(data, "type");
|
||||||
|
|
||||||
std::string monitorTypeString = Utils::Obs::StringHelper::GetInputMonitorType(monitorType);
|
std::string monitorTypeString =
|
||||||
|
Utils::Obs::StringHelper::GetInputMonitorType(monitorType);
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["inputName"] = obs_source_get_name(source);
|
eventData["inputName"] = obs_source_get_name(source);
|
||||||
eventData["monitorType"] = monitorTypeString;
|
eventData["monitorType"] = monitorTypeString;
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioMonitorTypeChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs,
|
||||||
|
"InputAudioMonitorTypeChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -398,5 +417,6 @@ void EventHandler::HandleInputVolumeMeters(std::vector<json> inputs)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["inputs"] = inputs;
|
eventData["inputs"] = inputs;
|
||||||
BroadcastEvent(EventSubscription::InputVolumeMeters, "InputVolumeMeters", eventData);
|
BroadcastEvent(EventSubscription::InputVolumeMeters,
|
||||||
|
"InputVolumeMeters", eventData);
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,14 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
#define CASE(x) case x: return #x;
|
#define CASE(x) \
|
||||||
|
case x: \
|
||||||
|
return #x;
|
||||||
|
|
||||||
std::string GetMediaInputActionString(ObsMediaInputAction action) {
|
std::string GetMediaInputActionString(ObsMediaInputAction action)
|
||||||
|
{
|
||||||
switch (action) {
|
switch (action) {
|
||||||
default:
|
default:
|
||||||
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE)
|
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE)
|
||||||
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY)
|
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY)
|
||||||
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART)
|
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART)
|
||||||
@ -35,7 +38,7 @@ std::string GetMediaInputActionString(ObsMediaInputAction action) {
|
|||||||
|
|
||||||
void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -44,12 +47,13 @@ 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(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE);
|
eventHandler->HandleMediaInputActionTriggered(
|
||||||
|
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -58,12 +62,13 @@ 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(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY);
|
eventHandler->HandleMediaInputActionTriggered(
|
||||||
|
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -72,12 +77,13 @@ 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(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART);
|
eventHandler->HandleMediaInputActionTriggered(
|
||||||
|
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -86,12 +92,13 @@ 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(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP);
|
eventHandler->HandleMediaInputActionTriggered(
|
||||||
|
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -100,12 +107,14 @@ 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(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT);
|
eventHandler->HandleMediaInputActionTriggered(
|
||||||
|
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaPreviousMultiHandler(void *param,
|
||||||
|
calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -114,7 +123,8 @@ void EventHandler::SourceMediaPreviousMultiHandler(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(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS);
|
eventHandler->HandleMediaInputActionTriggered(
|
||||||
|
source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,9 +140,10 @@ void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data
|
|||||||
* @api events
|
* @api events
|
||||||
* @category media inputs
|
* @category media inputs
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data)
|
void EventHandler::HandleMediaInputPlaybackStarted(void *param,
|
||||||
|
calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -143,7 +154,8 @@ void EventHandler::HandleMediaInputPlaybackStarted(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, "MediaInputPlaybackStarted", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::MediaInputs,
|
||||||
|
"MediaInputPlaybackStarted", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,7 +173,7 @@ void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
|
void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -172,7 +184,8 @@ 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, "MediaInputPlaybackEnded", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::MediaInputs,
|
||||||
|
"MediaInputPlaybackEnded", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -189,10 +202,12 @@ 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, ObsMediaInputAction action)
|
void EventHandler::HandleMediaInputActionTriggered(obs_source_t *source,
|
||||||
|
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, "MediaInputActionTriggered", eventData);
|
BroadcastEvent(EventSubscription::MediaInputs,
|
||||||
|
"MediaInputActionTriggered", eventData);
|
||||||
}
|
}
|
@ -19,18 +19,19 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
static bool GetOutputStateActive(ObsOutputState state) {
|
static bool GetOutputStateActive(ObsOutputState state)
|
||||||
switch(state) {
|
{
|
||||||
case OBS_WEBSOCKET_OUTPUT_STARTED:
|
switch (state) {
|
||||||
case OBS_WEBSOCKET_OUTPUT_RESUMED:
|
case OBS_WEBSOCKET_OUTPUT_STARTED:
|
||||||
return true;
|
case OBS_WEBSOCKET_OUTPUT_RESUMED:
|
||||||
case OBS_WEBSOCKET_OUTPUT_STARTING:
|
return true;
|
||||||
case OBS_WEBSOCKET_OUTPUT_STOPPING:
|
case OBS_WEBSOCKET_OUTPUT_STARTING:
|
||||||
case OBS_WEBSOCKET_OUTPUT_STOPPED:
|
case OBS_WEBSOCKET_OUTPUT_STOPPING:
|
||||||
case OBS_WEBSOCKET_OUTPUT_PAUSED:
|
case OBS_WEBSOCKET_OUTPUT_STOPPED:
|
||||||
return false;
|
case OBS_WEBSOCKET_OUTPUT_PAUSED:
|
||||||
default:
|
return false;
|
||||||
return false;
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,8 +53,10 @@ void EventHandler::HandleStreamStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
eventData["outputState"] =
|
||||||
BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged", eventData);
|
Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
|
BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,8 +77,10 @@ void EventHandler::HandleRecordStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
eventData["outputState"] =
|
||||||
BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged", eventData);
|
Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
|
BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,8 +101,10 @@ void EventHandler::HandleReplayBufferStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
eventData["outputState"] =
|
||||||
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged", eventData);
|
Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
|
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,8 +125,10 @@ void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
eventData["outputState"] =
|
||||||
BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged", eventData);
|
Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
|
BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,6 +147,8 @@ void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
|
|||||||
void EventHandler::HandleReplayBufferSaved()
|
void EventHandler::HandleReplayBufferSaved()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFilePath();
|
eventData["savedReplayPath"] =
|
||||||
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferSaved", eventData);
|
Utils::Obs::StringHelper::GetLastReplayBufferFilePath();
|
||||||
|
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferSaved",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
@ -37,22 +37,27 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
|
obs_sceneitem_t *sceneItem =
|
||||||
|
GetCalldataPointer<obs_sceneitem_t>(data, "item");
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
eventData["sceneName"] =
|
||||||
eventData["sourceName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
|
obs_source_get_name(obs_scene_get_source(scene));
|
||||||
|
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"] = obs_sceneitem_get_order_position(sceneItem);
|
eventData["sceneItemIndex"] =
|
||||||
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemCreated", eventData);
|
obs_sceneitem_get_order_position(sceneItem);
|
||||||
|
eventHandler->BroadcastEvent(EventSubscription::SceneItems,
|
||||||
|
"SceneItemCreated", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,21 +79,25 @@ void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
|
obs_sceneitem_t *sceneItem =
|
||||||
|
GetCalldataPointer<obs_sceneitem_t>(data, "item");
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
eventData["sceneName"] =
|
||||||
eventData["sourceName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
|
obs_source_get_name(obs_scene_get_source(scene));
|
||||||
|
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, "SceneItemRemoved", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::SceneItems,
|
||||||
|
"SceneItemRemoved", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,16 +116,19 @@ void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
eventData["sceneName"] =
|
||||||
eventData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList(scene, true);
|
obs_source_get_name(obs_scene_get_source(scene));
|
||||||
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemListReindexed", eventData);
|
eventData["sceneItems"] =
|
||||||
|
Utils::Obs::ArrayHelper::GetSceneItemList(scene, true);
|
||||||
|
eventHandler->BroadcastEvent(EventSubscription::SceneItems,
|
||||||
|
"SceneItemListReindexed", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -134,25 +146,29 @@ void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
|
|||||||
* @api events
|
* @api events
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemEnableStateChanged(void *param,
|
||||||
|
calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
|
obs_sceneitem_t *sceneItem =
|
||||||
|
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"] = obs_source_get_name(obs_scene_get_source(scene));
|
eventData["sceneName"] =
|
||||||
|
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, "SceneItemEnableStateChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::SceneItems,
|
||||||
|
"SceneItemEnableStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -170,25 +186,29 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
|
|||||||
* @api events
|
* @api events
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemLockStateChanged(void *param,
|
||||||
|
calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
|
obs_sceneitem_t *sceneItem =
|
||||||
|
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"] = obs_source_get_name(obs_scene_get_source(scene));
|
eventData["sceneName"] =
|
||||||
|
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, "SceneItemLockStateChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::SceneItems,
|
||||||
|
"SceneItemLockStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,20 +227,23 @@ void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
|
obs_sceneitem_t *sceneItem =
|
||||||
|
GetCalldataPointer<obs_sceneitem_t>(data, "item");
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
eventData["sceneName"] =
|
||||||
|
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, "SceneItemSelected", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::SceneItems,
|
||||||
|
"SceneItemSelected", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -238,9 +261,10 @@ void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
|
|||||||
* @api events
|
* @api events
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemTransformChanged(void *param,
|
||||||
|
calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
if (!eventHandler->_sceneItemTransformChangedRef.load())
|
if (!eventHandler->_sceneItemTransformChangedRef.load())
|
||||||
return;
|
return;
|
||||||
@ -249,13 +273,18 @@ void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data
|
|||||||
if (!scene)
|
if (!scene)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
|
obs_sceneitem_t *sceneItem =
|
||||||
|
GetCalldataPointer<obs_sceneitem_t>(data, "item");
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
eventData["sceneName"] =
|
||||||
|
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"] = Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem);
|
eventData["sceneItemTransform"] =
|
||||||
eventHandler->BroadcastEvent(EventSubscription::SceneItemTransformChanged, "SceneItemTransformChanged", eventData);
|
Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem);
|
||||||
|
eventHandler->BroadcastEvent(
|
||||||
|
EventSubscription::SceneItemTransformChanged,
|
||||||
|
"SceneItemTransformChanged", eventData);
|
||||||
}
|
}
|
||||||
|
@ -77,12 +77,15 @@ void EventHandler::HandleSceneRemoved(obs_source_t *source)
|
|||||||
* @api events
|
* @api events
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneNameChanged(obs_source_t *, std::string oldSceneName, std::string sceneName)
|
void EventHandler::HandleSceneNameChanged(obs_source_t *,
|
||||||
|
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", eventData);
|
BroadcastEvent(EventSubscription::Scenes, "SceneNameChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,7 +107,8 @@ 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", eventData);
|
BroadcastEvent(EventSubscription::Scenes, "CurrentProgramSceneChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,15 +126,17 @@ void EventHandler::HandleCurrentProgramSceneChanged()
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleCurrentPreviewSceneChanged()
|
void EventHandler::HandleCurrentPreviewSceneChanged()
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease currentPreviewScene = obs_frontend_get_current_preview_scene();
|
OBSSourceAutoRelease currentPreviewScene =
|
||||||
|
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)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneName"] = obs_source_get_name(currentPreviewScene);
|
eventData["sceneName"] = obs_source_get_name(currentPreviewScene);
|
||||||
BroadcastEvent(EventSubscription::Scenes, "CurrentPreviewSceneChanged", eventData);
|
BroadcastEvent(EventSubscription::Scenes, "CurrentPreviewSceneChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -152,5 +158,6 @@ void EventHandler::HandleSceneListChanged()
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["scenes"] = Utils::Obs::ArrayHelper::GetSceneList();
|
eventData["scenes"] = Utils::Obs::ArrayHelper::GetSceneList();
|
||||||
BroadcastEvent(EventSubscription::Scenes, "SceneListChanged", eventData);
|
BroadcastEvent(EventSubscription::Scenes, "SceneListChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,8 @@ void EventHandler::HandleCurrentSceneTransitionChanged()
|
|||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["transitionName"] = obs_source_get_name(transition);
|
eventData["transitionName"] = obs_source_get_name(transition);
|
||||||
BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionChanged", eventData);
|
BroadcastEvent(EventSubscription::Transitions,
|
||||||
|
"CurrentSceneTransitionChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,8 +58,10 @@ void EventHandler::HandleCurrentSceneTransitionChanged()
|
|||||||
void EventHandler::HandleCurrentSceneTransitionDurationChanged()
|
void EventHandler::HandleCurrentSceneTransitionDurationChanged()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["transitionDuration"] = obs_frontend_get_transition_duration();
|
eventData["transitionDuration"] =
|
||||||
BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionDurationChanged", eventData);
|
obs_frontend_get_transition_duration();
|
||||||
|
BroadcastEvent(EventSubscription::Transitions,
|
||||||
|
"CurrentSceneTransitionDurationChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,7 +79,7 @@ void EventHandler::HandleCurrentSceneTransitionDurationChanged()
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data)
|
void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -84,7 +87,8 @@ 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, "SceneTransitionStarted", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Transitions,
|
||||||
|
"SceneTransitionStarted", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,7 +108,7 @@ void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data)
|
void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -112,7 +116,8 @@ 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, "SceneTransitionEnded", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Transitions,
|
||||||
|
"SceneTransitionEnded", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,9 +138,10 @@ void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data)
|
|||||||
* @api events
|
* @api events
|
||||||
* @category transitions
|
* @category transitions
|
||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneTransitionVideoEnded(void *param, calldata_t *data)
|
void EventHandler::HandleSceneTransitionVideoEnded(void *param,
|
||||||
|
calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler *>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -143,5 +149,6 @@ void EventHandler::HandleSceneTransitionVideoEnded(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, "SceneTransitionVideoEnded", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Transitions,
|
||||||
|
"SceneTransitionVideoEnded", eventData);
|
||||||
}
|
}
|
||||||
|
@ -36,5 +36,6 @@ void EventHandler::HandleStudioModeStateChanged(bool enabled)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["studioModeEnabled"] = enabled;
|
eventData["studioModeEnabled"] = enabled;
|
||||||
BroadcastEvent(EventSubscription::Ui, "StudioModeStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Ui, "StudioModeStateChanged",
|
||||||
|
eventData);
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace EventSubscription {
|
namespace EventSubscription {
|
||||||
enum EventSubscription {
|
enum EventSubscription {
|
||||||
/**
|
/**
|
||||||
* Subcription value used to disable all events.
|
* Subcription value used to disable all events.
|
||||||
*
|
*
|
||||||
* @enumIdentifier None
|
* @enumIdentifier None
|
||||||
@ -31,8 +31,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
None = 0,
|
None = 0,
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive events in the `General` category.
|
* Subscription value to receive events in the `General` category.
|
||||||
*
|
*
|
||||||
* @enumIdentifier General
|
* @enumIdentifier General
|
||||||
@ -42,8 +42,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
General = (1 << 0),
|
General = (1 << 0),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive events in the `Config` category.
|
* Subscription value to receive events in the `Config` category.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Config
|
* @enumIdentifier Config
|
||||||
@ -53,8 +53,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Config = (1 << 1),
|
Config = (1 << 1),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive events in the `Scenes` category.
|
* Subscription value to receive events in the `Scenes` category.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Scenes
|
* @enumIdentifier Scenes
|
||||||
@ -64,8 +64,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Scenes = (1 << 2),
|
Scenes = (1 << 2),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive events in the `Inputs` category.
|
* Subscription value to receive events in the `Inputs` category.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Inputs
|
* @enumIdentifier Inputs
|
||||||
@ -75,8 +75,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Inputs = (1 << 3),
|
Inputs = (1 << 3),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive events in the `Transitions` category.
|
* Subscription value to receive events in the `Transitions` category.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Transitions
|
* @enumIdentifier Transitions
|
||||||
@ -86,8 +86,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Transitions = (1 << 4),
|
Transitions = (1 << 4),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive events in the `Filters` category.
|
* Subscription value to receive events in the `Filters` category.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Filters
|
* @enumIdentifier Filters
|
||||||
@ -97,8 +97,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Filters = (1 << 5),
|
Filters = (1 << 5),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive events in the `Outputs` category.
|
* Subscription value to receive events in the `Outputs` category.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Outputs
|
* @enumIdentifier Outputs
|
||||||
@ -108,8 +108,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Outputs = (1 << 6),
|
Outputs = (1 << 6),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive events in the `SceneItems` category.
|
* Subscription value to receive events in the `SceneItems` category.
|
||||||
*
|
*
|
||||||
* @enumIdentifier SceneItems
|
* @enumIdentifier SceneItems
|
||||||
@ -119,8 +119,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
SceneItems = (1 << 7),
|
SceneItems = (1 << 7),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive events in the `MediaInputs` category.
|
* Subscription value to receive events in the `MediaInputs` category.
|
||||||
*
|
*
|
||||||
* @enumIdentifier MediaInputs
|
* @enumIdentifier MediaInputs
|
||||||
@ -130,8 +130,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
MediaInputs = (1 << 8),
|
MediaInputs = (1 << 8),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive the `VendorEvent` event.
|
* Subscription value to receive the `VendorEvent` event.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Vendors
|
* @enumIdentifier Vendors
|
||||||
@ -141,8 +141,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Vendors = (1 << 9),
|
Vendors = (1 << 9),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive events in the `Ui` category.
|
* Subscription value to receive events in the `Ui` category.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Ui
|
* @enumIdentifier Ui
|
||||||
@ -152,8 +152,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Ui = (1 << 10),
|
Ui = (1 << 10),
|
||||||
/**
|
/**
|
||||||
* Helper to receive all non-high-volume events.
|
* Helper to receive all non-high-volume events.
|
||||||
*
|
*
|
||||||
* @enumIdentifier All
|
* @enumIdentifier All
|
||||||
@ -163,8 +163,9 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
All = (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Ui | Vendors),
|
All = (General | Config | Scenes | Inputs | Transitions | Filters |
|
||||||
/**
|
Outputs | SceneItems | MediaInputs | Ui | Vendors),
|
||||||
|
/**
|
||||||
* Subscription value to receive the `InputVolumeMeters` high-volume event.
|
* Subscription value to receive the `InputVolumeMeters` high-volume event.
|
||||||
*
|
*
|
||||||
* @enumIdentifier InputVolumeMeters
|
* @enumIdentifier InputVolumeMeters
|
||||||
@ -174,8 +175,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InputVolumeMeters = (1 << 16),
|
InputVolumeMeters = (1 << 16),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive the `InputActiveStateChanged` high-volume event.
|
* Subscription value to receive the `InputActiveStateChanged` high-volume event.
|
||||||
*
|
*
|
||||||
* @enumIdentifier InputActiveStateChanged
|
* @enumIdentifier InputActiveStateChanged
|
||||||
@ -185,8 +186,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InputActiveStateChanged = (1 << 17),
|
InputActiveStateChanged = (1 << 17),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive the `InputShowStateChanged` high-volume event.
|
* Subscription value to receive the `InputShowStateChanged` high-volume event.
|
||||||
*
|
*
|
||||||
* @enumIdentifier InputShowStateChanged
|
* @enumIdentifier InputShowStateChanged
|
||||||
@ -196,8 +197,8 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InputShowStateChanged = (1 << 18),
|
InputShowStateChanged = (1 << 18),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive the `SceneItemTransformChanged` high-volume event.
|
* Subscription value to receive the `SceneItemTransformChanged` high-volume event.
|
||||||
*
|
*
|
||||||
* @enumIdentifier SceneItemTransformChanged
|
* @enumIdentifier SceneItemTransformChanged
|
||||||
@ -207,6 +208,6 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
SceneItemTransformChanged = (1 << 19),
|
SceneItemTransformChanged = (1 << 19),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -28,18 +28,17 @@ 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),
|
: QDialog(parent, Qt::Dialog), ui(new Ui::ConnectInfo)
|
||||||
ui(new Ui::ConnectInfo)
|
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
connect(ui->copyServerIpButton, &QPushButton::clicked,
|
connect(ui->copyServerIpButton, &QPushButton::clicked, this,
|
||||||
this, &ConnectInfo::CopyServerIpButtonClicked);
|
&ConnectInfo::CopyServerIpButtonClicked);
|
||||||
connect(ui->copyServerPortButton, &QPushButton::clicked,
|
connect(ui->copyServerPortButton, &QPushButton::clicked, this,
|
||||||
this, &ConnectInfo::CopyServerPortButtonClicked);
|
&ConnectInfo::CopyServerPortButtonClicked);
|
||||||
connect(ui->copyServerPasswordButton, &QPushButton::clicked,
|
connect(ui->copyServerPasswordButton, &QPushButton::clicked, this,
|
||||||
this, &ConnectInfo::CopyServerPasswordButtonClicked);
|
&ConnectInfo::CopyServerPasswordButtonClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectInfo::~ConnectInfo()
|
ConnectInfo::~ConnectInfo()
|
||||||
@ -56,11 +55,13 @@ void ConnectInfo::RefreshData()
|
|||||||
{
|
{
|
||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
blog(LOG_ERROR, "[ConnectInfo::showEvent] Unable to retreive config!");
|
blog(LOG_ERROR,
|
||||||
|
"[ConnectInfo::showEvent] Unable to retreive config!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString serverIp = QString::fromStdString(Utils::Platform::GetLocalAddress());
|
QString serverIp =
|
||||||
|
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);
|
||||||
@ -72,15 +73,20 @@ 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("OBSWebSocket.ConnectInfo.ServerPasswordPlaceholderText");
|
serverPassword = obs_module_text(
|
||||||
|
"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").arg(serverIp).arg(serverPort).arg(serverPassword);
|
connectString = QString("obsws://%1:%2/%3")
|
||||||
|
.arg(serverIp)
|
||||||
|
.arg(serverPort)
|
||||||
|
.arg(serverPassword);
|
||||||
else
|
else
|
||||||
connectString = QString("obsws://%1:%2").arg(serverIp).arg(serverPort);
|
connectString =
|
||||||
|
QString("obsws://%1:%2").arg(serverIp).arg(serverPort);
|
||||||
DrawQr(connectString);
|
DrawQr(connectString);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,14 +119,15 @@ void ConnectInfo::DrawQr(QString qrText)
|
|||||||
QPixmap map(230, 230);
|
QPixmap map(230, 230);
|
||||||
map.fill(Qt::white);
|
map.fill(Qt::white);
|
||||||
QPainter painter(&map);
|
QPainter painter(&map);
|
||||||
|
|
||||||
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(QT_TO_UTF8(qrText), qrcodegen::QrCode::Ecc::MEDIUM);
|
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(
|
||||||
|
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();
|
||||||
const double aspect = w/h;
|
const double aspect = w / h;
|
||||||
const double size = ((aspect > 1.0) ? h : w);
|
const double size = ((aspect > 1.0) ? h : w);
|
||||||
const double scale = size / (s+2);
|
const double scale = size / (s + 2);
|
||||||
painter.setPen(Qt::NoPen);
|
painter.setPen(Qt::NoPen);
|
||||||
painter.setBrush(Qt::black);
|
painter.setBrush(Qt::black);
|
||||||
|
|
||||||
|
@ -25,12 +25,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "ui_ConnectInfo.h"
|
#include "ui_ConnectInfo.h"
|
||||||
|
|
||||||
class ConnectInfo : public QDialog
|
class ConnectInfo : public QDialog {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ConnectInfo(QWidget* parent = 0);
|
explicit ConnectInfo(QWidget *parent = 0);
|
||||||
~ConnectInfo();
|
~ConnectInfo();
|
||||||
void showEvent(QShowEvent *event);
|
void showEvent(QShowEvent *event);
|
||||||
void RefreshData();
|
void RefreshData();
|
||||||
|
@ -32,20 +32,23 @@ 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" : ":toolTip/images/help_light.svg";
|
QString iconFile = lightTheme ? ":toolTip/images/help.svg"
|
||||||
QString iconTemplate = "<html> <img src='%1' style=' vertical-align: bottom; ' /></html>";
|
: ":toolTip/images/help_light.svg";
|
||||||
|
QString iconTemplate =
|
||||||
|
"<html> <img src='%1' style=' vertical-align: bottom; ' /></html>";
|
||||||
return iconTemplate.arg(iconFile);
|
return iconTemplate.arg(iconFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::SettingsDialog(QWidget* parent) :
|
SettingsDialog::SettingsDialog(QWidget *parent)
|
||||||
QDialog(parent, Qt::Dialog),
|
: QDialog(parent, Qt::Dialog),
|
||||||
ui(new Ui::SettingsDialog),
|
ui(new Ui::SettingsDialog),
|
||||||
connectInfo(new ConnectInfo),
|
connectInfo(new ConnectInfo),
|
||||||
sessionTableTimer(new QTimer),
|
sessionTableTimer(new QTimer),
|
||||||
passwordManuallyEdited(false)
|
passwordManuallyEdited(false)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->websocketSessionTable->horizontalHeader()->resizeSection(3, 100); // Resize Session Table column widths
|
ui->websocketSessionTable->horizontalHeader()->resizeSection(
|
||||||
|
3, 100); // Resize Session Table column widths
|
||||||
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
|
||||||
@ -56,18 +59,18 @@ SettingsDialog::SettingsDialog(QWidget* parent) :
|
|||||||
ui->enableDebugLoggingToolTipLabel->setText(toolTipHtml);
|
ui->enableDebugLoggingToolTipLabel->setText(toolTipHtml);
|
||||||
ui->allowExternalToolTipLabel->setText(toolTipHtml);
|
ui->allowExternalToolTipLabel->setText(toolTipHtml);
|
||||||
|
|
||||||
connect(sessionTableTimer, &QTimer::timeout,
|
connect(sessionTableTimer, &QTimer::timeout, this,
|
||||||
this, &SettingsDialog::FillSessionTable);
|
&SettingsDialog::FillSessionTable);
|
||||||
connect(ui->buttonBox, &QDialogButtonBox::clicked,
|
connect(ui->buttonBox, &QDialogButtonBox::clicked, this,
|
||||||
this, &SettingsDialog::DialogButtonClicked);
|
&SettingsDialog::DialogButtonClicked);
|
||||||
connect(ui->enableAuthenticationCheckBox, &QCheckBox::stateChanged,
|
connect(ui->enableAuthenticationCheckBox, &QCheckBox::stateChanged,
|
||||||
this, &SettingsDialog::EnableAuthenticationCheckBoxChanged);
|
this, &SettingsDialog::EnableAuthenticationCheckBoxChanged);
|
||||||
connect(ui->generatePasswordButton, &QPushButton::clicked,
|
connect(ui->generatePasswordButton, &QPushButton::clicked, this,
|
||||||
this, &SettingsDialog::GeneratePasswordButtonClicked);
|
&SettingsDialog::GeneratePasswordButtonClicked);
|
||||||
connect(ui->showConnectInfoButton, &QPushButton::clicked,
|
connect(ui->showConnectInfoButton, &QPushButton::clicked, this,
|
||||||
this, &SettingsDialog::ShowConnectInfoButtonClicked);
|
&SettingsDialog::ShowConnectInfoButtonClicked);
|
||||||
connect(ui->serverPasswordLineEdit, &QLineEdit::textEdited,
|
connect(ui->serverPasswordLineEdit, &QLineEdit::textEdited, this,
|
||||||
this, &SettingsDialog::PasswordEdited);
|
&SettingsDialog::PasswordEdited);
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::~SettingsDialog()
|
SettingsDialog::~SettingsDialog()
|
||||||
@ -81,7 +84,8 @@ void SettingsDialog::showEvent(QShowEvent *)
|
|||||||
{
|
{
|
||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
blog(LOG_ERROR, "[SettingsDialog::showEvent] Unable to retreive config!");
|
blog(LOG_ERROR,
|
||||||
|
"[SettingsDialog::showEvent] Unable to retreive config!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +125,8 @@ void SettingsDialog::RefreshData()
|
|||||||
{
|
{
|
||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
blog(LOG_ERROR, "[SettingsDialog::RefreshData] Unable to retreive config!");
|
blog(LOG_ERROR,
|
||||||
|
"[SettingsDialog::RefreshData] Unable to retreive config!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +142,11 @@ void SettingsDialog::RefreshData()
|
|||||||
ui->serverPasswordLineEdit->setEnabled(conf->AuthRequired);
|
ui->serverPasswordLineEdit->setEnabled(conf->AuthRequired);
|
||||||
ui->generatePasswordButton->setEnabled(conf->AuthRequired);
|
ui->generatePasswordButton->setEnabled(conf->AuthRequired);
|
||||||
|
|
||||||
ui->showConnectInfoButton->setToolTip(ui->allowExternalCheckBox->isChecked() ? "" : obs_module_text("OBSWebSocket.Settings.ShowConnectInfoHoverText"));
|
ui->showConnectInfoButton->setToolTip(
|
||||||
|
ui->allowExternalCheckBox->isChecked()
|
||||||
|
? ""
|
||||||
|
: obs_module_text(
|
||||||
|
"OBSWebSocket.Settings.ShowConnectInfoHoverText"));
|
||||||
|
|
||||||
FillSessionTable();
|
FillSessionTable();
|
||||||
}
|
}
|
||||||
@ -155,43 +164,55 @@ void SettingsDialog::SaveFormData()
|
|||||||
{
|
{
|
||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
blog(LOG_ERROR, "[SettingsDialog::SaveFormData] Unable to retreive config!");
|
blog(LOG_ERROR,
|
||||||
|
"[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("OBSWebSocket.Settings.Save.PasswordInvalidErrorTitle"));
|
msgBox.setWindowTitle(obs_module_text(
|
||||||
msgBox.setText(obs_module_text("OBSWebSocket.Settings.Save.PasswordInvalidErrorMessage"));
|
"OBSWebSocket.Settings.Save.PasswordInvalidErrorTitle"));
|
||||||
|
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 && (conf->ServerPassword != ui->serverPasswordLineEdit->text())) {
|
if (passwordManuallyEdited &&
|
||||||
|
(conf->ServerPassword != ui->serverPasswordLineEdit->text())) {
|
||||||
QMessageBox msgBox;
|
QMessageBox msgBox;
|
||||||
msgBox.setWindowTitle(obs_module_text("OBSWebSocket.Settings.Save.UserPasswordWarningTitle"));
|
msgBox.setWindowTitle(obs_module_text(
|
||||||
msgBox.setText(obs_module_text("OBSWebSocket.Settings.Save.UserPasswordWarningMessage"));
|
"OBSWebSocket.Settings.Save.UserPasswordWarningTitle"));
|
||||||
msgBox.setInformativeText(obs_module_text("OBSWebSocket.Settings.Save.UserPasswordWarningInfoText"));
|
msgBox.setText(obs_module_text(
|
||||||
|
"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();
|
||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case QMessageBox::Yes:
|
case QMessageBox::Yes:
|
||||||
break;
|
break;
|
||||||
case QMessageBox::No:
|
case QMessageBox::No:
|
||||||
default:
|
default:
|
||||||
ui->serverPasswordLineEdit->setText(conf->ServerPassword);
|
ui->serverPasswordLineEdit->setText(
|
||||||
return;
|
conf->ServerPassword);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needsRestart = (conf->ServerEnabled != ui->enableWebSocketServerCheckBox->isChecked()) ||
|
bool needsRestart =
|
||||||
(ui->enableAuthenticationCheckBox->isChecked() && conf->ServerPassword != ui->serverPasswordLineEdit->text()) ||
|
(conf->ServerEnabled !=
|
||||||
(conf->BindLoopback == ui->allowExternalCheckBox->isChecked()) ||
|
ui->enableWebSocketServerCheckBox->isChecked()) ||
|
||||||
(conf->ServerPort != ui->serverPortSpinBox->value());
|
(ui->enableAuthenticationCheckBox->isChecked() &&
|
||||||
|
conf->ServerPassword != ui->serverPasswordLineEdit->text()) ||
|
||||||
|
(conf->BindLoopback ==
|
||||||
|
ui->allowExternalCheckBox->isChecked()) ||
|
||||||
|
(conf->ServerPort != ui->serverPortSpinBox->value());
|
||||||
|
|
||||||
conf->ServerEnabled = ui->enableWebSocketServerCheckBox->isChecked();
|
conf->ServerEnabled = ui->enableWebSocketServerCheckBox->isChecked();
|
||||||
conf->AlertsEnabled = ui->enableSystemTrayAlertsCheckBox->isChecked();
|
conf->AlertsEnabled = ui->enableSystemTrayAlertsCheckBox->isChecked();
|
||||||
@ -207,7 +228,8 @@ void SettingsDialog::SaveFormData()
|
|||||||
connectInfo->RefreshData();
|
connectInfo->RefreshData();
|
||||||
|
|
||||||
if (needsRestart) {
|
if (needsRestart) {
|
||||||
blog(LOG_INFO, "[SettingsDialog::SaveFormData] A setting was changed which requires a server restart.");
|
blog(LOG_INFO,
|
||||||
|
"[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) {
|
||||||
@ -220,7 +242,8 @@ void SettingsDialog::FillSessionTable()
|
|||||||
{
|
{
|
||||||
auto webSocketServer = GetWebSocketServer();
|
auto webSocketServer = GetWebSocketServer();
|
||||||
if (!webSocketServer) {
|
if (!webSocketServer) {
|
||||||
blog(LOG_ERROR, "[SettingsDialog::FillSessionTable] Unable to fetch websocket server instance!");
|
blog(LOG_ERROR,
|
||||||
|
"[SettingsDialog::FillSessionTable] Unable to fetch websocket server instance!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,19 +257,26 @@ 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 = obs_module_text("OBSWebSocket.SessionTable.KickButtonText");
|
QString 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(QString::fromStdString(session.remoteAddress));
|
QTableWidgetItem *addressItem = new QTableWidgetItem(
|
||||||
|
QString::fromStdString(session.remoteAddress));
|
||||||
ui->websocketSessionTable->setItem(i, 0, addressItem);
|
ui->websocketSessionTable->setItem(i, 0, addressItem);
|
||||||
|
|
||||||
uint64_t sessionDuration = QDateTime::currentSecsSinceEpoch() - session.connectedAt;
|
uint64_t sessionDuration = QDateTime::currentSecsSinceEpoch() -
|
||||||
QTableWidgetItem *durationItem = new QTableWidgetItem(QTime(0, 0, sessionDuration).toString("hh:mm:ss"));
|
session.connectedAt;
|
||||||
|
QTableWidgetItem *durationItem = new QTableWidgetItem(
|
||||||
|
QTime(0, 0, sessionDuration).toString("hh:mm:ss"));
|
||||||
ui->websocketSessionTable->setItem(i, 1, durationItem);
|
ui->websocketSessionTable->setItem(i, 1, durationItem);
|
||||||
|
|
||||||
QTableWidgetItem *statsItem = new QTableWidgetItem(QString("%1/%2").arg(session.incomingMessages).arg(session.outgoingMessages));
|
QTableWidgetItem *statsItem = new QTableWidgetItem(
|
||||||
|
QString("%1/%2")
|
||||||
|
.arg(session.incomingMessages)
|
||||||
|
.arg(session.outgoingMessages));
|
||||||
ui->websocketSessionTable->setItem(i, 2, statsItem);
|
ui->websocketSessionTable->setItem(i, 2, statsItem);
|
||||||
|
|
||||||
QLabel *identifiedLabel = new QLabel();
|
QLabel *identifiedLabel = new QLabel();
|
||||||
@ -258,14 +288,17 @@ void SettingsDialog::FillSessionTable()
|
|||||||
}
|
}
|
||||||
ui->websocketSessionTable->setCellWidget(i, 3, identifiedLabel);
|
ui->websocketSessionTable->setCellWidget(i, 3, identifiedLabel);
|
||||||
|
|
||||||
QPushButton *invalidateButton = new QPushButton(kickButtonText, this);
|
QPushButton *invalidateButton =
|
||||||
|
new QPushButton(kickButtonText, this);
|
||||||
QWidget *invalidateButtonWidget = new QWidget();
|
QWidget *invalidateButtonWidget = new QWidget();
|
||||||
QHBoxLayout *invalidateButtonLayout = new QHBoxLayout(invalidateButtonWidget);
|
QHBoxLayout *invalidateButtonLayout =
|
||||||
|
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(i, 4, invalidateButtonWidget);
|
ui->websocketSessionTable->setCellWidget(
|
||||||
|
i, 4, invalidateButtonWidget);
|
||||||
connect(invalidateButton, &QPushButton::clicked, [=]() {
|
connect(invalidateButton, &QPushButton::clicked, [=]() {
|
||||||
webSocketServer->InvalidateSession(session.hdl);
|
webSocketServer->InvalidateSession(session.hdl);
|
||||||
});
|
});
|
||||||
@ -287,7 +320,8 @@ void SettingsDialog::EnableAuthenticationCheckBoxChanged()
|
|||||||
|
|
||||||
void SettingsDialog::GeneratePasswordButtonClicked()
|
void SettingsDialog::GeneratePasswordButtonClicked()
|
||||||
{
|
{
|
||||||
QString newPassword = QString::fromStdString(Utils::Crypto::GeneratePassword());
|
QString newPassword =
|
||||||
|
QString::fromStdString(Utils::Crypto::GeneratePassword());
|
||||||
ui->serverPasswordLineEdit->setText(newPassword);
|
ui->serverPasswordLineEdit->setText(newPassword);
|
||||||
ui->serverPasswordLineEdit->selectAll();
|
ui->serverPasswordLineEdit->selectAll();
|
||||||
passwordManuallyEdited = false;
|
passwordManuallyEdited = false;
|
||||||
@ -297,19 +331,22 @@ void SettingsDialog::ShowConnectInfoButtonClicked()
|
|||||||
{
|
{
|
||||||
if (obs_video_active()) {
|
if (obs_video_active()) {
|
||||||
QMessageBox msgBox;
|
QMessageBox msgBox;
|
||||||
msgBox.setWindowTitle(obs_module_text("OBSWebSocket.Settings.ShowConnectInfoWarningTitle"));
|
msgBox.setWindowTitle(obs_module_text(
|
||||||
msgBox.setText(obs_module_text("OBSWebSocket.Settings.ShowConnectInfoWarningMessage"));
|
"OBSWebSocket.Settings.ShowConnectInfoWarningTitle"));
|
||||||
msgBox.setInformativeText(obs_module_text("OBSWebSocket.Settings.ShowConnectInfoWarningInfoText"));
|
msgBox.setText(obs_module_text(
|
||||||
|
"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();
|
||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case QMessageBox::Yes:
|
case QMessageBox::Yes:
|
||||||
break;
|
break;
|
||||||
case QMessageBox::No:
|
case QMessageBox::No:
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,12 +27,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "ui_SettingsDialog.h"
|
#include "ui_SettingsDialog.h"
|
||||||
|
|
||||||
class SettingsDialog : public QDialog
|
class SettingsDialog : public QDialog {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SettingsDialog(QWidget* parent = 0);
|
explicit SettingsDialog(QWidget *parent = 0);
|
||||||
~SettingsDialog();
|
~SettingsDialog();
|
||||||
void showEvent(QShowEvent *event);
|
void showEvent(QShowEvent *event);
|
||||||
void hideEvent(QHideEvent *event);
|
void hideEvent(QHideEvent *event);
|
||||||
|
@ -32,23 +32,35 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
OBS_DECLARE_MODULE()
|
OBS_DECLARE_MODULE()
|
||||||
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
||||||
OBS_MODULE_AUTHOR("OBSProject")
|
OBS_MODULE_AUTHOR("OBSProject")
|
||||||
const char *obs_module_name(void) { return "obs-websocket"; }
|
const char *obs_module_name(void)
|
||||||
const char *obs_module_description(void) { return obs_module_text("OBSWebSocket.Plugin.Description"); }
|
{
|
||||||
|
return "obs-websocket";
|
||||||
|
}
|
||||||
|
const char *obs_module_description(void)
|
||||||
|
{
|
||||||
|
return obs_module_text("OBSWebSocket.Plugin.Description");
|
||||||
|
}
|
||||||
|
|
||||||
os_cpu_usage_info_t* _cpuUsageInfo;
|
os_cpu_usage_info_t *_cpuUsageInfo;
|
||||||
ConfigPtr _config;
|
ConfigPtr _config;
|
||||||
EventHandlerPtr _eventHandler;
|
EventHandlerPtr _eventHandler;
|
||||||
WebSocketApiPtr _webSocketApi;
|
WebSocketApiPtr _webSocketApi;
|
||||||
WebSocketServerPtr _webSocketServer;
|
WebSocketServerPtr _webSocketServer;
|
||||||
SettingsDialog *_settingsDialog = nullptr;
|
SettingsDialog *_settingsDialog = nullptr;
|
||||||
|
|
||||||
void WebSocketApiEventCallback(std::string vendorName, std::string eventType, obs_data_t *obsEventData);
|
void WebSocketApiEventCallback(std::string vendorName, std::string eventType,
|
||||||
|
obs_data_t *obsEventData);
|
||||||
|
|
||||||
bool obs_module_load(void)
|
bool obs_module_load(void)
|
||||||
{
|
{
|
||||||
blog(LOG_INFO, "[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_VERSION, OBS_WEBSOCKET_RPC_VERSION);
|
blog(LOG_INFO,
|
||||||
blog(LOG_INFO, "[obs_module_load] Qt version (compile-time): %s | Qt version (run-time): %s", QT_VERSION_STR, qVersion());
|
"[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)",
|
||||||
blog(LOG_INFO, "[obs_module_load] Linked ASIO Version: %d", ASIO_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, "[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();
|
||||||
@ -69,14 +81,18 @@ bool obs_module_load(void)
|
|||||||
|
|
||||||
// Initialize the settings dialog
|
// Initialize the settings dialog
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window());
|
QMainWindow *mainWindow =
|
||||||
|
static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
||||||
_settingsDialog = new SettingsDialog(mainWindow);
|
_settingsDialog = new SettingsDialog(mainWindow);
|
||||||
obs_frontend_pop_ui_translation();
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
// Add the settings dialog to the tools menu
|
// Add the settings dialog to the tools menu
|
||||||
const char* menuActionText = obs_module_text("OBSWebSocket.Settings.DialogTitle");
|
const char *menuActionText =
|
||||||
QAction* menuAction = (QAction*)obs_frontend_add_tools_menu_qaction(menuActionText);
|
obs_module_text("OBSWebSocket.Settings.DialogTitle");
|
||||||
QObject::connect(menuAction, &QAction::triggered, [] { _settingsDialog->ToggleShowHide(); });
|
QAction *menuAction =
|
||||||
|
(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;
|
||||||
@ -88,7 +104,8 @@ 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("[obs_module_unload] WebSocket server is running. Stopping...");
|
blog_debug(
|
||||||
|
"[obs_module_unload] WebSocket server is running. Stopping...");
|
||||||
_webSocketServer->Stop();
|
_webSocketServer->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +128,7 @@ void obs_module_unload()
|
|||||||
blog(LOG_INFO, "[obs_module_unload] Finished shutting down.");
|
blog(LOG_INFO, "[obs_module_unload] Finished shutting down.");
|
||||||
}
|
}
|
||||||
|
|
||||||
os_cpu_usage_info_t* GetCpuUsageInfo()
|
os_cpu_usage_info_t *GetCpuUsageInfo()
|
||||||
{
|
{
|
||||||
return _cpuUsageInfo;
|
return _cpuUsageInfo;
|
||||||
}
|
}
|
||||||
@ -159,7 +176,8 @@ bool IsDebugEnabled()
|
|||||||
* @api events
|
* @api events
|
||||||
* @category general
|
* @category general
|
||||||
*/
|
*/
|
||||||
void WebSocketApiEventCallback(std::string vendorName, std::string eventType, obs_data_t *obsEventData)
|
void WebSocketApiEventCallback(std::string vendorName, std::string eventType,
|
||||||
|
obs_data_t *obsEventData)
|
||||||
{
|
{
|
||||||
json eventData = Utils::Json::ObsDataToJson(obsEventData);
|
json eventData = Utils::Json::ObsDataToJson(obsEventData);
|
||||||
|
|
||||||
@ -168,16 +186,19 @@ void WebSocketApiEventCallback(std::string vendorName, std::string eventType, ob
|
|||||||
broadcastEventData["eventType"] = eventType;
|
broadcastEventData["eventType"] = eventType;
|
||||||
broadcastEventData["eventData"] = eventData;
|
broadcastEventData["eventData"] = eventData;
|
||||||
|
|
||||||
_webSocketServer->BroadcastEvent(EventSubscription::Vendors, "VendorEvent", broadcastEventData);
|
_webSocketServer->BroadcastEvent(EventSubscription::Vendors,
|
||||||
|
"VendorEvent", broadcastEventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PLUGIN_TESTS
|
#ifdef PLUGIN_TESTS
|
||||||
|
|
||||||
static void test_vendor_request_cb(obs_data_t *requestData, obs_data_t *responseData, void *priv_data)
|
static void test_vendor_request_cb(obs_data_t *requestData,
|
||||||
|
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", obs_data_get_json(requestData));
|
blog(LOG_INFO, "[test_vendor_request_cb] Request data: %s",
|
||||||
|
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");
|
||||||
@ -192,25 +213,34 @@ 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, "[obs_module_post_load] obs-websocket plugin API version: %u", apiVersion);
|
blog(LOG_INFO,
|
||||||
|
"[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 = obs_websocket_call_request("GetVersion");
|
struct obs_websocket_request_response *response =
|
||||||
|
obs_websocket_call_request("GetVersion");
|
||||||
if (response) {
|
if (response) {
|
||||||
blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s", response->status_code, response->comment, response->response_data);
|
blog(LOG_INFO,
|
||||||
|
"[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
|
||||||
|
response->status_code, response->comment,
|
||||||
|
response->response_data);
|
||||||
obs_websocket_request_response_free(response);
|
obs_websocket_request_response_free(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, "[obs_module_post_load] Failed to create vendor!");
|
blog(LOG_WARNING,
|
||||||
|
"[obs_module_post_load] Failed to create vendor!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test vendor request registration
|
// Test vendor request registration
|
||||||
if (!obs_websocket_vendor_register_request(vendor, "TestRequest", test_vendor_request_cb, vendor)) {
|
if (!obs_websocket_vendor_register_request(
|
||||||
blog(LOG_WARNING, "[obs_module_post_load] Failed to register vendor request!");
|
vendor, "TestRequest", test_vendor_request_cb, vendor)) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[obs_module_post_load] Failed to register vendor request!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ typedef std::shared_ptr<WebSocketApi> WebSocketApiPtr;
|
|||||||
class WebSocketServer;
|
class WebSocketServer;
|
||||||
typedef std::shared_ptr<WebSocketServer> WebSocketServerPtr;
|
typedef std::shared_ptr<WebSocketServer> WebSocketServerPtr;
|
||||||
|
|
||||||
os_cpu_usage_info_t* GetCpuUsageInfo();
|
os_cpu_usage_info_t *GetCpuUsageInfo();
|
||||||
|
|
||||||
ConfigPtr GetConfig();
|
ConfigPtr GetConfig();
|
||||||
|
|
||||||
|
@ -24,8 +24,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../utils/Compat.h"
|
#include "../utils/Compat.h"
|
||||||
#include "../obs-websocket.h"
|
#include "../obs-websocket.h"
|
||||||
|
|
||||||
struct SerialFrameBatch
|
struct SerialFrameBatch {
|
||||||
{
|
|
||||||
RequestHandler &requestHandler;
|
RequestHandler &requestHandler;
|
||||||
std::queue<RequestBatchRequest> requests;
|
std::queue<RequestBatchRequest> requests;
|
||||||
std::vector<RequestResult> results;
|
std::vector<RequestResult> results;
|
||||||
@ -37,43 +36,51 @@ struct SerialFrameBatch
|
|||||||
std::mutex conditionMutex;
|
std::mutex conditionMutex;
|
||||||
std::condition_variable condition;
|
std::condition_variable condition;
|
||||||
|
|
||||||
SerialFrameBatch(RequestHandler &requestHandler, json &variables, bool haltOnFailure) :
|
SerialFrameBatch(RequestHandler &requestHandler, json &variables,
|
||||||
requestHandler(requestHandler),
|
bool haltOnFailure)
|
||||||
variables(variables),
|
: requestHandler(requestHandler),
|
||||||
haltOnFailure(haltOnFailure),
|
variables(variables),
|
||||||
frameCount(0),
|
haltOnFailure(haltOnFailure),
|
||||||
sleepUntilFrame(0)
|
frameCount(0),
|
||||||
{}
|
sleepUntilFrame(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParallelBatchResults
|
struct ParallelBatchResults {
|
||||||
{
|
|
||||||
RequestHandler &requestHandler;
|
RequestHandler &requestHandler;
|
||||||
std::vector<RequestResult> results;
|
std::vector<RequestResult> results;
|
||||||
|
|
||||||
std::mutex conditionMutex;
|
std::mutex conditionMutex;
|
||||||
std::condition_variable condition;
|
std::condition_variable condition;
|
||||||
|
|
||||||
ParallelBatchResults(RequestHandler &requestHandler) :
|
ParallelBatchResults(RequestHandler &requestHandler)
|
||||||
requestHandler(requestHandler)
|
: requestHandler(requestHandler)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// `{"inputName": "inputNameVariable"}` is essentially `inputName = inputNameVariable`
|
// `{"inputName": "inputNameVariable"}` is essentially `inputName = inputNameVariable`
|
||||||
static void PreProcessVariables(const json &variables, RequestBatchRequest &request)
|
static void PreProcessVariables(const json &variables,
|
||||||
|
RequestBatchRequest &request)
|
||||||
{
|
{
|
||||||
if (variables.empty() || !request.InputVariables.is_object() || request.InputVariables.empty() || !request.RequestData.is_object())
|
if (variables.empty() || !request.InputVariables.is_object() ||
|
||||||
|
request.InputVariables.empty() || !request.RequestData.is_object())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto& [key, value] : request.InputVariables.items()) {
|
for (auto &[key, value] : request.InputVariables.items()) {
|
||||||
if (!value.is_string()) {
|
if (!value.is_string()) {
|
||||||
blog_debug("[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `inputVariables `is not a string. Skipping!", key.c_str());
|
blog_debug(
|
||||||
|
"[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `inputVariables `is not a string. Skipping!",
|
||||||
|
key.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string valueString = value;
|
std::string valueString = value;
|
||||||
if (!variables.contains(valueString)) {
|
if (!variables.contains(valueString)) {
|
||||||
blog_debug("[WebSocketServer::ProcessRequestBatch] `inputVariables` requested variable `%s`, but it does not exist. Skipping!", valueString.c_str());
|
blog_debug(
|
||||||
|
"[WebSocketServer::ProcessRequestBatch] `inputVariables` requested variable `%s`, but it does not exist. Skipping!",
|
||||||
|
valueString.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,20 +91,28 @@ static void PreProcessVariables(const json &variables, RequestBatchRequest &requ
|
|||||||
}
|
}
|
||||||
|
|
||||||
// `{"sceneItemIdVariable": "sceneItemId"}` is essentially `sceneItemIdVariable = sceneItemId`
|
// `{"sceneItemIdVariable": "sceneItemId"}` is essentially `sceneItemIdVariable = sceneItemId`
|
||||||
static void PostProcessVariables(json &variables, const RequestBatchRequest &request, const RequestResult &requestResult)
|
static void PostProcessVariables(json &variables,
|
||||||
|
const RequestBatchRequest &request,
|
||||||
|
const RequestResult &requestResult)
|
||||||
{
|
{
|
||||||
if (!request.OutputVariables.is_object() || request.OutputVariables.empty() || requestResult.ResponseData.empty())
|
if (!request.OutputVariables.is_object() ||
|
||||||
|
request.OutputVariables.empty() ||
|
||||||
|
requestResult.ResponseData.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto& [key, value] : request.OutputVariables.items()) {
|
for (auto &[key, value] : request.OutputVariables.items()) {
|
||||||
if (!value.is_string()) {
|
if (!value.is_string()) {
|
||||||
blog_debug("[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `outputVariables` is not a string. Skipping!", key.c_str());
|
blog_debug(
|
||||||
|
"[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `outputVariables` is not a string. Skipping!",
|
||||||
|
key.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string valueString = value;
|
std::string valueString = value;
|
||||||
if (!requestResult.ResponseData.contains(valueString)) {
|
if (!requestResult.ResponseData.contains(valueString)) {
|
||||||
blog_debug("[WebSocketServer::ProcessRequestBatch] `outputVariables` requested responseData field `%s`, but it does not exist. Skipping!", valueString.c_str());
|
blog_debug(
|
||||||
|
"[WebSocketServer::ProcessRequestBatch] `outputVariables` requested responseData field `%s`, but it does not exist. Skipping!",
|
||||||
|
valueString.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,13 +124,14 @@ static void ObsTickCallback(void *param, float)
|
|||||||
{
|
{
|
||||||
ScopeProfiler prof{"obs_websocket_request_batch_frame_tick"};
|
ScopeProfiler prof{"obs_websocket_request_batch_frame_tick"};
|
||||||
|
|
||||||
auto serialFrameBatch = static_cast<SerialFrameBatch*>(param);
|
auto serialFrameBatch = static_cast<SerialFrameBatch *>(param);
|
||||||
|
|
||||||
// Increment frame count
|
// Increment frame count
|
||||||
serialFrameBatch->frameCount++;
|
serialFrameBatch->frameCount++;
|
||||||
|
|
||||||
if (serialFrameBatch->sleepUntilFrame) {
|
if (serialFrameBatch->sleepUntilFrame) {
|
||||||
if (serialFrameBatch->frameCount < serialFrameBatch->sleepUntilFrame)
|
if (serialFrameBatch->frameCount <
|
||||||
|
serialFrameBatch->sleepUntilFrame)
|
||||||
// Do not process any requests if in "sleep mode"
|
// Do not process any requests if in "sleep mode"
|
||||||
return;
|
return;
|
||||||
else
|
else
|
||||||
@ -126,27 +142,35 @@ 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 = serialFrameBatch->requests.front();
|
RequestBatchRequest request =
|
||||||
|
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 = serialFrameBatch->requestHandler.ProcessRequest(request);
|
RequestResult requestResult =
|
||||||
|
serialFrameBatch->requestHandler.ProcessRequest(
|
||||||
|
request);
|
||||||
// Post-process batch variables
|
// Post-process batch variables
|
||||||
PostProcessVariables(serialFrameBatch->variables, request, requestResult);
|
PostProcessVariables(serialFrameBatch->variables, request,
|
||||||
|
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 && requestResult.StatusCode != RequestStatus::Success) {
|
if (serialFrameBatch->haltOnFailure &&
|
||||||
serialFrameBatch->requests = std::queue<RequestBatchRequest>();
|
requestResult.StatusCode != RequestStatus::Success) {
|
||||||
|
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->frameCount + requestResult.SleepFrames;
|
serialFrameBatch->sleepUntilFrame =
|
||||||
|
serialFrameBatch->frameCount +
|
||||||
|
requestResult.SleepFrames;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,7 +180,11 @@ static void ObsTickCallback(void *param, float)
|
|||||||
serialFrameBatch->condition.notify_one();
|
serialFrameBatch->condition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session, RequestBatchExecutionType::RequestBatchExecutionType executionType, std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure)
|
std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(
|
||||||
|
QThreadPool &threadPool, SessionPtr session,
|
||||||
|
RequestBatchExecutionType::RequestBatchExecutionType executionType,
|
||||||
|
std::vector<RequestBatchRequest> &requests, json &variables,
|
||||||
|
bool haltOnFailure)
|
||||||
{
|
{
|
||||||
RequestHandler requestHandler(session);
|
RequestHandler requestHandler(session);
|
||||||
if (executionType == RequestBatchExecutionType::SerialRealtime) {
|
if (executionType == RequestBatchExecutionType::SerialRealtime) {
|
||||||
@ -166,19 +194,22 @@ std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(QThreadPool
|
|||||||
for (auto &request : requests) {
|
for (auto &request : requests) {
|
||||||
PreProcessVariables(variables, request);
|
PreProcessVariables(variables, request);
|
||||||
|
|
||||||
RequestResult requestResult = requestHandler.ProcessRequest(request);
|
RequestResult requestResult =
|
||||||
|
requestHandler.ProcessRequest(request);
|
||||||
|
|
||||||
PostProcessVariables(variables, request, requestResult);
|
PostProcessVariables(variables, request, requestResult);
|
||||||
|
|
||||||
ret.push_back(requestResult);
|
ret.push_back(requestResult);
|
||||||
|
|
||||||
if (haltOnFailure && requestResult.StatusCode != RequestStatus::Success)
|
if (haltOnFailure &&
|
||||||
|
requestResult.StatusCode != RequestStatus::Success)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
} else if (executionType == RequestBatchExecutionType::SerialFrame) {
|
} else if (executionType == RequestBatchExecutionType::SerialFrame) {
|
||||||
SerialFrameBatch serialFrameBatch(requestHandler, variables, haltOnFailure);
|
SerialFrameBatch serialFrameBatch(requestHandler, variables,
|
||||||
|
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)
|
||||||
@ -188,8 +219,11 @@ std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(QThreadPool
|
|||||||
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(serialFrameBatch.conditionMutex);
|
std::unique_lock<std::mutex> lock(
|
||||||
serialFrameBatch.condition.wait(lock, [&serialFrameBatch]{return serialFrameBatch.requests.empty();});
|
serialFrameBatch.conditionMutex);
|
||||||
|
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);
|
||||||
@ -199,23 +233,33 @@ std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(QThreadPool
|
|||||||
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(parallelResults.conditionMutex);
|
std::unique_lock<std::mutex> lock(
|
||||||
|
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([¶llelResults, &request]() {
|
threadPool.start(Utils::Compat::CreateFunctionRunnable(
|
||||||
RequestResult requestResult = parallelResults.requestHandler.ProcessRequest(request);
|
[¶llelResults, &request]() {
|
||||||
|
RequestResult requestResult =
|
||||||
|
parallelResults.requestHandler
|
||||||
|
.ProcessRequest(
|
||||||
|
request);
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(parallelResults.conditionMutex);
|
std::unique_lock<std::mutex> lock(
|
||||||
parallelResults.results.push_back(requestResult);
|
parallelResults.conditionMutex);
|
||||||
lock.unlock();
|
parallelResults.results.push_back(
|
||||||
parallelResults.condition.notify_one();
|
requestResult);
|
||||||
}));
|
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, [¶llelResults, requestCount]{return parallelResults.results.size() == requestCount;});
|
parallelResults.condition.wait(lock, [¶llelResults,
|
||||||
|
requestCount] {
|
||||||
|
return parallelResults.results.size() == requestCount;
|
||||||
|
});
|
||||||
|
|
||||||
return parallelResults.results;
|
return parallelResults.results;
|
||||||
}
|
}
|
||||||
|
@ -24,5 +24,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "rpc/RequestBatchRequest.h"
|
#include "rpc/RequestBatchRequest.h"
|
||||||
|
|
||||||
namespace RequestBatchHandler {
|
namespace RequestBatchHandler {
|
||||||
std::vector<RequestResult> ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session, RequestBatchExecutionType::RequestBatchExecutionType executionType, std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure);
|
std::vector<RequestResult> ProcessRequestBatch(
|
||||||
|
QThreadPool &threadPool, SessionPtr session,
|
||||||
|
RequestBatchExecutionType::RequestBatchExecutionType executionType,
|
||||||
|
std::vector<RequestBatchRequest> &requests, json &variables,
|
||||||
|
bool haltOnFailure);
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
|
|
||||||
const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_handlerMap{
|
||||||
{
|
|
||||||
// General
|
// General
|
||||||
{"GetVersion", &RequestHandler::GetVersion},
|
{"GetVersion", &RequestHandler::GetVersion},
|
||||||
{"GetStats", &RequestHandler::GetStats},
|
{"GetStats", &RequestHandler::GetStats},
|
||||||
@ -32,14 +31,16 @@ 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", &RequestHandler::TriggerHotkeyByKeySequence},
|
{"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", &RequestHandler::SetCurrentSceneCollection},
|
{"SetCurrentSceneCollection",
|
||||||
|
&RequestHandler::SetCurrentSceneCollection},
|
||||||
{"CreateSceneCollection", &RequestHandler::CreateSceneCollection},
|
{"CreateSceneCollection", &RequestHandler::CreateSceneCollection},
|
||||||
{"GetProfileList", &RequestHandler::GetProfileList},
|
{"GetProfileList", &RequestHandler::GetProfileList},
|
||||||
{"SetCurrentProfile", &RequestHandler::SetCurrentProfile},
|
{"SetCurrentProfile", &RequestHandler::SetCurrentProfile},
|
||||||
@ -70,8 +71,10 @@ 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", &RequestHandler::GetSceneSceneTransitionOverride},
|
{"GetSceneSceneTransitionOverride",
|
||||||
{"SetSceneSceneTransitionOverride", &RequestHandler::SetSceneSceneTransitionOverride},
|
&RequestHandler::GetSceneSceneTransitionOverride},
|
||||||
|
{"SetSceneSceneTransitionOverride",
|
||||||
|
&RequestHandler::SetSceneSceneTransitionOverride},
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
{"GetInputList", &RequestHandler::GetInputList},
|
{"GetInputList", &RequestHandler::GetInputList},
|
||||||
@ -96,23 +99,32 @@ 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", &RequestHandler::GetInputPropertiesListPropertyItems},
|
{"GetInputPropertiesListPropertyItems",
|
||||||
{"PressInputPropertiesButton", &RequestHandler::PressInputPropertiesButton},
|
&RequestHandler::GetInputPropertiesListPropertyItems},
|
||||||
|
{"PressInputPropertiesButton",
|
||||||
|
&RequestHandler::PressInputPropertiesButton},
|
||||||
|
|
||||||
// Transitions
|
// Transitions
|
||||||
{"GetTransitionKindList", &RequestHandler::GetTransitionKindList},
|
{"GetTransitionKindList", &RequestHandler::GetTransitionKindList},
|
||||||
{"GetSceneTransitionList", &RequestHandler::GetSceneTransitionList},
|
{"GetSceneTransitionList", &RequestHandler::GetSceneTransitionList},
|
||||||
{"GetCurrentSceneTransition", &RequestHandler::GetCurrentSceneTransition},
|
{"GetCurrentSceneTransition",
|
||||||
{"SetCurrentSceneTransition", &RequestHandler::SetCurrentSceneTransition},
|
&RequestHandler::GetCurrentSceneTransition},
|
||||||
{"SetCurrentSceneTransitionDuration", &RequestHandler::SetCurrentSceneTransitionDuration},
|
{"SetCurrentSceneTransition",
|
||||||
{"SetCurrentSceneTransitionSettings", &RequestHandler::SetCurrentSceneTransitionSettings},
|
&RequestHandler::SetCurrentSceneTransition},
|
||||||
{"GetCurrentSceneTransitionCursor", &RequestHandler::GetCurrentSceneTransitionCursor},
|
{"SetCurrentSceneTransitionDuration",
|
||||||
{"TriggerStudioModeTransition", &RequestHandler::TriggerStudioModeTransition},
|
&RequestHandler::SetCurrentSceneTransitionDuration},
|
||||||
|
{"SetCurrentSceneTransitionSettings",
|
||||||
|
&RequestHandler::SetCurrentSceneTransitionSettings},
|
||||||
|
{"GetCurrentSceneTransitionCursor",
|
||||||
|
&RequestHandler::GetCurrentSceneTransitionCursor},
|
||||||
|
{"TriggerStudioModeTransition",
|
||||||
|
&RequestHandler::TriggerStudioModeTransition},
|
||||||
{"SetTBarPosition", &RequestHandler::SetTBarPosition},
|
{"SetTBarPosition", &RequestHandler::SetTBarPosition},
|
||||||
|
|
||||||
// Filters
|
// Filters
|
||||||
{"GetSourceFilterList", &RequestHandler::GetSourceFilterList},
|
{"GetSourceFilterList", &RequestHandler::GetSourceFilterList},
|
||||||
{"GetSourceFilterDefaultSettings", &RequestHandler::GetSourceFilterDefaultSettings},
|
{"GetSourceFilterDefaultSettings",
|
||||||
|
&RequestHandler::GetSourceFilterDefaultSettings},
|
||||||
{"CreateSourceFilter", &RequestHandler::CreateSourceFilter},
|
{"CreateSourceFilter", &RequestHandler::CreateSourceFilter},
|
||||||
{"RemoveSourceFilter", &RequestHandler::RemoveSourceFilter},
|
{"RemoveSourceFilter", &RequestHandler::RemoveSourceFilter},
|
||||||
{"SetSourceFilterName", &RequestHandler::SetSourceFilterName},
|
{"SetSourceFilterName", &RequestHandler::SetSourceFilterName},
|
||||||
@ -138,8 +150,10 @@ 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", &RequestHandler::GetSceneItemPrivateSettings},
|
{"GetSceneItemPrivateSettings",
|
||||||
{"SetSceneItemPrivateSettings", &RequestHandler::SetSceneItemPrivateSettings},
|
&RequestHandler::GetSceneItemPrivateSettings},
|
||||||
|
{"SetSceneItemPrivateSettings",
|
||||||
|
&RequestHandler::SetSceneItemPrivateSettings},
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
{"GetVirtualCamStatus", &RequestHandler::GetVirtualCamStatus},
|
{"GetVirtualCamStatus", &RequestHandler::GetVirtualCamStatus},
|
||||||
@ -151,7 +165,8 @@ 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", &RequestHandler::GetLastReplayBufferReplay},
|
{"GetLastReplayBufferReplay",
|
||||||
|
&RequestHandler::GetLastReplayBufferReplay},
|
||||||
|
|
||||||
// Stream
|
// Stream
|
||||||
{"GetStreamStatus", &RequestHandler::GetStreamStatus},
|
{"GetStreamStatus", &RequestHandler::GetStreamStatus},
|
||||||
@ -178,34 +193,37 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
|
|||||||
// Ui
|
// Ui
|
||||||
{"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled},
|
{"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled},
|
||||||
{"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled},
|
{"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled},
|
||||||
{"OpenInputPropertiesDialog", &RequestHandler::OpenInputPropertiesDialog},
|
{"OpenInputPropertiesDialog",
|
||||||
|
&RequestHandler::OpenInputPropertiesDialog},
|
||||||
{"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog},
|
{"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog},
|
||||||
{"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog},
|
{"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog},
|
||||||
{"GetMonitorList", &RequestHandler::GetMonitorList},
|
{"GetMonitorList", &RequestHandler::GetMonitorList},
|
||||||
};
|
};
|
||||||
|
|
||||||
RequestHandler::RequestHandler(SessionPtr session) :
|
RequestHandler::RequestHandler(SessionPtr session) : _session(session) {}
|
||||||
_session(session)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestResult RequestHandler::ProcessRequest(const Request& request)
|
RequestResult RequestHandler::ProcessRequest(const Request &request)
|
||||||
{
|
{
|
||||||
#ifdef PLUGIN_TESTS
|
#ifdef PLUGIN_TESTS
|
||||||
ScopeProfiler prof{"obs_websocket_request_processing"};
|
ScopeProfiler prof{"obs_websocket_request_processing"};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!request.RequestData.is_object() && !request.RequestData.is_null())
|
if (!request.RequestData.is_object() && !request.RequestData.is_null())
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidRequestFieldType,
|
||||||
|
"Your request data is not an object.");
|
||||||
|
|
||||||
if (request.RequestType.empty())
|
if (request.RequestType.empty())
|
||||||
return RequestResult::Error(RequestStatus::MissingRequestType, "Your request is missing a `requestType`");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::MissingRequestType,
|
||||||
|
"Your request 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, "Your request type is not valid.");
|
return RequestResult::Error(RequestStatus::UnknownRequestType,
|
||||||
|
"Your request type is not valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::bind(handler, this, std::placeholders::_1)(request);
|
return std::bind(handler, this, std::placeholders::_1)(request);
|
||||||
@ -214,7 +232,7 @@ RequestResult RequestHandler::ProcessRequest(const Request& request)
|
|||||||
std::vector<std::string> RequestHandler::GetRequestList()
|
std::vector<std::string> RequestHandler::GetRequestList()
|
||||||
{
|
{
|
||||||
std::vector<std::string> ret;
|
std::vector<std::string> ret;
|
||||||
for (auto const& [key, val] : _handlerMap) {
|
for (auto const &[key, val] : _handlerMap) {
|
||||||
ret.push_back(key);
|
ret.push_back(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,174 +33,175 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
class RequestHandler;
|
class RequestHandler;
|
||||||
typedef RequestResult(RequestHandler::*RequestMethodHandler)(const Request&);
|
typedef RequestResult (RequestHandler::*RequestMethodHandler)(const Request &);
|
||||||
|
|
||||||
class RequestHandler {
|
class RequestHandler {
|
||||||
public:
|
public:
|
||||||
RequestHandler(SessionPtr session = nullptr);
|
RequestHandler(SessionPtr session = nullptr);
|
||||||
|
|
||||||
RequestResult ProcessRequest(const Request& request);
|
RequestResult ProcessRequest(const Request &request);
|
||||||
std::vector<std::string> GetRequestList();
|
std::vector<std::string> GetRequestList();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// General
|
// General
|
||||||
RequestResult GetVersion(const Request&);
|
RequestResult GetVersion(const Request &);
|
||||||
RequestResult GetStats(const Request&);
|
RequestResult GetStats(const Request &);
|
||||||
RequestResult BroadcastCustomEvent(const Request&);
|
RequestResult BroadcastCustomEvent(const Request &);
|
||||||
RequestResult CallVendorRequest(const Request&);
|
RequestResult CallVendorRequest(const Request &);
|
||||||
RequestResult GetHotkeyList(const Request&);
|
RequestResult GetHotkeyList(const Request &);
|
||||||
RequestResult TriggerHotkeyByName(const Request&);
|
RequestResult TriggerHotkeyByName(const Request &);
|
||||||
RequestResult TriggerHotkeyByKeySequence(const Request&);
|
RequestResult TriggerHotkeyByKeySequence(const Request &);
|
||||||
RequestResult Sleep(const Request&);
|
RequestResult Sleep(const Request &);
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
RequestResult GetPersistentData(const Request&);
|
RequestResult GetPersistentData(const Request &);
|
||||||
RequestResult SetPersistentData(const Request&);
|
RequestResult SetPersistentData(const Request &);
|
||||||
RequestResult GetSceneCollectionList(const Request&);
|
RequestResult GetSceneCollectionList(const Request &);
|
||||||
RequestResult SetCurrentSceneCollection(const Request&);
|
RequestResult SetCurrentSceneCollection(const Request &);
|
||||||
RequestResult CreateSceneCollection(const Request&);
|
RequestResult CreateSceneCollection(const Request &);
|
||||||
RequestResult GetProfileList(const Request&);
|
RequestResult GetProfileList(const Request &);
|
||||||
RequestResult SetCurrentProfile(const Request&);
|
RequestResult SetCurrentProfile(const Request &);
|
||||||
RequestResult CreateProfile(const Request&);
|
RequestResult CreateProfile(const Request &);
|
||||||
RequestResult RemoveProfile(const Request&);
|
RequestResult RemoveProfile(const Request &);
|
||||||
RequestResult GetProfileParameter(const Request&);
|
RequestResult GetProfileParameter(const Request &);
|
||||||
RequestResult SetProfileParameter(const Request&);
|
RequestResult SetProfileParameter(const Request &);
|
||||||
RequestResult GetVideoSettings(const Request&);
|
RequestResult GetVideoSettings(const Request &);
|
||||||
RequestResult SetVideoSettings(const Request&);
|
RequestResult SetVideoSettings(const Request &);
|
||||||
RequestResult GetStreamServiceSettings(const Request&);
|
RequestResult GetStreamServiceSettings(const Request &);
|
||||||
RequestResult SetStreamServiceSettings(const Request&);
|
RequestResult SetStreamServiceSettings(const Request &);
|
||||||
RequestResult GetRecordDirectory(const Request&);
|
RequestResult GetRecordDirectory(const Request &);
|
||||||
|
|
||||||
// Sources
|
// Sources
|
||||||
RequestResult GetSourceActive(const Request&);
|
RequestResult GetSourceActive(const Request &);
|
||||||
RequestResult GetSourceScreenshot(const Request&);
|
RequestResult GetSourceScreenshot(const Request &);
|
||||||
RequestResult SaveSourceScreenshot(const Request&);
|
RequestResult SaveSourceScreenshot(const Request &);
|
||||||
RequestResult GetSourcePrivateSettings(const Request&);
|
RequestResult GetSourcePrivateSettings(const Request &);
|
||||||
RequestResult SetSourcePrivateSettings(const Request&);
|
RequestResult SetSourcePrivateSettings(const Request &);
|
||||||
|
|
||||||
// Scenes
|
// Scenes
|
||||||
RequestResult GetSceneList(const Request&);
|
RequestResult GetSceneList(const Request &);
|
||||||
RequestResult GetGroupList(const Request&);
|
RequestResult GetGroupList(const Request &);
|
||||||
RequestResult GetCurrentProgramScene(const Request&);
|
RequestResult GetCurrentProgramScene(const Request &);
|
||||||
RequestResult SetCurrentProgramScene(const Request&);
|
RequestResult SetCurrentProgramScene(const Request &);
|
||||||
RequestResult GetCurrentPreviewScene(const Request&);
|
RequestResult GetCurrentPreviewScene(const Request &);
|
||||||
RequestResult SetCurrentPreviewScene(const Request&);
|
RequestResult SetCurrentPreviewScene(const Request &);
|
||||||
RequestResult CreateScene(const Request&);
|
RequestResult CreateScene(const Request &);
|
||||||
RequestResult RemoveScene(const Request&);
|
RequestResult RemoveScene(const Request &);
|
||||||
RequestResult SetSceneName(const Request&);
|
RequestResult SetSceneName(const Request &);
|
||||||
RequestResult GetSceneSceneTransitionOverride(const Request&);
|
RequestResult GetSceneSceneTransitionOverride(const Request &);
|
||||||
RequestResult SetSceneSceneTransitionOverride(const Request&);
|
RequestResult SetSceneSceneTransitionOverride(const Request &);
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
RequestResult GetInputList(const Request&);
|
RequestResult GetInputList(const Request &);
|
||||||
RequestResult GetInputKindList(const Request&);
|
RequestResult GetInputKindList(const Request &);
|
||||||
RequestResult GetSpecialInputs(const Request&);
|
RequestResult GetSpecialInputs(const Request &);
|
||||||
RequestResult CreateInput(const Request&);
|
RequestResult CreateInput(const Request &);
|
||||||
RequestResult RemoveInput(const Request&);
|
RequestResult RemoveInput(const Request &);
|
||||||
RequestResult SetInputName(const Request&);
|
RequestResult SetInputName(const Request &);
|
||||||
RequestResult GetInputDefaultSettings(const Request&);
|
RequestResult GetInputDefaultSettings(const Request &);
|
||||||
RequestResult GetInputSettings(const Request&);
|
RequestResult GetInputSettings(const Request &);
|
||||||
RequestResult SetInputSettings(const Request&);
|
RequestResult SetInputSettings(const Request &);
|
||||||
RequestResult GetInputMute(const Request&);
|
RequestResult GetInputMute(const Request &);
|
||||||
RequestResult SetInputMute(const Request&);
|
RequestResult SetInputMute(const Request &);
|
||||||
RequestResult ToggleInputMute(const Request&);
|
RequestResult ToggleInputMute(const Request &);
|
||||||
RequestResult GetInputVolume(const Request&);
|
RequestResult GetInputVolume(const Request &);
|
||||||
RequestResult SetInputVolume(const Request&);
|
RequestResult SetInputVolume(const Request &);
|
||||||
RequestResult GetInputAudioBalance(const Request&);
|
RequestResult GetInputAudioBalance(const Request &);
|
||||||
RequestResult SetInputAudioBalance(const Request&);
|
RequestResult SetInputAudioBalance(const Request &);
|
||||||
RequestResult GetInputAudioSyncOffset(const Request&);
|
RequestResult GetInputAudioSyncOffset(const Request &);
|
||||||
RequestResult SetInputAudioSyncOffset(const Request&);
|
RequestResult SetInputAudioSyncOffset(const Request &);
|
||||||
RequestResult GetInputAudioMonitorType(const Request&);
|
RequestResult GetInputAudioMonitorType(const Request &);
|
||||||
RequestResult SetInputAudioMonitorType(const Request&);
|
RequestResult SetInputAudioMonitorType(const Request &);
|
||||||
RequestResult GetInputAudioTracks(const Request&);
|
RequestResult GetInputAudioTracks(const Request &);
|
||||||
RequestResult SetInputAudioTracks(const Request&);
|
RequestResult SetInputAudioTracks(const Request &);
|
||||||
RequestResult GetInputPropertiesListPropertyItems(const Request&);
|
RequestResult GetInputPropertiesListPropertyItems(const Request &);
|
||||||
RequestResult PressInputPropertiesButton(const Request&);
|
RequestResult PressInputPropertiesButton(const Request &);
|
||||||
|
|
||||||
// Transitions
|
// Transitions
|
||||||
RequestResult GetTransitionKindList(const Request&);
|
RequestResult GetTransitionKindList(const Request &);
|
||||||
RequestResult GetSceneTransitionList(const Request&);
|
RequestResult GetSceneTransitionList(const Request &);
|
||||||
RequestResult GetCurrentSceneTransition(const Request&);
|
RequestResult GetCurrentSceneTransition(const Request &);
|
||||||
RequestResult SetCurrentSceneTransition(const Request&);
|
RequestResult SetCurrentSceneTransition(const Request &);
|
||||||
RequestResult SetCurrentSceneTransitionDuration(const Request&);
|
RequestResult SetCurrentSceneTransitionDuration(const Request &);
|
||||||
RequestResult SetCurrentSceneTransitionSettings(const Request&);
|
RequestResult SetCurrentSceneTransitionSettings(const Request &);
|
||||||
RequestResult GetCurrentSceneTransitionCursor(const Request&);
|
RequestResult GetCurrentSceneTransitionCursor(const Request &);
|
||||||
RequestResult TriggerStudioModeTransition(const Request&);
|
RequestResult TriggerStudioModeTransition(const Request &);
|
||||||
RequestResult SetTBarPosition(const Request&);
|
RequestResult SetTBarPosition(const Request &);
|
||||||
|
|
||||||
// Filters
|
// Filters
|
||||||
RequestResult GetSourceFilterList(const Request&);
|
RequestResult GetSourceFilterList(const Request &);
|
||||||
RequestResult GetSourceFilterDefaultSettings(const Request&);
|
RequestResult GetSourceFilterDefaultSettings(const Request &);
|
||||||
RequestResult CreateSourceFilter(const Request&);
|
RequestResult CreateSourceFilter(const Request &);
|
||||||
RequestResult RemoveSourceFilter(const Request&);
|
RequestResult RemoveSourceFilter(const Request &);
|
||||||
RequestResult SetSourceFilterName(const Request&);
|
RequestResult SetSourceFilterName(const Request &);
|
||||||
RequestResult GetSourceFilter(const Request&);
|
RequestResult GetSourceFilter(const Request &);
|
||||||
RequestResult SetSourceFilterIndex(const Request&);
|
RequestResult SetSourceFilterIndex(const Request &);
|
||||||
RequestResult SetSourceFilterSettings(const Request&);
|
RequestResult SetSourceFilterSettings(const Request &);
|
||||||
RequestResult SetSourceFilterEnabled(const Request&);
|
RequestResult SetSourceFilterEnabled(const Request &);
|
||||||
|
|
||||||
// Scene Items
|
// Scene Items
|
||||||
RequestResult GetSceneItemList(const Request&);
|
RequestResult GetSceneItemList(const Request &);
|
||||||
RequestResult GetGroupSceneItemList(const Request&);
|
RequestResult GetGroupSceneItemList(const Request &);
|
||||||
RequestResult GetSceneItemId(const Request&);
|
RequestResult GetSceneItemId(const Request &);
|
||||||
RequestResult CreateSceneItem(const Request&);
|
RequestResult CreateSceneItem(const Request &);
|
||||||
RequestResult RemoveSceneItem(const Request&);
|
RequestResult RemoveSceneItem(const Request &);
|
||||||
RequestResult DuplicateSceneItem(const Request&);
|
RequestResult DuplicateSceneItem(const Request &);
|
||||||
RequestResult GetSceneItemTransform(const Request&);
|
RequestResult GetSceneItemTransform(const Request &);
|
||||||
RequestResult SetSceneItemTransform(const Request&);
|
RequestResult SetSceneItemTransform(const Request &);
|
||||||
RequestResult GetSceneItemEnabled(const Request&);
|
RequestResult GetSceneItemEnabled(const Request &);
|
||||||
RequestResult SetSceneItemEnabled(const Request&);
|
RequestResult SetSceneItemEnabled(const Request &);
|
||||||
RequestResult GetSceneItemLocked(const Request&);
|
RequestResult GetSceneItemLocked(const Request &);
|
||||||
RequestResult SetSceneItemLocked(const Request&);
|
RequestResult SetSceneItemLocked(const Request &);
|
||||||
RequestResult GetSceneItemIndex(const Request&);
|
RequestResult GetSceneItemIndex(const Request &);
|
||||||
RequestResult SetSceneItemIndex(const Request&);
|
RequestResult SetSceneItemIndex(const Request &);
|
||||||
RequestResult GetSceneItemBlendMode(const Request&);
|
RequestResult GetSceneItemBlendMode(const Request &);
|
||||||
RequestResult SetSceneItemBlendMode(const Request&);
|
RequestResult SetSceneItemBlendMode(const Request &);
|
||||||
RequestResult GetSceneItemPrivateSettings(const Request&);
|
RequestResult GetSceneItemPrivateSettings(const Request &);
|
||||||
RequestResult SetSceneItemPrivateSettings(const Request&);
|
RequestResult SetSceneItemPrivateSettings(const Request &);
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
RequestResult GetVirtualCamStatus(const Request&);
|
RequestResult GetVirtualCamStatus(const Request &);
|
||||||
RequestResult ToggleVirtualCam(const Request&);
|
RequestResult ToggleVirtualCam(const Request &);
|
||||||
RequestResult StartVirtualCam(const Request&);
|
RequestResult StartVirtualCam(const Request &);
|
||||||
RequestResult StopVirtualCam(const Request&);
|
RequestResult StopVirtualCam(const Request &);
|
||||||
RequestResult GetReplayBufferStatus(const Request&);
|
RequestResult GetReplayBufferStatus(const Request &);
|
||||||
RequestResult ToggleReplayBuffer(const Request&);
|
RequestResult ToggleReplayBuffer(const Request &);
|
||||||
RequestResult StartReplayBuffer(const Request&);
|
RequestResult StartReplayBuffer(const Request &);
|
||||||
RequestResult StopReplayBuffer(const Request&);
|
RequestResult StopReplayBuffer(const Request &);
|
||||||
RequestResult SaveReplayBuffer(const Request&);
|
RequestResult SaveReplayBuffer(const Request &);
|
||||||
RequestResult GetLastReplayBufferReplay(const Request&);
|
RequestResult GetLastReplayBufferReplay(const Request &);
|
||||||
|
|
||||||
// Stream
|
// Stream
|
||||||
RequestResult GetStreamStatus(const Request&);
|
RequestResult GetStreamStatus(const Request &);
|
||||||
RequestResult ToggleStream(const Request&);
|
RequestResult ToggleStream(const Request &);
|
||||||
RequestResult StartStream(const Request&);
|
RequestResult StartStream(const Request &);
|
||||||
RequestResult StopStream(const Request&);
|
RequestResult StopStream(const Request &);
|
||||||
RequestResult SendStreamCaption(const Request&);
|
RequestResult SendStreamCaption(const Request &);
|
||||||
|
|
||||||
// Record
|
// Record
|
||||||
RequestResult GetRecordStatus(const Request&);
|
RequestResult GetRecordStatus(const Request &);
|
||||||
RequestResult ToggleRecord(const Request&);
|
RequestResult ToggleRecord(const Request &);
|
||||||
RequestResult StartRecord(const Request&);
|
RequestResult StartRecord(const Request &);
|
||||||
RequestResult StopRecord(const Request&);
|
RequestResult StopRecord(const Request &);
|
||||||
RequestResult ToggleRecordPause(const Request&);
|
RequestResult ToggleRecordPause(const Request &);
|
||||||
RequestResult PauseRecord(const Request&);
|
RequestResult PauseRecord(const Request &);
|
||||||
RequestResult ResumeRecord(const Request&);
|
RequestResult ResumeRecord(const Request &);
|
||||||
|
|
||||||
// Media Inputs
|
// Media Inputs
|
||||||
RequestResult GetMediaInputStatus(const Request&);
|
RequestResult GetMediaInputStatus(const Request &);
|
||||||
RequestResult SetMediaInputCursor(const Request&);
|
RequestResult SetMediaInputCursor(const Request &);
|
||||||
RequestResult OffsetMediaInputCursor(const Request&);
|
RequestResult OffsetMediaInputCursor(const Request &);
|
||||||
RequestResult TriggerMediaInputAction(const Request&);
|
RequestResult TriggerMediaInputAction(const Request &);
|
||||||
|
|
||||||
// Ui
|
// Ui
|
||||||
RequestResult GetStudioModeEnabled(const Request&);
|
RequestResult GetStudioModeEnabled(const Request &);
|
||||||
RequestResult SetStudioModeEnabled(const Request&);
|
RequestResult SetStudioModeEnabled(const Request &);
|
||||||
RequestResult OpenInputPropertiesDialog(const Request&);
|
RequestResult OpenInputPropertiesDialog(const Request &);
|
||||||
RequestResult OpenInputFiltersDialog(const Request&);
|
RequestResult OpenInputFiltersDialog(const Request &);
|
||||||
RequestResult OpenInputInteractDialog(const Request&);
|
RequestResult OpenInputInteractDialog(const Request &);
|
||||||
RequestResult GetMonitorList(const Request&);
|
RequestResult GetMonitorList(const Request &);
|
||||||
|
|
||||||
SessionPtr _session;
|
SessionPtr _session;
|
||||||
static const std::unordered_map<std::string, RequestMethodHandler> _handlerMap;
|
static const std::unordered_map<std::string, RequestMethodHandler>
|
||||||
|
_handlerMap;
|
||||||
};
|
};
|
||||||
|
@ -37,27 +37,34 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetPersistentData(const Request& request)
|
RequestResult RequestHandler::GetPersistentData(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!(request.ValidateString("realm", statusCode, comment) && request.ValidateString("slotName", statusCode, comment)))
|
if (!(request.ValidateString("realm", 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 = Utils::Obs::StringHelper::GetCurrentProfilePath();
|
std::string persistentDataPath =
|
||||||
|
Utils::Obs::StringHelper::GetCurrentProfilePath();
|
||||||
if (realm == "OBS_WEBSOCKET_DATA_REALM_GLOBAL")
|
if (realm == "OBS_WEBSOCKET_DATA_REALM_GLOBAL")
|
||||||
persistentDataPath += "/../../../obsWebSocketPersistentData.json";
|
persistentDataPath +=
|
||||||
|
"/../../../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(RequestStatus::ResourceNotFound, "You have specified an invalid persistent data realm.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceNotFound,
|
||||||
|
"You have specified an invalid persistent data realm.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
json persistentData;
|
json persistentData;
|
||||||
if (Utils::Json::GetJsonFileContent(persistentDataPath, persistentData) && persistentData.contains(slotName))
|
if (Utils::Json::GetJsonFileContent(persistentDataPath,
|
||||||
|
persistentData) &&
|
||||||
|
persistentData.contains(slotName))
|
||||||
responseData["slotValue"] = persistentData[slotName];
|
responseData["slotValue"] = persistentData[slotName];
|
||||||
else
|
else
|
||||||
responseData["slotValue"] = nullptr;
|
responseData["slotValue"] = nullptr;
|
||||||
@ -79,30 +86,39 @@ RequestResult RequestHandler::GetPersistentData(const Request& request)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetPersistentData(const Request& request)
|
RequestResult RequestHandler::SetPersistentData(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!(request.ValidateString("realm", statusCode, comment) && request.ValidateString("slotName", statusCode, comment) && request.ValidateBasic("slotValue", statusCode, comment)))
|
if (!(request.ValidateString("realm", statusCode, comment) &&
|
||||||
|
request.ValidateString("slotName", statusCode, comment) &&
|
||||||
|
request.ValidateBasic("slotValue", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string realm = request.RequestData["realm"];
|
std::string realm = request.RequestData["realm"];
|
||||||
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 = Utils::Obs::StringHelper::GetCurrentProfilePath();
|
std::string persistentDataPath =
|
||||||
|
Utils::Obs::StringHelper::GetCurrentProfilePath();
|
||||||
if (realm == "OBS_WEBSOCKET_DATA_REALM_GLOBAL")
|
if (realm == "OBS_WEBSOCKET_DATA_REALM_GLOBAL")
|
||||||
persistentDataPath += "/../../../obsWebSocketPersistentData.json";
|
persistentDataPath +=
|
||||||
|
"/../../../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(RequestStatus::ResourceNotFound, "You have specified an invalid persistent data realm.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceNotFound,
|
||||||
|
"You have specified an invalid persistent data realm.");
|
||||||
|
|
||||||
json persistentData = json::object();
|
json persistentData = json::object();
|
||||||
Utils::Json::GetJsonFileContent(persistentDataPath, persistentData);
|
Utils::Json::GetJsonFileContent(persistentDataPath, persistentData);
|
||||||
persistentData[slotName] = slotValue;
|
persistentData[slotName] = slotValue;
|
||||||
if (!Utils::Json::SetJsonFileContent(persistentDataPath, persistentData))
|
if (!Utils::Json::SetJsonFileContent(persistentDataPath,
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to write persistent data. No permissions?");
|
persistentData))
|
||||||
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestProcessingFailed,
|
||||||
|
"Unable to write persistent data. No permissions?");
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -120,11 +136,13 @@ RequestResult RequestHandler::SetPersistentData(const Request& request)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSceneCollectionList(const Request&)
|
RequestResult RequestHandler::GetSceneCollectionList(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["currentSceneCollectionName"] = Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
responseData["currentSceneCollectionName"] =
|
||||||
responseData["sceneCollections"] = Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
||||||
|
responseData["sceneCollections"] =
|
||||||
|
Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,25 +160,33 @@ RequestResult RequestHandler::GetSceneCollectionList(const Request&)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetCurrentSceneCollection(const Request& request)
|
RequestResult RequestHandler::SetCurrentSceneCollection(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
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 = request.RequestData["sceneCollectionName"];
|
std::string sceneCollectionName =
|
||||||
|
request.RequestData["sceneCollectionName"];
|
||||||
|
|
||||||
auto sceneCollections = Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
auto sceneCollections =
|
||||||
if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) == sceneCollections.end())
|
Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
||||||
|
if (std::find(sceneCollections.begin(), sceneCollections.end(),
|
||||||
|
sceneCollectionName) == sceneCollections.end())
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound);
|
return RequestResult::Error(RequestStatus::ResourceNotFound);
|
||||||
|
|
||||||
std::string currentSceneCollectionName = Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
std::string currentSceneCollectionName =
|
||||||
|
Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
||||||
// Avoid queueing tasks if nothing will change
|
// Avoid queueing tasks if nothing will change
|
||||||
if (currentSceneCollectionName != sceneCollectionName) {
|
if (currentSceneCollectionName != sceneCollectionName) {
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(
|
||||||
obs_frontend_set_current_scene_collection(static_cast<const char*>(param));
|
OBS_TASK_UI,
|
||||||
}, (void*)sceneCollectionName.c_str(), true);
|
[](void *param) {
|
||||||
|
obs_frontend_set_current_scene_collection(
|
||||||
|
static_cast<const char *>(param));
|
||||||
|
},
|
||||||
|
(void *)sceneCollectionName.c_str(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
@ -180,24 +206,34 @@ RequestResult RequestHandler::SetCurrentSceneCollection(const Request& request)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::CreateSceneCollection(const Request& request)
|
RequestResult RequestHandler::CreateSceneCollection(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
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 = request.RequestData["sceneCollectionName"];
|
std::string sceneCollectionName =
|
||||||
|
request.RequestData["sceneCollectionName"];
|
||||||
|
|
||||||
auto sceneCollections = Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
auto sceneCollections =
|
||||||
if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) != sceneCollections.end())
|
Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
|
if (std::find(sceneCollections.begin(), sceneCollections.end(),
|
||||||
|
sceneCollectionName) != sceneCollections.end())
|
||||||
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceAlreadyExists);
|
||||||
|
|
||||||
QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window());
|
QMainWindow *mainWindow =
|
||||||
|
static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
||||||
bool success = false;
|
bool success = false;
|
||||||
QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
|
QMetaObject::invokeMethod(
|
||||||
|
mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(bool, success), Q_ARG(bool, true),
|
||||||
|
Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
|
||||||
if (!success)
|
if (!success)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene collection.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceCreationFailed,
|
||||||
|
"Failed to create the scene collection.");
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -215,10 +251,11 @@ RequestResult RequestHandler::CreateSceneCollection(const Request& request)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetProfileList(const Request&)
|
RequestResult RequestHandler::GetProfileList(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["currentProfileName"] = Utils::Obs::StringHelper::GetCurrentProfile();
|
responseData["currentProfileName"] =
|
||||||
|
Utils::Obs::StringHelper::GetCurrentProfile();
|
||||||
responseData["profiles"] = Utils::Obs::ArrayHelper::GetProfileList();
|
responseData["profiles"] = Utils::Obs::ArrayHelper::GetProfileList();
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -235,7 +272,7 @@ RequestResult RequestHandler::GetProfileList(const Request&)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetCurrentProfile(const Request& request)
|
RequestResult RequestHandler::SetCurrentProfile(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
@ -245,15 +282,21 @@ 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) == profiles.end())
|
if (std::find(profiles.begin(), profiles.end(), profileName) ==
|
||||||
|
profiles.end())
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound);
|
return RequestResult::Error(RequestStatus::ResourceNotFound);
|
||||||
|
|
||||||
std::string currentProfileName = Utils::Obs::StringHelper::GetCurrentProfile();
|
std::string currentProfileName =
|
||||||
|
Utils::Obs::StringHelper::GetCurrentProfile();
|
||||||
// Avoid queueing tasks if nothing will change
|
// Avoid queueing tasks if nothing will change
|
||||||
if (currentProfileName != profileName) {
|
if (currentProfileName != profileName) {
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(
|
||||||
obs_frontend_set_current_profile(static_cast<const char*>(param));
|
OBS_TASK_UI,
|
||||||
}, (void*)profileName.c_str(), true);
|
[](void *param) {
|
||||||
|
obs_frontend_set_current_profile(
|
||||||
|
static_cast<const char *>(param));
|
||||||
|
},
|
||||||
|
(void *)profileName.c_str(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
@ -271,7 +314,7 @@ RequestResult RequestHandler::SetCurrentProfile(const Request& request)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::CreateProfile(const Request& request)
|
RequestResult RequestHandler::CreateProfile(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
@ -281,11 +324,16 @@ 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) != profiles.end())
|
if (std::find(profiles.begin(), profiles.end(), profileName) !=
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
|
profiles.end())
|
||||||
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceAlreadyExists);
|
||||||
|
|
||||||
QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window());
|
QMainWindow *mainWindow =
|
||||||
QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
|
static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
||||||
|
QMetaObject::invokeMethod(
|
||||||
|
mainWindow, "NewProfile", Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(QString, QString::fromStdString(profileName)));
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -302,7 +350,7 @@ RequestResult RequestHandler::CreateProfile(const Request& request)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::RemoveProfile(const Request& request)
|
RequestResult RequestHandler::RemoveProfile(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
@ -312,14 +360,18 @@ 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) == profiles.end())
|
if (std::find(profiles.begin(), profiles.end(), profileName) ==
|
||||||
|
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 = static_cast<QMainWindow*>(obs_frontend_get_main_window());
|
QMainWindow *mainWindow =
|
||||||
QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
|
static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
||||||
|
QMetaObject::invokeMethod(
|
||||||
|
mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(QString, QString::fromStdString(profileName)));
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -340,27 +392,40 @@ RequestResult RequestHandler::RemoveProfile(const Request& request)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetProfileParameter(const Request& request)
|
RequestResult RequestHandler::GetProfileParameter(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!(request.ValidateString("parameterCategory", statusCode, comment) && request.ValidateString("parameterName", statusCode, comment)))
|
if (!(request.ValidateString("parameterCategory", statusCode,
|
||||||
|
comment) &&
|
||||||
|
request.ValidateString("parameterName", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string parameterCategory = request.RequestData["parameterCategory"];
|
std::string parameterCategory =
|
||||||
|
request.RequestData["parameterCategory"];
|
||||||
std::string parameterName = request.RequestData["parameterName"];
|
std::string parameterName = request.RequestData["parameterName"];
|
||||||
|
|
||||||
config_t* profile = obs_frontend_get_profile_config();
|
config_t *profile = obs_frontend_get_profile_config();
|
||||||
|
|
||||||
if (!profile)
|
if (!profile)
|
||||||
blog(LOG_ERROR, "[RequestHandler::GetProfileParameter] Profile is invalid.");
|
blog(LOG_ERROR,
|
||||||
|
"[RequestHandler::GetProfileParameter] Profile is invalid.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
if (config_has_default_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
|
if (config_has_default_value(profile, parameterCategory.c_str(),
|
||||||
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
parameterName.c_str())) {
|
||||||
responseData["defaultParameterValue"] = config_get_default_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
responseData["parameterValue"] =
|
||||||
} else if (config_has_user_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
|
config_get_string(profile, parameterCategory.c_str(),
|
||||||
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
parameterName.c_str());
|
||||||
|
responseData["defaultParameterValue"] =
|
||||||
|
config_get_default_string(profile,
|
||||||
|
parameterCategory.c_str(),
|
||||||
|
parameterName.c_str());
|
||||||
|
} else if (config_has_user_value(profile, parameterCategory.c_str(),
|
||||||
|
parameterName.c_str())) {
|
||||||
|
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;
|
||||||
@ -384,28 +449,39 @@ RequestResult RequestHandler::GetProfileParameter(const Request& request)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetProfileParameter(const Request& request)
|
RequestResult RequestHandler::SetProfileParameter(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!(request.ValidateString("parameterCategory", statusCode, comment) &&
|
if (!(request.ValidateString("parameterCategory", statusCode,
|
||||||
request.ValidateString("parameterName", statusCode, comment)))
|
comment) &&
|
||||||
|
request.ValidateString("parameterName", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string parameterCategory = request.RequestData["parameterCategory"];
|
std::string parameterCategory =
|
||||||
|
request.RequestData["parameterCategory"];
|
||||||
std::string parameterName = request.RequestData["parameterName"];
|
std::string parameterName = request.RequestData["parameterName"];
|
||||||
|
|
||||||
config_t* profile = obs_frontend_get_profile_config();
|
config_t *profile = obs_frontend_get_profile_config();
|
||||||
|
|
||||||
// Using check helpers here would just make the logic more complicated
|
// Using check helpers here would just make the logic more complicated
|
||||||
if (!request.RequestData.contains("parameterValue") || request.RequestData["parameterValue"].is_null()) {
|
if (!request.RequestData.contains("parameterValue") ||
|
||||||
if (!config_remove_value(profile, parameterCategory.c_str(), parameterName.c_str()))
|
request.RequestData["parameterValue"].is_null()) {
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "There are no existing instances of that profile parameter.");
|
if (!config_remove_value(profile, parameterCategory.c_str(),
|
||||||
|
parameterName.c_str()))
|
||||||
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceNotFound,
|
||||||
|
"There are no existing instances of that profile parameter.");
|
||||||
} else if (request.RequestData["parameterValue"].is_string()) {
|
} else if (request.RequestData["parameterValue"].is_string()) {
|
||||||
std::string parameterValue = request.RequestData["parameterValue"];
|
std::string parameterValue =
|
||||||
config_set_string(profile, parameterCategory.c_str(), parameterName.c_str(), parameterValue.c_str());
|
request.RequestData["parameterValue"];
|
||||||
|
config_set_string(profile, parameterCategory.c_str(),
|
||||||
|
parameterName.c_str(),
|
||||||
|
parameterValue.c_str());
|
||||||
} else {
|
} else {
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The field `parameterValue` must be a string.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidRequestFieldType,
|
||||||
|
"The field `parameterValue` must be a string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
config_save(profile);
|
config_save(profile);
|
||||||
@ -432,11 +508,13 @@ RequestResult RequestHandler::SetProfileParameter(const Request& request)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetVideoSettings(const Request&)
|
RequestResult RequestHandler::GetVideoSettings(const Request &)
|
||||||
{
|
{
|
||||||
struct obs_video_info ovi;
|
struct obs_video_info ovi;
|
||||||
if (!obs_get_video_info(&ovi))
|
if (!obs_get_video_info(&ovi))
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to get internal OBS video info.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestProcessingFailed,
|
||||||
|
"Unable to get internal OBS video info.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["fpsNumerator"] = ovi.fps_num;
|
responseData["fpsNumerator"] = ovi.fps_num;
|
||||||
@ -468,41 +546,63 @@ RequestResult RequestHandler::GetVideoSettings(const Request&)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetVideoSettings(const Request& request)
|
RequestResult RequestHandler::SetVideoSettings(const Request &request)
|
||||||
{
|
{
|
||||||
if (obs_video_active())
|
if (obs_video_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning, "Video settings cannot be changed while an output is active.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::OutputRunning,
|
||||||
|
"Video settings cannot be changed while an output is active.");
|
||||||
|
|
||||||
RequestStatus::RequestStatus statusCode = RequestStatus::NoError;
|
RequestStatus::RequestStatus statusCode = RequestStatus::NoError;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
bool changeFps = (request.Contains("fpsNumerator") && request.Contains("fpsDenominator"));
|
bool changeFps = (request.Contains("fpsNumerator") &&
|
||||||
if (changeFps && !(request.ValidateOptionalNumber("fpsNumerator", statusCode, comment, 1) && request.ValidateOptionalNumber("fpsDenominator", statusCode, comment, 1)))
|
request.Contains("fpsDenominator"));
|
||||||
|
if (changeFps && !(request.ValidateOptionalNumber(
|
||||||
|
"fpsNumerator", statusCode, comment, 1) &&
|
||||||
|
request.ValidateOptionalNumber(
|
||||||
|
"fpsDenominator", statusCode, comment, 1)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
bool changeBaseRes = (request.Contains("baseWidth") && request.Contains("baseHeight"));
|
bool changeBaseRes = (request.Contains("baseWidth") &&
|
||||||
if (changeBaseRes && !(request.ValidateOptionalNumber("baseWidth", statusCode, comment, 8, 4096) && request.ValidateOptionalNumber("baseHeight", statusCode, comment, 8, 4096)))
|
request.Contains("baseHeight"));
|
||||||
|
if (changeBaseRes &&
|
||||||
|
!(request.ValidateOptionalNumber("baseWidth", statusCode, comment,
|
||||||
|
8, 4096) &&
|
||||||
|
request.ValidateOptionalNumber("baseHeight", statusCode, comment,
|
||||||
|
8, 4096)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
bool changeOutputRes = (request.Contains("outputWidth") && request.Contains("outputHeight"));
|
bool changeOutputRes = (request.Contains("outputWidth") &&
|
||||||
if (changeOutputRes && !(request.ValidateOptionalNumber("outputWidth", statusCode, comment, 8, 4096) && request.ValidateOptionalNumber("outputHeight", statusCode, comment, 8, 4096)))
|
request.Contains("outputHeight"));
|
||||||
|
if (changeOutputRes &&
|
||||||
|
!(request.ValidateOptionalNumber("outputWidth", statusCode, comment,
|
||||||
|
8, 4096) &&
|
||||||
|
request.ValidateOptionalNumber("outputHeight", statusCode,
|
||||||
|
comment, 8, 4096)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
config_t *config = obs_frontend_get_profile_config();
|
config_t *config = obs_frontend_get_profile_config();
|
||||||
|
|
||||||
if (changeFps) {
|
if (changeFps) {
|
||||||
config_set_uint(config, "Video", "FPSType", 2);
|
config_set_uint(config, "Video", "FPSType", 2);
|
||||||
config_set_uint(config, "Video", "FPSNum", request.RequestData["fpsNumerator"]);
|
config_set_uint(config, "Video", "FPSNum",
|
||||||
config_set_uint(config, "Video", "FPSDen", request.RequestData["fpsDenominator"]);
|
request.RequestData["fpsNumerator"]);
|
||||||
|
config_set_uint(config, "Video", "FPSDen",
|
||||||
|
request.RequestData["fpsDenominator"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changeBaseRes) {
|
if (changeBaseRes) {
|
||||||
config_set_uint(config, "Video", "BaseCX", request.RequestData["baseWidth"]);
|
config_set_uint(config, "Video", "BaseCX",
|
||||||
config_set_uint(config, "Video", "BaseCY", request.RequestData["baseHeight"]);
|
request.RequestData["baseWidth"]);
|
||||||
|
config_set_uint(config, "Video", "BaseCY",
|
||||||
|
request.RequestData["baseHeight"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changeOutputRes) {
|
if (changeOutputRes) {
|
||||||
config_set_uint(config, "Video", "OutputCX", request.RequestData["outputWidth"]);
|
config_set_uint(config, "Video", "OutputCX",
|
||||||
config_set_uint(config, "Video", "OutputCY", request.RequestData["outputHeight"]);
|
request.RequestData["outputWidth"]);
|
||||||
|
config_set_uint(config, "Video", "OutputCY",
|
||||||
|
request.RequestData["outputHeight"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changeFps || changeBaseRes || changeOutputRes) {
|
if (changeFps || changeBaseRes || changeOutputRes) {
|
||||||
@ -511,7 +611,9 @@ RequestResult RequestHandler::SetVideoSettings(const Request& request)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestResult::Error(RequestStatus::MissingRequestField, "You must specify at least one video-changing pair.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::MissingRequestField,
|
||||||
|
"You must specify at least one video-changing pair.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -527,14 +629,15 @@ RequestResult RequestHandler::SetVideoSettings(const Request& request)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetStreamServiceSettings(const Request&)
|
RequestResult RequestHandler::GetStreamServiceSettings(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
|
|
||||||
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"] = Utils::Json::ObsDataToJson(serviceSettings, true);
|
responseData["streamServiceSettings"] =
|
||||||
|
Utils::Json::ObsDataToJson(serviceSettings, true);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -554,38 +657,56 @@ RequestResult RequestHandler::GetStreamServiceSettings(const Request&)
|
|||||||
* @category config
|
* @category config
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetStreamServiceSettings(const Request& request)
|
RequestResult RequestHandler::SetStreamServiceSettings(const Request &request)
|
||||||
{
|
{
|
||||||
if (obs_frontend_streaming_active())
|
if (obs_frontend_streaming_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning, "You cannot change stream service settings while streaming.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::OutputRunning,
|
||||||
|
"You cannot change stream service settings while streaming.");
|
||||||
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!(request.ValidateString("streamServiceType", statusCode, comment) && request.ValidateObject("streamServiceSettings", statusCode, comment)))
|
if (!(request.ValidateString("streamServiceType", statusCode,
|
||||||
|
comment) &&
|
||||||
|
request.ValidateObject("streamServiceSettings", statusCode,
|
||||||
|
comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
OBSService currentStreamService = obs_frontend_get_streaming_service();
|
OBSService currentStreamService = obs_frontend_get_streaming_service();
|
||||||
|
|
||||||
std::string streamServiceType = obs_service_get_type(currentStreamService);
|
std::string streamServiceType =
|
||||||
std::string requestedStreamServiceType = request.RequestData["streamServiceType"];
|
obs_service_get_type(currentStreamService);
|
||||||
OBSDataAutoRelease requestedStreamServiceSettings = Utils::Json::JsonToObsData(request.RequestData["streamServiceSettings"]);
|
std::string requestedStreamServiceType =
|
||||||
|
request.RequestData["streamServiceType"];
|
||||||
|
OBSDataAutoRelease requestedStreamServiceSettings =
|
||||||
|
Utils::Json::JsonToObsData(
|
||||||
|
request.RequestData["streamServiceSettings"]);
|
||||||
|
|
||||||
// Don't create a new service if the current service is the same type.
|
// Don't create a new service if the current service is the same type.
|
||||||
if (streamServiceType == requestedStreamServiceType) {
|
if (streamServiceType == requestedStreamServiceType) {
|
||||||
OBSDataAutoRelease currentStreamServiceSettings = obs_service_get_settings(currentStreamService);
|
OBSDataAutoRelease currentStreamServiceSettings =
|
||||||
|
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, currentStreamServiceSettings);
|
obs_data_apply(newStreamServiceSettings,
|
||||||
obs_data_apply(newStreamServiceSettings, requestedStreamServiceSettings);
|
currentStreamServiceSettings);
|
||||||
|
obs_data_apply(newStreamServiceSettings,
|
||||||
|
requestedStreamServiceSettings);
|
||||||
|
|
||||||
obs_service_update(currentStreamService, newStreamServiceSettings);
|
obs_service_update(currentStreamService,
|
||||||
|
newStreamServiceSettings);
|
||||||
} else {
|
} else {
|
||||||
// TODO: This leaks memory. I have no idea why.
|
// TODO: This leaks memory. I have no idea why.
|
||||||
OBSService newStreamService = obs_service_create(requestedStreamServiceType.c_str(), "obs_websocket_custom_service", requestedStreamServiceSettings, nullptr);
|
OBSService newStreamService = obs_service_create(
|
||||||
|
requestedStreamServiceType.c_str(),
|
||||||
|
"obs_websocket_custom_service",
|
||||||
|
requestedStreamServiceSettings, nullptr);
|
||||||
// TODO: Check service type here, instead of relying on service creation to fail.
|
// TODO: Check service type here, instead of relying on service creation to fail.
|
||||||
if (!newStreamService)
|
if (!newStreamService)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the stream service with the requested streamServiceType. It may be an invalid type.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceCreationFailed,
|
||||||
|
"Failed to create the stream service with the requested streamServiceType. It may be an invalid type.");
|
||||||
|
|
||||||
obs_frontend_set_streaming_service(newStreamService);
|
obs_frontend_set_streaming_service(newStreamService);
|
||||||
}
|
}
|
||||||
@ -607,10 +728,11 @@ RequestResult RequestHandler::SetStreamServiceSettings(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category rconfig
|
* @category rconfig
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetRecordDirectory(const Request&)
|
RequestResult RequestHandler::GetRecordDirectory(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["recordDirectory"] = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
|
responseData["recordDirectory"] =
|
||||||
|
Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
@ -33,16 +33,18 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSourceFilterList(const Request& request)
|
RequestResult RequestHandler::GetSourceFilterList(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
OBSSourceAutoRelease source =
|
||||||
if(!source)
|
request.ValidateSource("sourceName", statusCode, comment);
|
||||||
|
if (!source)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["filters"] = Utils::Obs::ArrayHelper::GetSourceFilterList(source);
|
responseData["filters"] =
|
||||||
|
Utils::Obs::ArrayHelper::GetSourceFilterList(source);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -61,7 +63,8 @@ RequestResult RequestHandler::GetSourceFilterList(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request& request)
|
RequestResult
|
||||||
|
RequestHandler::GetSourceFilterDefaultSettings(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
@ -73,12 +76,14 @@ RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request& requ
|
|||||||
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 = obs_get_source_defaults(filterKind.c_str());
|
OBSDataAutoRelease defaultSettings =
|
||||||
|
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"] = Utils::Json::ObsDataToJson(defaultSettings, true);
|
responseData["defaultFilterSettings"] =
|
||||||
|
Utils::Json::ObsDataToJson(defaultSettings, true);
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,37 +102,51 @@ RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request& requ
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::CreateSourceFilter(const Request& request)
|
RequestResult RequestHandler::CreateSourceFilter(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
|
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
OBSSourceAutoRelease source =
|
||||||
if (!(source && request.ValidateString("filterName", statusCode, comment) && request.ValidateString("filterKind", statusCode, comment)))
|
request.ValidateSource("sourceName", statusCode, comment);
|
||||||
|
if (!(source &&
|
||||||
|
request.ValidateString("filterName", statusCode, comment) &&
|
||||||
|
request.ValidateString("filterKind", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string filterName = request.RequestData["filterName"];
|
std::string filterName = request.RequestData["filterName"];
|
||||||
OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name(source, filterName.c_str());
|
OBSSourceAutoRelease existingFilter =
|
||||||
|
obs_source_get_filter_by_name(source, filterName.c_str());
|
||||||
if (existingFilter)
|
if (existingFilter)
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A filter already exists by that name.");
|
return RequestResult::Error(
|
||||||
|
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();
|
||||||
if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end())
|
if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end())
|
||||||
return RequestResult::Error(RequestStatus::InvalidFilterKind, "Your specified filter kind is not supported by OBS. Check that any necessary plugins are loaded.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidFilterKind,
|
||||||
|
"Your specified filter kind is not supported by OBS. Check that any necessary plugins are loaded.");
|
||||||
|
|
||||||
OBSDataAutoRelease filterSettings = nullptr;
|
OBSDataAutoRelease filterSettings = nullptr;
|
||||||
if (request.Contains("filterSettings")) {
|
if (request.Contains("filterSettings")) {
|
||||||
if (!request.ValidateOptionalObject("filterSettings", statusCode, comment, true))
|
if (!request.ValidateOptionalObject("filterSettings",
|
||||||
|
statusCode, comment, true))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
filterSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
|
filterSettings = Utils::Json::JsonToObsData(
|
||||||
|
request.RequestData["filterSettings"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSSourceAutoRelease filter = Utils::Obs::ActionHelper::CreateSourceFilter(source, filterName, filterKind, filterSettings);
|
OBSSourceAutoRelease filter =
|
||||||
|
Utils::Obs::ActionHelper::CreateSourceFilter(
|
||||||
|
source, filterName, filterKind, filterSettings);
|
||||||
|
|
||||||
if(!filter)
|
if (!filter)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the filter failed.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceCreationFailed,
|
||||||
|
"Creation of the filter failed.");
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -145,11 +164,12 @@ RequestResult RequestHandler::CreateSourceFilter(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::RemoveSourceFilter(const Request& request)
|
RequestResult RequestHandler::RemoveSourceFilter(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
FilterPair pair = request.ValidateFilter("sourceName", "filterName",
|
||||||
|
statusCode, comment);
|
||||||
if (!pair.filter)
|
if (!pair.filter)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -172,19 +192,24 @@ RequestResult RequestHandler::RemoveSourceFilter(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetSourceFilterName(const Request& request)
|
RequestResult RequestHandler::SetSourceFilterName(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
FilterPair pair = request.ValidateFilter("sourceName", "filterName",
|
||||||
if (!pair.filter || !request.ValidateString("newFilterName", statusCode, comment))
|
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(pair.source, newFilterName.c_str());
|
OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name(
|
||||||
|
pair.source, newFilterName.c_str());
|
||||||
if (existingFilter)
|
if (existingFilter)
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A filter already exists by that new name.");
|
return RequestResult::Error(
|
||||||
|
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());
|
||||||
|
|
||||||
@ -209,21 +234,27 @@ RequestResult RequestHandler::SetSourceFilterName(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSourceFilter(const Request& request)
|
RequestResult RequestHandler::GetSourceFilter(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
FilterPair pair = request.ValidateFilter("sourceName", "filterName",
|
||||||
|
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"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(pair.source, pair.filter); // Todo: Use `GetSourceFilterlist` to select this filter maybe
|
responseData["filterIndex"] =
|
||||||
|
Utils::Obs::NumberHelper::GetSourceFilterIndex(
|
||||||
|
pair.source,
|
||||||
|
pair.filter); // Todo: Use `GetSourceFilterlist` to select this filter maybe
|
||||||
responseData["filterKind"] = obs_source_get_id(pair.filter);
|
responseData["filterKind"] = obs_source_get_id(pair.filter);
|
||||||
|
|
||||||
OBSDataAutoRelease filterSettings = obs_source_get_settings(pair.filter);
|
OBSDataAutoRelease filterSettings =
|
||||||
responseData["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
|
obs_source_get_settings(pair.filter);
|
||||||
|
responseData["filterSettings"] =
|
||||||
|
Utils::Json::ObsDataToJson(filterSettings);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -242,17 +273,20 @@ RequestResult RequestHandler::GetSourceFilter(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetSourceFilterIndex(const Request& request)
|
RequestResult RequestHandler::SetSourceFilterIndex(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
FilterPair pair = request.ValidateFilter("sourceName", "filterName",
|
||||||
if (!(pair.filter && request.ValidateNumber("filterIndex", statusCode, comment, 0, 8192)))
|
statusCode, comment);
|
||||||
|
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, filterIndex);
|
Utils::Obs::ActionHelper::SetSourceFilterIndex(pair.source, pair.filter,
|
||||||
|
filterIndex);
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -272,27 +306,33 @@ RequestResult RequestHandler::SetSourceFilterIndex(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetSourceFilterSettings(const Request& request)
|
RequestResult RequestHandler::SetSourceFilterSettings(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
FilterPair pair = request.ValidateFilter("sourceName", "filterName",
|
||||||
if (!(pair.filter && request.ValidateObject("filterSettings", statusCode, comment, true)))
|
statusCode, comment);
|
||||||
|
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, comment))
|
if (!request.ValidateOptionalBoolean("overlay", statusCode,
|
||||||
|
comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
overlay = request.RequestData["overlay"];
|
overlay = request.RequestData["overlay"];
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
|
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(
|
||||||
|
request.RequestData["filterSettings"]);
|
||||||
if (!newSettings)
|
if (!newSettings)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestProcessingFailed,
|
||||||
|
"An internal data conversion operation failed. Please report this!");
|
||||||
|
|
||||||
if (overlay)
|
if (overlay)
|
||||||
obs_source_update(pair.filter, newSettings);
|
obs_source_update(pair.filter, newSettings);
|
||||||
@ -318,12 +358,14 @@ RequestResult RequestHandler::SetSourceFilterSettings(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category filters
|
* @category filters
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetSourceFilterEnabled(const Request& request)
|
RequestResult RequestHandler::SetSourceFilterEnabled(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
FilterPair pair = request.ValidateFilter("sourceName", "filterName",
|
||||||
if (!(pair.filter && request.ValidateBoolean("filterEnabled", statusCode, comment)))
|
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"];
|
||||||
|
@ -26,7 +26,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../WebSocketApi.h"
|
#include "../WebSocketApi.h"
|
||||||
#include "../obs-websocket.h"
|
#include "../obs-websocket.h"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets data about the current plugin and RPC version.
|
* Gets data about the current plugin and RPC version.
|
||||||
*
|
*
|
||||||
@ -45,7 +44,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @category general
|
* @category general
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetVersion(const Request&)
|
RequestResult RequestHandler::GetVersion(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["obsVersion"] = Utils::Obs::StringHelper::GetObsVersion();
|
responseData["obsVersion"] = Utils::Obs::StringHelper::GetObsVersion();
|
||||||
@ -53,15 +52,17 @@ 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 = QImageWriter::supportedImageFormats();
|
QList<QByteArray> imageWriterFormats =
|
||||||
|
QImageWriter::supportedImageFormats();
|
||||||
std::vector<std::string> supportedImageFormats;
|
std::vector<std::string> supportedImageFormats;
|
||||||
for (const QByteArray& format : imageWriterFormats) {
|
for (const QByteArray &format : imageWriterFormats) {
|
||||||
supportedImageFormats.push_back(format.toStdString());
|
supportedImageFormats.push_back(format.toStdString());
|
||||||
}
|
}
|
||||||
responseData["supportedImageFormats"] = supportedImageFormats;
|
responseData["supportedImageFormats"] = supportedImageFormats;
|
||||||
|
|
||||||
responseData["platform"] = QSysInfo::productType().toStdString();
|
responseData["platform"] = QSysInfo::productType().toStdString();
|
||||||
responseData["platformDescription"] = QSysInfo::prettyProductName().toStdString();
|
responseData["platformDescription"] =
|
||||||
|
QSysInfo::prettyProductName().toStdString();
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -88,13 +89,15 @@ RequestResult RequestHandler::GetVersion(const Request&)
|
|||||||
* @category general
|
* @category general
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetStats(const Request&)
|
RequestResult RequestHandler::GetStats(const Request &)
|
||||||
{
|
{
|
||||||
json responseData = Utils::Obs::ObjectHelper::GetStats();
|
json responseData = Utils::Obs::ObjectHelper::GetStats();
|
||||||
|
|
||||||
if (_session) {
|
if (_session) {
|
||||||
responseData["webSocketSessionIncomingMessages"] = _session->IncomingMessages();
|
responseData["webSocketSessionIncomingMessages"] =
|
||||||
responseData["webSocketSessionOutgoingMessages"] = _session->OutgoingMessages();
|
_session->IncomingMessages();
|
||||||
|
responseData["webSocketSessionOutgoingMessages"] =
|
||||||
|
_session->OutgoingMessages();
|
||||||
} else {
|
} else {
|
||||||
responseData["webSocketSessionIncomingMessages"] = nullptr;
|
responseData["webSocketSessionIncomingMessages"] = nullptr;
|
||||||
responseData["webSocketSessionOutgoingMessages"] = nullptr;
|
responseData["webSocketSessionOutgoingMessages"] = nullptr;
|
||||||
@ -115,7 +118,7 @@ RequestResult RequestHandler::GetStats(const Request&)
|
|||||||
* @category general
|
* @category general
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::BroadcastCustomEvent(const Request& request)
|
RequestResult RequestHandler::BroadcastCustomEvent(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
@ -124,9 +127,13 @@ RequestResult RequestHandler::BroadcastCustomEvent(const Request& request)
|
|||||||
|
|
||||||
auto webSocketServer = GetWebSocketServer();
|
auto webSocketServer = GetWebSocketServer();
|
||||||
if (!webSocketServer)
|
if (!webSocketServer)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to send event due to internal error.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestProcessingFailed,
|
||||||
|
"Unable to send event due to internal error.");
|
||||||
|
|
||||||
webSocketServer->BroadcastEvent(EventSubscription::General, "CustomEvent", request.RequestData["eventData"]);
|
webSocketServer->BroadcastEvent(EventSubscription::General,
|
||||||
|
"CustomEvent",
|
||||||
|
request.RequestData["eventData"]);
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -150,11 +157,12 @@ RequestResult RequestHandler::BroadcastCustomEvent(const Request& request)
|
|||||||
* @category general
|
* @category general
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::CallVendorRequest(const Request& request)
|
RequestResult RequestHandler::CallVendorRequest(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!request.ValidateString("vendorName", statusCode, comment) || !request.ValidateString("requestType", statusCode, comment))
|
if (!request.ValidateString("vendorName", statusCode, comment) ||
|
||||||
|
!request.ValidateString("requestType", statusCode, comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string vendorName = request.RequestData["vendorName"];
|
std::string vendorName = request.RequestData["vendorName"];
|
||||||
@ -162,31 +170,41 @@ 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, comment))
|
if (!request.ValidateOptionalObject("requestData", statusCode,
|
||||||
|
comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
requestData = Utils::Json::JsonToObsData(request.RequestData["requestData"]);
|
requestData = Utils::Json::JsonToObsData(
|
||||||
|
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(RequestStatus::RequestProcessingFailed, "Unable to call request due to internal error.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestProcessingFailed,
|
||||||
|
"Unable to call request due to internal error.");
|
||||||
|
|
||||||
auto ret = webSocketApi->PerformVendorRequest(vendorName, requestType, requestData, obsResponseData);
|
auto ret = webSocketApi->PerformVendorRequest(
|
||||||
|
vendorName, requestType, requestData, obsResponseData);
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
default:
|
default:
|
||||||
case WebSocketApi::RequestReturnCode::Normal:
|
case WebSocketApi::RequestReturnCode::Normal:
|
||||||
break;
|
break;
|
||||||
case WebSocketApi::RequestReturnCode::NoVendor:
|
case WebSocketApi::RequestReturnCode::NoVendor:
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "No vendor was found by that name.");
|
return RequestResult::Error(
|
||||||
case WebSocketApi::RequestReturnCode::NoVendorRequest:
|
RequestStatus::ResourceNotFound,
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "No request was found by that name.");
|
"No vendor was found by that name.");
|
||||||
|
case WebSocketApi::RequestReturnCode::NoVendorRequest:
|
||||||
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceNotFound,
|
||||||
|
"No request was found by that name.");
|
||||||
}
|
}
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["responseData"] = Utils::Json::ObsDataToJson(obsResponseData);
|
responseData["responseData"] =
|
||||||
|
Utils::Json::ObsDataToJson(obsResponseData);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -203,7 +221,7 @@ RequestResult RequestHandler::CallVendorRequest(const Request& request)
|
|||||||
* @category general
|
* @category general
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetHotkeyList(const Request&)
|
RequestResult RequestHandler::GetHotkeyList(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["hotkeys"] = Utils::Obs::ArrayHelper::GetHotkeyNameList();
|
responseData["hotkeys"] = Utils::Obs::ArrayHelper::GetHotkeyNameList();
|
||||||
@ -222,16 +240,19 @@ RequestResult RequestHandler::GetHotkeyList(const Request&)
|
|||||||
* @category general
|
* @category general
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::TriggerHotkeyByName(const Request& request)
|
RequestResult RequestHandler::TriggerHotkeyByName(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
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(request.RequestData["hotkeyName"]);
|
obs_hotkey_t *hotkey = Utils::Obs::SearchHelper::GetHotkeyByName(
|
||||||
|
request.RequestData["hotkeyName"]);
|
||||||
if (!hotkey)
|
if (!hotkey)
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "No hotkeys were found by that name.");
|
return RequestResult::Error(
|
||||||
|
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);
|
||||||
|
|
||||||
@ -255,7 +276,7 @@ RequestResult RequestHandler::TriggerHotkeyByName(const Request& request)
|
|||||||
* @category general
|
* @category general
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request& request)
|
RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request &request)
|
||||||
{
|
{
|
||||||
obs_key_combination_t combo = {0};
|
obs_key_combination_t combo = {0};
|
||||||
|
|
||||||
@ -263,7 +284,8 @@ 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, comment))
|
if (!request.ValidateOptionalString("keyId", statusCode,
|
||||||
|
comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string keyId = request.RequestData["keyId"];
|
std::string keyId = request.RequestData["keyId"];
|
||||||
@ -272,24 +294,37 @@ 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, comment, true))
|
if (!request.ValidateOptionalObject("keyModifiers", statusCode,
|
||||||
|
comment, true))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
const json keyModifiersJson = request.RequestData["keyModifiers"];
|
const json keyModifiersJson =
|
||||||
|
request.RequestData["keyModifiers"];
|
||||||
uint32_t keyModifiers = 0;
|
uint32_t keyModifiers = 0;
|
||||||
if (keyModifiersJson.contains("shift") && keyModifiersJson["shift"].is_boolean() && keyModifiersJson["shift"].get<bool>())
|
if (keyModifiersJson.contains("shift") &&
|
||||||
|
keyModifiersJson["shift"].is_boolean() &&
|
||||||
|
keyModifiersJson["shift"].get<bool>())
|
||||||
keyModifiers |= INTERACT_SHIFT_KEY;
|
keyModifiers |= INTERACT_SHIFT_KEY;
|
||||||
if (keyModifiersJson.contains("control") && keyModifiersJson["control"].is_boolean() && keyModifiersJson["control"].get<bool>())
|
if (keyModifiersJson.contains("control") &&
|
||||||
|
keyModifiersJson["control"].is_boolean() &&
|
||||||
|
keyModifiersJson["control"].get<bool>())
|
||||||
keyModifiers |= INTERACT_CONTROL_KEY;
|
keyModifiers |= INTERACT_CONTROL_KEY;
|
||||||
if (keyModifiersJson.contains("alt") && keyModifiersJson["alt"].is_boolean() && keyModifiersJson["alt"].get<bool>())
|
if (keyModifiersJson.contains("alt") &&
|
||||||
|
keyModifiersJson["alt"].is_boolean() &&
|
||||||
|
keyModifiersJson["alt"].get<bool>())
|
||||||
keyModifiers |= INTERACT_ALT_KEY;
|
keyModifiers |= INTERACT_ALT_KEY;
|
||||||
if (keyModifiersJson.contains("command") && keyModifiersJson["command"].is_boolean() && keyModifiersJson["command"].get<bool>())
|
if (keyModifiersJson.contains("command") &&
|
||||||
|
keyModifiersJson["command"].is_boolean() &&
|
||||||
|
keyModifiersJson["command"].get<bool>())
|
||||||
keyModifiers |= INTERACT_COMMAND_KEY;
|
keyModifiers |= INTERACT_COMMAND_KEY;
|
||||||
combo.modifiers = keyModifiers;
|
combo.modifiers = keyModifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!combo.modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE))
|
if (!combo.modifiers &&
|
||||||
return RequestResult::Error(RequestStatus::CannotAct, "Your provided request fields cannot be used to trigger a hotkey.");
|
(combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE))
|
||||||
|
return RequestResult::Error(
|
||||||
|
RequestStatus::CannotAct,
|
||||||
|
"Your provided request fields cannot be used to trigger a hotkey.");
|
||||||
|
|
||||||
// Apparently things break when you don't start by setting the combo to false
|
// Apparently things break when you don't start by setting the combo to false
|
||||||
obs_hotkey_inject_event(combo, false);
|
obs_hotkey_inject_event(combo, false);
|
||||||
@ -312,24 +347,30 @@ RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request& request)
|
|||||||
* @category general
|
* @category general
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::Sleep(const Request& request)
|
RequestResult RequestHandler::Sleep(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
|
|
||||||
if (request.ExecutionType == RequestBatchExecutionType::SerialRealtime) {
|
if (request.ExecutionType ==
|
||||||
if (!request.ValidateNumber("sleepMillis", statusCode, comment, 0, 50000))
|
RequestBatchExecutionType::SerialRealtime) {
|
||||||
|
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::chrono::milliseconds(sleepMillis));
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::milliseconds(sleepMillis));
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
} else if (request.ExecutionType == RequestBatchExecutionType::SerialFrame) {
|
} else if (request.ExecutionType ==
|
||||||
if (!request.ValidateNumber("sleepFrames", statusCode, comment, 0, 10000))
|
RequestBatchExecutionType::SerialFrame) {
|
||||||
|
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(RequestStatus::UnsupportedRequestBatchExecutionType);
|
return RequestResult::Error(
|
||||||
|
RequestStatus::UnsupportedRequestBatchExecutionType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,21 +33,23 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetInputList(const Request& request)
|
RequestResult RequestHandler::GetInputList(const Request &request)
|
||||||
{
|
{
|
||||||
std::string inputKind;
|
std::string inputKind;
|
||||||
|
|
||||||
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, comment))
|
if (!request.ValidateOptionalString("inputKind", statusCode,
|
||||||
|
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"] = Utils::Obs::ArrayHelper::GetInputList(inputKind);
|
responseData["inputs"] =
|
||||||
|
Utils::Obs::ArrayHelper::GetInputList(inputKind);
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,21 +67,23 @@ RequestResult RequestHandler::GetInputList(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetInputKindList(const Request& request)
|
RequestResult RequestHandler::GetInputKindList(const Request &request)
|
||||||
{
|
{
|
||||||
bool unversioned = false;
|
bool unversioned = false;
|
||||||
|
|
||||||
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, comment))
|
if (!request.ValidateOptionalBoolean("unversioned", statusCode,
|
||||||
|
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"] = Utils::Obs::ArrayHelper::GetInputKindList(unversioned);
|
responseData["inputKinds"] =
|
||||||
|
Utils::Obs::ArrayHelper::GetInputKindList(unversioned);
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,11 +104,12 @@ RequestResult RequestHandler::GetInputKindList(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSpecialInputs(const Request&)
|
RequestResult RequestHandler::GetSpecialInputs(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
|
|
||||||
std::vector<std::string> channels = {"desktop1", "desktop2", "mic1", "mic2", "mic3", "mic4"};
|
std::vector<std::string> channels = {"desktop1", "desktop2", "mic1",
|
||||||
|
"mic2", "mic3", "mic4"};
|
||||||
|
|
||||||
size_t channelId = 1;
|
size_t channelId = 1;
|
||||||
for (auto &channel : channels) {
|
for (auto &channel : channels) {
|
||||||
@ -138,47 +143,63 @@ RequestResult RequestHandler::GetSpecialInputs(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::CreateInput(const Request& request)
|
RequestResult RequestHandler::CreateInput(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment);
|
OBSSourceAutoRelease sceneSource =
|
||||||
if (!(sceneSource && request.ValidateString("inputName", statusCode, comment) && request.ValidateString("inputKind", statusCode, comment)))
|
request.ValidateScene("sceneName", statusCode, comment);
|
||||||
|
if (!(sceneSource &&
|
||||||
|
request.ValidateString("inputName", statusCode, comment) &&
|
||||||
|
request.ValidateString("inputKind", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string inputName = request.RequestData["inputName"];
|
std::string inputName = request.RequestData["inputName"];
|
||||||
OBSSourceAutoRelease existingInput = obs_get_source_by_name(inputName.c_str());
|
OBSSourceAutoRelease existingInput =
|
||||||
|
obs_get_source_by_name(inputName.c_str());
|
||||||
if (existingInput)
|
if (existingInput)
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that input name.");
|
return RequestResult::Error(
|
||||||
|
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();
|
||||||
if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end())
|
if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end())
|
||||||
return RequestResult::Error(RequestStatus::InvalidInputKind, "Your specified input kind is not supported by OBS. Check that your specified kind is properly versioned and that any necessary plugins are loaded.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidInputKind,
|
||||||
|
"Your specified input kind is not supported by OBS. Check that your specified kind is properly versioned and that any necessary plugins are loaded.");
|
||||||
|
|
||||||
OBSDataAutoRelease inputSettings = nullptr;
|
OBSDataAutoRelease inputSettings = nullptr;
|
||||||
if (request.Contains("inputSettings")) {
|
if (request.Contains("inputSettings")) {
|
||||||
if (!request.ValidateOptionalObject("inputSettings", statusCode, comment, true))
|
if (!request.ValidateOptionalObject("inputSettings", statusCode,
|
||||||
|
comment, true))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
inputSettings = Utils::Json::JsonToObsData(request.RequestData["inputSettings"]);
|
inputSettings = Utils::Json::JsonToObsData(
|
||||||
|
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", statusCode, comment))
|
if (!request.ValidateOptionalBoolean("sceneItemEnabled",
|
||||||
|
statusCode, comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
sceneItemEnabled = request.RequestData["sceneItemEnabled"];
|
sceneItemEnabled = request.RequestData["sceneItemEnabled"];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the input and add it as a scene item to the destination scene
|
// Create the input and add it as a scene item to the destination scene
|
||||||
OBSSceneItemAutoRelease sceneItem = Utils::Obs::ActionHelper::CreateInput(inputName, inputKind, inputSettings, scene, sceneItemEnabled);
|
OBSSceneItemAutoRelease sceneItem =
|
||||||
|
Utils::Obs::ActionHelper::CreateInput(inputName, inputKind,
|
||||||
|
inputSettings, scene,
|
||||||
|
sceneItemEnabled);
|
||||||
|
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the input or scene item failed.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceCreationFailed,
|
||||||
|
"Creation of the input or scene item failed.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
responseData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
||||||
@ -199,11 +220,12 @@ RequestResult RequestHandler::CreateInput(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::RemoveInput(const Request& request)
|
RequestResult RequestHandler::RemoveInput(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
request.ValidateInput("inputName", statusCode, comment);
|
||||||
if (!input)
|
if (!input)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -228,19 +250,24 @@ RequestResult RequestHandler::RemoveInput(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetInputName(const Request& request)
|
RequestResult RequestHandler::SetInputName(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!(input && request.ValidateString("newInputName", statusCode, comment)))
|
request.ValidateInput("inputName", 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 = obs_get_source_by_name(newInputName.c_str());
|
OBSSourceAutoRelease existingSource =
|
||||||
|
obs_get_source_by_name(newInputName.c_str());
|
||||||
if (existingSource)
|
if (existingSource)
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that new input name.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceAlreadyExists,
|
||||||
|
"A source already exists by that new input name.");
|
||||||
|
|
||||||
obs_source_set_name(input, newInputName.c_str());
|
obs_source_set_name(input, newInputName.c_str());
|
||||||
|
|
||||||
@ -261,7 +288,7 @@ RequestResult RequestHandler::SetInputName(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetInputDefaultSettings(const Request& request)
|
RequestResult RequestHandler::GetInputDefaultSettings(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
@ -273,12 +300,14 @@ 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 = obs_get_source_defaults(inputKind.c_str());
|
OBSDataAutoRelease defaultSettings =
|
||||||
|
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"] = Utils::Json::ObsDataToJson(defaultSettings, true);
|
responseData["defaultInputSettings"] =
|
||||||
|
Utils::Json::ObsDataToJson(defaultSettings, true);
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,18 +328,20 @@ RequestResult RequestHandler::GetInputDefaultSettings(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetInputSettings(const Request& request)
|
RequestResult RequestHandler::GetInputSettings(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
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"] = Utils::Json::ObsDataToJson(inputSettings);
|
responseData["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);
|
||||||
}
|
}
|
||||||
@ -329,27 +360,33 @@ RequestResult RequestHandler::GetInputSettings(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetInputSettings(const Request& request)
|
RequestResult RequestHandler::SetInputSettings(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!(input && request.ValidateObject("inputSettings", statusCode, comment, true)))
|
request.ValidateInput("inputName", statusCode, comment);
|
||||||
|
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, comment))
|
if (!request.ValidateOptionalBoolean("overlay", statusCode,
|
||||||
|
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(request.RequestData["inputSettings"]);
|
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(
|
||||||
|
request.RequestData["inputSettings"]);
|
||||||
if (!newSettings)
|
if (!newSettings)
|
||||||
// This should never happen
|
// This should never happen
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestProcessingFailed,
|
||||||
|
"An internal data conversion operation failed. Please report this!");
|
||||||
|
|
||||||
if (overlay)
|
if (overlay)
|
||||||
// Applies the new settings on top of the existing user settings
|
// Applies the new settings on top of the existing user settings
|
||||||
@ -378,16 +415,19 @@ RequestResult RequestHandler::SetInputSettings(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetInputMute(const Request& request)
|
RequestResult RequestHandler::GetInputMute(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"The specified input does not support audio.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["inputMuted"] = obs_source_muted(input);
|
responseData["inputMuted"] = obs_source_muted(input);
|
||||||
@ -407,16 +447,20 @@ RequestResult RequestHandler::GetInputMute(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetInputMute(const Request& request)
|
RequestResult RequestHandler::SetInputMute(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!(input && request.ValidateBoolean("inputMuted", statusCode, comment)))
|
request.ValidateInput("inputName", 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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
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"]);
|
||||||
|
|
||||||
@ -437,16 +481,19 @@ RequestResult RequestHandler::SetInputMute(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::ToggleInputMute(const Request& request)
|
RequestResult RequestHandler::ToggleInputMute(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
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);
|
||||||
@ -471,16 +518,19 @@ RequestResult RequestHandler::ToggleInputMute(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetInputVolume(const Request& request)
|
RequestResult RequestHandler::GetInputVolume(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
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);
|
||||||
@ -507,36 +557,46 @@ RequestResult RequestHandler::GetInputVolume(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetInputVolume(const Request& request)
|
RequestResult RequestHandler::SetInputVolume(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"The specified input does not support audio.");
|
||||||
|
|
||||||
bool hasMul = request.Contains("inputVolumeMul");
|
bool hasMul = request.Contains("inputVolumeMul");
|
||||||
if (hasMul && !request.ValidateOptionalNumber("inputVolumeMul", statusCode, comment, 0, 20))
|
if (hasMul && !request.ValidateOptionalNumber(
|
||||||
|
"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("inputVolumeDb", statusCode, comment, -100, 26))
|
if (hasDb && !request.ValidateOptionalNumber(
|
||||||
|
"inputVolumeDb", statusCode, comment, -100, 26))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (hasMul && hasDb)
|
if (hasMul && hasDb)
|
||||||
return RequestResult::Error(RequestStatus::TooManyRequestFields, "You may only specify one volume field.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::TooManyRequestFields,
|
||||||
|
"You may only specify one volume field.");
|
||||||
|
|
||||||
if (!hasMul && !hasDb)
|
if (!hasMul && !hasDb)
|
||||||
return RequestResult::Error(RequestStatus::MissingRequestField, "You must specify one volume field.");
|
return RequestResult::Error(
|
||||||
|
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 = obs_db_to_mul(request.RequestData["inputVolumeDb"]);
|
inputVolumeMul =
|
||||||
|
obs_db_to_mul(request.RequestData["inputVolumeDb"]);
|
||||||
|
|
||||||
obs_source_set_volume(input, inputVolumeMul);
|
obs_source_set_volume(input, inputVolumeMul);
|
||||||
|
|
||||||
@ -557,16 +617,19 @@ RequestResult RequestHandler::SetInputVolume(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetInputAudioBalance(const Request& request)
|
RequestResult RequestHandler::GetInputAudioBalance(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"The specified input does not support audio.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["inputAudioBalance"] = obs_source_get_balance_value(input);
|
responseData["inputAudioBalance"] = obs_source_get_balance_value(input);
|
||||||
@ -587,16 +650,20 @@ RequestResult RequestHandler::GetInputAudioBalance(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetInputAudioBalance(const Request& request)
|
RequestResult RequestHandler::SetInputAudioBalance(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!(input && request.ValidateNumber("inputAudioBalance", statusCode, comment, 0.0, 1.0)))
|
request.ValidateInput("inputName", statusCode, comment);
|
||||||
|
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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
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);
|
||||||
@ -620,20 +687,24 @@ RequestResult RequestHandler::SetInputAudioBalance(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetInputAudioSyncOffset(const Request& request)
|
RequestResult RequestHandler::GetInputAudioSyncOffset(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"The specified input does not support audio.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
// Offset is stored in nanoseconds in OBS.
|
// Offset is stored in nanoseconds in OBS.
|
||||||
responseData["inputAudioSyncOffset"] = obs_source_get_sync_offset(input) / 1000000;
|
responseData["inputAudioSyncOffset"] =
|
||||||
|
obs_source_get_sync_offset(input) / 1000000;
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -651,16 +722,21 @@ RequestResult RequestHandler::GetInputAudioSyncOffset(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetInputAudioSyncOffset(const Request& request)
|
RequestResult RequestHandler::SetInputAudioSyncOffset(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!(input && request.ValidateNumber("inputAudioSyncOffset", statusCode, comment, -950, 20000)))
|
request.ValidateInput("inputName", statusCode, comment);
|
||||||
|
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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
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);
|
||||||
@ -688,19 +764,23 @@ RequestResult RequestHandler::SetInputAudioSyncOffset(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetInputAudioMonitorType(const Request& request)
|
RequestResult RequestHandler::GetInputAudioMonitorType(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"The specified input does not support audio.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["monitorType"] = Utils::Obs::StringHelper::GetInputMonitorType(input);
|
responseData["monitorType"] =
|
||||||
|
Utils::Obs::StringHelper::GetInputMonitorType(input);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -718,19 +798,25 @@ RequestResult RequestHandler::GetInputAudioMonitorType(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
|
RequestResult RequestHandler::SetInputAudioMonitorType(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!(input && request.ValidateString("monitorType", statusCode, comment)))
|
request.ValidateInput("inputName", 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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"The specified input does not support audio.");
|
||||||
|
|
||||||
if (!obs_audio_monitoring_available())
|
if (!obs_audio_monitoring_available())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Audio monitoring is not available on this platform.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"Audio monitoring is not available on this platform.");
|
||||||
|
|
||||||
enum obs_monitoring_type monitorType;
|
enum obs_monitoring_type monitorType;
|
||||||
std::string monitorTypeString = request.RequestData["monitorType"];
|
std::string monitorTypeString = request.RequestData["monitorType"];
|
||||||
@ -741,7 +827,10 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
|
|||||||
else if (monitorTypeString == "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT")
|
else if (monitorTypeString == "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT")
|
||||||
monitorType = OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT;
|
monitorType = OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT;
|
||||||
else
|
else
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField, std::string("Unknown monitor type: ") + monitorTypeString);
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidRequestField,
|
||||||
|
std::string("Unknown monitor type: ") +
|
||||||
|
monitorTypeString);
|
||||||
|
|
||||||
obs_source_set_monitoring_type(input, monitorType);
|
obs_source_set_monitoring_type(input, monitorType);
|
||||||
|
|
||||||
@ -762,22 +851,26 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetInputAudioTracks(const Request& request)
|
RequestResult RequestHandler::GetInputAudioTracks(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
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)] = (bool)((tracks >> i) & 1);
|
inputAudioTracks[std::to_string(i + 1)] =
|
||||||
|
(bool)((tracks >> i) & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
@ -799,16 +892,20 @@ RequestResult RequestHandler::GetInputAudioTracks(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetInputAudioTracks(const Request& request)
|
RequestResult RequestHandler::SetInputAudioTracks(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!input || !request.ValidateObject("inputAudioTracks", statusCode, comment))
|
request.ValidateInput("inputName", 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(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"The specified input does not support audio.");
|
||||||
|
|
||||||
json inputAudioTracks = request.RequestData["inputAudioTracks"];
|
json inputAudioTracks = request.RequestData["inputAudioTracks"];
|
||||||
|
|
||||||
@ -821,7 +918,9 @@ RequestResult RequestHandler::SetInputAudioTracks(const Request& request)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!inputAudioTracks[track].is_boolean())
|
if (!inputAudioTracks[track].is_boolean())
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The value of one of your tracks is not a boolean.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidRequestFieldType,
|
||||||
|
"The value of one of your tracks is not a boolean.");
|
||||||
|
|
||||||
bool enabled = inputAudioTracks[track];
|
bool enabled = inputAudioTracks[track];
|
||||||
|
|
||||||
@ -854,25 +953,34 @@ RequestResult RequestHandler::SetInputAudioTracks(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetInputPropertiesListPropertyItems(const Request& request)
|
RequestResult
|
||||||
|
RequestHandler::GetInputPropertiesListPropertyItems(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!(input && request.ValidateString("propertyName", statusCode, comment)))
|
request.ValidateInput("inputName", 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_properties_get(inputProperties, propertyName.c_str());
|
obs_property_t *property =
|
||||||
|
obs_properties_get(inputProperties, propertyName.c_str());
|
||||||
if (!property)
|
if (!property)
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "Unable to find a property by that name.");
|
return RequestResult::Error(
|
||||||
|
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(RequestStatus::InvalidResourceType, "The property found is not a list.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceType,
|
||||||
|
"The property found is not a list.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["propertyItems"] = Utils::Obs::ArrayHelper::GetListPropertyItems(property);
|
responseData["propertyItems"] =
|
||||||
|
Utils::Obs::ArrayHelper::GetListPropertyItems(property);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -892,24 +1000,33 @@ RequestResult RequestHandler::GetInputPropertiesListPropertyItems(const Request&
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category inputs
|
* @category inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::PressInputPropertiesButton(const Request& request)
|
RequestResult RequestHandler::PressInputPropertiesButton(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!(input && request.ValidateString("propertyName", statusCode, comment)))
|
request.ValidateInput("inputName", 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_properties_get(inputProperties, propertyName.c_str());
|
obs_property_t *property =
|
||||||
|
obs_properties_get(inputProperties, propertyName.c_str());
|
||||||
if (!property)
|
if (!property)
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "Unable to find a property by that name.");
|
return RequestResult::Error(
|
||||||
|
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(RequestStatus::InvalidResourceType, "The property found is not a button.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceType,
|
||||||
|
"The property found is not a button.");
|
||||||
if (!obs_property_enabled(property))
|
if (!obs_property_enabled(property))
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The property item found is not enabled.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"The property item found is not enabled.");
|
||||||
|
|
||||||
obs_property_button_clicked(property, input);
|
obs_property_button_clicked(property, input);
|
||||||
|
|
||||||
|
@ -22,7 +22,8 @@ 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 || mediaState == OBS_MEDIA_STATE_PAUSED;
|
return mediaState == OBS_MEDIA_STATE_PLAYING ||
|
||||||
|
mediaState == OBS_MEDIA_STATE_PAUSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,19 +53,22 @@ bool IsMediaTimeValid(obs_source_t *input)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category media inputs
|
* @category media inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetMediaInputStatus(const Request& request)
|
RequestResult RequestHandler::GetMediaInputStatus(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
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"] = Utils::Obs::StringHelper::GetMediaInputState(input);
|
responseData["mediaState"] =
|
||||||
|
Utils::Obs::StringHelper::GetMediaInputState(input);
|
||||||
|
|
||||||
if (IsMediaTimeValid(input)) {
|
if (IsMediaTimeValid(input)) {
|
||||||
responseData["mediaDuration"] = obs_source_media_get_duration(input);
|
responseData["mediaDuration"] =
|
||||||
|
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;
|
||||||
@ -89,16 +93,20 @@ RequestResult RequestHandler::GetMediaInputStatus(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category media inputs
|
* @category media inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetMediaInputCursor(const Request& request)
|
RequestResult RequestHandler::SetMediaInputCursor(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!(input && request.ValidateNumber("mediaCursor", statusCode, comment, 0)))
|
request.ValidateInput("inputName", statusCode, comment);
|
||||||
|
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(RequestStatus::InvalidResourceState, "The media input must be playing or paused in order to set the cursor position.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"The media input must be playing or paused in order to set the cursor position.");
|
||||||
|
|
||||||
int64_t mediaCursor = request.RequestData["mediaCursor"];
|
int64_t mediaCursor = request.RequestData["mediaCursor"];
|
||||||
|
|
||||||
@ -123,19 +131,24 @@ RequestResult RequestHandler::SetMediaInputCursor(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category media inputs
|
* @category media inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::OffsetMediaInputCursor(const Request& request)
|
RequestResult RequestHandler::OffsetMediaInputCursor(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!(input && request.ValidateNumber("mediaCursorOffset", statusCode, comment)))
|
request.ValidateInput("inputName", 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(RequestStatus::InvalidResourceState, "The media input must be playing or paused in order to set the cursor position.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"The media input must be playing or paused in order to set the cursor position.");
|
||||||
|
|
||||||
int64_t mediaCursorOffset = request.RequestData["mediaCursorOffset"];
|
int64_t mediaCursorOffset = request.RequestData["mediaCursorOffset"];
|
||||||
int64_t mediaCursor = obs_source_media_get_time(input) + mediaCursorOffset;
|
int64_t mediaCursor =
|
||||||
|
obs_source_media_get_time(input) + mediaCursorOffset;
|
||||||
|
|
||||||
if (mediaCursor < 0)
|
if (mediaCursor < 0)
|
||||||
mediaCursor = 0;
|
mediaCursor = 0;
|
||||||
@ -158,41 +171,46 @@ RequestResult RequestHandler::OffsetMediaInputCursor(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category media inputs
|
* @category media inputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::TriggerMediaInputAction(const Request& request)
|
RequestResult RequestHandler::TriggerMediaInputAction(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
if (!(input && request.ValidateString("mediaAction", statusCode, comment)))
|
request.ValidateInput("inputName", 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 = Utils::Obs::EnumHelper::GetMediaInputAction(mediaActionString);
|
auto mediaAction =
|
||||||
|
Utils::Obs::EnumHelper::GetMediaInputAction(mediaActionString);
|
||||||
|
|
||||||
switch (mediaAction) {
|
switch (mediaAction) {
|
||||||
default:
|
default:
|
||||||
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE:
|
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE:
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField, "You have specified an invalid media input action.");
|
return RequestResult::Error(
|
||||||
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY:
|
RequestStatus::InvalidRequestField,
|
||||||
// Shoutout to whoever implemented this API call like this
|
"You have specified an invalid media input action.");
|
||||||
obs_source_media_play_pause(input, false);
|
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY:
|
||||||
break;
|
// Shoutout to whoever implemented this API call like this
|
||||||
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE:
|
obs_source_media_play_pause(input, false);
|
||||||
obs_source_media_play_pause(input, true);
|
break;
|
||||||
break;
|
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE:
|
||||||
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP:
|
obs_source_media_play_pause(input, true);
|
||||||
obs_source_media_stop(input);
|
break;
|
||||||
break;
|
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP:
|
||||||
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART:
|
obs_source_media_stop(input);
|
||||||
// I'm only implementing this because I'm nice. I think its a really dumb action.
|
break;
|
||||||
obs_source_media_restart(input);
|
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART:
|
||||||
break;
|
// I'm only implementing this because I'm nice. I think its a really dumb action.
|
||||||
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT:
|
obs_source_media_restart(input);
|
||||||
obs_source_media_next(input);
|
break;
|
||||||
break;
|
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT:
|
||||||
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS:
|
obs_source_media_next(input);
|
||||||
obs_source_media_previous(input);
|
break;
|
||||||
break;
|
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS:
|
||||||
|
obs_source_media_previous(input);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
|
@ -46,10 +46,11 @@ static bool ReplayBufferAvailable()
|
|||||||
* @category outputs
|
* @category outputs
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetVirtualCamStatus(const Request&)
|
RequestResult RequestHandler::GetVirtualCamStatus(const Request &)
|
||||||
{
|
{
|
||||||
if (!VirtualCamAvailable())
|
if (!VirtualCamAvailable())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"VirtualCam is not available.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["outputActive"] = obs_frontend_virtualcam_active();
|
responseData["outputActive"] = obs_frontend_virtualcam_active();
|
||||||
@ -68,10 +69,11 @@ RequestResult RequestHandler::GetVirtualCamStatus(const Request&)
|
|||||||
* @category outputs
|
* @category outputs
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::ToggleVirtualCam(const Request&)
|
RequestResult RequestHandler::ToggleVirtualCam(const Request &)
|
||||||
{
|
{
|
||||||
if (!VirtualCamAvailable())
|
if (!VirtualCamAvailable())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"VirtualCam is not available.");
|
||||||
|
|
||||||
bool outputActive = obs_frontend_virtualcam_active();
|
bool outputActive = obs_frontend_virtualcam_active();
|
||||||
|
|
||||||
@ -95,10 +97,11 @@ RequestResult RequestHandler::ToggleVirtualCam(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category outputs
|
* @category outputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::StartVirtualCam(const Request&)
|
RequestResult RequestHandler::StartVirtualCam(const Request &)
|
||||||
{
|
{
|
||||||
if (!VirtualCamAvailable())
|
if (!VirtualCamAvailable())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"VirtualCam is not available.");
|
||||||
|
|
||||||
if (obs_frontend_virtualcam_active())
|
if (obs_frontend_virtualcam_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning);
|
return RequestResult::Error(RequestStatus::OutputRunning);
|
||||||
@ -118,10 +121,11 @@ RequestResult RequestHandler::StartVirtualCam(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category outputs
|
* @category outputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::StopVirtualCam(const Request&)
|
RequestResult RequestHandler::StopVirtualCam(const Request &)
|
||||||
{
|
{
|
||||||
if (!VirtualCamAvailable())
|
if (!VirtualCamAvailable())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"VirtualCam is not available.");
|
||||||
|
|
||||||
if (!obs_frontend_virtualcam_active())
|
if (!obs_frontend_virtualcam_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
||||||
@ -143,10 +147,11 @@ RequestResult RequestHandler::StopVirtualCam(const Request&)
|
|||||||
* @category outputs
|
* @category outputs
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetReplayBufferStatus(const Request&)
|
RequestResult RequestHandler::GetReplayBufferStatus(const Request &)
|
||||||
{
|
{
|
||||||
if (!ReplayBufferAvailable())
|
if (!ReplayBufferAvailable())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"Replay buffer is not available.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["outputActive"] = obs_frontend_replay_buffer_active();
|
responseData["outputActive"] = obs_frontend_replay_buffer_active();
|
||||||
@ -165,10 +170,11 @@ RequestResult RequestHandler::GetReplayBufferStatus(const Request&)
|
|||||||
* @category outputs
|
* @category outputs
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::ToggleReplayBuffer(const Request&)
|
RequestResult RequestHandler::ToggleReplayBuffer(const Request &)
|
||||||
{
|
{
|
||||||
if (!ReplayBufferAvailable())
|
if (!ReplayBufferAvailable())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"Replay buffer is not available.");
|
||||||
|
|
||||||
bool outputActive = obs_frontend_replay_buffer_active();
|
bool outputActive = obs_frontend_replay_buffer_active();
|
||||||
|
|
||||||
@ -192,10 +198,11 @@ RequestResult RequestHandler::ToggleReplayBuffer(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category outputs
|
* @category outputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::StartReplayBuffer(const Request&)
|
RequestResult RequestHandler::StartReplayBuffer(const Request &)
|
||||||
{
|
{
|
||||||
if (!ReplayBufferAvailable())
|
if (!ReplayBufferAvailable())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"Replay buffer is not available.");
|
||||||
|
|
||||||
if (obs_frontend_replay_buffer_active())
|
if (obs_frontend_replay_buffer_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning);
|
return RequestResult::Error(RequestStatus::OutputRunning);
|
||||||
@ -215,10 +222,11 @@ RequestResult RequestHandler::StartReplayBuffer(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category outputs
|
* @category outputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::StopReplayBuffer(const Request&)
|
RequestResult RequestHandler::StopReplayBuffer(const Request &)
|
||||||
{
|
{
|
||||||
if (!ReplayBufferAvailable())
|
if (!ReplayBufferAvailable())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"Replay buffer is not available.");
|
||||||
|
|
||||||
if (!obs_frontend_replay_buffer_active())
|
if (!obs_frontend_replay_buffer_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
||||||
@ -238,10 +246,11 @@ RequestResult RequestHandler::StopReplayBuffer(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category outputs
|
* @category outputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SaveReplayBuffer(const Request&)
|
RequestResult RequestHandler::SaveReplayBuffer(const Request &)
|
||||||
{
|
{
|
||||||
if (!ReplayBufferAvailable())
|
if (!ReplayBufferAvailable())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"Replay buffer is not available.");
|
||||||
|
|
||||||
if (!obs_frontend_replay_buffer_active())
|
if (!obs_frontend_replay_buffer_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
||||||
@ -263,15 +272,17 @@ RequestResult RequestHandler::SaveReplayBuffer(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category outputs
|
* @category outputs
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetLastReplayBufferReplay(const Request&)
|
RequestResult RequestHandler::GetLastReplayBufferReplay(const Request &)
|
||||||
{
|
{
|
||||||
if (!ReplayBufferAvailable())
|
if (!ReplayBufferAvailable())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"Replay buffer is not available.");
|
||||||
|
|
||||||
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"] = Utils::Obs::StringHelper::GetLastReplayBufferFilePath();
|
responseData["savedReplayPath"] =
|
||||||
|
Utils::Obs::StringHelper::GetLastReplayBufferFilePath();
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
@ -35,18 +35,21 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category record
|
* @category record
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetRecordStatus(const Request&)
|
RequestResult RequestHandler::GetRecordStatus(const Request &)
|
||||||
{
|
{
|
||||||
OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output();
|
OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output();
|
||||||
|
|
||||||
uint64_t outputDuration = Utils::Obs::NumberHelper::GetOutputDuration(recordOutput);
|
uint64_t outputDuration =
|
||||||
|
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"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
|
responseData["outputTimecode"] =
|
||||||
|
Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
|
||||||
responseData["outputDuration"] = outputDuration;
|
responseData["outputDuration"] = outputDuration;
|
||||||
responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(recordOutput);
|
responseData["outputBytes"] =
|
||||||
|
(uint64_t)obs_output_get_total_bytes(recordOutput);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -61,7 +64,7 @@ RequestResult RequestHandler::GetRecordStatus(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category record
|
* @category record
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::ToggleRecord(const Request&)
|
RequestResult RequestHandler::ToggleRecord(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
if (obs_frontend_recording_active()) {
|
if (obs_frontend_recording_active()) {
|
||||||
@ -85,7 +88,7 @@ RequestResult RequestHandler::ToggleRecord(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category record
|
* @category record
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::StartRecord(const Request&)
|
RequestResult RequestHandler::StartRecord(const Request &)
|
||||||
{
|
{
|
||||||
if (obs_frontend_recording_active())
|
if (obs_frontend_recording_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning);
|
return RequestResult::Error(RequestStatus::OutputRunning);
|
||||||
@ -106,7 +109,7 @@ RequestResult RequestHandler::StartRecord(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category record
|
* @category record
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::StopRecord(const Request&)
|
RequestResult RequestHandler::StopRecord(const Request &)
|
||||||
{
|
{
|
||||||
if (!obs_frontend_recording_active())
|
if (!obs_frontend_recording_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
||||||
@ -127,7 +130,7 @@ RequestResult RequestHandler::StopRecord(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category record
|
* @category record
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::ToggleRecordPause(const Request&)
|
RequestResult RequestHandler::ToggleRecordPause(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
if (obs_frontend_recording_paused()) {
|
if (obs_frontend_recording_paused()) {
|
||||||
@ -151,7 +154,7 @@ RequestResult RequestHandler::ToggleRecordPause(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category record
|
* @category record
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::PauseRecord(const Request&)
|
RequestResult RequestHandler::PauseRecord(const Request &)
|
||||||
{
|
{
|
||||||
if (obs_frontend_recording_paused())
|
if (obs_frontend_recording_paused())
|
||||||
return RequestResult::Error(RequestStatus::OutputPaused);
|
return RequestResult::Error(RequestStatus::OutputPaused);
|
||||||
@ -172,7 +175,7 @@ RequestResult RequestHandler::PauseRecord(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category record
|
* @category record
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::ResumeRecord(const Request&)
|
RequestResult RequestHandler::ResumeRecord(const Request &)
|
||||||
{
|
{
|
||||||
if (!obs_frontend_recording_paused())
|
if (!obs_frontend_recording_paused())
|
||||||
return RequestResult::Error(RequestStatus::OutputNotPaused);
|
return RequestResult::Error(RequestStatus::OutputNotPaused);
|
||||||
|
@ -35,16 +35,18 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemList(const Request& request)
|
RequestResult RequestHandler::GetSceneItemList(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
|
OBSSourceAutoRelease scene =
|
||||||
|
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(obs_scene_from_source(scene));
|
responseData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList(
|
||||||
|
obs_scene_from_source(scene));
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -67,16 +69,19 @@ RequestResult RequestHandler::GetSceneItemList(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetGroupSceneItemList(const Request& request)
|
RequestResult RequestHandler::GetGroupSceneItemList(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY);
|
OBSSourceAutoRelease scene =
|
||||||
|
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(obs_group_from_source(scene));
|
responseData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList(
|
||||||
|
obs_group_from_source(scene));
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -99,26 +104,34 @@ RequestResult RequestHandler::GetGroupSceneItemList(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemId(const Request& request)
|
RequestResult RequestHandler::GetSceneItemId(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneAutoRelease scene = request.ValidateScene2("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneAutoRelease scene = request.ValidateScene2(
|
||||||
if (!(scene && request.ValidateString("sourceName", statusCode, comment)))
|
"sceneName", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
|
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, comment, -1))
|
if (!request.ValidateOptionalNumber("searchOffset", statusCode,
|
||||||
|
comment, -1))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
offset = request.RequestData["searchOffset"];
|
offset = request.RequestData["searchOffset"];
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName, offset);
|
OBSSceneItemAutoRelease item =
|
||||||
|
Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName,
|
||||||
|
offset);
|
||||||
if (!item)
|
if (!item)
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene items were found in the specified scene by that name or offset.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceNotFound,
|
||||||
|
"No scene items were found in the specified scene by that name or offset.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItemId"] = obs_sceneitem_get_id(item);
|
responseData["sceneItemId"] = obs_sceneitem_get_id(item);
|
||||||
@ -144,33 +157,43 @@ RequestResult RequestHandler::GetSceneItemId(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::CreateSceneItem(const Request& request)
|
RequestResult RequestHandler::CreateSceneItem(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment);
|
OBSSourceAutoRelease sceneSource =
|
||||||
|
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 = request.ValidateSource("sourceName", statusCode, comment);
|
OBSSourceAutoRelease source =
|
||||||
|
request.ValidateSource("sourceName", statusCode, comment);
|
||||||
if (!source)
|
if (!source)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (request.RequestData["sceneName"] == request.RequestData["sourceName"])
|
if (request.RequestData["sceneName"] ==
|
||||||
return RequestResult::Error(RequestStatus::CannotAct, "You cannot create scene item of a scene within itself.");
|
request.RequestData["sourceName"])
|
||||||
|
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", statusCode, comment))
|
if (!request.ValidateOptionalBoolean("sceneItemEnabled",
|
||||||
|
statusCode, comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
sceneItemEnabled = request.RequestData["sceneItemEnabled"];
|
sceneItemEnabled = request.RequestData["sceneItemEnabled"];
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSSceneItemAutoRelease sceneItem = Utils::Obs::ActionHelper::CreateSceneItem(source, scene, sceneItemEnabled);
|
OBSSceneItemAutoRelease sceneItem =
|
||||||
|
Utils::Obs::ActionHelper::CreateSceneItem(source, scene,
|
||||||
|
sceneItemEnabled);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene item.");
|
return RequestResult::Error(
|
||||||
|
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);
|
||||||
@ -193,11 +216,12 @@ RequestResult RequestHandler::CreateSceneItem(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::RemoveSceneItem(const Request& request)
|
RequestResult RequestHandler::RemoveSceneItem(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
|
"sceneName", "sceneItemId", statusCode, comment);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -225,29 +249,37 @@ RequestResult RequestHandler::RemoveSceneItem(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
|
RequestResult RequestHandler::DuplicateSceneItem(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
|
"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("destinationSceneName", statusCode, comment);
|
destinationScene = request.ValidateScene2(
|
||||||
|
"destinationSceneName", statusCode, comment);
|
||||||
if (!destinationScene)
|
if (!destinationScene)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
} else {
|
} else {
|
||||||
destinationScene = obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem));
|
destinationScene =
|
||||||
|
obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem));
|
||||||
if (!destinationScene)
|
if (!destinationScene)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Internal error: Failed to get ref for scene of scene item.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestProcessingFailed,
|
||||||
|
"Internal error: Failed to get ref for scene of scene item.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obs_sceneitem_is_group(sceneItem) && obs_sceneitem_get_scene(sceneItem) == destinationScene) {
|
if (obs_sceneitem_is_group(sceneItem) &&
|
||||||
|
obs_sceneitem_get_scene(sceneItem) == destinationScene) {
|
||||||
obs_scene_release(destinationScene);
|
obs_scene_release(destinationScene);
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Scenes may only have one instance of a group.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceCreationFailed,
|
||||||
|
"Scenes may only have one instance of a group.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get scene item details
|
// Get scene item details
|
||||||
@ -259,10 +291,15 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
|
|||||||
obs_sceneitem_get_crop(sceneItem, &sceneItemCrop);
|
obs_sceneitem_get_crop(sceneItem, &sceneItemCrop);
|
||||||
|
|
||||||
// Create the new item
|
// Create the new item
|
||||||
OBSSceneItemAutoRelease newSceneItem = Utils::Obs::ActionHelper::CreateSceneItem(sceneItemSource, destinationScene, sceneItemEnabled, &sceneItemTransform, &sceneItemCrop);
|
OBSSceneItemAutoRelease newSceneItem =
|
||||||
|
Utils::Obs::ActionHelper::CreateSceneItem(
|
||||||
|
sceneItemSource, destinationScene, sceneItemEnabled,
|
||||||
|
&sceneItemTransform, &sceneItemCrop);
|
||||||
obs_scene_release(destinationScene);
|
obs_scene_release(destinationScene);
|
||||||
if (!newSceneItem)
|
if (!newSceneItem)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene item.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceCreationFailed,
|
||||||
|
"Failed to create the scene item.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItemId"] = obs_sceneitem_get_id(newSceneItem);
|
responseData["sceneItemId"] = obs_sceneitem_get_id(newSceneItem);
|
||||||
@ -287,16 +324,19 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemTransform(const Request& request)
|
RequestResult RequestHandler::GetSceneItemTransform(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItemTransform"] = Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem);
|
responseData["sceneItemTransform"] =
|
||||||
|
Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -315,12 +355,15 @@ RequestResult RequestHandler::GetSceneItemTransform(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
|
RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
if (!(sceneItem && request.ValidateObject("sceneItemTransform", statusCode, comment)))
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
|
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
|
||||||
@ -338,20 +381,23 @@ 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, -90001.0, 90001.0))
|
if (!r.ValidateOptionalNumber("positionX", statusCode, comment,
|
||||||
|
-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, -90001.0, 90001.0))
|
if (!r.ValidateOptionalNumber("positionY", statusCode, comment,
|
||||||
|
-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, -360.0, 360.0))
|
if (!r.ValidateOptionalNumber("rotation", statusCode, comment,
|
||||||
|
-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;
|
||||||
@ -363,86 +409,110 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
|
|||||||
float scaleX = r.RequestData["scaleX"];
|
float scaleX = r.RequestData["scaleX"];
|
||||||
float finalWidth = scaleX * sourceWidth;
|
float finalWidth = scaleX * sourceWidth;
|
||||||
if (!(finalWidth > -90001.0 && finalWidth < 90001.0))
|
if (!(finalWidth > -90001.0 && finalWidth < 90001.0))
|
||||||
return RequestResult::Error(RequestStatus::RequestFieldOutOfRange, "The field scaleX is too small or large for the current source resolution.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestFieldOutOfRange,
|
||||||
|
"The field scaleX is too small or large for the current source resolution.");
|
||||||
sceneItemTransform.scale.x = scaleX;
|
sceneItemTransform.scale.x = scaleX;
|
||||||
transformChanged = true;
|
transformChanged = true;
|
||||||
}
|
}
|
||||||
if (r.Contains("scaleY")) {
|
if (r.Contains("scaleY")) {
|
||||||
if (!r.ValidateOptionalNumber("scaleY", statusCode, comment, -90001.0, 90001.0))
|
if (!r.ValidateOptionalNumber("scaleY", statusCode, comment,
|
||||||
|
-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(RequestStatus::RequestFieldOutOfRange, "The field scaleY is too small or large for the current source resolution.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestFieldOutOfRange,
|
||||||
|
"The field scaleY is too small or large for the current source resolution.");
|
||||||
sceneItemTransform.scale.y = scaleY;
|
sceneItemTransform.scale.y = scaleY;
|
||||||
transformChanged = true;
|
transformChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r.Contains("alignment")) {
|
if (r.Contains("alignment")) {
|
||||||
if (!r.ValidateOptionalNumber("alignment", statusCode, comment, 0, std::numeric_limits<uint32_t>::max()))
|
if (!r.ValidateOptionalNumber(
|
||||||
|
"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, comment))
|
if (!r.ValidateOptionalString("boundsType", statusCode,
|
||||||
|
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 = Utils::Obs::EnumHelper::GetSceneItemBoundsType(boundsTypeString);
|
enum obs_bounds_type boundsType =
|
||||||
if (boundsType == OBS_BOUNDS_NONE && boundsTypeString != "OBS_BOUNDS_NONE")
|
Utils::Obs::EnumHelper::GetSceneItemBoundsType(
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField, "The field boundsType has an invalid value.");
|
boundsTypeString);
|
||||||
|
if (boundsType == OBS_BOUNDS_NONE &&
|
||||||
|
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("boundsAlignment", statusCode, comment, 0, std::numeric_limits<uint32_t>::max()))
|
if (!r.ValidateOptionalNumber(
|
||||||
|
"boundsAlignment", statusCode, comment, 0,
|
||||||
|
std::numeric_limits<uint32_t>::max()))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
sceneItemTransform.bounds_alignment = r.RequestData["boundsAlignment"];
|
sceneItemTransform.bounds_alignment =
|
||||||
|
r.RequestData["boundsAlignment"];
|
||||||
transformChanged = true;
|
transformChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r.Contains("boundsWidth")) {
|
if (r.Contains("boundsWidth")) {
|
||||||
if (!r.ValidateOptionalNumber("boundsWidth", statusCode, comment, 1.0, 90001.0))
|
if (!r.ValidateOptionalNumber("boundsWidth", statusCode,
|
||||||
|
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, comment, 1.0, 90001.0))
|
if (!r.ValidateOptionalNumber("boundsHeight", statusCode,
|
||||||
|
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, 0.0, 100000.0))
|
if (!r.ValidateOptionalNumber("cropLeft", statusCode, comment,
|
||||||
|
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, 0.0, 100000.0))
|
if (!r.ValidateOptionalNumber("cropRight", statusCode, comment,
|
||||||
|
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, 0.0, 100000.0))
|
if (!r.ValidateOptionalNumber("cropTop", statusCode, comment,
|
||||||
|
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, 0.0, 100000.0))
|
if (!r.ValidateOptionalNumber("cropBottom", statusCode, comment,
|
||||||
|
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(RequestStatus::CannotAct, "You have not provided any valid transform changes.");
|
return RequestResult::Error(
|
||||||
|
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);
|
||||||
@ -470,11 +540,13 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemEnabled(const Request& request)
|
RequestResult RequestHandler::GetSceneItemEnabled(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -500,12 +572,15 @@ RequestResult RequestHandler::GetSceneItemEnabled(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetSceneItemEnabled(const Request& request)
|
RequestResult RequestHandler::SetSceneItemEnabled(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
if (!(sceneItem && request.ValidateBoolean("sceneItemEnabled", statusCode, comment)))
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
|
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"];
|
||||||
@ -532,11 +607,13 @@ RequestResult RequestHandler::SetSceneItemEnabled(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemLocked(const Request& request)
|
RequestResult RequestHandler::GetSceneItemLocked(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -562,12 +639,15 @@ RequestResult RequestHandler::GetSceneItemLocked(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetSceneItemLocked(const Request& request)
|
RequestResult RequestHandler::SetSceneItemLocked(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
if (!(sceneItem && request.ValidateBoolean("sceneItemLocked", statusCode, comment)))
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
|
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"];
|
||||||
@ -596,16 +676,19 @@ RequestResult RequestHandler::SetSceneItemLocked(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemIndex(const Request& request)
|
RequestResult RequestHandler::GetSceneItemIndex(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItemIndex"] = obs_sceneitem_get_order_position(sceneItem);
|
responseData["sceneItemIndex"] =
|
||||||
|
obs_sceneitem_get_order_position(sceneItem);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -626,12 +709,15 @@ RequestResult RequestHandler::GetSceneItemIndex(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetSceneItemIndex(const Request& request)
|
RequestResult RequestHandler::SetSceneItemIndex(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
if (!(sceneItem && request.ValidateNumber("sceneItemIndex", statusCode, comment, 0, 8192)))
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
|
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"];
|
||||||
@ -668,18 +754,21 @@ RequestResult RequestHandler::SetSceneItemIndex(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemBlendMode(const Request& request)
|
RequestResult RequestHandler::GetSceneItemBlendMode(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
auto blendMode = obs_sceneitem_get_blending_mode(sceneItem);
|
auto blendMode = obs_sceneitem_get_blending_mode(sceneItem);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItemBlendMode"] = Utils::Obs::StringHelper::GetSceneItemBlendMode(blendMode);
|
responseData["sceneItemBlendMode"] =
|
||||||
|
Utils::Obs::StringHelper::GetSceneItemBlendMode(blendMode);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -700,19 +789,26 @@ RequestResult RequestHandler::GetSceneItemBlendMode(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scene items
|
* @category scene items
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetSceneItemBlendMode(const Request& request)
|
RequestResult RequestHandler::SetSceneItemBlendMode(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
if (!(sceneItem && request.ValidateString("sceneItemBlendMode", statusCode, comment)))
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
|
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 = Utils::Obs::EnumHelper::GetSceneItemBlendMode(blendModeString);
|
auto blendMode =
|
||||||
if (blendMode == OBS_BLEND_NORMAL && blendModeString != "OBS_BLEND_NORMAL")
|
Utils::Obs::EnumHelper::GetSceneItemBlendMode(blendModeString);
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField, "The field sceneItemBlendMode has an invalid value.");
|
if (blendMode == OBS_BLEND_NORMAL &&
|
||||||
|
blendModeString != "OBS_BLEND_NORMAL")
|
||||||
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidRequestField,
|
||||||
|
"The field sceneItemBlendMode has an invalid value.");
|
||||||
|
|
||||||
obs_sceneitem_set_blending_mode(sceneItem, blendMode);
|
obs_sceneitem_set_blending_mode(sceneItem, blendMode);
|
||||||
|
|
||||||
@ -720,34 +816,45 @@ RequestResult RequestHandler::SetSceneItemBlendMode(const Request& request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Intentionally undocumented
|
// Intentionally undocumented
|
||||||
RequestResult RequestHandler::GetSceneItemPrivateSettings(const Request& request)
|
RequestResult
|
||||||
|
RequestHandler::GetSceneItemPrivateSettings(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_sceneitem_get_private_settings(sceneItem);
|
OBSDataAutoRelease privateSettings =
|
||||||
|
obs_sceneitem_get_private_settings(sceneItem);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItemSettings"] = Utils::Json::ObsDataToJson(privateSettings);
|
responseData["sceneItemSettings"] =
|
||||||
|
Utils::Json::ObsDataToJson(privateSettings);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intentionally undocumented
|
// Intentionally undocumented
|
||||||
RequestResult RequestHandler::SetSceneItemPrivateSettings(const Request& request)
|
RequestResult
|
||||||
|
RequestHandler::SetSceneItemPrivateSettings(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(
|
||||||
if (!sceneItem || !request.ValidateObject("sceneItemSettings", statusCode, comment))
|
"sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
|
if (!sceneItem ||
|
||||||
|
!request.ValidateObject("sceneItemSettings", statusCode, comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_sceneitem_get_private_settings(sceneItem);
|
OBSDataAutoRelease privateSettings =
|
||||||
|
obs_sceneitem_get_private_settings(sceneItem);
|
||||||
|
|
||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["sceneItemSettings"]);
|
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(
|
||||||
|
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);
|
||||||
|
@ -33,19 +33,23 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSceneList(const Request&)
|
RequestResult RequestHandler::GetSceneList(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
|
|
||||||
OBSSourceAutoRelease currentProgramScene = obs_frontend_get_current_scene();
|
OBSSourceAutoRelease currentProgramScene =
|
||||||
|
obs_frontend_get_current_scene();
|
||||||
if (currentProgramScene)
|
if (currentProgramScene)
|
||||||
responseData["currentProgramSceneName"] = obs_source_get_name(currentProgramScene);
|
responseData["currentProgramSceneName"] =
|
||||||
|
obs_source_get_name(currentProgramScene);
|
||||||
else
|
else
|
||||||
responseData["currentProgramSceneName"] = nullptr;
|
responseData["currentProgramSceneName"] = nullptr;
|
||||||
|
|
||||||
OBSSourceAutoRelease currentPreviewScene = obs_frontend_get_current_preview_scene();
|
OBSSourceAutoRelease currentPreviewScene =
|
||||||
|
obs_frontend_get_current_preview_scene();
|
||||||
if (currentPreviewScene)
|
if (currentPreviewScene)
|
||||||
responseData["currentPreviewSceneName"] = obs_source_get_name(currentPreviewScene);
|
responseData["currentPreviewSceneName"] =
|
||||||
|
obs_source_get_name(currentPreviewScene);
|
||||||
else
|
else
|
||||||
responseData["currentPreviewSceneName"] = nullptr;
|
responseData["currentPreviewSceneName"] = nullptr;
|
||||||
|
|
||||||
@ -68,7 +72,7 @@ RequestResult RequestHandler::GetSceneList(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetGroupList(const Request&)
|
RequestResult RequestHandler::GetGroupList(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
|
|
||||||
@ -89,11 +93,13 @@ RequestResult RequestHandler::GetGroupList(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetCurrentProgramScene(const Request&)
|
RequestResult RequestHandler::GetCurrentProgramScene(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
OBSSourceAutoRelease currentProgramScene = obs_frontend_get_current_scene();
|
OBSSourceAutoRelease currentProgramScene =
|
||||||
responseData["currentProgramSceneName"] = obs_source_get_name(currentProgramScene);
|
obs_frontend_get_current_scene();
|
||||||
|
responseData["currentProgramSceneName"] =
|
||||||
|
obs_source_get_name(currentProgramScene);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -110,11 +116,12 @@ RequestResult RequestHandler::GetCurrentProgramScene(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetCurrentProgramScene(const Request& request)
|
RequestResult RequestHandler::SetCurrentProgramScene(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
|
OBSSourceAutoRelease scene =
|
||||||
|
request.ValidateScene("sceneName", statusCode, comment);
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -137,15 +144,17 @@ RequestResult RequestHandler::SetCurrentProgramScene(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetCurrentPreviewScene(const Request&)
|
RequestResult RequestHandler::GetCurrentPreviewScene(const Request &)
|
||||||
{
|
{
|
||||||
if (!obs_frontend_preview_program_mode_active())
|
if (!obs_frontend_preview_program_mode_active())
|
||||||
return RequestResult::Error(RequestStatus::StudioModeNotActive);
|
return RequestResult::Error(RequestStatus::StudioModeNotActive);
|
||||||
|
|
||||||
OBSSourceAutoRelease currentPreviewScene = obs_frontend_get_current_preview_scene();
|
OBSSourceAutoRelease currentPreviewScene =
|
||||||
|
obs_frontend_get_current_preview_scene();
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["currentPreviewSceneName"] = obs_source_get_name(currentPreviewScene);
|
responseData["currentPreviewSceneName"] =
|
||||||
|
obs_source_get_name(currentPreviewScene);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -164,14 +173,15 @@ RequestResult RequestHandler::GetCurrentPreviewScene(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetCurrentPreviewScene(const Request& request)
|
RequestResult RequestHandler::SetCurrentPreviewScene(const Request &request)
|
||||||
{
|
{
|
||||||
if (!obs_frontend_preview_program_mode_active())
|
if (!obs_frontend_preview_program_mode_active())
|
||||||
return RequestResult::Error(RequestStatus::StudioModeNotActive);
|
return RequestResult::Error(RequestStatus::StudioModeNotActive);
|
||||||
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
|
OBSSourceAutoRelease scene =
|
||||||
|
request.ValidateScene("sceneName", statusCode, comment);
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -192,7 +202,7 @@ RequestResult RequestHandler::SetCurrentPreviewScene(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::CreateScene(const Request& request)
|
RequestResult RequestHandler::CreateScene(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
@ -203,11 +213,15 @@ 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(RequestStatus::ResourceAlreadyExists, "A source already exists by that scene name.");
|
return RequestResult::Error(
|
||||||
|
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(RequestStatus::ResourceCreationFailed, "Failed to create the scene.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceCreationFailed,
|
||||||
|
"Failed to create the scene.");
|
||||||
|
|
||||||
obs_scene_release(createdScene);
|
obs_scene_release(createdScene);
|
||||||
|
|
||||||
@ -226,16 +240,19 @@ RequestResult RequestHandler::CreateScene(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::RemoveScene(const Request& request)
|
RequestResult RequestHandler::RemoveScene(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
|
OBSSourceAutoRelease scene =
|
||||||
|
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(RequestStatus::NotEnoughResources, "You cannot remove the last scene in the collection.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::NotEnoughResources,
|
||||||
|
"You cannot remove the last scene in the collection.");
|
||||||
|
|
||||||
obs_source_remove(scene);
|
obs_source_remove(scene);
|
||||||
|
|
||||||
@ -255,19 +272,24 @@ RequestResult RequestHandler::RemoveScene(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetSceneName(const Request& request)
|
RequestResult RequestHandler::SetSceneName(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
|
OBSSourceAutoRelease scene =
|
||||||
if (!(scene && request.ValidateString("newSceneName", statusCode, comment)))
|
request.ValidateScene("sceneName", 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 = obs_get_source_by_name(newSceneName.c_str());
|
OBSSourceAutoRelease existingSource =
|
||||||
|
obs_get_source_by_name(newSceneName.c_str());
|
||||||
if (existingSource)
|
if (existingSource)
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that new scene name.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceAlreadyExists,
|
||||||
|
"A source already exists by that new scene name.");
|
||||||
|
|
||||||
obs_source_set_name(scene, newSceneName.c_str());
|
obs_source_set_name(scene, newSceneName.c_str());
|
||||||
|
|
||||||
@ -289,25 +311,30 @@ RequestResult RequestHandler::SetSceneName(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSceneSceneTransitionOverride(const Request& request)
|
RequestResult
|
||||||
|
RequestHandler::GetSceneSceneTransitionOverride(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
|
OBSSourceAutoRelease scene =
|
||||||
|
request.ValidateScene("sceneName", statusCode, comment);
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(scene);
|
OBSDataAutoRelease privateSettings =
|
||||||
|
obs_source_get_private_settings(scene);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
const char *transitionName = obs_data_get_string(privateSettings, "transition");
|
const char *transitionName =
|
||||||
|
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(privateSettings, "transition_duration");
|
responseData["transitionDuration"] = obs_data_get_int(
|
||||||
|
privateSettings, "transition_duration");
|
||||||
else
|
else
|
||||||
responseData["transitionDuration"] = nullptr;
|
responseData["transitionDuration"] = nullptr;
|
||||||
|
|
||||||
@ -328,40 +355,55 @@ RequestResult RequestHandler::GetSceneSceneTransitionOverride(const Request& req
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category scenes
|
* @category scenes
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request& request)
|
RequestResult
|
||||||
|
RequestHandler::SetSceneSceneTransitionOverride(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
|
OBSSourceAutoRelease scene =
|
||||||
|
request.ValidateScene("sceneName", statusCode, comment);
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(scene);
|
OBSDataAutoRelease privateSettings =
|
||||||
|
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", statusCode, comment))
|
if (!request.ValidateOptionalString("transitionName",
|
||||||
|
statusCode, comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
OBSSourceAutoRelease transition = Utils::Obs::SearchHelper::GetSceneTransitionByName(request.RequestData["transitionName"]);
|
OBSSourceAutoRelease transition =
|
||||||
|
Utils::Obs::SearchHelper::GetSceneTransitionByName(
|
||||||
|
request.RequestData["transitionName"]);
|
||||||
if (!transition)
|
if (!transition)
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene transition was found by that name.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceNotFound,
|
||||||
|
"No scene transition was found by that name.");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasDuration = request.RequestData.contains("transitionDuration");
|
bool hasDuration = request.RequestData.contains("transitionDuration");
|
||||||
if (hasDuration && !request.RequestData["transitionDuration"].is_null()) {
|
if (hasDuration &&
|
||||||
if (!request.ValidateOptionalNumber("transitionDuration", statusCode, comment, 50, 20000))
|
!request.RequestData["transitionDuration"].is_null()) {
|
||||||
|
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(RequestStatus::MissingRequestField, "Your request data must include either `transitionName` or `transitionDuration`.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::MissingRequestField,
|
||||||
|
"Your request data must include either `transitionName` or `transitionDuration`.");
|
||||||
|
|
||||||
if (hasName) {
|
if (hasName) {
|
||||||
if (request.RequestData["transitionName"].is_null()) {
|
if (request.RequestData["transitionName"].is_null()) {
|
||||||
obs_data_erase(privateSettings, "transition");
|
obs_data_erase(privateSettings, "transition");
|
||||||
} else {
|
} else {
|
||||||
std::string transitionName = request.RequestData["transitionName"];
|
std::string transitionName =
|
||||||
obs_data_set_string(privateSettings, "transition", transitionName.c_str());
|
request.RequestData["transitionName"];
|
||||||
|
obs_data_set_string(privateSettings, "transition",
|
||||||
|
transitionName.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +411,9 @@ RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request& req
|
|||||||
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(privateSettings, "transition_duration", request.RequestData["transitionDuration"]);
|
obs_data_set_int(
|
||||||
|
privateSettings, "transition_duration",
|
||||||
|
request.RequestData["transitionDuration"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,12 +25,15 @@ 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, uint32_t requestedWidth = 0, uint32_t requestedHeight = 0)
|
QImage TakeSourceScreenshot(obs_source_t *source, bool &success,
|
||||||
|
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 = ((double)sourceWidth / (double)sourceHeight);
|
const double sourceAspectRatio =
|
||||||
|
((double)sourceWidth / (double)sourceHeight);
|
||||||
|
|
||||||
uint32_t imgWidth = sourceWidth;
|
uint32_t imgWidth = sourceWidth;
|
||||||
uint32_t imgHeight = sourceHeight;
|
uint32_t imgHeight = sourceHeight;
|
||||||
@ -56,14 +59,15 @@ QImage TakeSourceScreenshot(obs_source_t *source, bool &success, uint32_t reques
|
|||||||
ret.fill(0);
|
ret.fill(0);
|
||||||
|
|
||||||
// Video image buffer
|
// Video image buffer
|
||||||
uint8_t* videoData = nullptr;
|
uint8_t *videoData = nullptr;
|
||||||
uint32_t videoLinesize = 0;
|
uint32_t videoLinesize = 0;
|
||||||
|
|
||||||
// Enter graphics context
|
// Enter graphics context
|
||||||
obs_enter_graphics();
|
obs_enter_graphics();
|
||||||
|
|
||||||
gs_texrender_t* texRender = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
gs_texrender_t *texRender = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
||||||
gs_stagesurf_t* stageSurface = gs_stagesurface_create(imgWidth, imgHeight, GS_RGBA);
|
gs_stagesurf_t *stageSurface =
|
||||||
|
gs_stagesurface_create(imgWidth, imgHeight, GS_RGBA);
|
||||||
|
|
||||||
success = false;
|
success = false;
|
||||||
gs_texrender_reset(texRender);
|
gs_texrender_reset(texRender);
|
||||||
@ -72,7 +76,8 @@ QImage TakeSourceScreenshot(obs_source_t *source, bool &success, uint32_t reques
|
|||||||
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, -100.0f, 100.0f);
|
gs_ortho(0.0f, (float)sourceWidth, 0.0f, (float)sourceHeight,
|
||||||
|
-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);
|
||||||
@ -84,11 +89,15 @@ QImage TakeSourceScreenshot(obs_source_t *source, bool &success, uint32_t reques
|
|||||||
gs_blend_state_pop();
|
gs_blend_state_pop();
|
||||||
gs_texrender_end(texRender);
|
gs_texrender_end(texRender);
|
||||||
|
|
||||||
gs_stage_texture(stageSurface, gs_texrender_get_texture(texRender));
|
gs_stage_texture(stageSurface,
|
||||||
if (gs_stagesurface_map(stageSurface, &videoData, &videoLinesize)) {
|
gs_texrender_get_texture(texRender));
|
||||||
|
if (gs_stagesurface_map(stageSurface, &videoData,
|
||||||
|
&videoLinesize)) {
|
||||||
int lineSize = ret.bytesPerLine();
|
int lineSize = ret.bytesPerLine();
|
||||||
for (uint y = 0; y < imgHeight; y++) {
|
for (uint y = 0; y < imgHeight; y++) {
|
||||||
memcpy(ret.scanLine(y), videoData + (y * videoLinesize), lineSize);
|
memcpy(ret.scanLine(y),
|
||||||
|
videoData + (y * videoLinesize),
|
||||||
|
lineSize);
|
||||||
}
|
}
|
||||||
gs_stagesurface_unmap(stageSurface);
|
gs_stagesurface_unmap(stageSurface);
|
||||||
success = true;
|
success = true;
|
||||||
@ -126,16 +135,20 @@ bool IsImageFormatValid(std::string format)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category sources
|
* @category sources
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSourceActive(const Request& request)
|
RequestResult RequestHandler::GetSourceActive(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
OBSSourceAutoRelease source =
|
||||||
|
request.ValidateSource("sourceName", statusCode, comment);
|
||||||
if (!source)
|
if (!source)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE)
|
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT &&
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceType, "The specified source is not an input or a 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.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["videoActive"] = obs_source_active(source);
|
responseData["videoActive"] = obs_source_active(source);
|
||||||
@ -166,63 +179,83 @@ RequestResult RequestHandler::GetSourceActive(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category sources
|
* @category sources
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSourceScreenshot(const Request& request)
|
RequestResult RequestHandler::GetSourceScreenshot(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
OBSSourceAutoRelease source =
|
||||||
if (!(source && request.ValidateString("imageFormat", statusCode, comment)))
|
request.ValidateSource("sourceName", 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 && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE)
|
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT &&
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceType, "The specified source is not an input or a 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.");
|
||||||
|
|
||||||
std::string imageFormat = request.RequestData["imageFormat"];
|
std::string imageFormat = request.RequestData["imageFormat"];
|
||||||
|
|
||||||
if (!IsImageFormatValid(imageFormat))
|
if (!IsImageFormatValid(imageFormat))
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField, "Your specified image format is invalid or not supported by this system.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidRequestField,
|
||||||
|
"Your specified image format is invalid or not supported by this system.");
|
||||||
|
|
||||||
uint32_t requestedWidth{0};
|
uint32_t requestedWidth{0};
|
||||||
uint32_t requestedHeight{0};
|
uint32_t requestedHeight{0};
|
||||||
int compressionQuality{-1};
|
int compressionQuality{-1};
|
||||||
|
|
||||||
if (request.Contains("imageWidth")) {
|
if (request.Contains("imageWidth")) {
|
||||||
if (!request.ValidateOptionalNumber("imageWidth", statusCode, comment, 8, 4096))
|
if (!request.ValidateOptionalNumber("imageWidth", statusCode,
|
||||||
|
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, comment, 8, 4096))
|
if (!request.ValidateOptionalNumber("imageHeight", statusCode,
|
||||||
|
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", statusCode, comment, -1, 100))
|
if (!request.ValidateOptionalNumber("imageCompressionQuality",
|
||||||
|
statusCode, comment, -1,
|
||||||
|
100))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
compressionQuality = request.RequestData["imageCompressionQuality"];
|
compressionQuality =
|
||||||
|
request.RequestData["imageCompressionQuality"];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success;
|
bool success;
|
||||||
QImage renderedImage = TakeSourceScreenshot(source, success, requestedWidth, requestedHeight);
|
QImage renderedImage = TakeSourceScreenshot(
|
||||||
|
source, success, requestedWidth, requestedHeight);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Failed to render screenshot.");
|
return RequestResult::Error(
|
||||||
|
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(), compressionQuality))
|
if (!renderedImage.save(&buffer, imageFormat.c_str(),
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Failed to encode screenshot.");
|
compressionQuality))
|
||||||
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestProcessingFailed,
|
||||||
|
"Failed to encode screenshot.");
|
||||||
|
|
||||||
buffer.close();
|
buffer.close();
|
||||||
|
|
||||||
QString encodedPicture = QString("data:image/%1;base64,").arg(imageFormat.c_str()).append(encodedImgBytes.toBase64());
|
QString encodedPicture = QString("data:image/%1;base64,")
|
||||||
|
.arg(imageFormat.c_str())
|
||||||
|
.append(encodedImgBytes.toBase64());
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["imageData"] = encodedPicture.toStdString();
|
responseData["imageData"] = encodedPicture.toStdString();
|
||||||
@ -253,95 +286,123 @@ RequestResult RequestHandler::GetSourceScreenshot(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category sources
|
* @category sources
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SaveSourceScreenshot(const Request& request)
|
RequestResult RequestHandler::SaveSourceScreenshot(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
OBSSourceAutoRelease source =
|
||||||
if (!(source && request.ValidateString("imageFormat", statusCode, comment) && request.ValidateString("imageFilePath", statusCode, comment)))
|
request.ValidateSource("sourceName", statusCode, comment);
|
||||||
|
if (!(source &&
|
||||||
|
request.ValidateString("imageFormat", statusCode, comment) &&
|
||||||
|
request.ValidateString("imageFilePath", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE)
|
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT &&
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceType, "The specified source is not an input or a 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.");
|
||||||
|
|
||||||
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(RequestStatus::InvalidRequestField, "Your specified image format is invalid or not supported by this system.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidRequestField,
|
||||||
|
"Your specified image format is invalid or not supported by this system.");
|
||||||
|
|
||||||
QFileInfo filePathInfo(QString::fromStdString(imageFilePath));
|
QFileInfo filePathInfo(QString::fromStdString(imageFilePath));
|
||||||
if (!filePathInfo.absoluteDir().exists())
|
if (!filePathInfo.absoluteDir().exists())
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "The directory for your file path does not exist.");
|
return RequestResult::Error(
|
||||||
|
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, comment, 8, 4096))
|
if (!request.ValidateOptionalNumber("imageWidth", statusCode,
|
||||||
|
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, comment, 8, 4096))
|
if (!request.ValidateOptionalNumber("imageHeight", statusCode,
|
||||||
|
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", statusCode, comment, -1, 100))
|
if (!request.ValidateOptionalNumber("imageCompressionQuality",
|
||||||
|
statusCode, comment, -1,
|
||||||
|
100))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
compressionQuality = request.RequestData["imageCompressionQuality"];
|
compressionQuality =
|
||||||
|
request.RequestData["imageCompressionQuality"];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success;
|
bool success;
|
||||||
QImage renderedImage = TakeSourceScreenshot(source, success, requestedWidth, requestedHeight);
|
QImage renderedImage = TakeSourceScreenshot(
|
||||||
|
source, success, requestedWidth, requestedHeight);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Failed to render screenshot.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestProcessingFailed,
|
||||||
|
"Failed to render screenshot.");
|
||||||
|
|
||||||
QString absoluteFilePath = filePathInfo.absoluteFilePath();
|
QString absoluteFilePath = filePathInfo.absoluteFilePath();
|
||||||
|
|
||||||
if (!renderedImage.save(absoluteFilePath, imageFormat.c_str(), compressionQuality))
|
if (!renderedImage.save(absoluteFilePath, imageFormat.c_str(),
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Failed to save screenshot.");
|
compressionQuality))
|
||||||
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestProcessingFailed,
|
||||||
|
"Failed to save screenshot.");
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intentionally undocumented
|
// Intentionally undocumented
|
||||||
RequestResult RequestHandler::GetSourcePrivateSettings(const Request& request)
|
RequestResult RequestHandler::GetSourcePrivateSettings(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
OBSSourceAutoRelease source =
|
||||||
|
request.ValidateSource("sourceName", statusCode, comment);
|
||||||
if (!source)
|
if (!source)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(source);
|
OBSDataAutoRelease privateSettings =
|
||||||
|
obs_source_get_private_settings(source);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sourceSettings"] = Utils::Json::ObsDataToJson(privateSettings);
|
responseData["sourceSettings"] =
|
||||||
|
Utils::Json::ObsDataToJson(privateSettings);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intentionally undocumented
|
// Intentionally undocumented
|
||||||
RequestResult RequestHandler::SetSourcePrivateSettings(const Request& request)
|
RequestResult RequestHandler::SetSourcePrivateSettings(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
OBSSourceAutoRelease source =
|
||||||
if (!source || !request.ValidateObject("sourceSettings", statusCode, comment))
|
request.ValidateSource("sourceName", statusCode, comment);
|
||||||
|
if (!source ||
|
||||||
|
!request.ValidateObject("sourceSettings", statusCode, comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(source);
|
OBSDataAutoRelease privateSettings =
|
||||||
|
obs_source_get_private_settings(source);
|
||||||
|
|
||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["sourceSettings"]);
|
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(
|
||||||
|
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);
|
||||||
|
@ -37,20 +37,26 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category stream
|
* @category stream
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetStreamStatus(const Request&)
|
RequestResult RequestHandler::GetStreamStatus(const Request &)
|
||||||
{
|
{
|
||||||
OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output();
|
OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output();
|
||||||
|
|
||||||
uint64_t outputDuration = Utils::Obs::NumberHelper::GetOutputDuration(streamOutput);
|
uint64_t outputDuration =
|
||||||
|
Utils::Obs::NumberHelper::GetOutputDuration(streamOutput);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["outputActive"] = obs_output_active(streamOutput);
|
responseData["outputActive"] = obs_output_active(streamOutput);
|
||||||
responseData["outputReconnecting"] = obs_output_reconnecting(streamOutput);
|
responseData["outputReconnecting"] =
|
||||||
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
|
obs_output_reconnecting(streamOutput);
|
||||||
|
responseData["outputTimecode"] =
|
||||||
|
Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
|
||||||
responseData["outputDuration"] = outputDuration;
|
responseData["outputDuration"] = outputDuration;
|
||||||
responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(streamOutput);
|
responseData["outputBytes"] =
|
||||||
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(streamOutput);
|
(uint64_t)obs_output_get_total_bytes(streamOutput);
|
||||||
responseData["outputTotalFrames"] = obs_output_get_total_frames(streamOutput);
|
responseData["outputSkippedFrames"] =
|
||||||
|
obs_output_get_frames_dropped(streamOutput);
|
||||||
|
responseData["outputTotalFrames"] =
|
||||||
|
obs_output_get_total_frames(streamOutput);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -67,7 +73,7 @@ RequestResult RequestHandler::GetStreamStatus(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category stream
|
* @category stream
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::ToggleStream(const Request&)
|
RequestResult RequestHandler::ToggleStream(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
if (obs_frontend_streaming_active()) {
|
if (obs_frontend_streaming_active()) {
|
||||||
@ -91,7 +97,7 @@ RequestResult RequestHandler::ToggleStream(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category stream
|
* @category stream
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::StartStream(const Request&)
|
RequestResult RequestHandler::StartStream(const Request &)
|
||||||
{
|
{
|
||||||
if (obs_frontend_streaming_active())
|
if (obs_frontend_streaming_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning);
|
return RequestResult::Error(RequestStatus::OutputRunning);
|
||||||
@ -112,7 +118,7 @@ RequestResult RequestHandler::StartStream(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category stream
|
* @category stream
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::StopStream(const Request&)
|
RequestResult RequestHandler::StopStream(const Request &)
|
||||||
{
|
{
|
||||||
if (!obs_frontend_streaming_active())
|
if (!obs_frontend_streaming_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
||||||
@ -135,7 +141,7 @@ RequestResult RequestHandler::StopStream(const Request&)
|
|||||||
* @category stream
|
* @category stream
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SendStreamCaption(const Request& request)
|
RequestResult RequestHandler::SendStreamCaption(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
|
@ -35,10 +35,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category transitions
|
* @category transitions
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetTransitionKindList(const Request&)
|
RequestResult RequestHandler::GetTransitionKindList(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["transitionKinds"] = Utils::Obs::ArrayHelper::GetTransitionKindList();
|
responseData["transitionKinds"] =
|
||||||
|
Utils::Obs::ArrayHelper::GetTransitionKindList();
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,20 +57,23 @@ RequestResult RequestHandler::GetTransitionKindList(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category transitions
|
* @category transitions
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetSceneTransitionList(const Request&)
|
RequestResult RequestHandler::GetSceneTransitionList(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
|
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
||||||
if (transition) {
|
if (transition) {
|
||||||
responseData["currentSceneTransitionName"] = obs_source_get_name(transition);
|
responseData["currentSceneTransitionName"] =
|
||||||
responseData["currentSceneTransitionKind"] = obs_source_get_id(transition);
|
obs_source_get_name(transition);
|
||||||
|
responseData["currentSceneTransitionKind"] =
|
||||||
|
obs_source_get_id(transition);
|
||||||
} else {
|
} else {
|
||||||
responseData["currentSceneTransitionName"] = nullptr;
|
responseData["currentSceneTransitionName"] = nullptr;
|
||||||
responseData["currentSceneTransitionKind"] = nullptr;
|
responseData["currentSceneTransitionKind"] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData["transitions"] = Utils::Obs::ArrayHelper::GetSceneTransitionList();
|
responseData["transitions"] =
|
||||||
|
Utils::Obs::ArrayHelper::GetSceneTransitionList();
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -91,11 +95,13 @@ RequestResult RequestHandler::GetSceneTransitionList(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category transitions
|
* @category transitions
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetCurrentSceneTransition(const Request&)
|
RequestResult RequestHandler::GetCurrentSceneTransition(const Request &)
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
||||||
if (!transition)
|
if (!transition)
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"OBS does not currently have a scene transition set."); // This should not happen!
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["transitionName"] = obs_source_get_name(transition);
|
responseData["transitionName"] = obs_source_get_name(transition);
|
||||||
@ -106,13 +112,16 @@ RequestResult RequestHandler::GetCurrentSceneTransition(const Request&)
|
|||||||
responseData["transitionDuration"] = nullptr;
|
responseData["transitionDuration"] = nullptr;
|
||||||
} else {
|
} else {
|
||||||
responseData["transitionFixed"] = false;
|
responseData["transitionFixed"] = false;
|
||||||
responseData["transitionDuration"] = obs_frontend_get_transition_duration();
|
responseData["transitionDuration"] =
|
||||||
|
obs_frontend_get_transition_duration();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obs_source_configurable(transition)) {
|
if (obs_source_configurable(transition)) {
|
||||||
responseData["transitionConfigurable"] = true;
|
responseData["transitionConfigurable"] = true;
|
||||||
OBSDataAutoRelease transitionSettings = obs_source_get_settings(transition);
|
OBSDataAutoRelease transitionSettings =
|
||||||
responseData["transitionSettings"] = Utils::Json::ObsDataToJson(transitionSettings);
|
obs_source_get_settings(transition);
|
||||||
|
responseData["transitionSettings"] =
|
||||||
|
Utils::Json::ObsDataToJson(transitionSettings);
|
||||||
} else {
|
} else {
|
||||||
responseData["transitionConfigurable"] = false;
|
responseData["transitionConfigurable"] = false;
|
||||||
responseData["transitionSettings"] = nullptr;
|
responseData["transitionSettings"] = nullptr;
|
||||||
@ -135,7 +144,7 @@ RequestResult RequestHandler::GetCurrentSceneTransition(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category transitions
|
* @category transitions
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetCurrentSceneTransition(const Request& request)
|
RequestResult RequestHandler::SetCurrentSceneTransition(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
@ -144,9 +153,13 @@ RequestResult RequestHandler::SetCurrentSceneTransition(const Request& request)
|
|||||||
|
|
||||||
std::string transitionName = request.RequestData["transitionName"];
|
std::string transitionName = request.RequestData["transitionName"];
|
||||||
|
|
||||||
OBSSourceAutoRelease transition = Utils::Obs::SearchHelper::GetSceneTransitionByName(transitionName);
|
OBSSourceAutoRelease transition =
|
||||||
|
Utils::Obs::SearchHelper::GetSceneTransitionByName(
|
||||||
|
transitionName);
|
||||||
if (!transition)
|
if (!transition)
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene transition was found by that name.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceNotFound,
|
||||||
|
"No scene transition was found by that name.");
|
||||||
|
|
||||||
obs_frontend_set_current_transition(transition);
|
obs_frontend_set_current_transition(transition);
|
||||||
|
|
||||||
@ -165,11 +178,13 @@ RequestResult RequestHandler::SetCurrentSceneTransition(const Request& request)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category transitions
|
* @category transitions
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetCurrentSceneTransitionDuration(const Request& request)
|
RequestResult
|
||||||
|
RequestHandler::SetCurrentSceneTransitionDuration(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!request.ValidateNumber("transitionDuration", statusCode, comment, 50, 20000))
|
if (!request.ValidateNumber("transitionDuration", statusCode, comment,
|
||||||
|
50, 20000))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
int transitionDuration = request.RequestData["transitionDuration"];
|
int transitionDuration = request.RequestData["transitionDuration"];
|
||||||
@ -192,31 +207,41 @@ RequestResult RequestHandler::SetCurrentSceneTransitionDuration(const Request& r
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category transitions
|
* @category transitions
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request& request)
|
RequestResult
|
||||||
|
RequestHandler::SetCurrentSceneTransitionSettings(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!request.ValidateObject("transitionSettings", statusCode, comment, true))
|
if (!request.ValidateObject("transitionSettings", statusCode, comment,
|
||||||
|
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(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"OBS does not currently have a scene transition set."); // This should not happen!
|
||||||
|
|
||||||
if (!obs_source_configurable(transition))
|
if (!obs_source_configurable(transition))
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotConfigurable, "The current transition does not support custom settings.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceNotConfigurable,
|
||||||
|
"The current transition does not support custom settings.");
|
||||||
|
|
||||||
bool overlay = true;
|
bool overlay = true;
|
||||||
if (request.Contains("overlay")) {
|
if (request.Contains("overlay")) {
|
||||||
if (!request.ValidateOptionalBoolean("overlay", statusCode, comment))
|
if (!request.ValidateOptionalBoolean("overlay", statusCode,
|
||||||
|
comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
overlay = request.RequestData["overlay"];
|
overlay = request.RequestData["overlay"];
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["transitionSettings"]);
|
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(
|
||||||
|
request.RequestData["transitionSettings"]);
|
||||||
if (!newSettings)
|
if (!newSettings)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::RequestProcessingFailed,
|
||||||
|
"An internal data conversion operation failed. Please report this!");
|
||||||
|
|
||||||
if (overlay)
|
if (overlay)
|
||||||
obs_source_update(transition, newSettings);
|
obs_source_update(transition, newSettings);
|
||||||
@ -242,11 +267,13 @@ RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request& r
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category transitions
|
* @category transitions
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetCurrentSceneTransitionCursor(const Request&)
|
RequestResult RequestHandler::GetCurrentSceneTransitionCursor(const Request &)
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
||||||
if (!transition)
|
if (!transition)
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"OBS does not currently have a scene transition set."); // This should not happen!
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["transitionCursor"] = obs_transition_get_time(transition);
|
responseData["transitionCursor"] = obs_transition_get_time(transition);
|
||||||
@ -264,12 +291,13 @@ RequestResult RequestHandler::GetCurrentSceneTransitionCursor(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category transitions
|
* @category transitions
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::TriggerStudioModeTransition(const Request&)
|
RequestResult RequestHandler::TriggerStudioModeTransition(const Request &)
|
||||||
{
|
{
|
||||||
if (!obs_frontend_preview_program_mode_active())
|
if (!obs_frontend_preview_program_mode_active())
|
||||||
return RequestResult::Error(RequestStatus::StudioModeNotActive);
|
return RequestResult::Error(RequestStatus::StudioModeNotActive);
|
||||||
|
|
||||||
OBSSourceAutoRelease previewScene = obs_frontend_get_current_preview_scene();
|
OBSSourceAutoRelease previewScene =
|
||||||
|
obs_frontend_get_current_preview_scene();
|
||||||
|
|
||||||
obs_frontend_set_current_scene(previewScene);
|
obs_frontend_set_current_scene(previewScene);
|
||||||
|
|
||||||
@ -291,7 +319,7 @@ RequestResult RequestHandler::TriggerStudioModeTransition(const Request&)
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @category transitions
|
* @category transitions
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetTBarPosition(const Request& request)
|
RequestResult RequestHandler::SetTBarPosition(const Request &request)
|
||||||
{
|
{
|
||||||
if (!obs_frontend_preview_program_mode_active())
|
if (!obs_frontend_preview_program_mode_active())
|
||||||
return RequestResult::Error(RequestStatus::StudioModeNotActive);
|
return RequestResult::Error(RequestStatus::StudioModeNotActive);
|
||||||
@ -303,13 +331,16 @@ 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, comment))
|
if (!request.ValidateOptionalBoolean("release", statusCode,
|
||||||
|
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(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"OBS does not currently have a scene transition set."); // This should not happen!
|
||||||
|
|
||||||
float position = request.RequestData["position"];
|
float position = request.RequestData["position"];
|
||||||
|
|
||||||
|
@ -36,10 +36,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @category ui
|
* @category ui
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetStudioModeEnabled(const Request&)
|
RequestResult RequestHandler::GetStudioModeEnabled(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["studioModeEnabled"] = obs_frontend_preview_program_mode_active();
|
responseData["studioModeEnabled"] =
|
||||||
|
obs_frontend_preview_program_mode_active();
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ RequestResult RequestHandler::GetStudioModeEnabled(const Request&)
|
|||||||
* @category ui
|
* @category ui
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::SetStudioModeEnabled(const Request& request)
|
RequestResult RequestHandler::SetStudioModeEnabled(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
@ -63,14 +64,20 @@ 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() != request.RequestData["studioModeEnabled"]) {
|
if (obs_frontend_preview_program_mode_active() !=
|
||||||
|
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 = request.RequestData["studioModeEnabled"];
|
bool studioModeEnabled =
|
||||||
|
request.RequestData["studioModeEnabled"];
|
||||||
// Queue the task inside of the UI thread to prevent race conditions
|
// Queue the task inside of the UI thread to prevent race conditions
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(
|
||||||
auto studioModeEnabled = (bool*)param;
|
OBS_TASK_UI,
|
||||||
obs_frontend_set_preview_program_mode(*studioModeEnabled);
|
[](void *param) {
|
||||||
}, &studioModeEnabled, true);
|
auto studioModeEnabled = (bool *)param;
|
||||||
|
obs_frontend_set_preview_program_mode(
|
||||||
|
*studioModeEnabled);
|
||||||
|
},
|
||||||
|
&studioModeEnabled, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
@ -88,11 +95,12 @@ RequestResult RequestHandler::SetStudioModeEnabled(const Request& request)
|
|||||||
* @category ui
|
* @category ui
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::OpenInputPropertiesDialog(const Request& request)
|
RequestResult RequestHandler::OpenInputPropertiesDialog(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
request.ValidateInput("inputName", statusCode, comment);
|
||||||
if (!input)
|
if (!input)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -113,11 +121,12 @@ RequestResult RequestHandler::OpenInputPropertiesDialog(const Request& request)
|
|||||||
* @category ui
|
* @category ui
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::OpenInputFiltersDialog(const Request& request)
|
RequestResult RequestHandler::OpenInputFiltersDialog(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
request.ValidateInput("inputName", statusCode, comment);
|
||||||
if (!input)
|
if (!input)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -138,16 +147,19 @@ RequestResult RequestHandler::OpenInputFiltersDialog(const Request& request)
|
|||||||
* @category ui
|
* @category ui
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::OpenInputInteractDialog(const Request& request)
|
RequestResult RequestHandler::OpenInputInteractDialog(const Request &request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
OBSSourceAutoRelease input =
|
||||||
|
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(RequestStatus::InvalidResourceState, "The specified input does not support interaction.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidResourceState,
|
||||||
|
"The specified input does not support interaction.");
|
||||||
|
|
||||||
obs_frontend_open_source_interaction(input);
|
obs_frontend_open_source_interaction(input);
|
||||||
|
|
||||||
@ -166,15 +178,15 @@ RequestResult RequestHandler::OpenInputInteractDialog(const Request& request)
|
|||||||
* @category ui
|
* @category ui
|
||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetMonitorList(const Request&)
|
RequestResult RequestHandler::GetMonitorList(const Request &)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
std::vector<json> monitorsData;
|
std::vector<json> monitorsData;
|
||||||
QList<QScreen *> screensList = QGuiApplication::screens();
|
QList<QScreen *> screensList = QGuiApplication::screens();
|
||||||
for (int screenIndex = 0; screenIndex < screensList.size(); screenIndex++)
|
for (int screenIndex = 0; screenIndex < screensList.size();
|
||||||
{
|
screenIndex++) {
|
||||||
json screenData;
|
json screenData;
|
||||||
QScreen const* screen = screensList[screenIndex];
|
QScreen const *screen = screensList[screenIndex];
|
||||||
std::stringstream nameAndIndex;
|
std::stringstream nameAndIndex;
|
||||||
nameAndIndex << screen->name().toStdString();
|
nameAndIndex << screen->name().toStdString();
|
||||||
nameAndIndex << '(' << screenIndex << ')';
|
nameAndIndex << '(' << screenIndex << ')';
|
||||||
|
@ -29,88 +29,115 @@ json GetDefaultJsonObject(const json &requestData)
|
|||||||
return requestData;
|
return requestData;
|
||||||
}
|
}
|
||||||
|
|
||||||
Request::Request(const std::string &requestType, const json &requestData, const RequestBatchExecutionType::RequestBatchExecutionType executionType) :
|
Request::Request(const std::string &requestType, const json &requestData,
|
||||||
RequestType(requestType),
|
const RequestBatchExecutionType::RequestBatchExecutionType
|
||||||
HasRequestData(requestData.is_object()),
|
executionType)
|
||||||
RequestData(GetDefaultJsonObject(requestData)),
|
: RequestType(requestType),
|
||||||
ExecutionType(executionType)
|
HasRequestData(requestData.is_object()),
|
||||||
|
RequestData(GetDefaultJsonObject(requestData)),
|
||||||
|
ExecutionType(executionType)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::Contains(const std::string &keyName) const
|
bool Request::Contains(const std::string &keyName) const
|
||||||
{
|
{
|
||||||
return (RequestData.contains(keyName) && !RequestData[keyName].is_null());
|
return (RequestData.contains(keyName) &&
|
||||||
|
!RequestData[keyName].is_null());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
bool Request::ValidateBasic(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const
|
||||||
{
|
{
|
||||||
if (!HasRequestData) {
|
if (!HasRequestData) {
|
||||||
statusCode = RequestStatus::MissingRequestData;
|
statusCode = RequestStatus::MissingRequestData;
|
||||||
comment = "Your request data is missing or invalid (non-object)";
|
comment =
|
||||||
|
"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 `") + keyName + "` field.";
|
comment = std::string("Your request is missing the `") +
|
||||||
|
keyName + "` field.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue, const double maxValue) const
|
bool Request::ValidateOptionalNumber(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const double minValue,
|
||||||
|
const double maxValue) const
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_number()) {
|
if (!RequestData[keyName].is_number()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
comment = std::string("The field value of `") + keyName + "` must be a number.";
|
comment = std::string("The field value of `") + keyName +
|
||||||
|
"` 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 + "` is below the minimum of `" + std::to_string(minValue) + "`";
|
comment = std::string("The field value of `") + keyName +
|
||||||
|
"` is below the minimum of `" +
|
||||||
|
std::to_string(minValue) + "`";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (value > maxValue) {
|
if (value > maxValue) {
|
||||||
statusCode = RequestStatus::RequestFieldOutOfRange;
|
statusCode = RequestStatus::RequestFieldOutOfRange;
|
||||||
comment = std::string("The field value of `") + keyName + "` is above the maximum of `" + std::to_string(maxValue) + "`";
|
comment = std::string("The field value of `") + keyName +
|
||||||
|
"` is above the maximum of `" +
|
||||||
|
std::to_string(maxValue) + "`";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue, const double maxValue) const
|
bool Request::ValidateNumber(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment, const double minValue,
|
||||||
|
const double maxValue) const
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!ValidateOptionalNumber(keyName, statusCode, comment, minValue, maxValue))
|
if (!ValidateOptionalNumber(keyName, statusCode, comment, minValue,
|
||||||
|
maxValue))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateOptionalString(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_string()) {
|
if (!RequestData[keyName].is_string()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
comment = std::string("The field value of `") + keyName + "` must be a string.";
|
comment = std::string("The field value of `") + keyName +
|
||||||
|
"` 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 + "` must not be empty.";
|
comment = std::string("The field value of `") + keyName +
|
||||||
|
"` must not be empty.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateString(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment, const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -121,18 +148,23 @@ bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
bool Request::ValidateOptionalBoolean(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_boolean()) {
|
if (!RequestData[keyName].is_boolean()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
comment = std::string("The field value of `") + keyName + "` must be boolean.";
|
comment = std::string("The field value of `") + keyName +
|
||||||
|
"` must be boolean.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
bool Request::ValidateBoolean(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -143,24 +175,31 @@ bool Request::ValidateBoolean(const std::string &keyName, RequestStatus::Request
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateOptionalObject(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_object()) {
|
if (!RequestData[keyName].is_object()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
comment = std::string("The field value of `") + keyName + "` must be an object.";
|
comment = std::string("The field value of `") + keyName +
|
||||||
|
"` 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 + "` must not be empty.";
|
comment = std::string("The field value of `") + keyName +
|
||||||
|
"` must not be empty.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateObject(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment, const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -171,24 +210,31 @@ bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateOptionalArray(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_array()) {
|
if (!RequestData[keyName].is_array()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
comment = std::string("The field value of `") + keyName + "` must be an array.";
|
comment = std::string("The field value of `") + keyName +
|
||||||
|
"` 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 + "` must not be empty.";
|
comment = std::string("The field value of `") + keyName +
|
||||||
|
"` must not be empty.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateArray(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment, const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -199,7 +245,9 @@ bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestSt
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
obs_source_t *Request::ValidateSource(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const
|
||||||
{
|
{
|
||||||
if (!ValidateString(keyName, statusCode, comment))
|
if (!ValidateString(keyName, statusCode, comment))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -209,14 +257,18 @@ obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus:
|
|||||||
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 `") + sourceName + "`.";
|
comment = std::string("No source was found by the name of `") +
|
||||||
|
sourceName + "`.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
|
obs_source_t *Request::ValidateScene(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const ObsWebSocketSceneFilter filter) const
|
||||||
{
|
{
|
||||||
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
@ -235,7 +287,8 @@ obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::
|
|||||||
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 && !isGroup) {
|
} else if (filter == OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY &&
|
||||||
|
!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)";
|
||||||
@ -245,9 +298,13 @@ obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
|
obs_scene_t *Request::ValidateScene2(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const ObsWebSocketSceneFilter filter) const
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease sceneSource = ValidateSource(keyName, statusCode, comment);
|
OBSSourceAutoRelease sceneSource =
|
||||||
|
ValidateSource(keyName, statusCode, comment);
|
||||||
if (!sceneSource)
|
if (!sceneSource)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
@ -261,21 +318,25 @@ obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::
|
|||||||
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 = "The specified source is not a scene. (Is group)";
|
comment =
|
||||||
|
"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 = "The specified source is not a group. (Is scene)";
|
comment =
|
||||||
|
"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, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
obs_source_t *Request::ValidateInput(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const
|
||||||
{
|
{
|
||||||
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
@ -291,9 +352,13 @@ obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
FilterPair Request::ValidateFilter(const std::string &sourceKeyName,
|
||||||
|
const std::string &filterKeyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const
|
||||||
{
|
{
|
||||||
obs_source_t *source = ValidateSource(sourceKeyName, statusCode, comment);
|
obs_source_t *source =
|
||||||
|
ValidateSource(sourceKeyName, statusCode, comment);
|
||||||
if (!source)
|
if (!source)
|
||||||
return FilterPair{source, nullptr};
|
return FilterPair{source, nullptr};
|
||||||
|
|
||||||
@ -302,19 +367,26 @@ FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::
|
|||||||
|
|
||||||
std::string filterName = RequestData[filterKeyName];
|
std::string filterName = RequestData[filterKeyName];
|
||||||
|
|
||||||
obs_source_t *filter = obs_source_get_filter_by_name(source, filterName.c_str());
|
obs_source_t *filter =
|
||||||
|
obs_source_get_filter_by_name(source, filterName.c_str());
|
||||||
if (!filter) {
|
if (!filter) {
|
||||||
statusCode = RequestStatus::ResourceNotFound;
|
statusCode = RequestStatus::ResourceNotFound;
|
||||||
comment = std::string("No filter was found in the source `") + RequestData[sourceKeyName].get<std::string>() + "` with the name `" + filterName + "`.";
|
comment = std::string("No filter was found in the source `") +
|
||||||
|
RequestData[sourceKeyName].get<std::string>() +
|
||||||
|
"` with the name `" + filterName + "`.";
|
||||||
return FilterPair{source, nullptr};
|
return FilterPair{source, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
return FilterPair{source, filter};
|
return FilterPair{source, filter};
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
|
obs_sceneitem_t *Request::ValidateSceneItem(
|
||||||
|
const std::string &sceneKeyName, const std::string &sceneItemIdKeyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const ObsWebSocketSceneFilter filter) const
|
||||||
{
|
{
|
||||||
OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter);
|
OBSSceneAutoRelease scene =
|
||||||
|
ValidateScene2(sceneKeyName, statusCode, comment, filter);
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
@ -323,10 +395,14 @@ obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, con
|
|||||||
|
|
||||||
int64_t sceneItemId = RequestData[sceneItemIdKeyName];
|
int64_t sceneItemId = RequestData[sceneItemIdKeyName];
|
||||||
|
|
||||||
OBSSceneItem sceneItem = obs_scene_find_sceneitem_by_id(scene, sceneItemId);
|
OBSSceneItem sceneItem =
|
||||||
|
obs_scene_find_sceneitem_by_id(scene, sceneItemId);
|
||||||
if (!sceneItem) {
|
if (!sceneItem) {
|
||||||
statusCode = RequestStatus::ResourceNotFound;
|
statusCode = RequestStatus::ResourceNotFound;
|
||||||
comment = std::string("No scene items were found in scene `") + RequestData[sceneKeyName].get<std::string>() + "` with the ID `" + std::to_string(sceneItemId) + "`.";
|
comment = std::string("No scene items were found in scene `") +
|
||||||
|
RequestData[sceneKeyName].get<std::string>() +
|
||||||
|
"` with the ID `" + std::to_string(sceneItemId) +
|
||||||
|
"`.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,32 +35,89 @@ struct FilterPair {
|
|||||||
OBSSourceAutoRelease filter;
|
OBSSourceAutoRelease filter;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Request
|
struct Request {
|
||||||
{
|
Request(const std::string &requestType,
|
||||||
Request(const std::string &requestType, const json &requestData = nullptr, const RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::None);
|
const json &requestData = nullptr,
|
||||||
|
const RequestBatchExecutionType::RequestBatchExecutionType
|
||||||
|
executionType = RequestBatchExecutionType::None);
|
||||||
|
|
||||||
// Contains the key and is not null
|
// Contains the key and is not null
|
||||||
bool Contains(const std::string &keyName) const;
|
bool Contains(const std::string &keyName) const;
|
||||||
|
|
||||||
bool ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
bool ValidateBasic(const std::string &keyName,
|
||||||
bool ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const;
|
RequestStatus::RequestStatus &statusCode,
|
||||||
bool ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const;
|
std::string &comment) const;
|
||||||
bool ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
bool ValidateOptionalNumber(const std::string &keyName,
|
||||||
bool ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
RequestStatus::RequestStatus &statusCode,
|
||||||
bool ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
std::string &comment,
|
||||||
bool ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
const double minValue = -INFINITY,
|
||||||
bool ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
const double maxValue = INFINITY) const;
|
||||||
bool ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
bool ValidateNumber(const std::string &keyName,
|
||||||
bool ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
RequestStatus::RequestStatus &statusCode,
|
||||||
bool ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
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;
|
||||||
|
bool ValidateString(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const bool allowEmpty = false) const;
|
||||||
|
bool ValidateOptionalBoolean(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const;
|
||||||
|
bool ValidateBoolean(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const;
|
||||||
|
bool ValidateOptionalObject(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const bool allowEmpty = false) const;
|
||||||
|
bool ValidateObject(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const bool allowEmpty = false) const;
|
||||||
|
bool ValidateOptionalArray(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const bool allowEmpty = false) const;
|
||||||
|
bool ValidateArray(const std::string &keyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const bool allowEmpty = false) const;
|
||||||
|
|
||||||
// All return values have incremented refcounts
|
// All return values have incremented refcounts
|
||||||
obs_source_t *ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
obs_source_t *ValidateSource(const std::string &keyName,
|
||||||
obs_source_t *ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, 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, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
std::string &comment) const;
|
||||||
obs_source_t *ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
obs_source_t *
|
||||||
FilterPair ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
ValidateScene(const std::string &keyName,
|
||||||
obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const ObsWebSocketSceneFilter filter =
|
||||||
|
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;
|
||||||
|
FilterPair ValidateFilter(const std::string &sourceKeyName,
|
||||||
|
const std::string &filterKeyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const;
|
||||||
|
obs_sceneitem_t *
|
||||||
|
ValidateSceneItem(const std::string &sceneKeyName,
|
||||||
|
const std::string &sceneItemIdKeyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment,
|
||||||
|
const ObsWebSocketSceneFilter filter =
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
||||||
|
|
||||||
std::string RequestType;
|
std::string RequestType;
|
||||||
bool HasRequestData;
|
bool HasRequestData;
|
||||||
|
@ -18,9 +18,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "RequestBatchRequest.h"
|
#include "RequestBatchRequest.h"
|
||||||
|
|
||||||
RequestBatchRequest::RequestBatchRequest(const std::string &requestType, const json &requestData, RequestBatchExecutionType::RequestBatchExecutionType executionType, const json &inputVariables, const json &outputVariables) :
|
RequestBatchRequest::RequestBatchRequest(
|
||||||
Request(requestType, requestData, executionType),
|
const std::string &requestType, const json &requestData,
|
||||||
InputVariables(inputVariables),
|
RequestBatchExecutionType::RequestBatchExecutionType executionType,
|
||||||
OutputVariables(outputVariables)
|
const json &inputVariables, const json &outputVariables)
|
||||||
|
: Request(requestType, requestData, executionType),
|
||||||
|
InputVariables(inputVariables),
|
||||||
|
OutputVariables(outputVariables)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Request.h"
|
#include "Request.h"
|
||||||
|
|
||||||
struct RequestBatchRequest : Request {
|
struct RequestBatchRequest : Request {
|
||||||
RequestBatchRequest(const std::string &requestType, const json &requestData, RequestBatchExecutionType::RequestBatchExecutionType executionType, const json &inputVariables = nullptr, const json &outputVariables = nullptr);
|
RequestBatchRequest(const std::string &requestType,
|
||||||
|
const json &requestData,
|
||||||
|
RequestBatchExecutionType::RequestBatchExecutionType
|
||||||
|
executionType,
|
||||||
|
const json &inputVariables = nullptr,
|
||||||
|
const json &outputVariables = nullptr);
|
||||||
|
|
||||||
json InputVariables;
|
json InputVariables;
|
||||||
json OutputVariables;
|
json OutputVariables;
|
||||||
|
@ -19,11 +19,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "RequestResult.h"
|
#include "RequestResult.h"
|
||||||
|
|
||||||
RequestResult::RequestResult(RequestStatus::RequestStatus statusCode, json responseData, std::string comment) :
|
RequestResult::RequestResult(RequestStatus::RequestStatus statusCode,
|
||||||
StatusCode(statusCode),
|
json responseData, std::string comment)
|
||||||
ResponseData(responseData),
|
: StatusCode(statusCode),
|
||||||
Comment(comment),
|
ResponseData(responseData),
|
||||||
SleepFrames(0)
|
Comment(comment),
|
||||||
|
SleepFrames(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +33,8 @@ RequestResult RequestResult::Success(json responseData)
|
|||||||
return RequestResult(RequestStatus::Success, responseData, "");
|
return RequestResult(RequestStatus::Success, responseData, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestResult RequestResult::Error(RequestStatus::RequestStatus statusCode, std::string comment)
|
RequestResult RequestResult::Error(RequestStatus::RequestStatus statusCode,
|
||||||
|
std::string comment)
|
||||||
{
|
{
|
||||||
return RequestResult(statusCode, nullptr, comment);
|
return RequestResult(statusCode, nullptr, comment);
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,13 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../types/RequestStatus.h"
|
#include "../types/RequestStatus.h"
|
||||||
#include "../../utils/Json.h"
|
#include "../../utils/Json.h"
|
||||||
|
|
||||||
struct RequestResult
|
struct RequestResult {
|
||||||
{
|
RequestResult(RequestStatus::RequestStatus statusCode =
|
||||||
RequestResult(RequestStatus::RequestStatus statusCode = RequestStatus::Success, json responseData = nullptr, std::string comment = "");
|
RequestStatus::Success,
|
||||||
|
json responseData = nullptr, std::string comment = "");
|
||||||
static RequestResult Success(json responseData = nullptr);
|
static RequestResult Success(json responseData = nullptr);
|
||||||
static RequestResult Error(RequestStatus::RequestStatus statusCode, std::string comment = "");
|
static RequestResult Error(RequestStatus::RequestStatus statusCode,
|
||||||
|
std::string comment = "");
|
||||||
RequestStatus::RequestStatus StatusCode;
|
RequestStatus::RequestStatus StatusCode;
|
||||||
json ResponseData;
|
json ResponseData;
|
||||||
std::string Comment;
|
std::string Comment;
|
||||||
|
@ -22,8 +22,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace RequestBatchExecutionType {
|
namespace RequestBatchExecutionType {
|
||||||
enum RequestBatchExecutionType: int8_t {
|
enum RequestBatchExecutionType : int8_t {
|
||||||
/**
|
/**
|
||||||
* Not a request batch.
|
* Not a request batch.
|
||||||
*
|
*
|
||||||
* @enumIdentifier None
|
* @enumIdentifier None
|
||||||
@ -33,8 +33,8 @@ namespace RequestBatchExecutionType {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
None = -1,
|
None = -1,
|
||||||
/**
|
/**
|
||||||
* A request batch which processes all requests serially, as fast as possible.
|
* A request batch which processes all requests serially, as fast as possible.
|
||||||
*
|
*
|
||||||
* Note: To introduce artificial delay, use the `Sleep` request and the `sleepMillis` request field.
|
* Note: To introduce artificial delay, use the `Sleep` request and the `sleepMillis` request field.
|
||||||
@ -46,8 +46,8 @@ namespace RequestBatchExecutionType {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
SerialRealtime = 0,
|
SerialRealtime = 0,
|
||||||
/**
|
/**
|
||||||
* A request batch type which processes all requests serially, in sync with the graphics thread. Designed to
|
* A request batch type which processes all requests serially, in sync with the graphics thread. Designed to
|
||||||
* provide high accuracy for animations.
|
* provide high accuracy for animations.
|
||||||
*
|
*
|
||||||
@ -60,8 +60,8 @@ namespace RequestBatchExecutionType {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
SerialFrame = 1,
|
SerialFrame = 1,
|
||||||
/**
|
/**
|
||||||
* A request batch type which processes all requests using all available threads in the thread pool.
|
* A request batch type which processes all requests using all available threads in the thread pool.
|
||||||
*
|
*
|
||||||
* Note: This is mainly experimental, and only really shows its colors during requests which require lots of
|
* Note: This is mainly experimental, and only really shows its colors during requests which require lots of
|
||||||
@ -74,11 +74,11 @@ namespace RequestBatchExecutionType {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Parallel = 2,
|
Parallel = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool IsValid(int8_t executionType)
|
inline bool IsValid(int8_t executionType)
|
||||||
{
|
{
|
||||||
return executionType >= None && executionType <= Parallel;
|
return executionType >= None && executionType <= Parallel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace RequestStatus {
|
namespace RequestStatus {
|
||||||
enum RequestStatus {
|
enum RequestStatus {
|
||||||
/**
|
/**
|
||||||
* Unknown status, should never be used.
|
* Unknown status, should never be used.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Unknown
|
* @enumIdentifier Unknown
|
||||||
@ -31,9 +31,9 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For internal use to signify a successful field check.
|
* For internal use to signify a successful field check.
|
||||||
*
|
*
|
||||||
* @enumIdentifier NoError
|
* @enumIdentifier NoError
|
||||||
@ -43,9 +43,9 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
NoError = 10,
|
NoError = 10,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The request has succeeded.
|
* The request has succeeded.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Success
|
* @enumIdentifier Success
|
||||||
@ -55,9 +55,9 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Success = 100,
|
Success = 100,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `requestType` field is missing from the request data.
|
* The `requestType` field is missing from the request data.
|
||||||
*
|
*
|
||||||
* @enumIdentifier MissingRequestType
|
* @enumIdentifier MissingRequestType
|
||||||
@ -67,8 +67,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
MissingRequestType = 203,
|
MissingRequestType = 203,
|
||||||
/**
|
/**
|
||||||
* The request type is invalid or does not exist.
|
* The request type is invalid or does not exist.
|
||||||
*
|
*
|
||||||
* @enumIdentifier UnknownRequestType
|
* @enumIdentifier UnknownRequestType
|
||||||
@ -78,8 +78,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
UnknownRequestType = 204,
|
UnknownRequestType = 204,
|
||||||
/**
|
/**
|
||||||
* Generic error code.
|
* Generic error code.
|
||||||
*
|
*
|
||||||
* Note: A comment is required to be provided by obs-websocket.
|
* Note: A comment is required to be provided by obs-websocket.
|
||||||
@ -91,8 +91,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
GenericError = 205,
|
GenericError = 205,
|
||||||
/**
|
/**
|
||||||
* The request batch execution type is not supported.
|
* The request batch execution type is not supported.
|
||||||
*
|
*
|
||||||
* @enumIdentifier UnsupportedRequestBatchExecutionType
|
* @enumIdentifier UnsupportedRequestBatchExecutionType
|
||||||
@ -102,9 +102,9 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
UnsupportedRequestBatchExecutionType = 206,
|
UnsupportedRequestBatchExecutionType = 206,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A required request field is missing.
|
* A required request field is missing.
|
||||||
*
|
*
|
||||||
* @enumIdentifier MissingRequestField
|
* @enumIdentifier MissingRequestField
|
||||||
@ -114,8 +114,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
MissingRequestField = 300,
|
MissingRequestField = 300,
|
||||||
/**
|
/**
|
||||||
* The request does not have a valid requestData object.
|
* The request does not have a valid requestData object.
|
||||||
*
|
*
|
||||||
* @enumIdentifier MissingRequestData
|
* @enumIdentifier MissingRequestData
|
||||||
@ -125,9 +125,9 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
MissingRequestData = 301,
|
MissingRequestData = 301,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic invalid request field message.
|
* Generic invalid request field message.
|
||||||
*
|
*
|
||||||
* Note: A comment is required to be provided by obs-websocket.
|
* Note: A comment is required to be provided by obs-websocket.
|
||||||
@ -139,8 +139,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InvalidRequestField = 400,
|
InvalidRequestField = 400,
|
||||||
/**
|
/**
|
||||||
* A request field has the wrong data type.
|
* A request field has the wrong data type.
|
||||||
*
|
*
|
||||||
* @enumIdentifier InvalidRequestFieldType
|
* @enumIdentifier InvalidRequestFieldType
|
||||||
@ -150,8 +150,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InvalidRequestFieldType = 401,
|
InvalidRequestFieldType = 401,
|
||||||
/**
|
/**
|
||||||
* A request field (number) is outside of the allowed range.
|
* A request field (number) is outside of the allowed range.
|
||||||
*
|
*
|
||||||
* @enumIdentifier RequestFieldOutOfRange
|
* @enumIdentifier RequestFieldOutOfRange
|
||||||
@ -161,8 +161,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
RequestFieldOutOfRange = 402,
|
RequestFieldOutOfRange = 402,
|
||||||
/**
|
/**
|
||||||
* A request field (string or array) is empty and cannot be.
|
* A request field (string or array) is empty and cannot be.
|
||||||
*
|
*
|
||||||
* @enumIdentifier RequestFieldEmpty
|
* @enumIdentifier RequestFieldEmpty
|
||||||
@ -172,8 +172,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
RequestFieldEmpty = 403,
|
RequestFieldEmpty = 403,
|
||||||
/**
|
/**
|
||||||
* There are too many request fields (eg. a request takes two optionals, where only one is allowed at a time).
|
* There are too many request fields (eg. a request takes two optionals, where only one is allowed at a time).
|
||||||
*
|
*
|
||||||
* @enumIdentifier TooManyRequestFields
|
* @enumIdentifier TooManyRequestFields
|
||||||
@ -183,9 +183,9 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
TooManyRequestFields = 404,
|
TooManyRequestFields = 404,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An output is running and cannot be in order to perform the request.
|
* An output is running and cannot be in order to perform the request.
|
||||||
*
|
*
|
||||||
* @enumIdentifier OutputRunning
|
* @enumIdentifier OutputRunning
|
||||||
@ -195,8 +195,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
OutputRunning = 500,
|
OutputRunning = 500,
|
||||||
/**
|
/**
|
||||||
* An output is not running and should be.
|
* An output is not running and should be.
|
||||||
*
|
*
|
||||||
* @enumIdentifier OutputNotRunning
|
* @enumIdentifier OutputNotRunning
|
||||||
@ -206,8 +206,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
OutputNotRunning = 501,
|
OutputNotRunning = 501,
|
||||||
/**
|
/**
|
||||||
* An output is paused and should not be.
|
* An output is paused and should not be.
|
||||||
*
|
*
|
||||||
* @enumIdentifier OutputPaused
|
* @enumIdentifier OutputPaused
|
||||||
@ -217,8 +217,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
OutputPaused = 502,
|
OutputPaused = 502,
|
||||||
/**
|
/**
|
||||||
* An output is not paused and should be.
|
* An output is not paused and should be.
|
||||||
*
|
*
|
||||||
* @enumIdentifier OutputNotPaused
|
* @enumIdentifier OutputNotPaused
|
||||||
@ -228,8 +228,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
OutputNotPaused = 503,
|
OutputNotPaused = 503,
|
||||||
/**
|
/**
|
||||||
* An output is disabled and should not be.
|
* An output is disabled and should not be.
|
||||||
*
|
*
|
||||||
* @enumIdentifier OutputDisabled
|
* @enumIdentifier OutputDisabled
|
||||||
@ -239,8 +239,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
OutputDisabled = 504,
|
OutputDisabled = 504,
|
||||||
/**
|
/**
|
||||||
* Studio mode is active and cannot be.
|
* Studio mode is active and cannot be.
|
||||||
*
|
*
|
||||||
* @enumIdentifier StudioModeActive
|
* @enumIdentifier StudioModeActive
|
||||||
@ -250,8 +250,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
StudioModeActive = 505,
|
StudioModeActive = 505,
|
||||||
/**
|
/**
|
||||||
* Studio mode is not active and should be.
|
* Studio mode is not active and should be.
|
||||||
*
|
*
|
||||||
* @enumIdentifier StudioModeNotActive
|
* @enumIdentifier StudioModeNotActive
|
||||||
@ -261,10 +261,9 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
StudioModeNotActive = 506,
|
StudioModeNotActive = 506,
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The resource was not found.
|
* The resource was not found.
|
||||||
*
|
*
|
||||||
* Note: Resources are any kind of object in obs-websocket, like inputs, profiles, outputs, etc.
|
* Note: Resources are any kind of object in obs-websocket, like inputs, profiles, outputs, etc.
|
||||||
@ -276,8 +275,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
ResourceNotFound = 600,
|
ResourceNotFound = 600,
|
||||||
/**
|
/**
|
||||||
* The resource already exists.
|
* The resource already exists.
|
||||||
*
|
*
|
||||||
* @enumIdentifier ResourceAlreadyExists
|
* @enumIdentifier ResourceAlreadyExists
|
||||||
@ -287,8 +286,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
ResourceAlreadyExists = 601,
|
ResourceAlreadyExists = 601,
|
||||||
/**
|
/**
|
||||||
* The type of resource found is invalid.
|
* The type of resource found is invalid.
|
||||||
*
|
*
|
||||||
* @enumIdentifier InvalidResourceType
|
* @enumIdentifier InvalidResourceType
|
||||||
@ -298,8 +297,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InvalidResourceType = 602,
|
InvalidResourceType = 602,
|
||||||
/**
|
/**
|
||||||
* There are not enough instances of the resource in order to perform the request.
|
* There are not enough instances of the resource in order to perform the request.
|
||||||
*
|
*
|
||||||
* @enumIdentifier NotEnoughResources
|
* @enumIdentifier NotEnoughResources
|
||||||
@ -309,8 +308,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
NotEnoughResources = 603,
|
NotEnoughResources = 603,
|
||||||
/**
|
/**
|
||||||
* The state of the resource is invalid. For example, if the resource is blocked from being accessed.
|
* The state of the resource is invalid. For example, if the resource is blocked from being accessed.
|
||||||
*
|
*
|
||||||
* @enumIdentifier InvalidResourceState
|
* @enumIdentifier InvalidResourceState
|
||||||
@ -320,8 +319,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InvalidResourceState = 604,
|
InvalidResourceState = 604,
|
||||||
/**
|
/**
|
||||||
* The specified input (obs_source_t-OBS_SOURCE_TYPE_INPUT) had the wrong kind.
|
* The specified input (obs_source_t-OBS_SOURCE_TYPE_INPUT) had the wrong kind.
|
||||||
*
|
*
|
||||||
* @enumIdentifier InvalidInputKind
|
* @enumIdentifier InvalidInputKind
|
||||||
@ -331,8 +330,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InvalidInputKind = 605,
|
InvalidInputKind = 605,
|
||||||
/**
|
/**
|
||||||
* The resource does not support being configured.
|
* The resource does not support being configured.
|
||||||
*
|
*
|
||||||
* This is particularly relevant to transitions, where they do not always have changeable settings.
|
* This is particularly relevant to transitions, where they do not always have changeable settings.
|
||||||
@ -344,8 +343,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
ResourceNotConfigurable = 606,
|
ResourceNotConfigurable = 606,
|
||||||
/**
|
/**
|
||||||
* The specified filter (obs_source_t-OBS_SOURCE_TYPE_FILTER) had the wrong kind.
|
* The specified filter (obs_source_t-OBS_SOURCE_TYPE_FILTER) had the wrong kind.
|
||||||
*
|
*
|
||||||
* @enumIdentifier InvalidFilterKind
|
* @enumIdentifier InvalidFilterKind
|
||||||
@ -355,9 +354,9 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InvalidFilterKind = 607,
|
InvalidFilterKind = 607,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creating the resource failed.
|
* Creating the resource failed.
|
||||||
*
|
*
|
||||||
* @enumIdentifier ResourceCreationFailed
|
* @enumIdentifier ResourceCreationFailed
|
||||||
@ -367,8 +366,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
ResourceCreationFailed = 700,
|
ResourceCreationFailed = 700,
|
||||||
/**
|
/**
|
||||||
* Performing an action on the resource failed.
|
* Performing an action on the resource failed.
|
||||||
*
|
*
|
||||||
* @enumIdentifier ResourceActionFailed
|
* @enumIdentifier ResourceActionFailed
|
||||||
@ -378,8 +377,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
ResourceActionFailed = 701,
|
ResourceActionFailed = 701,
|
||||||
/**
|
/**
|
||||||
* Processing the request failed unexpectedly.
|
* Processing the request failed unexpectedly.
|
||||||
*
|
*
|
||||||
* Note: A comment is required to be provided by obs-websocket.
|
* Note: A comment is required to be provided by obs-websocket.
|
||||||
@ -391,8 +390,8 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
RequestProcessingFailed = 702,
|
RequestProcessingFailed = 702,
|
||||||
/**
|
/**
|
||||||
* The combination of request fields cannot be used to perform an action.
|
* The combination of request fields cannot be used to perform an action.
|
||||||
*
|
*
|
||||||
* @enumIdentifier CannotAct
|
* @enumIdentifier CannotAct
|
||||||
@ -402,6 +401,6 @@ namespace RequestStatus {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
CannotAct = 703,
|
CannotAct = 703,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "Compat.h"
|
#include "Compat.h"
|
||||||
|
|
||||||
Utils::Compat::StdFunctionRunnable::StdFunctionRunnable(std::function<void()> func) :
|
Utils::Compat::StdFunctionRunnable::StdFunctionRunnable(
|
||||||
cb(std::move(func))
|
std::function<void()> func)
|
||||||
|
: cb(std::move(func))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,15 +23,16 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Compat {
|
namespace Compat {
|
||||||
// Reimplement QRunnable for std::function. Retrocompatability for Qt < 5.15
|
// Reimplement QRunnable for std::function. Retrocompatability for Qt < 5.15
|
||||||
class StdFunctionRunnable : public QRunnable {
|
class StdFunctionRunnable : public QRunnable {
|
||||||
std::function<void()> cb;
|
std::function<void()> cb;
|
||||||
public:
|
|
||||||
StdFunctionRunnable(std::function<void()> func);
|
|
||||||
void run() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
QRunnable *CreateFunctionRunnable(std::function<void()> func);
|
public:
|
||||||
}
|
StdFunctionRunnable(std::function<void()> func);
|
||||||
|
void run() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
QRunnable *CreateFunctionRunnable(std::function<void()> func);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,8 @@ 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[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
static const char allowedChars[] =
|
||||||
|
"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()
|
||||||
@ -42,10 +43,12 @@ std::string Utils::Crypto::GenerateSalt()
|
|||||||
return randomChars.toBase64().toStdString();
|
return randomChars.toBase64().toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Crypto::GenerateSecret(std::string password, std::string salt)
|
std::string Utils::Crypto::GenerateSecret(std::string password,
|
||||||
|
std::string salt)
|
||||||
{
|
{
|
||||||
// Create challenge hash
|
// Create challenge hash
|
||||||
auto challengeHash = QCryptographicHash(QCryptographicHash::Algorithm::Sha256);
|
auto challengeHash =
|
||||||
|
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
|
||||||
@ -55,7 +58,9 @@ std::string Utils::Crypto::GenerateSecret(std::string password, std::string salt
|
|||||||
return challengeHash.result().toBase64().toStdString();
|
return challengeHash.result().toBase64().toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::Crypto::CheckAuthenticationString(std::string secret, std::string challenge, std::string authenticationString)
|
bool Utils::Crypto::CheckAuthenticationString(std::string secret,
|
||||||
|
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 = "";
|
||||||
@ -63,13 +68,13 @@ bool Utils::Crypto::CheckAuthenticationString(std::string secret, std::string ch
|
|||||||
secretAndChallenge += QString::fromStdString(challenge);
|
secretAndChallenge += QString::fromStdString(challenge);
|
||||||
|
|
||||||
// Generate a SHA256 hash of secretAndChallenge
|
// Generate a SHA256 hash of secretAndChallenge
|
||||||
auto hash = QCryptographicHash::hash(
|
auto hash =
|
||||||
secretAndChallenge.toUtf8(),
|
QCryptographicHash::hash(secretAndChallenge.toUtf8(),
|
||||||
QCryptographicHash::Algorithm::Sha256
|
QCryptographicHash::Algorithm::Sha256);
|
||||||
);
|
|
||||||
|
|
||||||
// Encode the SHA256 hash to Base64
|
// Encode the SHA256 hash to Base64
|
||||||
std::string expectedAuthenticationString = hash.toBase64().toStdString();
|
std::string expectedAuthenticationString =
|
||||||
|
hash.toBase64().toStdString();
|
||||||
|
|
||||||
return (authenticationString == expectedAuthenticationString);
|
return (authenticationString == expectedAuthenticationString);
|
||||||
}
|
}
|
||||||
|
@ -23,10 +23,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
namespace Utils {
|
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, std::string authenticationString);
|
bool CheckAuthenticationString(std::string secret, std::string challenge,
|
||||||
std::string GeneratePassword(size_t length = 16);
|
std::string authenticationString);
|
||||||
}
|
std::string GeneratePassword(size_t length = 16);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ void obs_data_set_json_array(obs_data_t *d, const char *key, json j)
|
|||||||
{
|
{
|
||||||
obs_data_array_t *array = obs_data_array_create();
|
obs_data_array_t *array = obs_data_array_create();
|
||||||
|
|
||||||
for (auto& [key, value] : j.items()) {
|
for (auto &[key, value] : j.items()) {
|
||||||
if (!value.is_object())
|
if (!value.is_object())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -61,17 +61,19 @@ void obs_data_set_json_array(obs_data_t *d, const char *key, json j)
|
|||||||
|
|
||||||
void obs_data_set_json_object_item(obs_data_t *d, json j)
|
void obs_data_set_json_object_item(obs_data_t *d, json j)
|
||||||
{
|
{
|
||||||
for (auto& [key, value] : j.items()) {
|
for (auto &[key, value] : j.items()) {
|
||||||
if (value.is_object()) {
|
if (value.is_object()) {
|
||||||
obs_data_set_json_object(d, key.c_str(), value);
|
obs_data_set_json_object(d, key.c_str(), value);
|
||||||
} else if (value.is_array()) {
|
} else if (value.is_array()) {
|
||||||
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(), value.get<std::string>().c_str());
|
obs_data_set_string(d, key.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(), value.get<double>());
|
obs_data_set_double(d, key.c_str(),
|
||||||
|
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>());
|
||||||
}
|
}
|
||||||
@ -114,13 +116,15 @@ 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, bool includeDefault)
|
void set_json_object(json *j, const char *name, obs_data_item_t *item,
|
||||||
|
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, bool includeDefault)
|
void set_json_array(json *j, const char *name, obs_data_item_t *item,
|
||||||
|
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);
|
||||||
@ -128,7 +132,8 @@ void set_json_array(json *j, const char *name, obs_data_item_t *item, bool inclu
|
|||||||
|
|
||||||
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 = Utils::Json::ObsDataToJson(subItem, includeDefault);
|
json jItem =
|
||||||
|
Utils::Json::ObsDataToJson(subItem, includeDefault);
|
||||||
obs_data_release(subItem);
|
obs_data_release(subItem);
|
||||||
jArray.push_back(jItem);
|
jArray.push_back(jItem);
|
||||||
}
|
}
|
||||||
@ -153,23 +158,22 @@ json Utils::Json::ObsDataToJson(obs_data_t *d, bool includeDefault)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case OBS_DATA_STRING:
|
case OBS_DATA_STRING:
|
||||||
set_json_string(&j, name, item);
|
set_json_string(&j, name, item);
|
||||||
break;
|
break;
|
||||||
case OBS_DATA_NUMBER:
|
case OBS_DATA_NUMBER:
|
||||||
set_json_number(&j, name, item);
|
set_json_number(&j, name, item);
|
||||||
break;
|
break;
|
||||||
case OBS_DATA_BOOLEAN:
|
case OBS_DATA_BOOLEAN:
|
||||||
set_json_bool(&j, name, item);
|
set_json_bool(&j, name, item);
|
||||||
break;
|
break;
|
||||||
case OBS_DATA_OBJECT:
|
case OBS_DATA_OBJECT:
|
||||||
set_json_object(&j, name, item, includeDefault);
|
set_json_object(&j, name, item, includeDefault);
|
||||||
break;
|
break;
|
||||||
case OBS_DATA_ARRAY:
|
case OBS_DATA_ARRAY:
|
||||||
set_json_array(&j, name, item, includeDefault);
|
set_json_array(&j, name, item, includeDefault);
|
||||||
break;
|
break;
|
||||||
default:
|
default:;
|
||||||
;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,15 +188,19 @@ bool Utils::Json::GetJsonFileContent(std::string fileName, json &content)
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
content = json::parse(textContent);
|
content = json::parse(textContent);
|
||||||
} catch (json::parse_error& e) {
|
} catch (json::parse_error &e) {
|
||||||
blog(LOG_WARNING, "Failed to decode content of JSON file `%s`. Error: %s", fileName.c_str(), e.what());
|
blog(LOG_WARNING,
|
||||||
|
"Failed to decode content of JSON file `%s`. Error: %s",
|
||||||
|
fileName.c_str(), e.what());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::Json::SetJsonFileContent(std::string fileName, const json &content, bool createNew)
|
bool Utils::Json::SetJsonFileContent(std::string fileName, const json &content,
|
||||||
|
bool createNew)
|
||||||
{
|
{
|
||||||
std::string textContent = content.dump(2);
|
std::string textContent = content.dump(2);
|
||||||
return Utils::Platform::SetTextFileContent(fileName, textContent, createNew);
|
return Utils::Platform::SetTextFileContent(fileName, textContent,
|
||||||
|
createNew);
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,16 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Json {
|
namespace Json {
|
||||||
bool JsonArrayIsValidObsArray(const json &j);
|
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 createNew = true);
|
bool SetJsonFileContent(std::string fileName, const json &content,
|
||||||
static inline bool Contains(const json &j, std::string key) { return j.contains(key) && !j[key].is_null(); }
|
bool createNew = true);
|
||||||
}
|
static inline bool Contains(const json &j, std::string key)
|
||||||
|
{
|
||||||
|
return j.contains(key) && !j[key].is_null();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
225
src/utils/Obs.h
225
src/utils/Obs.h
@ -26,43 +26,65 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
// Autorelease object definitions
|
// Autorelease object definitions
|
||||||
inline void ___properties_dummy_addref(obs_properties_t*){}
|
inline void ___properties_dummy_addref(obs_properties_t *) {}
|
||||||
using OBSPropertiesAutoDestroy = OBSRef<obs_properties_t*, ___properties_dummy_addref, obs_properties_destroy>;
|
using OBSPropertiesAutoDestroy =
|
||||||
|
OBSRef<obs_properties_t *, ___properties_dummy_addref,
|
||||||
|
obs_properties_destroy>;
|
||||||
|
|
||||||
#if !defined(OBS_AUTORELEASE)
|
#if !defined(OBS_AUTORELEASE)
|
||||||
inline void ___source_dummy_addref(obs_source_t*){}
|
inline void ___source_dummy_addref(obs_source_t *) {}
|
||||||
inline void ___scene_dummy_addref(obs_scene_t*){}
|
inline void ___scene_dummy_addref(obs_scene_t *) {}
|
||||||
inline void ___sceneitem_dummy_addref(obs_sceneitem_t*){}
|
inline void ___sceneitem_dummy_addref(obs_sceneitem_t *) {}
|
||||||
inline void ___data_dummy_addref(obs_data_t*){}
|
inline void ___data_dummy_addref(obs_data_t *) {}
|
||||||
inline void ___data_array_dummy_addref(obs_data_array_t*){}
|
inline void ___data_array_dummy_addref(obs_data_array_t *) {}
|
||||||
inline void ___output_dummy_addref(obs_output_t*){}
|
inline void ___output_dummy_addref(obs_output_t *) {}
|
||||||
inline void ___encoder_dummy_addref(obs_encoder_t *){}
|
inline void ___encoder_dummy_addref(obs_encoder_t *) {}
|
||||||
inline void ___service_dummy_addref(obs_service_t *){}
|
inline void ___service_dummy_addref(obs_service_t *) {}
|
||||||
|
|
||||||
inline void ___weak_source_dummy_addref(obs_weak_source_t*){}
|
inline void ___weak_source_dummy_addref(obs_weak_source_t *) {}
|
||||||
inline void ___weak_output_dummy_addref(obs_weak_output_t *){}
|
inline void ___weak_output_dummy_addref(obs_weak_output_t *) {}
|
||||||
inline void ___weak_encoder_dummy_addref(obs_weak_encoder_t *){}
|
inline void ___weak_encoder_dummy_addref(obs_weak_encoder_t *) {}
|
||||||
inline void ___weak_service_dummy_addref(obs_weak_service_t *){}
|
inline void ___weak_service_dummy_addref(obs_weak_service_t *) {}
|
||||||
|
|
||||||
using OBSSourceAutoRelease = OBSRef<obs_source_t*, ___source_dummy_addref, obs_source_release>;
|
using OBSSourceAutoRelease =
|
||||||
using OBSSceneAutoRelease = OBSRef<obs_scene_t*, ___scene_dummy_addref, obs_scene_release>;
|
OBSRef<obs_source_t *, ___source_dummy_addref, obs_source_release>;
|
||||||
using OBSSceneItemAutoRelease = OBSRef<obs_sceneitem_t*, ___sceneitem_dummy_addref, obs_sceneitem_release>;
|
using OBSSceneAutoRelease =
|
||||||
using OBSDataAutoRelease = OBSRef<obs_data_t*, ___data_dummy_addref, obs_data_release>;
|
OBSRef<obs_scene_t *, ___scene_dummy_addref, obs_scene_release>;
|
||||||
using OBSDataArrayAutoRelease = OBSRef<obs_data_array_t*, ___data_array_dummy_addref, obs_data_array_release>;
|
using OBSSceneItemAutoRelease =
|
||||||
using OBSOutputAutoRelease = OBSRef<obs_output_t*, ___output_dummy_addref, obs_output_release>;
|
OBSRef<obs_sceneitem_t *, ___sceneitem_dummy_addref,
|
||||||
using OBSEncoderAutoRelease = OBSRef<obs_encoder_t *, ___encoder_dummy_addref, obs_encoder_release>;
|
obs_sceneitem_release>;
|
||||||
using OBSServiceAutoRelease = OBSRef<obs_service_t *, ___service_dummy_addref, obs_service_release>;
|
using OBSDataAutoRelease =
|
||||||
|
OBSRef<obs_data_t *, ___data_dummy_addref, obs_data_release>;
|
||||||
|
using OBSDataArrayAutoRelease =
|
||||||
|
OBSRef<obs_data_array_t *, ___data_array_dummy_addref,
|
||||||
|
obs_data_array_release>;
|
||||||
|
using 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 = OBSRef<obs_weak_source_t*, ___weak_source_dummy_addref, obs_weak_source_release>;
|
using OBSWeakSourceAutoRelease =
|
||||||
using OBSWeakOutputAutoRelease = OBSRef<obs_weak_output_t *, ___weak_output_dummy_addref, obs_weak_output_release>;
|
OBSRef<obs_weak_source_t *, ___weak_source_dummy_addref,
|
||||||
using OBSWeakEncoderAutoRelease = OBSRef<obs_weak_encoder_t *, ___weak_encoder_dummy_addref, obs_weak_encoder_release>;
|
obs_weak_source_release>;
|
||||||
using OBSWeakServiceAutoRelease = OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref, obs_weak_service_release>;
|
using OBSWeakOutputAutoRelease =
|
||||||
|
OBSRef<obs_weak_output_t *, ___weak_output_dummy_addref,
|
||||||
|
obs_weak_output_release>;
|
||||||
|
using OBSWeakEncoderAutoRelease =
|
||||||
|
OBSRef<obs_weak_encoder_t *, ___weak_encoder_dummy_addref,
|
||||||
|
obs_weak_encoder_release>;
|
||||||
|
using OBSWeakServiceAutoRelease =
|
||||||
|
OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref,
|
||||||
|
obs_weak_service_release>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename T> T* GetCalldataPointer(const calldata_t *data, const char* name) {
|
template<typename T>
|
||||||
|
T *GetCalldataPointer(const calldata_t *data, const char *name)
|
||||||
|
{
|
||||||
void *ptr = nullptr;
|
void *ptr = nullptr;
|
||||||
calldata_get_ptr(data, name, &ptr);
|
calldata_get_ptr(data, name, &ptr);
|
||||||
return static_cast<T*>(ptr);
|
return static_cast<T *>(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ObsOutputState {
|
enum ObsOutputState {
|
||||||
@ -150,69 +172,86 @@ enum ObsMediaInputAction {
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Obs {
|
namespace Obs {
|
||||||
namespace StringHelper {
|
namespace StringHelper {
|
||||||
std::string GetObsVersion();
|
std::string GetObsVersion();
|
||||||
std::string GetCurrentSceneCollection();
|
std::string GetCurrentSceneCollection();
|
||||||
std::string GetCurrentProfile();
|
std::string GetCurrentProfile();
|
||||||
std::string GetCurrentProfilePath();
|
std::string GetCurrentProfilePath();
|
||||||
std::string GetCurrentRecordOutputPath();
|
std::string GetCurrentRecordOutputPath();
|
||||||
std::string GetSourceType(obs_source_t *source);
|
std::string GetSourceType(obs_source_t *source);
|
||||||
std::string GetInputMonitorType(enum obs_monitoring_type monitorType);
|
std::string GetInputMonitorType(enum obs_monitoring_type monitorType);
|
||||||
std::string GetInputMonitorType(obs_source_t *input);
|
std::string GetInputMonitorType(obs_source_t *input);
|
||||||
std::string GetMediaInputState(obs_source_t *input);
|
std::string GetMediaInputState(obs_source_t *input);
|
||||||
std::string GetLastReplayBufferFilePath();
|
std::string GetLastReplayBufferFilePath();
|
||||||
std::string GetSceneItemBoundsType(enum obs_bounds_type type);
|
std::string GetSceneItemBoundsType(enum obs_bounds_type type);
|
||||||
std::string GetSceneItemBlendMode(enum obs_blending_type mode);
|
std::string GetSceneItemBlendMode(enum obs_blending_type mode);
|
||||||
std::string DurationToTimecode(uint64_t);
|
std::string DurationToTimecode(uint64_t);
|
||||||
std::string GetOutputState(ObsOutputState state);
|
std::string GetOutputState(ObsOutputState state);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace EnumHelper {
|
namespace EnumHelper {
|
||||||
enum obs_bounds_type GetSceneItemBoundsType(std::string boundsType);
|
enum obs_bounds_type GetSceneItemBoundsType(std::string boundsType);
|
||||||
enum ObsMediaInputAction GetMediaInputAction(std::string mediaAction);
|
enum ObsMediaInputAction GetMediaInputAction(std::string mediaAction);
|
||||||
enum obs_blending_type GetSceneItemBlendMode(std::string mode);
|
enum obs_blending_type GetSceneItemBlendMode(std::string mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace NumberHelper {
|
namespace NumberHelper {
|
||||||
uint64_t GetOutputDuration(obs_output_t *output);
|
uint64_t GetOutputDuration(obs_output_t *output);
|
||||||
size_t GetSceneCount();
|
size_t GetSceneCount();
|
||||||
size_t GetSourceFilterIndex(obs_source_t *source, obs_source_t *filter);
|
size_t GetSourceFilterIndex(obs_source_t *source, obs_source_t *filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ArrayHelper {
|
namespace ArrayHelper {
|
||||||
std::vector<std::string> GetSceneCollectionList();
|
std::vector<std::string> GetSceneCollectionList();
|
||||||
std::vector<std::string> GetProfileList();
|
std::vector<std::string> GetProfileList();
|
||||||
std::vector<obs_hotkey_t *> GetHotkeyList();
|
std::vector<obs_hotkey_t *> GetHotkeyList();
|
||||||
std::vector<std::string> GetHotkeyNameList();
|
std::vector<std::string> GetHotkeyNameList();
|
||||||
std::vector<json> GetSceneList();
|
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, bool includeDisabled = false);
|
std::vector<std::string> GetInputKindList(bool unversioned = false,
|
||||||
std::vector<json> GetListPropertyItems(obs_property_t *property);
|
bool includeDisabled = false);
|
||||||
std::vector<std::string> GetTransitionKindList();
|
std::vector<json> GetListPropertyItems(obs_property_t *property);
|
||||||
std::vector<json> GetSceneTransitionList();
|
std::vector<std::string> GetTransitionKindList();
|
||||||
std::vector<json> GetSourceFilterList(obs_source_t *source);
|
std::vector<json> GetSceneTransitionList();
|
||||||
std::vector<std::string> GetFilterKindList();
|
std::vector<json> GetSourceFilterList(obs_source_t *source);
|
||||||
}
|
std::vector<std::string> GetFilterKindList();
|
||||||
|
}
|
||||||
namespace ObjectHelper {
|
|
||||||
json GetStats();
|
namespace ObjectHelper {
|
||||||
json GetSceneItemTransform(obs_sceneitem_t *item);
|
json GetStats();
|
||||||
}
|
json GetSceneItemTransform(obs_sceneitem_t *item);
|
||||||
|
}
|
||||||
namespace SearchHelper {
|
|
||||||
obs_hotkey_t *GetHotkeyByName(std::string name);
|
namespace SearchHelper {
|
||||||
obs_source_t *GetSceneTransitionByName(std::string name); // Increments source ref. Use OBSSourceAutoRelease
|
obs_hotkey_t *GetHotkeyByName(std::string name);
|
||||||
obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name, int offset = 0); // Increments ref. Use OBSSceneItemAutoRelease
|
obs_source_t *GetSceneTransitionByName(
|
||||||
}
|
std::string name); // Increments source ref. Use OBSSourceAutoRelease
|
||||||
|
obs_sceneitem_t *GetSceneItemByName(
|
||||||
namespace ActionHelper {
|
obs_scene_t *scene, std::string name,
|
||||||
obs_sceneitem_t *CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled = true, obs_transform_info *sceneItemTransform = nullptr, obs_sceneitem_crop *sceneItemCrop = nullptr); // Increments ref. Use OBSSceneItemAutoRelease
|
int offset = 0); // Increments ref. Use OBSSceneItemAutoRelease
|
||||||
obs_sceneitem_t *CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled = true); // Increments ref. Use OBSSceneItemAutoRelease
|
}
|
||||||
obs_source_t *CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind, obs_data_t *filterSettings); // Increments source ref. Use OBSSourceAutoRelease
|
|
||||||
void SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index);
|
namespace ActionHelper {
|
||||||
}
|
obs_sceneitem_t *
|
||||||
}
|
CreateSceneItem(obs_source_t *source, obs_scene_t *scene,
|
||||||
|
bool sceneItemEnabled = true,
|
||||||
|
obs_transform_info *sceneItemTransform = nullptr,
|
||||||
|
obs_sceneitem_crop *sceneItemCrop =
|
||||||
|
nullptr); // Increments ref. Use OBSSceneItemAutoRelease
|
||||||
|
obs_sceneitem_t *
|
||||||
|
CreateInput(std::string inputName, std::string inputKind,
|
||||||
|
obs_data_t *inputSettings, obs_scene_t *scene,
|
||||||
|
bool sceneItemEnabled =
|
||||||
|
true); // Increments ref. Use OBSSceneItemAutoRelease
|
||||||
|
obs_source_t *CreateSourceFilter(
|
||||||
|
obs_source_t *source, std::string filterName, std::string filterKind,
|
||||||
|
obs_data_t *
|
||||||
|
filterSettings); // Increments source ref. Use OBSSourceAutoRelease
|
||||||
|
void SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter,
|
||||||
|
size_t index);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,20 +20,21 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
struct CreateSceneItemData {
|
struct CreateSceneItemData {
|
||||||
obs_source_t *source; // In
|
obs_source_t *source; // In
|
||||||
bool sceneItemEnabled; // In
|
bool sceneItemEnabled; // In
|
||||||
obs_transform_info *sceneItemTransform = nullptr; // In
|
obs_transform_info *sceneItemTransform = nullptr; // In
|
||||||
obs_sceneitem_crop *sceneItemCrop = nullptr; // In
|
obs_sceneitem_crop *sceneItemCrop = nullptr; // In
|
||||||
OBSSceneItem sceneItem; // Out
|
OBSSceneItem sceneItem; // Out
|
||||||
};
|
};
|
||||||
|
|
||||||
void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
||||||
{
|
{
|
||||||
auto *data = static_cast<CreateSceneItemData*>(_data);
|
auto *data = static_cast<CreateSceneItemData *>(_data);
|
||||||
data->sceneItem = obs_scene_add(scene, data->source);
|
data->sceneItem = obs_scene_add(scene, data->source);
|
||||||
|
|
||||||
if (data->sceneItemTransform)
|
if (data->sceneItemTransform)
|
||||||
obs_sceneitem_set_info(data->sceneItem, data->sceneItemTransform);
|
obs_sceneitem_set_info(data->sceneItem,
|
||||||
|
data->sceneItemTransform);
|
||||||
|
|
||||||
if (data->sceneItemCrop)
|
if (data->sceneItemCrop)
|
||||||
obs_sceneitem_set_crop(data->sceneItem, data->sceneItemCrop);
|
obs_sceneitem_set_crop(data->sceneItem, data->sceneItemCrop);
|
||||||
@ -41,7 +42,10 @@ void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
|||||||
obs_sceneitem_set_visible(data->sceneItem, data->sceneItemEnabled);
|
obs_sceneitem_set_visible(data->sceneItem, data->sceneItemEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled, obs_transform_info *sceneItemTransform, obs_sceneitem_crop *sceneItemCrop)
|
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(
|
||||||
|
obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled,
|
||||||
|
obs_transform_info *sceneItemTransform,
|
||||||
|
obs_sceneitem_crop *sceneItemCrop)
|
||||||
{
|
{
|
||||||
// Sanity check for valid scene
|
// Sanity check for valid scene
|
||||||
if (!(source && scene))
|
if (!(source && scene))
|
||||||
@ -64,10 +68,13 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source,
|
|||||||
return data.sceneItem;
|
return data.sceneItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled)
|
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(
|
||||||
|
std::string inputName, std::string inputKind, obs_data_t *inputSettings,
|
||||||
|
obs_scene_t *scene, bool sceneItemEnabled)
|
||||||
{
|
{
|
||||||
// Create the input
|
// Create the input
|
||||||
OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
|
OBSSourceAutoRelease input = obs_source_create(
|
||||||
|
inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
|
||||||
|
|
||||||
// Check that everything was created properly
|
// Check that everything was created properly
|
||||||
if (!input)
|
if (!input)
|
||||||
@ -76,7 +83,8 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, st
|
|||||||
// 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(input, OBS_MONITORING_TYPE_MONITOR_ONLY);
|
obs_source_set_monitoring_type(
|
||||||
|
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);
|
||||||
@ -88,9 +96,12 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, st
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind, obs_data_t *filterSettings)
|
obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter(
|
||||||
|
obs_source_t *source, std::string filterName, std::string filterKind,
|
||||||
|
obs_data_t *filterSettings)
|
||||||
{
|
{
|
||||||
obs_source_t *filter = obs_source_create_private(filterKind.c_str(), filterName.c_str(), filterSettings);
|
obs_source_t *filter = obs_source_create_private(
|
||||||
|
filterKind.c_str(), filterName.c_str(), filterSettings);
|
||||||
|
|
||||||
if (!filter)
|
if (!filter)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -100,12 +111,16 @@ obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter(obs_source_t *source,
|
|||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::Obs::ActionHelper::SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index)
|
void Utils::Obs::ActionHelper::SetSourceFilterIndex(obs_source_t *source,
|
||||||
|
obs_source_t *filter,
|
||||||
|
size_t index)
|
||||||
{
|
{
|
||||||
size_t currentIndex = Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter);
|
size_t currentIndex =
|
||||||
obs_order_movement direction = index > currentIndex ? OBS_ORDER_MOVE_DOWN : OBS_ORDER_MOVE_UP;
|
Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter);
|
||||||
|
obs_order_movement direction =
|
||||||
|
index > currentIndex ? OBS_ORDER_MOVE_DOWN : OBS_ORDER_MOVE_UP;
|
||||||
|
|
||||||
while(currentIndex != index) {
|
while (currentIndex != index) {
|
||||||
obs_source_filter_set_order(source, filter, direction);
|
obs_source_filter_set_order(source, filter, direction);
|
||||||
|
|
||||||
if (direction == OBS_ORDER_MOVE_DOWN)
|
if (direction == OBS_ORDER_MOVE_DOWN)
|
||||||
|
@ -29,7 +29,7 @@ static std::vector<std::string> ConvertStringArray(char **array)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
char* value = nullptr;
|
char *value = nullptr;
|
||||||
do {
|
do {
|
||||||
value = array[index];
|
value = array[index];
|
||||||
if (value)
|
if (value)
|
||||||
@ -42,7 +42,7 @@ static std::vector<std::string> ConvertStringArray(char **array)
|
|||||||
|
|
||||||
std::vector<std::string> Utils::Obs::ArrayHelper::GetSceneCollectionList()
|
std::vector<std::string> Utils::Obs::ArrayHelper::GetSceneCollectionList()
|
||||||
{
|
{
|
||||||
char** sceneCollections = obs_frontend_get_scene_collections();
|
char **sceneCollections = obs_frontend_get_scene_collections();
|
||||||
auto ret = ConvertStringArray(sceneCollections);
|
auto ret = ConvertStringArray(sceneCollections);
|
||||||
bfree(sceneCollections);
|
bfree(sceneCollections);
|
||||||
return ret;
|
return ret;
|
||||||
@ -50,7 +50,7 @@ std::vector<std::string> Utils::Obs::ArrayHelper::GetSceneCollectionList()
|
|||||||
|
|
||||||
std::vector<std::string> Utils::Obs::ArrayHelper::GetProfileList()
|
std::vector<std::string> Utils::Obs::ArrayHelper::GetProfileList()
|
||||||
{
|
{
|
||||||
char** profiles = obs_frontend_get_profiles();
|
char **profiles = obs_frontend_get_profiles();
|
||||||
auto ret = ConvertStringArray(profiles);
|
auto ret = ConvertStringArray(profiles);
|
||||||
bfree(profiles);
|
bfree(profiles);
|
||||||
return ret;
|
return ret;
|
||||||
@ -60,13 +60,16 @@ std::vector<obs_hotkey_t *> Utils::Obs::ArrayHelper::GetHotkeyList()
|
|||||||
{
|
{
|
||||||
std::vector<obs_hotkey_t *> ret;
|
std::vector<obs_hotkey_t *> ret;
|
||||||
|
|
||||||
obs_enum_hotkeys([](void* data, obs_hotkey_id, obs_hotkey_t* hotkey) {
|
obs_enum_hotkeys(
|
||||||
auto ret = static_cast<std::vector<obs_hotkey_t *> *>(data);
|
[](void *data, obs_hotkey_id, obs_hotkey_t *hotkey) {
|
||||||
|
auto ret = static_cast<std::vector<obs_hotkey_t *> *>(
|
||||||
|
data);
|
||||||
|
|
||||||
ret->push_back(hotkey);
|
ret->push_back(hotkey);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, &ret);
|
},
|
||||||
|
&ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -112,7 +115,7 @@ std::vector<std::string> Utils::Obs::ArrayHelper::GetGroupList()
|
|||||||
std::vector<std::string> ret;
|
std::vector<std::string> ret;
|
||||||
|
|
||||||
auto cb = [](void *priv_data, obs_source_t *scene) {
|
auto cb = [](void *priv_data, obs_source_t *scene) {
|
||||||
auto ret = static_cast<std::vector<std::string>*>(priv_data);
|
auto ret = static_cast<std::vector<std::string> *>(priv_data);
|
||||||
|
|
||||||
if (!obs_source_is_group(scene))
|
if (!obs_source_is_group(scene))
|
||||||
return true;
|
return true;
|
||||||
@ -127,36 +130,48 @@ std::vector<std::string> Utils::Obs::ArrayHelper::GetGroupList()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene, bool basic)
|
std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene,
|
||||||
|
bool basic)
|
||||||
{
|
{
|
||||||
std::pair<std::vector<json>, bool> enumData;
|
std::pair<std::vector<json>, bool> enumData;
|
||||||
enumData.second = basic;
|
enumData.second = basic;
|
||||||
|
|
||||||
obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) {
|
obs_scene_enum_items(
|
||||||
auto enumData = static_cast<std::pair<std::vector<json>, bool>*>(param);
|
scene,
|
||||||
|
[](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
|
||||||
|
auto enumData = static_cast<
|
||||||
|
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 = obs_sceneitem_get_source(sceneItem);
|
OBSSource itemSource =
|
||||||
item["sourceName"] = obs_source_get_name(itemSource);
|
obs_sceneitem_get_source(sceneItem);
|
||||||
item["sourceType"] = StringHelper::GetSourceType(itemSource);
|
item["sourceName"] =
|
||||||
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
|
obs_source_get_name(itemSource);
|
||||||
item["inputKind"] = obs_source_get_id(itemSource);
|
item["sourceType"] =
|
||||||
else
|
StringHelper::GetSourceType(itemSource);
|
||||||
item["inputKind"] = nullptr;
|
if (obs_source_get_type(itemSource) ==
|
||||||
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_SCENE)
|
OBS_SOURCE_TYPE_INPUT)
|
||||||
item["isGroup"] = obs_source_is_group(itemSource);
|
item["inputKind"] =
|
||||||
else
|
obs_source_get_id(itemSource);
|
||||||
item["isGroup"] = nullptr;
|
else
|
||||||
}
|
item["inputKind"] = nullptr;
|
||||||
|
if (obs_source_get_type(itemSource) ==
|
||||||
|
OBS_SOURCE_TYPE_SCENE)
|
||||||
|
item["isGroup"] =
|
||||||
|
obs_source_is_group(itemSource);
|
||||||
|
else
|
||||||
|
item["isGroup"] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
enumData->first.push_back(item);
|
enumData->first.push_back(item);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, &enumData);
|
},
|
||||||
|
&enumData);
|
||||||
|
|
||||||
return enumData.first;
|
return enumData.first;
|
||||||
}
|
}
|
||||||
@ -176,17 +191,19 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
|
|||||||
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
auto inputInfo = static_cast<EnumInputInfo*>(param);
|
auto inputInfo = static_cast<EnumInputInfo *>(param);
|
||||||
|
|
||||||
std::string inputKind = obs_source_get_id(input);
|
std::string inputKind = obs_source_get_id(input);
|
||||||
|
|
||||||
if (!inputInfo->inputKind.empty() && inputInfo->inputKind != inputKind)
|
if (!inputInfo->inputKind.empty() &&
|
||||||
|
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"] = obs_source_get_unversioned_id(input);
|
inputJson["unversionedInputKind"] =
|
||||||
|
obs_source_get_unversioned_id(input);
|
||||||
|
|
||||||
inputInfo->inputs.push_back(inputJson);
|
inputInfo->inputs.push_back(inputJson);
|
||||||
return true;
|
return true;
|
||||||
@ -197,7 +214,9 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
|
|||||||
return inputInfo.inputs;
|
return inputInfo.inputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Utils::Obs::ArrayHelper::GetInputKindList(bool unversioned, bool includeDisabled)
|
std::vector<std::string>
|
||||||
|
Utils::Obs::ArrayHelper::GetInputKindList(bool unversioned,
|
||||||
|
bool includeDisabled)
|
||||||
{
|
{
|
||||||
std::vector<std::string> ret;
|
std::vector<std::string> ret;
|
||||||
|
|
||||||
@ -219,7 +238,8 @@ std::vector<std::string> Utils::Obs::ArrayHelper::GetInputKindList(bool unversio
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<json> Utils::Obs::ArrayHelper::GetListPropertyItems(obs_property_t *property)
|
std::vector<json>
|
||||||
|
Utils::Obs::ArrayHelper::GetListPropertyItems(obs_property_t *property)
|
||||||
{
|
{
|
||||||
std::vector<json> ret;
|
std::vector<json> ret;
|
||||||
|
|
||||||
@ -231,13 +251,17 @@ std::vector<json> Utils::Obs::ArrayHelper::GetListPropertyItems(obs_property_t *
|
|||||||
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"] = !obs_property_list_item_disabled(property, i);
|
itemData["itemEnabled"] =
|
||||||
|
!obs_property_list_item_disabled(property, i);
|
||||||
if (itemFormat == OBS_COMBO_FORMAT_INT) {
|
if (itemFormat == OBS_COMBO_FORMAT_INT) {
|
||||||
itemData["itemValue"] = obs_property_list_item_int(property, i);
|
itemData["itemValue"] =
|
||||||
|
obs_property_list_item_int(property, i);
|
||||||
} else if (itemFormat == OBS_COMBO_FORMAT_FLOAT) {
|
} else if (itemFormat == OBS_COMBO_FORMAT_FLOAT) {
|
||||||
itemData["itemValue"] = obs_property_list_item_float(property, i);
|
itemData["itemValue"] =
|
||||||
|
obs_property_list_item_float(property, i);
|
||||||
} else if (itemFormat == OBS_COMBO_FORMAT_STRING) {
|
} else if (itemFormat == OBS_COMBO_FORMAT_STRING) {
|
||||||
itemData["itemValue"] = obs_property_list_item_string(property, i);
|
itemData["itemValue"] =
|
||||||
|
obs_property_list_item_string(property, i);
|
||||||
} else {
|
} else {
|
||||||
itemData["itemValue"] = nullptr;
|
itemData["itemValue"] = nullptr;
|
||||||
}
|
}
|
||||||
@ -269,10 +293,14 @@ 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"] = obs_source_get_name(transition);
|
transitionJson["transitionName"] =
|
||||||
transitionJson["transitionKind"] = obs_source_get_id(transition);
|
obs_source_get_name(transition);
|
||||||
transitionJson["transitionFixed"] = obs_transition_fixed(transition);
|
transitionJson["transitionKind"] =
|
||||||
transitionJson["transitionConfigurable"] = obs_source_configurable(transition);
|
obs_source_get_id(transition);
|
||||||
|
transitionJson["transitionFixed"] =
|
||||||
|
obs_transition_fixed(transition);
|
||||||
|
transitionJson["transitionConfigurable"] =
|
||||||
|
obs_source_configurable(transition);
|
||||||
ret.push_back(transitionJson);
|
ret.push_back(transitionJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,18 +315,20 @@ std::vector<std::string> Utils::Obs::ArrayHelper::GetFilterKindList()
|
|||||||
|
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
const char *kind;
|
const char *kind;
|
||||||
while(obs_enum_filter_types(idx++, &kind))
|
while (obs_enum_filter_types(idx++, &kind))
|
||||||
ret.push_back(kind);
|
ret.push_back(kind);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<json> Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *source)
|
std::vector<json>
|
||||||
|
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, void *param) {
|
auto enumFilters = [](obs_source_t *, obs_source_t *filter,
|
||||||
auto filters = reinterpret_cast<std::vector<json>*>(param);
|
void *param) {
|
||||||
|
auto filters = reinterpret_cast<std::vector<json> *>(param);
|
||||||
|
|
||||||
json filterJson;
|
json filterJson;
|
||||||
filterJson["filterEnabled"] = obs_source_enabled(filter);
|
filterJson["filterEnabled"] = obs_source_enabled(filter);
|
||||||
@ -306,8 +336,10 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *sou
|
|||||||
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 = obs_source_get_settings(filter);
|
OBSDataAutoRelease filterSettings =
|
||||||
filterJson["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
|
obs_source_get_settings(filter);
|
||||||
|
filterJson["filterSettings"] =
|
||||||
|
Utils::Json::ObsDataToJson(filterSettings);
|
||||||
|
|
||||||
filters->push_back(filterJson);
|
filters->push_back(filterJson);
|
||||||
};
|
};
|
||||||
|
@ -19,9 +19,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Obs.h"
|
#include "Obs.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
#define RET_COMPARE(str, x) if (str == #x) return x;
|
#define RET_COMPARE(str, x) \
|
||||||
|
if (str == #x) \
|
||||||
|
return x;
|
||||||
|
|
||||||
enum obs_bounds_type Utils::Obs::EnumHelper::GetSceneItemBoundsType(std::string boundsType)
|
enum obs_bounds_type
|
||||||
|
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)
|
||||||
@ -34,7 +37,8 @@ enum obs_bounds_type Utils::Obs::EnumHelper::GetSceneItemBoundsType(std::string
|
|||||||
return OBS_BOUNDS_NONE;
|
return OBS_BOUNDS_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ObsMediaInputAction Utils::Obs::EnumHelper::GetMediaInputAction(std::string mediaAction)
|
enum ObsMediaInputAction
|
||||||
|
Utils::Obs::EnumHelper::GetMediaInputAction(std::string mediaAction)
|
||||||
{
|
{
|
||||||
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY)
|
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY)
|
||||||
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE)
|
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE)
|
||||||
@ -46,7 +50,8 @@ enum ObsMediaInputAction Utils::Obs::EnumHelper::GetMediaInputAction(std::string
|
|||||||
return OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE;
|
return OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum obs_blending_type Utils::Obs::EnumHelper::GetSceneItemBlendMode(std::string mode)
|
enum obs_blending_type
|
||||||
|
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)
|
||||||
|
@ -28,7 +28,7 @@ uint64_t Utils::Obs::NumberHelper::GetOutputDuration(obs_output_t *output)
|
|||||||
if (!output || !obs_output_active(output))
|
if (!output || !obs_output_active(output))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
video_t* video = obs_output_video(output);
|
video_t *video = obs_output_video(output);
|
||||||
uint64_t frameTimeNs = video_output_get_frame_time(video);
|
uint64_t frameTimeNs = video_output_get_frame_time(video);
|
||||||
int totalFrames = obs_output_get_total_frames(output);
|
int totalFrames = obs_output_get_total_frames(output);
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ size_t Utils::Obs::NumberHelper::GetSceneCount()
|
|||||||
{
|
{
|
||||||
size_t ret;
|
size_t ret;
|
||||||
auto sceneEnumProc = [](void *param, obs_source_t *scene) {
|
auto sceneEnumProc = [](void *param, obs_source_t *scene) {
|
||||||
auto ret = static_cast<size_t*>(param);
|
auto ret = static_cast<size_t *>(param);
|
||||||
|
|
||||||
if (obs_source_is_group(scene))
|
if (obs_source_is_group(scene))
|
||||||
return true;
|
return true;
|
||||||
@ -53,7 +53,8 @@ size_t Utils::Obs::NumberHelper::GetSceneCount()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Utils::Obs::NumberHelper::GetSourceFilterIndex(obs_source_t *source, obs_source_t *filter)
|
size_t Utils::Obs::NumberHelper::GetSourceFilterIndex(obs_source_t *source,
|
||||||
|
obs_source_t *filter)
|
||||||
{
|
{
|
||||||
struct FilterSearch {
|
struct FilterSearch {
|
||||||
obs_source_t *filter;
|
obs_source_t *filter;
|
||||||
@ -61,8 +62,9 @@ size_t Utils::Obs::NumberHelper::GetSourceFilterIndex(obs_source_t *source, obs_
|
|||||||
size_t index;
|
size_t index;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto search = [](obs_source_t *, obs_source_t *filter, void *priv_data) {
|
auto search = [](obs_source_t *, obs_source_t *filter,
|
||||||
auto filterSearch = static_cast<FilterSearch*>(priv_data);
|
void *priv_data) {
|
||||||
|
auto filterSearch = static_cast<FilterSearch *>(priv_data);
|
||||||
|
|
||||||
if (filter == filterSearch->filter)
|
if (filter == filterSearch->filter)
|
||||||
filterSearch->found = true;
|
filterSearch->found = true;
|
||||||
|
@ -27,15 +27,20 @@ json Utils::Obs::ObjectHelper::GetStats()
|
|||||||
{
|
{
|
||||||
json ret;
|
json ret;
|
||||||
|
|
||||||
std::string outputPath = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
|
std::string outputPath =
|
||||||
|
Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
|
||||||
|
|
||||||
video_t* video = obs_get_video();
|
video_t *video = obs_get_video();
|
||||||
|
|
||||||
ret["cpuUsage"] = os_cpu_usage_info_query(GetCpuUsageInfo());
|
ret["cpuUsage"] = os_cpu_usage_info_query(GetCpuUsageInfo());
|
||||||
ret["memoryUsage"] = (double)os_get_proc_resident_size() / (1024.0 * 1024.0);
|
ret["memoryUsage"] =
|
||||||
ret["availableDiskSpace"] = (double)os_get_free_disk_space(outputPath.c_str()) / (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["activeFps"] = obs_get_active_fps();
|
ret["activeFps"] = obs_get_active_fps();
|
||||||
ret["averageFrameRenderTime"] = (double)obs_get_average_frame_time_ns() / 1000000.0;
|
ret["averageFrameRenderTime"] =
|
||||||
|
(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);
|
||||||
@ -73,7 +78,8 @@ json Utils::Obs::ObjectHelper::GetSceneItemTransform(obs_sceneitem_t *item)
|
|||||||
|
|
||||||
ret["alignment"] = osi.alignment;
|
ret["alignment"] = osi.alignment;
|
||||||
|
|
||||||
ret["boundsType"] = StringHelper::GetSceneItemBoundsType(osi.bounds_type);
|
ret["boundsType"] =
|
||||||
|
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;
|
||||||
|
@ -35,7 +35,8 @@ obs_hotkey_t *Utils::Obs::SearchHelper::GetHotkeyByName(std::string name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Increments source ref. Use OBSSourceAutoRelease
|
// Increments source ref. Use OBSSourceAutoRelease
|
||||||
obs_source_t *Utils::Obs::SearchHelper::GetSceneTransitionByName(std::string name)
|
obs_source_t *
|
||||||
|
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);
|
||||||
@ -61,7 +62,9 @@ struct SceneItemSearchData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Increments item ref. Use OBSSceneItemAutoRelease
|
// Increments item ref. Use OBSSceneItemAutoRelease
|
||||||
obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name, int offset)
|
obs_sceneitem_t *
|
||||||
|
Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene,
|
||||||
|
std::string name, int offset)
|
||||||
{
|
{
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -70,26 +73,34 @@ obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene
|
|||||||
enumData.name = name;
|
enumData.name = name;
|
||||||
enumData.offset = offset;
|
enumData.offset = offset;
|
||||||
|
|
||||||
obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) {
|
obs_scene_enum_items(
|
||||||
auto enumData = static_cast<SceneItemSearchData*>(param);
|
scene,
|
||||||
|
[](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
|
||||||
|
auto enumData =
|
||||||
|
static_cast<SceneItemSearchData *>(param);
|
||||||
|
|
||||||
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
OBSSource itemSource =
|
||||||
std::string sourceName = obs_source_get_name(itemSource);
|
obs_sceneitem_get_source(sceneItem);
|
||||||
if (sourceName == enumData->name) {
|
std::string sourceName =
|
||||||
if (enumData->offset > 0) {
|
obs_source_get_name(itemSource);
|
||||||
enumData->offset--;
|
if (sourceName == enumData->name) {
|
||||||
} else {
|
if (enumData->offset > 0) {
|
||||||
if (enumData->ret) // Release existing selection in the case of last match selection
|
enumData->offset--;
|
||||||
obs_sceneitem_release(enumData->ret);
|
} else {
|
||||||
obs_sceneitem_addref(sceneItem);
|
if (enumData->ret) // Release existing selection in the case of last match selection
|
||||||
enumData->ret = sceneItem;
|
obs_sceneitem_release(
|
||||||
if (enumData->offset == 0) // Only break if in normal selection mode (not offset == -1)
|
enumData->ret);
|
||||||
return false;
|
obs_sceneitem_addref(sceneItem);
|
||||||
|
enumData->ret = sceneItem;
|
||||||
|
if (enumData->offset ==
|
||||||
|
0) // Only break if in normal selection mode (not offset == -1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, &enumData);
|
},
|
||||||
|
&enumData);
|
||||||
|
|
||||||
return enumData.ret;
|
return enumData.ret;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Obs.h"
|
#include "Obs.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
#define CASE(x) case x: return #x;
|
#define CASE(x) \
|
||||||
|
case x: \
|
||||||
|
return #x;
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetObsVersion()
|
std::string Utils::Obs::StringHelper::GetObsVersion()
|
||||||
{
|
{
|
||||||
@ -75,7 +77,7 @@ std::string Utils::Obs::StringHelper::GetSourceType(obs_source_t *source)
|
|||||||
obs_source_type sourceType = obs_source_get_type(source);
|
obs_source_type sourceType = obs_source_get_type(source);
|
||||||
|
|
||||||
switch (sourceType) {
|
switch (sourceType) {
|
||||||
default:
|
default:
|
||||||
CASE(OBS_SOURCE_TYPE_INPUT)
|
CASE(OBS_SOURCE_TYPE_INPUT)
|
||||||
CASE(OBS_SOURCE_TYPE_FILTER)
|
CASE(OBS_SOURCE_TYPE_FILTER)
|
||||||
CASE(OBS_SOURCE_TYPE_TRANSITION)
|
CASE(OBS_SOURCE_TYPE_TRANSITION)
|
||||||
@ -83,10 +85,11 @@ std::string Utils::Obs::StringHelper::GetSourceType(obs_source_t *source)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetInputMonitorType(enum obs_monitoring_type monitorType)
|
std::string Utils::Obs::StringHelper::GetInputMonitorType(
|
||||||
|
enum obs_monitoring_type monitorType)
|
||||||
{
|
{
|
||||||
switch (monitorType) {
|
switch (monitorType) {
|
||||||
default:
|
default:
|
||||||
CASE(OBS_MONITORING_TYPE_NONE)
|
CASE(OBS_MONITORING_TYPE_NONE)
|
||||||
CASE(OBS_MONITORING_TYPE_MONITOR_ONLY)
|
CASE(OBS_MONITORING_TYPE_MONITOR_ONLY)
|
||||||
CASE(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT)
|
CASE(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT)
|
||||||
@ -105,7 +108,7 @@ std::string Utils::Obs::StringHelper::GetMediaInputState(obs_source_t *input)
|
|||||||
obs_media_state mediaState = obs_source_media_get_state(input);
|
obs_media_state mediaState = obs_source_media_get_state(input);
|
||||||
|
|
||||||
switch (mediaState) {
|
switch (mediaState) {
|
||||||
default:
|
default:
|
||||||
CASE(OBS_MEDIA_STATE_NONE)
|
CASE(OBS_MEDIA_STATE_NONE)
|
||||||
CASE(OBS_MEDIA_STATE_PLAYING)
|
CASE(OBS_MEDIA_STATE_PLAYING)
|
||||||
CASE(OBS_MEDIA_STATE_OPENING)
|
CASE(OBS_MEDIA_STATE_OPENING)
|
||||||
@ -135,10 +138,11 @@ std::string Utils::Obs::StringHelper::GetLastReplayBufferFilePath()
|
|||||||
return savedReplayPath;
|
return savedReplayPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
|
std::string
|
||||||
|
Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
default:
|
default:
|
||||||
CASE(OBS_BOUNDS_NONE)
|
CASE(OBS_BOUNDS_NONE)
|
||||||
CASE(OBS_BOUNDS_STRETCH)
|
CASE(OBS_BOUNDS_STRETCH)
|
||||||
CASE(OBS_BOUNDS_SCALE_INNER)
|
CASE(OBS_BOUNDS_SCALE_INNER)
|
||||||
@ -149,10 +153,11 @@ std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_typ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetSceneItemBlendMode(enum obs_blending_type mode)
|
std::string
|
||||||
|
Utils::Obs::StringHelper::GetSceneItemBlendMode(enum obs_blending_type mode)
|
||||||
{
|
{
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
default:
|
default:
|
||||||
CASE(OBS_BLEND_NORMAL)
|
CASE(OBS_BLEND_NORMAL)
|
||||||
CASE(OBS_BLEND_ADDITIVE)
|
CASE(OBS_BLEND_ADDITIVE)
|
||||||
CASE(OBS_BLEND_SUBTRACT)
|
CASE(OBS_BLEND_SUBTRACT)
|
||||||
@ -173,14 +178,17 @@ std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
|
|||||||
uint64_t secsPart = secs % 60ULL;
|
uint64_t secsPart = secs % 60ULL;
|
||||||
uint64_t msPart = ms % 1000ULL;
|
uint64_t msPart = ms % 1000ULL;
|
||||||
|
|
||||||
QString formatted = QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
|
QString formatted = QString::asprintf("%02" PRIu64 ":%02" PRIu64
|
||||||
|
":%02" PRIu64 ".%03" PRIu64,
|
||||||
|
hoursPart, minutesPart, secsPart,
|
||||||
|
msPart);
|
||||||
return formatted.toStdString();
|
return formatted.toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetOutputState(ObsOutputState state)
|
std::string Utils::Obs::StringHelper::GetOutputState(ObsOutputState state)
|
||||||
{
|
{
|
||||||
switch (state) {
|
switch (state) {
|
||||||
default:
|
default:
|
||||||
CASE(OBS_WEBSOCKET_OUTPUT_UNKNOWN)
|
CASE(OBS_WEBSOCKET_OUTPUT_UNKNOWN)
|
||||||
CASE(OBS_WEBSOCKET_OUTPUT_STARTING)
|
CASE(OBS_WEBSOCKET_OUTPUT_STARTING)
|
||||||
CASE(OBS_WEBSOCKET_OUTPUT_STARTED)
|
CASE(OBS_WEBSOCKET_OUTPUT_STARTED)
|
||||||
|
@ -26,35 +26,43 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Obs_VolumeMeter_Helpers.h"
|
#include "Obs_VolumeMeter_Helpers.h"
|
||||||
#include "../obs-websocket.h"
|
#include "../obs-websocket.h"
|
||||||
|
|
||||||
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) :
|
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input)
|
||||||
PeakMeterType(SAMPLE_PEAK_METER),
|
: PeakMeterType(SAMPLE_PEAK_METER),
|
||||||
_input(obs_source_get_weak_source(input)),
|
_input(obs_source_get_weak_source(input)),
|
||||||
_channels(0),
|
_channels(0),
|
||||||
_lastUpdate(0),
|
_lastUpdate(0),
|
||||||
_volume(obs_source_get_volume(input))
|
_volume(obs_source_get_volume(input))
|
||||||
{
|
{
|
||||||
signal_handler_t *sh = obs_source_get_signal_handler(input);
|
signal_handler_t *sh = obs_source_get_signal_handler(input);
|
||||||
signal_handler_connect(sh, "volume", Meter::InputVolumeCallback, this);
|
signal_handler_connect(sh, "volume", Meter::InputVolumeCallback, this);
|
||||||
|
|
||||||
obs_source_add_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this);
|
obs_source_add_audio_capture_callback(
|
||||||
|
input, Meter::InputAudioCaptureCallback, this);
|
||||||
|
|
||||||
blog_debug("[Utils::Obs::VolumeMeter::Meter::Meter] Meter created for input: %s", obs_source_get_name(input));
|
blog_debug(
|
||||||
|
"[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()
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||||
if (!input) {
|
if (!input) {
|
||||||
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?");
|
blog(LOG_WARNING,
|
||||||
|
"[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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, this);
|
signal_handler_disconnect(sh, "volume", Meter::InputVolumeCallback,
|
||||||
|
this);
|
||||||
|
|
||||||
obs_source_remove_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this);
|
obs_source_remove_audio_capture_callback(
|
||||||
|
input, Meter::InputAudioCaptureCallback, this);
|
||||||
|
|
||||||
blog_debug("[Utils::Obs::VolumeMeter::Meter::~Meter] Meter destroyed for input: %s", obs_source_get_name(input));
|
blog_debug(
|
||||||
|
"[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()
|
||||||
@ -68,7 +76,8 @@ json Utils::Obs::VolumeMeter::Meter::GetMeterData()
|
|||||||
|
|
||||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||||
if (!input) {
|
if (!input) {
|
||||||
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
|
blog(LOG_WARNING,
|
||||||
|
"[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +86,8 @@ json Utils::Obs::VolumeMeter::Meter::GetMeterData()
|
|||||||
|
|
||||||
std::unique_lock<std::mutex> l(_mutex);
|
std::unique_lock<std::mutex> l(_mutex);
|
||||||
|
|
||||||
if (_lastUpdate != 0 && (os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3)
|
if (_lastUpdate != 0 &&
|
||||||
|
(os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3)
|
||||||
ResetAudioLevels();
|
ResetAudioLevels();
|
||||||
|
|
||||||
for (int channel = 0; channel < _channels; channel++) {
|
for (int channel = 0; channel < _channels; channel++) {
|
||||||
@ -100,14 +110,16 @@ 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; channelNumber++) {
|
for (int channelNumber = 0; channelNumber < MAX_AUDIO_CHANNELS;
|
||||||
|
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(const struct audio_data *data)
|
void Utils::Obs::VolumeMeter::Meter::ProcessAudioChannels(
|
||||||
|
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++) {
|
||||||
@ -129,7 +141,7 @@ void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
|
|||||||
int channelNumber = 0;
|
int channelNumber = 0;
|
||||||
|
|
||||||
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
||||||
float *samples = (float*)data->data[planeNumber];
|
float *samples = (float *)data->data[planeNumber];
|
||||||
if (!samples)
|
if (!samples)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -139,45 +151,64 @@ void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
__m128 previousSamples = _mm_loadu_ps(_previousSamples[channelNumber]);
|
__m128 previousSamples =
|
||||||
|
_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, sampleCount);
|
peak = GetSamplePeak(previousSamples, samples,
|
||||||
break;
|
sampleCount);
|
||||||
case TRUE_PEAK_METER:
|
break;
|
||||||
peak = GetTruePeak(previousSamples, samples, sampleCount);
|
case TRUE_PEAK_METER:
|
||||||
break;
|
peak = GetTruePeak(previousSamples, samples,
|
||||||
|
sampleCount);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (sampleCount) {
|
switch (sampleCount) {
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][1];
|
_previousSamples[channelNumber][0] =
|
||||||
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][2];
|
_previousSamples[channelNumber][1];
|
||||||
_previousSamples[channelNumber][2] = _previousSamples[channelNumber][3];
|
_previousSamples[channelNumber][1] =
|
||||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
_previousSamples[channelNumber][2];
|
||||||
break;
|
_previousSamples[channelNumber][2] =
|
||||||
case 2:
|
_previousSamples[channelNumber][3];
|
||||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][2];
|
_previousSamples[channelNumber][3] =
|
||||||
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][3];
|
samples[sampleCount - 1];
|
||||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
break;
|
||||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
case 2:
|
||||||
break;
|
_previousSamples[channelNumber][0] =
|
||||||
case 3:
|
_previousSamples[channelNumber][2];
|
||||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][3];
|
_previousSamples[channelNumber][1] =
|
||||||
_previousSamples[channelNumber][1] = samples[sampleCount - 3];
|
_previousSamples[channelNumber][3];
|
||||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
_previousSamples[channelNumber][2] =
|
||||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
samples[sampleCount - 2];
|
||||||
break;
|
_previousSamples[channelNumber][3] =
|
||||||
default:
|
samples[sampleCount - 1];
|
||||||
_previousSamples[channelNumber][0] = samples[sampleCount - 4];
|
break;
|
||||||
_previousSamples[channelNumber][1] = samples[sampleCount - 3];
|
case 3:
|
||||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
_previousSamples[channelNumber][0] =
|
||||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
_previousSamples[channelNumber][3];
|
||||||
|
_previousSamples[channelNumber][1] =
|
||||||
|
samples[sampleCount - 3];
|
||||||
|
_previousSamples[channelNumber][2] =
|
||||||
|
samples[sampleCount - 2];
|
||||||
|
_previousSamples[channelNumber][3] =
|
||||||
|
samples[sampleCount - 1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_previousSamples[channelNumber][0] =
|
||||||
|
samples[sampleCount - 4];
|
||||||
|
_previousSamples[channelNumber][1] =
|
||||||
|
samples[sampleCount - 3];
|
||||||
|
_previousSamples[channelNumber][2] =
|
||||||
|
samples[sampleCount - 2];
|
||||||
|
_previousSamples[channelNumber][3] =
|
||||||
|
samples[sampleCount - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
_peak[channelNumber] = peak;
|
_peak[channelNumber] = peak;
|
||||||
@ -190,13 +221,14 @@ void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MUST HOLD LOCK
|
// MUST HOLD LOCK
|
||||||
void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *data)
|
void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(
|
||||||
|
const struct audio_data *data)
|
||||||
{
|
{
|
||||||
size_t sampleCount = data->frames;
|
size_t sampleCount = data->frames;
|
||||||
|
|
||||||
int channelNumber = 0;
|
int channelNumber = 0;
|
||||||
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
||||||
float *samples = (float*)data->data[planeNumber];
|
float *samples = (float *)data->data[planeNumber];
|
||||||
if (!samples)
|
if (!samples)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -212,9 +244,11 @@ void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data, bool muted)
|
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(
|
||||||
|
void *priv_data, obs_source_t *, const struct audio_data *data,
|
||||||
|
bool muted)
|
||||||
{
|
{
|
||||||
auto c = static_cast<Meter*>(priv_data);
|
auto c = static_cast<Meter *>(priv_data);
|
||||||
|
|
||||||
std::unique_lock<std::mutex> l(c->_mutex);
|
std::unique_lock<std::mutex> l(c->_mutex);
|
||||||
|
|
||||||
@ -226,24 +260,24 @@ void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data,
|
|||||||
c->_lastUpdate = os_gettime_ns();
|
c->_lastUpdate = os_gettime_ns();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd)
|
void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data,
|
||||||
|
calldata_t *cd)
|
||||||
{
|
{
|
||||||
auto c = static_cast<Meter*>(priv_data);
|
auto c = static_cast<Meter *>(priv_data);
|
||||||
|
|
||||||
c->_volume = (float)calldata_float(cd, "volume");
|
c->_volume = (float)calldata_float(cd, "volume");
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod) :
|
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb,
|
||||||
_updateCallback(cb),
|
uint64_t updatePeriod)
|
||||||
_updatePeriod(updatePeriod),
|
: _updateCallback(cb), _updatePeriod(updatePeriod), _running(false)
|
||||||
_running(false)
|
|
||||||
{
|
{
|
||||||
signal_handler_t *sh = obs_get_signal_handler();
|
signal_handler_t *sh = obs_get_signal_handler();
|
||||||
if (!sh)
|
if (!sh)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto enumProc = [](void *priv_data, obs_source_t *input) {
|
auto enumProc = [](void *priv_data, obs_source_t *input) {
|
||||||
auto c = static_cast<Handler*>(priv_data);
|
auto c = static_cast<Handler *>(priv_data);
|
||||||
|
|
||||||
if (!obs_source_active(input))
|
if (!obs_source_active(input))
|
||||||
return true;
|
return true;
|
||||||
@ -258,13 +292,16 @@ Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeri
|
|||||||
};
|
};
|
||||||
obs_enum_sources(enumProc, this);
|
obs_enum_sources(enumProc, this);
|
||||||
|
|
||||||
signal_handler_connect(sh, "source_activate", Handler::InputActivateCallback, this);
|
signal_handler_connect(sh, "source_activate",
|
||||||
signal_handler_connect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
|
Handler::InputActivateCallback, 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("[Utils::Obs::VolumeMeter::Handler::Handler] Handler created.");
|
blog_debug(
|
||||||
|
"[Utils::Obs::VolumeMeter::Handler::Handler] Handler created.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Obs::VolumeMeter::Handler::~Handler()
|
Utils::Obs::VolumeMeter::Handler::~Handler()
|
||||||
@ -273,8 +310,10 @@ Utils::Obs::VolumeMeter::Handler::~Handler()
|
|||||||
if (!sh)
|
if (!sh)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
signal_handler_disconnect(sh, "source_activate", Handler::InputActivateCallback, this);
|
signal_handler_disconnect(sh, "source_activate",
|
||||||
signal_handler_disconnect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
|
Handler::InputActivateCallback, this);
|
||||||
|
signal_handler_disconnect(sh, "source_deactivate",
|
||||||
|
Handler::InputDeactivateCallback, this);
|
||||||
|
|
||||||
if (_running) {
|
if (_running) {
|
||||||
_running = false;
|
_running = false;
|
||||||
@ -284,16 +323,20 @@ Utils::Obs::VolumeMeter::Handler::~Handler()
|
|||||||
if (_updateThread.joinable())
|
if (_updateThread.joinable())
|
||||||
_updateThread.join();
|
_updateThread.join();
|
||||||
|
|
||||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed.");
|
blog_debug(
|
||||||
|
"[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::Obs::VolumeMeter::Handler::UpdateThread()
|
void Utils::Obs::VolumeMeter::Handler::UpdateThread()
|
||||||
{
|
{
|
||||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread started.");
|
blog_debug(
|
||||||
|
"[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(l, std::chrono::milliseconds(_updatePeriod), [this]{ return !_running; }))
|
if (_cond.wait_for(
|
||||||
|
l, std::chrono::milliseconds(_updatePeriod),
|
||||||
|
[this] { return !_running; }))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,12 +351,14 @@ void Utils::Obs::VolumeMeter::Handler::UpdateThread()
|
|||||||
if (_updateCallback)
|
if (_updateCallback)
|
||||||
_updateCallback(inputs);
|
_updateCallback(inputs);
|
||||||
}
|
}
|
||||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped.");
|
blog_debug(
|
||||||
|
"[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd)
|
void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data,
|
||||||
|
calldata_t *cd)
|
||||||
{
|
{
|
||||||
auto c = static_cast<Handler*>(priv_data);
|
auto c = static_cast<Handler *>(priv_data);
|
||||||
|
|
||||||
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
||||||
if (!input)
|
if (!input)
|
||||||
@ -330,9 +375,10 @@ void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, ca
|
|||||||
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, calldata_t *cd)
|
void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data,
|
||||||
|
calldata_t *cd)
|
||||||
{
|
{
|
||||||
auto c = static_cast<Handler*>(priv_data);
|
auto c = static_cast<Handler *>(priv_data);
|
||||||
|
|
||||||
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
||||||
if (!input)
|
if (!input)
|
||||||
@ -345,7 +391,8 @@ 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(iter->get()->GetWeakInput(), input))
|
if (obs_weak_source_references_source(
|
||||||
|
iter->get()->GetWeakInput(), input))
|
||||||
iter = c->_meters.erase(iter);
|
iter = c->_meters.erase(iter);
|
||||||
else
|
else
|
||||||
++iter;
|
++iter;
|
||||||
|
@ -31,69 +31,72 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Obs {
|
namespace Obs {
|
||||||
namespace VolumeMeter {
|
namespace VolumeMeter {
|
||||||
// Some code copied from https://github.com/obsproject/obs-studio/blob/master/libobs/obs-audio-controls.c
|
// Some code copied from https://github.com/obsproject/obs-studio/blob/master/libobs/obs-audio-controls.c
|
||||||
// Keeps a running tally of the current audio levels, for a specific input
|
// Keeps a running tally of the current audio levels, for a specific input
|
||||||
class Meter {
|
class Meter {
|
||||||
public:
|
public:
|
||||||
Meter(obs_source_t *input);
|
Meter(obs_source_t *input);
|
||||||
~Meter();
|
~Meter();
|
||||||
|
|
||||||
bool InputValid();
|
bool InputValid();
|
||||||
obs_weak_source_t *GetWeakInput() { return _input; }
|
obs_weak_source_t *GetWeakInput() { return _input; }
|
||||||
json GetMeterData();
|
json GetMeterData();
|
||||||
|
|
||||||
std::atomic<enum obs_peak_meter_type> PeakMeterType;
|
std::atomic<enum obs_peak_meter_type> PeakMeterType;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OBSWeakSourceAutoRelease _input;
|
OBSWeakSourceAutoRelease _input;
|
||||||
|
|
||||||
// All values in mul
|
// All values in mul
|
||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
bool _muted;
|
bool _muted;
|
||||||
int _channels;
|
int _channels;
|
||||||
float _magnitude[MAX_AUDIO_CHANNELS];
|
float _magnitude[MAX_AUDIO_CHANNELS];
|
||||||
float _peak[MAX_AUDIO_CHANNELS];
|
float _peak[MAX_AUDIO_CHANNELS];
|
||||||
float _previousSamples[MAX_AUDIO_CHANNELS][4];
|
float _previousSamples[MAX_AUDIO_CHANNELS][4];
|
||||||
|
|
||||||
std::atomic<uint64_t> _lastUpdate;
|
std::atomic<uint64_t> _lastUpdate;
|
||||||
std::atomic<float> _volume;
|
std::atomic<float> _volume;
|
||||||
|
|
||||||
void ResetAudioLevels();
|
void ResetAudioLevels();
|
||||||
void ProcessAudioChannels(const struct audio_data *data);
|
void ProcessAudioChannels(const struct audio_data *data);
|
||||||
void ProcessPeak(const struct audio_data *data);
|
void ProcessPeak(const struct audio_data *data);
|
||||||
void ProcessMagnitude(const struct audio_data *data);
|
void ProcessMagnitude(const struct audio_data *data);
|
||||||
|
|
||||||
static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source, const struct audio_data *data, bool muted);
|
static void InputAudioCaptureCallback(void *priv_data,
|
||||||
static void InputVolumeCallback(void *priv_data, calldata_t *cd);
|
obs_source_t *source,
|
||||||
};
|
const struct audio_data *data,
|
||||||
|
bool muted);
|
||||||
|
static void InputVolumeCallback(void *priv_data, calldata_t *cd);
|
||||||
|
};
|
||||||
|
|
||||||
// Maintains an array of active inputs
|
// Maintains an array of active inputs
|
||||||
class Handler {
|
class Handler {
|
||||||
typedef std::function<void(std::vector<json>)> UpdateCallback;
|
typedef std::function<void(std::vector<json>)> UpdateCallback;
|
||||||
typedef std::unique_ptr<Meter> MeterPtr;
|
typedef std::unique_ptr<Meter> MeterPtr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Handler(UpdateCallback cb, uint64_t updatePeriod = 50);
|
Handler(UpdateCallback cb, uint64_t updatePeriod = 50);
|
||||||
~Handler();
|
~Handler();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UpdateCallback _updateCallback;
|
UpdateCallback _updateCallback;
|
||||||
|
|
||||||
std::mutex _meterMutex;
|
std::mutex _meterMutex;
|
||||||
std::vector<MeterPtr> _meters;
|
std::vector<MeterPtr> _meters;
|
||||||
uint64_t _updatePeriod;
|
uint64_t _updatePeriod;
|
||||||
|
|
||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
std::condition_variable _cond;
|
std::condition_variable _cond;
|
||||||
std::atomic<bool> _running;
|
std::atomic<bool> _running;
|
||||||
std::thread _updateThread;
|
std::thread _updateThread;
|
||||||
|
|
||||||
void UpdateThread();
|
void UpdateThread();
|
||||||
static void InputActivateCallback(void *priv_data, calldata_t *cd);
|
static void InputActivateCallback(void *priv_data, calldata_t *cd);
|
||||||
static void InputDeactivateCallback(void *priv_data, calldata_t *cd);
|
static void InputDeactivateCallback(void *priv_data, calldata_t *cd);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,8 @@ 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, size_t sampleCount)
|
static float GetSamplePeak(__m128 previousSamples, const float *samples,
|
||||||
|
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) {
|
||||||
@ -69,12 +70,17 @@ static float GetSamplePeak(__m128 previousSamples, const float *samples, size_t
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float GetTruePeak(__m128 previousSamples, const float *samples, size_t sampleCount)
|
static float GetTruePeak(__m128 previousSamples, const float *samples,
|
||||||
|
size_t sampleCount)
|
||||||
{
|
{
|
||||||
const __m128 m3 = _mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f);
|
const __m128 m3 =
|
||||||
const __m128 m1 = _mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f);
|
_mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f);
|
||||||
const __m128 p1 = _mm_set_ps(-0.189207f, 0.504551f, 0.756827f, -0.216236f);
|
const __m128 m1 =
|
||||||
const __m128 p3 = _mm_set_ps(-0.103943f, 0.233872f, 0.935489f, -0.155915f);
|
_mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f);
|
||||||
|
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;
|
||||||
|
@ -53,24 +53,36 @@ std::string Utils::Platform::GetLocalAddress()
|
|||||||
std::vector<std::pair<QString, uint8_t>> preferredAddresses;
|
std::vector<std::pair<QString, uint8_t>> preferredAddresses;
|
||||||
for (auto address : validAddresses) {
|
for (auto address : validAddresses) {
|
||||||
// Attribute a priority (0 is best) to the address to choose the best picks
|
// Attribute a priority (0 is best) to the address to choose the best picks
|
||||||
if (address.startsWith("192.168.1.") || address.startsWith("192.168.0.")) { // Prefer common consumer router network prefixes
|
if (address.startsWith("192.168.1.") ||
|
||||||
|
address.startsWith(
|
||||||
|
"192.168.0.")) { // Prefer common consumer router network prefixes
|
||||||
if (address.startsWith("192.168.56."))
|
if (address.startsWith("192.168.56."))
|
||||||
preferredAddresses.push_back(std::make_pair(address, 255)); // Ignore virtualbox default
|
preferredAddresses.push_back(std::make_pair(
|
||||||
|
address,
|
||||||
|
255)); // Ignore virtualbox default
|
||||||
else
|
else
|
||||||
preferredAddresses.push_back(std::make_pair(address, 0));
|
preferredAddresses.push_back(
|
||||||
} else if (address.startsWith("172.16.")) { // Slightly less common consumer router network prefixes
|
std::make_pair(address, 0));
|
||||||
preferredAddresses.push_back(std::make_pair(address, 1));
|
} else if (address.startsWith(
|
||||||
} else if (address.startsWith("10.")) { // Even less common consumer router network prefixes
|
"172.16.")) { // Slightly less common consumer router network prefixes
|
||||||
preferredAddresses.push_back(std::make_pair(address, 2));
|
preferredAddresses.push_back(
|
||||||
|
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(std::make_pair(address, 255));
|
preferredAddresses.push_back(
|
||||||
|
std::make_pair(address, 255));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by priority
|
// Sort by priority
|
||||||
std::sort(preferredAddresses.begin(), preferredAddresses.end(), [=](std::pair<QString, uint8_t> a, std::pair<QString, uint8_t> b) {
|
std::sort(preferredAddresses.begin(), preferredAddresses.end(),
|
||||||
return a.second < b.second;
|
[=](std::pair<QString, uint8_t> a,
|
||||||
});
|
std::pair<QString, uint8_t> b) {
|
||||||
|
return a.second < b.second;
|
||||||
|
});
|
||||||
|
|
||||||
// Return highest priority address
|
// Return highest priority address
|
||||||
return preferredAddresses[0].first.toStdString();
|
return preferredAddresses[0].first.toStdString();
|
||||||
@ -120,24 +132,35 @@ struct SystemTrayNotification {
|
|||||||
QString body;
|
QString body;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Utils::Platform::SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QString title, QString body)
|
void Utils::Platform::SendTrayNotification(QSystemTrayIcon::MessageIcon icon,
|
||||||
|
QString title, QString body)
|
||||||
{
|
{
|
||||||
if (!QSystemTrayIcon::isSystemTrayAvailable() || !QSystemTrayIcon::supportsMessages())
|
if (!QSystemTrayIcon::isSystemTrayAvailable() ||
|
||||||
|
!QSystemTrayIcon::supportsMessages())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SystemTrayNotification *notification = new SystemTrayNotification{icon, title, body};
|
SystemTrayNotification *notification =
|
||||||
|
new SystemTrayNotification{icon, title, body};
|
||||||
|
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(
|
||||||
void *systemTrayPtr = obs_frontend_get_system_tray();
|
OBS_TASK_UI,
|
||||||
auto systemTray = static_cast<QSystemTrayIcon*>(systemTrayPtr);
|
[](void *param) {
|
||||||
|
void *systemTrayPtr = obs_frontend_get_system_tray();
|
||||||
|
auto systemTray =
|
||||||
|
static_cast<QSystemTrayIcon *>(systemTrayPtr);
|
||||||
|
|
||||||
auto notification = static_cast<SystemTrayNotification*>(param);
|
auto notification =
|
||||||
systemTray->showMessage(notification->title, notification->body, notification->icon);
|
static_cast<SystemTrayNotification *>(param);
|
||||||
delete notification;
|
systemTray->showMessage(notification->title,
|
||||||
}, (void*)notification, false);
|
notification->body,
|
||||||
|
notification->icon);
|
||||||
|
delete notification;
|
||||||
|
},
|
||||||
|
(void *)notification, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::Platform::GetTextFileContent(std::string fileName, std::string &content)
|
bool Utils::Platform::GetTextFileContent(std::string fileName,
|
||||||
|
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))
|
||||||
@ -147,7 +170,8 @@ bool Utils::Platform::GetTextFileContent(std::string fileName, std::string &cont
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::Platform::SetTextFileContent(std::string fileName, std::string content, bool createNew)
|
bool Utils::Platform::SetTextFileContent(std::string fileName,
|
||||||
|
std::string content, bool createNew)
|
||||||
{
|
{
|
||||||
if (!createNew && !QFile::exists(QString::fromStdString(fileName)))
|
if (!createNew && !QFile::exists(QString::fromStdString(fileName)))
|
||||||
return false;
|
return false;
|
||||||
|
@ -24,13 +24,15 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include <QSystemTrayIcon>
|
#include <QSystemTrayIcon>
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
std::string GetLocalAddress();
|
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, QString body);
|
void SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QString title,
|
||||||
bool GetTextFileContent(std::string fileName, std::string &content);
|
QString body);
|
||||||
bool SetTextFileContent(std::string filePath, std::string content, bool createNew = true);
|
bool GetTextFileContent(std::string fileName, std::string &content);
|
||||||
}
|
bool SetTextFileContent(std::string filePath, std::string content,
|
||||||
|
bool createNew = true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../utils/Platform.h"
|
#include "../utils/Platform.h"
|
||||||
#include "../utils/Compat.h"
|
#include "../utils/Compat.h"
|
||||||
|
|
||||||
WebSocketServer::WebSocketServer() :
|
WebSocketServer::WebSocketServer() : QObject(nullptr), _sessions()
|
||||||
QObject(nullptr),
|
|
||||||
_sessions()
|
|
||||||
{
|
{
|
||||||
_server.get_alog().clear_channels(websocketpp::log::alevel::all);
|
_server.get_alog().clear_channels(websocketpp::log::alevel::all);
|
||||||
_server.get_elog().clear_channels(websocketpp::log::elevel::all);
|
_server.get_elog().clear_channels(websocketpp::log::elevel::all);
|
||||||
@ -44,38 +42,27 @@ WebSocketServer::WebSocketServer() :
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
_server.set_validate_handler(
|
_server.set_validate_handler(
|
||||||
websocketpp::lib::bind(
|
websocketpp::lib::bind(&WebSocketServer::onValidate, this,
|
||||||
&WebSocketServer::onValidate, this, websocketpp::lib::placeholders::_1
|
websocketpp::lib::placeholders::_1));
|
||||||
)
|
|
||||||
);
|
|
||||||
_server.set_open_handler(
|
_server.set_open_handler(
|
||||||
websocketpp::lib::bind(
|
websocketpp::lib::bind(&WebSocketServer::onOpen, this,
|
||||||
&WebSocketServer::onOpen, this, websocketpp::lib::placeholders::_1
|
websocketpp::lib::placeholders::_1));
|
||||||
)
|
|
||||||
);
|
|
||||||
_server.set_close_handler(
|
_server.set_close_handler(
|
||||||
websocketpp::lib::bind(
|
websocketpp::lib::bind(&WebSocketServer::onClose, this,
|
||||||
&WebSocketServer::onClose, this, websocketpp::lib::placeholders::_1
|
websocketpp::lib::placeholders::_1));
|
||||||
)
|
|
||||||
);
|
|
||||||
_server.set_message_handler(
|
_server.set_message_handler(
|
||||||
websocketpp::lib::bind(
|
websocketpp::lib::bind(&WebSocketServer::onMessage, this,
|
||||||
&WebSocketServer::onMessage, this, websocketpp::lib::placeholders::_1, websocketpp::lib::placeholders::_2
|
websocketpp::lib::placeholders::_1,
|
||||||
)
|
websocketpp::lib::placeholders::_2));
|
||||||
);
|
|
||||||
|
|
||||||
auto eventHandler = GetEventHandler();
|
auto eventHandler = GetEventHandler();
|
||||||
eventHandler->SetBroadcastCallback(
|
eventHandler->SetBroadcastCallback(
|
||||||
std::bind(
|
std::bind(&WebSocketServer::BroadcastEvent, this,
|
||||||
&WebSocketServer::BroadcastEvent, this, std::placeholders::_1, 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(
|
std::bind(&WebSocketServer::onObsLoaded, this));
|
||||||
&WebSocketServer::onObsLoaded, this
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketServer::~WebSocketServer()
|
WebSocketServer::~WebSocketServer()
|
||||||
@ -89,12 +76,17 @@ void WebSocketServer::ServerRunner()
|
|||||||
blog(LOG_INFO, "[WebSocketServer::ServerRunner] IO thread started.");
|
blog(LOG_INFO, "[WebSocketServer::ServerRunner] IO thread started.");
|
||||||
try {
|
try {
|
||||||
_server.run();
|
_server.run();
|
||||||
} catch (websocketpp::exception const & e) {
|
} catch (websocketpp::exception const &e) {
|
||||||
blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s", e.what());
|
blog(LOG_ERROR,
|
||||||
} catch (const std::exception & e) {
|
"[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s",
|
||||||
blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s", e.what());
|
e.what());
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
blog(LOG_ERROR,
|
||||||
|
"[WebSocketServer::ServerRunner] websocketpp instance returned an error: %s",
|
||||||
|
e.what());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
blog(LOG_ERROR, "[WebSocketServer::ServerRunner] websocketpp instance returned an error");
|
blog(LOG_ERROR,
|
||||||
|
"[WebSocketServer::ServerRunner] websocketpp instance returned an error");
|
||||||
}
|
}
|
||||||
blog(LOG_INFO, "[WebSocketServer::ServerRunner] IO thread exited.");
|
blog(LOG_INFO, "[WebSocketServer::ServerRunner] IO thread exited.");
|
||||||
}
|
}
|
||||||
@ -102,52 +94,70 @@ void WebSocketServer::ServerRunner()
|
|||||||
void WebSocketServer::Start()
|
void WebSocketServer::Start()
|
||||||
{
|
{
|
||||||
if (_server.is_listening()) {
|
if (_server.is_listening()) {
|
||||||
blog(LOG_WARNING, "[WebSocketServer::Start] Call to Start() but the server is already listening.");
|
blog(LOG_WARNING,
|
||||||
|
"[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, "[WebSocketServer::Start] Unable to retreive config!");
|
blog(LOG_ERROR,
|
||||||
|
"[WebSocketServer::Start] Unable to retreive config!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_authenticationSalt = Utils::Crypto::GenerateSalt();
|
_authenticationSalt = Utils::Crypto::GenerateSalt();
|
||||||
_authenticationSecret = Utils::Crypto::GenerateSecret(conf->ServerPassword.toStdString(), _authenticationSalt);
|
_authenticationSecret = Utils::Crypto::GenerateSecret(
|
||||||
|
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(websocketpp::log::alevel::frame_header | websocketpp::log::alevel::frame_payload | websocketpp::log::alevel::control);
|
_server.get_alog().clear_channels(
|
||||||
|
websocketpp::log::alevel::frame_header |
|
||||||
|
websocketpp::log::alevel::frame_payload |
|
||||||
|
websocketpp::log::alevel::control);
|
||||||
_server.get_elog().set_channels(websocketpp::log::elevel::all);
|
_server.get_elog().set_channels(websocketpp::log::elevel::all);
|
||||||
_server.get_alog().clear_channels(websocketpp::log::elevel::devel | websocketpp::log::elevel::library);
|
_server.get_alog().clear_channels(
|
||||||
|
websocketpp::log::elevel::devel |
|
||||||
|
websocketpp::log::elevel::library);
|
||||||
} else {
|
} else {
|
||||||
_server.get_alog().clear_channels(websocketpp::log::alevel::all);
|
_server.get_alog().clear_channels(
|
||||||
_server.get_elog().clear_channels(websocketpp::log::elevel::all);
|
websocketpp::log::alevel::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 = Utils::Platform::GetLoopbackAddress(!conf->Ipv4Only);
|
std::string addr =
|
||||||
|
Utils::Platform::GetLoopbackAddress(!conf->Ipv4Only);
|
||||||
if (addr.empty()) {
|
if (addr.empty()) {
|
||||||
blog(LOG_ERROR, "[WebSocketServer::Start] Failed to find loopback interface. Server not started.");
|
blog(LOG_ERROR,
|
||||||
|
"[WebSocketServer::Start] Failed to find loopback interface. Server not started.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_server.listen(addr, std::to_string(conf->ServerPort), errorCode);
|
_server.listen(addr, std::to_string(conf->ServerPort),
|
||||||
blog(LOG_INFO, "[WebSocketServer::Start] Locked to loopback interface.");
|
errorCode);
|
||||||
|
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(), conf->ServerPort, errorCode);
|
_server.listen(websocketpp::lib::asio::ip::tcp::v4(),
|
||||||
blog(LOG_INFO, "[WebSocketServer::Start] Locked to IPv4 bindings.");
|
conf->ServerPort, errorCode);
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"[WebSocketServer::Start] Locked to IPv4 bindings.");
|
||||||
} else {
|
} else {
|
||||||
_server.listen(conf->ServerPort, errorCode);
|
_server.listen(conf->ServerPort, errorCode);
|
||||||
blog(LOG_INFO, "[WebSocketServer::Start] Bound to all interfaces.");
|
blog(LOG_INFO,
|
||||||
|
"[WebSocketServer::Start] Bound to all interfaces.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
std::string errorCodeMessage = errorCode.message();
|
std::string errorCodeMessage = errorCode.message();
|
||||||
blog(LOG_ERROR, "[WebSocketServer::Start] Listen failed: %s", errorCodeMessage.c_str());
|
blog(LOG_ERROR, "[WebSocketServer::Start] Listen failed: %s",
|
||||||
|
errorCodeMessage.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,30 +165,37 @@ void WebSocketServer::Start()
|
|||||||
|
|
||||||
_serverThread = std::thread(&WebSocketServer::ServerRunner, this);
|
_serverThread = std::thread(&WebSocketServer::ServerRunner, this);
|
||||||
|
|
||||||
blog(LOG_INFO, "[WebSocketServer::Start] Server started successfully on port %d. Possible connect address: %s", conf->ServerPort.load(), Utils::Platform::GetLocalAddress().c_str());
|
blog(LOG_INFO,
|
||||||
|
"[WebSocketServer::Start] Server started successfully on port %d. Possible connect address: %s",
|
||||||
|
conf->ServerPort.load(),
|
||||||
|
Utils::Platform::GetLocalAddress().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::Stop()
|
void WebSocketServer::Stop()
|
||||||
{
|
{
|
||||||
if (!_server.is_listening()) {
|
if (!_server.is_listening()) {
|
||||||
blog(LOG_WARNING, "[WebSocketServer::Stop] Call to Stop() but the server is not listening.");
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketServer::Stop] Call to Stop() but the server is not listening.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_server.stop_listening();
|
_server.stop_listening();
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(_sessionMutex);
|
std::unique_lock<std::mutex> lock(_sessionMutex);
|
||||||
for (auto const& [hdl, session] : _sessions) {
|
for (auto const &[hdl, session] : _sessions) {
|
||||||
websocketpp::lib::error_code errorCode;
|
websocketpp::lib::error_code errorCode;
|
||||||
_server.pause_reading(hdl, errorCode);
|
_server.pause_reading(hdl, errorCode);
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
blog(LOG_INFO, "[WebSocketServer::Stop] Error: %s", errorCode.message().c_str());
|
blog(LOG_INFO, "[WebSocketServer::Stop] Error: %s",
|
||||||
|
errorCode.message().c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_server.close(hdl, websocketpp::close::status::going_away, "Server stopping.", errorCode);
|
_server.close(hdl, websocketpp::close::status::going_away,
|
||||||
|
"Server stopping.", errorCode);
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
blog(LOG_INFO, "[WebSocketServer::Stop] Error: %s", errorCode.message().c_str());
|
blog(LOG_INFO, "[WebSocketServer::Stop] Error: %s",
|
||||||
|
errorCode.message().c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,35 +215,42 @@ void WebSocketServer::Stop()
|
|||||||
|
|
||||||
void WebSocketServer::InvalidateSession(websocketpp::connection_hdl hdl)
|
void WebSocketServer::InvalidateSession(websocketpp::connection_hdl hdl)
|
||||||
{
|
{
|
||||||
blog(LOG_INFO, "[WebSocketServer::InvalidateSession] Invalidating a session.");
|
blog(LOG_INFO,
|
||||||
|
"[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", errorCode.message().c_str());
|
blog(LOG_INFO, "[WebSocketServer::InvalidateSession] Error: %s",
|
||||||
|
errorCode.message().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_server.close(hdl, WebSocketCloseCode::SessionInvalidated, "Your session has been invalidated.", errorCode);
|
_server.close(hdl, WebSocketCloseCode::SessionInvalidated,
|
||||||
|
"Your session has been invalidated.", errorCode);
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
blog(LOG_INFO, "[WebSocketServer::InvalidateSession] Error: %s", errorCode.message().c_str());
|
blog(LOG_INFO, "[WebSocketServer::InvalidateSession] Error: %s",
|
||||||
|
errorCode.message().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<WebSocketServer::WebSocketSessionState> WebSocketServer::GetWebSocketSessions()
|
std::vector<WebSocketServer::WebSocketSessionState>
|
||||||
|
WebSocketServer::GetWebSocketSessions()
|
||||||
{
|
{
|
||||||
std::vector<WebSocketServer::WebSocketSessionState> webSocketSessions;
|
std::vector<WebSocketServer::WebSocketSessionState> webSocketSessions;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(_sessionMutex);
|
std::unique_lock<std::mutex> lock(_sessionMutex);
|
||||||
for (auto & [hdl, session] : _sessions) {
|
for (auto &[hdl, session] : _sessions) {
|
||||||
uint64_t connectedAt = session->ConnectedAt();
|
uint64_t connectedAt = session->ConnectedAt();
|
||||||
uint64_t incomingMessages = session->IncomingMessages();
|
uint64_t incomingMessages = session->IncomingMessages();
|
||||||
uint64_t outgoingMessages = session->OutgoingMessages();
|
uint64_t outgoingMessages = session->OutgoingMessages();
|
||||||
std::string remoteAddress = session->RemoteAddress();
|
std::string remoteAddress = session->RemoteAddress();
|
||||||
bool isIdentified = session->IsIdentified();
|
bool isIdentified = session->IsIdentified();
|
||||||
|
|
||||||
webSocketSessions.emplace_back(WebSocketSessionState{hdl, remoteAddress, connectedAt, incomingMessages, outgoingMessages, isIdentified});
|
webSocketSessions.emplace_back(WebSocketSessionState{
|
||||||
|
hdl, remoteAddress, connectedAt, incomingMessages,
|
||||||
|
outgoingMessages, isIdentified});
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
@ -237,12 +261,14 @@ void WebSocketServer::onObsLoaded()
|
|||||||
{
|
{
|
||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
blog(LOG_ERROR, "[WebSocketServer::onObsLoaded] Unable to retreive config!");
|
blog(LOG_ERROR,
|
||||||
|
"[WebSocketServer::onObsLoaded] Unable to retreive config!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conf->ServerEnabled) {
|
if (conf->ServerEnabled) {
|
||||||
blog(LOG_INFO, "[WebSocketServer::onObsLoaded] WebSocket server is enabled, starting...");
|
blog(LOG_INFO,
|
||||||
|
"[WebSocketServer::onObsLoaded] WebSocket server is enabled, starting...");
|
||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,9 +277,11 @@ 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 = conn->get_requested_subprotocols();
|
std::vector<std::string> requestedSubprotocols =
|
||||||
|
conn->get_requested_subprotocols();
|
||||||
for (auto subprotocol : requestedSubprotocols) {
|
for (auto subprotocol : requestedSubprotocols) {
|
||||||
if (subprotocol == "obswebsocket.json" || subprotocol == "obswebsocket.msgpack") {
|
if (subprotocol == "obswebsocket.json" ||
|
||||||
|
subprotocol == "obswebsocket.msgpack") {
|
||||||
conn->select_subprotocol(subprotocol);
|
conn->select_subprotocol(subprotocol);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -268,13 +296,15 @@ void WebSocketServer::onOpen(websocketpp::connection_hdl hdl)
|
|||||||
|
|
||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
blog(LOG_ERROR, "[WebSocketServer::onOpen] Unable to retreive config!");
|
blog(LOG_ERROR,
|
||||||
|
"[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] = std::make_shared<WebSocketSession>();
|
SessionPtr session = _sessions[hdl] =
|
||||||
|
std::make_shared<WebSocketSession>();
|
||||||
std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
|
std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
@ -299,8 +329,10 @@ 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"] = sessionChallenge;
|
helloMessageData["authentication"]["challenge"] =
|
||||||
helloMessageData["authentication"]["salt"] = _authenticationSalt;
|
sessionChallenge;
|
||||||
|
helloMessageData["authentication"]["salt"] =
|
||||||
|
_authenticationSalt;
|
||||||
}
|
}
|
||||||
json helloMessage;
|
json helloMessage;
|
||||||
helloMessage["op"] = 0;
|
helloMessage["op"] = 0;
|
||||||
@ -320,20 +352,27 @@ void WebSocketServer::onOpen(websocketpp::connection_hdl hdl)
|
|||||||
emit ClientConnected(state);
|
emit ClientConnected(state);
|
||||||
|
|
||||||
// Log connection
|
// Log connection
|
||||||
blog(LOG_INFO, "[WebSocketServer::onOpen] New WebSocket client has connected from %s", session->RemoteAddress().c_str());
|
blog(LOG_INFO,
|
||||||
|
"[WebSocketServer::onOpen] New WebSocket client has connected from %s",
|
||||||
|
session->RemoteAddress().c_str());
|
||||||
|
|
||||||
blog_debug("[WebSocketServer::onOpen] Sending Op 0 (Hello) message:\n%s", helloMessage.dump(2).c_str());
|
blog_debug(
|
||||||
|
"[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, websocketpp::frame::opcode::text, errorCode);
|
_server.send(hdl, helloMessageJson,
|
||||||
|
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(), msgPackData.end());
|
std::string messageMsgPack(msgPackData.begin(),
|
||||||
_server.send(hdl, messageMsgPack, websocketpp::frame::opcode::binary, errorCode);
|
msgPackData.end());
|
||||||
|
_server.send(hdl, messageMsgPack,
|
||||||
|
websocketpp::frame::opcode::binary, errorCode);
|
||||||
}
|
}
|
||||||
session->IncrementOutgoingMessages();
|
session->IncrementOutgoingMessages();
|
||||||
}
|
}
|
||||||
@ -372,24 +411,37 @@ void WebSocketServer::onClose(websocketpp::connection_hdl hdl)
|
|||||||
emit ClientDisconnected(state, conn->get_local_close_code());
|
emit ClientDisconnected(state, conn->get_local_close_code());
|
||||||
|
|
||||||
// Log disconnection
|
// Log disconnection
|
||||||
blog(LOG_INFO, "[WebSocketServer::onClose] WebSocket client %s has disconnected", remoteAddress.c_str());
|
blog(LOG_INFO,
|
||||||
|
"[WebSocketServer::onClose] WebSocket client %s has disconnected",
|
||||||
|
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, "[WebSocketServer::onClose] Unable to retreive config!");
|
blog(LOG_ERROR,
|
||||||
|
"[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 && (conn->get_local_close_code() != websocketpp::close::status::going_away) && conf->AlertsEnabled) {
|
if (isIdentified &&
|
||||||
QString title = obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Title");
|
(conn->get_local_close_code() !=
|
||||||
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Body")).arg(QString::fromStdString(remoteAddress));
|
websocketpp::close::status::going_away) &&
|
||||||
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
|
conf->AlertsEnabled) {
|
||||||
|
QString title = obs_module_text(
|
||||||
|
"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(websocketpp::connection_hdl hdl, websocketpp::server<websocketpp::config::asio>::message_ptr message)
|
void WebSocketServer::onMessage(
|
||||||
|
websocketpp::connection_hdl hdl,
|
||||||
|
websocketpp::server<websocketpp::config::asio>::message_ptr message)
|
||||||
{
|
{
|
||||||
auto opCode = message->get_opcode();
|
auto opCode = message->get_opcode();
|
||||||
std::string payload = message->get_payload();
|
std::string payload = message->get_payload();
|
||||||
@ -398,7 +450,7 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se
|
|||||||
SessionPtr session;
|
SessionPtr session;
|
||||||
try {
|
try {
|
||||||
session = _sessions.at(hdl);
|
session = _sessions.at(hdl);
|
||||||
} catch (const std::out_of_range& oor) {
|
} catch (const std::out_of_range &oor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
@ -412,31 +464,52 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se
|
|||||||
uint8_t sessionEncoding = session->Encoding();
|
uint8_t sessionEncoding = session->Encoding();
|
||||||
if (sessionEncoding == WebSocketEncoding::Json) {
|
if (sessionEncoding == WebSocketEncoding::Json) {
|
||||||
if (opCode != websocketpp::frame::opcode::text) {
|
if (opCode != websocketpp::frame::opcode::text) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, "Your session encoding is set to Json, but a binary message was received.", errorCode);
|
_server.close(
|
||||||
|
hdl,
|
||||||
|
WebSocketCloseCode::MessageDecodeError,
|
||||||
|
"Your session encoding is set to Json, but a binary message was received.",
|
||||||
|
errorCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
incomingMessage = json::parse(payload);
|
incomingMessage = json::parse(payload);
|
||||||
} catch (json::parse_error& e) {
|
} catch (json::parse_error &e) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, std::string("Unable to decode Json: ") + e.what(), errorCode);
|
_server.close(
|
||||||
|
hdl,
|
||||||
|
WebSocketCloseCode::MessageDecodeError,
|
||||||
|
std::string("Unable to decode Json: ") +
|
||||||
|
e.what(),
|
||||||
|
errorCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (sessionEncoding == WebSocketEncoding::MsgPack) {
|
} else if (sessionEncoding == WebSocketEncoding::MsgPack) {
|
||||||
if (opCode != websocketpp::frame::opcode::binary) {
|
if (opCode != websocketpp::frame::opcode::binary) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, "Your session encoding is set to MsgPack, but a text message was received.", errorCode);
|
_server.close(
|
||||||
|
hdl,
|
||||||
|
WebSocketCloseCode::MessageDecodeError,
|
||||||
|
"Your session encoding is set to MsgPack, but a text message was received.",
|
||||||
|
errorCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
incomingMessage = json::from_msgpack(payload);
|
incomingMessage = json::from_msgpack(payload);
|
||||||
} catch (json::parse_error& e) {
|
} catch (json::parse_error &e) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, std::string("Unable to decode MsgPack: ") + e.what(), errorCode);
|
_server.close(
|
||||||
|
hdl,
|
||||||
|
WebSocketCloseCode::MessageDecodeError,
|
||||||
|
std::string(
|
||||||
|
"Unable to decode MsgPack: ") +
|
||||||
|
e.what(),
|
||||||
|
errorCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blog_debug("[WebSocketServer::onMessage] Incoming message (decoded):\n%s", incomingMessage.dump(2).c_str());
|
blog_debug(
|
||||||
|
"[WebSocketServer::onMessage] Incoming message (decoded):\n%s",
|
||||||
|
incomingMessage.dump(2).c_str());
|
||||||
|
|
||||||
ProcessResult ret;
|
ProcessResult ret;
|
||||||
|
|
||||||
@ -448,10 +521,15 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect client if 4.x protocol is detected
|
// Disconnect client if 4.x protocol is detected
|
||||||
if (!session->IsIdentified() && incomingMessage.contains("request-type")) {
|
if (!session->IsIdentified() &&
|
||||||
blog(LOG_WARNING, "[WebSocketServer::onMessage] Client %s appears to be running a pre-5.0.0 protocol.", session->RemoteAddress().c_str());
|
incomingMessage.contains("request-type")) {
|
||||||
ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion;
|
blog(LOG_WARNING,
|
||||||
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.";
|
"[WebSocketServer::onMessage] Client %s appears to be running a pre-5.0.0 protocol.",
|
||||||
|
session->RemoteAddress().c_str());
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::UnsupportedRpcVersion;
|
||||||
|
ret.closeReason =
|
||||||
|
"You appear to be attempting to connect with the pre-5.0.0 plugin protocol. Check to make sure your client is updated.";
|
||||||
goto skipProcessing;
|
goto skipProcessing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,31 +540,44 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se
|
|||||||
goto skipProcessing;
|
goto skipProcessing;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessMessage(session, ret, incomingMessage["op"], incomingMessage["d"]);
|
ProcessMessage(session, ret, incomingMessage["op"],
|
||||||
|
incomingMessage["d"]);
|
||||||
|
|
||||||
skipProcessing:
|
skipProcessing:
|
||||||
if (ret.closeCode != WebSocketCloseCode::DontClose) {
|
if (ret.closeCode != WebSocketCloseCode::DontClose) {
|
||||||
websocketpp::lib::error_code errorCode;
|
websocketpp::lib::error_code errorCode;
|
||||||
_server.close(hdl, ret.closeCode, ret.closeReason, errorCode);
|
_server.close(hdl, ret.closeCode, ret.closeReason,
|
||||||
|
errorCode);
|
||||||
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 = ret.result.dump();
|
std::string helloMessageJson =
|
||||||
_server.send(hdl, helloMessageJson, websocketpp::frame::opcode::text, errorCode);
|
ret.result.dump();
|
||||||
} else if (sessionEncoding == WebSocketEncoding::MsgPack) {
|
_server.send(hdl, helloMessageJson,
|
||||||
|
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(), msgPackData.end());
|
std::string messageMsgPack(msgPackData.begin(),
|
||||||
_server.send(hdl, messageMsgPack, websocketpp::frame::opcode::binary, errorCode);
|
msgPackData.end());
|
||||||
|
_server.send(hdl, messageMsgPack,
|
||||||
|
websocketpp::frame::opcode::binary,
|
||||||
|
errorCode);
|
||||||
}
|
}
|
||||||
session->IncrementOutgoingMessages();
|
session->IncrementOutgoingMessages();
|
||||||
|
|
||||||
blog_debug("[WebSocketServer::onMessage] Outgoing message:\n%s", ret.result.dump(2).c_str());
|
blog_debug(
|
||||||
|
"[WebSocketServer::onMessage] Outgoing message:\n%s",
|
||||||
|
ret.result.dump(2).c_str());
|
||||||
|
|
||||||
if (errorCode)
|
if (errorCode)
|
||||||
blog(LOG_WARNING, "[WebSocketServer::onMessage] Sending message to client failed: %s", errorCode.message().c_str());
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketServer::onMessage] Sending message to client failed: %s",
|
||||||
|
errorCode.message().c_str());
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -34,73 +34,79 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../requesthandler/rpc/Request.h"
|
#include "../requesthandler/rpc/Request.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
class WebSocketServer : QObject
|
class WebSocketServer : QObject {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum WebSocketEncoding {
|
enum WebSocketEncoding { Json, MsgPack };
|
||||||
Json,
|
|
||||||
MsgPack
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WebSocketSessionState {
|
struct WebSocketSessionState {
|
||||||
websocketpp::connection_hdl hdl;
|
websocketpp::connection_hdl hdl;
|
||||||
std::string remoteAddress;
|
std::string remoteAddress;
|
||||||
uint64_t connectedAt;
|
uint64_t connectedAt;
|
||||||
uint64_t incomingMessages;
|
uint64_t incomingMessages;
|
||||||
uint64_t outgoingMessages;
|
uint64_t outgoingMessages;
|
||||||
bool isIdentified;
|
bool isIdentified;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocketServer();
|
WebSocketServer();
|
||||||
~WebSocketServer();
|
~WebSocketServer();
|
||||||
|
|
||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
void InvalidateSession(websocketpp::connection_hdl hdl);
|
void InvalidateSession(websocketpp::connection_hdl hdl);
|
||||||
void BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData = nullptr, uint8_t rpcVersion = 0);
|
void BroadcastEvent(uint64_t requiredIntent,
|
||||||
|
const std::string &eventType,
|
||||||
|
const json &eventData = nullptr,
|
||||||
|
uint8_t rpcVersion = 0);
|
||||||
|
|
||||||
bool IsListening() {
|
bool IsListening() { return _server.is_listening(); }
|
||||||
return _server.is_listening();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<WebSocketSessionState> GetWebSocketSessions();
|
std::vector<WebSocketSessionState> GetWebSocketSessions();
|
||||||
|
|
||||||
QThreadPool *GetThreadPool() {
|
QThreadPool *GetThreadPool() { return &_threadPool; }
|
||||||
return &_threadPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void ClientConnected(WebSocketSessionState state);
|
void ClientConnected(WebSocketSessionState state);
|
||||||
void ClientDisconnected(WebSocketSessionState state, uint16_t closeCode);
|
void ClientDisconnected(WebSocketSessionState state,
|
||||||
|
uint16_t closeCode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ProcessResult {
|
struct ProcessResult {
|
||||||
WebSocketCloseCode::WebSocketCloseCode closeCode = WebSocketCloseCode::DontClose;
|
WebSocketCloseCode::WebSocketCloseCode closeCode =
|
||||||
std::string closeReason;
|
WebSocketCloseCode::DontClose;
|
||||||
json result;
|
std::string closeReason;
|
||||||
};
|
json result;
|
||||||
|
};
|
||||||
|
|
||||||
void ServerRunner();
|
void ServerRunner();
|
||||||
|
|
||||||
void onObsLoaded();
|
void onObsLoaded();
|
||||||
bool onValidate(websocketpp::connection_hdl hdl);
|
bool onValidate(websocketpp::connection_hdl hdl);
|
||||||
void onOpen(websocketpp::connection_hdl hdl);
|
void onOpen(websocketpp::connection_hdl hdl);
|
||||||
void onClose(websocketpp::connection_hdl hdl);
|
void onClose(websocketpp::connection_hdl hdl);
|
||||||
void onMessage(websocketpp::connection_hdl hdl, websocketpp::server<websocketpp::config::asio>::message_ptr message);
|
void
|
||||||
|
onMessage(websocketpp::connection_hdl hdl,
|
||||||
|
websocketpp::server<websocketpp::config::asio>::message_ptr
|
||||||
|
message);
|
||||||
|
|
||||||
static void SetSessionParameters(SessionPtr session, WebSocketServer::ProcessResult &ret, const json &payloadData);
|
static void SetSessionParameters(SessionPtr session,
|
||||||
void ProcessMessage(SessionPtr session, ProcessResult &ret, WebSocketOpCode::WebSocketOpCode opCode, json &payloadData);
|
WebSocketServer::ProcessResult &ret,
|
||||||
|
const json &payloadData);
|
||||||
|
void ProcessMessage(SessionPtr session, ProcessResult &ret,
|
||||||
|
WebSocketOpCode::WebSocketOpCode opCode,
|
||||||
|
json &payloadData);
|
||||||
|
|
||||||
QThreadPool _threadPool;
|
QThreadPool _threadPool;
|
||||||
|
|
||||||
std::thread _serverThread;
|
std::thread _serverThread;
|
||||||
websocketpp::server<websocketpp::config::asio> _server;
|
websocketpp::server<websocketpp::config::asio> _server;
|
||||||
|
|
||||||
std::string _authenticationSecret;
|
std::string _authenticationSecret;
|
||||||
std::string _authenticationSalt;
|
std::string _authenticationSalt;
|
||||||
|
|
||||||
std::mutex _sessionMutex;
|
std::mutex _sessionMutex;
|
||||||
std::map<websocketpp::connection_hdl, SessionPtr, std::owner_less<websocketpp::connection_hdl>> _sessions;
|
std::map<websocketpp::connection_hdl, SessionPtr,
|
||||||
|
std::owner_less<websocketpp::connection_hdl>>
|
||||||
|
_sessions;
|
||||||
};
|
};
|
||||||
|
@ -35,19 +35,20 @@ static bool IsSupportedRpcVersion(uint8_t requestedVersion)
|
|||||||
return (requestedVersion == 1);
|
return (requestedVersion == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static json ConstructRequestResult(RequestResult requestResult, const json &requestJson)
|
static json ConstructRequestResult(RequestResult requestResult,
|
||||||
|
const json &requestJson)
|
||||||
{
|
{
|
||||||
json ret;
|
json ret;
|
||||||
|
|
||||||
ret["requestType"] = requestJson["requestType"];
|
ret["requestType"] = requestJson["requestType"];
|
||||||
|
|
||||||
if (requestJson.contains("requestId") && !requestJson["requestId"].is_null())
|
if (requestJson.contains("requestId") &&
|
||||||
|
!requestJson["requestId"].is_null())
|
||||||
ret["requestId"] = requestJson["requestId"];
|
ret["requestId"] = requestJson["requestId"];
|
||||||
|
|
||||||
ret["requestStatus"] = {
|
ret["requestStatus"] = {{"result", requestResult.StatusCode ==
|
||||||
{"result", requestResult.StatusCode == RequestStatus::Success},
|
RequestStatus::Success},
|
||||||
{"code", requestResult.StatusCode}
|
{"code", requestResult.StatusCode}};
|
||||||
};
|
|
||||||
|
|
||||||
if (!requestResult.Comment.empty())
|
if (!requestResult.Comment.empty())
|
||||||
ret["requestStatus"]["comment"] = requestResult.Comment;
|
ret["requestStatus"]["comment"] = requestResult.Comment;
|
||||||
@ -58,27 +59,37 @@ static json ConstructRequestResult(RequestResult requestResult, const json &requ
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::SetSessionParameters(SessionPtr session, ProcessResult &ret, const json &payloadData)
|
void WebSocketServer::SetSessionParameters(SessionPtr session,
|
||||||
|
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 = WebSocketCloseCode::InvalidDataFieldType;
|
ret.closeCode =
|
||||||
ret.closeReason = "Your `eventSubscriptions` is not an unsigned number.";
|
WebSocketCloseCode::InvalidDataFieldType;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your `eventSubscriptions` is not an unsigned number.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
session->SetEventSubscriptions(payloadData["eventSubscriptions"]);
|
session->SetEventSubscriptions(
|
||||||
|
payloadData["eventSubscriptions"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::ProcessResult &ret, WebSocketOpCode::WebSocketOpCode opCode, json &payloadData)
|
void WebSocketServer::ProcessMessage(SessionPtr session,
|
||||||
|
WebSocketServer::ProcessResult &ret,
|
||||||
|
WebSocketOpCode::WebSocketOpCode opCode,
|
||||||
|
json &payloadData)
|
||||||
{
|
{
|
||||||
if (!payloadData.is_object()) {
|
if (!payloadData.is_object()) {
|
||||||
if (payloadData.is_null()) {
|
if (payloadData.is_null()) {
|
||||||
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 = WebSocketCloseCode::InvalidDataFieldType;
|
ret.closeCode =
|
||||||
ret.closeReason = "Your payload's data (`d`) is not an object.";
|
WebSocketCloseCode::InvalidDataFieldType;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your payload's data (`d`) is not an object.";
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -86,223 +97,301 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
// 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 = "You attempted to send a non-Identify message while not identified.";
|
ret.closeReason =
|
||||||
|
"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(session->OperationMutex);
|
std::unique_lock<std::mutex> sessionLock(
|
||||||
if (session->IsIdentified()) {
|
session->OperationMutex);
|
||||||
ret.closeCode = WebSocketCloseCode::AlreadyIdentified;
|
if (session->IsIdentified()) {
|
||||||
ret.closeReason = "You are already Identified with the obs-websocket server.";
|
ret.closeCode = WebSocketCloseCode::AlreadyIdentified;
|
||||||
return;
|
ret.closeReason =
|
||||||
}
|
"You are already Identified with the obs-websocket server.";
|
||||||
|
|
||||||
if (session->AuthenticationRequired()) {
|
|
||||||
if (!payloadData.contains("authentication")) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
|
|
||||||
ret.closeReason = "Your payload's data is missing an `authentication` string, however authentication is required.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!Utils::Crypto::CheckAuthenticationString(session->Secret(), session->Challenge(), payloadData["authentication"])) {
|
|
||||||
auto conf = GetConfig();
|
|
||||||
if (conf && conf->AlertsEnabled) {
|
|
||||||
QString title = obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Title");
|
|
||||||
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Body")).arg(QString::fromStdString(session->RemoteAddress()));
|
|
||||||
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Warning, title, body);
|
|
||||||
}
|
|
||||||
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
|
|
||||||
ret.closeReason = "Authentication failed.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!payloadData.contains("rpcVersion")) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
|
||||||
ret.closeReason = "Your payload's data is missing an `rpcVersion`.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!payloadData["rpcVersion"].is_number_unsigned()) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
|
||||||
ret.closeReason = "Your `rpcVersion` is not an unsigned number.";
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t requestedRpcVersion = payloadData["rpcVersion"];
|
|
||||||
if (!IsSupportedRpcVersion(requestedRpcVersion)) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion;
|
|
||||||
ret.closeReason = "Your requested RPC version is not supported by this server.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
session->SetRpcVersion(requestedRpcVersion);
|
|
||||||
|
|
||||||
SetSessionParameters(session, ret, payloadData);
|
|
||||||
if (ret.closeCode != WebSocketCloseCode::DontClose) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment refs for event subscriptions
|
|
||||||
auto eventHandler = GetEventHandler();
|
|
||||||
eventHandler->ProcessSubscription(session->EventSubscriptions());
|
|
||||||
|
|
||||||
// Mark session as identified
|
|
||||||
session->SetIsIdentified(true);
|
|
||||||
|
|
||||||
// Send desktop notification. TODO: Move to UI code
|
|
||||||
auto conf = GetConfig();
|
|
||||||
if (conf && conf->AlertsEnabled) {
|
|
||||||
QString title = obs_module_text("OBSWebSocket.TrayNotification.Identified.Title");
|
|
||||||
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Identified.Body")).arg(QString::fromStdString(session->RemoteAddress()));
|
|
||||||
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.result["op"] = WebSocketOpCode::Identified;
|
|
||||||
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
|
||||||
} return;
|
|
||||||
case WebSocketOpCode::Reidentify: { // Reidentify
|
|
||||||
std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
|
|
||||||
|
|
||||||
// Decrement refs for current subscriptions
|
|
||||||
auto eventHandler = GetEventHandler();
|
|
||||||
eventHandler->ProcessUnsubscription(session->EventSubscriptions());
|
|
||||||
|
|
||||||
SetSessionParameters(session, ret, payloadData);
|
|
||||||
if (ret.closeCode != WebSocketCloseCode::DontClose) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment refs for new subscriptions
|
|
||||||
eventHandler->ProcessSubscription(session->EventSubscriptions());
|
|
||||||
|
|
||||||
ret.result["op"] = WebSocketOpCode::Identified;
|
|
||||||
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
|
||||||
} return;
|
|
||||||
case WebSocketOpCode::Request: { // Request
|
|
||||||
// RequestID checking has to be done here where we are able to close the connection.
|
|
||||||
if (!payloadData.contains("requestId")) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
|
||||||
ret.closeReason = "Your payload data is missing a `requestId`.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestHandler requestHandler(session);
|
|
||||||
Request request(payloadData["requestType"], payloadData["requestData"]);
|
|
||||||
|
|
||||||
RequestResult requestResult = requestHandler.ProcessRequest(request);
|
|
||||||
|
|
||||||
json resultPayloadData;
|
|
||||||
resultPayloadData["requestType"] = payloadData["requestType"];
|
|
||||||
resultPayloadData["requestId"] = payloadData["requestId"];
|
|
||||||
resultPayloadData["requestStatus"] = {
|
|
||||||
{"result", requestResult.StatusCode == RequestStatus::Success},
|
|
||||||
{"code", requestResult.StatusCode}
|
|
||||||
};
|
|
||||||
if (!requestResult.Comment.empty())
|
|
||||||
resultPayloadData["requestStatus"]["comment"] = requestResult.Comment;
|
|
||||||
if (requestResult.ResponseData.is_object())
|
|
||||||
resultPayloadData["responseData"] = requestResult.ResponseData;
|
|
||||||
ret.result["op"] = WebSocketOpCode::RequestResponse;
|
|
||||||
ret.result["d"] = resultPayloadData;
|
|
||||||
} return;
|
|
||||||
case WebSocketOpCode::RequestBatch: { // RequestBatch
|
|
||||||
// RequestID checking has to be done here where we are able to close the connection.
|
|
||||||
if (!payloadData.contains("requestId")) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
|
||||||
ret.closeReason = "Your payload data is missing a `requestId`.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!payloadData.contains("requests")) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
|
||||||
ret.closeReason = "Your payload data is missing a `requests`.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!payloadData["requests"].is_array()) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
|
||||||
ret.closeReason = "Your `requests` is not an array.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::SerialRealtime;
|
|
||||||
if (payloadData.contains("executionType") && !payloadData["executionType"].is_null()) {
|
|
||||||
if (!payloadData["executionType"].is_number_unsigned()) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
|
||||||
ret.closeReason = "Your `executionType` is not a number.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t requestedExecutionType = payloadData["executionType"];
|
|
||||||
if (!RequestBatchExecutionType::IsValid(requestedExecutionType) || requestedExecutionType == RequestBatchExecutionType::None) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldValue;
|
|
||||||
ret.closeReason = "Your `executionType` has an invalid value.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The thread pool must support 2 or more threads else parallel requests will deadlock.
|
|
||||||
if (requestedExecutionType == RequestBatchExecutionType::Parallel && _threadPool.maxThreadCount() < 2) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
|
|
||||||
ret.closeReason = "Parallel request batch processing is not available on this system due to limited core count.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
executionType = (RequestBatchExecutionType::RequestBatchExecutionType)requestedExecutionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payloadData.contains("variables") && !payloadData["variables"].is_null()) {
|
|
||||||
if (!payloadData.is_object()) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
|
||||||
ret.closeReason = "Your `variables` is not an object.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executionType == RequestBatchExecutionType::Parallel) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
|
|
||||||
ret.closeReason = "Variables are not supported in Parallel mode.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool haltOnFailure = false;
|
|
||||||
if (payloadData.contains("haltOnFailure") && !payloadData["haltOnFailure"].is_null()) {
|
|
||||||
if (!payloadData["haltOnFailure"].is_boolean()) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
|
||||||
ret.closeReason = "Your `haltOnFailure` is not a boolean.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
haltOnFailure = payloadData["haltOnFailure"];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<json> requests = payloadData["requests"];
|
|
||||||
|
|
||||||
std::vector<RequestBatchRequest> requestsVector;
|
|
||||||
for (auto &requestJson : requests)
|
|
||||||
requestsVector.emplace_back(requestJson["requestType"], requestJson["requestData"], executionType, requestJson["inputVariables"], requestJson["outputVariables"]);
|
|
||||||
|
|
||||||
auto resultsVector = RequestBatchHandler::ProcessRequestBatch(_threadPool, session, executionType, requestsVector, payloadData["variables"], haltOnFailure);
|
|
||||||
|
|
||||||
size_t i = 0;
|
|
||||||
std::vector<json> results;
|
|
||||||
for (auto &requestResult : resultsVector) {
|
|
||||||
results.push_back(ConstructRequestResult(requestResult, requests[i]));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.result["op"] = WebSocketOpCode::RequestBatchResponse;
|
|
||||||
ret.result["d"]["requestId"] = payloadData["requestId"];
|
|
||||||
ret.result["d"]["results"] = results;
|
|
||||||
} return;
|
|
||||||
default:
|
|
||||||
ret.closeCode = WebSocketCloseCode::UnknownOpCode;
|
|
||||||
ret.closeReason = std::string("Unknown OpCode: ") + std::to_string(opCode);
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session->AuthenticationRequired()) {
|
||||||
|
if (!payloadData.contains("authentication")) {
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::AuthenticationFailed;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your payload's data is missing an `authentication` string, however authentication is required.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Utils::Crypto::CheckAuthenticationString(
|
||||||
|
session->Secret(), session->Challenge(),
|
||||||
|
payloadData["authentication"])) {
|
||||||
|
auto conf = GetConfig();
|
||||||
|
if (conf && conf->AlertsEnabled) {
|
||||||
|
QString title = obs_module_text(
|
||||||
|
"OBSWebSocket.TrayNotification.AuthenticationFailed.Title");
|
||||||
|
QString body =
|
||||||
|
QString(obs_module_text(
|
||||||
|
"OBSWebSocket.TrayNotification.AuthenticationFailed.Body"))
|
||||||
|
.arg(QString::fromStdString(
|
||||||
|
session->RemoteAddress()));
|
||||||
|
Utils::Platform::SendTrayNotification(
|
||||||
|
QSystemTrayIcon::Warning, title,
|
||||||
|
body);
|
||||||
|
}
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::AuthenticationFailed;
|
||||||
|
ret.closeReason = "Authentication failed.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payloadData.contains("rpcVersion")) {
|
||||||
|
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your payload's data is missing an `rpcVersion`.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payloadData["rpcVersion"].is_number_unsigned()) {
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::InvalidDataFieldType;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your `rpcVersion` is not an unsigned number.";
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t requestedRpcVersion = payloadData["rpcVersion"];
|
||||||
|
if (!IsSupportedRpcVersion(requestedRpcVersion)) {
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::UnsupportedRpcVersion;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your requested RPC version is not supported by this server.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
session->SetRpcVersion(requestedRpcVersion);
|
||||||
|
|
||||||
|
SetSessionParameters(session, ret, payloadData);
|
||||||
|
if (ret.closeCode != WebSocketCloseCode::DontClose) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment refs for event subscriptions
|
||||||
|
auto eventHandler = GetEventHandler();
|
||||||
|
eventHandler->ProcessSubscription(
|
||||||
|
session->EventSubscriptions());
|
||||||
|
|
||||||
|
// Mark session as identified
|
||||||
|
session->SetIsIdentified(true);
|
||||||
|
|
||||||
|
// Send desktop notification. TODO: Move to UI code
|
||||||
|
auto conf = GetConfig();
|
||||||
|
if (conf && conf->AlertsEnabled) {
|
||||||
|
QString title = obs_module_text(
|
||||||
|
"OBSWebSocket.TrayNotification.Identified.Title");
|
||||||
|
QString body =
|
||||||
|
QString(obs_module_text(
|
||||||
|
"OBSWebSocket.TrayNotification.Identified.Body"))
|
||||||
|
.arg(QString::fromStdString(
|
||||||
|
session->RemoteAddress()));
|
||||||
|
Utils::Platform::SendTrayNotification(
|
||||||
|
QSystemTrayIcon::Information, title, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.result["op"] = WebSocketOpCode::Identified;
|
||||||
|
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case WebSocketOpCode::Reidentify: { // Reidentify
|
||||||
|
std::unique_lock<std::mutex> sessionLock(
|
||||||
|
session->OperationMutex);
|
||||||
|
|
||||||
|
// Decrement refs for current subscriptions
|
||||||
|
auto eventHandler = GetEventHandler();
|
||||||
|
eventHandler->ProcessUnsubscription(
|
||||||
|
session->EventSubscriptions());
|
||||||
|
|
||||||
|
SetSessionParameters(session, ret, payloadData);
|
||||||
|
if (ret.closeCode != WebSocketCloseCode::DontClose) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment refs for new subscriptions
|
||||||
|
eventHandler->ProcessSubscription(
|
||||||
|
session->EventSubscriptions());
|
||||||
|
|
||||||
|
ret.result["op"] = WebSocketOpCode::Identified;
|
||||||
|
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case WebSocketOpCode::Request: { // Request
|
||||||
|
// RequestID checking has to be done here where we are able to close the connection.
|
||||||
|
if (!payloadData.contains("requestId")) {
|
||||||
|
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your payload data is missing a `requestId`.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestHandler requestHandler(session);
|
||||||
|
Request request(payloadData["requestType"],
|
||||||
|
payloadData["requestData"]);
|
||||||
|
|
||||||
|
RequestResult requestResult =
|
||||||
|
requestHandler.ProcessRequest(request);
|
||||||
|
|
||||||
|
json resultPayloadData;
|
||||||
|
resultPayloadData["requestType"] = payloadData["requestType"];
|
||||||
|
resultPayloadData["requestId"] = payloadData["requestId"];
|
||||||
|
resultPayloadData["requestStatus"] = {
|
||||||
|
{"result",
|
||||||
|
requestResult.StatusCode == RequestStatus::Success},
|
||||||
|
{"code", requestResult.StatusCode}};
|
||||||
|
if (!requestResult.Comment.empty())
|
||||||
|
resultPayloadData["requestStatus"]["comment"] =
|
||||||
|
requestResult.Comment;
|
||||||
|
if (requestResult.ResponseData.is_object())
|
||||||
|
resultPayloadData["responseData"] =
|
||||||
|
requestResult.ResponseData;
|
||||||
|
ret.result["op"] = WebSocketOpCode::RequestResponse;
|
||||||
|
ret.result["d"] = resultPayloadData;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case WebSocketOpCode::RequestBatch: { // RequestBatch
|
||||||
|
// RequestID checking has to be done here where we are able to close the connection.
|
||||||
|
if (!payloadData.contains("requestId")) {
|
||||||
|
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your payload data is missing a `requestId`.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payloadData.contains("requests")) {
|
||||||
|
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your payload data is missing a `requests`.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payloadData["requests"].is_array()) {
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::InvalidDataFieldType;
|
||||||
|
ret.closeReason = "Your `requests` is not an array.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBatchExecutionType::RequestBatchExecutionType
|
||||||
|
executionType =
|
||||||
|
RequestBatchExecutionType::SerialRealtime;
|
||||||
|
if (payloadData.contains("executionType") &&
|
||||||
|
!payloadData["executionType"].is_null()) {
|
||||||
|
if (!payloadData["executionType"].is_number_unsigned()) {
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::InvalidDataFieldType;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your `executionType` is not a number.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t requestedExecutionType =
|
||||||
|
payloadData["executionType"];
|
||||||
|
if (!RequestBatchExecutionType::IsValid(
|
||||||
|
requestedExecutionType) ||
|
||||||
|
requestedExecutionType ==
|
||||||
|
RequestBatchExecutionType::None) {
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::InvalidDataFieldValue;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your `executionType` has an invalid value.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The thread pool must support 2 or more threads else parallel requests will deadlock.
|
||||||
|
if (requestedExecutionType ==
|
||||||
|
RequestBatchExecutionType::Parallel &&
|
||||||
|
_threadPool.maxThreadCount() < 2) {
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::UnsupportedFeature;
|
||||||
|
ret.closeReason =
|
||||||
|
"Parallel request batch processing is not available on this system due to limited core count.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
executionType = (RequestBatchExecutionType::
|
||||||
|
RequestBatchExecutionType)
|
||||||
|
requestedExecutionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payloadData.contains("variables") &&
|
||||||
|
!payloadData["variables"].is_null()) {
|
||||||
|
if (!payloadData.is_object()) {
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::InvalidDataFieldType;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your `variables` is not an object.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (executionType ==
|
||||||
|
RequestBatchExecutionType::Parallel) {
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::UnsupportedFeature;
|
||||||
|
ret.closeReason =
|
||||||
|
"Variables are not supported in Parallel mode.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool haltOnFailure = false;
|
||||||
|
if (payloadData.contains("haltOnFailure") &&
|
||||||
|
!payloadData["haltOnFailure"].is_null()) {
|
||||||
|
if (!payloadData["haltOnFailure"].is_boolean()) {
|
||||||
|
ret.closeCode =
|
||||||
|
WebSocketCloseCode::InvalidDataFieldType;
|
||||||
|
ret.closeReason =
|
||||||
|
"Your `haltOnFailure` is not a boolean.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
haltOnFailure = payloadData["haltOnFailure"];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<json> requests = payloadData["requests"];
|
||||||
|
|
||||||
|
std::vector<RequestBatchRequest> requestsVector;
|
||||||
|
for (auto &requestJson : requests)
|
||||||
|
requestsVector.emplace_back(
|
||||||
|
requestJson["requestType"],
|
||||||
|
requestJson["requestData"], executionType,
|
||||||
|
requestJson["inputVariables"],
|
||||||
|
requestJson["outputVariables"]);
|
||||||
|
|
||||||
|
auto resultsVector = RequestBatchHandler::ProcessRequestBatch(
|
||||||
|
_threadPool, session, executionType, requestsVector,
|
||||||
|
payloadData["variables"], haltOnFailure);
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
std::vector<json> results;
|
||||||
|
for (auto &requestResult : resultsVector) {
|
||||||
|
results.push_back(ConstructRequestResult(requestResult,
|
||||||
|
requests[i]));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.result["op"] = WebSocketOpCode::RequestBatchResponse;
|
||||||
|
ret.result["d"]["requestId"] = payloadData["requestId"];
|
||||||
|
ret.result["d"]["results"] = results;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
ret.closeCode = WebSocketCloseCode::UnknownOpCode;
|
||||||
|
ret.closeReason = std::string("Unknown OpCode: ") +
|
||||||
|
std::to_string(opCode);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// It isn't consistent to directly call the WebSocketServer from the events system, but it would also be dumb to make it unnecessarily complicated.
|
// It isn't consistent to directly call the WebSocketServer from the events system, but it would also be dumb to make it unnecessarily complicated.
|
||||||
void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData, uint8_t rpcVersion)
|
void WebSocketServer::BroadcastEvent(uint64_t requiredIntent,
|
||||||
|
const std::string &eventType,
|
||||||
|
const json &eventData, uint8_t rpcVersion)
|
||||||
{
|
{
|
||||||
if (!_server.is_listening())
|
if (!_server.is_listening())
|
||||||
return;
|
return;
|
||||||
@ -322,38 +411,61 @@ void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string
|
|||||||
|
|
||||||
// Recurse connected sessions and send the event to suitable sessions.
|
// Recurse connected sessions and send the event to suitable sessions.
|
||||||
std::unique_lock<std::mutex> lock(_sessionMutex);
|
std::unique_lock<std::mutex> lock(_sessionMutex);
|
||||||
for (auto & it : _sessions) {
|
for (auto &it : _sessions) {
|
||||||
if (!it.second->IsIdentified()) {
|
if (!it.second->IsIdentified()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (rpcVersion && it.second->RpcVersion() != rpcVersion) {
|
if (rpcVersion &&
|
||||||
|
it.second->RpcVersion() != rpcVersion) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((it.second->EventSubscriptions() & requiredIntent) != 0) {
|
if ((it.second->EventSubscriptions() &
|
||||||
|
requiredIntent) != 0) {
|
||||||
websocketpp::lib::error_code errorCode;
|
websocketpp::lib::error_code errorCode;
|
||||||
switch (it.second->Encoding()) {
|
switch (it.second->Encoding()) {
|
||||||
case WebSocketEncoding::Json:
|
case WebSocketEncoding::Json:
|
||||||
if (messageJson.empty()) {
|
if (messageJson.empty()) {
|
||||||
messageJson = eventMessage.dump();
|
messageJson =
|
||||||
}
|
eventMessage.dump();
|
||||||
_server.send((websocketpp::connection_hdl)it.first, messageJson, websocketpp::frame::opcode::text, errorCode);
|
}
|
||||||
it.second->IncrementOutgoingMessages();
|
_server.send(
|
||||||
break;
|
(websocketpp::connection_hdl)
|
||||||
case WebSocketEncoding::MsgPack:
|
it.first,
|
||||||
if (messageMsgPack.empty()) {
|
messageJson,
|
||||||
auto msgPackData = json::to_msgpack(eventMessage);
|
websocketpp::frame::opcode::text,
|
||||||
messageMsgPack = std::string(msgPackData.begin(), msgPackData.end());
|
errorCode);
|
||||||
}
|
it.second->IncrementOutgoingMessages();
|
||||||
_server.send((websocketpp::connection_hdl)it.first, messageMsgPack, websocketpp::frame::opcode::binary, errorCode);
|
break;
|
||||||
it.second->IncrementOutgoingMessages();
|
case WebSocketEncoding::MsgPack:
|
||||||
break;
|
if (messageMsgPack.empty()) {
|
||||||
|
auto msgPackData =
|
||||||
|
json::to_msgpack(
|
||||||
|
eventMessage);
|
||||||
|
messageMsgPack = std::string(
|
||||||
|
msgPackData.begin(),
|
||||||
|
msgPackData.end());
|
||||||
|
}
|
||||||
|
_server.send(
|
||||||
|
(websocketpp::connection_hdl)
|
||||||
|
it.first,
|
||||||
|
messageMsgPack,
|
||||||
|
websocketpp::frame::opcode::binary,
|
||||||
|
errorCode);
|
||||||
|
it.second->IncrementOutgoingMessages();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (errorCode)
|
if (errorCode)
|
||||||
blog(LOG_ERROR, "[WebSocketServer::BroadcastEvent] Error sending event message: %s", errorCode.message().c_str());
|
blog(LOG_ERROR,
|
||||||
|
"[WebSocketServer::BroadcastEvent] Error sending event message: %s",
|
||||||
|
errorCode.message().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
if (IsDebugEnabled() && (EventSubscription::All & requiredIntent) != 0) // Don't log high volume events
|
if (IsDebugEnabled() &&
|
||||||
blog(LOG_INFO, "[WebSocketServer::BroadcastEvent] Outgoing event:\n%s", eventMessage.dump(2).c_str());
|
(EventSubscription::All & requiredIntent) !=
|
||||||
|
0) // Don't log high volume events
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"[WebSocketServer::BroadcastEvent] Outgoing event:\n%s",
|
||||||
|
eventMessage.dump(2).c_str());
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -20,16 +20,16 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "WebSocketSession.h"
|
#include "WebSocketSession.h"
|
||||||
#include "../../eventhandler/types/EventSubscription.h"
|
#include "../../eventhandler/types/EventSubscription.h"
|
||||||
|
|
||||||
WebSocketSession::WebSocketSession() :
|
WebSocketSession::WebSocketSession()
|
||||||
_remoteAddress(""),
|
: _remoteAddress(""),
|
||||||
_connectedAt(0),
|
_connectedAt(0),
|
||||||
_incomingMessages(0),
|
_incomingMessages(0),
|
||||||
_outgoingMessages(0),
|
_outgoingMessages(0),
|
||||||
_encoding(0),
|
_encoding(0),
|
||||||
_challenge(""),
|
_challenge(""),
|
||||||
_rpcVersion(OBS_WEBSOCKET_RPC_VERSION),
|
_rpcVersion(OBS_WEBSOCKET_RPC_VERSION),
|
||||||
_isIdentified(false),
|
_isIdentified(false),
|
||||||
_eventSubscriptions(EventSubscription::All)
|
_eventSubscriptions(EventSubscription::All)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,59 +29,58 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
class WebSocketSession;
|
class WebSocketSession;
|
||||||
typedef std::shared_ptr<WebSocketSession> SessionPtr;
|
typedef std::shared_ptr<WebSocketSession> SessionPtr;
|
||||||
|
|
||||||
class WebSocketSession
|
class WebSocketSession {
|
||||||
{
|
public:
|
||||||
public:
|
WebSocketSession();
|
||||||
WebSocketSession();
|
|
||||||
|
|
||||||
std::string RemoteAddress();
|
std::string RemoteAddress();
|
||||||
void SetRemoteAddress(std::string address);
|
void SetRemoteAddress(std::string address);
|
||||||
|
|
||||||
uint64_t ConnectedAt();
|
uint64_t ConnectedAt();
|
||||||
void SetConnectedAt(uint64_t at);
|
void SetConnectedAt(uint64_t at);
|
||||||
|
|
||||||
uint64_t IncomingMessages();
|
uint64_t IncomingMessages();
|
||||||
void IncrementIncomingMessages();
|
void IncrementIncomingMessages();
|
||||||
|
|
||||||
uint64_t OutgoingMessages();
|
uint64_t OutgoingMessages();
|
||||||
void IncrementOutgoingMessages();
|
void IncrementOutgoingMessages();
|
||||||
|
|
||||||
uint8_t Encoding();
|
uint8_t Encoding();
|
||||||
void SetEncoding(uint8_t encoding);
|
void SetEncoding(uint8_t encoding);
|
||||||
|
|
||||||
bool AuthenticationRequired();
|
bool AuthenticationRequired();
|
||||||
void SetAuthenticationRequired(bool required);
|
void SetAuthenticationRequired(bool required);
|
||||||
|
|
||||||
std::string Secret();
|
std::string Secret();
|
||||||
void SetSecret(std::string secret);
|
void SetSecret(std::string secret);
|
||||||
|
|
||||||
std::string Challenge();
|
std::string Challenge();
|
||||||
void SetChallenge(std::string challenge);
|
void SetChallenge(std::string challenge);
|
||||||
|
|
||||||
uint8_t RpcVersion();
|
uint8_t RpcVersion();
|
||||||
void SetRpcVersion(uint8_t version);
|
void SetRpcVersion(uint8_t version);
|
||||||
|
|
||||||
bool IsIdentified();
|
bool IsIdentified();
|
||||||
void SetIsIdentified(bool identified);
|
void SetIsIdentified(bool identified);
|
||||||
|
|
||||||
uint64_t EventSubscriptions();
|
uint64_t EventSubscriptions();
|
||||||
void SetEventSubscriptions(uint64_t subscriptions);
|
void SetEventSubscriptions(uint64_t subscriptions);
|
||||||
|
|
||||||
std::mutex OperationMutex;
|
std::mutex OperationMutex;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::mutex _remoteAddressMutex;
|
std::mutex _remoteAddressMutex;
|
||||||
std::string _remoteAddress;
|
std::string _remoteAddress;
|
||||||
std::atomic<uint64_t> _connectedAt;
|
std::atomic<uint64_t> _connectedAt;
|
||||||
std::atomic<uint64_t> _incomingMessages;
|
std::atomic<uint64_t> _incomingMessages;
|
||||||
std::atomic<uint64_t> _outgoingMessages;
|
std::atomic<uint64_t> _outgoingMessages;
|
||||||
std::atomic<uint8_t> _encoding;
|
std::atomic<uint8_t> _encoding;
|
||||||
std::atomic<bool> _authenticationRequired;
|
std::atomic<bool> _authenticationRequired;
|
||||||
std::mutex _secretMutex;
|
std::mutex _secretMutex;
|
||||||
std::string _secret;
|
std::string _secret;
|
||||||
std::mutex _challengeMutex;
|
std::mutex _challengeMutex;
|
||||||
std::string _challenge;
|
std::string _challenge;
|
||||||
std::atomic<uint8_t> _rpcVersion;
|
std::atomic<uint8_t> _rpcVersion;
|
||||||
std::atomic<bool> _isIdentified;
|
std::atomic<bool> _isIdentified;
|
||||||
std::atomic<uint64_t> _eventSubscriptions;
|
std::atomic<uint64_t> _eventSubscriptions;
|
||||||
};
|
};
|
||||||
|
@ -20,8 +20,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace WebSocketCloseCode {
|
namespace WebSocketCloseCode {
|
||||||
enum WebSocketCloseCode {
|
enum WebSocketCloseCode {
|
||||||
/**
|
/**
|
||||||
* For internal use only to tell the request handler not to perform any close action.
|
* For internal use only to tell the request handler not to perform any close action.
|
||||||
*
|
*
|
||||||
* @enumIdentifier DontClose
|
* @enumIdentifier DontClose
|
||||||
@ -31,8 +31,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
DontClose = 0,
|
DontClose = 0,
|
||||||
/**
|
/**
|
||||||
* Unknown reason, should never be used.
|
* Unknown reason, should never be used.
|
||||||
*
|
*
|
||||||
* @enumIdentifier UnknownReason
|
* @enumIdentifier UnknownReason
|
||||||
@ -42,8 +42,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
UnknownReason = 4000,
|
UnknownReason = 4000,
|
||||||
/**
|
/**
|
||||||
* The server was unable to decode the incoming websocket message.
|
* The server was unable to decode the incoming websocket message.
|
||||||
*
|
*
|
||||||
* @enumIdentifier MessageDecodeError
|
* @enumIdentifier MessageDecodeError
|
||||||
@ -53,8 +53,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
MessageDecodeError = 4002,
|
MessageDecodeError = 4002,
|
||||||
/**
|
/**
|
||||||
* A data field is required but missing from the payload.
|
* A data field is required but missing from the payload.
|
||||||
*
|
*
|
||||||
* @enumIdentifier MissingDataField
|
* @enumIdentifier MissingDataField
|
||||||
@ -64,8 +64,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
MissingDataField = 4003,
|
MissingDataField = 4003,
|
||||||
/**
|
/**
|
||||||
* A data field's value type is invalid.
|
* A data field's value type is invalid.
|
||||||
*
|
*
|
||||||
* @enumIdentifier InvalidDataFieldType
|
* @enumIdentifier InvalidDataFieldType
|
||||||
@ -75,8 +75,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InvalidDataFieldType = 4004,
|
InvalidDataFieldType = 4004,
|
||||||
/**
|
/**
|
||||||
* A data field's value is invalid.
|
* A data field's value is invalid.
|
||||||
*
|
*
|
||||||
* @enumIdentifier InvalidDataFieldValue
|
* @enumIdentifier InvalidDataFieldValue
|
||||||
@ -86,8 +86,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InvalidDataFieldValue = 4005,
|
InvalidDataFieldValue = 4005,
|
||||||
/**
|
/**
|
||||||
* The specified `op` was invalid or missing.
|
* The specified `op` was invalid or missing.
|
||||||
*
|
*
|
||||||
* @enumIdentifier UnknownOpCode
|
* @enumIdentifier UnknownOpCode
|
||||||
@ -97,8 +97,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
UnknownOpCode = 4006,
|
UnknownOpCode = 4006,
|
||||||
/**
|
/**
|
||||||
* The client sent a websocket message without first sending `Identify` message.
|
* The client sent a websocket message without first sending `Identify` message.
|
||||||
*
|
*
|
||||||
* @enumIdentifier NotIdentified
|
* @enumIdentifier NotIdentified
|
||||||
@ -108,8 +108,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
NotIdentified = 4007,
|
NotIdentified = 4007,
|
||||||
/**
|
/**
|
||||||
* The client sent an `Identify` message while already identified.
|
* The client sent an `Identify` message while already identified.
|
||||||
*
|
*
|
||||||
* Note: Once a client has identified, only `Reidentify` may be used to change session parameters.
|
* Note: Once a client has identified, only `Reidentify` may be used to change session parameters.
|
||||||
@ -121,8 +121,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
AlreadyIdentified = 4008,
|
AlreadyIdentified = 4008,
|
||||||
/**
|
/**
|
||||||
* The authentication attempt (via `Identify`) failed.
|
* The authentication attempt (via `Identify`) failed.
|
||||||
*
|
*
|
||||||
* @enumIdentifier AuthenticationFailed
|
* @enumIdentifier AuthenticationFailed
|
||||||
@ -132,8 +132,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
AuthenticationFailed = 4009,
|
AuthenticationFailed = 4009,
|
||||||
/**
|
/**
|
||||||
* The server detected the usage of an old version of the obs-websocket RPC protocol.
|
* The server detected the usage of an old version of the obs-websocket RPC protocol.
|
||||||
*
|
*
|
||||||
* @enumIdentifier UnsupportedRpcVersion
|
* @enumIdentifier UnsupportedRpcVersion
|
||||||
@ -143,8 +143,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
UnsupportedRpcVersion = 4010,
|
UnsupportedRpcVersion = 4010,
|
||||||
/**
|
/**
|
||||||
* The websocket session has been invalidated by the obs-websocket server.
|
* The websocket session has been invalidated by the obs-websocket server.
|
||||||
*
|
*
|
||||||
* Note: This is the code used by the `Kick` button in the UI Session List. If you receive this code, you must not automatically reconnect.
|
* Note: This is the code used by the `Kick` button in the UI Session List. If you receive this code, you must not automatically reconnect.
|
||||||
@ -156,8 +156,8 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
SessionInvalidated = 4011,
|
SessionInvalidated = 4011,
|
||||||
/**
|
/**
|
||||||
* A requested feature is not supported due to hardware/software limitations.
|
* A requested feature is not supported due to hardware/software limitations.
|
||||||
*
|
*
|
||||||
* @enumIdentifier UnsupportedFeature
|
* @enumIdentifier UnsupportedFeature
|
||||||
@ -167,6 +167,6 @@ namespace WebSocketCloseCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
UnsupportedFeature = 4012,
|
UnsupportedFeature = 4012,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace WebSocketOpCode {
|
namespace WebSocketOpCode {
|
||||||
enum WebSocketOpCode: uint8_t {
|
enum WebSocketOpCode : uint8_t {
|
||||||
/**
|
/**
|
||||||
* The initial message sent by obs-websocket to newly connected clients.
|
* The initial message sent by obs-websocket to newly connected clients.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Hello
|
* @enumIdentifier Hello
|
||||||
@ -31,8 +31,8 @@ namespace WebSocketOpCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Hello = 0,
|
Hello = 0,
|
||||||
/**
|
/**
|
||||||
* The message sent by a newly connected client to obs-websocket in response to a `Hello`.
|
* The message sent by a newly connected client to obs-websocket in response to a `Hello`.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Identify
|
* @enumIdentifier Identify
|
||||||
@ -42,8 +42,8 @@ namespace WebSocketOpCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Identify = 1,
|
Identify = 1,
|
||||||
/**
|
/**
|
||||||
* The response sent by obs-websocket to a client after it has successfully identified with obs-websocket.
|
* The response sent by obs-websocket to a client after it has successfully identified with obs-websocket.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Identified
|
* @enumIdentifier Identified
|
||||||
@ -53,8 +53,8 @@ namespace WebSocketOpCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Identified = 2,
|
Identified = 2,
|
||||||
/**
|
/**
|
||||||
* The message sent by an already-identified client to update identification parameters.
|
* The message sent by an already-identified client to update identification parameters.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Reidentify
|
* @enumIdentifier Reidentify
|
||||||
@ -64,8 +64,8 @@ namespace WebSocketOpCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Reidentify = 3,
|
Reidentify = 3,
|
||||||
/**
|
/**
|
||||||
* The message sent by obs-websocket containing an event payload.
|
* The message sent by obs-websocket containing an event payload.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Event
|
* @enumIdentifier Event
|
||||||
@ -75,8 +75,8 @@ namespace WebSocketOpCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Event = 5,
|
Event = 5,
|
||||||
/**
|
/**
|
||||||
* The message sent by a client to obs-websocket to perform a request.
|
* The message sent by a client to obs-websocket to perform a request.
|
||||||
*
|
*
|
||||||
* @enumIdentifier Request
|
* @enumIdentifier Request
|
||||||
@ -86,8 +86,8 @@ namespace WebSocketOpCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
Request = 6,
|
Request = 6,
|
||||||
/**
|
/**
|
||||||
* The message sent by obs-websocket in response to a particular request from a client.
|
* The message sent by obs-websocket in response to a particular request from a client.
|
||||||
*
|
*
|
||||||
* @enumIdentifier RequestResponse
|
* @enumIdentifier RequestResponse
|
||||||
@ -97,8 +97,8 @@ namespace WebSocketOpCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
RequestResponse = 7,
|
RequestResponse = 7,
|
||||||
/**
|
/**
|
||||||
* The message sent by a client to obs-websocket to perform a batch of requests.
|
* The message sent by a client to obs-websocket to perform a batch of requests.
|
||||||
*
|
*
|
||||||
* @enumIdentifier RequestBatch
|
* @enumIdentifier RequestBatch
|
||||||
@ -108,8 +108,8 @@ namespace WebSocketOpCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
RequestBatch = 8,
|
RequestBatch = 8,
|
||||||
/**
|
/**
|
||||||
* The message sent by obs-websocket in response to a particular batch of requests from a client.
|
* The message sent by obs-websocket in response to a particular batch of requests from a client.
|
||||||
*
|
*
|
||||||
* @enumIdentifier RequestBatchResponse
|
* @enumIdentifier RequestBatchResponse
|
||||||
@ -119,11 +119,11 @@ namespace WebSocketOpCode {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
RequestBatchResponse = 9,
|
RequestBatchResponse = 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool IsValid(uint8_t opCode)
|
inline bool IsValid(uint8_t opCode)
|
||||||
{
|
{
|
||||||
return opCode >= Hello && opCode <= RequestBatchResponse;
|
return opCode >= Hello && opCode <= RequestBatchResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user