|
@@ -24,6 +24,8 @@
|
|
|
|
|
|
extern const char *get_module_extension(void);
|
|
|
|
|
|
+obs_module_t *loadingModule = NULL;
|
|
|
+
|
|
|
static inline int req_func_not_found(const char *name, const char *path)
|
|
|
{
|
|
|
blog(LOG_DEBUG,
|
|
@@ -94,6 +96,47 @@ static inline char *get_module_name(const char *file)
|
|
|
extern void reset_win32_symbol_paths(void);
|
|
|
#endif
|
|
|
|
|
|
+int obs_module_load_metadata(struct obs_module *mod)
|
|
|
+{
|
|
|
+ struct obs_module_metadata *md = NULL;
|
|
|
+
|
|
|
+ /* Check if the metadata file exists */
|
|
|
+ struct dstr path = {0};
|
|
|
+
|
|
|
+ dstr_copy(&path, mod->data_path);
|
|
|
+ if (!dstr_is_empty(&path) && dstr_end(&path) != '/') {
|
|
|
+ dstr_cat_ch(&path, '/');
|
|
|
+ }
|
|
|
+ dstr_cat(&path, "manifest.json");
|
|
|
+
|
|
|
+ if (os_file_exists(path.array)) {
|
|
|
+ /* If we find a metadata file, allocate a new metadata. */
|
|
|
+ md = bmalloc(sizeof(obs_module_metadata_t));
|
|
|
+ obs_data_t *metadata = obs_data_create_from_json_file(path.array);
|
|
|
+
|
|
|
+ md->display_name = bstrdup(obs_data_get_string(metadata, "display_name"));
|
|
|
+ md->id = bstrdup(obs_data_get_string(metadata, "id"));
|
|
|
+ md->version = bstrdup(obs_data_get_string(metadata, "version"));
|
|
|
+ md->os_arch = bstrdup(obs_data_get_string(metadata, "os_arch"));
|
|
|
+ md->name = bstrdup(obs_data_get_string(metadata, "name"));
|
|
|
+ md->description = bstrdup(obs_data_get_string(metadata, "description"));
|
|
|
+ md->long_description = bstrdup(obs_data_get_string(metadata, "long_description"));
|
|
|
+
|
|
|
+ obs_data_t *urls = obs_data_get_obj(metadata, "urls");
|
|
|
+ md->repository_url = bstrdup(obs_data_get_string(urls, "repository"));
|
|
|
+ md->website_url = bstrdup(obs_data_get_string(urls, "website"));
|
|
|
+ md->support_url = bstrdup(obs_data_get_string(urls, "support"));
|
|
|
+ obs_data_release(urls);
|
|
|
+
|
|
|
+ md->has_banner = obs_data_get_bool(metadata, "has_banner");
|
|
|
+ md->has_icon = obs_data_get_bool(metadata, "has_icon");
|
|
|
+ obs_data_release(metadata);
|
|
|
+ }
|
|
|
+ dstr_free(&path);
|
|
|
+ mod->metadata = md;
|
|
|
+ return MODULE_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
int obs_open_module(obs_module_t **module, const char *path, const char *data_path)
|
|
|
{
|
|
|
struct obs_module mod = {0};
|
|
@@ -138,11 +181,19 @@ int obs_open_module(obs_module_t **module, const char *path, const char *data_pa
|
|
|
mod.mod_name = get_module_name(mod.file);
|
|
|
mod.data_path = bstrdup(data_path);
|
|
|
mod.next = obs->first_module;
|
|
|
+ mod.load_state = OBS_MODULE_ENABLED;
|
|
|
+
|
|
|
+ da_init(mod.sources);
|
|
|
+ da_init(mod.outputs);
|
|
|
+ da_init(mod.encoders);
|
|
|
+ da_init(mod.services);
|
|
|
|
|
|
if (mod.file) {
|
|
|
blog(LOG_DEBUG, "Loading module: %s", mod.file);
|
|
|
}
|
|
|
|
|
|
+ obs_module_load_metadata(&mod);
|
|
|
+
|
|
|
*module = bmemdup(&mod, sizeof(mod));
|
|
|
obs->first_module = (*module);
|
|
|
mod.set_pointer(*module);
|
|
@@ -153,6 +204,32 @@ int obs_open_module(obs_module_t **module, const char *path, const char *data_pa
|
|
|
return MODULE_SUCCESS;
|
|
|
}
|
|
|
|
|
|
+bool obs_create_disabled_module(obs_module_t **module, const char *path, const char *data_path,
|
|
|
+ enum obs_module_load_state state)
|
|
|
+{
|
|
|
+ struct obs_module mod = {0};
|
|
|
+
|
|
|
+ mod.bin_path = bstrdup(path);
|
|
|
+ mod.file = strrchr(mod.bin_path, '/');
|
|
|
+ mod.file = (!mod.file) ? mod.bin_path : (mod.file + 1);
|
|
|
+ mod.mod_name = get_module_name(mod.file);
|
|
|
+ mod.data_path = bstrdup(data_path);
|
|
|
+ mod.next = obs->first_disabled_module;
|
|
|
+ mod.load_state = state;
|
|
|
+
|
|
|
+ da_init(mod.sources);
|
|
|
+ da_init(mod.outputs);
|
|
|
+ da_init(mod.encoders);
|
|
|
+ da_init(mod.services);
|
|
|
+
|
|
|
+ obs_module_load_metadata(&mod);
|
|
|
+
|
|
|
+ *module = bmemdup(&mod, sizeof(mod));
|
|
|
+ obs->first_disabled_module = (*module);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
bool obs_init_module(obs_module_t *module)
|
|
|
{
|
|
|
if (!module || !obs)
|
|
@@ -164,7 +241,10 @@ bool obs_init_module(obs_module_t *module)
|
|
|
profile_store_name(obs_get_profiler_name_store(), "obs_init_module(%s)", module->file);
|
|
|
profile_start(profile_name);
|
|
|
|
|
|
+ loadingModule = module;
|
|
|
module->loaded = module->load();
|
|
|
+ loadingModule = NULL;
|
|
|
+
|
|
|
if (!module->loaded)
|
|
|
blog(LOG_WARNING, "Failed to initialize module '%s'", module->file);
|
|
|
|
|
@@ -187,6 +267,10 @@ const char *obs_get_module_file_name(obs_module_t *module)
|
|
|
|
|
|
const char *obs_get_module_name(obs_module_t *module)
|
|
|
{
|
|
|
+ if (module && module->metadata && module->metadata->display_name) {
|
|
|
+ return module->metadata->display_name;
|
|
|
+ }
|
|
|
+
|
|
|
return (module && module->name) ? module->name() : NULL;
|
|
|
}
|
|
|
|
|
@@ -210,6 +294,48 @@ const char *obs_get_module_data_path(obs_module_t *module)
|
|
|
return module ? module->data_path : NULL;
|
|
|
}
|
|
|
|
|
|
+const char *obs_get_module_id(obs_module_t *module)
|
|
|
+{
|
|
|
+ return module && module->metadata ? module->metadata->id : NULL;
|
|
|
+}
|
|
|
+
|
|
|
+const char *obs_get_module_version(obs_module_t *module)
|
|
|
+{
|
|
|
+ return module && module->metadata ? module->metadata->version : NULL;
|
|
|
+}
|
|
|
+
|
|
|
+void obs_module_add_source(obs_module_t *module, const char *id)
|
|
|
+{
|
|
|
+ char *source_id = bstrdup(id);
|
|
|
+ if (module) {
|
|
|
+ da_push_back(module->sources, &source_id);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void obs_module_add_output(obs_module_t *module, const char *id)
|
|
|
+{
|
|
|
+ char *output_id = bstrdup(id);
|
|
|
+ if (module) {
|
|
|
+ da_push_back(module->outputs, &output_id);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void obs_module_add_encoder(obs_module_t *module, const char *id)
|
|
|
+{
|
|
|
+ char *encoder_id = bstrdup(id);
|
|
|
+ if (module) {
|
|
|
+ da_push_back(module->encoders, &encoder_id);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void obs_module_add_service(obs_module_t *module, const char *id)
|
|
|
+{
|
|
|
+ char *service_id = bstrdup(id);
|
|
|
+ if (module) {
|
|
|
+ da_push_back(module->services, &service_id);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
obs_module_t *obs_get_module(const char *name)
|
|
|
{
|
|
|
obs_module_t *module = obs->first_module;
|
|
@@ -224,6 +350,20 @@ obs_module_t *obs_get_module(const char *name)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+obs_module_t *obs_get_disabled_module(const char *name)
|
|
|
+{
|
|
|
+ obs_module_t *module = obs->first_disabled_module;
|
|
|
+ while (module) {
|
|
|
+ if (strcmp(module->mod_name, name) == 0) {
|
|
|
+ return module;
|
|
|
+ }
|
|
|
+
|
|
|
+ module = module->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
void *obs_get_module_lib(obs_module_t *module)
|
|
|
{
|
|
|
return module ? module->module : NULL;
|
|
@@ -284,6 +424,24 @@ void obs_add_safe_module(const char *name)
|
|
|
da_push_back(obs->safe_modules, &item);
|
|
|
}
|
|
|
|
|
|
+void obs_add_core_module(const char *name)
|
|
|
+{
|
|
|
+ if (!obs || !name)
|
|
|
+ return;
|
|
|
+
|
|
|
+ char *item = bstrdup(name);
|
|
|
+ da_push_back(obs->core_modules, &item);
|
|
|
+}
|
|
|
+
|
|
|
+void obs_add_disabled_module(const char *name)
|
|
|
+{
|
|
|
+ if (!obs || !name)
|
|
|
+ return;
|
|
|
+
|
|
|
+ char *item = bstrdup(name);
|
|
|
+ da_push_back(obs->disabled_modules, &item);
|
|
|
+}
|
|
|
+
|
|
|
extern void get_plugin_info(const char *path, bool *is_obs_plugin, bool *can_load);
|
|
|
|
|
|
struct fail_info {
|
|
@@ -304,10 +462,39 @@ static bool is_safe_module(const char *name)
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+static bool is_core_module(const char *name)
|
|
|
+{
|
|
|
+ for (size_t i = 0; i < obs->core_modules.num; i++) {
|
|
|
+ if (strcmp(name, obs->core_modules.array[i]) == 0)
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool is_disabled_module(const char *name)
|
|
|
+{
|
|
|
+ if (obs->disabled_modules.num == 0)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ for (size_t i = 0; i < obs->disabled_modules.num; i++) {
|
|
|
+ if (strcmp(name, obs->disabled_modules.array[i]) == 0)
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool obs_get_module_allow_disable(const char *name)
|
|
|
+{
|
|
|
+ return !is_core_module(name);
|
|
|
+}
|
|
|
+
|
|
|
static void load_all_callback(void *param, const struct obs_module_info2 *info)
|
|
|
{
|
|
|
struct fail_info *fail_info = param;
|
|
|
obs_module_t *module;
|
|
|
+ obs_module_t *disabled_module;
|
|
|
|
|
|
bool is_obs_plugin;
|
|
|
bool can_load_obs_plugin;
|
|
@@ -320,10 +507,17 @@ static void load_all_callback(void *param, const struct obs_module_info2 *info)
|
|
|
}
|
|
|
|
|
|
if (!is_safe_module(info->name)) {
|
|
|
+ obs_create_disabled_module(&disabled_module, info->bin_path, info->data_path, OBS_MODULE_DISABLED_SAFE);
|
|
|
blog(LOG_WARNING, "Skipping module '%s', not on safe list", info->name);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ if (is_disabled_module(info->name)) {
|
|
|
+ obs_create_disabled_module(&disabled_module, info->bin_path, info->data_path, OBS_MODULE_DISABLED);
|
|
|
+ blog(LOG_WARNING, "Skipping module '%s', is disabled", info->name);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
if (!can_load_obs_plugin) {
|
|
|
blog(LOG_WARNING,
|
|
|
"Skipping module '%s' due to possible "
|
|
@@ -350,8 +544,10 @@ static void load_all_callback(void *param, const struct obs_module_info2 *info)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (!obs_init_module(module))
|
|
|
+ if (!obs_init_module(module)) {
|
|
|
free_module(module);
|
|
|
+ obs_create_disabled_module(&disabled_module, info->bin_path, info->data_path, OBS_MODULE_ERROR);
|
|
|
+ }
|
|
|
|
|
|
UNUSED_PARAMETER(param);
|
|
|
return;
|
|
@@ -602,19 +798,57 @@ void free_module(struct obs_module *mod)
|
|
|
/* os_dlclose(mod->module); */
|
|
|
}
|
|
|
|
|
|
- for (obs_module_t *m = obs->first_module; !!m; m = m->next) {
|
|
|
- if (m->next == mod) {
|
|
|
- m->next = mod->next;
|
|
|
- break;
|
|
|
+ /* Is this module an active / loaded module, or a disabled module? */
|
|
|
+ if (mod->load_state == OBS_MODULE_ENABLED) {
|
|
|
+ for (obs_module_t *m = obs->first_module; !!m; m = m->next) {
|
|
|
+ if (m->next == mod) {
|
|
|
+ m->next = mod->next;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (obs->first_module == mod)
|
|
|
+ obs->first_module = mod->next;
|
|
|
+ } else {
|
|
|
+ for (obs_module_t *m = obs->first_disabled_module; !!m; m = m->next) {
|
|
|
+ if (m->next == mod) {
|
|
|
+ m->next = mod->next;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if (obs->first_module == mod)
|
|
|
- obs->first_module = mod->next;
|
|
|
+ if (obs->first_disabled_module == mod)
|
|
|
+ obs->first_disabled_module = mod->next;
|
|
|
+ }
|
|
|
|
|
|
bfree(mod->mod_name);
|
|
|
bfree(mod->bin_path);
|
|
|
bfree(mod->data_path);
|
|
|
+
|
|
|
+ for (size_t i = 0; i < mod->sources.num; i++) {
|
|
|
+ bfree(mod->sources.array[i]);
|
|
|
+ }
|
|
|
+ da_free(mod->sources);
|
|
|
+
|
|
|
+ for (size_t i = 0; i < mod->outputs.num; i++) {
|
|
|
+ bfree(mod->outputs.array[i]);
|
|
|
+ }
|
|
|
+ da_free(mod->outputs);
|
|
|
+
|
|
|
+ for (size_t i = 0; i < mod->encoders.num; i++) {
|
|
|
+ bfree(mod->encoders.array[i]);
|
|
|
+ }
|
|
|
+ da_free(mod->encoders);
|
|
|
+
|
|
|
+ for (size_t i = 0; i < mod->services.num; i++) {
|
|
|
+ bfree(mod->services.array[i]);
|
|
|
+ }
|
|
|
+ da_free(mod->services);
|
|
|
+
|
|
|
+ if (mod->metadata) {
|
|
|
+ free_module_metadata(mod->metadata);
|
|
|
+ bfree(mod->metadata);
|
|
|
+ }
|
|
|
bfree(mod);
|
|
|
}
|
|
|
|
|
@@ -754,6 +988,13 @@ void obs_register_source_s(const struct obs_source_info *info, size_t size)
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
+ /* NOTE: The assignment of data.module must occur before memcpy! */
|
|
|
+ if (loadingModule) {
|
|
|
+ data.module = loadingModule;
|
|
|
+ char *source_id = bstrdup(info->id);
|
|
|
+ da_push_back(loadingModule->sources, &source_id);
|
|
|
+ }
|
|
|
+
|
|
|
memcpy(&data, info, size);
|
|
|
|
|
|
/* mark audio-only filters as an async filter categorically */
|
|
@@ -875,6 +1116,7 @@ void obs_register_output_s(const struct obs_output_info *info, size_t size)
|
|
|
}
|
|
|
strlist_free(protocols);
|
|
|
}
|
|
|
+
|
|
|
return;
|
|
|
|
|
|
error:
|
|
@@ -911,6 +1153,12 @@ void obs_register_encoder_s(const struct obs_encoder_info *info, size_t size)
|
|
|
#undef CHECK_REQUIRED_VAL_
|
|
|
|
|
|
REGISTER_OBS_DEF(size, obs_encoder_info, obs->encoder_types, info);
|
|
|
+
|
|
|
+ if (loadingModule) {
|
|
|
+ char *encoder_id = bstrdup(info->id);
|
|
|
+ da_push_back(loadingModule->encoders, &encoder_id);
|
|
|
+ }
|
|
|
+
|
|
|
return;
|
|
|
|
|
|
error:
|
|
@@ -934,6 +1182,12 @@ void obs_register_service_s(const struct obs_service_info *info, size_t size)
|
|
|
#undef CHECK_REQUIRED_VAL_
|
|
|
|
|
|
REGISTER_OBS_DEF(size, obs_service_info, obs->service_types, info);
|
|
|
+
|
|
|
+ if (loadingModule) {
|
|
|
+ char *service_id = bstrdup(info->id);
|
|
|
+ da_push_back(loadingModule->services, &service_id);
|
|
|
+ }
|
|
|
+
|
|
|
return;
|
|
|
|
|
|
error:
|