mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
requesthandler: Add projector creation requests
I didn't think I'd be able to make remotely usable requests using OBS' existing projector API, but I'm actually pretty happy with how it turned out. Closes #929 Co-authored-by: Brendan Allan <brendonovich@outlook.com>
This commit is contained in:
@ -181,6 +181,8 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
|
||||
{"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog},
|
||||
{"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog},
|
||||
{"GetMonitorList", &RequestHandler::GetMonitorList},
|
||||
{"OpenVideoMixProjector", &RequestHandler::OpenVideoMixProjector},
|
||||
{"OpenSourceProjector", &RequestHandler::OpenSourceProjector},
|
||||
};
|
||||
|
||||
RequestHandler::RequestHandler(SessionPtr session) : _session(session) {}
|
||||
|
@ -200,6 +200,8 @@ private:
|
||||
RequestResult OpenInputFiltersDialog(const Request &);
|
||||
RequestResult OpenInputInteractDialog(const Request &);
|
||||
RequestResult GetMonitorList(const Request &);
|
||||
RequestResult OpenVideoMixProjector(const Request &);
|
||||
RequestResult OpenSourceProjector(const Request &);
|
||||
|
||||
SessionPtr _session;
|
||||
static const std::unordered_map<std::string, RequestMethodHandler> _handlerMap;
|
||||
|
@ -370,7 +370,7 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
|
||||
float finalWidth = scaleX * sourceWidth;
|
||||
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.");
|
||||
"The field `scaleX` is too small or large for the current source resolution.");
|
||||
sceneItemTransform.scale.x = scaleX;
|
||||
transformChanged = true;
|
||||
}
|
||||
@ -381,7 +381,7 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
|
||||
float finalHeight = scaleY * sourceHeight;
|
||||
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.");
|
||||
"The field `scaleY` is too small or large for the current source resolution.");
|
||||
sceneItemTransform.scale.y = scaleY;
|
||||
transformChanged = true;
|
||||
}
|
||||
@ -399,7 +399,7 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
|
||||
enum obs_bounds_type boundsType = r.RequestData["boundsType"];
|
||||
if (boundsType == OBS_BOUNDS_NONE && r.RequestData["boundsType"] != "OBS_BOUNDS_NONE")
|
||||
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
||||
"The field boundsType has an invalid value.");
|
||||
"The field `boundsType` has an invalid value.");
|
||||
sceneItemTransform.bounds_type = boundsType;
|
||||
transformChanged = true;
|
||||
}
|
||||
|
@ -182,6 +182,7 @@ RequestResult RequestHandler::GetMonitorList(const Request &)
|
||||
nameAndIndex << screen->name().toStdString();
|
||||
nameAndIndex << '(' << screenIndex << ')';
|
||||
screenData["monitorName"] = nameAndIndex.str();
|
||||
screenData["monitorIndex"] = screenIndex;
|
||||
const QRect screenGeometry = screen->geometry();
|
||||
screenData["monitorWidth"] = screenGeometry.width();
|
||||
screenData["monitorHeight"] = screenGeometry.height();
|
||||
@ -192,3 +193,111 @@ RequestResult RequestHandler::GetMonitorList(const Request &)
|
||||
responseData["monitors"] = monitorsData;
|
||||
return RequestResult::Success(responseData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a projector for a specific output video mix.
|
||||
*
|
||||
* Mix types:
|
||||
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_PREVIEW`
|
||||
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_PROGRAM`
|
||||
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW`
|
||||
*
|
||||
* Note: This request serves to provide feature parity with 4.x. It is very likely to be changed/deprecated in a future release.
|
||||
*
|
||||
* @requestField videoMixType | String | Type of mix to open
|
||||
* @requestField ?monitorIndex | Number | Monitor index, use `GetMonitorList` to obtain index | None | -1: Opens projector in windowed mode
|
||||
* @requestField ?projectorGeometry | String | Size/Position data for a windowed projector, in Qt Base64 encoded format. Mutually exclusive with `monitorIndex` | N/A
|
||||
*
|
||||
* @requestType OpenVideoMixProjector
|
||||
* @complexity 3
|
||||
* @rpcVersion -1
|
||||
* @initialVersion 5.0.0
|
||||
* @category ui
|
||||
* @api requests
|
||||
*/
|
||||
RequestResult RequestHandler::OpenVideoMixProjector(const Request &request)
|
||||
{
|
||||
RequestStatus::RequestStatus statusCode;
|
||||
std::string comment;
|
||||
if (!request.ValidateString("mixType", statusCode, comment))
|
||||
return RequestResult::Error(statusCode, comment);
|
||||
|
||||
std::string videoMixType = request.RequestData["videoMixType"];
|
||||
const char *projectorType;
|
||||
if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PREVIEW")
|
||||
projectorType = "Preview";
|
||||
else if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PROGRAM")
|
||||
projectorType = "Program";
|
||||
else if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW")
|
||||
projectorType = "Multiview";
|
||||
else
|
||||
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
||||
"The field `videoMixType` has an invalid enum value.");
|
||||
|
||||
int monitorIndex = -1;
|
||||
if (request.Contains("monitorIndex")) {
|
||||
if (!request.ValidateOptionalNumber("monitorIndex", statusCode, comment, -1, 9))
|
||||
return RequestResult::Error(statusCode, comment);
|
||||
monitorIndex = request.RequestData["monitorIndex"];
|
||||
}
|
||||
|
||||
std::string projectorGeometry;
|
||||
if (request.Contains("projectorGeometry")) {
|
||||
if (!request.ValidateOptionalString("projectorGeometry", statusCode, comment))
|
||||
return RequestResult::Error(statusCode, comment);
|
||||
if (monitorIndex != -1)
|
||||
return RequestResult::Error(RequestStatus::TooManyRequestFields,
|
||||
"`monitorIndex` and `projectorGeometry` are mutually exclusive.");
|
||||
projectorGeometry = request.RequestData["projectorGeometry"];
|
||||
}
|
||||
|
||||
obs_frontend_open_projector(projectorType, monitorIndex, projectorGeometry.c_str(), nullptr);
|
||||
|
||||
return RequestResult::Success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a projector for a source.
|
||||
*
|
||||
* Note: This request serves to provide feature parity with 4.x. It is very likely to be changed/deprecated in a future release.
|
||||
*
|
||||
* @requestField sourceName | String | Name of the source to open a projector for
|
||||
* @requestField ?monitorIndex | Number | Monitor index, use `GetMonitorList` to obtain index | None | -1: Opens projector in windowed mode
|
||||
* @requestField ?projectorGeometry | String | Size/Position data for a windowed projector, in Qt Base64 encoded format. Mutually exclusive with `monitorIndex` | N/A
|
||||
*
|
||||
* @requestType OpenSourceProjector
|
||||
* @complexity 3
|
||||
* @rpcVersion -1
|
||||
* @initialVersion 5.0.0
|
||||
* @category ui
|
||||
* @api requests
|
||||
*/
|
||||
RequestResult RequestHandler::OpenSourceProjector(const Request &request)
|
||||
{
|
||||
RequestStatus::RequestStatus statusCode;
|
||||
std::string comment;
|
||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
||||
if (!source)
|
||||
return RequestResult::Error(statusCode, comment);
|
||||
|
||||
int monitorIndex = -1;
|
||||
if (request.Contains("monitorIndex")) {
|
||||
if (!request.ValidateOptionalNumber("monitorIndex", statusCode, comment, -1, 9))
|
||||
return RequestResult::Error(statusCode, comment);
|
||||
monitorIndex = request.RequestData["monitorIndex"];
|
||||
}
|
||||
|
||||
std::string projectorGeometry;
|
||||
if (request.Contains("projectorGeometry")) {
|
||||
if (!request.ValidateOptionalString("projectorGeometry", statusCode, comment))
|
||||
return RequestResult::Error(statusCode, comment);
|
||||
if (monitorIndex != -1)
|
||||
return RequestResult::Error(RequestStatus::TooManyRequestFields,
|
||||
"`monitorIndex` and `projectorGeometry` are mutually exclusive.");
|
||||
projectorGeometry = request.RequestData["projectorGeometry"];
|
||||
}
|
||||
|
||||
obs_frontend_open_projector("Source", monitorIndex, projectorGeometry.c_str(), obs_source_get_name(source));
|
||||
|
||||
return RequestResult::Success();
|
||||
}
|
||||
|
@ -60,14 +60,13 @@ std::vector<obs_hotkey_t *> Utils::Obs::ArrayHelper::GetHotkeyList()
|
||||
{
|
||||
std::vector<obs_hotkey_t *> ret;
|
||||
|
||||
auto cb =
|
||||
[](void *data, obs_hotkey_id, obs_hotkey_t *hotkey) {
|
||||
auto ret = static_cast<std::vector<obs_hotkey_t *> *>(data);
|
||||
auto cb = [](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;
|
||||
};
|
||||
|
||||
obs_enum_hotkeys(cb, &ret);
|
||||
|
||||
@ -135,38 +134,37 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene,
|
||||
std::pair<std::vector<json>, bool> enumData;
|
||||
enumData.second = basic;
|
||||
|
||||
auto cb =
|
||||
[](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
|
||||
auto enumData = static_cast<std::pair<std::vector<json>, bool> *>(param);
|
||||
auto cb = [](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
|
||||
auto enumData = static_cast<std::pair<std::vector<json>, bool> *>(param);
|
||||
|
||||
// TODO: Make ObjectHelper util for scene items
|
||||
// TODO: Make ObjectHelper util for scene items
|
||||
|
||||
json item;
|
||||
item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
||||
item["sceneItemIndex"] =
|
||||
enumData->first.size(); // Should be slightly faster than calling obs_sceneitem_get_order_position()
|
||||
if (!enumData->second) {
|
||||
item["sceneItemEnabled"] = obs_sceneitem_visible(sceneItem);
|
||||
item["sceneItemLocked"] = obs_sceneitem_locked(sceneItem);
|
||||
item["sceneItemTransform"] = ObjectHelper::GetSceneItemTransform(sceneItem);
|
||||
item["sceneItemBlendMode"] = obs_sceneitem_get_blending_mode(sceneItem);
|
||||
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
||||
item["sourceName"] = obs_source_get_name(itemSource);
|
||||
item["sourceType"] = obs_source_get_type(itemSource);
|
||||
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
|
||||
item["inputKind"] = obs_source_get_id(itemSource);
|
||||
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;
|
||||
}
|
||||
json item;
|
||||
item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
||||
item["sceneItemIndex"] =
|
||||
enumData->first.size(); // Should be slightly faster than calling obs_sceneitem_get_order_position()
|
||||
if (!enumData->second) {
|
||||
item["sceneItemEnabled"] = obs_sceneitem_visible(sceneItem);
|
||||
item["sceneItemLocked"] = obs_sceneitem_locked(sceneItem);
|
||||
item["sceneItemTransform"] = ObjectHelper::GetSceneItemTransform(sceneItem);
|
||||
item["sceneItemBlendMode"] = obs_sceneitem_get_blending_mode(sceneItem);
|
||||
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
||||
item["sourceName"] = obs_source_get_name(itemSource);
|
||||
item["sourceType"] = obs_source_get_type(itemSource);
|
||||
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
|
||||
item["inputKind"] = obs_source_get_id(itemSource);
|
||||
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;
|
||||
};
|
||||
|
||||
obs_scene_enum_items(scene, cb, &enumData);
|
||||
|
||||
|
@ -70,27 +70,26 @@ obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene
|
||||
enumData.name = name;
|
||||
enumData.offset = offset;
|
||||
|
||||
auto cb =
|
||||
[](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
|
||||
auto enumData = static_cast<SceneItemSearchData *>(param);
|
||||
auto cb = [](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
|
||||
auto enumData = static_cast<SceneItemSearchData *>(param);
|
||||
|
||||
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
||||
std::string sourceName = obs_source_get_name(itemSource);
|
||||
if (sourceName == enumData->name) {
|
||||
if (enumData->offset > 0) {
|
||||
enumData->offset--;
|
||||
} else {
|
||||
if (enumData->ret) // Release existing selection in the case of last match selection
|
||||
obs_sceneitem_release(enumData->ret);
|
||||
obs_sceneitem_addref(sceneItem);
|
||||
enumData->ret = sceneItem;
|
||||
if (enumData->offset == 0) // Only break if in normal selection mode (not offset == -1)
|
||||
return false;
|
||||
}
|
||||
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
||||
std::string sourceName = obs_source_get_name(itemSource);
|
||||
if (sourceName == enumData->name) {
|
||||
if (enumData->offset > 0) {
|
||||
enumData->offset--;
|
||||
} else {
|
||||
if (enumData->ret) // Release existing selection in the case of last match selection
|
||||
obs_sceneitem_release(enumData->ret);
|
||||
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;
|
||||
};
|
||||
|
||||
obs_scene_enum_items(scene, cb, &enumData);
|
||||
|
||||
|
@ -302,7 +302,8 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
||||
std::vector<RequestBatchRequest> requestsVector;
|
||||
for (auto &requestJson : requests) {
|
||||
if (!requestJson["requestType"].is_string())
|
||||
requestJson["requestType"] = ""; // Workaround for what would otherwise be extensive additional logic for a rare edge case
|
||||
requestJson["requestType"] =
|
||||
""; // Workaround for what would otherwise be extensive additional logic for a rare edge case
|
||||
std::string requestType = requestJson["requestType"];
|
||||
json requestData = requestJson["requestData"];
|
||||
json inputVariables = requestJson["inputVariables"];
|
||||
|
Reference in New Issue
Block a user