Compare commits

...

2 Commits

Author SHA1 Message Date
WarmUpTill
5d568683f7 Update obs-websocket-api.h 2025-07-06 11:06:25 +02:00
WarmUpTill
a26c37021d Fix crash on OBS shutdown 2025-07-06 11:06:22 +02:00
4 changed files with 80 additions and 12 deletions

View File

@@ -22,7 +22,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <obs.h> #include <obs.h>
#define OBS_WEBSOCKET_API_VERSION 2 #define OBS_WEBSOCKET_API_VERSION 3
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -31,6 +31,8 @@ extern "C" {
typedef void *obs_websocket_vendor; typedef void *obs_websocket_vendor;
typedef void (*obs_websocket_request_callback_function)(obs_data_t *, typedef void (*obs_websocket_request_callback_function)(obs_data_t *,
obs_data_t *, void *); obs_data_t *, void *);
typedef void (*obs_websocket_event_callback_function)(uint64_t, const char *,
const char *, void *);
struct obs_websocket_request_response { struct obs_websocket_request_response {
unsigned int status_code; unsigned int status_code;
@@ -45,6 +47,11 @@ struct obs_websocket_request_callback {
void *priv_data; void *priv_data;
}; };
struct obs_websocket_event_callback {
obs_websocket_event_callback_function callback;
void *priv_data;
};
static proc_handler_t *_ph; static proc_handler_t *_ph;
/* ==================== INTERNAL API FUNCTIONS ==================== */ /* ==================== INTERNAL API FUNCTIONS ==================== */
@@ -123,7 +130,6 @@ obs_websocket_call_request(const char *request_type, obs_data_t *request_data
request_data_string = obs_data_get_json(request_data); request_data_string = obs_data_get_json(request_data);
calldata_t cd = {0, 0, 0, 0}; calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "request_type", request_type); calldata_set_string(&cd, "request_type", request_type);
calldata_set_string(&cd, "request_data", request_data_string); calldata_set_string(&cd, "request_data", request_data_string);
@@ -152,6 +158,48 @@ static inline void obs_websocket_request_response_free(
bfree(response); bfree(response);
} }
// Register an event handler to receive obs-websocket events
static inline bool obs_websocket_register_event_callback(
obs_websocket_event_callback_function event_callback, void *priv_data)
{
if (!obs_websocket_ensure_ph())
return false;
struct obs_websocket_event_callback cb = {event_callback, priv_data};
calldata_t cd = {0, 0, 0, 0};
calldata_set_ptr(&cd, "callback", &cb);
proc_handler_call(_ph, "register_event_callback", &cd);
bool ret = calldata_bool(&cd, "success");
calldata_free(&cd);
return ret;
}
// Unregister an existing event handler
static inline bool obs_websocket_unregister_event_callback(
obs_websocket_event_callback_function event_callback, void *priv_data)
{
if (!obs_websocket_ensure_ph())
return false;
struct obs_websocket_event_callback cb = {event_callback, priv_data};
calldata_t cd = {0, 0, 0, 0};
calldata_set_ptr(&cd, "callback", &cb);
proc_handler_call(_ph, "unregister_event_callback", &cd);
bool ret = calldata_bool(&cd, "success");
calldata_free(&cd);
return ret;
}
/* ==================== VENDOR API FUNCTIONS ==================== */ /* ==================== VENDOR API FUNCTIONS ==================== */
// ALWAYS CALL ONLY VIA `obs_module_post_load()` CALLBACK! // ALWAYS CALL ONLY VIA `obs_module_post_load()` CALLBACK!
@@ -163,7 +211,6 @@ obs_websocket_register_vendor(const char *vendor_name)
return NULL; return NULL;
calldata_t cd = {0, 0, 0, 0}; calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "name", vendor_name); calldata_set_string(&cd, "name", vendor_name);
proc_handler_call(_ph, "vendor_register", &cd); proc_handler_call(_ph, "vendor_register", &cd);
@@ -179,11 +226,10 @@ static inline bool obs_websocket_vendor_register_request(
obs_websocket_request_callback_function request_callback, obs_websocket_request_callback_function request_callback,
void *priv_data) void *priv_data)
{ {
calldata_t cd = {0, 0, 0, 0};
struct obs_websocket_request_callback cb = {request_callback, struct obs_websocket_request_callback cb = {request_callback,
priv_data}; priv_data};
calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "type", request_type); calldata_set_string(&cd, "type", request_type);
calldata_set_ptr(&cd, "callback", &cb); calldata_set_ptr(&cd, "callback", &cb);
@@ -200,7 +246,6 @@ obs_websocket_vendor_unregister_request(obs_websocket_vendor vendor,
const char *request_type) const char *request_type)
{ {
calldata_t cd = {0, 0, 0, 0}; calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "type", request_type); calldata_set_string(&cd, "type", request_type);
bool success = obs_websocket_vendor_run_simple_proc( bool success = obs_websocket_vendor_run_simple_proc(
@@ -217,7 +262,6 @@ static inline bool obs_websocket_vendor_emit_event(obs_websocket_vendor vendor,
obs_data_t *event_data) obs_data_t *event_data)
{ {
calldata_t cd = {0, 0, 0, 0}; calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "type", event_name); calldata_set_string(&cd, "type", event_name);
calldata_set_ptr(&cd, "data", (void *)event_data); calldata_set_ptr(&cd, "data", (void *)event_data);

View File

@@ -25,6 +25,14 @@ SwitcherData::SwitcherData(obs_module_t *m, translateFunc t)
_translate = t; _translate = t;
} }
SwitcherData::~SwitcherData()
{
// Assume that OBS is shutting down, as the switcher object will only be
// destroyed when the plugin is unloaded
obsIsShuttingDown = true;
Stop();
}
const char *SwitcherData::Translate(const char *text) const char *SwitcherData::Translate(const char *text)
{ {
return _translate(text); return _translate(text);

View File

@@ -45,6 +45,9 @@ std::unique_lock<std::mutex> *GetSwitcherLoopLock();
class SwitcherData { class SwitcherData {
public: public:
SwitcherData(obs_module_t *modulePtr, translateFunc translate);
~SwitcherData();
void Thread(); void Thread();
void Start(); void Start();
void Stop(); void Stop();
@@ -85,9 +88,6 @@ public:
/* --- End of saving / loading section --- */ /* --- End of saving / loading section --- */
SwitcherData(obs_module_t *m, translateFunc t);
inline ~SwitcherData() { Stop(); }
public: public:
SwitcherThread *th = nullptr; SwitcherThread *th = nullptr;
std::mutex m; std::mutex m;
@@ -102,7 +102,7 @@ public:
bool firstBoot = true; bool firstBoot = true;
bool transitionActive = false; bool transitionActive = false;
bool sceneColletionStop = false; bool sceneColletionStop = false;
bool obsIsShuttingDown = false; std::atomic_bool obsIsShuttingDown = {false};
bool firstInterval = true; bool firstInterval = true;
bool firstIntervalAfterStop = true; bool firstIntervalAfterStop = true;
bool startupLoadDone = false; bool startupLoadDone = false;

View File

@@ -117,9 +117,25 @@ void RegisterWebsocketRequest(
}); });
} }
static bool websocketModuleIsLoaded()
{
bool websocketModuleFound = false;
const auto checkModule = [](void *param, obs_module_t *module) {
bool *moduleFound = static_cast<bool *>(param);
auto name = obs_get_module_name(module);
if (name && strcmp(name, "obs-websocket") == 0) {
*moduleFound = true;
}
};
obs_enum_modules(checkModule, &websocketModuleFound);
return websocketModuleFound;
}
void SendWebsocketVendorEvent(const std::string &eventName, obs_data_t *data) void SendWebsocketVendorEvent(const std::string &eventName, obs_data_t *data)
{ {
if (OBSIsShuttingDown()) { // If obs-websocket was loaded, but was unloaded at some point any calls
// to obs_websocket_vendor_emit_event() will segfault
if (OBSIsShuttingDown() || !websocketModuleIsLoaded()) {
return; return;
} }
obs_websocket_vendor_emit_event(vendor, eventName.c_str(), data); obs_websocket_vendor_emit_event(vendor, eventName.c_str(), data);