diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index ee115c756..101eece0f 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -586,9 +586,15 @@ void obs_encoder_destroy(obs_encoder_t *encoder); /* ------------------------------------------------------------------------- */ /* services */ +struct obs_weak_service { + struct obs_weak_ref ref; + struct obs_service *service; +}; + struct obs_service { struct obs_context_data context; struct obs_service_info info; + struct obs_weak_service *control; bool active; bool destroy; @@ -601,3 +607,6 @@ extern void obs_service_activate(struct obs_service *service); extern void obs_service_deactivate(struct obs_service *service, bool remove); extern bool obs_service_initialize(struct obs_service *service, struct obs_output *output); + +void obs_service_destroy(obs_service_t *service); + diff --git a/libobs/obs-service.c b/libobs/obs-service.c index 222513423..7b63cf4eb 100644 --- a/libobs/obs-service.c +++ b/libobs/obs-service.c @@ -60,6 +60,9 @@ obs_service_t *obs_service_create(const char *id, const char *name, return NULL; } + service->control = bzalloc(sizeof(obs_weak_service_t)); + service->control->service = service; + obs_context_data_insert(&service->context, &obs->data.services_mutex, &obs->data.first_service); @@ -247,3 +250,78 @@ void obs_service_apply_encoder_settings(obs_service_t *service, service->info.apply_encoder_settings(service->context.data, video_encoder_settings, audio_encoder_settings); } + +void obs_service_addref(obs_service_t *service) +{ + if (!service) + return; + + obs_ref_addref(&service->control->ref); +} + +void obs_service_release(obs_service_t *service) +{ + if (!service) + return; + + obs_weak_service_t *control = service->control; + if (obs_ref_release(&control->ref)) { + // The order of operations is important here since + // get_context_by_name in obs.c relies on weak refs + // being alive while the context is listed + obs_service_destroy(service); + obs_weak_service_release(control); + } +} + +void obs_weak_service_addref(obs_weak_service_t *weak) +{ + if (!weak) + return; + + obs_weak_ref_addref(&weak->ref); +} + +void obs_weak_service_release(obs_weak_service_t *weak) +{ + if (!weak) + return; + + if (obs_weak_ref_release(&weak->ref)) + bfree(weak); +} + +obs_service_t *obs_service_get_ref(obs_service_t *service) +{ + if (!service) + return NULL; + + return obs_weak_service_get_service(service->control); +} + +obs_weak_service_t *obs_service_get_weak_service(obs_service_t *service) +{ + if (!service) + return NULL; + + obs_weak_service_t *weak = service->control; + obs_weak_service_addref(weak); + return weak; +} + +obs_service_t *obs_weak_service_get_service(obs_weak_service_t *weak) +{ + if (!weak) + return NULL; + + if (obs_weak_ref_get_ref(&weak->ref)) + return weak->service; + + return NULL; +} + +bool obs_weak_service_references_service(obs_weak_service_t *weak, + obs_service_t *service) +{ + return weak && service && weak->service == service; +} diff --git a/libobs/obs.c b/libobs/obs.c index d2feb51dd..142d1bbd0 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -1194,6 +1194,11 @@ static inline void *obs_encoder_addref_safe_(void *ref) return obs_encoder_get_ref(ref); } +static inline void *obs_service_addref_safe_(void *ref) +{ + return obs_service_get_ref(ref); +} + static inline void *obs_id_(void *data) { return data; @@ -1217,7 +1222,7 @@ obs_service_t *obs_get_service_by_name(const char *name) { if (!obs) return NULL; return get_context_by_name(&obs->data.first_service, name, - &obs->data.services_mutex, obs_id_); + &obs->data.services_mutex, obs_service_addref_safe_); } gs_effect_t *obs_get_default_effect(void) diff --git a/libobs/obs.h b/libobs/obs.h index d25a1eadd..230895709 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -65,6 +65,7 @@ typedef struct obs_volmeter obs_volmeter_t; typedef struct obs_weak_source obs_weak_source_t; typedef struct obs_weak_output obs_weak_output_t; typedef struct obs_weak_encoder obs_weak_encoder_t; +typedef struct obs_weak_service obs_weak_service_t; #include "obs-source.h" #include "obs-encoder.h" @@ -1433,7 +1434,23 @@ EXPORT const char *obs_service_get_display_name(const char *id); EXPORT obs_service_t *obs_service_create(const char *id, const char *name, obs_data_t *settings); -EXPORT void obs_service_destroy(obs_service_t *service); + +/** + * Adds/releases a reference to a service. When the last reference is + * released, the service is destroyed. + */ +EXPORT void obs_service_addref(obs_service_t *service); +EXPORT void obs_service_release(obs_service_t *service); + +EXPORT void obs_weak_service_addref(obs_weak_service_t *weak); +EXPORT void obs_weak_service_release(obs_weak_service_t *weak); + +EXPORT obs_service_t *obs_service_get_ref(obs_service_t *service); +EXPORT obs_weak_service_t *obs_service_get_weak_service(obs_service_t *service); +EXPORT obs_service_t *obs_weak_service_get_service(obs_weak_service_t *weak); + +EXPORT bool obs_weak_service_references_service(obs_weak_service_t *weak, + obs_service_t *service); EXPORT const char *obs_service_get_name(const obs_service_t *service); diff --git a/libobs/obs.hpp b/libobs/obs.hpp index 8a5dcf085..1db3eb385 100644 --- a/libobs/obs.hpp +++ b/libobs/obs.hpp @@ -36,6 +36,8 @@ using OBSDataArray = OBSRef; using OBSEncoder = OBSRef; +using OBSService = OBSRef; using OBSWeakSource = OBSRef; @@ -43,6 +45,8 @@ using OBSWeakOutput = OBSRef; using OBSWeakEncoder = OBSRef; +using OBSWeakService = OBSRef; template class OBSRef { @@ -94,6 +98,9 @@ public: friend OBSEncoder OBSGetStrongRef(obs_weak_encoder_t *weak); friend OBSWeakEncoder OBSGetWeakRef(obs_encoder_t *encoder); + + friend OBSService OBSGetStrongRef(obs_weak_service_t *weak); + friend OBSWeakService OBSGetWeakRef(obs_service_t *service); }; inline OBSSource OBSGetStrongRef(obs_weak_source_t *weak) @@ -130,6 +137,18 @@ inline OBSWeakEncoder OBSGetWeakRef(obs_encoder_t *encoder) OBSWeakEncoder::TakeOwnership()}; } +inline OBSService OBSGetStrongRef(obs_weak_service_t *weak) +{ + return {obs_weak_service_get_service(weak), + OBSService::TakeOwnership()}; +} + +inline OBSWeakService OBSGetWeakRef(obs_service_t *service) +{ + return {obs_service_get_weak_service(service), + OBSWeakService::TakeOwnership()}; +} + /* objects that are not meant to be instanced */ template class OBSObj { T obj; diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 052bbd144..2cd39506d 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -387,11 +387,6 @@ bool OBSBasic::LoadService() { const char *type; - if (service) { - obs_service_destroy(service); - service = nullptr; - } - char serviceJsonPath[512]; int ret = os_get_config_path(serviceJsonPath, sizeof(serviceJsonPath), SERVICE_PATH); @@ -410,6 +405,7 @@ bool OBSBasic::LoadService() obs_data_t *settings = obs_data_get_obj(data, "settings"); service = obs_service_create(type, "default_service", settings); + obs_service_release(service); obs_data_release(settings); obs_data_release(data); @@ -425,6 +421,7 @@ bool OBSBasic::InitService() service = obs_service_create("rtmp_common", "default_service", nullptr); if (!service) return false; + obs_service_release(service); return true; } @@ -690,6 +687,7 @@ OBSBasic::~OBSBasic() delete cpuUsageTimer; os_cpu_usage_info_destroy(cpuUsageInfo); + service = nullptr; outputHandler.reset(); if (interaction) @@ -1447,18 +1445,17 @@ void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy) obs_service_t *OBSBasic::GetService() { - if (!service) + if (!service) { service = obs_service_create("rtmp_common", NULL, NULL); + obs_service_release(service); + } return service; } void OBSBasic::SetService(obs_service_t *newService) { - if (newService) { - if (service) - obs_service_destroy(service); + if (newService) service = newService; - } } bool OBSBasic::StreamingActive() diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp index 5fa791662..3f6013498 100644 --- a/obs/window-basic-main.hpp +++ b/obs/window-basic-main.hpp @@ -82,7 +82,7 @@ private: QPointer cpuUsageTimer; os_cpu_usage_info_t *cpuUsageInfo = nullptr; - obs_service_t *service = nullptr; + OBSService service; std::unique_ptr outputHandler; gs_vertbuffer_t *box = nullptr;