Base: Add variable support to request batches + refactor

- Adds variables to execution types SERIAL_REALTIME and SERIAL_FRAME
- Pass by reference where copy is unnecessary
- Start WebSocket server after OBS finishes loading instead of on
plugin load
This commit is contained in:
tt2468 2021-10-01 17:34:09 -07:00
parent 16ea2c82e1
commit f0b207d021
9 changed files with 200 additions and 64 deletions

View File

@ -51,6 +51,12 @@ WebSocketServer::WebSocketServer() :
&WebSocketServer::BroadcastEvent, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4
)
);
eventHandler->SetObsLoadedCallback(
std::bind(
&WebSocketServer::onObsLoaded, this
)
);
}
WebSocketServer::~WebSocketServer()
@ -198,7 +204,20 @@ std::vector<WebSocketServer::WebSocketSessionState> WebSocketServer::GetWebSocke
return webSocketSessions;
}
// 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::onObsLoaded()
{
auto conf = GetConfig();
if (!conf) {
blog(LOG_ERROR, "[WebSocketServer::onObsLoaded] Unable to retreive config!");
return;
}
if (conf->ServerEnabled) {
if (conf->DebugEnabled)
blog(LOG_INFO, "[WebSocketServer::onObsLoaded] Server is enabled in configuration. Starting server...");
Start();
}
}
bool WebSocketServer::onValidate(websocketpp::connection_hdl hdl)
{

View File

@ -95,15 +95,16 @@ class WebSocketServer : QObject
void ServerRunner();
void onObsLoaded();
bool onValidate(websocketpp::connection_hdl hdl);
void onOpen(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 SetSessionParameters(SessionPtr session, WebSocketServer::ProcessResult &ret, json payloadData);
void ProcessMessage(SessionPtr session, ProcessResult &ret, const uint8_t opCode, json incomingMessage);
void SetSessionParameters(SessionPtr session, WebSocketServer::ProcessResult &ret, const json &payloadData);
void ProcessMessage(SessionPtr session, ProcessResult &ret, const uint8_t opCode, json &payloadData);
void ProcessRequestBatch(SessionPtr session, ObsWebSocketRequestBatchExecutionType executionType, std::vector<json> &requests, std::vector<json> &results);
void ProcessRequestBatch(SessionPtr session, ObsWebSocketRequestBatchExecutionType executionType, std::vector<json> &requests, std::vector<json> &results, json &variables);
std::thread _serverThread;
websocketpp::server<websocketpp::config::asio> _server;

View File

@ -28,7 +28,7 @@ bool IsSupportedRpcVersion(uint8_t requestedVersion)
return (requestedVersion == 1);
}
void WebSocketServer::SetSessionParameters(SessionPtr session, ProcessResult &ret, json payloadData)
void WebSocketServer::SetSessionParameters(SessionPtr session, ProcessResult &ret, const json &payloadData)
{
if (payloadData.contains("ignoreInvalidMessages")) {
if (!payloadData["ignoreInvalidMessages"].is_boolean()) {
@ -49,7 +49,7 @@ void WebSocketServer::SetSessionParameters(SessionPtr session, ProcessResult &re
}
}
void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::ProcessResult &ret, uint8_t opCode, json payloadData)
void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::ProcessResult &ret, uint8_t opCode, json &payloadData)
{
if (!payloadData.is_object()) {
if (payloadData.is_null()) {
@ -236,6 +236,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
}
return;
}
executionType = OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_PARALLEL;
} else {
if (!session->IgnoreInvalidMessages()) {
@ -246,9 +247,28 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
}
}
if (payloadData.contains("variables") && !payloadData.is_null()) {
if (!payloadData.is_object()) {
if (!session->IgnoreInvalidMessages()) {
ret.closeCode = WebSocketCloseCode::InvalidDataKeyType;
ret.closeReason = "Your `variables` is not an object.";
}
return;
}
if (executionType == OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_PARALLEL) {
if (!session->IgnoreInvalidMessages()) {
ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
ret.closeReason = "Variables are not supported in PARALLEL mode.";
}
return;
}
}
std::vector<json> requests = payloadData["requests"];
json variables = payloadData["variables"];
std::vector<json> results;
ProcessRequestBatch(session, executionType, requests, results);
ProcessRequestBatch(session, executionType, requests, results, variables);
ret.result["op"] = WebSocketOpCode::RequestBatchResponse;
ret.result["d"]["requestId"] = payloadData["requestId"];
@ -263,6 +283,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
}
}
// 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, std::string eventType, json eventData, uint8_t rpcVersion)
{
if (!_server.is_listening())

View File

@ -5,18 +5,33 @@
#include "obs-websocket.h"
#include "utils/Compat.h"
struct SerialFrameRequest
{
Request request;
const json inputVariables;
const json outputVariables;
SerialFrameRequest(const std::string &requestType, const json &requestData, const json &inputVariables, const json &outputVariables) :
request(requestType, requestData, OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_SERIAL_FRAME),
inputVariables(inputVariables),
outputVariables(outputVariables)
{}
};
struct SerialFrameBatch
{
RequestHandler *requestHandler;
RequestHandler &requestHandler;
json &variables;
size_t frameCount;
size_t sleepUntilFrame;
std::queue<Request> requests;
std::queue<SerialFrameRequest> requests;
std::vector<RequestResult> results;
std::mutex conditionMutex;
std::condition_variable condition;
SerialFrameBatch(RequestHandler *requestHandler) :
SerialFrameBatch(RequestHandler &requestHandler, json &variables) :
requestHandler(requestHandler),
variables(variables),
frameCount(0),
sleepUntilFrame(0)
{}
@ -24,19 +39,73 @@ struct SerialFrameBatch
struct ParallelBatchResults
{
RequestHandler *requestHandler;
RequestHandler &requestHandler;
size_t requestCount;
std::mutex resultsMutex;
std::vector<json> results;
std::condition_variable condition;
ParallelBatchResults(RequestHandler *requestHandler, size_t requestCount) :
ParallelBatchResults(RequestHandler &requestHandler, size_t requestCount) :
requestHandler(requestHandler),
requestCount(requestCount)
{}
};
json ConstructRequestResult(RequestResult requestResult, json requestJson)
bool PreProcessVariables(const json &variables, const json &inputVariables, json &requestData)
{
if (variables.empty() || inputVariables.empty() || !inputVariables.is_object() || !requestData.is_object())
return !requestData.empty();
for (auto it = inputVariables.begin(); it != inputVariables.end(); ++it) {
std::string key = it.key();
if (!variables.contains(key)) {
if (IsDebugMode())
blog(LOG_WARNING, "[WebSocketServer::ProcessRequestBatch] inputVariables requested variable `%s`, but it does not exist. Skipping!", key.c_str());
continue;
}
if (!it.value().is_string()) {
if (IsDebugMode())
blog(LOG_WARNING, "[WebSocketServer::ProcessRequestBatch] Value of item `%s` in inputVariables is not a string. Skipping!", key.c_str());
continue;
}
std::string value = it.value();
requestData[value] = variables[key];
}
return !requestData.empty();
}
void PostProcessVariables(json &variables, const json &outputVariables, const json &responseData)
{
if (outputVariables.empty() || !outputVariables.is_object() || responseData.empty())
return;
for (auto it = outputVariables.begin(); it != outputVariables.end(); ++it) {
std::string key = it.key();
if (!responseData.contains(key)) {
if (IsDebugMode())
blog(LOG_WARNING, "[WebSocketServer::ProcessRequestBatch] outputVariables requested responseData item `%s`, but it does not exist. Skipping!", key.c_str());
continue;
}
if (!it.value().is_string()) {
if (IsDebugMode())
blog(LOG_WARNING, "[WebSocketServer::ProcessRequestBatch] Value of item `%s` in outputVariables is not a string. Skipping!", key.c_str());
continue;
}
std::string value = it.value();
variables[key] = responseData[value];
}
}
json ConstructRequestResult(RequestResult requestResult, const json &requestJson)
{
json ret;
@ -82,9 +151,13 @@ void ObsTickCallback(void *param, float)
// Begin recursing any unprocessed requests
while (!serialFrameBatch->requests.empty()) {
// Fetch first in queue
Request request = serialFrameBatch->requests.front();
SerialFrameRequest frameRequest = serialFrameBatch->requests.front();
// Pre-process batch variables
frameRequest.request.HasRequestData = PreProcessVariables(serialFrameBatch->variables, frameRequest.inputVariables, frameRequest.request.RequestData);
// Process request and get result
RequestResult requestResult = serialFrameBatch->requestHandler->ProcessRequest(request);
RequestResult requestResult = serialFrameBatch->requestHandler.ProcessRequest(frameRequest.request);
// Post-process batch variables
PostProcessVariables(serialFrameBatch->variables, frameRequest.outputVariables, requestResult.ResponseData);
// Add to results vector
serialFrameBatch->results.push_back(requestResult);
// Remove from front of queue
@ -105,27 +178,31 @@ void ObsTickCallback(void *param, float)
profile_end("obs-websocket-request-batch-frame-tick");
}
void WebSocketServer::ProcessRequestBatch(SessionPtr session, ObsWebSocketRequestBatchExecutionType executionType, std::vector<json> &requests, std::vector<json> &results)
void WebSocketServer::ProcessRequestBatch(SessionPtr session, ObsWebSocketRequestBatchExecutionType executionType, std::vector<json> &requests, std::vector<json> &results, json &variables)
{
RequestHandler requestHandler(session);
if (executionType == OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_SERIAL_REALTIME) {
// Recurse all requests in batch serially, processing the request then moving to the next one
for (auto requestJson : requests) {
Request request(requestJson["requestType"], requestJson["requestData"], executionType);
Request request(requestJson["requestType"], requestJson["requestData"], OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_SERIAL_REALTIME);
request.HasRequestData = PreProcessVariables(variables, requestJson["inputVariables"], request.RequestData);
RequestResult requestResult = requestHandler.ProcessRequest(request);
PostProcessVariables(variables, requestJson["outputVariables"], requestResult.ResponseData);
json result = ConstructRequestResult(requestResult, requestJson);
results.push_back(result);
}
} else if (executionType == OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_SERIAL_FRAME) {
SerialFrameBatch serialFrameBatch(&requestHandler);
SerialFrameBatch serialFrameBatch(requestHandler, variables);
// Create Request objects in the worker thread (avoid unnecessary processing in graphics thread)
for (auto requestJson : requests) {
Request request(requestJson["requestType"], requestJson["requestData"], executionType);
serialFrameBatch.requests.push(request);
SerialFrameRequest frameRequest(requestJson["requestType"], requestJson["requestData"], requestJson["inputVariables"], requestJson["outputVariables"]);
serialFrameBatch.requests.push(frameRequest);
}
// Create a callback entry for the graphics thread to execute on each video frame
@ -145,14 +222,14 @@ void WebSocketServer::ProcessRequestBatch(SessionPtr session, ObsWebSocketReques
i++;
}
} else if (executionType == OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_PARALLEL) {
ParallelBatchResults parallelResults(&requestHandler, requests.size());
ParallelBatchResults parallelResults(requestHandler, requests.size());
// Submit each request as a task to the thread pool to be processed ASAP
for (auto requestJson : requests) {
_threadPool.start(Utils::Compat::CreateFunctionRunnable([&parallelResults, &executionType, requestJson]() {
Request request(requestJson["requestType"], requestJson["requestData"], executionType);
Request request(requestJson["requestType"], requestJson["requestData"], OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_PARALLEL);
RequestResult requestResult = parallelResults.requestHandler->ProcessRequest(request);
RequestResult requestResult = parallelResults.requestHandler.ProcessRequest(request);
json result = ConstructRequestResult(requestResult, requestJson);

View File

@ -48,6 +48,11 @@ void EventHandler::SetBroadcastCallback(EventHandler::BroadcastCallback cb)
_broadcastCallback = cb;
}
void EventHandler::SetObsLoadedCallback(EventHandler::ObsLoadedCallback cb)
{
_obsLoadedCallback = cb;
}
// Function to increment refcounts for high volume event subscriptions
void EventHandler::ProcessSubscription(uint64_t eventSubscriptions)
{
@ -189,6 +194,9 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
}, private_data);
blog(LOG_INFO, "[EventHandler::OnFrontendEvent] Finished.");
if (eventHandler->_obsLoadedCallback)
eventHandler->_obsLoadedCallback();
} else {
return;
}

View File

@ -24,12 +24,15 @@ class EventHandler
typedef std::function<void(uint64_t, std::string, json, uint8_t)> BroadcastCallback;
void SetBroadcastCallback(BroadcastCallback cb);
typedef std::function<void()> ObsLoadedCallback;
void SetObsLoadedCallback(ObsLoadedCallback cb);
void ProcessSubscription(uint64_t eventSubscriptions);
void ProcessUnsubscription(uint64_t eventSubscriptions);
private:
BroadcastCallback _broadcastCallback;
ObsLoadedCallback _obsLoadedCallback;
std::atomic<bool> _obsLoaded;

View File

@ -54,9 +54,6 @@ bool obs_module_load(void)
_cpuUsageInfo = os_cpu_usage_info_start();
if (_config->ServerEnabled)
_webSocketServer->Start();
// Loading finished
blog(LOG_INFO, "[obs_module_load] Module loaded.");

View File

@ -2,7 +2,7 @@
#include "../../obs-websocket.h"
#include "../../plugin-macros.generated.h"
json GetDefaultJsonObject(json requestData)
json GetDefaultJsonObject(const json &requestData)
{
// Always provide an object to prevent exceptions while running checks in requests
if (!requestData.is_object())
@ -11,20 +11,28 @@ json GetDefaultJsonObject(json requestData)
return requestData;
}
Request::Request(std::string requestType, json requestData, ObsWebSocketRequestBatchExecutionType requestBatchExecutionType) :
HasRequestData(requestData.is_object()),
Request::Request(const std::string &requestType, const json &requestData) :
RequestType(requestType),
HasRequestData(requestData.is_object()),
RequestData(GetDefaultJsonObject(requestData)),
RequestBatchExecutionType(OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_NONE)
{
}
Request::Request(const std::string &requestType, const json &requestData, const ObsWebSocketRequestBatchExecutionType requestBatchExecutionType) :
RequestType(requestType),
HasRequestData(requestData.is_object()),
RequestData(GetDefaultJsonObject(requestData)),
RequestBatchExecutionType(requestBatchExecutionType)
{
}
const bool Request::Contains(const std::string keyName) const
const bool Request::Contains(const std::string &keyName) const
{
return (RequestData.contains(keyName) && !RequestData[keyName].is_null());
}
const bool Request::ValidateBasic(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
const bool Request::ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
{
if (!HasRequestData) {
statusCode = RequestStatus::MissingRequestData;
@ -41,7 +49,7 @@ const bool Request::ValidateBasic(const std::string keyName, RequestStatus::Requ
return true;
}
const bool Request::ValidateOptionalNumber(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue, const double maxValue) const
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()) {
statusCode = RequestStatus::InvalidRequestParameterType;
@ -64,7 +72,7 @@ const bool Request::ValidateOptionalNumber(const std::string keyName, RequestSta
return true;
}
const bool Request::ValidateNumber(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue, const double maxValue) const
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))
return false;
@ -75,7 +83,7 @@ const bool Request::ValidateNumber(const std::string keyName, RequestStatus::Req
return true;
}
const bool Request::ValidateOptionalString(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
const bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
{
if (!RequestData[keyName].is_string()) {
statusCode = RequestStatus::InvalidRequestParameterType;
@ -92,7 +100,7 @@ const bool Request::ValidateOptionalString(const std::string keyName, RequestSta
return true;
}
const bool Request::ValidateString(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
const bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
{
if (!ValidateBasic(keyName, statusCode, comment))
return false;
@ -103,7 +111,7 @@ const bool Request::ValidateString(const std::string keyName, RequestStatus::Req
return true;
}
const bool Request::ValidateOptionalBoolean(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
const bool Request::ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
{
if (!RequestData[keyName].is_boolean()) {
statusCode = RequestStatus::InvalidRequestParameterType;
@ -114,7 +122,7 @@ const bool Request::ValidateOptionalBoolean(const std::string keyName, RequestSt
return true;
}
const bool Request::ValidateBoolean(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
const bool Request::ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
{
if (!ValidateBasic(keyName, statusCode, comment))
return false;
@ -125,7 +133,7 @@ const bool Request::ValidateBoolean(const std::string keyName, RequestStatus::Re
return true;
}
const bool Request::ValidateOptionalObject(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
const bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
{
if (!RequestData[keyName].is_object()) {
statusCode = RequestStatus::InvalidRequestParameterType;
@ -142,7 +150,7 @@ const bool Request::ValidateOptionalObject(const std::string keyName, RequestSta
return true;
}
const bool Request::ValidateObject(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
const bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
{
if (!ValidateBasic(keyName, statusCode, comment))
return false;
@ -153,7 +161,7 @@ const bool Request::ValidateObject(const std::string keyName, RequestStatus::Req
return true;
}
const bool Request::ValidateOptionalArray(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
const bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
{
if (!RequestData[keyName].is_array()) {
statusCode = RequestStatus::InvalidRequestParameterType;
@ -170,7 +178,7 @@ const bool Request::ValidateOptionalArray(const std::string keyName, RequestStat
return true;
}
const bool Request::ValidateArray(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
const bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
{
if (!ValidateBasic(keyName, statusCode, comment))
return false;
@ -181,7 +189,7 @@ const bool Request::ValidateArray(const std::string keyName, RequestStatus::Requ
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))
return nullptr;
@ -198,7 +206,7 @@ obs_source_t *Request::ValidateSource(const std::string keyName, RequestStatus::
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);
if (!ret)
@ -227,7 +235,7 @@ obs_source_t *Request::ValidateScene(const std::string keyName, RequestStatus::R
return ret;
}
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);
if (!ret)
@ -243,7 +251,7 @@ obs_source_t *Request::ValidateInput(const std::string keyName, RequestStatus::R
return ret;
}
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
{
OBSSourceAutoRelease sceneSource = ValidateScene(sceneKeyName, statusCode, comment, filter);
if (!sceneSource)

View File

@ -4,6 +4,7 @@
#include "../../utils/Json.h"
enum ObsWebSocketRequestBatchExecutionType {
OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_NONE,
OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_SERIAL_REALTIME,
OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_SERIAL_FRAME,
OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_PARALLEL
@ -17,31 +18,32 @@ enum ObsWebSocketSceneFilter {
struct Request
{
Request(const std::string requestType, const json requestData = nullptr, const ObsWebSocketRequestBatchExecutionType requestBatchExecutionType = OBS_WEBSOCKET_REQUEST_BATCH_EXECUTION_TYPE_SERIAL_REALTIME);
Request(const std::string &requestType, const json &requestData = nullptr);
Request(const std::string &requestType, const json &requestData, const ObsWebSocketRequestBatchExecutionType requestBatchExecutionType);
// Contains the key and is not null
const bool Contains(const std::string keyName) const;
const bool Contains(const std::string &keyName) const;
const bool ValidateBasic(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
const bool ValidateOptionalNumber(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const;
const bool ValidateNumber(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const;
const bool ValidateOptionalString(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
const bool ValidateString(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
const bool ValidateOptionalBoolean(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
const bool ValidateBoolean(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
const bool ValidateOptionalObject(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
const bool ValidateObject(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
const bool ValidateOptionalArray(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
const bool ValidateArray(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
const bool ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
const bool ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const;
const bool ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const;
const bool ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
const bool ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
const bool ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
const bool ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
const bool ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
const bool ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
const bool ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
const bool ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
// All return values have incremented refcounts
obs_source_t *ValidateSource(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
obs_source_t *ValidateScene(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;
obs_sceneitem_t *ValidateSceneItem(const std::string sceneKeyName, const std::string sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
obs_source_t *ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
obs_source_t *ValidateScene(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;
obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
const bool HasRequestData;
const std::string RequestType;
const json RequestData;
bool HasRequestData;
json RequestData;
const ObsWebSocketRequestBatchExecutionType RequestBatchExecutionType;
};