obs-scripting-lua-source.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. /******************************************************************************
  2. Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
  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 "obs-scripting-lua.h"
  15. #include "cstrcache.h"
  16. #include <obs-module.h>
  17. /* ========================================================================= */
  18. static inline const char *get_table_string_(lua_State *script, int idx, const char *name, const char *func)
  19. {
  20. const char *str = "";
  21. lua_pushstring(script, name);
  22. lua_gettable(script, idx - 1);
  23. if (!lua_isstring(script, -1))
  24. warn("%s: no item '%s' of type %s", func, name, "string");
  25. else
  26. str = cstrcache_get(lua_tostring(script, -1));
  27. lua_pop(script, 1);
  28. return str;
  29. }
  30. static inline int get_table_int_(lua_State *script, int idx, const char *name, const char *func)
  31. {
  32. int val = 0;
  33. lua_pushstring(script, name);
  34. lua_gettable(script, idx - 1);
  35. val = (int)lua_tointeger(script, -1);
  36. lua_pop(script, 1);
  37. UNUSED_PARAMETER(func);
  38. return val;
  39. }
  40. static inline void get_callback_from_table_(lua_State *script, int idx, const char *name, int *p_reg_idx,
  41. const char *func)
  42. {
  43. *p_reg_idx = LUA_REFNIL;
  44. lua_pushstring(script, name);
  45. lua_gettable(script, idx - 1);
  46. if (!lua_isfunction(script, -1)) {
  47. if (!lua_isnil(script, -1)) {
  48. warn("%s: item '%s' is not a function", func, name);
  49. }
  50. lua_pop(script, 1);
  51. } else {
  52. *p_reg_idx = luaL_ref(script, LUA_REGISTRYINDEX);
  53. }
  54. }
  55. #define get_table_string(script, idx, name) get_table_string_(script, idx, name, __FUNCTION__)
  56. #define get_table_int(script, idx, name) get_table_int_(script, idx, name, __FUNCTION__)
  57. #define get_callback_from_table(script, idx, name, p_reg_idx) \
  58. get_callback_from_table_(script, idx, name, p_reg_idx, __FUNCTION__)
  59. bool ls_get_libobs_obj_(lua_State *script, const char *type, int lua_idx, void *libobs_out, const char *id,
  60. const char *func, int line)
  61. {
  62. swig_type_info *info = SWIG_TypeQuery(script, type);
  63. if (info == NULL) {
  64. warn("%s:%d: SWIG could not find type: %s%s%s", func, line, id ? id : "", id ? "::" : "", type);
  65. return false;
  66. }
  67. int ret = SWIG_ConvertPtr(script, lua_idx, libobs_out, info, 0);
  68. if (!SWIG_IsOK(ret)) {
  69. warn("%s:%d: SWIG failed to convert lua object to obs "
  70. "object: %s%s%s",
  71. func, line, id ? id : "", id ? "::" : "", type);
  72. return false;
  73. }
  74. return true;
  75. }
  76. #define ls_get_libobs_obj(type, lua_index, obs_obj) \
  77. ls_get_libobs_obj_(ls->script, #type " *", lua_index, obs_obj, ls->id, __FUNCTION__, __LINE__)
  78. bool ls_push_libobs_obj_(lua_State *script, const char *type, void *libobs_in, bool ownership, const char *id,
  79. const char *func, int line)
  80. {
  81. swig_type_info *info = SWIG_TypeQuery(script, type);
  82. if (info == NULL) {
  83. warn("%s:%d: SWIG could not find type: %s%s%s", func, line, id ? id : "", id ? "::" : "", type);
  84. return false;
  85. }
  86. SWIG_NewPointerObj(script, libobs_in, info, (int)ownership);
  87. return true;
  88. }
  89. #define ls_push_libobs_obj(type, obs_obj, ownership) \
  90. ls_push_libobs_obj_(ls->script, #type " *", obs_obj, ownership, ls->id, __FUNCTION__, __LINE__)
  91. /* ========================================================================= */
  92. struct obs_lua_data;
  93. struct obs_lua_source {
  94. struct obs_lua_script *data;
  95. lua_State *script;
  96. const char *id;
  97. const char *display_name;
  98. int func_create;
  99. int func_destroy;
  100. int func_get_width;
  101. int func_get_height;
  102. int func_get_defaults;
  103. int func_get_properties;
  104. int func_update;
  105. int func_activate;
  106. int func_deactivate;
  107. int func_show;
  108. int func_hide;
  109. int func_video_tick;
  110. int func_video_render;
  111. int func_save;
  112. int func_load;
  113. pthread_mutex_t definition_mutex;
  114. struct obs_lua_data *first_source;
  115. struct obs_lua_source *next;
  116. struct obs_lua_source **p_prev_next;
  117. };
  118. extern pthread_mutex_t lua_source_def_mutex;
  119. struct obs_lua_source *first_source_def = NULL;
  120. struct obs_lua_data {
  121. obs_source_t *source;
  122. struct obs_lua_source *ls;
  123. int lua_data_ref;
  124. struct obs_lua_data *next;
  125. struct obs_lua_data **p_prev_next;
  126. };
  127. #define call_func(name, args, rets) call_func_(ls->script, ls->func_##name, args, rets, #name, ls->display_name)
  128. #define have_func(name) (ls->func_##name != LUA_REFNIL)
  129. #define ls_push_data() lua_rawgeti(ls->script, LUA_REGISTRYINDEX, ld->lua_data_ref)
  130. #define ls_pop(count) lua_pop(ls->script, count)
  131. #define lock_script() \
  132. struct obs_lua_script *__data = ls->data; \
  133. struct obs_lua_script *__prev_script = current_lua_script; \
  134. current_lua_script = __data; \
  135. pthread_mutex_lock(&__data->mutex);
  136. #define unlock_script() \
  137. pthread_mutex_unlock(&__data->mutex); \
  138. current_lua_script = __prev_script;
  139. static const char *obs_lua_source_get_name(void *type_data)
  140. {
  141. struct obs_lua_source *ls = type_data;
  142. return ls->display_name;
  143. }
  144. static void *obs_lua_source_create(obs_data_t *settings, obs_source_t *source)
  145. {
  146. struct obs_lua_source *ls = obs_source_get_type_data(source);
  147. struct obs_lua_data *data = NULL;
  148. pthread_mutex_lock(&ls->definition_mutex);
  149. if (!ls->script)
  150. goto fail;
  151. if (!have_func(create))
  152. goto fail;
  153. lock_script();
  154. ls_push_libobs_obj(obs_data_t, settings, false);
  155. ls_push_libobs_obj(obs_source_t, source, false);
  156. call_func(create, 2, 1);
  157. int lua_data_ref = luaL_ref(ls->script, LUA_REGISTRYINDEX);
  158. if (lua_data_ref != LUA_REFNIL) {
  159. data = bmalloc(sizeof(*data));
  160. data->source = source;
  161. data->ls = ls;
  162. data->lua_data_ref = lua_data_ref;
  163. }
  164. unlock_script();
  165. if (data) {
  166. struct obs_lua_data *next = ls->first_source;
  167. data->next = next;
  168. data->p_prev_next = &ls->first_source;
  169. if (next)
  170. next->p_prev_next = &data->next;
  171. ls->first_source = data;
  172. }
  173. fail:
  174. pthread_mutex_unlock(&ls->definition_mutex);
  175. return data;
  176. }
  177. static void call_destroy(struct obs_lua_data *ld)
  178. {
  179. struct obs_lua_source *ls = ld->ls;
  180. ls_push_data();
  181. call_func(destroy, 1, 0);
  182. luaL_unref(ls->script, LUA_REGISTRYINDEX, ld->lua_data_ref);
  183. ld->lua_data_ref = LUA_REFNIL;
  184. }
  185. static void obs_lua_source_destroy(void *data)
  186. {
  187. struct obs_lua_data *ld = data;
  188. struct obs_lua_source *ls = ld->ls;
  189. struct obs_lua_data *next;
  190. pthread_mutex_lock(&ls->definition_mutex);
  191. if (!ls->script)
  192. goto fail;
  193. if (!have_func(destroy))
  194. goto fail;
  195. lock_script();
  196. call_destroy(ld);
  197. unlock_script();
  198. fail:
  199. next = ld->next;
  200. *ld->p_prev_next = next;
  201. if (next)
  202. next->p_prev_next = ld->p_prev_next;
  203. bfree(data);
  204. pthread_mutex_unlock(&ls->definition_mutex);
  205. }
  206. static uint32_t obs_lua_source_get_width(void *data)
  207. {
  208. struct obs_lua_data *ld = data;
  209. struct obs_lua_source *ls = ld->ls;
  210. uint32_t width = 0;
  211. pthread_mutex_lock(&ls->definition_mutex);
  212. if (!ls->script)
  213. goto fail;
  214. if (!have_func(get_width))
  215. goto fail;
  216. lock_script();
  217. ls_push_data();
  218. if (call_func(get_width, 1, 1)) {
  219. width = (uint32_t)lua_tointeger(ls->script, -1);
  220. ls_pop(1);
  221. }
  222. unlock_script();
  223. fail:
  224. pthread_mutex_unlock(&ls->definition_mutex);
  225. return width;
  226. }
  227. static uint32_t obs_lua_source_get_height(void *data)
  228. {
  229. struct obs_lua_data *ld = data;
  230. struct obs_lua_source *ls = ld->ls;
  231. uint32_t height = 0;
  232. pthread_mutex_lock(&ls->definition_mutex);
  233. if (!ls->script)
  234. goto fail;
  235. if (!have_func(get_height))
  236. goto fail;
  237. lock_script();
  238. ls_push_data();
  239. if (call_func(get_height, 1, 1)) {
  240. height = (uint32_t)lua_tointeger(ls->script, -1);
  241. ls_pop(1);
  242. }
  243. unlock_script();
  244. fail:
  245. pthread_mutex_unlock(&ls->definition_mutex);
  246. return height;
  247. }
  248. static void obs_lua_source_get_defaults(void *type_data, obs_data_t *settings)
  249. {
  250. struct obs_lua_source *ls = type_data;
  251. pthread_mutex_lock(&ls->definition_mutex);
  252. if (!ls->script)
  253. goto fail;
  254. if (!have_func(get_defaults))
  255. goto fail;
  256. lock_script();
  257. ls_push_libobs_obj(obs_data_t, settings, false);
  258. call_func(get_defaults, 1, 0);
  259. unlock_script();
  260. fail:
  261. pthread_mutex_unlock(&ls->definition_mutex);
  262. }
  263. static obs_properties_t *obs_lua_source_get_properties(void *data)
  264. {
  265. struct obs_lua_data *ld = data;
  266. struct obs_lua_source *ls = ld->ls;
  267. obs_properties_t *props = NULL;
  268. pthread_mutex_lock(&ls->definition_mutex);
  269. if (!ls->script)
  270. goto fail;
  271. if (!have_func(get_properties))
  272. goto fail;
  273. lock_script();
  274. ls_push_data();
  275. if (call_func(get_properties, 1, 1)) {
  276. ls_get_libobs_obj(obs_properties_t, -1, &props);
  277. ls_pop(1);
  278. }
  279. unlock_script();
  280. fail:
  281. pthread_mutex_unlock(&ls->definition_mutex);
  282. return props;
  283. }
  284. static void obs_lua_source_update(void *data, obs_data_t *settings)
  285. {
  286. struct obs_lua_data *ld = data;
  287. struct obs_lua_source *ls = ld->ls;
  288. pthread_mutex_lock(&ls->definition_mutex);
  289. if (!ls->script)
  290. goto fail;
  291. if (!have_func(update))
  292. goto fail;
  293. lock_script();
  294. ls_push_data();
  295. ls_push_libobs_obj(obs_data_t, settings, false);
  296. call_func(update, 2, 0);
  297. unlock_script();
  298. fail:
  299. pthread_mutex_unlock(&ls->definition_mutex);
  300. }
  301. #define DEFINE_VOID_DATA_CALLBACK(name) \
  302. static void obs_lua_source_##name(void *data) \
  303. { \
  304. struct obs_lua_data *ld = data; \
  305. struct obs_lua_source *ls = ld->ls; \
  306. if (!have_func(name)) \
  307. return; \
  308. lock_script(); \
  309. ls_push_data(); \
  310. call_func(name, 1, 0); \
  311. unlock_script(); \
  312. }
  313. DEFINE_VOID_DATA_CALLBACK(activate)
  314. DEFINE_VOID_DATA_CALLBACK(deactivate)
  315. DEFINE_VOID_DATA_CALLBACK(show)
  316. DEFINE_VOID_DATA_CALLBACK(hide)
  317. #undef DEFINE_VOID_DATA_CALLBACK
  318. static void obs_lua_source_video_tick(void *data, float seconds)
  319. {
  320. struct obs_lua_data *ld = data;
  321. struct obs_lua_source *ls = ld->ls;
  322. pthread_mutex_lock(&ls->definition_mutex);
  323. if (!ls->script)
  324. goto fail;
  325. if (!have_func(video_tick))
  326. goto fail;
  327. lock_script();
  328. ls_push_data();
  329. lua_pushnumber(ls->script, (double)seconds);
  330. call_func(video_tick, 2, 0);
  331. unlock_script();
  332. fail:
  333. pthread_mutex_unlock(&ls->definition_mutex);
  334. }
  335. static void obs_lua_source_video_render(void *data, gs_effect_t *effect)
  336. {
  337. struct obs_lua_data *ld = data;
  338. struct obs_lua_source *ls = ld->ls;
  339. pthread_mutex_lock(&ls->definition_mutex);
  340. if (!ls->script)
  341. goto fail;
  342. if (!have_func(video_render))
  343. goto fail;
  344. lock_script();
  345. ls_push_data();
  346. ls_push_libobs_obj(gs_effect_t, effect, false);
  347. call_func(video_render, 2, 0);
  348. unlock_script();
  349. fail:
  350. pthread_mutex_unlock(&ls->definition_mutex);
  351. }
  352. static void obs_lua_source_save(void *data, obs_data_t *settings)
  353. {
  354. struct obs_lua_data *ld = data;
  355. struct obs_lua_source *ls = ld->ls;
  356. pthread_mutex_lock(&ls->definition_mutex);
  357. if (!ls->script)
  358. goto fail;
  359. if (!have_func(save))
  360. goto fail;
  361. lock_script();
  362. ls_push_data();
  363. ls_push_libobs_obj(obs_data_t, settings, false);
  364. call_func(save, 2, 0);
  365. unlock_script();
  366. fail:
  367. pthread_mutex_unlock(&ls->definition_mutex);
  368. }
  369. static void obs_lua_source_load(void *data, obs_data_t *settings)
  370. {
  371. struct obs_lua_data *ld = data;
  372. struct obs_lua_source *ls = ld->ls;
  373. pthread_mutex_lock(&ls->definition_mutex);
  374. if (!ls->script)
  375. goto fail;
  376. if (!have_func(load))
  377. goto fail;
  378. lock_script();
  379. ls_push_data();
  380. ls_push_libobs_obj(obs_data_t, settings, false);
  381. call_func(load, 2, 0);
  382. unlock_script();
  383. fail:
  384. pthread_mutex_unlock(&ls->definition_mutex);
  385. }
  386. static void source_type_unload(struct obs_lua_source *ls)
  387. {
  388. #define unref(name) \
  389. luaL_unref(ls->script, LUA_REGISTRYINDEX, name); \
  390. name = LUA_REFNIL
  391. unref(ls->func_create);
  392. unref(ls->func_destroy);
  393. unref(ls->func_get_width);
  394. unref(ls->func_get_height);
  395. unref(ls->func_get_defaults);
  396. unref(ls->func_get_properties);
  397. unref(ls->func_update);
  398. unref(ls->func_activate);
  399. unref(ls->func_deactivate);
  400. unref(ls->func_show);
  401. unref(ls->func_hide);
  402. unref(ls->func_video_tick);
  403. unref(ls->func_video_render);
  404. unref(ls->func_save);
  405. unref(ls->func_load);
  406. #undef unref
  407. }
  408. static void obs_lua_source_free_type_data(void *type_data)
  409. {
  410. struct obs_lua_source *ls = type_data;
  411. pthread_mutex_lock(&ls->definition_mutex);
  412. if (ls->script) {
  413. lock_script();
  414. source_type_unload(ls);
  415. unlock_script();
  416. ls->script = NULL;
  417. }
  418. pthread_mutex_unlock(&ls->definition_mutex);
  419. pthread_mutex_destroy(&ls->definition_mutex);
  420. bfree(ls);
  421. }
  422. EXPORT void obs_enable_source_type(const char *name, bool enable);
  423. static inline struct obs_lua_source *find_existing(const char *id)
  424. {
  425. struct obs_lua_source *existing = NULL;
  426. pthread_mutex_lock(&lua_source_def_mutex);
  427. struct obs_lua_source *ls = first_source_def;
  428. while (ls) {
  429. /* can compare pointers here due to string table */
  430. if (ls->id == id) {
  431. existing = ls;
  432. break;
  433. }
  434. ls = ls->next;
  435. }
  436. pthread_mutex_unlock(&lua_source_def_mutex);
  437. return existing;
  438. }
  439. static int obs_lua_register_source(lua_State *script)
  440. {
  441. struct obs_lua_source ls = {0};
  442. struct obs_lua_source *existing = NULL;
  443. struct obs_lua_source *v = NULL;
  444. struct obs_source_info info = {0};
  445. const char *id;
  446. if (!verify_args1(script, is_table))
  447. goto fail;
  448. id = get_table_string(script, -1, "id");
  449. if (!id || !*id)
  450. goto fail;
  451. /* redefinition */
  452. existing = find_existing(id);
  453. if (existing) {
  454. if (existing->script) {
  455. existing = NULL;
  456. goto fail;
  457. }
  458. pthread_mutex_lock(&existing->definition_mutex);
  459. }
  460. v = existing ? existing : &ls;
  461. v->script = script;
  462. v->id = id;
  463. info.id = v->id;
  464. info.type = (enum obs_source_type)get_table_int(script, -1, "type");
  465. info.output_flags = get_table_int(script, -1, "output_flags");
  466. lua_pushstring(script, "get_name");
  467. lua_gettable(script, -2);
  468. if (lua_pcall(script, 0, 1, 0) == 0) {
  469. v->display_name = cstrcache_get(lua_tostring(script, -1));
  470. lua_pop(script, 1);
  471. }
  472. if (!v->display_name || !*v->display_name || !*info.id || !info.output_flags)
  473. goto fail;
  474. #define get_callback(val) \
  475. do { \
  476. get_callback_from_table(script, -1, #val, &v->func_##val); \
  477. info.val = obs_lua_source_##val; \
  478. } while (false)
  479. get_callback(create);
  480. get_callback(destroy);
  481. get_callback(get_width);
  482. get_callback(get_height);
  483. get_callback(get_properties);
  484. get_callback(update);
  485. get_callback(activate);
  486. get_callback(deactivate);
  487. get_callback(show);
  488. get_callback(hide);
  489. get_callback(video_tick);
  490. get_callback(video_render);
  491. get_callback(save);
  492. get_callback(load);
  493. #undef get_callback
  494. get_callback_from_table(script, -1, "get_defaults", &v->func_get_defaults);
  495. if (!existing) {
  496. ls.data = current_lua_script;
  497. pthread_mutex_init_recursive(&ls.definition_mutex);
  498. info.type_data = bmemdup(&ls, sizeof(ls));
  499. info.free_type_data = obs_lua_source_free_type_data;
  500. info.get_name = obs_lua_source_get_name;
  501. info.get_defaults2 = obs_lua_source_get_defaults;
  502. obs_register_source(&info);
  503. pthread_mutex_lock(&lua_source_def_mutex);
  504. v = info.type_data;
  505. struct obs_lua_source *next = first_source_def;
  506. v->next = next;
  507. if (next)
  508. next->p_prev_next = &v->next;
  509. v->p_prev_next = &first_source_def;
  510. first_source_def = v;
  511. pthread_mutex_unlock(&lua_source_def_mutex);
  512. } else {
  513. existing->script = script;
  514. existing->data = current_lua_script;
  515. obs_enable_source_type(id, true);
  516. struct obs_lua_data *ld = v->first_source;
  517. while (ld) {
  518. struct obs_lua_source *ls = v;
  519. if (have_func(create)) {
  520. obs_source_t *source = ld->source;
  521. obs_data_t *settings = obs_source_get_settings(source);
  522. ls_push_libobs_obj(obs_data_t, settings, false);
  523. ls_push_libobs_obj(obs_source_t, source, false);
  524. call_func(create, 2, 1);
  525. ld->lua_data_ref = luaL_ref(ls->script, LUA_REGISTRYINDEX);
  526. obs_data_release(settings);
  527. }
  528. ld = ld->next;
  529. }
  530. }
  531. fail:
  532. if (existing) {
  533. pthread_mutex_unlock(&existing->definition_mutex);
  534. }
  535. return 0;
  536. }
  537. /* ========================================================================= */
  538. void add_lua_source_functions(lua_State *script)
  539. {
  540. lua_getglobal(script, "obslua");
  541. lua_pushstring(script, "obs_register_source");
  542. lua_pushcfunction(script, obs_lua_register_source);
  543. lua_rawset(script, -3);
  544. lua_pop(script, 1);
  545. }
  546. static inline void undef_source_type(struct obs_lua_script *data, struct obs_lua_source *ls)
  547. {
  548. pthread_mutex_lock(&ls->definition_mutex);
  549. pthread_mutex_lock(&data->mutex);
  550. obs_enable_source_type(ls->id, false);
  551. struct obs_lua_data *ld = ls->first_source;
  552. while (ld) {
  553. call_destroy(ld);
  554. ld = ld->next;
  555. }
  556. source_type_unload(ls);
  557. ls->script = NULL;
  558. pthread_mutex_unlock(&data->mutex);
  559. pthread_mutex_unlock(&ls->definition_mutex);
  560. }
  561. void undef_lua_script_sources(struct obs_lua_script *data)
  562. {
  563. pthread_mutex_lock(&lua_source_def_mutex);
  564. struct obs_lua_source *def = first_source_def;
  565. while (def) {
  566. if (def->script == data->script)
  567. undef_source_type(data, def);
  568. def = def->next;
  569. }
  570. pthread_mutex_unlock(&lua_source_def_mutex);
  571. }