diff --git a/libobs/obs-cocoa.c b/libobs/obs-cocoa.c index ea5501ab7..9fbbe5a6a 100644 --- a/libobs/obs-cocoa.c +++ b/libobs/obs-cocoa.c @@ -31,28 +31,23 @@ const char *get_module_extension(void) return ".so"; } -// support both foo.so and libfoo.so for now -static const char *plugin_patterns[] = { - OBS_INSTALL_PREFIX "obs-plugins/%s.so", - OBS_INSTALL_PREFIX "obs-plugins/lib%s.so", - "../obs-plugins/%s.so", - "../obs-plugins/lib%s.so" +static const char *module_bin[] = { + "../obs-plugins", + OBS_INSTALL_PREFIX "obs-plugins", }; -static const int plugin_patterns_size = - sizeof(plugin_patterns)/sizeof(plugin_patterns[0]); +static const char *module_data[] = { + "../data/obs-plugins/%module%", + OBS_INSTALL_DATA_PATH "obs-plugins/%module%", +}; -char *find_plugin(const char *plugin) +static const int module_patterns_size = + sizeof(module_bin)/sizeof(module_bin[0]); + +void add_default_module_paths(void) { - struct dstr path; - dstr_init(&path); - for(int i = 0; i < plugin_patterns_size; i++) { - dstr_printf(&path, plugin_patterns[i], plugin); - if(!access(path.array, F_OK)) - break; - } - - return path.array; + for (int i = 0; i < module_patterns_size; i++) + obs_add_module_path(module_bin[i], module_data[i]); } char *find_libobs_data_file(const char *file) @@ -63,14 +58,6 @@ char *find_libobs_data_file(const char *file) return path.array; } -char *obs_find_plugin_file(const char *file) -{ - struct dstr path; - dstr_init_copy(&path, OBS_INSTALL_DATA_PATH "/obs-plugins/"); - dstr_cat(&path, file); - return path.array; -} - static void log_processor_name(void) { char *name = NULL; diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 853003556..470bd221c 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -22,6 +22,7 @@ #include "util/circlebuf.h" #include "util/dstr.h" #include "util/threading.h" +#include "util/platform.h" #include "callback/signal.h" #include "callback/proc.h" @@ -50,13 +51,49 @@ struct draw_callback { /* modules */ struct obs_module { - char *name; + const char *file; + char *bin_path; + char *data_path; void *module; - void (*set_locale)(const char *locale); + bool loaded; + + bool (*load)(uint32_t libobs_ver); + void (*unload)(void); + void (*set_locale)(const char *locale); + void (*free_locale)(void); + uint32_t (*ver)(void); + void (*set_pointer)(obs_module_t module); + const char *(*name)(void); + const char *(*description)(void); + const char *(*author)(void); + + struct obs_module *next; }; extern void free_module(struct obs_module *mod); +struct obs_module_path { + char *bin; + char *data; +}; + +static inline void free_module_path(struct obs_module_path *omp) +{ + if (omp) { + bfree(omp->bin); + bfree(omp->data); + } +} + +static inline bool check_path(const char *data, const char *path, + struct dstr *output) +{ + dstr_copy(output, path); + dstr_cat(output, data); + + return os_file_exists(output->array); +} + /* ------------------------------------------------------------------------- */ /* views */ @@ -161,7 +198,9 @@ struct obs_core_data { }; struct obs_core { - DARRAY(struct obs_module) modules; + struct obs_module *first_module; + DARRAY(struct obs_module_path) module_paths; + DARRAY(struct obs_source_info) input_types; DARRAY(struct obs_source_info) filter_types; DARRAY(struct obs_source_info) transition_types; diff --git a/libobs/obs-module.c b/libobs/obs-module.c index 7816c7ac0..af1e355d4 100644 --- a/libobs/obs-module.c +++ b/libobs/obs-module.c @@ -22,7 +22,6 @@ #include "obs-internal.h" #include "obs-module.h" -extern char *find_plugin(const char *plugin); extern const char *get_module_extension(void); static inline int req_func_not_found(const char *name, const char *path) @@ -33,76 +32,313 @@ static inline int req_func_not_found(const char *name, const char *path) return MODULE_MISSING_EXPORTS; } -#define LOAD_REQ_SIZE_FUNC(func, module, path) \ - func = os_dlsym(module, #func); \ - if (!func) \ - return req_func_not_found(#func, path) - -static int call_module_load(void *module, const char *path) +static int load_module_exports(struct obs_module *mod, const char *path) { - bool (*obs_module_load)(uint32_t obs_ver) = NULL; - - obs_module_load = os_dlsym(module, "obs_module_load"); - if (!obs_module_load) + mod->load = os_dlsym(mod->module, "obs_module_load"); + if (!mod->load) return req_func_not_found("obs_module_load", path); - if (!obs_module_load(LIBOBS_API_VER)) { - blog(LOG_ERROR, "Module '%s' failed to load: " - "obs_module_load failed", path); - return MODULE_ERROR; - } + mod->set_pointer = os_dlsym(mod->module, "obs_module_set_pointer"); + if (!mod->set_pointer) + return req_func_not_found("obs_module_set_pointer", path); + mod->ver = os_dlsym(mod->module, "obs_module_ver"); + if (!mod->ver) + return req_func_not_found("obs_module_ver", path); + + /* optional exports */ + mod->unload = os_dlsym(mod->module, "obs_module_unload"); + mod->set_locale = os_dlsym(mod->module, "obs_module_set_locale"); + mod->free_locale = os_dlsym(mod->module, "obs_module_free_locale"); + mod->name = os_dlsym(mod->module, "obs_module_name"); + mod->description = os_dlsym(mod->module, "obs_module_description"); + mod->author = os_dlsym(mod->module, "obs_module_author"); return MODULE_SUCCESS; } -int obs_load_module(const char *path) +int obs_open_module(obs_module_t *module, const char *path, + const char *data_path) { - struct obs_module mod; - char *plugin_path = find_plugin(path); + struct obs_module mod = {0}; int errorcode; - mod.module = os_dlopen(plugin_path); - bfree(plugin_path); + if (!module || !path || !obs) + return MODULE_ERROR; + + mod.module = os_dlopen(path); if (!mod.module) { blog(LOG_WARNING, "Module '%s' not found", path); return MODULE_FILE_NOT_FOUND; } - errorcode = call_module_load(mod.module, path); - if (errorcode != MODULE_SUCCESS) { - os_dlclose(mod.module); + errorcode = load_module_exports(&mod, path); + if (errorcode != MODULE_SUCCESS) return errorcode; - } - mod.name = bstrdup(path); - mod.set_locale = os_dlsym(mod.module, "obs_module_set_locale"); + mod.bin_path = bstrdup(path); + mod.file = strrchr(mod.bin_path, '/'); + mod.file = (!mod.file) ? mod.bin_path : (mod.file + 1); + mod.data_path = bstrdup(data_path); + mod.next = obs->first_module; + + *module = bmemdup(&mod, sizeof(mod)); + obs->first_module = (*module); + mod.set_pointer(*module); if (mod.set_locale) mod.set_locale(obs->locale); - da_push_back(obs->modules, &mod); return MODULE_SUCCESS; } +bool obs_init_module(obs_module_t module) +{ + if (!module || !obs) + return false; + if (module->loaded) + return true; + + module->loaded = module->load(LIBOBS_API_VER); + if (!module->loaded) + blog(LOG_WARNING, "Failed to initialize module '%s'", + module->file); + + return module->loaded; +} + +char *obs_find_module_file(obs_module_t module, const char *file) +{ + struct dstr output = {0}; + + if (!module) + return NULL; + + dstr_copy(&output, module->data_path); + if (!dstr_isempty(&output) && dstr_end(&output) != '/') + dstr_cat_ch(&output, '/'); + dstr_cat(&output, file); + + if (!os_file_exists(output.array)) + dstr_free(&output); + return output.array; +} + +void obs_add_module_path(const char *bin, const char *data) +{ + struct obs_module_path omp; + + if (!obs || !bin || !data) return; + + omp.bin = bstrdup(bin); + omp.data = bstrdup(data); + da_push_back(obs->module_paths, &omp); +} + +static void load_all_callback(void *param, const struct obs_module_info *info) +{ + obs_module_t module; + + int code = obs_open_module(&module, info->bin_path, info->data_path); + if (code != MODULE_SUCCESS) { + blog(LOG_DEBUG, "Failed to load module file '%s': %d", + info->bin_path, code); + return; + } + + obs_init_module(module); + + UNUSED_PARAMETER(param); +} + +void obs_load_all_modules(void) +{ + obs_find_modules(load_all_callback, NULL); +} + +static inline void make_data_dir(struct dstr *parsed_data_dir, + const char *data_dir, const char *name) +{ + dstr_copy(parsed_data_dir, data_dir); + dstr_replace(parsed_data_dir, "%module%", name); + if (dstr_end(parsed_data_dir) == '/') + dstr_resize(parsed_data_dir, parsed_data_dir->len - 1); +} + +static char *make_data_directory(const char *module_name, const char *data_dir) +{ + struct dstr parsed_data_dir = {0}; + bool found = false; + + make_data_dir(&parsed_data_dir, data_dir, module_name); + + found = os_file_exists(parsed_data_dir.array); + + if (!found && astrcmpi_n(module_name, "lib", 3) == 0) { + make_data_dir(&parsed_data_dir, data_dir, module_name + 3); + found = os_file_exists(parsed_data_dir.array); + } + + if (!found) + dstr_free(&parsed_data_dir); + + return parsed_data_dir.array; +} + +static bool parse_binary_from_directory(struct dstr *parsed_bin_path, + const char *bin_path, const char *file) +{ + struct dstr directory = {0}; + bool found = true; + + dstr_copy(&directory, bin_path); + dstr_replace(&directory, "%module%", file); + if (dstr_end(&directory) != '/') + dstr_cat_ch(&directory, '/'); + + dstr_copy_dstr(parsed_bin_path, &directory); + dstr_cat(parsed_bin_path, file); + dstr_cat(parsed_bin_path, get_module_extension()); + + if (!os_file_exists(parsed_bin_path->array)) { + /* if the file doesn't exist, check with 'lib' prefix */ + dstr_copy_dstr(parsed_bin_path, &directory); + dstr_cat(parsed_bin_path, "lib"); + dstr_cat(parsed_bin_path, file); + dstr_cat(parsed_bin_path, get_module_extension()); + + /* if neither exist, don't include this as a library */ + if (!os_file_exists(parsed_bin_path->array)) { + dstr_free(parsed_bin_path); + found = false; + } + } + + dstr_free(&directory); + return found; +} + +static void process_found_module(struct obs_module_path *omp, + const char *path, bool directory, + obs_find_module_callback_t callback, void *param) +{ + struct obs_module_info info; + struct dstr name = {0}; + struct dstr parsed_bin_path = {0}; + const char *file; + char *parsed_data_dir; + bool bin_found = true; + + file = strrchr(path, '/'); + file = file ? (file + 1) : path; + + dstr_copy(&name, file); + if (!directory) { + char *ext = strrchr(name.array, '.'); + if (ext) + dstr_resize(&name, ext - name.array); + + dstr_copy(&parsed_bin_path, path); + } else { + bin_found = parse_binary_from_directory(&parsed_bin_path, + omp->bin, file); + } + + parsed_data_dir = make_data_directory(name.array, omp->data); + + if (parsed_data_dir && bin_found) { + info.bin_path = parsed_bin_path.array; + info.data_path = parsed_data_dir; + callback(param, &info); + } + + bfree(parsed_data_dir); + dstr_free(&name); + dstr_free(&parsed_bin_path); +} + +static void find_modules_in_path(struct obs_module_path *omp, + obs_find_module_callback_t callback, void *param) +{ + struct dstr search_path = {0}; + char *module_start; + bool search_directories = false; + os_glob_t gi; + + dstr_copy(&search_path, omp->bin); + + module_start = strstr(search_path.array, "%module%"); + if (module_start) { + dstr_resize(&search_path, module_start - search_path.array); + search_directories = true; + } + + if (!dstr_isempty(&search_path) && dstr_end(&search_path) != '/') + dstr_cat_ch(&search_path, '/'); + + dstr_cat_ch(&search_path, '*'); + if (!search_directories) + dstr_cat(&search_path, get_module_extension()); + + if (os_glob(search_path.array, 0, &gi) == 0) { + for (size_t i = 0; i < gi->gl_pathc; i++) { + if (search_directories == gi->gl_pathv[i].directory) + process_found_module(omp, + gi->gl_pathv[i].path, + search_directories, + callback, param); + } + + os_globfree(gi); + } + + dstr_free(&search_path); +} + +void obs_find_modules(obs_find_module_callback_t callback, void *param) +{ + if (!obs) + return; + + for (size_t i = 0; i < obs->module_paths.num; i++) { + struct obs_module_path *omp = obs->module_paths.array + i; + find_modules_in_path(omp, callback, param); + } +} + +void obs_enum_modules(obs_enum_module_callback_t callback, void *param) +{ + struct obs_module *module; + if (!obs) + return; + + module = obs->first_module; + while (module) { + callback(param, module); + module = module->next; + } +} + void free_module(struct obs_module *mod) { if (!mod) return; if (mod->module) { - void (*module_unload)(void); + if (mod->free_locale) + mod->free_locale(); - module_unload = os_dlsym(mod->module, "obs_module_unload"); - if (module_unload) - module_unload(); + if (mod->loaded && mod->unload) + mod->unload(); os_dlclose(mod->module); } - bfree(mod->name); + bfree(mod->bin_path); + bfree(mod->data_path); + bfree(mod); } -lookup_t obs_module_load_locale(const char *module, const char *default_locale, +lookup_t obs_module_load_locale(obs_module_t module, const char *default_locale, const char *locale) { struct dstr str = {0}; @@ -113,12 +349,11 @@ lookup_t obs_module_load_locale(const char *module, const char *default_locale, return NULL; } - dstr_copy(&str, module); - dstr_cat(&str, "/locale/"); + dstr_copy(&str, "locale/"); dstr_cat(&str, default_locale); dstr_cat(&str, ".ini"); - char *file = obs_find_plugin_file(str.array); + char *file = obs_find_module_file(module, str.array); if (file) lookup = text_lookup_create(file); @@ -126,23 +361,22 @@ lookup_t obs_module_load_locale(const char *module, const char *default_locale, if (!lookup) { blog(LOG_WARNING, "Failed to load '%s' text for module: '%s'", - default_locale, module); + default_locale, module->file); goto cleanup; } if (astrcmpi(locale, default_locale) == 0) goto cleanup; - dstr_copy(&str, module); - dstr_cat(&str, "/locale/"); + dstr_copy(&str, "/locale/"); dstr_cat(&str, locale); dstr_cat(&str, ".ini"); - file = obs_find_plugin_file(str.array); + file = obs_find_module_file(module, str.array); if (!text_lookup_add(lookup, file)) blog(LOG_WARNING, "Failed to load '%s' text for module: '%s'", - locale, module); + locale, module->file); bfree(file); cleanup: diff --git a/libobs/obs-module.h b/libobs/obs-module.h index b2be6a887..f9089ccab 100644 --- a/libobs/obs-module.h +++ b/libobs/obs-module.h @@ -32,8 +32,15 @@ */ /** Required: Declares a libobs module. */ -#define OBS_DECLARE_MODULE() \ - MODULE_EXPORT uint32_t obs_module_ver(void); \ +#define OBS_DECLARE_MODULE() \ + static obs_module_t obs_module_pointer; \ + MODULE_EXPORT void obs_module_set_pointer(obs_module_t module); \ + void obs_module_set_pointer(obs_module_t module) \ + { \ + obs_module_pointer = module; \ + } \ + obs_module_t obs_current_module(void) {return obs_module_pointer;} \ + MODULE_EXPORT uint32_t obs_module_ver(void); \ uint32_t obs_module_ver(void) {return LIBOBS_API_VER;} /** @@ -53,11 +60,10 @@ MODULE_EXPORT void obs_module_unload(void); /** Called to set the current locale data for the module. */ MODULE_EXPORT void obs_module_set_locale(const char *locale); -/** - * Optional: Use this macro in a module to use default locale handling. Use - * the OBS_MODULE_FREE_DEFAULT_LOCALE macro in obs_module_unload to free the - * locale data when the module unloads. - */ +/** Called to free the current locale data for the module. */ +MODULE_EXPORT void obs_module_free_locale(void); + +/** Optional: Use this macro in a module to use default locale handling. */ #define OBS_MODULE_USE_DEFAULT_LOCALE(module_name, default_locale) \ lookup_t obs_module_lookup = NULL; \ const char *obs_module_text(const char *val) \ @@ -69,7 +75,8 @@ MODULE_EXPORT void obs_module_set_locale(const char *locale); void obs_module_set_locale(const char *locale) \ { \ if (obs_module_lookup) text_lookup_destroy(obs_module_lookup); \ - obs_module_lookup = obs_module_load_locale(module_name, \ + obs_module_lookup = obs_module_load_locale( \ + obs_current_module(), \ default_locale, locale); \ } @@ -79,6 +86,16 @@ MODULE_EXPORT void obs_module_set_locale(const char *locale); /** Helper function for looking up locale if default locale handler was used */ extern const char *obs_module_text(const char *lookup_string); +/** Helper function that returns the current module */ +extern obs_module_t obs_current_module(void); + +/** + * Returns the location to a module data file associated with the current + * module. Free with bfree when complete. Equivalent to: + * obs_find_module_file(obs_current_modile(), file); + */ +#define obs_module_file(file) obs_find_module_file(obs_current_module(), file) + /** * Optional: Declares the author(s) of the module * @@ -88,5 +105,8 @@ extern const char *obs_module_text(const char *lookup_string); MODULE_EXPORT const char *obs_module_author(void); \ const char *obs_module_author(void) {return name;} +/** Optional: Returns the full name of the module */ +MODULE_EXPORT const char *obs_module_name(void); + /** Optional: Returns a description of the module */ MODULE_EXPORT const char *obs_module_description(void); diff --git a/libobs/obs-nix.c b/libobs/obs-nix.c index 80f7bbce7..599fc5421 100644 --- a/libobs/obs-nix.c +++ b/libobs/obs-nix.c @@ -22,77 +22,36 @@ #include #include #include "util/dstr.h" -#include "obs.h" +#include "obs-internal.h" const char *get_module_extension(void) { return ".so"; } -static inline bool check_path(const char* data, const char *path, - struct dstr * output) +#ifdef __LP64__ +#define BIT_STRING "64bit" +#else +#define BIT_STRING "32bit" +#endif + +static const char *module_bin[] = { + "../../obs-plugins/" BIT_STRING, + OBS_INSTALL_PREFIX "lib/obs-plugins", +}; + +static const char *module_data[] = { + OBS_DATA_PATH "/obs-plugins/%module%", + OBS_INSTALL_DATA_PATH "/obs-plugins/%module%", +}; + +static const int module_patterns_size = + sizeof(module_bin)/sizeof(module_bin[0]); + +void add_default_module_paths(void) { - dstr_copy(output, path); - dstr_cat(output, data); - - blog(LOG_INFO, "Attempting path: %s\n", output->array); - - return access(output->array, R_OK) == 0; -} - -static inline bool check_lib_path(const char* data, const char *path, - struct dstr *output) -{ - bool result = false; - struct dstr tmp; - - dstr_init(&tmp); - dstr_cat(&tmp, data); - dstr_cat(&tmp, ".so"); - result = check_path(tmp.array, path, output); - - dstr_free(&tmp); - - return result; -} - -/* - * /usr/local/lib/obs-plugins - * /usr/lib/obs-plugins - */ -char *find_plugin(const char *plugin) -{ - struct dstr output; - dstr_init(&output); - - if(sizeof(void*) == 4) { - if (check_lib_path(plugin, "../../obs-plugins/32bit/", &output)) - return output.array; - - if (check_lib_path(plugin, "../../obs-plugins/32bit/lib", &output)) - return output.array; - } else { - if (check_lib_path(plugin, "../../obs-plugins/64bit/", &output)) - return output.array; - - if (check_lib_path(plugin, "../../obs-plugins/64bit/lib", &output)) - return output.array; - } - - if (OBS_INSTALL_PREFIX [0] != 0) { - if (check_lib_path(plugin, - OBS_INSTALL_PREFIX "lib/obs-plugins/", - &output)) - return output.array; - - if (check_lib_path(plugin, - OBS_INSTALL_PREFIX "lib/obs-plugins/lib", - &output)) - return output.array; - } - - dstr_free(&output); - return NULL; + for (int i = 0; i < module_patterns_size; i++) + obs_add_module_path(module_bin[i], module_data[i]); } /* @@ -117,28 +76,6 @@ char *find_libobs_data_file(const char *file) return NULL; } -/* - * /usr/local/share/obs-plugins - * /usr/share/obs-plugins - */ -char *obs_find_plugin_file(const char *file) -{ - struct dstr output; - dstr_init(&output); - - if (check_path(file, OBS_DATA_PATH "/obs-plugins/", &output)) - return output.array; - - if (OBS_INSTALL_PREFIX [0] != 0) { - if (check_path(file, OBS_INSTALL_DATA_PATH "/obs-plugins/", - &output)) - return output.array; - } - - dstr_free(&output); - return NULL; -} - static void log_processor_info(void) { FILE *fp; diff --git a/libobs/obs-windows.c b/libobs/obs-windows.c index 14fdbd1c0..9e7c83ad7 100644 --- a/libobs/obs-windows.c +++ b/libobs/obs-windows.c @@ -27,57 +27,32 @@ const char *get_module_extension(void) return ".dll"; } -static inline bool check_path(const char* data, const char *path, - struct dstr * output) -{ - dstr_copy(output, path); - dstr_cat(output, data); - - blog(LOG_DEBUG, "Attempting path: %s\n", output->array); - - return os_file_exists(output->array); -} - -static inline bool check_lib_path(const char* data, const char *path, - struct dstr *output) -{ - bool result = false; - struct dstr tmp; - - dstr_init_copy(&tmp, data); - dstr_cat(&tmp, ".dll"); - result = check_path(tmp.array, path, output); - - dstr_free(&tmp); - - return result; -} - -/* on windows, plugin files are located in [base directory]/plugins/[bit] */ -char *find_plugin(const char *plugin) -{ - struct dstr path; - dstr_init(&path); - #ifdef _WIN64 - if (check_lib_path(plugin, "obs-plugins/64bit/", &path)) +#define BIT_STRING "64bit" #else - if (check_lib_path(plugin, "obs-plugins/32bit/", &path)) +#define BIT_STRING "32bit" #endif - return path.array; -#ifdef _WIN64 - if (check_lib_path(plugin, "../../obs-plugins/64bit/", &path)) -#else - if (check_lib_path(plugin, "../../obs-plugins/32bit/", &path)) -#endif - return path.array; +static const char *module_bin[] = { + "obs-plugins/" BIT_STRING, + "../../obs-plugins/" BIT_STRING, +}; - dstr_free(&path); - return NULL; +static const char *module_data[] = { + "data/%module%", + "../../data/obs-plugins/%module%" +}; + +static const int module_patterns_size = + sizeof(module_bin)/sizeof(module_bin[0]); + +void add_default_module_paths(void) +{ + for (int i = 0; i < module_patterns_size; i++) + obs_add_module_path(module_bin[i], module_data[i]); } -/* on windows, points to [base directory]/libobs */ +/* on windows, points to [base directory]/data/libobs */ char *find_libobs_data_file(const char *file) { struct dstr path; @@ -93,22 +68,6 @@ char *find_libobs_data_file(const char *file) return NULL; } -/* on windows, data files should always be in [base directory]/data */ -char *obs_find_plugin_file(const char *file) -{ - struct dstr path; - dstr_init(&path); - - if (check_path(file, "data/obs-plugins/", &path)) - return path.array; - - if (check_path(file, "../../data/obs-plugins/", &path)) - return path.array; - - dstr_free(&path); - return NULL; -} - static void log_processor_info(void) { HKEY key; diff --git a/libobs/obs.c b/libobs/obs.c index 0d7acd105..323c938f5 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -24,6 +24,7 @@ struct obs_core *obs = NULL; +extern void add_default_module_paths(void); extern char *find_libobs_data_file(const char *file); static inline void make_gs_init_data(struct gs_init_data *gid, @@ -540,6 +541,7 @@ static bool obs_init(const char *locale) obs->locale = bstrdup(locale); obs_register_source(&scene_info); + add_default_module_paths(); return true; } @@ -561,6 +563,8 @@ bool obs_startup(const char *locale) void obs_shutdown(void) { + struct obs_module *module; + if (!obs) return; @@ -582,9 +586,17 @@ void obs_shutdown(void) proc_handler_destroy(obs->procs); signal_handler_destroy(obs->signals); - for (size_t i = 0; i < obs->modules.num; i++) - free_module(obs->modules.array+i); - da_free(obs->modules); + module = obs->first_module; + while (module) { + struct obs_module *next = module->next; + free_module(module); + module = next; + } + obs->first_module = NULL; + + for (size_t i = 0; i < obs->module_paths.num; i++) + free_module_path(obs->module_paths.array+i); + da_free(obs->module_paths); bfree(obs->locale); bfree(obs); @@ -603,6 +615,7 @@ uint32_t obs_get_version(void) void obs_set_locale(const char *locale) { + struct obs_module *module; if (!obs) return; @@ -610,11 +623,12 @@ void obs_set_locale(const char *locale) bfree(obs->locale); obs->locale = bstrdup(locale); - for (size_t i = 0; i < obs->modules.num; i++) { - struct obs_module *module = obs->modules.array+i; - + module = obs->first_module; + while (module) { if (module->set_locale) module->set_locale(locale); + + module = module->next; } } diff --git a/libobs/obs.h b/libobs/obs.h index f36e2c5eb..bb39d242c 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -45,6 +45,7 @@ struct obs_scene_item; struct obs_output; struct obs_encoder; struct obs_service; +struct obs_module; typedef struct obs_display *obs_display_t; typedef struct obs_view *obs_view_t; @@ -54,6 +55,7 @@ typedef struct obs_scene_item *obs_sceneitem_t; typedef struct obs_output *obs_output_t; typedef struct obs_encoder *obs_encoder_t; typedef struct obs_service *obs_service_t; +typedef struct obs_module *obs_module_t; #include "obs-source.h" #include "obs-encoder.h" @@ -257,18 +259,82 @@ EXPORT bool obs_get_video_info(struct obs_video_info *ovi); EXPORT bool obs_get_audio_info(struct audio_output_info *ai); /** - * Loads a plugin module + * Opens a plugin module directly from a specific path. * - * A plugin module contains exports for inputs/fitlers/transitions/outputs. - * See obs-source.h and obs-output.h for more information on the exports to - * use. + * If the module already exists then the function will return successful, and + * the module parameter will be given the pointer to the existing module. + * + * This does not initialize the module, it only loads the module image. To + * initialize the module, call obs_init_module. + * + * @param module The pointer to the created module. + * @param path Specifies the path to the module library file. If the + * extension is not specified, it will use the extension + * appropriate to the operating system. + * @param data_path Specifies the path to the directory where the module's + * data files are stored. + * @returns MODULE_SUCCESS if successful + * MODULE_ERROR if a generic error occurred + * MODULE_FILE_NOT_FOUND if the module was not found + * MODULE_MISSING_EXPORTS if required exports are missing + * MODULE_INCOMPATIBLE_VER if incompatible version */ -EXPORT int obs_load_module(const char *path); +EXPORT int obs_open_module(obs_module_t *module, const char *path, + const char *data_path); + +/** + * Initializes the module, which calls its obs_module_load export. If the + * module is alrady loaded, then this function does nothing and returns + * successful. + */ +EXPORT bool obs_init_module(obs_module_t module); + +/** + * Adds a module search path to be used with obs_find_modules. If the search + * path strings contain %module%, that text will be replaced with the module + * name when used. + * + * @param bin Specifies the module's binary directory search path. + * @param data Specifies the module's data directory search path. + */ +EXPORT void obs_add_module_path(const char *bin, const char *data); + +/** Automatically loads all modules from module paths (convenience function) */ +EXPORT void obs_load_all_modules(void); + +struct obs_module_info { + const char *bin_path; + const char *data_path; +}; + +typedef void (*obs_find_module_callback_t)(void *param, + const struct obs_module_info *info); + +/** Finds all modules within the search paths added by obs_add_module_path. */ +EXPORT void obs_find_modules(obs_find_module_callback_t callback, void *param); + +typedef void (*obs_enum_module_callback_t)(void *param, obs_module_t module); + +/** Enumerates all loaded modules */ +EXPORT void obs_enum_modules(obs_enum_module_callback_t callback, void *param); /** Helper function for using default module locale */ -EXPORT lookup_t obs_module_load_locale(const char *module, +EXPORT lookup_t obs_module_load_locale(obs_module_t module, const char *default_locale, const char *locale); +/** + * Returns the location of a plugin module data file. + * + * @note Modules should use obs_module_file function defined in obs-module.h + * as a more elegant means of getting their files without having to + * specify the module parameter. + * + * @param module The module associated with the file to locate + * @param file The file to locate + * @return Path string, or NULL if not found. Use bfree to free string. + */ +EXPORT char *obs_find_module_file(obs_module_t module, const char *file); + /** * Enumerates all available inputs source types. * @@ -368,14 +434,6 @@ EXPORT obs_encoder_t obs_get_encoder_by_name(const char *name); /** Gets an service by its name. */ EXPORT obs_service_t obs_get_service_by_name(const char *name); -/** - * Returns the location of a plugin data file. - * - * file: Name of file to locate. For example, "myplugin/mydata.data" - * returns: Path string, or NULL if not found. Use bfree to free string. - */ -EXPORT char *obs_find_plugin_file(const char *file); - /** Returns the default effect for generic RGB/YUV drawing */ EXPORT effect_t obs_get_default_effect(void); diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 483a594af..1bad5943b 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -534,28 +534,7 @@ void OBSBasic::OBSInit() InitOBSCallbacks(); - /* TODO: this is a test, all modules will be searched for and loaded - * automatically later */ - obs_load_module("image-source"); - // obs_load_module("test-input"); - obs_load_module("obs-ffmpeg"); - obs_load_module("obs-libfdk"); - obs_load_module("obs-x264"); - obs_load_module("obs-outputs"); - obs_load_module("rtmp-services"); -#ifdef __APPLE__ - obs_load_module("mac-avcapture"); - obs_load_module("mac-capture"); -#elif _WIN32 - obs_load_module("win-wasapi"); - obs_load_module("win-capture"); - obs_load_module("win-dshow"); -#else - obs_load_module("linux-xshm"); - obs_load_module("linux-xcomposite"); - obs_load_module("linux-pulseaudio"); - obs_load_module("linux-v4l2"); -#endif + obs_load_all_modules(); if (!InitOutputs()) throw "Failed to initialize outputs"; diff --git a/plugins/mac-capture/mac-display-capture.m b/plugins/mac-capture/mac-display-capture.m index e05ff7e78..1d1f7eefc 100644 --- a/plugins/mac-capture/mac-display-capture.m +++ b/plugins/mac-capture/mac-display-capture.m @@ -181,8 +181,7 @@ static void *display_capture_create(obs_data_t settings, if (!dc->sampler) goto fail; - char *effect_file = obs_find_plugin_file( - "mac-capture/draw_rect.effect"); + char *effect_file = obs_module_file("draw_rect.effect"); dc->draw_effect = gs_create_effect_from_file(effect_file, NULL); bfree(effect_file); if (!dc->draw_effect) diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index 315a53dd9..1de5f8c04 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -207,7 +207,7 @@ static obs_properties_t rtmp_common_properties(void) obs_module_text("Service"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); - file = obs_find_plugin_file("rtmp-services/services.json"); + file = obs_module_file("services.json"); if (file) { json_t *root = build_service_list(list, file); obs_properties_set_param(ppts, root, properties_data_destroy); @@ -305,7 +305,7 @@ static bool rtmp_common_initialize(void *data, obs_output_t output) struct rtmp_common *service = data; char *file; - file = obs_find_plugin_file("rtmp-services/services.json"); + file = obs_module_file("services.json"); if (file) { json_t *root = open_json_file(file); if (root) { diff --git a/plugins/win-capture/dc-capture.c b/plugins/win-capture/dc-capture.c index abd6f1f05..090ba809a 100644 --- a/plugins/win-capture/dc-capture.c +++ b/plugins/win-capture/dc-capture.c @@ -216,7 +216,7 @@ effect_t create_opaque_effect(void) char *effect_file; char *error_string = NULL; - effect_file = obs_find_plugin_file("win-capture/opaque.effect"); + effect_file = obs_module_file("opaque.effect"); if (!effect_file) { blog(LOG_ERROR, "[create_opaque_effect] Could not find " "opaque effect file"); diff --git a/test/osx/test.mm b/test/osx/test.mm index 02a39179b..593243527 100644 --- a/test/osx/test.mm +++ b/test/osx/test.mm @@ -62,9 +62,8 @@ static void CreateOBS(NSView *view) static SceneContext SetupScene() { /* ------------------------------------------------------ */ - /* load module */ - if (obs_load_module("test-input") != 0) - throw "Couldn't load module"; + /* load modules */ + obs_load_all_modules(); /* ------------------------------------------------------ */ /* create source */ diff --git a/test/test-input/test-filter.c b/test/test-input/test-filter.c index ae993c6c8..e232b256a 100644 --- a/test/test-input/test-filter.c +++ b/test/test-input/test-filter.c @@ -1,4 +1,4 @@ -#include +#include struct test_filter { obs_source_t source; @@ -31,7 +31,7 @@ static void *filter_create(obs_data_t settings, obs_source_t source) gs_entercontext(obs_graphics()); - effect_file = obs_find_plugin_file("test-input/test.effect"); + effect_file = obs_module_file("test.effect"); tf->source = source; tf->whatever = gs_create_effect_from_file(effect_file, NULL); diff --git a/test/win/test.cpp b/test/win/test.cpp index c9abea957..b35acce4c 100644 --- a/test/win/test.cpp +++ b/test/win/test.cpp @@ -152,9 +152,8 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, CreateOBS(hwnd); /* ------------------------------------------------------ */ - /* load module */ - if (obs_load_module("test-input") != 0) - throw "Couldn't load module"; + /* load modules */ + obs_load_all_modules(); /* ------------------------------------------------------ */ /* create source */