From ab19104729ac81ebe6f89b80f70807d5deb6b06d Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 30 Dec 2021 01:07:33 -0800 Subject: [PATCH] Add obs-websocket 5.0.0 integration This adds integration into obs-websocket's new plugin API. The vendor name registered is `obs-browser`. Added vendor requests: - `emit_event` - Takes `event_name` and ?`event_data` parameters. Emits a custom event to all browser sources. --- CMakeLists.txt | 1 + README.md | 9 ++ deps/obs-websocket-api/.clang-format | 3 + deps/obs-websocket-api/obs-websocket-api.h | 135 +++++++++++++++++++++ obs-browser-plugin.cpp | 28 +++++ 5 files changed, 176 insertions(+) create mode 100644 deps/obs-websocket-api/.clang-format create mode 100644 deps/obs-websocket-api/obs-websocket-api.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 40f9dfe60..911127319 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,6 +131,7 @@ set(obs-browser_HEADERS browser-version.h deps/json11/json11.hpp deps/base64/base64.hpp + deps/obs-websocket-api/obs-websocket-api.h deps/wide-string.hpp cef-headers.hpp ) diff --git a/README.md b/README.md index 0a45dfb83..d928b869a 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ Descriptions for these events can be [found here](https://obsproject.com/docs/re * obsVirtualcamStarted * obsVirtualcamStopped * obsExit +* [Any custom event emitted via obs-websocket vendor requests] ### Control OBS @@ -346,6 +347,14 @@ window.obsstudio.onActiveChange = function(active) { }; ``` +### obs-websocket Vendor +obs-browser includes integration with obs-websocket's Vendor requests. The vendor name to use is `obs-browser`, and available requests are: + +- `emit_event` - Takes `event_name` and ?`event_data` parameters. Emits a custom event to all browser sources. To subscribe to events, see [here](#register-for-event-callbacks) + - See [#340](https://github.com/obsproject/obs-browser/pull/340) for example usage. + +There are no available vendor events at this time. + ## Building OBS Browser cannot be built standalone. It is built as part of OBS Studio. diff --git a/deps/obs-websocket-api/.clang-format b/deps/obs-websocket-api/.clang-format new file mode 100644 index 000000000..6420a4688 --- /dev/null +++ b/deps/obs-websocket-api/.clang-format @@ -0,0 +1,3 @@ +Language: Cpp +SortIncludes: false +DisableFormat: true diff --git a/deps/obs-websocket-api/obs-websocket-api.h b/deps/obs-websocket-api/obs-websocket-api.h new file mode 100644 index 000000000..749c00826 --- /dev/null +++ b/deps/obs-websocket-api/obs-websocket-api.h @@ -0,0 +1,135 @@ +/* +obs-websocket +Copyright (C) 2016-2021 Stephane Lepin +Copyright (C) 2020-2021 Kyle Manning + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see +*/ + +#ifndef _OBS_WEBSOCKET_API_H +#define _OBS_WEBSOCKET_API_H + +#include + +#define OBS_WEBSOCKET_API_VERSION 1 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* obs_websocket_vendor; +typedef void (*obs_websocket_request_callback_function)(obs_data_t*, obs_data_t*, void*); + +struct obs_websocket_request_callback { + obs_websocket_request_callback_function callback; + void *priv_data; +}; + +inline proc_handler_t *ph; + +static inline proc_handler_t *obs_websocket_get_ph(void) +{ + proc_handler_t *global_ph = obs_get_proc_handler(); + assert(global_ph != NULL); + + calldata_t cd = {0}; + if (!proc_handler_call(global_ph, "obs_websocket_api_get_ph", &cd)) + blog(LOG_DEBUG, "Unable to fetch obs-websocket proc handler object. obs-websocket not installed?"); + proc_handler_t *ret = (proc_handler_t*)calldata_ptr(&cd, "ph"); + calldata_free(&cd); + + return ret; +} + +static inline bool obs_websocket_run_simple_proc(obs_websocket_vendor vendor, const char *proc_name, calldata_t *cd) +{ + if (!ph || !vendor || !proc_name || !strlen(proc_name) || !cd) + return false; + + calldata_set_ptr(cd, "vendor", vendor); + + proc_handler_call(ph, proc_name, cd); + return calldata_bool(cd, "success"); +} + +// ALWAYS CALL VIA `obs_module_post_load()` CALLBACK! +// Registers a new "vendor" (Example: obs-ndi) +static inline obs_websocket_vendor obs_websocket_register_vendor(const char *vendor_name) +{ + ph = obs_websocket_get_ph(); + if (!ph) + return NULL; + + calldata_t cd = {0}; + + calldata_set_string(&cd, "name", vendor_name); + + proc_handler_call(ph, "vendor_register", &cd); + obs_websocket_vendor ret = calldata_ptr(&cd, "vendor"); + calldata_free(&cd); + + return ret; +} + +// Registers a new request for a vendor +static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor vendor, const char *request_type, obs_websocket_request_callback_function request_callback, void* priv_data) +{ + calldata_t cd = {0}; + + struct obs_websocket_request_callback cb = {}; + cb.callback = request_callback; + cb.priv_data = priv_data; + + calldata_set_string(&cd, "type", request_type); + calldata_set_ptr(&cd, "callback", &cb); + + bool success = obs_websocket_run_simple_proc(vendor, "vendor_request_register", &cd); + calldata_free(&cd); + + return success; +} + +// Unregisters an existing vendor request +static inline bool obs_websocket_vendor_unregister_request(obs_websocket_vendor vendor, const char *request_type) +{ + calldata_t cd = {0}; + + calldata_set_string(&cd, "type", request_type); + + bool success = obs_websocket_run_simple_proc(vendor, "vendor_request_unregister", &cd); + calldata_free(&cd); + + return success; +} + +// Does not affect event_data refcount. +// Emits an event under the vendor's name +static inline bool obs_websocket_vendor_emit_event(obs_websocket_vendor vendor, const char *event_name, obs_data_t *event_data) +{ + calldata_t cd = {0}; + + calldata_set_string(&cd, "type", event_name); + calldata_set_ptr(&cd, "data", (void*)event_data); + + bool success = obs_websocket_run_simple_proc(vendor, "vendor_event_emit", &cd); + calldata_free(&cd); + + return success; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/obs-browser-plugin.cpp b/obs-browser-plugin.cpp index 87d701004..736f1b51b 100644 --- a/obs-browser-plugin.cpp +++ b/obs-browser-plugin.cpp @@ -35,6 +35,7 @@ #include "browser-config.h" #include "json11/json11.hpp" +#include "obs-websocket-api/obs-websocket-api.h" #include "cef-headers.hpp" #ifdef _WIN32 @@ -736,6 +737,33 @@ bool obs_module_load(void) return true; } +void obs_module_post_load(void) +{ + auto vendor = obs_websocket_register_vendor("obs-browser"); + if (!vendor) + return; + + auto emit_event_request_cb = [](obs_data_t *request_data, obs_data_t *, + void *) { + const char *event_name = + obs_data_get_string(request_data, "event_name"); + if (!event_name) + return; + + OBSDataAutoRelease event_data = + obs_data_get_obj(request_data, "event_data"); + const char *event_data_string = + event_data ? obs_data_get_json(event_data) : "{}"; + + DispatchJSEvent(event_name, event_data_string, nullptr); + }; + + if (!obs_websocket_vendor_register_request( + vendor, "emit_event", emit_event_request_cb, nullptr)) + blog(LOG_WARNING, + "[obs-browser]: Failed to register obs-websocket request emit_event"); +} + void obs_module_unload(void) { #ifdef USE_QT_LOOP