diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index b545211f9..59979ec09 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -444,9 +444,15 @@ extern float obs_source_get_target_volume(obs_source_t *source, /* ------------------------------------------------------------------------- */ /* outputs */ +struct obs_weak_output { + struct obs_weak_ref ref; + struct obs_output *output; +}; + struct obs_output { struct obs_context_data context; struct obs_output_info info; + struct obs_weak_output *control; bool received_video; bool received_audio; @@ -495,6 +501,8 @@ extern const struct obs_output_info *find_output(const char *id); extern void obs_output_remove_encoder(struct obs_output *output, struct obs_encoder *encoder); +void obs_output_destroy(obs_output_t *output); + /* ------------------------------------------------------------------------- */ /* encoders */ diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 326add9eb..36c597ead 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -95,6 +95,9 @@ obs_output_t *obs_output_create(const char *id, const char *name, output->reconnect_retry_max = 20; output->valid = true; + output->control = bzalloc(sizeof(obs_weak_output_t)); + output->control->output = output; + obs_context_data_insert(&output->context, &obs->data.outputs_mutex, &obs->data.first_output); @@ -1273,3 +1276,78 @@ void obs_output_signal_stop(obs_output_t *output, int code) else signal_stop(output, code); } + +void obs_output_addref(obs_output_t *output) +{ + if (!output) + return; + + obs_ref_addref(&output->control->ref); +} + +void obs_output_release(obs_output_t *output) +{ + if (!output) + return; + + obs_weak_output_t *control = output->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_output_destroy(output); + obs_weak_output_release(control); + } +} + +void obs_weak_output_addref(obs_weak_output_t *weak) +{ + if (!weak) + return; + + obs_weak_ref_addref(&weak->ref); +} + +void obs_weak_output_release(obs_weak_output_t *weak) +{ + if (!weak) + return; + + if (obs_weak_ref_release(&weak->ref)) + bfree(weak); +} + +obs_output_t *obs_output_get_ref(obs_output_t *output) +{ + if (!output) + return NULL; + + return obs_weak_output_get_output(output->control); +} + +obs_weak_output_t *obs_output_get_weak_output(obs_output_t *output) +{ + if (!output) + return NULL; + + obs_weak_output_t *weak = output->control; + obs_weak_output_addref(weak); + return weak; +} + +obs_output_t *obs_weak_output_get_output(obs_weak_output_t *weak) +{ + if (!weak) + return NULL; + + if (obs_weak_ref_get_ref(&weak->ref)) + return weak->output; + + return NULL; +} + +bool obs_weak_output_references_output(obs_weak_output_t *weak, + obs_output_t *output) +{ + return weak && output && weak->output == output; +} diff --git a/libobs/obs.c b/libobs/obs.c index eeb0febfe..b2068662c 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -1184,6 +1184,11 @@ static inline void *get_context_by_name(void *vfirst, const char *name, return context; } +static inline void *obs_output_addref_safe_(void *ref) +{ + return obs_output_get_ref(ref); +} + static inline void *obs_id_(void *data) { return data; @@ -1193,7 +1198,7 @@ obs_output_t *obs_get_output_by_name(const char *name) { if (!obs) return NULL; return get_context_by_name(&obs->data.first_output, name, - &obs->data.outputs_mutex, obs_id_); + &obs->data.outputs_mutex, obs_output_addref_safe_); } obs_encoder_t *obs_get_encoder_by_name(const char *name) diff --git a/libobs/obs.h b/libobs/obs.h index 381e5fc27..14fbb57df 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -63,6 +63,7 @@ typedef struct obs_fader obs_fader_t; 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; #include "obs-source.h" #include "obs-encoder.h" @@ -1089,7 +1090,23 @@ EXPORT const char *obs_output_get_display_name(const char *id); */ EXPORT obs_output_t *obs_output_create(const char *id, const char *name, obs_data_t *settings); -EXPORT void obs_output_destroy(obs_output_t *output); + +/** + * Adds/releases a reference to an output. When the last reference is + * released, the output is destroyed. + */ +EXPORT void obs_output_addref(obs_output_t *output); +EXPORT void obs_output_release(obs_output_t *output); + +EXPORT void obs_weak_output_addref(obs_weak_output_t *weak); +EXPORT void obs_weak_output_release(obs_weak_output_t *weak); + +EXPORT obs_output_t *obs_output_get_ref(obs_output_t *output); +EXPORT obs_weak_output_t *obs_output_get_weak_output(obs_output_t *output); +EXPORT obs_output_t *obs_weak_output_get_output(obs_weak_output_t *weak); + +EXPORT bool obs_weak_output_references_output(obs_weak_output_t *weak, + obs_output_t *output); EXPORT const char *obs_output_get_name(const obs_output_t *output); diff --git a/libobs/obs.hpp b/libobs/obs.hpp index 6d8c5e267..312281cae 100644 --- a/libobs/obs.hpp +++ b/libobs/obs.hpp @@ -33,9 +33,12 @@ using OBSSceneItem = OBSRef; using OBSDataArray = OBSRef; +using OBSOutput = OBSRef; using OBSWeakSource = OBSRef; +using OBSWeakOutput = OBSRef; template class OBSRef { @@ -81,6 +84,9 @@ public: friend OBSSource OBSGetStrongRef(obs_weak_source_t *weak); friend OBSWeakSource OBSGetWeakRef(obs_source_t *source); + + friend OBSOutput OBSGetStrongRef(obs_weak_output_t *weak); + friend OBSWeakOutput OBSGetWeakRef(obs_output_t *output); }; inline OBSSource OBSGetStrongRef(obs_weak_source_t *weak) @@ -94,6 +100,17 @@ inline OBSWeakSource OBSGetWeakRef(obs_source_t *source) OBSWeakSource::TakeOwnership()}; } +inline OBSOutput OBSGetStrongRef(obs_weak_output_t *weak) +{ + return {obs_weak_output_get_output(weak), OBSOutput::TakeOwnership()}; +} + +inline OBSWeakOutput OBSGetWeakRef(obs_output_t *output) +{ + return {obs_output_get_weak_output(output), + OBSWeakOutput::TakeOwnership()}; +} + /* objects that are not meant to be instanced */ template class OBSObj { T obj; @@ -132,7 +149,6 @@ public: using OBSDisplay = OBSObj; using OBSEncoder = OBSObj; using OBSView = OBSObj; -using OBSOutput = OBSObj; /* signal handler connection */ class OBSSignal { diff --git a/obs/window-basic-main-outputs.cpp b/obs/window-basic-main-outputs.cpp index 993c9f74b..af316d1aa 100644 --- a/obs/window-basic-main-outputs.cpp +++ b/obs/window-basic-main-outputs.cpp @@ -68,11 +68,13 @@ SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_) nullptr); if (!streamOutput) throw "Failed to create stream output (simple output)"; + obs_output_release(streamOutput); fileOutput = obs_output_create("flv_output", "simple_file_output", nullptr); if (!fileOutput) throw "Failed to create recording output (simple output)"; + obs_output_release(fileOutput); h264 = obs_video_encoder_create("obs_x264", "simple_h264", nullptr); if (!h264) @@ -321,6 +323,7 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_) nullptr); if (!streamOutput) throw "Failed to create stream output (advanced output)"; + obs_output_release(streamOutput); if (ffmpegRecording) { fileOutput = obs_output_create("ffmpeg_output", @@ -328,12 +331,14 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_) if (!fileOutput) throw "Failed to create recording FFmpeg output " "(advanced output)"; + obs_output_release(fileOutput); } else { fileOutput = obs_output_create("flv_output", "adv_file_output", nullptr); if (!fileOutput) throw "Failed to create recording output " "(advanced output)"; + obs_output_release(fileOutput); if (!useStreamEncoder) { h264Recording = obs_video_encoder_create(recordEncoder,