diff --git a/src/Utils.cpp b/src/Utils.cpp index 11c55e9c..cd197519 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -875,3 +875,10 @@ QString Utils::nsToTimestamp(uint64_t ns) return QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart); } + +void Utils::AddSourceHelper(void *_data, obs_scene_t *scene) +{ + auto *data = reinterpret_cast<AddSourceData*>(_data); + data->sceneItem = obs_scene_add(scene, data->source); + obs_sceneitem_set_visible(data->sceneItem, data->setVisible); +} diff --git a/src/Utils.h b/src/Utils.h index 69b86fc5..0851a8ea 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -86,4 +86,10 @@ namespace Utils { bool SetFilenameFormatting(const char* filenameFormatting); QString nsToTimestamp(uint64_t ns); + struct AddSourceData { + obs_source_t *source; + obs_sceneitem_t *sceneItem; + bool setVisible; + }; + void AddSourceHelper(void *_data, obs_scene_t *scene); }; diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 95741acd..42b39f7e 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -101,6 +101,7 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap{ { "ReleaseTBar", &WSRequestHandler::ReleaseTBar }, { "SetTBarPosition", &WSRequestHandler::SetTBarPosition }, + { "CreateSource", &WSRequestHandler::CreateSource }, { "SetVolume", &WSRequestHandler::SetVolume }, { "GetVolume", &WSRequestHandler::GetVolume }, { "ToggleMute", &WSRequestHandler::ToggleMute }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index e9c5b9e1..4986cd1c 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -118,6 +118,7 @@ class WSRequestHandler { RpcResponse ReleaseTBar(const RpcRequest&); RpcResponse SetTBarPosition(const RpcRequest&); + RpcResponse CreateSource(const RpcRequest&); RpcResponse SetVolume(const RpcRequest&); RpcResponse GetVolume(const RpcRequest&); RpcResponse ToggleMute(const RpcRequest&); diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 2d09780d..b5334d89 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -2,18 +2,6 @@ #include "WSRequestHandler.h" -struct AddSourceData { - obs_source_t *source; - obs_sceneitem_t *sceneItem; - bool setVisible; -}; - -void AddSourceHelper(void *_data, obs_scene_t *scene) { - auto *data = reinterpret_cast<AddSourceData*>(_data); - data->sceneItem = obs_scene_add(scene, data->source); - obs_sceneitem_set_visible(data->sceneItem, data->setVisible); -} - /** * Get a list of all scene items in a scene. * @@ -663,7 +651,7 @@ RpcResponse WSRequestHandler::AddSceneItem(const RpcRequest& request) { return request.failed("you cannot add a scene as a sceneitem to itself"); } - AddSourceData data; + Utils::AddSourceData data; data.source = source; data.setVisible = true; if (request.hasField("setVisible")) { @@ -671,7 +659,7 @@ RpcResponse WSRequestHandler::AddSceneItem(const RpcRequest& request) { } obs_enter_graphics(); - obs_scene_atomic_update(scene, AddSourceHelper, &data); + obs_scene_atomic_update(scene, Utils::AddSourceHelper, &data); obs_leave_graphics(); OBSDataAutoRelease responseData = obs_data_create(); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index ba98ecdb..9aa5c260 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -18,6 +18,75 @@ bool isTextFreeType2Source(const QString& sourceKind) return (sourceKind == "text_ft2_source" || sourceKind == "text_ft2_source_v2"); } +/** + * Create a source and add it as a sceneitem to a scene. + * + * @param {String} `sourceName` Source name. + * @param {String} `sourceKind` Source kind, Eg. `vlc_source`. + * @param {String} `sceneName` Scene to add the new source to. + * @param {Object (optional)} `sourceSettings` Source settings data. + * @param {boolean (optional)} `setVisible` Set the created SceneItem as visible or not. Defaults to true + * + * @return {int} `itemId` ID of the SceneItem in the scene. + * + * @api requests + * @name CreateSource + * @category sources + * @since unreleased + */ +RpcResponse WSRequestHandler::CreateSource(const RpcRequest& request) +{ + if (!request.hasField("sourceName") || !request.hasField("sourceKind") || !request.hasField("sceneName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + QString sourceKind = obs_data_get_string(request.parameters(), "sourceKind"); + if (sourceName.isEmpty() || sourceKind.isEmpty()) { + return request.failed("empty sourceKind or sourceName parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (source) { + return request.failed("a source with that name already exists"); + } + + const char* sceneName = obs_data_get_string(request.parameters(), "sceneName"); + OBSSourceAutoRelease sceneSource = obs_get_source_by_name(sceneName); + OBSScene scene = obs_scene_from_source(sceneSource); + if (!scene) { + return request.failed("requested scene is invalid or doesnt exist"); + } + + OBSDataAutoRelease sourceSettings = nullptr; + if (request.hasField("sourceSettings")) { + sourceSettings = obs_data_get_obj(request.parameters(), "sourceSettings"); + } + + OBSSourceAutoRelease newSource = obs_source_create(sourceKind.toUtf8(), sourceName.toUtf8(), sourceSettings, nullptr); + + if (!newSource) { + return request.failed("failed to create the source"); + } + obs_source_set_enabled(newSource, true); + + Utils::AddSourceData data; + data.source = newSource; + data.setVisible = true; + if (request.hasField("setVisible")) { + data.setVisible = obs_data_get_bool(request.parameters(), "setVisible"); + } + + obs_enter_graphics(); + obs_scene_atomic_update(scene, Utils::AddSourceHelper, &data); + obs_leave_graphics(); + + OBSDataAutoRelease responseData = obs_data_create(); + obs_data_set_int(responseData, "itemId", obs_sceneitem_get_id(data.sceneItem)); + + return request.success(responseData); +} + /** * List all sources available in the running OBS instance *