diff --git a/.gitignore b/.gitignore index 11f19da9..b234aa97 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ /docs/node_modules/ /src/plugin-macros.generated.h /installer/installer-windows.generated.iss +/cmake-build-debug/ diff --git a/src/eventhandler/EventHandler.cpp b/src/eventhandler/EventHandler.cpp index 1ac8f9f3..211fd750 100644 --- a/src/eventhandler/EventHandler.cpp +++ b/src/eventhandler/EventHandler.cpp @@ -135,9 +135,12 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu signal_handler_connect(sh, "mute", HandleInputMuteStateChanged, this); signal_handler_connect(sh, "volume", HandleInputVolumeChanged, this); signal_handler_connect(sh, "audio_balance", HandleInputAudioBalanceChanged, this); - signal_handler_connect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this); - signal_handler_connect(sh, "audio_mixers", HandleInputAudioTracksChanged, this); - signal_handler_connect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this); + signal_handler_connect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this); + signal_handler_connect(sh, "audio_mixers", HandleInputAudioTracksChanged, this); + signal_handler_connect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this); + signal_handler_connect(sh, "filter_add", HandleSourceFilterCreated, this); + signal_handler_connect(sh, "filter_remove", HandleSourceFilterRemoved, this); + signal_handler_connect(sh, "reorder_filters", HandleSourceFilterListReindexed, this); if (sourceType == OBS_SOURCE_TYPE_INPUT) { signal_handler_connect(sh, "media_started", HandleMediaInputPlaybackStarted, this); @@ -188,6 +191,9 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source) signal_handler_disconnect(sh, "media_stopped", SourceMediaStopMultiHandler, this); signal_handler_disconnect(sh, "media_next", SourceMediaNextMultiHandler, this); signal_handler_disconnect(sh, "media_previous", SourceMediaPreviousMultiHandler, this); + signal_handler_disconnect(sh, "filter_add", HandleSourceFilterCreated, this); + signal_handler_disconnect(sh, "filter_remove", HandleSourceFilterRemoved, this); + signal_handler_disconnect(sh, "reorder_filters", HandleSourceFilterListReindexed, this); // Scenes signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this); @@ -199,6 +205,30 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source) signal_handler_disconnect(sh, "item_transform", HandleSceneItemTransformChanged, this); } +void EventHandler::ConnectFilterSignals(obs_source_t *filter) +{ + if (!filter || obs_source_removed(filter)) + return; + + DisconnectFilterSignals(filter); + + signal_handler_t* sh = obs_source_get_signal_handler(filter); + + signal_handler_connect(sh, "enable", HandleSourceFilterEnableStateChanged, this); + signal_handler_connect(sh, "rename", HandleSourceFilterNameChanged, this); +} + +void EventHandler::DisconnectFilterSignals(obs_source_t *filter) +{ + if (!filter) + return; + + signal_handler_t* sh = obs_source_get_signal_handler(filter); + + signal_handler_disconnect(sh, "enable", HandleSourceFilterEnableStateChanged, this); + signal_handler_disconnect(sh, "rename", HandleSourceFilterNameChanged, this); +} + void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_data) { auto eventHandler = static_cast(private_data); @@ -211,18 +241,38 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_ // In the case that plugins become hotloadable, this will have to go back into `EventHandler::EventHandler()` // Enumerate inputs and connect each one - obs_enum_sources([](void* param, obs_source_t* source) { - auto eventHandler = static_cast(param); - eventHandler->ConnectSourceSignals(source); - return true; - }, private_data); + { + auto enumInputs = [](void *param, obs_source_t *source) { + auto eventHandler = static_cast(param); + eventHandler->ConnectSourceSignals(source); + + auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){ + auto eventHandler = static_cast(param); + eventHandler->ConnectFilterSignals(filter); + }; + obs_source_enum_filters(source, enumFilters, param); + + return true; + }; + obs_enum_sources(enumInputs, private_data); + } // Enumerate scenes and connect each one - obs_enum_scenes([](void* param, obs_source_t* source) { - auto eventHandler = static_cast(param); - eventHandler->ConnectSourceSignals(source); - return true; - }, private_data); + { + auto enumScenes = [](void *param, obs_source_t *source) { + auto eventHandler = static_cast(param); + eventHandler->ConnectSourceSignals(source); + + auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){ + auto eventHandler = static_cast(param); + eventHandler->ConnectFilterSignals(filter); + }; + obs_source_enum_filters(source, enumFilters, param); + + return true; + }; + obs_enum_scenes(enumScenes, private_data); + } blog_debug("[EventHandler::OnFrontendEvent] Finished."); @@ -244,18 +294,38 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_ // In the case that plugins become hotloadable, this will have to go back into `EventHandler::~EventHandler()` // Enumerate inputs and disconnect each one - obs_enum_sources([](void* param, obs_source_t* source) { - auto eventHandler = static_cast(param); - eventHandler->DisconnectSourceSignals(source); - return true; - }, private_data); + { + auto enumInputs = [](void *param, obs_source_t *source) { + auto eventHandler = static_cast(param); + eventHandler->DisconnectSourceSignals(source); + + auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){ + auto eventHandler = static_cast(param); + eventHandler->ConnectFilterSignals(filter); + }; + obs_source_enum_filters(source, enumFilters, param); + + return true; + }; + obs_enum_sources(enumInputs, private_data); + } // Enumerate scenes and disconnect each one - obs_enum_scenes([](void* param, obs_source_t* source) { - auto eventHandler = static_cast(param); - eventHandler->DisconnectSourceSignals(source); - return true; - }, private_data); + { + auto enumScenes = [](void *param, obs_source_t *source) { + auto eventHandler = static_cast(param); + eventHandler->DisconnectSourceSignals(source); + + auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){ + auto eventHandler = static_cast(param); + eventHandler->ConnectFilterSignals(filter); + }; + obs_source_enum_filters(source, enumFilters, param); + + return true; + }; + obs_enum_scenes(enumScenes, private_data); + } blog_debug("[EventHandler::OnFrontendEvent] Finished."); @@ -465,8 +535,6 @@ void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data) case OBS_SOURCE_TYPE_INPUT: eventHandler->HandleInputNameChanged(source, oldSourceName, sourceName); break; - case OBS_SOURCE_TYPE_FILTER: - break; case OBS_SOURCE_TYPE_TRANSITION: break; case OBS_SOURCE_TYPE_SCENE: diff --git a/src/eventhandler/EventHandler.h b/src/eventhandler/EventHandler.h index 7049864e..3f71caff 100644 --- a/src/eventhandler/EventHandler.h +++ b/src/eventhandler/EventHandler.h @@ -58,6 +58,9 @@ class EventHandler void ConnectSourceSignals(obs_source_t *source); void DisconnectSourceSignals(obs_source_t *source); + void ConnectFilterSignals(obs_source_t *filter); + void DisconnectFilterSignals(obs_source_t *filter); + void BroadcastEvent(uint64_t requiredIntent, std::string eventType, json eventData = nullptr, uint8_t rpcVersion = 0); // Signal handler: frontend @@ -136,4 +139,11 @@ class EventHandler 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); + + // Filters + static void HandleSourceFilterNameChanged(void *param, calldata_t *data); // Direct callback + static void HandleSourceFilterCreated(void *param, calldata_t *data); // Direct callback + static void HandleSourceFilterRemoved(void *param, calldata_t *data); // Direct callback + static void HandleSourceFilterListReindexed(void *param, calldata_t *data); // Direct callback + static void HandleSourceFilterEnableStateChanged(void *param, calldata_t *data); // Direct callback }; diff --git a/src/eventhandler/EventHandler_Filters.cpp b/src/eventhandler/EventHandler_Filters.cpp index 6e0f4c11..308b81b8 100644 --- a/src/eventhandler/EventHandler_Filters.cpp +++ b/src/eventhandler/EventHandler_Filters.cpp @@ -18,3 +18,174 @@ with this program. If not, see */ #include "EventHandler.h" + +/** + * A filter has been added to a source. + * + * @dataField sourceName | String | Name of the source the filter was added to + * @dataField filterName | String | Name of the filter + * @dataField filterKind | String | The kind of the filter + * @dataField filterIndex | Number | Index position of the filter + * @dataField filterSettings | Object | The settings configured to the filter when it was created + * @dataField defaultFilterSettings | Object | The default settings for the filter + * + * @eventType SourceFilterCreated + * @eventSubscription Filters + * @complexity 2 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api events + * @category filters + */ +void EventHandler::HandleSourceFilterCreated(void *param, calldata_t *data) +{ + auto eventHandler = static_cast(param); + + obs_source_t *source = GetCalldataPointer(data, "source"); + obs_source_t *filter = GetCalldataPointer(data, "filter"); + + if (!(source && filter)) + return; + + eventHandler->ConnectFilterSignals(filter); + + std::string filterKind = obs_source_get_id(filter); + OBSDataAutoRelease filterSettings = obs_source_get_settings(filter); + OBSDataAutoRelease defaultFilterSettings = obs_get_source_defaults(filterKind.c_str()); + + json eventData; + eventData["sourceName"] = obs_source_get_name(source); + eventData["filterName"] = obs_source_get_name(filter); + eventData["filterKind"] = filterKind; + eventData["filterIndex"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter); + eventData["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings); + eventData["defaultFilterSettings"] = Utils::Json::ObsDataToJson(defaultFilterSettings, true); + eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterCreated", eventData); +} + +/** + * A filter has been removed from a source. + * + * @dataField sourceName | String | Name of the source the filter was on + * @dataField filterName | String | Name of the filter + * + * @eventType SourceFilterRemoved + * @eventSubscription Filters + * @complexity 2 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api events + * @category filters + */ +void EventHandler::HandleSourceFilterRemoved(void *param, calldata_t *data) +{ + auto eventHandler = static_cast(param); + + obs_source_t *source = GetCalldataPointer(data, "source"); + obs_source_t *filter = GetCalldataPointer(data, "filter"); + + if (!(source && filter)) + return; + + eventHandler->DisconnectFilterSignals(filter); + + json eventData; + eventData["sourceName"] = obs_source_get_name(source); + eventData["filterName"] = obs_source_get_name(filter); + eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterRemoved", eventData); +} + +/** + * A source's filter list has been reindexed. + * + * @dataField sourceName | String | Name of the source + * @dataField filters | Array | Array of filter objects + * + * @eventType SourceFilterListReindexed + * @eventSubscription Filters + * @complexity 3 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api events + * @category filters + */ +void EventHandler::HandleSourceFilterListReindexed(void *param, calldata_t *data) +{ + auto eventHandler = static_cast(param); + + obs_source_t *source = GetCalldataPointer(data, "source"); + if (!source) + return; + + json eventData; + eventData["sourceName"] = obs_source_get_name(source); + eventData["filters"] = Utils::Obs::ArrayHelper::GetSourceFilterList(source); + eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterListReindexed", eventData); +} + +/** + * A source filter's enable state has changed. + * + * @dataField sourceName | String | Name of the source the filter is on + * @dataField filterName | String | Name of the filter + * @dataField filterEnabled | Boolean | Whether the filter is enabled + * + * @eventType SourceFilterEnableStateChanged + * @eventSubscription Filters + * @complexity 3 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api events + * @category filters + */ +void EventHandler::HandleSourceFilterEnableStateChanged(void *param, calldata_t *data) +{ + auto eventHandler = static_cast(param); + + obs_source_t *filter = GetCalldataPointer(data, "source"); + if (!filter) + return; + + // Not OBSSourceAutoRelease as get_parent doesn't increment refcount + obs_source_t *source = obs_filter_get_parent(filter); + if (!source) + return; + + bool filterEnabled = calldata_bool(data, "enabled"); + + json eventData; + eventData["sourceName"] = obs_source_get_name(source); + eventData["filterName"] = obs_source_get_name(filter); + eventData["filterEnabled"] = filterEnabled; + eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterEnableStateChanged", eventData); +} + +/** + * The name of a source filter has changed. + * + * @dataField sourceName | String | The source the filter is on + * @dataField oldFilterName | String | Old name of the filter + * @dataField filterName | String | New name of the filter + * + * @eventType SourceFilterNameChanged + * @eventSubscription Filters + * @complexity 2 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api events + * @category filters + */ +void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data) +{ + auto eventHandler = static_cast(param); + + obs_source_t *filter = GetCalldataPointer(data, "source"); + if (!filter) + return; + + json eventData; + eventData["sourceName"] = obs_source_get_name(obs_filter_get_parent(filter)); + eventData["oldFilterName"] = calldata_string(data, "prev_name"); + eventData["filterName"] = calldata_string(data, "new_name"); + eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterNameChanged", eventData); +} diff --git a/src/requesthandler/RequestHandler.cpp b/src/requesthandler/RequestHandler.cpp index b9933890..59ef9d1e 100644 --- a/src/requesthandler/RequestHandler.cpp +++ b/src/requesthandler/RequestHandler.cpp @@ -111,7 +111,14 @@ const std::unordered_map RequestHandler::_han {"SetTBarPosition", &RequestHandler::SetTBarPosition}, // Filters + {"GetSourceFilterList", &RequestHandler::GetSourceFilterList}, + {"CreateSourceFilter", &RequestHandler::CreateSourceFilter}, + {"RemoveSourceFilter", &RequestHandler::RemoveSourceFilter}, + {"GetSourceFilterDefaultSettings", &RequestHandler::GetSourceFilterDefaultSettings}, {"GetSourceFilter", &RequestHandler::GetSourceFilter}, + {"SetSourceFilterIndex", &RequestHandler::SetSourceFilterIndex}, + {"SetSourceFilterSettings", &RequestHandler::SetSourceFilterSettings}, + {"SetSourceFilterEnabled", &RequestHandler::SetSourceFilterEnabled}, // Scene Items {"GetSceneItemList", &RequestHandler::GetSceneItemList}, diff --git a/src/requesthandler/RequestHandler.h b/src/requesthandler/RequestHandler.h index 65517370..2d9bf20c 100644 --- a/src/requesthandler/RequestHandler.h +++ b/src/requesthandler/RequestHandler.h @@ -129,7 +129,14 @@ class RequestHandler { RequestResult SetTBarPosition(const Request&); // Filters + RequestResult GetSourceFilterList(const Request&); + RequestResult CreateSourceFilter(const Request&); + RequestResult RemoveSourceFilter(const Request&); + RequestResult GetSourceFilterDefaultSettings(const Request&); RequestResult GetSourceFilter(const Request&); + RequestResult SetSourceFilterIndex(const Request&); + RequestResult SetSourceFilterSettings(const Request&); + RequestResult SetSourceFilterEnabled(const Request&); // Scene Items RequestResult GetSceneItemList(const Request&); diff --git a/src/requesthandler/RequestHandler_Filters.cpp b/src/requesthandler/RequestHandler_Filters.cpp index 7bb6e0e0..21d51097 100644 --- a/src/requesthandler/RequestHandler_Filters.cpp +++ b/src/requesthandler/RequestHandler_Filters.cpp @@ -19,6 +19,145 @@ with this program. If not, see #include "RequestHandler.h" +/** + * Gets an array of all of a source's filters. + * + * @requestField sourceName | String | Name of the source + * + * @responseField filters | Array | Array of filters + * + * @requestType GetSourceFilterList + * @complexity 2 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api requests + * @category filters + */ +RequestResult RequestHandler::GetSourceFilterList(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment); + if(!source) + return RequestResult::Error(statusCode, comment); + + json responseData; + responseData["filters"] = Utils::Obs::ArrayHelper::GetSourceFilterList(source); + + return RequestResult::Success(responseData); +} + +/** + * Creates a new filter, adding it to the specified source. + * + * @requestField sourceName | String | Name of the source to add the filter to + * @requestField filterName | String | Name of the new filter to be created + * @requestField filterKind | String | The kind of filter to be created + * @requestField ?filterSettings | Object | Settings object to initialize the filter with | Default settings used + * + * @requestType CreateSourceFilter + * @complexity 3 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api requests + * @category filters + */ +RequestResult RequestHandler::CreateSourceFilter(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + + OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment); + if (!(source && request.ValidateString("filterName", statusCode, comment) && request.ValidateString("filterKind", statusCode, comment))) + return RequestResult::Error(statusCode, comment); + + std::string filterName = request.RequestData["filterName"]; + OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name(source, filterName.c_str()); + if (existingFilter) + return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A filter already exists by that name."); + + std::string filterKind = request.RequestData["filterKind"]; + auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList(); + 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."); + + OBSDataAutoRelease filterSettings = nullptr; + if (request.Contains("filterSettings")) { + if (!request.ValidateOptionalObject("filterSettings", statusCode, comment, true)) + return RequestResult::Error(statusCode, comment); + + filterSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]); + } + + OBSSourceAutoRelease filter = Utils::Obs::ActionHelper::CreateSourceFilter(source, filterName, filterKind, filterSettings); + + if(!filter) + return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the filter failed."); + + return RequestResult::Success(); +} + +/** + * Removes a filter from a source. + * + * @requestField sourceName | String | Name of the source the filter is on + * @requestField filterName | String | Name of the filter to remove + * + * @requestType RemoveSourceFilter + * @complexity 2 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api requests + * @category filters + */ +RequestResult RequestHandler::RemoveSourceFilter(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment); + if (!pair.filter) + return RequestResult::Error(statusCode, comment); + + obs_source_filter_remove(pair.source, pair.filter); + + return RequestResult::Success(); +} + +/** + * Gets the default settings for a filter kind. + * + * @requestField filterKind | String | Filter kind to get the default settings for + * + * @responseField defaultFilterSettings | Object | Object of default settings for the filter kind + * + * @requestType GetSourceFilterDefaultSettings + * @complexity 3 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api requests + * @category filters + */ +RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + if (!request.ValidateString("filterKind", statusCode, comment)) + return RequestResult::Error(statusCode, comment); + + std::string filterKind = request.RequestData["filterKind"]; + auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList(); + if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end()) + return RequestResult::Error(RequestStatus::InvalidFilterKind); + + OBSDataAutoRelease defaultSettings = obs_get_source_defaults(filterKind.c_str()); + if (!defaultSettings) + return RequestResult::Error(RequestStatus::InvalidFilterKind); + + json responseData; + responseData["defaultFilterSettings"] = Utils::Json::ObsDataToJson(defaultSettings, true); + return RequestResult::Success(responseData); +} + /** * Gets the info for a specific source filter. * @@ -55,3 +194,108 @@ RequestResult RequestHandler::GetSourceFilter(const Request& request) return RequestResult::Success(responseData); } + +/** + * Sets the index position of a filter on a source. + * + * @requestField sourceName | String | Name of the source the filter is on + * @requestField filterName | String | Name of the filter + * @requestField filterIndex | Number | New index position of the filter | >= 0 + * + * @requestType SetSourceFilterIndex + * @complexity 3 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api requests + * @category filters + */ +RequestResult RequestHandler::SetSourceFilterIndex(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment); + if (!(pair.filter && request.ValidateNumber("filterIndex", statusCode, comment, 0, 8192))) + return RequestResult::Error(statusCode, comment); + + int filterIndex = request.RequestData["filterIndex"]; + + Utils::Obs::ActionHelper::SetSourceFilterIndex(pair.source, pair.filter, filterIndex); + + return RequestResult::Success(); +} + +/** + * Sets the settings of a source filter. + * + * @requestField sourceName | String | Name of the source the filter is on + * @requestField filterName | String | Name of the filter to set the settings of + * @requestField filterSettings | Object | Object of settings to apply + * @requestField ?overlay | Boolean | True == apply the settings on top of existing ones, False == reset the input to its defaults, then apply settings. | true + * + * @requestType SetSourceFilterSettings + * @complexity 3 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api requests + * @category filters + */ +RequestResult RequestHandler::SetSourceFilterSettings(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment); + if (!(pair.filter && request.ValidateObject("filterSettings", statusCode, comment, true))) + return RequestResult::Error(statusCode, comment); + + // Almost identical to SetInputSettings + + bool overlay = true; + if (request.Contains("overlay")) { + if (!request.ValidateOptionalBoolean("overlay", statusCode, comment)) + return RequestResult::Error(statusCode, comment); + + overlay = request.RequestData["overlay"]; + } + + OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]); + if (!newSettings) + return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!"); + + if (overlay) + obs_source_update(pair.filter, newSettings); + else + obs_source_reset_settings(pair.filter, newSettings); + + obs_source_update_properties(pair.filter); + + return RequestResult::Success(); +} + +/** + * Sets the enable state of a source filter. + * + * @requestField sourceName | String | Name of the source the filter is on + * @requestField filterName | Number | Name of the filter + * @requestField filterEnabled | Boolean | New enable state of the filter + * + * @requestType SetSourceFilterEnabled + * @complexity 3 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api requests + * @category filters + */ +RequestResult RequestHandler::SetSourceFilterEnabled(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment); + if (!(pair.filter && request.ValidateBoolean("filterEnabled", statusCode, comment))) + return RequestResult::Error(statusCode, comment); + + bool filterEnabled = request.RequestData["filterEnabled"]; + + obs_source_set_enabled(pair.filter, filterEnabled); + + return RequestResult::Success(); +} diff --git a/src/requesthandler/RequestHandler_Inputs.cpp b/src/requesthandler/RequestHandler_Inputs.cpp index 3ec5d076..227ae656 100644 --- a/src/requesthandler/RequestHandler_Inputs.cpp +++ b/src/requesthandler/RequestHandler_Inputs.cpp @@ -269,6 +269,9 @@ RequestResult RequestHandler::GetInputDefaultSettings(const Request& request) return RequestResult::Error(statusCode, comment); std::string inputKind = request.RequestData["inputKind"]; + auto kinds = Utils::Obs::ArrayHelper::GetInputKindList(); + if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end()) + return RequestResult::Error(RequestStatus::InvalidInputKind); OBSDataAutoRelease defaultSettings = obs_get_source_defaults(inputKind.c_str()); if (!defaultSettings) diff --git a/src/requesthandler/types/RequestStatus.h b/src/requesthandler/types/RequestStatus.h index 12999bfb..45d7e04e 100644 --- a/src/requesthandler/types/RequestStatus.h +++ b/src/requesthandler/types/RequestStatus.h @@ -345,6 +345,17 @@ namespace RequestStatus { * @api enums */ ResourceNotConfigurable = 606, + /** + * The specified filter (obs_source_t-OBS_SOURCE_TYPE_FILTER) had the wrong kind. + * + * @enumIdentifier InvalidFilterKind + * @enumValue 607 + * @enumType RequestStatus + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api enums + */ + InvalidFilterKind = 607, /** * Creating the resource failed. diff --git a/src/utils/Obs.h b/src/utils/Obs.h index f0d1baff..40f7c93c 100644 --- a/src/utils/Obs.h +++ b/src/utils/Obs.h @@ -193,6 +193,8 @@ namespace Utils { std::vector GetListPropertyItems(obs_property_t *property); std::vector GetTransitionKindList(); std::vector GetSceneTransitionList(); + std::vector GetSourceFilterList(obs_source_t *source); + std::vector GetFilterKindList(); } namespace ObjectHelper { @@ -209,6 +211,8 @@ namespace Utils { 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); } } } diff --git a/src/utils/Obs_ActionHelper.cpp b/src/utils/Obs_ActionHelper.cpp index 407f374e..c68e4f77 100644 --- a/src/utils/Obs_ActionHelper.cpp +++ b/src/utils/Obs_ActionHelper.cpp @@ -87,3 +87,30 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, st 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 *filter = obs_source_create_private(filterKind.c_str(), filterName.c_str(), filterSettings); + + if (!filter) + return nullptr; + + obs_source_filter_add(source, filter); + + return filter; +} + +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); + obs_order_movement direction = index > currentIndex ? OBS_ORDER_MOVE_DOWN : OBS_ORDER_MOVE_UP; + + while(currentIndex != index) { + obs_source_filter_set_order(source, filter, direction); + + if (direction == OBS_ORDER_MOVE_DOWN) + currentIndex++; + else + currentIndex--; + } +} diff --git a/src/utils/Obs_ArrayHelper.cpp b/src/utils/Obs_ArrayHelper.cpp index 997512c1..ec6ed9b4 100644 --- a/src/utils/Obs_ArrayHelper.cpp +++ b/src/utils/Obs_ArrayHelper.cpp @@ -276,3 +276,38 @@ std::vector Utils::Obs::ArrayHelper::GetSceneTransitionList() return ret; } + +std::vector Utils::Obs::ArrayHelper::GetFilterKindList() +{ + std::vector ret; + + size_t idx = 0; + const char *kind; + while(obs_enum_filter_types(idx++, &kind)) + ret.push_back(kind); + + return ret; +} + +std::vector Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *source) +{ + std::vector filters; + + auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param) { + auto filters = reinterpret_cast*>(param); + + json filterJson; + filterJson["filterEnabled"] = obs_source_enabled(filter); + filterJson["filterIndex"] = filters->size(); + filterJson["filterKind"] = obs_source_get_id(filter); + filterJson["filterName"] = obs_source_get_name(filter); + + OBSDataAutoRelease filterSettings = obs_source_get_settings(filter); + filterJson["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings); + + filters->push_back(filterJson); + }; + obs_source_enum_filters(source, enumFilters, &filters); + + return filters; +}