From 6c85b335396b331655846e1be81cc6cda7fc2e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20L?= Date: Tue, 7 May 2019 22:53:07 +0200 Subject: [PATCH 1/4] WSEvents: source signal connect/disconnect methods --- src/WSEvents.cpp | 101 ++++++++++++++++++++++++++++++++++------------- src/WSEvents.h | 8 +++- 2 files changed, 79 insertions(+), 30 deletions(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index fdfbbc8a..ef2f67e1 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -247,6 +247,73 @@ void WSEvents::broadcastUpdate(const char* updateType, } } +void WSEvents::connectSourceSignals(obs_source_t* source) { + if (!source) { + return; + } + + // Disconnect everything first to avoid double-binding + disconnectSourceSignals(source); + + obs_source_type sourceType = obs_source_get_type(source); + signal_handler_t* sh = obs_source_get_signal_handler(source); + + signal_handler_connect(sh, "rename", OnSourceRename, this); + + signal_handler_connect(sh, "mute", OnSourceMuteStateChange, this); + signal_handler_connect(sh, "volume", OnSourceVolumeChange, this); + signal_handler_connect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this); + signal_handler_connect(sh, "audio_mixers", OnSourceAudioMixersChanged, this); + + signal_handler_connect(sh, "filter_add", OnSourceFilterAdded, this); + signal_handler_connect(sh, "filter_remove", OnSourceFilterRemoved, this); + signal_handler_connect(sh, "reorder_filters", OnSourceFilterOrderChanged, this); + + if (sourceType == OBS_SOURCE_TYPE_SCENE) { + signal_handler_connect(sh, "reorder", OnSceneReordered, this); + signal_handler_connect(sh, "item_add", OnSceneItemAdd, this); + signal_handler_connect(sh, "item_remove", OnSceneItemDelete, this); + signal_handler_connect(sh, + "item_visible", OnSceneItemVisibilityChanged, this); + signal_handler_connect(sh, "item_transform", OnSceneItemTransform, this); + signal_handler_connect(sh, "item_select", OnSceneItemSelected, this); + signal_handler_connect(sh, "item_deselect", OnSceneItemDeselected, this); + } + if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { + signal_handler_connect(sh, "transition_start", OnTransitionBegin, this); + } +} + +void WSEvents::disconnectSourceSignals(obs_source_t* source) { + if (!source) { + return; + } + + signal_handler_t* sh = obs_source_get_signal_handler(source); + + signal_handler_disconnect(sh, "rename", OnSourceRename, this); + + signal_handler_disconnect(sh, "mute", OnSourceMuteStateChange, this); + signal_handler_disconnect(sh, "volume", OnSourceVolumeChange, this); + signal_handler_disconnect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this); + signal_handler_disconnect(sh, "audio_mixers", OnSourceAudioMixersChanged, this); + + signal_handler_disconnect(sh, "filter_add", OnSourceFilterAdded, this); + signal_handler_disconnect(sh, "filter_remove", OnSourceFilterRemoved, this); + signal_handler_disconnect(sh, "reorder_filters", OnSourceFilterOrderChanged, this); + + signal_handler_disconnect(sh, "reorder", OnSceneReordered, this); + signal_handler_disconnect(sh, "item_add", OnSceneItemAdd, this); + signal_handler_disconnect(sh, "item_remove", OnSceneItemDelete, this); + signal_handler_disconnect(sh, + "item_visible", OnSceneItemVisibilityChanged, this); + signal_handler_disconnect(sh, "item_transform", OnSceneItemTransform, this); + signal_handler_disconnect(sh, "item_select", OnSceneItemSelected, this); + signal_handler_disconnect(sh, "item_deselect", OnSceneItemDeselected, this); + + signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this); +} + uint64_t WSEvents::GetStreamingTime() { if (obs_frontend_streaming_active()) return (os_gettime_ns() - _streamStarttime); @@ -801,39 +868,15 @@ void WSEvents::OnSourceCreate(void* param, calldata_t* data) { return; } - obs_source_type sourceType = obs_source_get_type(source); - signal_handler_t* sh = obs_source_get_signal_handler(source); - - signal_handler_connect(sh, "rename", OnSourceRename, self); - - signal_handler_connect(sh, "mute", OnSourceMuteStateChange, self); - signal_handler_connect(sh, "volume", OnSourceVolumeChange, self); - signal_handler_connect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, self); - signal_handler_connect(sh, "audio_mixers", OnSourceAudioMixersChanged, self); - - signal_handler_connect(sh, "filter_add", OnSourceFilterAdded, self); - signal_handler_connect(sh, "filter_remove", OnSourceFilterRemoved, self); - signal_handler_connect(sh, "reorder_filters", OnSourceFilterOrderChanged, self); - - if (sourceType == OBS_SOURCE_TYPE_SCENE) { - signal_handler_connect(sh, "reorder", OnSceneReordered, self); - signal_handler_connect(sh, "item_add", OnSceneItemAdd, self); - signal_handler_connect(sh, "item_remove", OnSceneItemDelete, self); - signal_handler_connect(sh, - "item_visible", OnSceneItemVisibilityChanged, self); - signal_handler_connect(sh, "item_transform", OnSceneItemTransform, self); - signal_handler_connect(sh, "item_select", OnSceneItemSelected, self); - signal_handler_connect(sh, "item_deselect", OnSceneItemDeselected, self); - } - if (sourceType == OBS_SOURCE_TYPE_TRANSITION) { - signal_handler_connect(sh, "transition_start", OnTransitionBegin, self); - } + self->connectSourceSignals(source); OBSDataAutoRelease sourceSettings = obs_source_get_settings(source); OBSDataAutoRelease fields = obs_data_create(); obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); - obs_data_set_string(fields, "sourceType", sourceTypeToString(sourceType)); + obs_data_set_string(fields, "sourceType", + sourceTypeToString(obs_source_get_type(source)) + ); obs_data_set_string(fields, "sourceKind", obs_source_get_id(source)); obs_data_set_obj(fields, "sourceSettings", sourceSettings); self->broadcastUpdate("SourceCreated", fields); @@ -859,6 +902,8 @@ void WSEvents::OnSourceDestroy(void* param, calldata_t* data) { return; } + self->disconnectSourceSignals(source); + obs_source_type sourceType = obs_source_get_type(source); OBSDataAutoRelease fields = obs_data_create(); diff --git a/src/WSEvents.h b/src/WSEvents.h index 5f1d5afc..c1a9c54f 100644 --- a/src/WSEvents.h +++ b/src/WSEvents.h @@ -42,8 +42,9 @@ public: explicit WSEvents(WSServerPtr srv); ~WSEvents(); - static void FrontendEventHandler( - enum obs_frontend_event event, void* privateData); + + void connectSourceSignals(obs_source_t* source); + void disconnectSourceSignals(obs_source_t* source); uint64_t GetStreamingTime(); const char* GetStreamingTimecode(); @@ -108,6 +109,9 @@ private: void OnExit(); + static void FrontendEventHandler( + enum obs_frontend_event event, void* privateData); + static void OnTransitionBegin(void* param, calldata_t* data); static void OnSourceCreate(void* param, calldata_t* data); From 3d9eac8e6da4bde197c7e2fd4a924d9b42fc066f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20L?= Date: Tue, 7 May 2019 22:58:20 +0200 Subject: [PATCH 2/4] WSEvents: connect to signals at construct and disconnect at destroy --- src/WSEvents.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index ef2f67e1..4582045e 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -114,6 +114,13 @@ WSEvents::WSEvents(WSServerPtr srv) : heartbeatTimer.start(STATUS_INTERVAL); + // Connect to signals of all existing sources + obs_enum_sources([](void* param, obs_source_t* source) { + auto self = reinterpret_cast(param); + self->connectSourceSignals(source); + return true; + }, this); + signal_handler_t* coreSignalHandler = obs_get_signal_handler(); if (coreSignalHandler) { signal_handler_connect(coreSignalHandler, "source_create", OnSourceCreate, this); @@ -128,6 +135,13 @@ WSEvents::~WSEvents() { signal_handler_disconnect(coreSignalHandler, "source_create", OnSourceCreate, this); } + // Disconnect from signals of all existing sources + obs_enum_sources([](void* param, obs_source_t* source) { + auto self = reinterpret_cast(param); + self->disconnectSourceSignals(source); + return true; + }, this); + obs_frontend_remove_event_callback(WSEvents::FrontendEventHandler, this); os_cpu_usage_info_destroy(cpuUsageInfo); } From e352d9750d60f24e6bada6ef9522b80b08c16e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20L?= Date: Tue, 7 May 2019 23:36:43 +0200 Subject: [PATCH 3/4] WSEvents: remove singleton + refactor pointer handling in plugin entrypoint --- src/WSEvents.cpp | 12 ++---------- src/WSEvents.h | 8 -------- src/WSRequestHandler_General.cpp | 13 +++++++------ src/WSRequestHandler_Streaming.cpp | 3 ++- src/obs-websocket.cpp | 30 ++++++++++++++++++------------ src/obs-websocket.h | 6 ++++++ 6 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 4582045e..f2f829f0 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -83,16 +83,6 @@ const char* calldata_get_string(const calldata_t* data, const char* name) { return value; } -WSEventsPtr WSEvents::_instance = WSEventsPtr(nullptr); - -WSEventsPtr WSEvents::Current() { - return _instance; -} - -void WSEvents::ResetCurrent(WSServerPtr srv) { - _instance = WSEventsPtr(new WSEvents(srv)); -} - WSEvents::WSEvents(WSServerPtr srv) : _srv(srv), _streamStarttime(0), @@ -144,6 +134,8 @@ WSEvents::~WSEvents() { obs_frontend_remove_event_callback(WSEvents::FrontendEventHandler, this); os_cpu_usage_info_destroy(cpuUsageInfo); + + blog(LOG_INFO, "wsevents destroyed"); } void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private_data) { diff --git a/src/WSEvents.h b/src/WSEvents.h index c1a9c54f..9250dc9e 100644 --- a/src/WSEvents.h +++ b/src/WSEvents.h @@ -29,17 +29,11 @@ with this program. If not, see #include "WSServer.h" -class WSEvents; -typedef QSharedPointer WSEventsPtr; - class WSEvents : public QObject { Q_OBJECT public: - static WSEventsPtr Current(); - static void ResetCurrent(WSServerPtr srv); - explicit WSEvents(WSServerPtr srv); ~WSEvents(); @@ -60,8 +54,6 @@ private slots: void TransitionDurationChanged(int ms); private: - static WSEventsPtr _instance; - WSServerPtr _srv; QTimer streamStatusTimer; QTimer heartbeatTimer; diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index 29bcc8b4..4c4409f0 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -1,3 +1,4 @@ +#include "obs-websocket.h" #include "Config.h" #include "Utils.h" #include "WSEvents.h" @@ -114,7 +115,7 @@ HandlerResponse WSRequestHandler::HandleSetHeartbeat(WSRequestHandler* req) { return req->SendErrorResponse("Heartbeat parameter missing"); } - auto events = WSEvents::Current(); + auto events = GetEventsSystem(); events->HeartbeatIsActive = obs_data_get_bool(req->data, "enable"); OBSDataAutoRelease response = obs_data_create(); @@ -164,18 +165,18 @@ HandlerResponse WSRequestHandler::HandleGetFilenameFormatting(WSRequestHandler* /** * Get OBS stats (almost the same info as provided in OBS' stats window) - * + * * @return {OBSStats} `stats` OBS stats - * + * * @api requests * @name GetStats * @category general - * @since 4.6.0 + * @since 4.6.0 */ HandlerResponse WSRequestHandler::HandleGetStats(WSRequestHandler* req) { - OBSDataAutoRelease stats = WSEvents::Current()->GetStats(); + OBSDataAutoRelease stats = GetEventsSystem()->GetStats(); OBSDataAutoRelease response = obs_data_create(); obs_data_set_obj(response, "stats", stats); return req->SendOKResponse(response); -} \ No newline at end of file +} diff --git a/src/WSRequestHandler_Streaming.cpp b/src/WSRequestHandler_Streaming.cpp index ca517b11..bbbf4bb1 100644 --- a/src/WSRequestHandler_Streaming.cpp +++ b/src/WSRequestHandler_Streaming.cpp @@ -1,3 +1,4 @@ +#include "obs-websocket.h" #include "Utils.h" #include "WSEvents.h" @@ -20,7 +21,7 @@ * @since 0.3 */ HandlerResponse WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req) { - auto events = WSEvents::Current(); + auto events = GetEventsSystem(); OBSDataAutoRelease data = obs_data_create(); obs_data_set_bool(data, "streaming", obs_frontend_streaming_active()); diff --git a/src/obs-websocket.cpp b/src/obs-websocket.cpp index 615d0782..1018efe2 100644 --- a/src/obs-websocket.cpp +++ b/src/obs-websocket.cpp @@ -38,7 +38,9 @@ void ___output_dummy_addref(obs_output_t*) {} OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US") -SettingsDialog* settings_dialog; +WSEventsPtr _eventsSystem; + +std::unique_ptr _settingsDialog; bool obs_module_load(void) { blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION); @@ -50,26 +52,27 @@ bool obs_module_load(void) { config->MigrateFromGlobalSettings(); // TODO remove this on the next minor jump config->Load(); - WSEvents::ResetCurrent(WSServer::Current()); + _eventsSystem = WSEventsPtr(new WSEvents(WSServer::Current())); if (config->ServerEnabled) { WSServer::Current()->start(config->ServerPort); } // UI setup - QAction* menu_action = (QAction*)obs_frontend_add_tools_menu_qaction( - obs_module_text("OBSWebsocket.Menu.SettingsItem") - ); - obs_frontend_push_ui_translation(obs_module_get_string); - QMainWindow* main_window = (QMainWindow*)obs_frontend_get_main_window(); - settings_dialog = new SettingsDialog(main_window); + QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window(); + _settingsDialog = std::unique_ptr( + new SettingsDialog(mainWindow) + ); obs_frontend_pop_ui_translation(); - auto menu_cb = [] { - settings_dialog->ToggleShowHide(); - }; - menu_action->connect(menu_action, &QAction::triggered, menu_cb); + const char* menuActionText = + obs_module_text("OBSWebsocket.Menu.SettingsItem"); + QAction* menuAction = + (QAction*)obs_frontend_add_tools_menu_qaction(menuActionText); + QObject::connect(menuAction, &QAction::triggered, [] { + _settingsDialog->ToggleShowHide(); + }); // Loading finished blog(LOG_INFO, "module loaded!"); @@ -82,3 +85,6 @@ void obs_module_unload() { blog(LOG_INFO, "goodbye!"); } +WSEventsPtr GetEventsSystem() { + return _eventsSystem; +} diff --git a/src/obs-websocket.h b/src/obs-websocket.h index 08aed786..692426f6 100644 --- a/src/obs-websocket.h +++ b/src/obs-websocket.h @@ -19,6 +19,7 @@ with this program. If not, see #pragma once #include +#include void ___source_dummy_addref(obs_source_t*); void ___sceneitem_dummy_addref(obs_sceneitem_t*); @@ -37,6 +38,11 @@ using OBSDataArrayAutoRelease = using OBSOutputAutoRelease = OBSRef; +class WSEvents; +typedef std::shared_ptr WSEventsPtr; + +std::shared_ptr GetEventsSystem(); + #define OBS_WEBSOCKET_VERSION "4.6.0" #define blog(level, msg, ...) blog(level, "[obs-websocket] " msg, ##__VA_ARGS__) From 7fce69457767bb168b04205d3b661e9e1122d7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20L?= Date: Tue, 7 May 2019 23:40:30 +0200 Subject: [PATCH 4/4] main: global settings dialog instance not required --- src/obs-websocket.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/obs-websocket.cpp b/src/obs-websocket.cpp index 1018efe2..852345bc 100644 --- a/src/obs-websocket.cpp +++ b/src/obs-websocket.cpp @@ -40,8 +40,6 @@ OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US") WSEventsPtr _eventsSystem; -std::unique_ptr _settingsDialog; - bool obs_module_load(void) { blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION); blog(LOG_INFO, "qt version (compile-time): %s ; qt version (run-time): %s", @@ -61,17 +59,17 @@ bool obs_module_load(void) { // UI setup obs_frontend_push_ui_translation(obs_module_get_string); QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window(); - _settingsDialog = std::unique_ptr( - new SettingsDialog(mainWindow) - ); + SettingsDialog* settingsDialog = new SettingsDialog(mainWindow); obs_frontend_pop_ui_translation(); const char* menuActionText = obs_module_text("OBSWebsocket.Menu.SettingsItem"); QAction* menuAction = (QAction*)obs_frontend_add_tools_menu_qaction(menuActionText); - QObject::connect(menuAction, &QAction::triggered, [] { - _settingsDialog->ToggleShowHide(); + QObject::connect(menuAction, &QAction::triggered, [settingsDialog] { + // The settings dialog belongs to the main window. Should be ok + // to pass the pointer to this QAction belonging to the main window + settingsDialog->ToggleShowHide(); }); // Loading finished