diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 9f4158534..b310b3f48 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -167,6 +167,7 @@ set(libobs_libobs_SOURCES ${libobs_PLATFORM_SOURCES} obs-avc.c obs-encoder.c + obs-service.c obs-source.c obs-output.c obs.c diff --git a/libobs/obs-display.c b/libobs/obs-display.c index 88282730c..96c57d64f 100644 --- a/libobs/obs-display.c +++ b/libobs/obs-display.c @@ -58,7 +58,11 @@ obs_display_t obs_display_create(struct gs_init_data *graphics_data) display = NULL; } else { pthread_mutex_lock(&obs->data.displays_mutex); - da_push_back(obs->data.displays, &display); + display->prev_next = &obs->data.first_display; + display->next = obs->data.first_display; + obs->data.first_display = display; + if (display->next) + display->next->prev_next = &display->next; pthread_mutex_unlock(&obs->data.displays_mutex); } @@ -82,7 +86,9 @@ void obs_display_destroy(obs_display_t display) { if (display) { pthread_mutex_lock(&obs->data.displays_mutex); - da_erase_item(obs->data.displays, &display); + *display->prev_next = display->next; + if (display->next) + display->next->prev_next = display->prev_next; pthread_mutex_unlock(&obs->data.displays_mutex); gs_entercontext(obs_graphics()); diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index ee4056343..9e7347f32 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -42,20 +42,16 @@ static bool init_encoder(struct obs_encoder *encoder, const char *name, pthread_mutex_init_value(&encoder->callbacks_mutex); pthread_mutex_init_value(&encoder->outputs_mutex); + if (!obs_context_data_init(&encoder->context, settings, name)) + return false; if (pthread_mutex_init(&encoder->callbacks_mutex, NULL) != 0) return false; if (pthread_mutex_init(&encoder->outputs_mutex, NULL) != 0) return false; - encoder->settings = obs_data_newref(settings); if (encoder->info.defaults) - encoder->info.defaults(encoder->settings); + encoder->info.defaults(encoder->context.settings); - pthread_mutex_lock(&obs->data.encoders_mutex); - da_push_back(obs->data.encoders, &encoder); - pthread_mutex_unlock(&obs->data.encoders_mutex); - - encoder->name = bstrdup(name); return true; } @@ -79,10 +75,14 @@ static struct obs_encoder *create_encoder(const char *id, success = init_encoder(encoder, name, settings); if (!success) { - bfree(encoder); + obs_encoder_destroy(encoder); encoder = NULL; } + obs_context_data_insert(&encoder->context, + &obs->data.encoders_mutex, + &obs->data.first_encoder); + return encoder; } @@ -123,7 +123,7 @@ static inline struct audio_convert_info *get_audio_info( memset(info, 0, sizeof(struct audio_convert_info)); if (encoder->info.audio_info) - encoder->info.audio_info(encoder->data, info); + encoder->info.audio_info(encoder->context.data, info); if (info->format == AUDIO_FORMAT_UNKNOWN) info->format = aoi->format; @@ -139,7 +139,7 @@ static inline struct video_scale_info *get_video_info( struct obs_encoder *encoder, struct video_scale_info *info) { if (encoder->info.video_info) - if (encoder->info.video_info(encoder->data, info)) + if (encoder->info.video_info(encoder->context.data, info)) return info; return NULL; @@ -199,13 +199,12 @@ static void obs_encoder_actually_destroy(obs_encoder_t encoder) free_audio_buffers(encoder); - if (encoder->data) - encoder->info.destroy(encoder->data); + if (encoder->context.data) + encoder->info.destroy(encoder->context.data); da_free(encoder->callbacks); - obs_data_release(encoder->settings); pthread_mutex_destroy(&encoder->callbacks_mutex); pthread_mutex_destroy(&encoder->outputs_mutex); - bfree(encoder->name); + obs_context_data_free(&encoder->context); bfree(encoder); } } @@ -217,9 +216,7 @@ void obs_encoder_destroy(obs_encoder_t encoder) if (encoder) { bool destroy; - pthread_mutex_lock(&obs->data.encoders_mutex); - da_erase_item(obs->data.encoders, &encoder); - pthread_mutex_unlock(&obs->data.encoders_mutex); + obs_context_data_remove(&encoder->context); pthread_mutex_lock(&encoder->callbacks_mutex); destroy = encoder->callbacks.num == 0; @@ -267,7 +264,7 @@ obs_properties_t obs_encoder_properties(obs_encoder_t encoder, if (encoder && encoder->info.properties) { obs_properties_t props; props = encoder->info.properties(locale); - obs_properties_apply_settings(props, encoder->settings); + obs_properties_apply_settings(props, encoder->context.settings); return props; } return NULL; @@ -277,17 +274,19 @@ void obs_encoder_update(obs_encoder_t encoder, obs_data_t settings) { if (!encoder) return; - obs_data_apply(encoder->settings, settings); - if (encoder->info.update && encoder->data) - encoder->info.update(encoder->data, encoder->settings); + obs_data_apply(encoder->context.settings, settings); + + if (encoder->info.update && encoder->context.data) + encoder->info.update(encoder->context.data, + encoder->context.settings); } bool obs_encoder_get_extra_data(obs_encoder_t encoder, uint8_t **extra_data, size_t *size) { - if (encoder && encoder->info.extra_data && encoder->data) - return encoder->info.extra_data(encoder->data, extra_data, - size); + if (encoder && encoder->info.extra_data && encoder->context.data) + return encoder->info.extra_data(encoder->context.data, + extra_data, size); return false; } @@ -296,8 +295,8 @@ obs_data_t obs_encoder_get_settings(obs_encoder_t encoder) { if (!encoder) return NULL; - obs_data_addref(encoder->settings); - return encoder->settings; + obs_data_addref(encoder->context.settings); + return encoder->context.settings; } static inline void reset_audio_buffers(struct obs_encoder *encoder) @@ -317,7 +316,7 @@ static void intitialize_audio_encoder(struct obs_encoder *encoder) encoder->samplerate = info.samples_per_sec; encoder->planes = get_audio_planes(info.format, info.speakers); encoder->blocksize = get_audio_size(info.format, info.speakers, 1); - encoder->framesize = encoder->info.frame_size(encoder->data); + encoder->framesize = encoder->info.frame_size(encoder->context.data); encoder->framesize_bytes = encoder->blocksize * encoder->framesize; reset_audio_buffers(encoder); @@ -330,11 +329,12 @@ bool obs_encoder_initialize(obs_encoder_t encoder) if (encoder->active) return true; - if (encoder->data) - encoder->info.destroy(encoder->data); + if (encoder->context.data) + encoder->info.destroy(encoder->context.data); - encoder->data = encoder->info.create(encoder->settings, encoder); - if (!encoder->data) + encoder->context.data = encoder->info.create(encoder->context.settings, + encoder); + if (!encoder->context.data) return false; encoder->paired_encoder = NULL; @@ -368,7 +368,7 @@ void obs_encoder_start(obs_encoder_t encoder, struct encoder_callback cb = {false, new_packet, param}; bool first = false; - if (!encoder || !new_packet || !encoder->data) return; + if (!encoder || !new_packet || !encoder->context.data) return; pthread_mutex_lock(&encoder->callbacks_mutex); @@ -434,7 +434,7 @@ static inline bool get_sei(struct obs_encoder *encoder, uint8_t **sei, size_t *size) { if (encoder->info.sei_data) - return encoder->info.sei_data(encoder->data, sei, size); + return encoder->info.sei_data(encoder->context.data, sei, size); return false; } @@ -500,11 +500,12 @@ static inline void do_encode(struct obs_encoder *encoder, pkt.timebase_num = encoder->timebase_num; pkt.timebase_den = encoder->timebase_den; - success = encoder->info.encode(encoder->data, frame, &pkt, &received); + success = encoder->info.encode(encoder->context.data, frame, &pkt, + &received); if (!success) { full_stop(encoder); blog(LOG_ERROR, "Error encoding with encoder '%s'", - encoder->name); + encoder->context.name); return; } diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 9993707bd..4a7b1597d 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -78,6 +78,9 @@ struct obs_display { swapchain_t swap; pthread_mutex_t draw_callbacks_mutex; DARRAY(struct draw_callback) draw_callbacks; + + struct obs_display *next; + struct obs_display **prev_next; }; extern bool obs_display_init(struct obs_display *display, @@ -133,17 +136,20 @@ struct obs_core_audio { /* user sources, output channels, and displays */ struct obs_core_data { - /* arrays of pointers jim? you should really stop being lazy and use - * linked lists. */ - DARRAY(struct obs_display*) displays; - DARRAY(struct obs_source*) sources; - DARRAY(struct obs_output*) outputs; - DARRAY(struct obs_encoder*) encoders; + pthread_mutex_t user_sources_mutex; + DARRAY(struct obs_source*) user_sources; + + struct obs_source *first_source; + struct obs_display *first_display; + struct obs_output *first_output; + struct obs_encoder *first_encoder; + struct obs_service *first_service; pthread_mutex_t sources_mutex; pthread_mutex_t displays_mutex; pthread_mutex_t outputs_mutex; pthread_mutex_t encoders_mutex; + pthread_mutex_t services_mutex; struct obs_view main_view; @@ -178,20 +184,42 @@ extern struct obs_core *obs; extern void *obs_video_thread(void *param); +/* ------------------------------------------------------------------------- */ +/* obs shared context data */ + +struct obs_context_data { + char *name; + void *data; + obs_data_t settings; + signal_handler_t signals; + proc_handler_t procs; + + pthread_mutex_t *mutex; + struct obs_context_data *next; + struct obs_context_data **prev_next; +}; + +extern bool obs_context_data_init( + struct obs_context_data *context, + obs_data_t settings, + const char *name); +extern void obs_context_data_free(struct obs_context_data *context); + +extern void obs_context_data_insert(struct obs_context_data *context, + pthread_mutex_t *mutex, void *first); +extern void obs_context_data_remove(struct obs_context_data *context); + +extern void obs_context_data_setname(struct obs_context_data *context, + const char *name); + + /* ------------------------------------------------------------------------- */ /* sources */ struct obs_source { - volatile long refs; + struct obs_context_data context; struct obs_source_info info; - - /* source-specific data */ - char *name; /* user-defined name */ - obs_data_t settings; - void *data; - - signal_handler_t signals; - proc_handler_t procs; + volatile long refs; /* signals to call the source update in the video thread */ bool defer_update; @@ -256,10 +284,13 @@ struct obs_source { bool rendering_filter; }; -bool obs_source_init_handlers(struct obs_source *source); +extern bool obs_source_init_context(struct obs_source *source, + obs_data_t settings, const char *name); extern bool obs_source_init(struct obs_source *source, const struct obs_source_info *info); +extern void obs_source_destroy(struct obs_source *source); + enum view_type { MAIN_VIEW, AUX_VIEW @@ -274,13 +305,8 @@ extern void obs_source_video_tick(obs_source_t source, float seconds); /* outputs */ struct obs_output { - char *name; - void *data; + struct obs_context_data context; struct obs_output_info info; - obs_data_t settings; - - signal_handler_t signals; - proc_handler_t procs; bool received_video; bool received_audio; @@ -319,10 +345,8 @@ struct encoder_callback { }; struct obs_encoder { - char *name; - void *data; + struct obs_context_data context; struct obs_encoder_info info; - obs_data_t settings; uint32_t samplerate; size_t planes; @@ -373,3 +397,11 @@ extern void obs_encoder_add_output(struct obs_encoder *encoder, struct obs_output *output); extern void obs_encoder_remove_output(struct obs_encoder *encoder, struct obs_output *output); + +/* ------------------------------------------------------------------------- */ +/* services */ + +struct obs_service { + struct obs_context_data context; + struct obs_service_info info; +}; diff --git a/libobs/obs-module.c b/libobs/obs-module.c index fd4cdbcec..6df1c7179 100644 --- a/libobs/obs-module.c +++ b/libobs/obs-module.c @@ -124,9 +124,9 @@ void obs_register_source_s(const struct obs_source_info *info, size_t size) struct obs_source_info data = {0}; struct darray *array; - CHECK_REQUIRED_VAL(info, getname, obs_register_source); - CHECK_REQUIRED_VAL(info, create, obs_register_source); - CHECK_REQUIRED_VAL(info, destroy, obs_register_source); + CHECK_REQUIRED_VAL(info, getname, obs_register_source); + CHECK_REQUIRED_VAL(info, create, obs_register_source); + CHECK_REQUIRED_VAL(info, destroy, obs_register_source); if (info->type == OBS_SOURCE_TYPE_INPUT && (info->output_flags & OBS_SOURCE_VIDEO) != 0 && @@ -177,10 +177,10 @@ void obs_register_output_s(const struct obs_output_info *info, size_t size) void obs_register_encoder_s(const struct obs_encoder_info *info, size_t size) { - CHECK_REQUIRED_VAL(info, getname, obs_register_encoder); - CHECK_REQUIRED_VAL(info, create, obs_register_encoder); - CHECK_REQUIRED_VAL(info, destroy, obs_register_encoder); - CHECK_REQUIRED_VAL(info, encode, obs_register_encoder); + CHECK_REQUIRED_VAL(info, getname, obs_register_encoder); + CHECK_REQUIRED_VAL(info, create, obs_register_encoder); + CHECK_REQUIRED_VAL(info, destroy, obs_register_encoder); + CHECK_REQUIRED_VAL(info, encode, obs_register_encoder); if (info->type == OBS_ENCODER_AUDIO) CHECK_REQUIRED_VAL(info, frame_size, obs_register_encoder); @@ -190,9 +190,11 @@ void obs_register_encoder_s(const struct obs_encoder_info *info, size_t size) void obs_register_service_s(const struct obs_service_info *info, size_t size) { - /* TODO */ - UNUSED_PARAMETER(size); - UNUSED_PARAMETER(info); + CHECK_REQUIRED_VAL(info, getname, obs_register_service); + CHECK_REQUIRED_VAL(info, create, obs_register_service); + CHECK_REQUIRED_VAL(info, destroy, obs_register_service); + + REGISTER_OBS_DEF(size, obs_service_info, obs->service_types, info); } void obs_regsiter_modal_ui_s(const struct obs_modal_ui *info, size_t size) diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 5224f67fb..6ef231d84 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -30,23 +30,25 @@ static inline const struct obs_output_info *find_output(const char *id) return NULL; } +const char *obs_output_getdisplayname(const char *id, const char *locale) +{ + const struct obs_output_info *info = find_output(id); + return (info != NULL) ? info->getname(locale) : NULL; +} + static const char *output_signals[] = { "void start(ptr output, int errorcode)", "void stop(ptr output)", NULL }; -static bool init_output_handlers(struct obs_output *output) +static bool init_output_handlers(struct obs_output *output, const char *name, + obs_data_t settings) { - output->signals = signal_handler_create(); - if (!output->signals) + if (!obs_context_data_init(&output->context, settings, name)) return false; - output->procs = proc_handler_create(); - if (!output->procs) - return false; - - signal_handler_add_array(output->signals, output_signals); + signal_handler_add_array(output->context.signals, output_signals); return true; } @@ -66,28 +68,25 @@ obs_output_t obs_output_create(const char *id, const char *name, if (pthread_mutex_init(&output->interleaved_mutex, NULL) != 0) goto fail; - if (!init_output_handlers(output)) + if (!init_output_handlers(output, name, settings)) goto fail; output->info = *info; output->video = obs_video(); output->audio = obs_audio(); - output->settings = obs_data_newref(settings); if (output->info.defaults) - output->info.defaults(output->settings); + output->info.defaults(output->context.settings); - output->data = info->create(output->settings, output); - if (!output->data) + output->context.data = info->create(output->context.settings, output); + if (!output->context.data) goto fail; - output->name = bstrdup(name); - - pthread_mutex_lock(&obs->data.outputs_mutex); - da_push_back(obs->data.outputs, &output); - pthread_mutex_unlock(&obs->data.outputs_mutex); - output->valid = true; + obs_context_data_insert(&output->context, + &obs->data.outputs_mutex, + &obs->data.first_output); + return output; fail: @@ -105,39 +104,32 @@ static inline void free_packets(struct obs_output *output) void obs_output_destroy(obs_output_t output) { if (output) { - if (output->valid) { - if (output->active) - output->info.stop(output->data); + obs_context_data_remove(&output->context); - pthread_mutex_lock(&obs->data.outputs_mutex); - da_erase_item(obs->data.outputs, &output); - pthread_mutex_unlock(&obs->data.outputs_mutex); - } + if (output->valid && output->active) + output->info.stop(output->context.data); free_packets(output); - if (output->data) - output->info.destroy(output->data); + if (output->context.data) + output->info.destroy(output->context.data); - signal_handler_destroy(output->signals); - proc_handler_destroy(output->procs); - - obs_data_release(output->settings); pthread_mutex_destroy(&output->interleaved_mutex); - bfree(output->name); + obs_context_data_free(&output->context); bfree(output); } } bool obs_output_start(obs_output_t output) { - return (output != NULL) ? output->info.start(output->data) : false; + return (output != NULL) ? + output->info.start(output->context.data) : false; } void obs_output_stop(obs_output_t output) { if (output) { - output->info.stop(output->data); + output->info.stop(output->context.data); signal_stop(output, OBS_OUTPUT_SUCCESS); } } @@ -181,7 +173,7 @@ obs_properties_t obs_output_properties(obs_output_t output, const char *locale) if (output && output->info.properties) { obs_properties_t props; props = output->info.properties(locale); - obs_properties_apply_settings(props, output->settings); + obs_properties_apply_settings(props, output->context.settings); return props; } @@ -192,10 +184,11 @@ void obs_output_update(obs_output_t output, obs_data_t settings) { if (!output) return; - obs_data_apply(output->settings, settings); + obs_data_apply(output->context.settings, settings); if (output->info.update) - output->info.update(output->data, output->settings); + output->info.update(output->context.data, + output->context.settings); } obs_data_t obs_output_get_settings(obs_output_t output) @@ -203,8 +196,8 @@ obs_data_t obs_output_get_settings(obs_output_t output) if (!output) return NULL; - obs_data_addref(output->settings); - return output->settings; + obs_data_addref(output->context.settings); + return output->context.settings; } bool obs_output_canpause(obs_output_t output) @@ -215,17 +208,17 @@ bool obs_output_canpause(obs_output_t output) void obs_output_pause(obs_output_t output) { if (output && output->info.pause) - output->info.pause(output->data); + output->info.pause(output->context.data); } signal_handler_t obs_output_signalhandler(obs_output_t output) { - return output ? output->signals : NULL; + return output ? output->context.signals : NULL; } proc_handler_t obs_output_prochandler(obs_output_t output) { - return output ? output->procs : NULL; + return output ? output->context.procs : NULL; } void obs_output_set_media(obs_output_t output, video_t video, audio_t audio) @@ -396,7 +389,7 @@ static inline void send_interleaved(struct obs_output *output) struct encoder_packet out = output->interleaved_packets.array[0]; da_erase(output->interleaved_packets, 0); - output->info.encoded_packet(output->data, &out); + output->info.encoded_packet(output->context.data, &out); obs_free_encoder_packet(&out); } @@ -446,7 +439,8 @@ static void hook_data_capture(struct obs_output *output, bool encoded, encoded_callback = (has_video && has_audio) ? interleave_packets : output->info.encoded_packet; - param = (has_video && has_audio) ? output : output->data; + param = (has_video && has_audio) ? + output : output->context.data; if (has_video) obs_encoder_start(output->video_encoder, @@ -458,11 +452,13 @@ static void hook_data_capture(struct obs_output *output, bool encoded, if (has_video) video_output_connect(output->video, get_video_conversion(output), - output->info.raw_video, output->data); + output->info.raw_video, + output->context.data); if (has_audio) audio_output_connect(output->audio, get_audio_conversion(output), - output->info.raw_audio, output->data); + output->info.raw_audio, + output->context.data); } } @@ -470,7 +466,7 @@ static inline void signal_start(struct obs_output *output) { struct calldata params = {0}; calldata_setptr(¶ms, "output", output); - signal_handler_signal(output->signals, "start", ¶ms); + signal_handler_signal(output->context.signals, "start", ¶ms); calldata_free(¶ms); } @@ -479,7 +475,7 @@ static inline void signal_stop(struct obs_output *output, int code) struct calldata params = {0}; calldata_setint(¶ms, "errorcode", code); calldata_setptr(¶ms, "output", output); - signal_handler_signal(output->signals, "stop", ¶ms); + signal_handler_signal(output->context.signals, "stop", ¶ms); calldata_free(¶ms); } @@ -566,7 +562,8 @@ void obs_output_end_data_capture(obs_output_t output) if (encoded) { encoded_callback = (has_video && has_audio) ? interleave_packets : output->info.encoded_packet; - param = (has_video && has_audio) ? output : output->data; + param = (has_video && has_audio) ? + output : output->context.data; if (has_video) obs_encoder_stop(output->video_encoder, @@ -577,10 +574,12 @@ void obs_output_end_data_capture(obs_output_t output) } else { if (has_video) video_output_disconnect(output->video, - output->info.raw_video, output->data); + output->info.raw_video, + output->context.data); if (has_audio) audio_output_disconnect(output->audio, - output->info.raw_audio, output->data); + output->info.raw_audio, + output->context.data); } output->active = false; diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index fe7c8e146..fdb5a6ded 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -25,8 +25,8 @@ static inline void signal_item_remove(struct obs_scene_item *item) calldata_setptr(¶ms, "scene", item->parent); calldata_setptr(¶ms, "item", item); - signal_handler_signal(item->parent->source->signals, "item_remove", - ¶ms); + signal_handler_signal(item->parent->source->context.signals, + "item_remove", ¶ms); calldata_free(¶ms); } @@ -205,35 +205,28 @@ static const char *obs_scene_signals[] = { NULL }; -void source_init_name(struct obs_source *source, const char *name); - obs_scene_t obs_scene_create(const char *name) { struct obs_source *source = bzalloc(sizeof(struct obs_source)); struct obs_scene *scene; - if (!obs_source_init_handlers(source)) { + if (!obs_source_init_context(source, NULL, name)) { bfree(source); return NULL; } - signal_handler_add_array(source->signals, obs_scene_signals); + signal_handler_add_array(source->context.signals, obs_scene_signals); - source->settings = obs_data_create(); - scene = scene_create(source->settings, source); - source->data = scene; + scene = scene_create(source->context.settings, source); + source->context.data = scene; assert(scene); if (!scene) { - obs_data_release(source->settings); - proc_handler_destroy(source->procs); - signal_handler_destroy(source->signals); + obs_context_data_free(&source->context); bfree(source); return NULL; } - source_init_name(source, name); - scene->source = source; obs_source_init(source, &scene_info); memcpy(&source->info, &scene_info, sizeof(struct obs_source_info)); @@ -262,7 +255,7 @@ obs_scene_t obs_scene_fromsource(obs_source_t source) if (!source || source->info.type != OBS_SOURCE_TYPE_SCENE) return NULL; - return source->data; + return source->context.data; } obs_sceneitem_t obs_scene_findsource(obs_scene_t scene, const char *name) @@ -276,7 +269,7 @@ obs_sceneitem_t obs_scene_findsource(obs_scene_t scene, const char *name) item = scene->first_item; while (item) { - if (strcmp(item->source->name, name) == 0) + if (strcmp(item->source->context.name, name) == 0) break; item = item->next; @@ -358,7 +351,8 @@ obs_sceneitem_t obs_scene_add(obs_scene_t scene, obs_source_t source) calldata_setptr(¶ms, "scene", scene); calldata_setptr(¶ms, "item", item); - signal_handler_signal(scene->source->signals, "item_add", ¶ms); + signal_handler_signal(scene->source->context.signals, "item_add", + ¶ms); calldata_free(¶ms); return item; diff --git a/libobs/obs-service.c b/libobs/obs-service.c new file mode 100644 index 000000000..90e00ca0e --- /dev/null +++ b/libobs/obs-service.c @@ -0,0 +1,158 @@ +/****************************************************************************** + Copyright (C) 2014 by Hugh Bailey + + 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 . +******************************************************************************/ + +#include "obs-internal.h" + +static inline const struct obs_service_info *find_service(const char *id) +{ + size_t i; + for (i = 0; i < obs->service_types.num; i++) + if (strcmp(obs->service_types.array[i].id, id) == 0) + return obs->service_types.array+i; + + return NULL; +} + +const char *obs_service_getdisplayname(const char *id, const char *locale) +{ + const struct obs_service_info *info = find_service(id); + return (info != NULL) ? info->getname(locale) : NULL; +} + +obs_service_t obs_service_create(const char *id, const char *name, + obs_data_t settings) +{ + const struct obs_service_info *info = find_service(id); + struct obs_service *service; + + if (!info) { + blog(LOG_ERROR, "Service '%s' not found", id); + return NULL; + } + + service = bzalloc(sizeof(struct obs_service)); + + if (!obs_context_data_init(&service->context, settings, name)) { + bfree(service); + return NULL; + } + + service->info = *info; + + obs_context_data_insert(&service->context, + &obs->data.services_mutex, + &obs->data.first_service); + + return service; +} + +void obs_service_destroy(obs_service_t service) +{ + if (service) { + obs_context_data_remove(&service->context); + + if (service->context.data) + service->info.destroy(service->context.data); + + obs_context_data_free(&service->context); + bfree(service); + } +} + +static inline obs_data_t get_defaults(const struct obs_service_info *info) +{ + obs_data_t settings = obs_data_create(); + if (info->defaults) + info->defaults(settings); + return settings; +} + +obs_data_t obs_service_defaults(const char *id) +{ + const struct obs_service_info *info = find_service(id); + return (info) ? get_defaults(info) : NULL; +} + +obs_properties_t obs_get_service_properties(const char *id, const char *locale) +{ + const struct obs_service_info *info = find_service(id); + if (info && info->properties) { + obs_data_t defaults = get_defaults(info); + obs_properties_t properties; + + properties = info->properties(locale); + obs_properties_apply_settings(properties, defaults); + obs_data_release(defaults); + return properties; + } + return NULL; +} + +obs_properties_t obs_service_properties(obs_service_t service, + const char *locale) +{ + if (service && service->info.properties) { + obs_properties_t props; + props = service->info.properties(locale); + obs_properties_apply_settings(props, service->context.settings); + return props; + } + + return NULL; +} + +void obs_service_update(obs_service_t service, obs_data_t settings) +{ + if (!service) return; + + obs_data_apply(service->context.settings, settings); + + if (service->info.update) + service->info.update(service->context.data, + service->context.settings); +} + +obs_data_t obs_service_get_settings(obs_service_t service) +{ + if (!service) + return NULL; + + obs_data_addref(service->context.settings); + return service->context.settings; +} + +signal_handler_t obs_service_signalhandler(obs_service_t service) +{ + return service ? service->context.signals : NULL; +} + +proc_handler_t obs_service_prochandler(obs_service_t service) +{ + return service ? service->context.procs : NULL; +} + +const char *obs_service_get_url(obs_service_t service) +{ + if (!service || !service->info.get_url) return NULL; + return service->info.get_url(service->context.data); +} + +const char *obs_service_get_key(obs_service_t service) +{ + if (!service || !service->info.get_key) return NULL; + return service->info.get_key(service->context.data); +} diff --git a/libobs/obs-service.h b/libobs/obs-service.h index 2eec5f645..383d7cc38 100644 --- a/libobs/obs-service.h +++ b/libobs/obs-service.h @@ -1,5 +1,5 @@ /****************************************************************************** - Copyright (C) 2013 by Hugh Bailey + Copyright (C) 2013-2014 by Hugh Bailey 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 @@ -19,7 +19,7 @@ struct obs_service_info { /* required */ - char *id; + const char *id; const char *(*getname)(const char *locale); void *(*create)(obs_data_t settings, obs_service_t service); @@ -28,9 +28,18 @@ struct obs_service_info { /* optional */ void (*update)(void *data, obs_data_t settings); - /* get stream url/key */ + void (*defaults)(obs_data_t settings); + + obs_properties_t (*properties)(const char *locale); + const char *(*get_url)(void *data); const char *(*get_key)(void *data); - /* send (current game/title/activate commercial/etc) */ + /* TODO: more stuff later */ }; + +EXPORT void obs_register_service_s(const struct obs_service_info *info, + size_t size); + +#define obs_register_service(info) \ + obs_register_service_s(info, sizeof(struct obs_service_info)) diff --git a/libobs/obs-source.c b/libobs/obs-source.c index b335a6761..8037208d3 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -28,8 +28,6 @@ #include "obs.h" #include "obs-internal.h" -static void obs_source_destroy(obs_source_t source); - static inline const struct obs_source_info *find_source(struct darray *list, const char *id) { @@ -84,17 +82,14 @@ static const char *source_signals[] = { NULL }; -bool obs_source_init_handlers(struct obs_source *source) +bool obs_source_init_context(struct obs_source *source, + obs_data_t settings, const char *name) { - source->signals = signal_handler_create(); - if (!source->signals) + if (!obs_context_data_init(&source->context, settings, name)) return false; - source->procs = proc_handler_create(); - if (!source->procs) - return false; - - return signal_handler_add_array(source->signals, source_signals); + return signal_handler_add_array(source->context.signals, + source_signals); } const char *obs_source_getdisplayname(enum obs_source_type type, @@ -127,14 +122,17 @@ bool obs_source_init(struct obs_source *source, if (info->output_flags & OBS_SOURCE_AUDIO) { source->audio_line = audio_output_createline(obs->audio.audio, - source->name); + source->context.name); if (!source->audio_line) { blog(LOG_ERROR, "Failed to create audio line for " - "source '%s'", source->name); + "source '%s'", source->context.name); return false; } } + obs_context_data_insert(&source->context, + &obs->data.sources_mutex, + &obs->data.first_source); return true; } @@ -148,22 +146,11 @@ static inline void obs_source_dosignal(struct obs_source *source, if (signal_obs) signal_handler_signal(obs->signals, signal_obs, &data); if (signal_source) - signal_handler_signal(source->signals, signal_source, &data); + signal_handler_signal(source->context.signals, signal_source, + &data); calldata_free(&data); } -void source_init_name(struct obs_source *source, const char *name) -{ - if (!name || !*name) { - struct dstr unnamed = {0}; - dstr_printf(&unnamed, "__unnamed%004lld", - obs->data.unnamed_index++); - source->name = unnamed.array; - } else { - source->name = bstrdup(name); - } -} - obs_source_t obs_source_create(enum obs_source_type type, const char *id, const char *name, obs_data_t settings) { @@ -177,17 +164,14 @@ obs_source_t obs_source_create(enum obs_source_type type, const char *id, source = bzalloc(sizeof(struct obs_source)); - if (!obs_source_init_handlers(source)) + if (!obs_source_init_context(source, settings, name)) goto fail; - source_init_name(source, name); - - source->settings = obs_data_newref(settings); if (info->defaults) - info->defaults(source->settings); + info->defaults(source->context.settings); - source->data = info->create(source->settings, source); - if (!source->data) + source->context.data = info->create(source->context.settings, source); + if (!source->context.data) goto fail; if (!obs_source_init(source, info)) @@ -221,13 +205,15 @@ void source_frame_init(struct source_frame *frame, enum video_format format, } } -static void obs_source_destroy(struct obs_source *source) +void obs_source_destroy(struct obs_source *source) { size_t i; if (!source) return; + obs_context_data_remove(&source->context); + obs_source_dosignal(source, "source_destroy", "destroy"); if (source->filter_parent) @@ -243,8 +229,8 @@ static void obs_source_destroy(struct obs_source *source) texture_destroy(source->async_texture); gs_leavecontext(); - if (source->data) - source->info.destroy(source->data); + if (source->context.data) + source->info.destroy(source->context.data); for (i = 0; i < MAX_AV_PLANES; i++) bfree(source->audio_data.data[i]); @@ -252,17 +238,13 @@ static void obs_source_destroy(struct obs_source *source) audio_line_destroy(source->audio_line); audio_resampler_destroy(source->resampler); - proc_handler_destroy(source->procs); - signal_handler_destroy(source->signals); - texrender_destroy(source->filter_texrender); da_free(source->video_frames); da_free(source->filters); pthread_mutex_destroy(&source->filter_mutex); pthread_mutex_destroy(&source->audio_mutex); pthread_mutex_destroy(&source->video_mutex); - obs_data_release(source->settings); - bfree(source->name); + obs_context_data_free(&source->context); bfree(source); } @@ -285,6 +267,7 @@ void obs_source_remove(obs_source_t source) { struct obs_core_data *data = &obs->data; size_t id; + bool exists; pthread_mutex_lock(&data->sources_mutex); @@ -297,15 +280,18 @@ void obs_source_remove(obs_source_t source) obs_source_addref(source); - id = da_find(data->sources, &source, 0); - if (id != DARRAY_INVALID) { - da_erase_item(data->sources, &source); + id = da_find(data->user_sources, &source, 0); + exists = (id != DARRAY_INVALID); + if (exists) { + da_erase(data->user_sources, id); obs_source_release(source); } pthread_mutex_unlock(&data->sources_mutex); - obs_source_dosignal(source, "source_remove", "remove"); + if (exists) + obs_source_dosignal(source, "source_remove", "remove"); + obs_source_release(source); } @@ -349,7 +335,7 @@ obs_properties_t obs_source_properties(obs_source_t source, const char *locale) if (source && source->info.properties) { obs_properties_t props; props = source->info.properties(locale); - obs_properties_apply_settings(props, source->settings); + obs_properties_apply_settings(props, source->context.settings); return props; } @@ -363,7 +349,7 @@ uint32_t obs_source_get_output_flags(obs_source_t source) static void obs_source_deferred_update(obs_source_t source) { - source->info.update(source->data, source->settings); + source->info.update(source->context.data, source->context.settings); source->defer_update = false; } @@ -371,41 +357,42 @@ void obs_source_update(obs_source_t source, obs_data_t settings) { if (!source) return; - obs_data_apply(source->settings, settings); + obs_data_apply(source->context.settings, settings); if (source->info.update) { if (source->info.output_flags & OBS_SOURCE_VIDEO) source->defer_update = true; else - source->info.update(source->data, source->settings); + source->info.update(source->context.data, + source->context.settings); } } static void activate_source(obs_source_t source) { if (source->info.activate) - source->info.activate(source->data); + source->info.activate(source->context.data); obs_source_dosignal(source, "source_activate", "activate"); } static void deactivate_source(obs_source_t source) { if (source->info.deactivate) - source->info.deactivate(source->data); + source->info.deactivate(source->context.data); obs_source_dosignal(source, "source_deactivate", "deactivate"); } static void show_source(obs_source_t source) { if (source->info.show) - source->info.show(source->data); + source->info.show(source->context.data); obs_source_dosignal(source, "source_show", "show"); } static void hide_source(obs_source_t source) { if (source->info.hide) - source->info.hide(source->data); + source->info.hide(source->context.data); obs_source_dosignal(source, "source_hide", "hide"); } @@ -494,7 +481,7 @@ void obs_source_video_tick(obs_source_t source, float seconds) texrender_reset(source->filter_texrender); if (source->info.video_tick) - source->info.video_tick(source->data, seconds); + source->info.video_tick(source->context.data, seconds); } /* unless the value is 3+ hours worth of frames, this won't overflow */ @@ -526,7 +513,7 @@ static inline void handle_ts_jump(obs_source_t source, uint64_t expected, { blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%"PRIu64"', " "expected value %"PRIu64", input value %"PRIu64, - source->name, diff, expected, ts); + source->context.name, diff, expected, ts); /* if has video, ignore audio data until reset */ if (source->info.output_flags & OBS_SOURCE_ASYNC) @@ -749,7 +736,7 @@ static inline void obs_source_default_render(obs_source_t source, passes = technique_begin(tech); for (i = 0; i < passes; i++) { technique_beginpass(tech, i); - source->info.video_render(source->data, effect); + source->info.video_render(source->context.data, effect); technique_endpass(tech); } technique_end(tech); @@ -767,7 +754,7 @@ static inline void obs_source_main_render(obs_source_t source) if (default_effect) obs_source_default_render(source, color_matrix); else - source->info.video_render(source->data, + source->info.video_render(source->context.data, custom_draw ? NULL : gs_geteffect()); } @@ -793,7 +780,7 @@ uint32_t obs_source_getwidth(obs_source_t source) if (!source) return 0; if (source->info.getwidth) - return source->info.getwidth(source->data); + return source->info.getwidth(source->context.data); return source->async_width; } @@ -802,7 +789,7 @@ uint32_t obs_source_getheight(obs_source_t source) if (!source) return 0; if (source->info.getheight) - return source->info.getheight(source->data); + return source->info.getheight(source->context.data); return source->async_height; } @@ -913,8 +900,8 @@ obs_data_t obs_source_getsettings(obs_source_t source) { if (!source) return NULL; - obs_data_addref(source->settings); - return source->settings; + obs_data_addref(source->context.settings); + return source->context.settings; } static inline struct source_frame *filter_async_video(obs_source_t source, @@ -924,7 +911,8 @@ static inline struct source_frame *filter_async_video(obs_source_t source, for (i = source->filters.num; i > 0; i--) { struct obs_source *filter = source->filters.array[i-1]; if (filter->info.filter_video) { - in = filter->info.filter_video(filter->data, in); + in = filter->info.filter_video(filter->context.data, + in); if (!in) return NULL; } @@ -1021,7 +1009,8 @@ static inline struct filtered_audio *filter_async_audio(obs_source_t source, for (i = source->filters.num; i > 0; i--) { struct obs_source *filter = source->filters.array[i-1]; if (filter->info.filter_audio) { - in = filter->info.filter_audio(filter->data, in); + in = filter->info.filter_audio(filter->context.data, + in); if (!in) return NULL; } @@ -1263,15 +1252,13 @@ void obs_source_releaseframe(obs_source_t source, struct source_frame *frame) const char *obs_source_getname(obs_source_t source) { - return source ? source->name : NULL; + return source ? source->context.name : NULL; } void obs_source_setname(obs_source_t source, const char *name) { if (!source) return; - - bfree(source->name); - source->name = bstrdup(name); + obs_context_data_setname(&source->context, name); } void obs_source_gettype(obs_source_t source, enum obs_source_type *type, @@ -1369,12 +1356,12 @@ void obs_source_process_filter(obs_source_t filter, effect_t effect, signal_handler_t obs_source_signalhandler(obs_source_t source) { - return source ? source->signals : NULL; + return source ? source->context.signals : NULL; } proc_handler_t obs_source_prochandler(obs_source_t source) { - return source ? source->procs : NULL; + return source ? source->context.procs : NULL; } void obs_source_setvolume(obs_source_t source, float volume) @@ -1384,7 +1371,7 @@ void obs_source_setvolume(obs_source_t source, float volume) calldata_setptr(&data, "source", source); calldata_setfloat(&data, "volume", volume); - signal_handler_signal(source->signals, "volume", &data); + signal_handler_signal(source->context.signals, "volume", &data); signal_handler_signal(obs->signals, "source_volume", &data); volume = (float)calldata_float(&data, "volume"); @@ -1451,7 +1438,7 @@ static void enum_source_tree_callback(obs_source_t parent, obs_source_t child, if (child->info.enum_sources && !child->enum_refs) { os_atomic_inc_long(&child->enum_refs); - child->info.enum_sources(child->data, + child->info.enum_sources(child->context.data, enum_source_tree_callback, data); os_atomic_dec_long(&child->enum_refs); @@ -1470,7 +1457,7 @@ void obs_source_enum_sources(obs_source_t source, obs_source_addref(source); os_atomic_inc_long(&source->enum_refs); - source->info.enum_sources(source->data, enum_callback, param); + source->info.enum_sources(source->context.data, enum_callback, param); os_atomic_dec_long(&source->enum_refs); obs_source_release(source); @@ -1488,7 +1475,8 @@ void obs_source_enum_tree(obs_source_t source, obs_source_addref(source); os_atomic_inc_long(&source->enum_refs); - source->info.enum_sources(source->data, enum_source_tree_callback, + source->info.enum_sources(source->context.data, + enum_source_tree_callback, &data); os_atomic_dec_long(&source->enum_refs); diff --git a/libobs/obs-video.c b/libobs/obs-video.c index 648d81fcb..405731330 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -22,20 +22,28 @@ static uint64_t tick_sources(uint64_t cur_time, uint64_t last_time) { - size_t i; - uint64_t delta_time; - float seconds; + struct obs_core_data *data = &obs->data; + struct obs_source *source; + uint64_t delta_time; + float seconds; if (!last_time) last_time = cur_time - video_getframetime(obs->video.video); delta_time = cur_time - last_time; seconds = (float)((double)delta_time / 1000000000.0); - for (i = 0; i < obs->data.sources.num; i++) - obs_source_video_tick(obs->data.sources.array[i], seconds); + pthread_mutex_lock(&data->sources_mutex); - last_time = cur_time; - return last_time; + source = data->first_source; + while (source) { + if (source->refs) + obs_source_video_tick(source, seconds); + source = (struct obs_source*)source->context.next; + } + + pthread_mutex_unlock(&data->sources_mutex); + + return cur_time; } /* in obs-display.c */ @@ -43,6 +51,8 @@ extern void render_display(struct obs_display *display); static inline void render_displays(void) { + struct obs_display *display; + if (!obs->data.valid) return; @@ -51,8 +61,11 @@ static inline void render_displays(void) /* render extra displays/swaps */ pthread_mutex_lock(&obs->data.displays_mutex); - for (size_t i = 0; i < obs->data.displays.num; i++) - render_display(obs->data.displays.array[i]); + display = obs->data.first_display; + while (display) { + render_display(display); + display = display->next; + } pthread_mutex_unlock(&obs->data.displays_mutex); diff --git a/libobs/obs.c b/libobs/obs.c index 194eaa309..49519788a 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -395,6 +395,8 @@ static bool obs_init_data(void) return false; if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) goto fail; + if (pthread_mutex_init(&data->user_sources_mutex, &attr) != 0) + goto fail; if (pthread_mutex_init(&data->sources_mutex, &attr) != 0) goto fail; if (pthread_mutex_init(&data->displays_mutex, &attr) != 0) @@ -403,6 +405,8 @@ static bool obs_init_data(void) goto fail; if (pthread_mutex_init(&data->encoders_mutex, &attr) != 0) goto fail; + if (pthread_mutex_init(&data->services_mutex, &attr) != 0) + goto fail; if (!obs_view_init(&data->main_view)) goto fail; @@ -413,32 +417,48 @@ fail: return data->valid; } +#define FREE_OBS_LINKED_LIST(type) \ + do { \ + int unfreed = 0; \ + while (data->first_ ## type ) { \ + obs_ ## type ## _destroy(data->first_ ## type ); \ + unfreed++; \ + } \ + if (unfreed) \ + blog(LOG_INFO, "\t%d " #type "(s) were remaining", \ + unfreed); \ + } while (false) + static void obs_free_data(void) { struct obs_core_data *data = &obs->data; - uint32_t i; data->valid = false; obs_view_free(&data->main_view); - while (data->outputs.num) - obs_output_destroy(data->outputs.array[0]); - while (data->encoders.num) - obs_encoder_destroy(data->encoders.array[0]); - while (data->displays.num) - obs_display_destroy(data->displays.array[0]); + blog(LOG_INFO, "Freeing OBS context data"); - pthread_mutex_lock(&obs->data.sources_mutex); - for (i = 0; i < data->sources.num; i++) - obs_source_release(data->sources.array[i]); - da_free(data->sources); - pthread_mutex_unlock(&obs->data.sources_mutex); + if (data->user_sources.num) + blog(LOG_INFO, "\t%d user source(s) were remaining", + (int)data->user_sources.num); + while (data->user_sources.num) + obs_source_remove(data->user_sources.array[0]); + da_free(data->user_sources); + + FREE_OBS_LINKED_LIST(source); + FREE_OBS_LINKED_LIST(output); + FREE_OBS_LINKED_LIST(encoder); + FREE_OBS_LINKED_LIST(display); + FREE_OBS_LINKED_LIST(service); + + pthread_mutex_destroy(&data->user_sources_mutex); pthread_mutex_destroy(&data->sources_mutex); pthread_mutex_destroy(&data->displays_mutex); pthread_mutex_destroy(&data->outputs_mutex); pthread_mutex_destroy(&data->encoders_mutex); + pthread_mutex_destroy(&data->services_mutex); } static const char *obs_signals[] = { @@ -522,11 +542,6 @@ void obs_shutdown(void) free_module(obs->modules.array+i); da_free(obs->modules); - da_free(obs->data.sources); - da_free(obs->data.outputs); - da_free(obs->data.encoders); - da_free(obs->data.displays); - bfree(obs); obs = NULL; } @@ -655,6 +670,26 @@ bool obs_enum_output_types(size_t idx, const char **id) return true; } +bool obs_enum_encoder_types(size_t idx, const char **id) +{ + if (!obs) return false; + + if (idx >= obs->encoder_types.num) + return false; + *id = obs->encoder_types.array[idx].id; + return true; +} + +bool obs_enum_service_types(size_t idx, const char **id) +{ + if (!obs) return false; + + if (idx >= obs->service_types.num) + return false; + *id = obs->service_types.array[idx].id; + return true; +} + graphics_t obs_graphics(void) { return (obs != NULL) ? obs->video.graphics : NULL; @@ -737,7 +772,7 @@ bool obs_add_source(obs_source_t source) if (!obs) return false; pthread_mutex_lock(&obs->data.sources_mutex); - da_push_back(obs->data.sources, &source); + da_push_back(obs->data.user_sources, &source); obs_source_addref(source); pthread_mutex_unlock(&obs->data.sources_mutex); @@ -791,49 +826,63 @@ void obs_set_output_source(uint32_t channel, obs_source_t source) } } -void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t), void *param) +void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t), void *param) { - struct obs_core_data *data = &obs->data; - if (!obs) return; - pthread_mutex_lock(&data->outputs_mutex); + pthread_mutex_lock(&obs->data.user_sources_mutex); - for (size_t i = 0; i < data->outputs.num; i++) - if (!enum_proc(param, data->outputs.array[i])) + for (size_t i = 0; i < obs->data.user_sources.num; i++) { + struct obs_source *source = obs->data.user_sources.array[i]; + if (!enum_proc(param, source)) + break; + } + + pthread_mutex_unlock(&obs->data.user_sources_mutex); +} + +static inline void obs_enum(void *pstart, pthread_mutex_t *mutex, void *proc, + void *param) +{ + struct obs_context_data **start = pstart, *context; + bool (*enum_proc)(void*, void*) = proc; + + assert(start); + assert(mutex); + assert(enum_proc); + + pthread_mutex_lock(mutex); + + context = *start; + while (context) { + if (!enum_proc(param, context)) break; - pthread_mutex_unlock(&data->outputs_mutex); + context = context->next; + } + + pthread_mutex_unlock(mutex); +} + +void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t), void *param) +{ + if (!obs) return; + obs_enum(&obs->data.first_output, &obs->data.outputs_mutex, + enum_proc, param); } void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t), void *param) { - struct obs_core_data *data = &obs->data; - if (!obs) return; - - pthread_mutex_lock(&data->encoders_mutex); - - for (size_t i = 0; i < data->encoders.num; i++) - if (!enum_proc(param, data->encoders.array[i])) - break; - - pthread_mutex_unlock(&data->encoders_mutex); + obs_enum(&obs->data.first_encoder, &obs->data.encoders_mutex, + enum_proc, param); } -void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t), void *param) +void obs_enum_services(bool (*enum_proc)(void*, obs_service_t), void *param) { - struct obs_core_data *data = &obs->data; - if (!obs) return; - - pthread_mutex_lock(&data->sources_mutex); - - for (size_t i = 0; i < data->sources.num; i++) - if (!enum_proc(param, data->sources.array[i])) - break; - - pthread_mutex_unlock(&data->sources_mutex); + obs_enum(&obs->data.first_service, &obs->data.services_mutex, + enum_proc, param); } obs_source_t obs_get_source_by_name(const char *name) @@ -844,18 +893,18 @@ obs_source_t obs_get_source_by_name(const char *name) if (!obs) return NULL; - pthread_mutex_lock(&data->sources_mutex); + pthread_mutex_lock(&data->user_sources_mutex); - for (i = 0; i < data->sources.num; i++) { - struct obs_source *cur_source = data->sources.array[i]; - if (strcmp(cur_source->name, name) == 0) { + for (i = 0; i < data->user_sources.num; i++) { + struct obs_source *cur_source = data->user_sources.array[i]; + if (strcmp(cur_source->context.name, name) == 0) { source = cur_source; obs_source_addref(source); break; } } - pthread_mutex_unlock(&data->sources_mutex); + pthread_mutex_unlock(&data->user_sources_mutex); return source; } @@ -936,3 +985,103 @@ float obs_get_present_volume(void) { return obs ? obs->audio.present_volume : 0.0f; } + +/* ensures that names are never blank */ +static inline char *dup_name(const char *name) +{ + if (!name || !*name) { + struct dstr unnamed = {0}; + dstr_printf(&unnamed, "__unnamed%004lld", + obs->data.unnamed_index++); + + return unnamed.array; + } else { + return bstrdup(name); + } +} + +static inline bool obs_context_data_init_wrap( + struct obs_context_data *context, + obs_data_t settings, + const char *name) +{ + assert(context); + + obs_context_data_free(context); + + context->signals = signal_handler_create(); + if (!context) + return false; + + context->procs = proc_handler_create(); + if (!context->procs) + return false; + + context->name = dup_name(name); + context->settings = obs_data_newref(settings); + return true; +} + +bool obs_context_data_init( + struct obs_context_data *context, + obs_data_t settings, + const char *name) +{ + if (obs_context_data_init_wrap(context, settings, name)) { + return true; + } else { + obs_context_data_free(context); + return false; + } +} + +void obs_context_data_free(struct obs_context_data *context) +{ + signal_handler_destroy(context->signals); + proc_handler_destroy(context->procs); + obs_data_release(context->settings); + obs_context_data_remove(context); + bfree(context->name); + + memset(context, 0, sizeof(*context)); +} + +void obs_context_data_insert(struct obs_context_data *context, + pthread_mutex_t *mutex, void *pfirst) +{ + struct obs_context_data **first = pfirst; + + assert(context); + assert(mutex); + assert(first); + + context->mutex = mutex; + + pthread_mutex_lock(mutex); + context->prev_next = first; + context->next = *first; + *first = context; + if (context->next) + context->next->prev_next = &context->next; + pthread_mutex_unlock(mutex); +} + +void obs_context_data_remove(struct obs_context_data *context) +{ + if (context && context->mutex) { + pthread_mutex_lock(context->mutex); + *context->prev_next = context->next; + if (context->next) + context->next->prev_next = context->prev_next; + pthread_mutex_unlock(context->mutex); + + context->mutex = NULL; + } +} + +void obs_context_data_setname(struct obs_context_data *context, + const char *name) +{ + bfree(context->name); + context->name = dup_name(name); +} diff --git a/libobs/obs.h b/libobs/obs.h index 0d9a0bba5..515a0ddbe 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -234,14 +234,15 @@ EXPORT bool obs_enum_filter_types(size_t idx, const char **id); */ EXPORT bool obs_enum_transition_types(size_t idx, const char **id); -/** - * Enumerates all available ouput types. - * - * Outputs handle color space conversion, encoding, and output to file or - * streams. - */ +/** Enumerates all available output types. */ EXPORT bool obs_enum_output_types(size_t idx, const char **id); +/** Enumerates all available encoder types. */ +EXPORT bool obs_enum_encoder_types(size_t idx, const char **id); + +/** Enumerates all available service types. */ +EXPORT bool obs_enum_service_types(size_t idx, const char **id); + /** Gets the main graphics context for this OBS context */ EXPORT graphics_t obs_graphics(void); @@ -864,13 +865,34 @@ EXPORT void obs_free_encoder_packet(struct encoder_packet *packet); /* ------------------------------------------------------------------------- */ /* Stream Services */ + EXPORT const char *obs_service_getdisplayname(const char *id, const char *locale); -EXPORT obs_service_t obs_service_create(const char *service, +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); +/** Gets the default settings for a service */ +EXPORT obs_data_t obs_service_defaults(const char *id); + +/** Returns the property list, if any. Free with obs_properties_destroy */ +EXPORT obs_properties_t obs_get_service_properties(const char *id, + const char *locale); + +/** + * Returns the property list of an existing service context, if any. Free with + * obs_properties_destroy + */ +EXPORT obs_properties_t obs_service_properties(obs_service_t service, + const char *locale); + +/** Returns the URL for this service context */ +const char *obs_service_get_url(obs_service_t service); + +/** Returns the stream key (if any) for this service context */ +const char *obs_service_get_key(obs_service_t service); + /* ------------------------------------------------------------------------- */ /* Source frame allocation functions */ diff --git a/vs/2013/libobs/libobs.vcxproj b/vs/2013/libobs/libobs.vcxproj index 65931075e..dd731ce86 100644 --- a/vs/2013/libobs/libobs.vcxproj +++ b/vs/2013/libobs/libobs.vcxproj @@ -109,6 +109,7 @@ + @@ -210,7 +211,7 @@ Windows true - winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) + jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) false @@ -231,7 +232,7 @@ Windows true - winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) + jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) false @@ -256,7 +257,7 @@ true true true - winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) + jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) false @@ -281,7 +282,7 @@ true true true - winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) + jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) false diff --git a/vs/2013/libobs/libobs.vcxproj.filters b/vs/2013/libobs/libobs.vcxproj.filters index 661dcb423..6b3242b62 100644 --- a/vs/2013/libobs/libobs.vcxproj.filters +++ b/vs/2013/libobs/libobs.vcxproj.filters @@ -380,5 +380,8 @@ libobs\Source Files + + libobs\Source Files + \ No newline at end of file