obs-module.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. /******************************************************************************
  2. Copyright (C) 2013-2014 by Hugh Bailey <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "util/platform.h"
  15. #include "util/dstr.h"
  16. #include "obs-defs.h"
  17. #include "obs-internal.h"
  18. #include "obs-module.h"
  19. extern const char *get_module_extension(void);
  20. static inline int req_func_not_found(const char *name, const char *path)
  21. {
  22. blog(LOG_ERROR, "Required module function '%s' in module '%s' not "
  23. "found, loading of module failed",
  24. name, path);
  25. return MODULE_MISSING_EXPORTS;
  26. }
  27. static int load_module_exports(struct obs_module *mod, const char *path)
  28. {
  29. mod->load = os_dlsym(mod->module, "obs_module_load");
  30. if (!mod->load)
  31. return req_func_not_found("obs_module_load", path);
  32. mod->set_pointer = os_dlsym(mod->module, "obs_module_set_pointer");
  33. if (!mod->set_pointer)
  34. return req_func_not_found("obs_module_set_pointer", path);
  35. mod->ver = os_dlsym(mod->module, "obs_module_ver");
  36. if (!mod->ver)
  37. return req_func_not_found("obs_module_ver", path);
  38. /* optional exports */
  39. mod->unload = os_dlsym(mod->module, "obs_module_unload");
  40. mod->set_locale = os_dlsym(mod->module, "obs_module_set_locale");
  41. mod->free_locale = os_dlsym(mod->module, "obs_module_free_locale");
  42. mod->name = os_dlsym(mod->module, "obs_module_name");
  43. mod->description = os_dlsym(mod->module, "obs_module_description");
  44. mod->author = os_dlsym(mod->module, "obs_module_author");
  45. return MODULE_SUCCESS;
  46. }
  47. int obs_open_module(obs_module_t **module, const char *path,
  48. const char *data_path)
  49. {
  50. struct obs_module mod = {0};
  51. int errorcode;
  52. if (!module || !path || !obs)
  53. return MODULE_ERROR;
  54. mod.module = os_dlopen(path);
  55. if (!mod.module) {
  56. blog(LOG_WARNING, "Module '%s' not found", path);
  57. return MODULE_FILE_NOT_FOUND;
  58. }
  59. errorcode = load_module_exports(&mod, path);
  60. if (errorcode != MODULE_SUCCESS)
  61. return errorcode;
  62. mod.bin_path = bstrdup(path);
  63. mod.file = strrchr(mod.bin_path, '/');
  64. mod.file = (!mod.file) ? mod.bin_path : (mod.file + 1);
  65. mod.data_path = bstrdup(data_path);
  66. mod.next = obs->first_module;
  67. if (mod.file) {
  68. blog(LOG_INFO, "---------------------------------\n"
  69. "Loading module: %s", mod.file);
  70. }
  71. *module = bmemdup(&mod, sizeof(mod));
  72. obs->first_module = (*module);
  73. mod.set_pointer(*module);
  74. if (mod.set_locale)
  75. mod.set_locale(obs->locale);
  76. return MODULE_SUCCESS;
  77. }
  78. bool obs_init_module(obs_module_t *module)
  79. {
  80. if (!module || !obs)
  81. return false;
  82. if (module->loaded)
  83. return true;
  84. const char *profile_name =
  85. profile_store_name(obs_get_profiler_name_store(),
  86. "obs_init_module(%s)", module->file);
  87. profile_start(profile_name);
  88. module->loaded = module->load();
  89. if (!module->loaded)
  90. blog(LOG_WARNING, "Failed to initialize module '%s'",
  91. module->file);
  92. profile_end(profile_name);
  93. return module->loaded;
  94. }
  95. const char *obs_get_module_file_name(obs_module_t *module)
  96. {
  97. return module ? module->file : NULL;
  98. }
  99. const char *obs_get_module_name(obs_module_t *module)
  100. {
  101. return (module && module->name) ? module->name() : NULL;
  102. }
  103. const char *obs_get_module_author(obs_module_t *module)
  104. {
  105. return (module && module->author) ? module->author() : NULL;
  106. }
  107. const char *obs_get_module_description(obs_module_t *module)
  108. {
  109. return (module && module->description) ? module->description() : NULL;
  110. }
  111. const char *obs_get_module_binary_path(obs_module_t *module)
  112. {
  113. return module ? module->bin_path : NULL;
  114. }
  115. const char *obs_get_module_data_path(obs_module_t *module)
  116. {
  117. return module ? module->data_path : NULL;
  118. }
  119. char *obs_find_module_file(obs_module_t *module, const char *file)
  120. {
  121. struct dstr output = {0};
  122. if (!module)
  123. return NULL;
  124. dstr_copy(&output, module->data_path);
  125. if (!dstr_is_empty(&output) && dstr_end(&output) != '/')
  126. dstr_cat_ch(&output, '/');
  127. dstr_cat(&output, file);
  128. if (!os_file_exists(output.array))
  129. dstr_free(&output);
  130. return output.array;
  131. }
  132. void obs_add_module_path(const char *bin, const char *data)
  133. {
  134. struct obs_module_path omp;
  135. if (!obs || !bin || !data) return;
  136. omp.bin = bstrdup(bin);
  137. omp.data = bstrdup(data);
  138. da_push_back(obs->module_paths, &omp);
  139. }
  140. static void load_all_callback(void *param, const struct obs_module_info *info)
  141. {
  142. obs_module_t *module;
  143. int code = obs_open_module(&module, info->bin_path, info->data_path);
  144. if (code != MODULE_SUCCESS) {
  145. blog(LOG_DEBUG, "Failed to load module file '%s': %d",
  146. info->bin_path, code);
  147. return;
  148. }
  149. obs_init_module(module);
  150. UNUSED_PARAMETER(param);
  151. }
  152. static const char *obs_load_all_modules_name = "obs_load_all_modules";
  153. void obs_load_all_modules(void)
  154. {
  155. profile_start(obs_load_all_modules_name);
  156. obs_find_modules(load_all_callback, NULL);
  157. profile_end(obs_load_all_modules_name);
  158. }
  159. static inline void make_data_dir(struct dstr *parsed_data_dir,
  160. const char *data_dir, const char *name)
  161. {
  162. dstr_copy(parsed_data_dir, data_dir);
  163. dstr_replace(parsed_data_dir, "%module%", name);
  164. if (dstr_end(parsed_data_dir) == '/')
  165. dstr_resize(parsed_data_dir, parsed_data_dir->len - 1);
  166. }
  167. static char *make_data_directory(const char *module_name, const char *data_dir)
  168. {
  169. struct dstr parsed_data_dir = {0};
  170. bool found = false;
  171. make_data_dir(&parsed_data_dir, data_dir, module_name);
  172. found = os_file_exists(parsed_data_dir.array);
  173. if (!found && astrcmpi_n(module_name, "lib", 3) == 0)
  174. make_data_dir(&parsed_data_dir, data_dir, module_name + 3);
  175. return parsed_data_dir.array;
  176. }
  177. static bool parse_binary_from_directory(struct dstr *parsed_bin_path,
  178. const char *bin_path, const char *file)
  179. {
  180. struct dstr directory = {0};
  181. bool found = true;
  182. dstr_copy(&directory, bin_path);
  183. dstr_replace(&directory, "%module%", file);
  184. if (dstr_end(&directory) != '/')
  185. dstr_cat_ch(&directory, '/');
  186. dstr_copy_dstr(parsed_bin_path, &directory);
  187. dstr_cat(parsed_bin_path, file);
  188. dstr_cat(parsed_bin_path, get_module_extension());
  189. if (!os_file_exists(parsed_bin_path->array)) {
  190. /* if the file doesn't exist, check with 'lib' prefix */
  191. dstr_copy_dstr(parsed_bin_path, &directory);
  192. dstr_cat(parsed_bin_path, "lib");
  193. dstr_cat(parsed_bin_path, file);
  194. dstr_cat(parsed_bin_path, get_module_extension());
  195. /* if neither exist, don't include this as a library */
  196. if (!os_file_exists(parsed_bin_path->array)) {
  197. dstr_free(parsed_bin_path);
  198. found = false;
  199. }
  200. }
  201. dstr_free(&directory);
  202. return found;
  203. }
  204. static void process_found_module(struct obs_module_path *omp,
  205. const char *path, bool directory,
  206. obs_find_module_callback_t callback, void *param)
  207. {
  208. struct obs_module_info info;
  209. struct dstr name = {0};
  210. struct dstr parsed_bin_path = {0};
  211. const char *file;
  212. char *parsed_data_dir;
  213. bool bin_found = true;
  214. file = strrchr(path, '/');
  215. file = file ? (file + 1) : path;
  216. if (strcmp(file, ".") == 0 || strcmp(file, "..") == 0)
  217. return;
  218. dstr_copy(&name, file);
  219. if (!directory) {
  220. char *ext = strrchr(name.array, '.');
  221. if (ext)
  222. dstr_resize(&name, ext - name.array);
  223. dstr_copy(&parsed_bin_path, path);
  224. } else {
  225. bin_found = parse_binary_from_directory(&parsed_bin_path,
  226. omp->bin, file);
  227. }
  228. parsed_data_dir = make_data_directory(name.array, omp->data);
  229. if (parsed_data_dir && bin_found) {
  230. info.bin_path = parsed_bin_path.array;
  231. info.data_path = parsed_data_dir;
  232. callback(param, &info);
  233. }
  234. bfree(parsed_data_dir);
  235. dstr_free(&name);
  236. dstr_free(&parsed_bin_path);
  237. }
  238. static void find_modules_in_path(struct obs_module_path *omp,
  239. obs_find_module_callback_t callback, void *param)
  240. {
  241. struct dstr search_path = {0};
  242. char *module_start;
  243. bool search_directories = false;
  244. os_glob_t *gi;
  245. dstr_copy(&search_path, omp->bin);
  246. module_start = strstr(search_path.array, "%module%");
  247. if (module_start) {
  248. dstr_resize(&search_path, module_start - search_path.array);
  249. search_directories = true;
  250. }
  251. if (!dstr_is_empty(&search_path) && dstr_end(&search_path) != '/')
  252. dstr_cat_ch(&search_path, '/');
  253. dstr_cat_ch(&search_path, '*');
  254. if (!search_directories)
  255. dstr_cat(&search_path, get_module_extension());
  256. if (os_glob(search_path.array, 0, &gi) == 0) {
  257. for (size_t i = 0; i < gi->gl_pathc; i++) {
  258. if (search_directories == gi->gl_pathv[i].directory)
  259. process_found_module(omp,
  260. gi->gl_pathv[i].path,
  261. search_directories,
  262. callback, param);
  263. }
  264. os_globfree(gi);
  265. }
  266. dstr_free(&search_path);
  267. }
  268. void obs_find_modules(obs_find_module_callback_t callback, void *param)
  269. {
  270. if (!obs)
  271. return;
  272. for (size_t i = 0; i < obs->module_paths.num; i++) {
  273. struct obs_module_path *omp = obs->module_paths.array + i;
  274. find_modules_in_path(omp, callback, param);
  275. }
  276. }
  277. void obs_enum_modules(obs_enum_module_callback_t callback, void *param)
  278. {
  279. struct obs_module *module;
  280. if (!obs)
  281. return;
  282. module = obs->first_module;
  283. while (module) {
  284. callback(param, module);
  285. module = module->next;
  286. }
  287. }
  288. void free_module(struct obs_module *mod)
  289. {
  290. if (!mod)
  291. return;
  292. if (mod->module) {
  293. if (mod->free_locale)
  294. mod->free_locale();
  295. if (mod->loaded && mod->unload)
  296. mod->unload();
  297. /* there is no real reason to close the dynamic libraries,
  298. * and sometimes this can cause issues. */
  299. /* os_dlclose(mod->module); */
  300. }
  301. bfree(mod->bin_path);
  302. bfree(mod->data_path);
  303. bfree(mod);
  304. }
  305. lookup_t *obs_module_load_locale(obs_module_t *module,
  306. const char *default_locale, const char *locale)
  307. {
  308. struct dstr str = {0};
  309. lookup_t *lookup = NULL;
  310. if (!module || !default_locale || !locale) {
  311. blog(LOG_WARNING, "obs_module_load_locale: Invalid parameters");
  312. return NULL;
  313. }
  314. dstr_copy(&str, "locale/");
  315. dstr_cat(&str, default_locale);
  316. dstr_cat(&str, ".ini");
  317. char *file = obs_find_module_file(module, str.array);
  318. if (file)
  319. lookup = text_lookup_create(file);
  320. bfree(file);
  321. if (!lookup) {
  322. blog(LOG_WARNING, "Failed to load '%s' text for module: '%s'",
  323. default_locale, module->file);
  324. goto cleanup;
  325. }
  326. if (astrcmpi(locale, default_locale) == 0)
  327. goto cleanup;
  328. dstr_copy(&str, "/locale/");
  329. dstr_cat(&str, locale);
  330. dstr_cat(&str, ".ini");
  331. file = obs_find_module_file(module, str.array);
  332. if (!text_lookup_add(lookup, file))
  333. blog(LOG_WARNING, "Failed to load '%s' text for module: '%s'",
  334. locale, module->file);
  335. bfree(file);
  336. cleanup:
  337. dstr_free(&str);
  338. return lookup;
  339. }
  340. #define REGISTER_OBS_DEF(size_var, structure, dest, info) \
  341. do { \
  342. struct structure data = {0}; \
  343. if (!size_var) { \
  344. blog(LOG_ERROR, "Tried to register " #structure \
  345. " outside of obs_module_load"); \
  346. return; \
  347. } \
  348. \
  349. memcpy(&data, info, size_var); \
  350. da_push_back(dest, &data); \
  351. } while (false)
  352. #define CHECK_REQUIRED_VAL(type, info, val, func) \
  353. do { \
  354. if ((offsetof(type, val) + sizeof(info->val) > size) || \
  355. !info->val) { \
  356. blog(LOG_ERROR, "Required value '" #val "' for " \
  357. "'%s' not found. " #func \
  358. " failed.", info->id); \
  359. return; \
  360. } \
  361. } while (false)
  362. void obs_register_source_s(const struct obs_source_info *info, size_t size)
  363. {
  364. struct obs_source_info data = {0};
  365. struct darray *array;
  366. if (info->type == OBS_SOURCE_TYPE_INPUT) {
  367. array = &obs->input_types.da;
  368. } else if (info->type == OBS_SOURCE_TYPE_FILTER) {
  369. array = &obs->filter_types.da;
  370. } else if (info->type == OBS_SOURCE_TYPE_TRANSITION) {
  371. array = &obs->transition_types.da;
  372. } else {
  373. blog(LOG_ERROR, "Tried to register unknown source type: %u",
  374. info->type);
  375. return;
  376. }
  377. if (find_source(array, info->id)) {
  378. blog(LOG_WARNING, "Source d '%s' already exists! "
  379. "Duplicate library?", info->id);
  380. return;
  381. }
  382. #define CHECK_REQUIRED_VAL_(info, val, func) \
  383. CHECK_REQUIRED_VAL(struct obs_source_info, info, val, func)
  384. CHECK_REQUIRED_VAL_(info, get_name, obs_register_source);
  385. CHECK_REQUIRED_VAL_(info, create, obs_register_source);
  386. CHECK_REQUIRED_VAL_(info, destroy, obs_register_source);
  387. if (info->type == OBS_SOURCE_TYPE_INPUT &&
  388. (info->output_flags & OBS_SOURCE_VIDEO) != 0 &&
  389. (info->output_flags & OBS_SOURCE_ASYNC) == 0) {
  390. CHECK_REQUIRED_VAL_(info, get_width, obs_register_source);
  391. CHECK_REQUIRED_VAL_(info, get_height, obs_register_source);
  392. }
  393. #undef CHECK_REQUIRED_VAL_
  394. memcpy(&data, info, size);
  395. /* mark audio-only filters as an async filter categorically */
  396. if (data.type == OBS_SOURCE_TYPE_FILTER) {
  397. if ((data.output_flags & OBS_SOURCE_VIDEO) == 0)
  398. data.output_flags |= OBS_SOURCE_ASYNC;
  399. }
  400. darray_push_back(sizeof(struct obs_source_info), array, &data);
  401. }
  402. void obs_register_output_s(const struct obs_output_info *info, size_t size)
  403. {
  404. if (find_output(info->id)) {
  405. blog(LOG_WARNING, "Output id '%s' already exists! "
  406. "Duplicate library?", info->id);
  407. return;
  408. }
  409. #define CHECK_REQUIRED_VAL_(info, val, func) \
  410. CHECK_REQUIRED_VAL(struct obs_output_info, info, val, func)
  411. CHECK_REQUIRED_VAL_(info, get_name, obs_register_output);
  412. CHECK_REQUIRED_VAL_(info, create, obs_register_output);
  413. CHECK_REQUIRED_VAL_(info, destroy, obs_register_output);
  414. CHECK_REQUIRED_VAL_(info, start, obs_register_output);
  415. CHECK_REQUIRED_VAL_(info, stop, obs_register_output);
  416. if (info->flags & OBS_OUTPUT_ENCODED) {
  417. CHECK_REQUIRED_VAL_(info, encoded_packet, obs_register_output);
  418. } else {
  419. if (info->flags & OBS_OUTPUT_VIDEO)
  420. CHECK_REQUIRED_VAL_(info, raw_video,
  421. obs_register_output);
  422. if (info->flags & OBS_OUTPUT_AUDIO)
  423. CHECK_REQUIRED_VAL_(info, raw_audio,
  424. obs_register_output);
  425. }
  426. #undef CHECK_REQUIRED_VAL_
  427. REGISTER_OBS_DEF(size, obs_output_info, obs->output_types, info);
  428. }
  429. void obs_register_encoder_s(const struct obs_encoder_info *info, size_t size)
  430. {
  431. if (find_encoder(info->id)) {
  432. blog(LOG_WARNING, "Encoder id '%s' already exists! "
  433. "Duplicate library?", info->id);
  434. return;
  435. }
  436. #define CHECK_REQUIRED_VAL_(info, val, func) \
  437. CHECK_REQUIRED_VAL(struct obs_encoder_info, info, val, func)
  438. CHECK_REQUIRED_VAL_(info, get_name, obs_register_encoder);
  439. CHECK_REQUIRED_VAL_(info, create, obs_register_encoder);
  440. CHECK_REQUIRED_VAL_(info, destroy, obs_register_encoder);
  441. CHECK_REQUIRED_VAL_(info, encode, obs_register_encoder);
  442. if (info->type == OBS_ENCODER_AUDIO)
  443. CHECK_REQUIRED_VAL_(info, get_frame_size, obs_register_encoder);
  444. #undef CHECK_REQUIRED_VAL_
  445. REGISTER_OBS_DEF(size, obs_encoder_info, obs->encoder_types, info);
  446. }
  447. void obs_register_service_s(const struct obs_service_info *info, size_t size)
  448. {
  449. if (find_service(info->id)) {
  450. blog(LOG_WARNING, "Service id '%s' already exists! "
  451. "Duplicate library?", info->id);
  452. return;
  453. }
  454. #define CHECK_REQUIRED_VAL_(info, val, func) \
  455. CHECK_REQUIRED_VAL(struct obs_service_info, info, val, func)
  456. CHECK_REQUIRED_VAL_(info, get_name, obs_register_service);
  457. CHECK_REQUIRED_VAL_(info, create, obs_register_service);
  458. CHECK_REQUIRED_VAL_(info, destroy, obs_register_service);
  459. #undef CHECK_REQUIRED_VAL_
  460. REGISTER_OBS_DEF(size, obs_service_info, obs->service_types, info);
  461. }
  462. void obs_regsiter_modal_ui_s(const struct obs_modal_ui *info, size_t size)
  463. {
  464. #define CHECK_REQUIRED_VAL_(info, val, func) \
  465. CHECK_REQUIRED_VAL(struct obs_modal_ui, info, val, func)
  466. CHECK_REQUIRED_VAL_(info, task, obs_regsiter_modal_ui);
  467. CHECK_REQUIRED_VAL_(info, target, obs_regsiter_modal_ui);
  468. CHECK_REQUIRED_VAL_(info, exec, obs_regsiter_modal_ui);
  469. #undef CHECK_REQUIRED_VAL_
  470. REGISTER_OBS_DEF(size, obs_modal_ui, obs->modal_ui_callbacks, info);
  471. }
  472. void obs_regsiter_modeless_ui_s(const struct obs_modeless_ui *info, size_t size)
  473. {
  474. #define CHECK_REQUIRED_VAL_(info, val, func) \
  475. CHECK_REQUIRED_VAL(struct obs_modeless_ui, info, val, func)
  476. CHECK_REQUIRED_VAL_(info, task, obs_regsiter_modeless_ui);
  477. CHECK_REQUIRED_VAL_(info, target, obs_regsiter_modeless_ui);
  478. CHECK_REQUIRED_VAL_(info, create, obs_regsiter_modeless_ui);
  479. #undef CHECK_REQUIRED_VAL_
  480. REGISTER_OBS_DEF(size, obs_modeless_ui, obs->modeless_ui_callbacks,
  481. info);
  482. }