From 7fade98407c4d9365070390a58282d5a604272e4 Mon Sep 17 00:00:00 2001 From: Tim Yuen Date: Wed, 23 Feb 2022 19:20:07 -0500 Subject: [PATCH 01/19] README: Add Godot obs-websocket-gd to library list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 87dfd285..7ba8c5a5 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ It is **highly recommended** to protect obs-websocket with a password against un Here's a list of available language APIs for obs-websocket: - Python 3.7+ (Asyncio): [simpleobsws](https://github.com/IRLToolkit/simpleobsws/tree/master) by IRLToolkit - Rust: [obws](https://github.com/dnaka91/obws/tree/v5-api) by dnaka91 +- Godot 3.4.x: [obs-websocket-gd](https://github.com/you-win/obs-websocket-gd) by you-win The 5.x server is a typical WebSocket server running by default on port 4455 (the port number can be changed in the Settings dialog under `Tools`). The protocol we use is documented in [PROTOCOL.md](docs/generated/protocol.md). From a7698a732fe7158b3255ee1b469b9263dde20029 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 1 Mar 2022 14:10:34 -0800 Subject: [PATCH 02/19] eventhandler: Add SceneTransitionStarted + cleanup This commit cleans up a bunch of code in the event handler, making it much easier to understand (IMO). I feel much better about how we handle connecting and disconnecting callbacks. Before, we were actually allowing a bunch of callbacks to stay connected and get cleaned up. Now, we actually properly disconnect them. --- src/eventhandler/EventHandler.cpp | 242 +++++++++++------- src/eventhandler/EventHandler.h | 20 +- src/eventhandler/EventHandler_Filters.cpp | 174 +++++++------ src/eventhandler/EventHandler_Transitions.cpp | 28 +- 4 files changed, 272 insertions(+), 192 deletions(-) diff --git a/src/eventhandler/EventHandler.cpp b/src/eventhandler/EventHandler.cpp index 07638dc0..a4359568 100644 --- a/src/eventhandler/EventHandler.cpp +++ b/src/eventhandler/EventHandler.cpp @@ -115,6 +115,7 @@ void EventHandler::BroadcastEvent(uint64_t requiredIntent, std::string eventType _broadcastCallback(requiredIntent, eventType, eventData, rpcVersion); } +// Connect source signals for Inputs, Scenes, and Transitions. Filters are automatically connected. void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inputs and scenes { if (!source || obs_source_removed(source)) @@ -128,21 +129,17 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu obs_source_type sourceType = obs_source_get_type(source); // Inputs - signal_handler_connect(sh, "activate", HandleInputActiveStateChanged, this); - signal_handler_connect(sh, "deactivate", HandleInputActiveStateChanged, this); - signal_handler_connect(sh, "show", HandleInputShowStateChanged, this); - signal_handler_connect(sh, "hide", HandleInputShowStateChanged, this); - 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, "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, "activate", HandleInputActiveStateChanged, this); + signal_handler_connect(sh, "deactivate", HandleInputActiveStateChanged, this); + signal_handler_connect(sh, "show", HandleInputShowStateChanged, this); + signal_handler_connect(sh, "hide", HandleInputShowStateChanged, this); + 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, "media_started", HandleMediaInputPlaybackStarted, this); signal_handler_connect(sh, "media_ended", HandleMediaInputPlaybackEnded, this); signal_handler_connect(sh, "media_pause", SourceMediaPauseMultiHandler, this); @@ -163,8 +160,32 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu signal_handler_connect(sh, "item_select", HandleSceneItemSelected, this); signal_handler_connect(sh, "item_transform", HandleSceneItemTransformChanged, this); } + + // Scenes and Inputs + if (sourceType == OBS_SOURCE_TYPE_INPUT || sourceType == OBS_SOURCE_TYPE_SCENE) { + signal_handler_connect(sh, "reorder_filters", HandleSourceFilterListReindexed, this); + signal_handler_connect(sh, "filter_add", FilterAddMultiHandler, this); + signal_handler_connect(sh, "filter_remove", FilterRemoveMultiHandler, this); + auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){ + auto eventHandler = static_cast(param); + eventHandler->ConnectSourceSignals(filter); + }; + obs_source_enum_filters(source, enumFilters, this); + } + + // Transitions + if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { + signal_handler_connect(sh, "transition_start", HandleSceneTransitionStarted, this); + } + + // Filters + if (sourceType == OBS_SOURCE_TYPE_FILTER) { + signal_handler_connect(sh, "enable", HandleSourceFilterEnableStateChanged, this); + signal_handler_connect(sh, "rename", HandleSourceFilterNameChanged, this); + } } +// Disconnect source signals for Inputs, Scenes, and Transitions. Filters are automatically disconnected. void EventHandler::DisconnectSourceSignals(obs_source_t *source) { if (!source) @@ -172,70 +193,77 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source) signal_handler_t* sh = obs_source_get_signal_handler(source); + obs_source_type sourceType = obs_source_get_type(source); + // Inputs - signal_handler_disconnect(sh, "activate", HandleInputActiveStateChanged, this); - signal_handler_disconnect(sh, "deactivate", HandleInputActiveStateChanged, this); - signal_handler_disconnect(sh, "show", HandleInputShowStateChanged, this); - signal_handler_disconnect(sh, "hide", HandleInputShowStateChanged, this); - signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, this); - signal_handler_disconnect(sh, "volume", HandleInputVolumeChanged, this); - signal_handler_disconnect(sh, "audio_balance", HandleInputAudioBalanceChanged, this); - signal_handler_disconnect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this); - signal_handler_disconnect(sh, "audio_mixers", HandleInputAudioTracksChanged, this); - signal_handler_disconnect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this); - signal_handler_disconnect(sh, "media_started", HandleMediaInputPlaybackStarted, this); - signal_handler_disconnect(sh, "media_ended", HandleMediaInputPlaybackEnded, this); - signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this); - signal_handler_disconnect(sh, "media_play", SourceMediaPlayMultiHandler, this); - signal_handler_disconnect(sh, "media_restart", SourceMediaRestartMultiHandler, this); - 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); + if (sourceType == OBS_SOURCE_TYPE_INPUT) { + signal_handler_disconnect(sh, "activate", HandleInputActiveStateChanged, this); + signal_handler_disconnect(sh, "deactivate", HandleInputActiveStateChanged, this); + signal_handler_disconnect(sh, "show", HandleInputShowStateChanged, this); + signal_handler_disconnect(sh, "hide", HandleInputShowStateChanged, this); + signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, this); + signal_handler_disconnect(sh, "volume", HandleInputVolumeChanged, this); + signal_handler_disconnect(sh, "audio_balance", HandleInputAudioBalanceChanged, this); + signal_handler_disconnect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this); + signal_handler_disconnect(sh, "audio_mixers", HandleInputAudioTracksChanged, this); + signal_handler_disconnect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this); + signal_handler_disconnect(sh, "media_started", HandleMediaInputPlaybackStarted, this); + signal_handler_disconnect(sh, "media_ended", HandleMediaInputPlaybackEnded, this); + signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this); + signal_handler_disconnect(sh, "media_play", SourceMediaPlayMultiHandler, this); + signal_handler_disconnect(sh, "media_restart", SourceMediaRestartMultiHandler, this); + signal_handler_disconnect(sh, "media_stopped", SourceMediaStopMultiHandler, this); + signal_handler_disconnect(sh, "media_next", SourceMediaNextMultiHandler, this); + signal_handler_disconnect(sh, "media_previous", SourceMediaPreviousMultiHandler, this); + } // Scenes - signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this); - signal_handler_disconnect(sh, "item_remove", HandleSceneItemRemoved, this); - signal_handler_disconnect(sh, "reorder", HandleSceneItemListReindexed, this); - signal_handler_disconnect(sh, "item_visible", HandleSceneItemEnableStateChanged, this); - signal_handler_disconnect(sh, "item_locked", HandleSceneItemLockStateChanged, this); - signal_handler_disconnect(sh, "item_select", HandleSceneItemSelected, this); - signal_handler_disconnect(sh, "item_transform", HandleSceneItemTransformChanged, this); -} + if (sourceType == OBS_SOURCE_TYPE_SCENE) { + signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this); + signal_handler_disconnect(sh, "item_remove", HandleSceneItemRemoved, this); + signal_handler_disconnect(sh, "reorder", HandleSceneItemListReindexed, this); + signal_handler_disconnect(sh, "item_visible", HandleSceneItemEnableStateChanged, this); + signal_handler_disconnect(sh, "item_locked", HandleSceneItemLockStateChanged, this); + signal_handler_disconnect(sh, "item_select", HandleSceneItemSelected, this); + signal_handler_disconnect(sh, "item_transform", HandleSceneItemTransformChanged, this); + } -void EventHandler::ConnectFilterSignals(obs_source_t *filter) -{ - if (!filter || obs_source_removed(filter)) - return; + // Inputs and Scenes + if (sourceType == OBS_SOURCE_TYPE_INPUT || sourceType == OBS_SOURCE_TYPE_SCENE) { + signal_handler_disconnect(sh, "reorder_filters", HandleSourceFilterListReindexed, this); + signal_handler_disconnect(sh, "filter_add", FilterAddMultiHandler, this); + signal_handler_disconnect(sh, "filter_remove", FilterRemoveMultiHandler, this); + auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){ + auto eventHandler = static_cast(param); + eventHandler->DisconnectSourceSignals(filter); + }; + obs_source_enum_filters(source, enumFilters, this); + } - DisconnectFilterSignals(filter); + // Transitions + if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { + signal_handler_disconnect(sh, "transition_start", HandleSceneTransitionStarted, this); + } - 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); + // Filters + if (sourceType == OBS_SOURCE_TYPE_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); - if (!eventHandler->_obsLoaded.load()) { - if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) { + if (!eventHandler->_obsLoaded.load() && event != OBS_FRONTEND_EVENT_FINISHED_LOADING) + return; + + switch (event) { + // General + case OBS_FRONTEND_EVENT_FINISHED_LOADING: blog_debug("[EventHandler::OnFrontendEvent] OBS has finished loading. Connecting final handlers and enabling events..."); + // Connect source signals and enable events only after OBS has fully loaded (to reduce extra logging). eventHandler->_obsLoaded.store(true); @@ -245,13 +273,6 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_ 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); @@ -262,29 +283,28 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_ 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); } + // Enumerate all scene transitions and connect each one + { + obs_frontend_source_list transitions = {}; + obs_frontend_get_transitions(&transitions); + for (size_t i = 0; i < transitions.sources.num; i++) { + obs_source_t* transition = transitions.sources.array[i]; + eventHandler->ConnectSourceSignals(transition); + } + obs_frontend_source_list_free(&transitions); + } + blog_debug("[EventHandler::OnFrontendEvent] Finished."); if (eventHandler->_obsLoadedCallback) eventHandler->_obsLoadedCallback(); - } else { - return; - } - } - switch (event) { - // General + break; case OBS_FRONTEND_EVENT_EXIT: eventHandler->HandleExitStarted(); @@ -298,13 +318,6 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_ 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); @@ -315,18 +328,22 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_ 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); } + // Enumerate all scene transitions and disconnect each one + { + obs_frontend_source_list transitions = {}; + obs_frontend_get_transitions(&transitions); + for (size_t i = 0; i < transitions.sources.num; i++) { + obs_source_t* transition = transitions.sources.array[i]; + eventHandler->DisconnectSourceSignals(transition); + } + obs_frontend_source_list_free(&transitions); + } + blog_debug("[EventHandler::OnFrontendEvent] Finished."); break; @@ -339,9 +356,27 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_ // Config case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING: + { + obs_frontend_source_list transitions = {}; + obs_frontend_get_transitions(&transitions); + for (size_t i = 0; i < transitions.sources.num; i++) { + obs_source_t* transition = transitions.sources.array[i]; + eventHandler->DisconnectSourceSignals(transition); + } + obs_frontend_source_list_free(&transitions); + } eventHandler->HandleCurrentSceneCollectionChanging(); break; case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED: + { + obs_frontend_source_list transitions = {}; + obs_frontend_get_transitions(&transitions); + for (size_t i = 0; i < transitions.sources.num; i++) { + obs_source_t* transition = transitions.sources.array[i]; + eventHandler->ConnectSourceSignals(transition); + } + obs_frontend_source_list_free(&transitions); + } eventHandler->HandleCurrentSceneCollectionChanged(); break; case OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED: @@ -373,6 +408,15 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_ eventHandler->HandleCurrentSceneTransitionChanged(); break; case OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED: + { + obs_frontend_source_list transitions = {}; + obs_frontend_get_transitions(&transitions); + for (size_t i = 0; i < transitions.sources.num; i++) { + obs_source_t* transition = transitions.sources.array[i]; + eventHandler->ConnectSourceSignals(transition); + } + obs_frontend_source_list_free(&transitions); + } break; case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED: eventHandler->HandleCurrentSceneTransitionDurationChanged(); diff --git a/src/eventhandler/EventHandler.h b/src/eventhandler/EventHandler.h index 00ac4410..305e525e 100644 --- a/src/eventhandler/EventHandler.h +++ b/src/eventhandler/EventHandler.h @@ -58,9 +58,6 @@ 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 @@ -118,6 +115,16 @@ class EventHandler // Transitions void HandleCurrentSceneTransitionChanged(); void HandleCurrentSceneTransitionDurationChanged(); + static void HandleSceneTransitionStarted(void *param, calldata_t *data); // Direct callback + + // Filters + static void FilterAddMultiHandler(void *param, calldata_t *data); // Direct callback + static void FilterRemoveMultiHandler(void *param, 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 // Outputs void HandleStreamStateChanged(ObsOutputState state); @@ -139,11 +146,4 @@ 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 a66d5035..c4f64309 100644 --- a/src/eventhandler/EventHandler_Filters.cpp +++ b/src/eventhandler/EventHandler_Filters.cpp @@ -19,25 +19,7 @@ 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) +void EventHandler::FilterAddMultiHandler(void *param, calldata_t *data) { auto eventHandler = static_cast(param); @@ -47,37 +29,12 @@ void EventHandler::HandleSourceFilterCreated(void *param, calldata_t *data) if (!(source && filter)) return; - eventHandler->ConnectFilterSignals(filter); + eventHandler->ConnectSourceSignals(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); + eventHandler->HandleSourceFilterCreated(source, filter); } -/** - * 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) +void EventHandler::FilterRemoveMultiHandler(void *param, calldata_t *data) { auto eventHandler = static_cast(param); @@ -87,12 +44,9 @@ void EventHandler::HandleSourceFilterRemoved(void *param, calldata_t *data) if (!(source && filter)) return; - eventHandler->DisconnectFilterSignals(filter); + eventHandler->DisconnectSourceSignals(filter); - json eventData; - eventData["sourceName"] = obs_source_get_name(source); - eventData["filterName"] = obs_source_get_name(filter); - eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterRemoved", eventData); + eventHandler->HandleSourceFilterRemoved(source, filter); } /** @@ -123,6 +77,92 @@ void EventHandler::HandleSourceFilterListReindexed(void *param, calldata_t *data eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterListReindexed", eventData); } +/** + * 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(obs_source_t *source, obs_source_t *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); + 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(obs_source_t *source, obs_source_t *filter) +{ + json eventData; + eventData["sourceName"] = obs_source_get_name(source); + eventData["filterName"] = obs_source_get_name(filter); + BroadcastEvent(EventSubscription::Filters, "SourceFilterRemoved", 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); +} + /** * A source filter's enable state has changed. * @@ -159,33 +199,3 @@ void EventHandler::HandleSourceFilterEnableStateChanged(void *param, calldata_t 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/eventhandler/EventHandler_Transitions.cpp b/src/eventhandler/EventHandler_Transitions.cpp index 860d5cc6..66f89342 100644 --- a/src/eventhandler/EventHandler_Transitions.cpp +++ b/src/eventhandler/EventHandler_Transitions.cpp @@ -26,7 +26,7 @@ with this program. If not, see * * @eventType CurrentSceneTransitionChanged * @eventSubscription Transitions - * @complexity 3 + * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api events @@ -60,3 +60,29 @@ void EventHandler::HandleCurrentSceneTransitionDurationChanged() eventData["transitionDuration"] = obs_frontend_get_transition_duration(); BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionDurationChanged", eventData); } + +/** + * A scene transition has started. + * + * @dataField transitionName | String | Scene transition name + * + * @eventType SceneTransitionStarted + * @eventSubscription Transitions + * @complexity 2 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api events + * @category transitions + */ +void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data) +{ + auto eventHandler = static_cast(param); + + obs_source_t *source = GetCalldataPointer(data, "source"); + if (!source) + return; + + json eventData; + eventData["transitionName"] = obs_source_get_name(source); + eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionStarted", eventData); +} From 9ac7c5890e3349a74634ad7437f27973c0b298a6 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 1 Mar 2022 14:19:35 -0800 Subject: [PATCH 03/19] eventhandler: Add SceneTransitionEnded --- src/eventhandler/EventHandler.cpp | 2 ++ src/eventhandler/EventHandler.h | 1 + src/eventhandler/EventHandler_Transitions.cpp | 28 +++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/src/eventhandler/EventHandler.cpp b/src/eventhandler/EventHandler.cpp index a4359568..73630dff 100644 --- a/src/eventhandler/EventHandler.cpp +++ b/src/eventhandler/EventHandler.cpp @@ -176,6 +176,7 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu // Transitions if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { signal_handler_connect(sh, "transition_start", HandleSceneTransitionStarted, this); + signal_handler_connect(sh, "transition_stop", HandleSceneTransitionEnded, this); } // Filters @@ -243,6 +244,7 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source) // Transitions if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { signal_handler_disconnect(sh, "transition_start", HandleSceneTransitionStarted, this); + signal_handler_disconnect(sh, "transition_stop", HandleSceneTransitionEnded, this); } // Filters diff --git a/src/eventhandler/EventHandler.h b/src/eventhandler/EventHandler.h index 305e525e..7452b29a 100644 --- a/src/eventhandler/EventHandler.h +++ b/src/eventhandler/EventHandler.h @@ -116,6 +116,7 @@ class EventHandler void HandleCurrentSceneTransitionChanged(); void HandleCurrentSceneTransitionDurationChanged(); static void HandleSceneTransitionStarted(void *param, calldata_t *data); // Direct callback + static void HandleSceneTransitionEnded(void *param, calldata_t *data); // Direct callback // Filters static void FilterAddMultiHandler(void *param, calldata_t *data); // Direct callback diff --git a/src/eventhandler/EventHandler_Transitions.cpp b/src/eventhandler/EventHandler_Transitions.cpp index 66f89342..35f0b67b 100644 --- a/src/eventhandler/EventHandler_Transitions.cpp +++ b/src/eventhandler/EventHandler_Transitions.cpp @@ -86,3 +86,31 @@ void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data) eventData["transitionName"] = obs_source_get_name(source); eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionStarted", eventData); } + +/** + * A scene transition has completed fully. + * + * Note: Does not appear to trigger when the transition is interrupted by the user. + * + * @dataField transitionName | String | Scene transition name + * + * @eventType SceneTransitionEnded + * @eventSubscription Transitions + * @complexity 2 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api events + * @category transitions + */ +void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data) +{ + auto eventHandler = static_cast(param); + + obs_source_t *source = GetCalldataPointer(data, "source"); + if (!source) + return; + + json eventData; + eventData["transitionName"] = obs_source_get_name(source); + eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionEnded", eventData); +} From bbf9c283c02c8a85f2e718a50d028ec81825ae15 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 1 Mar 2022 14:36:16 -0800 Subject: [PATCH 04/19] eventhandler: Add SceneTransitionVideoEnded --- src/eventhandler/EventHandler.cpp | 2 ++ src/eventhandler/EventHandler.h | 1 + src/eventhandler/EventHandler_Transitions.cpp | 31 +++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/eventhandler/EventHandler.cpp b/src/eventhandler/EventHandler.cpp index 73630dff..a25c3847 100644 --- a/src/eventhandler/EventHandler.cpp +++ b/src/eventhandler/EventHandler.cpp @@ -177,6 +177,7 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { signal_handler_connect(sh, "transition_start", HandleSceneTransitionStarted, this); signal_handler_connect(sh, "transition_stop", HandleSceneTransitionEnded, this); + signal_handler_connect(sh, "transition_video_stop", HandleSceneTransitionVideoEnded, this); } // Filters @@ -245,6 +246,7 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source) if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { signal_handler_disconnect(sh, "transition_start", HandleSceneTransitionStarted, this); signal_handler_disconnect(sh, "transition_stop", HandleSceneTransitionEnded, this); + signal_handler_disconnect(sh, "transition_video_stop", HandleSceneTransitionVideoEnded, this); } // Filters diff --git a/src/eventhandler/EventHandler.h b/src/eventhandler/EventHandler.h index 7452b29a..458470d3 100644 --- a/src/eventhandler/EventHandler.h +++ b/src/eventhandler/EventHandler.h @@ -117,6 +117,7 @@ class EventHandler void HandleCurrentSceneTransitionDurationChanged(); static void HandleSceneTransitionStarted(void *param, calldata_t *data); // Direct callback static void HandleSceneTransitionEnded(void *param, calldata_t *data); // Direct callback + static void HandleSceneTransitionVideoEnded(void *param, calldata_t *data); // Direct callback // Filters static void FilterAddMultiHandler(void *param, calldata_t *data); // Direct callback diff --git a/src/eventhandler/EventHandler_Transitions.cpp b/src/eventhandler/EventHandler_Transitions.cpp index 35f0b67b..94bb34d1 100644 --- a/src/eventhandler/EventHandler_Transitions.cpp +++ b/src/eventhandler/EventHandler_Transitions.cpp @@ -114,3 +114,34 @@ void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data) eventData["transitionName"] = obs_source_get_name(source); eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionEnded", eventData); } + +/** + * A scene transition's video has completed fully. + * + * Useful for stinger transitions to tell when the video *actually* ends. + * `SceneTransitionEnded` only signifies the cut point, not the completion of transition playback. + * + * Note: Appears to be called by every transition, regardless of relevance. + * + * @dataField transitionName | String | Scene transition name + * + * @eventType SceneTransitionVideoEnded + * @eventSubscription Transitions + * @complexity 2 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @api events + * @category transitions + */ +void EventHandler::HandleSceneTransitionVideoEnded(void *param, calldata_t *data) +{ + auto eventHandler = static_cast(param); + + obs_source_t *source = GetCalldataPointer(data, "source"); + if (!source) + return; + + json eventData; + eventData["transitionName"] = obs_source_get_name(source); + eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionVideoEnded", eventData); +} From 444032714130630c8ecf5fb1475365988b3734a3 Mon Sep 17 00:00:00 2001 From: Github Actions <> Date: Tue, 1 Mar 2022 22:37:04 +0000 Subject: [PATCH 05/19] docs(ci): Update generated docs - bbf9c28 [skip ci] --- docs/generated/protocol.json | 121 +++++++++++++++++++++++++---------- docs/generated/protocol.md | 115 +++++++++++++++++++++++++-------- 2 files changed, 174 insertions(+), 62 deletions(-) diff --git a/docs/generated/protocol.json b/docs/generated/protocol.json index 16ac5dfa..caa0ac3b 100644 --- a/docs/generated/protocol.json +++ b/docs/generated/protocol.json @@ -4288,6 +4288,28 @@ } ] }, + { + "description": "A source's filter list has been reindexed.", + "eventType": "SourceFilterListReindexed", + "eventSubscription": "Filters", + "complexity": 3, + "rpcVersion": "1", + "deprecated": false, + "initialVersion": "5.0.0", + "category": "filters", + "dataFields": [ + { + "valueName": "sourceName", + "valueType": "String", + "valueDescription": "Name of the source" + }, + { + "valueName": "filters", + "valueType": "Array", + "valueDescription": "Array of filter objects" + } + ] + }, { "description": "A filter has been added to a source.", "eventType": "SourceFilterCreated", @@ -4353,10 +4375,10 @@ ] }, { - "description": "A source's filter list has been reindexed.", - "eventType": "SourceFilterListReindexed", + "description": "The name of a source filter has changed.", + "eventType": "SourceFilterNameChanged", "eventSubscription": "Filters", - "complexity": 3, + "complexity": 2, "rpcVersion": "1", "deprecated": false, "initialVersion": "5.0.0", @@ -4365,12 +4387,17 @@ { "valueName": "sourceName", "valueType": "String", - "valueDescription": "Name of the source" + "valueDescription": "The source the filter is on" }, { - "valueName": "filters", - "valueType": "Array", - "valueDescription": "Array of filter objects" + "valueName": "oldFilterName", + "valueType": "String", + "valueDescription": "Old name of the filter" + }, + { + "valueName": "filterName", + "valueType": "String", + "valueDescription": "New name of the filter" } ] }, @@ -4401,33 +4428,6 @@ } ] }, - { - "description": "The name of a source filter has changed.", - "eventType": "SourceFilterNameChanged", - "eventSubscription": "Filters", - "complexity": 2, - "rpcVersion": "1", - "deprecated": false, - "initialVersion": "5.0.0", - "category": "filters", - "dataFields": [ - { - "valueName": "sourceName", - "valueType": "String", - "valueDescription": "The source the filter is on" - }, - { - "valueName": "oldFilterName", - "valueType": "String", - "valueDescription": "Old name of the filter" - }, - { - "valueName": "filterName", - "valueType": "String", - "valueDescription": "New name of the filter" - } - ] - }, { "description": "OBS has begun the shutdown process.", "eventType": "ExitStarted", @@ -5179,7 +5179,7 @@ "description": "The current scene transition has changed.", "eventType": "CurrentSceneTransitionChanged", "eventSubscription": "Transitions", - "complexity": 3, + "complexity": 2, "rpcVersion": "1", "deprecated": false, "initialVersion": "5.0.0", @@ -5209,6 +5209,57 @@ } ] }, + { + "description": "A scene transition has started.", + "eventType": "SceneTransitionStarted", + "eventSubscription": "Transitions", + "complexity": 2, + "rpcVersion": "1", + "deprecated": false, + "initialVersion": "5.0.0", + "category": "transitions", + "dataFields": [ + { + "valueName": "transitionName", + "valueType": "String", + "valueDescription": "Scene transition name" + } + ] + }, + { + "description": "A scene transition has completed fully.\n\nNote: Does not appear to trigger when the transition is interrupted by the user.", + "eventType": "SceneTransitionEnded", + "eventSubscription": "Transitions", + "complexity": 2, + "rpcVersion": "1", + "deprecated": false, + "initialVersion": "5.0.0", + "category": "transitions", + "dataFields": [ + { + "valueName": "transitionName", + "valueType": "String", + "valueDescription": "Scene transition name" + } + ] + }, + { + "description": "A scene transition's video has completed fully.\n\nUseful for stinger transitions to tell when the video *actually* ends.\n`SceneTransitionEnded` only signifies the cut point, not the completion of transition playback.\n\nNote: Appears to be called by every transition, regardless of relevance.", + "eventType": "SceneTransitionVideoEnded", + "eventSubscription": "Transitions", + "complexity": 2, + "rpcVersion": "1", + "deprecated": false, + "initialVersion": "5.0.0", + "category": "transitions", + "dataFields": [ + { + "valueName": "transitionName", + "valueType": "String", + "valueDescription": "Scene transition name" + } + ] + }, { "description": "Studio mode has been enabled or disabled.", "eventType": "StudioModeStateChanged", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 54a07b73..2046c370 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -1282,12 +1282,15 @@ Subscription value to receive the `SceneItemTransformChanged` high-volume event. - [Transitions](#transitions) - [CurrentSceneTransitionChanged](#currentscenetransitionchanged) - [CurrentSceneTransitionDurationChanged](#currentscenetransitiondurationchanged) + - [SceneTransitionStarted](#scenetransitionstarted) + - [SceneTransitionEnded](#scenetransitionended) + - [SceneTransitionVideoEnded](#scenetransitionvideoended) - [Filters](#filters) + - [SourceFilterListReindexed](#sourcefilterlistreindexed) - [SourceFilterCreated](#sourcefiltercreated) - [SourceFilterRemoved](#sourcefilterremoved) - - [SourceFilterListReindexed](#sourcefilterlistreindexed) - - [SourceFilterEnableStateChanged](#sourcefilterenablestatechanged) - [SourceFilterNameChanged](#sourcefilternamechanged) + - [SourceFilterEnableStateChanged](#sourcefilterenablestatechanged) - [Scene Items](#scene-items) - [SceneItemCreated](#sceneitemcreated) - [SceneItemRemoved](#sceneitemremoved) @@ -1785,7 +1788,7 @@ A high-volume event providing volume levels of all active inputs every 50 millis The current scene transition has changed. -- Complexity Rating: `3/5` +- Complexity Rating: `2/5` - Latest Supported RPC Version: `1` - Added in v5.0.0 @@ -1812,8 +1815,84 @@ The current scene transition duration has changed. | Name | Type | Description | | ---- | :---: | ----------- | | transitionDuration | Number | Transition duration in milliseconds | + +--- + +### SceneTransitionStarted + +A scene transition has started. + +- Complexity Rating: `2/5` +- Latest Supported RPC Version: `1` +- Added in v5.0.0 + + +**Data Fields:** + +| Name | Type | Description | +| ---- | :---: | ----------- | +| transitionName | String | Scene transition name | + +--- + +### SceneTransitionEnded + +A scene transition has completed fully. + +Note: Does not appear to trigger when the transition is interrupted by the user. + +- Complexity Rating: `2/5` +- Latest Supported RPC Version: `1` +- Added in v5.0.0 + + +**Data Fields:** + +| Name | Type | Description | +| ---- | :---: | ----------- | +| transitionName | String | Scene transition name | + +--- + +### SceneTransitionVideoEnded + +A scene transition's video has completed fully. + +Useful for stinger transitions to tell when the video *actually* ends. +`SceneTransitionEnded` only signifies the cut point, not the completion of transition playback. + +Note: Appears to be called by every transition, regardless of relevance. + +- Complexity Rating: `2/5` +- Latest Supported RPC Version: `1` +- Added in v5.0.0 + + +**Data Fields:** + +| Name | Type | Description | +| ---- | :---: | ----------- | +| transitionName | String | Scene transition name | ## Filters +### SourceFilterListReindexed + +A source's filter list has been reindexed. + +- Complexity Rating: `3/5` +- Latest Supported RPC Version: `1` +- Added in v5.0.0 + + +**Data Fields:** + +| Name | Type | Description | +| ---- | :---: | ----------- | +| sourceName | String | Name of the source | +| filters | Array<Object> | Array of filter objects | + +--- + ### SourceFilterCreated A filter has been added to a source. @@ -1854,11 +1933,11 @@ A filter has been removed from a source. --- -### SourceFilterListReindexed +### SourceFilterNameChanged -A source's filter list has been reindexed. +The name of a source filter has changed. -- Complexity Rating: `3/5` +- Complexity Rating: `2/5` - Latest Supported RPC Version: `1` - Added in v5.0.0 @@ -1867,8 +1946,9 @@ A source's filter list has been reindexed. | Name | Type | Description | | ---- | :---: | ----------- | -| sourceName | String | Name of the source | -| filters | Array<Object> | Array of filter objects | +| sourceName | String | The source the filter is on | +| oldFilterName | String | Old name of the filter | +| filterName | String | New name of the filter | --- @@ -1888,25 +1968,6 @@ A source filter's enable state has changed. | sourceName | String | Name of the source the filter is on | | filterName | String | Name of the filter | | filterEnabled | Boolean | Whether the filter is enabled | - ---- - -### SourceFilterNameChanged - -The name of a source filter has changed. - -- Complexity Rating: `2/5` -- Latest Supported RPC Version: `1` -- Added in v5.0.0 - - -**Data Fields:** - -| Name | Type | Description | -| ---- | :---: | ----------- | -| sourceName | String | The source the filter is on | -| oldFilterName | String | Old name of the filter | -| filterName | String | New name of the filter | ## Scene Items ### SceneItemCreated From 71a32c981ced7d286cc3ec0097c5e40c471d2a40 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 1 Mar 2022 14:48:10 -0800 Subject: [PATCH 06/19] utils: Reserve vector capacity where possible Slight optimization for iteration --- src/utils/Obs_ArrayHelper.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/utils/Obs_ArrayHelper.cpp b/src/utils/Obs_ArrayHelper.cpp index ecf26c16..3b99f2ce 100644 --- a/src/utils/Obs_ArrayHelper.cpp +++ b/src/utils/Obs_ArrayHelper.cpp @@ -88,6 +88,7 @@ std::vector Utils::Obs::ArrayHelper::GetSceneList() obs_frontend_get_scenes(&sceneList); std::vector ret; + ret.reserve(sceneList.sources.num); for (size_t i = 0; i < sceneList.sources.num; i++) { obs_source_t *scene = sceneList.sources.array[i]; @@ -225,6 +226,8 @@ std::vector Utils::Obs::ArrayHelper::GetListPropertyItems(obs_property_t * enum obs_combo_format itemFormat = obs_property_list_format(property); size_t itemCount = obs_property_list_item_count(property); + ret.reserve(itemCount); + for (size_t i = 0; i < itemCount; i++) { json itemData; itemData["itemName"] = obs_property_list_item_name(property, i); @@ -262,6 +265,7 @@ std::vector Utils::Obs::ArrayHelper::GetSceneTransitionList() obs_frontend_get_transitions(&transitionList); std::vector ret; + ret.reserve(transitionList.sources.num); for (size_t i = 0; i < transitionList.sources.num; i++) { obs_source_t *transition = transitionList.sources.array[i]; json transitionJson; From ce31ed177d7e8ef6f8111a53023d549f7ff3c8cc Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 1 Mar 2022 14:50:12 -0800 Subject: [PATCH 07/19] base: Stuff for beta1 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index f4067645..c2aa27a8 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -69,6 +69,7 @@ body: label: obs-websocket Version description: What version of obs-websocket are you using? options: + - 5.0.0-beta1 - 5.0.0-alpha3 - 5.0.0-alpha2 - 4.9.1 From b02a32ce06e5bb0229d4a52333844659504e17b1 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 1 Mar 2022 15:26:45 -0800 Subject: [PATCH 08/19] ci: Apply version suffix to macOS builds --- .github/workflows/main.yml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 93b7dad7..0068dac2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -444,12 +444,12 @@ jobs: mkdir -p ./build cd ./build cmake .. \ - -DQTDIR=${{ github.workspace }}/obsdeps \ - -DDepsPath=${{ github.workspace }}/obsdeps \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \ - -DDISABLE_PLUGINS=true \ - -DENABLE_SCRIPTING=0 \ - -DCMAKE_PREFIX_PATH=${{ github.workspace }}/obsdeps/lib/cmake + -DQTDIR=${{ github.workspace }}/obsdeps \ + -DDepsPath=${{ github.workspace }}/obsdeps \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \ + -DDISABLE_PLUGINS=true \ + -DENABLE_SCRIPTING=0 \ + -DCMAKE_PREFIX_PATH=${{ github.workspace }}/obsdeps/lib/cmake - name: 'Build OBS Studio' if: steps.cache-obs-build.outputs.cache-hit != 'true' working-directory: ${{ github.workspace }}/obs-studio/build @@ -464,13 +464,14 @@ jobs: mkdir -p build cd build cmake .. \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \ - -DQTDIR=${{ github.workspace }}/obsdeps \ - -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs \ - -DLIBOBS_LIB=${{ github.workspace }}/obs-studio/libobs \ - -DOBS_FRONTEND_LIB="${{ github.workspace }}/obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_INSTALL_PREFIX=/usr + -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \ + -DQTDIR=${{ github.workspace }}/obsdeps \ + -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs \ + -DLIBOBS_LIB=${{ github.workspace }}/obs-studio/libobs \ + -DOBS_FRONTEND_LIB="${{ github.workspace }}/obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" - name: 'Build obs-websocket' working-directory: ${{ github.workspace }}/obs-websocket/build shell: bash From 620f11e8a3e2a16d907bcadd72a0d5af91251957 Mon Sep 17 00:00:00 2001 From: t2t2 Date: Tue, 22 Mar 2022 20:16:48 +0200 Subject: [PATCH 09/19] Readme: Add obs-websocket-js to client libraries --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7ba8c5a5..568b9943 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Here's a list of available language APIs for obs-websocket: - Python 3.7+ (Asyncio): [simpleobsws](https://github.com/IRLToolkit/simpleobsws/tree/master) by IRLToolkit - Rust: [obws](https://github.com/dnaka91/obws/tree/v5-api) by dnaka91 - Godot 3.4.x: [obs-websocket-gd](https://github.com/you-win/obs-websocket-gd) by you-win +- Javascript (Node and web): [obs-websocket-js](https://github.com/obs-websocket-community-projects/obs-websocket-js) by OBS Websocket Community The 5.x server is a typical WebSocket server running by default on port 4455 (the port number can be changed in the Settings dialog under `Tools`). The protocol we use is documented in [PROTOCOL.md](docs/generated/protocol.md). From c355c72f4bc1691fff05393eb982e679027b7cf9 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 13 Apr 2022 02:01:40 -0700 Subject: [PATCH 10/19] requesthandler, utils: Add search offset to GetSceneItemId Allows you to select a specific match by offset, or select the last (top) scene item by specifying `-1`. --- .../RequestHandler_SceneItems.cpp | 16 ++++++-- src/utils/Obs.h | 2 +- src/utils/Obs_SearchHelper.cpp | 37 ++++++++++++++++--- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/requesthandler/RequestHandler_SceneItems.cpp b/src/requesthandler/RequestHandler_SceneItems.cpp index 78e72f59..e4a9be84 100644 --- a/src/requesthandler/RequestHandler_SceneItems.cpp +++ b/src/requesthandler/RequestHandler_SceneItems.cpp @@ -86,8 +86,9 @@ RequestResult RequestHandler::GetGroupSceneItemList(const Request& request) * * Scenes and Groups * - * @requestField sceneName | String | Name of the scene or group to search in - * @requestField sourceName | String | Name of the source to find + * @requestField sceneName | String | Name of the scene or group to search in + * @requestField sourceName | String | Name of the source to find + * @requestField ?searchOffset | Number | Number of matches to skip during search. >= 0 means first forward. -1 means last (top) item | >= -1 | 0 * * @responseField sceneItemId | Number | Numeric ID of the scene item * @@ -108,9 +109,16 @@ RequestResult RequestHandler::GetSceneItemId(const Request& request) std::string sourceName = request.RequestData["sourceName"]; - OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName); + int offset = 0; + if (request.Contains("searchOffset")) { + if (!request.ValidateOptionalNumber("searchOffset", statusCode, comment, -1)) + return RequestResult::Error(statusCode, comment); + offset = request.RequestData["searchOffset"]; + } + + OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName, offset); if (!item) - return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene items were found in the specified scene by that name."); + return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene items were found in the specified scene by that name or offset."); json responseData; responseData["sceneItemId"] = obs_sceneitem_get_id(item); diff --git a/src/utils/Obs.h b/src/utils/Obs.h index 4eda079a..82ffa3a9 100644 --- a/src/utils/Obs.h +++ b/src/utils/Obs.h @@ -205,7 +205,7 @@ namespace Utils { namespace SearchHelper { obs_hotkey_t *GetHotkeyByName(std::string name); obs_source_t *GetSceneTransitionByName(std::string name); // Increments source ref. Use OBSSourceAutoRelease - obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name); // Increments ref. Use OBSSceneItemAutoRelease + obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name, int offset = 0); // Increments ref. Use OBSSceneItemAutoRelease } namespace ActionHelper { diff --git a/src/utils/Obs_SearchHelper.cpp b/src/utils/Obs_SearchHelper.cpp index c39501f7..f16501d6 100644 --- a/src/utils/Obs_SearchHelper.cpp +++ b/src/utils/Obs_SearchHelper.cpp @@ -54,15 +54,42 @@ obs_source_t *Utils::Obs::SearchHelper::GetSceneTransitionByName(std::string nam return ret; } +struct SceneItemSearchData { + std::string name; + int offset; + obs_sceneitem_t *ret = nullptr; +}; + // Increments item ref. Use OBSSceneItemAutoRelease -obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name) +obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name, int offset) { if (name.empty()) return nullptr; - // Finds first matching scene item in scene, search starts at index 0 - OBSSceneItem ret = obs_scene_find_source(scene, name.c_str()); - obs_sceneitem_addref(ret); + SceneItemSearchData enumData; + enumData.name = name; + enumData.offset = offset; - return ret; + obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) { + auto enumData = static_cast(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; + } + } + + return true; + }, &enumData); + + return enumData.ret; } From d8c042fe4a638f192a4b891ca35bafafb5d49419 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 13 Apr 2022 02:13:30 -0700 Subject: [PATCH 11/19] requesthandler: Add private scene item settings get/set Some may need it, similarly to Get/SetSourcePrivateSettings. --- src/requesthandler/RequestHandler.cpp | 2 ++ src/requesthandler/RequestHandler.h | 2 ++ .../RequestHandler_SceneItems.cpp | 36 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/src/requesthandler/RequestHandler.cpp b/src/requesthandler/RequestHandler.cpp index 9df48aa4..251fc75a 100644 --- a/src/requesthandler/RequestHandler.cpp +++ b/src/requesthandler/RequestHandler.cpp @@ -138,6 +138,8 @@ const std::unordered_map RequestHandler::_han {"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex}, {"GetSceneItemBlendMode", &RequestHandler::GetSceneItemBlendMode}, {"SetSceneItemBlendMode", &RequestHandler::SetSceneItemBlendMode}, + {"GetSceneItemPrivateSettings", &RequestHandler::GetSceneItemPrivateSettings}, + {"SetSceneItemPrivateSettings", &RequestHandler::SetSceneItemPrivateSettings}, // Outputs {"GetVirtualCamStatus", &RequestHandler::GetVirtualCamStatus}, diff --git a/src/requesthandler/RequestHandler.h b/src/requesthandler/RequestHandler.h index e198a304..e0c77160 100644 --- a/src/requesthandler/RequestHandler.h +++ b/src/requesthandler/RequestHandler.h @@ -156,6 +156,8 @@ class RequestHandler { RequestResult SetSceneItemIndex(const Request&); RequestResult GetSceneItemBlendMode(const Request&); RequestResult SetSceneItemBlendMode(const Request&); + RequestResult GetSceneItemPrivateSettings(const Request&); + RequestResult SetSceneItemPrivateSettings(const Request&); // Outputs RequestResult GetVirtualCamStatus(const Request&); diff --git a/src/requesthandler/RequestHandler_SceneItems.cpp b/src/requesthandler/RequestHandler_SceneItems.cpp index e4a9be84..2b1d5fd9 100644 --- a/src/requesthandler/RequestHandler_SceneItems.cpp +++ b/src/requesthandler/RequestHandler_SceneItems.cpp @@ -718,3 +718,39 @@ RequestResult RequestHandler::SetSceneItemBlendMode(const Request& request) return RequestResult::Success(); } + +// Intentionally undocumented +RequestResult RequestHandler::GetSceneItemPrivateSettings(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); + if (!sceneItem) + return RequestResult::Error(statusCode, comment); + + OBSDataAutoRelease privateSettings = obs_sceneitem_get_private_settings(sceneItem); + + json responseData; + responseData["sceneItemSettings"] = Utils::Json::ObsDataToJson(privateSettings); + + return RequestResult::Success(responseData); +} + +// Intentionally undocumented +RequestResult RequestHandler::SetSceneItemPrivateSettings(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP); + if (!sceneItem || !request.ValidateObject("sceneItemSettings", statusCode, comment)) + return RequestResult::Error(statusCode, comment); + + OBSDataAutoRelease privateSettings = obs_sceneitem_get_private_settings(sceneItem); + + OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["sceneItemSettings"]); + + // Always overlays to prevent destroying internal source unintentionally + obs_data_apply(privateSettings, newSettings); + + return RequestResult::Success(); +} From 9d899376a5ff38995efd0887b339390a924e8467 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 13 Apr 2022 02:17:27 -0700 Subject: [PATCH 12/19] requesthandler: Fix docs for SetSourceFilterEnabled --- src/requesthandler/RequestHandler_Filters.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/requesthandler/RequestHandler_Filters.cpp b/src/requesthandler/RequestHandler_Filters.cpp index 2e6229eb..e69f6df6 100644 --- a/src/requesthandler/RequestHandler_Filters.cpp +++ b/src/requesthandler/RequestHandler_Filters.cpp @@ -308,7 +308,7 @@ RequestResult RequestHandler::SetSourceFilterSettings(const Request& request) * 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 filterName | String | Name of the filter * @requestField filterEnabled | Boolean | New enable state of the filter * * @requestType SetSourceFilterEnabled From 95df4782f3feae174d7e61fec03c6bb8591b5294 Mon Sep 17 00:00:00 2001 From: Github Actions <> Date: Wed, 13 Apr 2022 09:18:28 +0000 Subject: [PATCH 13/19] docs(ci): Update generated docs - 9d89937 [skip ci] --- docs/generated/protocol.json | 44 ++++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 21 +++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/docs/generated/protocol.json b/docs/generated/protocol.json index caa0ac3b..a6e28d85 100644 --- a/docs/generated/protocol.json +++ b/docs/generated/protocol.json @@ -1443,6 +1443,42 @@ ], "responseFields": [] }, + { + "description": "Sets the enable state of a source filter.", + "requestType": "SetSourceFilterEnabled", + "complexity": 3, + "rpcVersion": "1", + "deprecated": false, + "initialVersion": "5.0.0", + "category": "filters", + "requestFields": [ + { + "valueName": "sourceName", + "valueType": "String", + "valueDescription": "Name of the source the filter is on", + "valueRestrictions": null, + "valueOptional": false, + "valueOptionalBehavior": null + }, + { + "valueName": "filterName", + "valueType": "String", + "valueDescription": "Name of the filter", + "valueRestrictions": null, + "valueOptional": false, + "valueOptionalBehavior": null + }, + { + "valueName": "filterEnabled", + "valueType": "Boolean", + "valueDescription": "New enable state of the filter", + "valueRestrictions": null, + "valueOptional": false, + "valueOptionalBehavior": null + } + ], + "responseFields": [] + }, { "description": "Gets data about the current plugin and RPC version.", "requestType": "GetVersion", @@ -2891,6 +2927,14 @@ "valueRestrictions": null, "valueOptional": false, "valueOptionalBehavior": null + }, + { + "valueName": "searchOffset", + "valueType": "Number", + "valueDescription": "Number of matches to skip during search. >= 0 means first forward. -1 means last (top) item", + "valueRestrictions": ">= -1", + "valueOptional": true, + "valueOptionalBehavior": "0" } ], "responseFields": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 2046c370..aa5faaf6 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -2346,6 +2346,7 @@ Studio mode has been enabled or disabled. - [GetSourceFilter](#getsourcefilter) - [SetSourceFilterIndex](#setsourcefilterindex) - [SetSourceFilterSettings](#setsourcefiltersettings) + - [SetSourceFilterEnabled](#setsourcefilterenabled) - [Scene Items](#scene-items-1) - [GetSceneItemList](#getsceneitemlist) - [GetGroupItemList](#getgroupitemlist) @@ -4049,6 +4050,25 @@ Sets the settings of a source filter. | filterSettings | Object | Object of settings to apply | None | N/A | | ?overlay | Boolean | True == apply the settings on top of existing ones, False == reset the input to its defaults, then apply settings. | None | true | +--- + +### SetSourceFilterEnabled + +Sets the enable state of a source filter. + +- Complexity Rating: `3/5` +- Latest Supported RPC Version: `1` +- Added in v5.0.0 + + +**Request Fields:** + +| Name | Type | Description | Value Restrictions | ?Default Behavior | +| ---- | :---: | ----------- | :----------------: | ----------------- | +| sourceName | String | Name of the source the filter is on | None | N/A | +| filterName | String | Name of the filter | None | N/A | +| filterEnabled | Boolean | New enable state of the filter | None | N/A | + ## Scene Items @@ -4123,6 +4143,7 @@ Scenes and Groups | ---- | :---: | ----------- | :----------------: | ----------------- | | sceneName | String | Name of the scene or group to search in | None | N/A | | sourceName | String | Name of the source to find | None | N/A | +| ?searchOffset | Number | Number of matches to skip during search. >= 0 means first forward. -1 means last (top) item | >= -1 | 0 | **Response Fields:** From a9c9363d4a6af03be705395157c7d2303c21d26d Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 16 Apr 2022 19:34:47 -0700 Subject: [PATCH 14/19] eventhandler: Fix group remove signals Like inputs, if a group is ungrouped, the group itself is not removed. What actually happens is the removal of the last scene item triggers a destroy, just like with inputs. This modifies the old signal selection in favor of falling back to the destroy signal if an object is not explicitly removed. --- src/eventhandler/EventHandler.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/eventhandler/EventHandler.cpp b/src/eventhandler/EventHandler.cpp index a25c3847..4ceeb362 100644 --- a/src/eventhandler/EventHandler.cpp +++ b/src/eventhandler/EventHandler.cpp @@ -511,7 +511,8 @@ void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data) } } -// Only called for destruction of a public source +// Only called for destruction of a public sourcs +// Used as a fallback if an input/scene is not explicitly removed void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data) { auto eventHandler = static_cast(param); @@ -530,16 +531,22 @@ void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data) switch (obs_source_get_type(source)) { case OBS_SOURCE_TYPE_INPUT: - // We have to call `InputRemoved` with source_destroy because source_removed is not called when an input's last scene item is removed - eventHandler->HandleInputRemoved(source); + // Only emit removed if the input has not already been removed. This is the case when removing the last scene item of an input. + if (!obs_source_removed(source)) + eventHandler->HandleInputRemoved(source); break; case OBS_SOURCE_TYPE_SCENE: + // Only emit removed if the scene has not already been removed. + if (!obs_source_removed(source)) + eventHandler->HandleSceneRemoved(source); break; default: break; } } +// We prefer remove signals over destroy signals because they are more time-accurate. +// For example, if an input is "removed" but there is a dangling ref, you still want to know that it shouldn't exist, but it's not guaranteed to be destroyed. void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data) { auto eventHandler = static_cast(param); @@ -553,9 +560,9 @@ void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data) switch (obs_source_get_type(source)) { case OBS_SOURCE_TYPE_INPUT: + eventHandler->HandleInputRemoved(source); break; case OBS_SOURCE_TYPE_SCENE: - // Scenes emit the `removed` signal when they are removed from OBS, as expected eventHandler->HandleSceneRemoved(source); break; default: From 8b85658c6160746880a26a04e8a25c5a699b3167 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Mon, 25 Apr 2022 19:43:40 -0700 Subject: [PATCH 15/19] requesthandler: Add platform info to GetVersion It can be very useful to know which platform you're connecting to in the case of things like text input modification, where Windows uses GDI while unix uses FT2. --- src/requesthandler/RequestHandler_General.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/requesthandler/RequestHandler_General.cpp b/src/requesthandler/RequestHandler_General.cpp index 97dd1483..3d8609f6 100644 --- a/src/requesthandler/RequestHandler_General.cpp +++ b/src/requesthandler/RequestHandler_General.cpp @@ -18,6 +18,7 @@ with this program. If not, see */ #include +#include #include "RequestHandler.h" #include "../websocketserver/WebSocketServer.h" @@ -34,6 +35,8 @@ with this program. If not, see * @responseField rpcVersion | Number | Current latest obs-websocket RPC version * @responseField availableRequests | Array | Array of available RPC requests for the currently negotiated RPC version * @responseField supportedImageFormats | Array | Image formats available in `GetSourceScreenshot` and `SaveSourceScreenshot` requests. + * @responseField platform | String | Name of the platform. Usually `windows`, `macos`, or `ubuntu` (linux flavor). Not guaranteed to be any of those + * @responseField platformDescription | String | Description of the platform, like `Windows 10 (10.0)` * * @requestType GetVersion * @complexity 1 @@ -57,6 +60,9 @@ RequestResult RequestHandler::GetVersion(const Request&) } responseData["supportedImageFormats"] = supportedImageFormats; + responseData["platform"] = QSysInfo::productType().toStdString(); + responseData["platformDescription"] = QSysInfo::prettyProductName().toStdString(); + return RequestResult::Success(responseData); } From 9f68e0166bf9dbbc75bd85c4af0ac4c11c00e513 Mon Sep 17 00:00:00 2001 From: Github Actions <> Date: Tue, 26 Apr 2022 02:46:24 +0000 Subject: [PATCH 16/19] docs(ci): Update generated docs - 8b85658 [skip ci] --- docs/generated/protocol.json | 10 ++++++++++ docs/generated/protocol.md | 2 ++ 2 files changed, 12 insertions(+) diff --git a/docs/generated/protocol.json b/docs/generated/protocol.json index a6e28d85..1a74e75e 100644 --- a/docs/generated/protocol.json +++ b/docs/generated/protocol.json @@ -1513,6 +1513,16 @@ "valueName": "supportedImageFormats", "valueType": "Array", "valueDescription": "Image formats available in `GetSourceScreenshot` and `SaveSourceScreenshot` requests." + }, + { + "valueName": "platform", + "valueType": "String", + "valueDescription": "Name of the platform. Usually `windows`, `macos`, or `ubuntu` (linux flavor). Not guaranteed to be any of those" + }, + { + "valueName": "platformDescription", + "valueType": "String", + "valueDescription": "Description of the platform, like `Windows 10 (10.0)`" } ] }, diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index aa5faaf6..e2ebeecf 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -2424,6 +2424,8 @@ Gets data about the current plugin and RPC version. | rpcVersion | Number | Current latest obs-websocket RPC version | | availableRequests | Array<String> | Array of available RPC requests for the currently negotiated RPC version | | supportedImageFormats | Array<String> | Image formats available in `GetSourceScreenshot` and `SaveSourceScreenshot` requests. | +| platform | String | Name of the platform. Usually `windows`, `macos`, or `ubuntu` (linux flavor). Not guaranteed to be any of those | +| platformDescription | String | Description of the platform, like `Windows 10 (10.0)` | --- From 3a5f0d89b99ffb304b3b01a50466d461f94f54be Mon Sep 17 00:00:00 2001 From: Chris Tallon Date: Sat, 12 Mar 2022 13:56:15 +0000 Subject: [PATCH 17/19] Server: Add --websocket_ipv4_only switch Socket listening default changed to IPv4 and IPv6, overridable to IPv4 only by using the command line switch. --- README.md | 2 +- src/Config.cpp | 8 ++++++++ src/Config.h | 1 + src/websocketserver/WebSocketServer.cpp | 8 +++++++- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 568b9943..2d0159ff 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Binaries for Windows, MacOS, and Linux are available in the [Releases](https://g It is **highly recommended** to protect obs-websocket with a password against unauthorized control. To do this, open the "Websocket server settings" dialog under OBS' "Tools" menu. In the settings dialogs, you can enable or disable authentication and set a password for it. -(Psst. You can use `--websocket_port`(value), `--websocket_password`(value), and `--websocket_debug`(flag) on the command line to override the configured values.) +(Psst. You can use `--websocket_port`(value), `--websocket_password`(value), `--websocket_debug`(flag) and `--websocket_ipv4_only`(flag) on the command line to override the configured values.) ### Possible use cases diff --git a/src/Config.cpp b/src/Config.cpp index 5c45b2dd..2243183b 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -33,6 +33,7 @@ with this program. If not, see #define PARAM_PASSWORD "ServerPassword" #define CMDLINE_WEBSOCKET_PORT "websocket_port" +#define CMDLINE_WEBSOCKET_IPV4_ONLY "websocket_ipv4_only" #define CMDLINE_WEBSOCKET_PASSWORD "websocket_password" #define CMDLINE_WEBSOCKET_DEBUG "websocket_debug" @@ -42,6 +43,7 @@ Config::Config() : FirstLoad(true), ServerEnabled(true), ServerPort(4455), + Ipv4Only(false), DebugEnabled(false), AlertsEnabled(false), AuthRequired(true), @@ -93,6 +95,12 @@ void Config::Load() } } + // Process `--websocket_ipv4_only` override + if (Utils::Platform::GetCommandLineFlagSet(CMDLINE_WEBSOCKET_IPV4_ONLY)) { + blog(LOG_INFO, "[Config::Load] --websocket_ipv4_only passed. Binding only to IPv4 interfaces."); + Ipv4Only = true; + } + // Process `--websocket_password` override QString passwordArgument = Utils::Platform::GetCommandLineArgument(CMDLINE_WEBSOCKET_PASSWORD); if (passwordArgument != "") { diff --git a/src/Config.h b/src/Config.h index beec3673..17533223 100644 --- a/src/Config.h +++ b/src/Config.h @@ -38,6 +38,7 @@ struct Config { std::atomic FirstLoad; std::atomic ServerEnabled; std::atomic ServerPort; + std::atomic Ipv4Only; std::atomic DebugEnabled; std::atomic AlertsEnabled; std::atomic AuthRequired; diff --git a/src/websocketserver/WebSocketServer.cpp b/src/websocketserver/WebSocketServer.cpp index 0219922d..2fa1c40f 100644 --- a/src/websocketserver/WebSocketServer.cpp +++ b/src/websocketserver/WebSocketServer.cpp @@ -129,7 +129,13 @@ void WebSocketServer::Start() _server.reset(); websocketpp::lib::error_code errorCode; - _server.listen(websocketpp::lib::asio::ip::tcp::v4(), conf->ServerPort, errorCode); + if (conf->Ipv4Only) { + blog(LOG_INFO, "[WebSocketServer::Start] Locked to IPv4 bindings"); + _server.listen(websocketpp::lib::asio::ip::tcp::v4(), conf->ServerPort, errorCode); + } else { + blog(LOG_INFO, "[WebSocketServer::Start] Not locked to IPv4 bindings"); + _server.listen(conf->ServerPort, errorCode); + } if (errorCode) { std::string errorCodeMessage = errorCode.message(); From 2479501879a3ed8777c75b32464df001fb665620 Mon Sep 17 00:00:00 2001 From: Christopher Sund Date: Sun, 16 Jan 2022 13:07:16 -0800 Subject: [PATCH 18/19] Requests: Add GetMonitorList Adds a new request `GetMonitorList` that returns a json Array of objects containing data about connected monitors. See #868 --- docs/README.md | 2 +- src/requesthandler/RequestHandler.cpp | 1 + src/requesthandler/RequestHandler.h | 1 + src/requesthandler/RequestHandler_Ui.cpp | 41 ++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 84f446d9..195d81a7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # obs-websocket documentation -This is the documentation for obs-websocket. Run build_docs.sh to auto generate the latest docs from the `src` directory. There are 3 components to the docs generation: +This is the documentation for obs-websocket. Run `build_docs.sh` to auto generate the latest docs from the `src` directory. There are 3 components to the docs generation: - `comments/comments.js`: Generates the `work/comments.json` file from the code comments in the src directory. - `docs/process_comments.py`: Processes `work/comments.json` to create `generated/protocol.json`, which is a machine-readable documentation format that can be used to create obs-websocket client libraries. - `docs/generate_md.py`: Processes `generated/protocol.json` to create `generated/protocol.md`, which is the actual human-readable documentation. diff --git a/src/requesthandler/RequestHandler.cpp b/src/requesthandler/RequestHandler.cpp index 251fc75a..19f9a79a 100644 --- a/src/requesthandler/RequestHandler.cpp +++ b/src/requesthandler/RequestHandler.cpp @@ -181,6 +181,7 @@ const std::unordered_map RequestHandler::_han {"OpenInputPropertiesDialog", &RequestHandler::OpenInputPropertiesDialog}, {"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog}, {"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog}, + {"GetMonitorList", &RequestHandler::GetMonitorList}, }; RequestHandler::RequestHandler(SessionPtr session) : diff --git a/src/requesthandler/RequestHandler.h b/src/requesthandler/RequestHandler.h index e0c77160..8df1e9e5 100644 --- a/src/requesthandler/RequestHandler.h +++ b/src/requesthandler/RequestHandler.h @@ -199,6 +199,7 @@ class RequestHandler { RequestResult OpenInputPropertiesDialog(const Request&); RequestResult OpenInputFiltersDialog(const Request&); RequestResult OpenInputInteractDialog(const Request&); + RequestResult GetMonitorList(const Request&); SessionPtr _session; static const std::unordered_map _handlerMap; diff --git a/src/requesthandler/RequestHandler_Ui.cpp b/src/requesthandler/RequestHandler_Ui.cpp index 12fa5d04..7aa2e277 100644 --- a/src/requesthandler/RequestHandler_Ui.cpp +++ b/src/requesthandler/RequestHandler_Ui.cpp @@ -17,6 +17,11 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ +#include +#include +#include +#include + #include "RequestHandler.h" /** @@ -148,3 +153,39 @@ RequestResult RequestHandler::OpenInputInteractDialog(const Request& request) return RequestResult::Success(); } + +/** + * Gets a list of connected monitors and information about them. + * + * @responseField monitors | Array | a list of detected monitors with some information + * + * @requestType GetMonitorList + * @complexity 2 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @category ui + * @api requests + */ +RequestResult RequestHandler::GetMonitorList(const Request&) +{ + json responseData; + std::vector monitorsData; + QList screensList = QGuiApplication::screens(); + for (int screenIndex = 0; screenIndex < screensList.size(); screenIndex++) + { + json screenData; + QScreen const* screen = screensList[screenIndex]; + std::stringstream nameAndIndex; + nameAndIndex << screen->name().toStdString(); + nameAndIndex << '(' << screenIndex << ')'; + screenData["monitorName"] = nameAndIndex.str(); + const QRect screenGeometry = screen->geometry(); + screenData["monitorWidth"] = screenGeometry.width(); + screenData["monitorHeight"] = screenGeometry.height(); + screenData["monitorPositionX"] = screenGeometry.x(); + screenData["monitorPositionY"] = screenGeometry.y(); + monitorsData.push_back(screenData); + } + responseData["monitors"] = monitorsData; + return RequestResult::Success(responseData); +} From 20e654186cf8dfabcdbcdfd599c6ee1609f2511a Mon Sep 17 00:00:00 2001 From: Github Actions <> Date: Tue, 26 Apr 2022 03:25:55 +0000 Subject: [PATCH 19/19] docs(ci): Update generated docs - f42cd21 [skip ci] --- docs/generated/protocol.json | 17 +++++++++++++++++ docs/generated/protocol.md | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/docs/generated/protocol.json b/docs/generated/protocol.json index 1a74e75e..b2ffb6e4 100644 --- a/docs/generated/protocol.json +++ b/docs/generated/protocol.json @@ -4237,6 +4237,23 @@ } ], "responseFields": [] + }, + { + "description": "Gets a list of connected monitors and information about them.", + "requestType": "GetMonitorList", + "complexity": 2, + "rpcVersion": "1", + "deprecated": false, + "initialVersion": "5.0.0", + "category": "ui", + "requestFields": [], + "responseFields": [ + { + "valueName": "monitors", + "valueType": "Array", + "valueDescription": "a list of detected monitors with some information" + } + ] } ], "events": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index e2ebeecf..4e84ca0f 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -2400,6 +2400,7 @@ Studio mode has been enabled or disabled. - [OpenInputPropertiesDialog](#openinputpropertiesdialog) - [OpenInputFiltersDialog](#openinputfiltersdialog) - [OpenInputInteractDialog](#openinputinteractdialog) + - [GetMonitorList](#getmonitorlist) @@ -4957,4 +4958,21 @@ Opens the interact dialog of an input. | ---- | :---: | ----------- | :----------------: | ----------------- | | inputName | String | Name of the input to open the dialog of | None | N/A | +--- + +### GetMonitorList + +Gets a list of connected monitors and information about them. + +- Complexity Rating: `2/5` +- Latest Supported RPC Version: `1` +- Added in v5.0.0 + + +**Response Fields:** + +| Name | Type | Description | +| ---- | :---: | ----------- | +| monitors | Array<Object> | a list of detected monitors with some information | +