obs-scene.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. /******************************************************************************
  2. Copyright (C) 2013 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/threading.h"
  15. #include "graphics/math-defs.h"
  16. #include "obs-scene.h"
  17. static inline void signal_item_remove(struct obs_scene_item *item)
  18. {
  19. struct calldata params = {0};
  20. calldata_setptr(&params, "scene", item->parent);
  21. calldata_setptr(&params, "item", item);
  22. signal_handler_signal(item->parent->source->signals, "item_remove",
  23. &params);
  24. calldata_free(&params);
  25. }
  26. static const char *scene_getname(const char *locale)
  27. {
  28. UNUSED_PARAMETER(locale);
  29. return "Scene internal source type";
  30. }
  31. static void *scene_create(obs_data_t settings, struct obs_source *source)
  32. {
  33. pthread_mutexattr_t attr;
  34. struct obs_scene *scene = bmalloc(sizeof(struct obs_scene));
  35. scene->source = source;
  36. scene->first_item = NULL;
  37. if (pthread_mutexattr_init(&attr) != 0)
  38. goto fail;
  39. if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
  40. goto fail;
  41. if (pthread_mutex_init(&scene->mutex, &attr) != 0) {
  42. blog(LOG_ERROR, "scene_create: Couldn't initialize mutex");
  43. goto fail;
  44. }
  45. UNUSED_PARAMETER(settings);
  46. return scene;
  47. fail:
  48. pthread_mutexattr_destroy(&attr);
  49. bfree(scene);
  50. return NULL;
  51. }
  52. static void scene_destroy(void *data)
  53. {
  54. struct obs_scene *scene = data;
  55. struct obs_scene_item *item;
  56. pthread_mutex_lock(&scene->mutex);
  57. item = scene->first_item;
  58. while (item) {
  59. struct obs_scene_item *del_item = item;
  60. item = item->next;
  61. obs_sceneitem_remove(del_item);
  62. }
  63. pthread_mutex_unlock(&scene->mutex);
  64. pthread_mutex_destroy(&scene->mutex);
  65. bfree(scene);
  66. }
  67. static void scene_enum_sources(void *data,
  68. obs_source_enum_proc_t enum_callback,
  69. void *param)
  70. {
  71. struct obs_scene *scene = data;
  72. struct obs_scene_item *item;
  73. pthread_mutex_lock(&scene->mutex);
  74. item = scene->first_item;
  75. while (item) {
  76. struct obs_scene_item *next = item->next;
  77. obs_sceneitem_addref(item);
  78. enum_callback(scene->source, item->source, param);
  79. obs_sceneitem_release(item);
  80. item = next;
  81. }
  82. pthread_mutex_unlock(&scene->mutex);
  83. }
  84. static inline void detach_sceneitem(struct obs_scene_item *item)
  85. {
  86. if (item->prev)
  87. item->prev->next = item->next;
  88. else
  89. item->parent->first_item = item->next;
  90. if (item->next)
  91. item->next->prev = item->prev;
  92. item->parent = NULL;
  93. }
  94. static inline void attach_sceneitem(struct obs_scene_item *item,
  95. struct obs_scene_item *prev)
  96. {
  97. item->prev = prev;
  98. if (prev) {
  99. item->next = prev->next;
  100. if (prev->next)
  101. prev->next->prev = item;
  102. prev->next = item;
  103. } else {
  104. item->next = item->parent->first_item;
  105. item->parent->first_item = item;
  106. }
  107. }
  108. static void scene_video_render(void *data, effect_t effect)
  109. {
  110. struct obs_scene *scene = data;
  111. struct obs_scene_item *item;
  112. pthread_mutex_lock(&scene->mutex);
  113. item = scene->first_item;
  114. while (item) {
  115. if (obs_source_removed(item->source)) {
  116. struct obs_scene_item *del_item = item;
  117. item = item->next;
  118. obs_sceneitem_remove(del_item);
  119. continue;
  120. }
  121. gs_matrix_push();
  122. gs_matrix_translate3f(item->origin.x, item->origin.y, 0.0f);
  123. gs_matrix_scale3f(item->scale.x, item->scale.y, 1.0f);
  124. gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD(-item->rot));
  125. gs_matrix_translate3f(-item->pos.x, -item->pos.y, 0.0f);
  126. obs_source_video_render(item->source);
  127. gs_matrix_pop();
  128. item = item->next;
  129. }
  130. pthread_mutex_unlock(&scene->mutex);
  131. UNUSED_PARAMETER(effect);
  132. }
  133. static uint32_t scene_getwidth(void *data)
  134. {
  135. UNUSED_PARAMETER(data);
  136. return obs->video.base_width;
  137. }
  138. static uint32_t scene_getheight(void *data)
  139. {
  140. UNUSED_PARAMETER(data);
  141. return obs->video.base_height;
  142. }
  143. static const struct obs_source_info scene_info =
  144. {
  145. .id = "scene",
  146. .type = OBS_SOURCE_TYPE_SCENE,
  147. .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW,
  148. .getname = scene_getname,
  149. .create = scene_create,
  150. .destroy = scene_destroy,
  151. .video_render = scene_video_render,
  152. .getwidth = scene_getwidth,
  153. .getheight = scene_getheight,
  154. .enum_sources = scene_enum_sources
  155. };
  156. static const char *obs_scene_signals[] = {
  157. "void item_add(ptr scene, ptr item)",
  158. "void item_remove(ptr scene, ptr item)",
  159. NULL
  160. };
  161. void source_init_name(struct obs_source *source, const char *name);
  162. obs_scene_t obs_scene_create(const char *name)
  163. {
  164. struct obs_source *source = bzalloc(sizeof(struct obs_source));
  165. struct obs_scene *scene;
  166. if (!obs_source_init_handlers(source)) {
  167. bfree(source);
  168. return NULL;
  169. }
  170. signal_handler_add_array(source->signals, obs_scene_signals);
  171. source->settings = obs_data_create();
  172. scene = scene_create(source->settings, source);
  173. source->data = scene;
  174. assert(scene);
  175. if (!scene) {
  176. obs_data_release(source->settings);
  177. proc_handler_destroy(source->procs);
  178. signal_handler_destroy(source->signals);
  179. bfree(source);
  180. return NULL;
  181. }
  182. source_init_name(source, name);
  183. scene->source = source;
  184. obs_source_init(source, &scene_info);
  185. memcpy(&source->info, &scene_info, sizeof(struct obs_source_info));
  186. return scene;
  187. }
  188. void obs_scene_addref(obs_scene_t scene)
  189. {
  190. if (scene)
  191. obs_source_addref(scene->source);
  192. }
  193. void obs_scene_release(obs_scene_t scene)
  194. {
  195. if (scene)
  196. obs_source_release(scene->source);
  197. }
  198. obs_source_t obs_scene_getsource(obs_scene_t scene)
  199. {
  200. return scene ? scene->source : NULL;
  201. }
  202. obs_scene_t obs_scene_fromsource(obs_source_t source)
  203. {
  204. if (!source || source->info.type != OBS_SOURCE_TYPE_SCENE)
  205. return NULL;
  206. return source->data;
  207. }
  208. obs_sceneitem_t obs_scene_findsource(obs_scene_t scene, const char *name)
  209. {
  210. struct obs_scene_item *item;
  211. if (!scene)
  212. return NULL;
  213. pthread_mutex_lock(&scene->mutex);
  214. item = scene->first_item;
  215. while (item) {
  216. if (strcmp(item->source->name, name) == 0)
  217. break;
  218. item = item->next;
  219. }
  220. pthread_mutex_unlock(&scene->mutex);
  221. return item;
  222. }
  223. void obs_scene_enum_items(obs_scene_t scene,
  224. bool (*callback)(obs_scene_t, obs_sceneitem_t, void*),
  225. void *param)
  226. {
  227. struct obs_scene_item *item;
  228. if (!scene || !callback)
  229. return;
  230. pthread_mutex_lock(&scene->mutex);
  231. item = scene->first_item;
  232. while (item) {
  233. struct obs_scene_item *next = item->next;
  234. obs_sceneitem_addref(item);
  235. if (!callback(scene, item, param)) {
  236. obs_sceneitem_release(item);
  237. break;
  238. }
  239. obs_sceneitem_release(item);
  240. item = next;
  241. }
  242. pthread_mutex_unlock(&scene->mutex);
  243. }
  244. obs_sceneitem_t obs_scene_add(obs_scene_t scene, obs_source_t source)
  245. {
  246. struct obs_scene_item *last;
  247. struct obs_scene_item *item;
  248. struct calldata params = {0};
  249. if (!scene)
  250. return NULL;
  251. if (!source) {
  252. blog(LOG_ERROR, "Tried to add a NULL source to a scene");
  253. return NULL;
  254. }
  255. item = bzalloc(sizeof(struct obs_scene_item));
  256. item->source = source;
  257. item->visible = true;
  258. item->parent = scene;
  259. item->ref = 1;
  260. vec2_set(&item->scale, 1.0f, 1.0f);
  261. obs_source_addref(source);
  262. obs_source_add_child(scene->source, source);
  263. pthread_mutex_lock(&scene->mutex);
  264. last = scene->first_item;
  265. if (!last) {
  266. scene->first_item = item;
  267. } else {
  268. while (last->next)
  269. last = last->next;
  270. last->next = item;
  271. item->prev = last;
  272. }
  273. pthread_mutex_unlock(&scene->mutex);
  274. calldata_setptr(&params, "scene", scene);
  275. calldata_setptr(&params, "item", item);
  276. signal_handler_signal(scene->source->signals, "item_add", &params);
  277. calldata_free(&params);
  278. return item;
  279. }
  280. static void obs_sceneitem_destroy(obs_sceneitem_t item)
  281. {
  282. if (item) {
  283. if (item->source)
  284. obs_source_release(item->source);
  285. bfree(item);
  286. }
  287. }
  288. void obs_sceneitem_addref(obs_sceneitem_t item)
  289. {
  290. if (item)
  291. os_atomic_inc_long(&item->ref);
  292. }
  293. void obs_sceneitem_release(obs_sceneitem_t item)
  294. {
  295. if (!item)
  296. return;
  297. if (os_atomic_dec_long(&item->ref) == 0)
  298. obs_sceneitem_destroy(item);
  299. }
  300. void obs_sceneitem_remove(obs_sceneitem_t item)
  301. {
  302. obs_scene_t scene;
  303. if (!item)
  304. return;
  305. scene = item->parent;
  306. if (scene)
  307. pthread_mutex_lock(&scene->mutex);
  308. if (item->removed) {
  309. if (scene)
  310. pthread_mutex_unlock(&scene->mutex);
  311. return;
  312. }
  313. item->removed = true;
  314. obs_source_remove_child(scene->source, item->source);
  315. signal_item_remove(item);
  316. detach_sceneitem(item);
  317. pthread_mutex_unlock(&scene->mutex);
  318. obs_sceneitem_release(item);
  319. }
  320. obs_scene_t obs_sceneitem_getscene(obs_sceneitem_t item)
  321. {
  322. return item ? item->parent : NULL;
  323. }
  324. obs_source_t obs_sceneitem_getsource(obs_sceneitem_t item)
  325. {
  326. return item ? item->source : NULL;
  327. }
  328. void obs_sceneitem_setpos(obs_sceneitem_t item, const struct vec2 *pos)
  329. {
  330. if (item)
  331. vec2_copy(&item->pos, pos);
  332. }
  333. void obs_sceneitem_setrot(obs_sceneitem_t item, float rot)
  334. {
  335. if (item)
  336. item->rot = rot;
  337. }
  338. void obs_sceneitem_setorigin(obs_sceneitem_t item, const struct vec2 *origin)
  339. {
  340. if (item)
  341. vec2_copy(&item->origin, origin);
  342. }
  343. void obs_sceneitem_setscale(obs_sceneitem_t item, const struct vec2 *scale)
  344. {
  345. if (item)
  346. vec2_copy(&item->scale, scale);
  347. }
  348. void obs_sceneitem_setorder(obs_sceneitem_t item, enum order_movement movement)
  349. {
  350. if (!item) return;
  351. struct obs_scene *scene = item->parent;
  352. obs_scene_addref(scene);
  353. pthread_mutex_lock(&scene->mutex);
  354. detach_sceneitem(item);
  355. if (movement == ORDER_MOVE_UP) {
  356. attach_sceneitem(item, item->prev);
  357. } else if (movement == ORDER_MOVE_DOWN) {
  358. attach_sceneitem(item, item->next);
  359. } else if (movement == ORDER_MOVE_TOP) {
  360. struct obs_scene_item *last = item->next;
  361. if (!last) {
  362. last = item->prev;
  363. } else {
  364. while (last->next)
  365. last = last->next;
  366. }
  367. attach_sceneitem(item, last);
  368. } else if (movement == ORDER_MOVE_BOTTOM) {
  369. attach_sceneitem(item, NULL);
  370. }
  371. pthread_mutex_unlock(&scene->mutex);
  372. obs_scene_release(scene);
  373. }
  374. void obs_sceneitem_getpos(obs_sceneitem_t item, struct vec2 *pos)
  375. {
  376. if (item)
  377. vec2_copy(pos, &item->pos);
  378. }
  379. float obs_sceneitem_getrot(obs_sceneitem_t item)
  380. {
  381. return item ? item->rot : 0.0f;
  382. }
  383. void obs_sceneitem_getorigin(obs_sceneitem_t item, struct vec2 *origin)
  384. {
  385. if (item)
  386. vec2_copy(origin, &item->origin);
  387. }
  388. void obs_sceneitem_getscale(obs_sceneitem_t item, struct vec2 *scale)
  389. {
  390. if (item)
  391. vec2_copy(scale, &item->scale);
  392. }