obs-scene.c 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001
  1. /******************************************************************************
  2. Copyright (C) 2013-2015 by Hugh Bailey <[email protected]>
  3. Philippe Groarke <[email protected]>
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. ******************************************************************************/
  15. #include "util/threading.h"
  16. #include "graphics/math-defs.h"
  17. #include "obs-scene.h"
  18. /* NOTE: For proper mutex lock order (preventing mutual cross-locks), never
  19. * lock the graphics mutex inside either of the scene mutexes.
  20. *
  21. * Another thing that must be done to prevent that cross-lock (and improve
  22. * performance), is to not create/release/update sources within the scene
  23. * mutexes.
  24. *
  25. * It's okay to lock the graphics mutex before locking either of the scene
  26. * mutexes, but not after.
  27. */
  28. static const char *obs_scene_signals[] = {
  29. "void item_add(ptr scene, ptr item)",
  30. "void item_remove(ptr scene, ptr item)",
  31. "void reorder(ptr scene)",
  32. "void item_visible(ptr scene, ptr item, bool visible)",
  33. "void item_select(ptr scene, ptr item)",
  34. "void item_deselect(ptr scene, ptr item)",
  35. "void item_transform(ptr scene, ptr item)",
  36. NULL
  37. };
  38. static inline void signal_item_remove(struct obs_scene_item *item)
  39. {
  40. struct calldata params;
  41. uint8_t stack[128];
  42. calldata_init_fixed(&params, stack, sizeof(stack));
  43. calldata_set_ptr(&params, "scene", item->parent);
  44. calldata_set_ptr(&params, "item", item);
  45. signal_handler_signal(item->parent->source->context.signals,
  46. "item_remove", &params);
  47. }
  48. static const char *scene_getname(void *unused)
  49. {
  50. UNUSED_PARAMETER(unused);
  51. return "Scene";
  52. }
  53. static void *scene_create(obs_data_t *settings, struct obs_source *source)
  54. {
  55. pthread_mutexattr_t attr;
  56. struct obs_scene *scene = bmalloc(sizeof(struct obs_scene));
  57. scene->source = source;
  58. scene->first_item = NULL;
  59. signal_handler_add_array(obs_source_get_signal_handler(source),
  60. obs_scene_signals);
  61. scene->id_counter = 0;
  62. if (pthread_mutexattr_init(&attr) != 0)
  63. goto fail;
  64. if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
  65. goto fail;
  66. if (pthread_mutex_init(&scene->audio_mutex, &attr) != 0) {
  67. blog(LOG_ERROR, "scene_create: Couldn't initialize audio "
  68. "mutex");
  69. goto fail;
  70. }
  71. if (pthread_mutex_init(&scene->video_mutex, &attr) != 0) {
  72. blog(LOG_ERROR, "scene_create: Couldn't initialize video "
  73. "mutex");
  74. goto fail;
  75. }
  76. UNUSED_PARAMETER(settings);
  77. return scene;
  78. fail:
  79. pthread_mutexattr_destroy(&attr);
  80. bfree(scene);
  81. return NULL;
  82. }
  83. #define audio_lock(scene) pthread_mutex_lock(&scene->audio_mutex)
  84. #define video_lock(scene) pthread_mutex_lock(&scene->video_mutex)
  85. #define audio_unlock(scene) pthread_mutex_unlock(&scene->audio_mutex)
  86. #define video_unlock(scene) pthread_mutex_unlock(&scene->video_mutex)
  87. static inline void full_lock(struct obs_scene *scene)
  88. {
  89. video_lock(scene);
  90. audio_lock(scene);
  91. }
  92. static inline void full_unlock(struct obs_scene *scene)
  93. {
  94. audio_unlock(scene);
  95. video_unlock(scene);
  96. }
  97. static void set_visibility(struct obs_scene_item *item, bool vis);
  98. static inline void detach_sceneitem(struct obs_scene_item *item);
  99. static inline void remove_without_release(struct obs_scene_item *item)
  100. {
  101. item->removed = true;
  102. set_visibility(item, false);
  103. signal_item_remove(item);
  104. detach_sceneitem(item);
  105. }
  106. static void remove_all_items(struct obs_scene *scene)
  107. {
  108. struct obs_scene_item *item;
  109. DARRAY(struct obs_scene_item*) items;
  110. da_init(items);
  111. full_lock(scene);
  112. item = scene->first_item;
  113. while (item) {
  114. struct obs_scene_item *del_item = item;
  115. item = item->next;
  116. remove_without_release(del_item);
  117. da_push_back(items, &del_item);
  118. }
  119. full_unlock(scene);
  120. for (size_t i = 0; i < items.num; i++)
  121. obs_sceneitem_release(items.array[i]);
  122. da_free(items);
  123. }
  124. static void scene_destroy(void *data)
  125. {
  126. struct obs_scene *scene = data;
  127. remove_all_items(scene);
  128. pthread_mutex_destroy(&scene->video_mutex);
  129. pthread_mutex_destroy(&scene->audio_mutex);
  130. bfree(scene);
  131. }
  132. static void scene_enum_sources(void *data,
  133. obs_source_enum_proc_t enum_callback,
  134. void *param, bool active)
  135. {
  136. struct obs_scene *scene = data;
  137. struct obs_scene_item *item;
  138. struct obs_scene_item *next;
  139. full_lock(scene);
  140. item = scene->first_item;
  141. while (item) {
  142. next = item->next;
  143. obs_sceneitem_addref(item);
  144. if (!active || os_atomic_load_long(&item->active_refs) > 0)
  145. enum_callback(scene->source, item->source, param);
  146. obs_sceneitem_release(item);
  147. item = next;
  148. }
  149. full_unlock(scene);
  150. }
  151. static void scene_enum_active_sources(void *data,
  152. obs_source_enum_proc_t enum_callback,
  153. void *param)
  154. {
  155. scene_enum_sources(data, enum_callback, param, true);
  156. }
  157. static void scene_enum_all_sources(void *data,
  158. obs_source_enum_proc_t enum_callback,
  159. void *param)
  160. {
  161. scene_enum_sources(data, enum_callback, param, false);
  162. }
  163. static inline void detach_sceneitem(struct obs_scene_item *item)
  164. {
  165. if (item->prev)
  166. item->prev->next = item->next;
  167. else
  168. item->parent->first_item = item->next;
  169. if (item->next)
  170. item->next->prev = item->prev;
  171. item->parent = NULL;
  172. }
  173. static inline void attach_sceneitem(struct obs_scene *parent,
  174. struct obs_scene_item *item, struct obs_scene_item *prev)
  175. {
  176. item->prev = prev;
  177. item->parent = parent;
  178. if (prev) {
  179. item->next = prev->next;
  180. if (prev->next)
  181. prev->next->prev = item;
  182. prev->next = item;
  183. } else {
  184. item->next = parent->first_item;
  185. if (parent->first_item)
  186. parent->first_item->prev = item;
  187. parent->first_item = item;
  188. }
  189. }
  190. void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy)
  191. {
  192. if (align & OBS_ALIGN_RIGHT)
  193. v->x += (float)cx;
  194. else if ((align & OBS_ALIGN_LEFT) == 0)
  195. v->x += (float)(cx / 2);
  196. if (align & OBS_ALIGN_BOTTOM)
  197. v->y += (float)cy;
  198. else if ((align & OBS_ALIGN_TOP) == 0)
  199. v->y += (float)(cy / 2);
  200. }
  201. static void calculate_bounds_data(struct obs_scene_item *item,
  202. struct vec2 *origin, struct vec2 *scale,
  203. uint32_t *cx, uint32_t *cy)
  204. {
  205. float width = (float)(*cx) * fabsf(scale->x);
  206. float height = (float)(*cy) * fabsf(scale->y);
  207. float item_aspect = width / height;
  208. float bounds_aspect = item->bounds.x / item->bounds.y;
  209. uint32_t bounds_type = item->bounds_type;
  210. float width_diff, height_diff;
  211. if (item->bounds_type == OBS_BOUNDS_MAX_ONLY)
  212. if (width > item->bounds.x || height > item->bounds.y)
  213. bounds_type = OBS_BOUNDS_SCALE_INNER;
  214. if (bounds_type == OBS_BOUNDS_SCALE_INNER ||
  215. bounds_type == OBS_BOUNDS_SCALE_OUTER) {
  216. bool use_width = (bounds_aspect < item_aspect);
  217. float mul;
  218. if (item->bounds_type == OBS_BOUNDS_SCALE_OUTER)
  219. use_width = !use_width;
  220. mul = use_width ?
  221. item->bounds.x / width :
  222. item->bounds.y / height;
  223. vec2_mulf(scale, scale, mul);
  224. } else if (bounds_type == OBS_BOUNDS_SCALE_TO_WIDTH) {
  225. vec2_mulf(scale, scale, item->bounds.x / width);
  226. } else if (bounds_type == OBS_BOUNDS_SCALE_TO_HEIGHT) {
  227. vec2_mulf(scale, scale, item->bounds.y / height);
  228. } else if (bounds_type == OBS_BOUNDS_STRETCH) {
  229. scale->x = item->bounds.x / (float)(*cx);
  230. scale->y = item->bounds.y / (float)(*cy);
  231. }
  232. width = (float)(*cx) * scale->x;
  233. height = (float)(*cy) * scale->y;
  234. width_diff = item->bounds.x - width;
  235. height_diff = item->bounds.y - height;
  236. *cx = (uint32_t)item->bounds.x;
  237. *cy = (uint32_t)item->bounds.y;
  238. add_alignment(origin, item->bounds_align,
  239. (int)-width_diff, (int)-height_diff);
  240. }
  241. static inline uint32_t calc_cx(const struct obs_scene_item *item,
  242. uint32_t width)
  243. {
  244. uint32_t crop_cx = item->crop.left + item->crop.right;
  245. return (crop_cx > width) ? 2 : (width - crop_cx);
  246. }
  247. static inline uint32_t calc_cy(const struct obs_scene_item *item,
  248. uint32_t height)
  249. {
  250. uint32_t crop_cy = item->crop.top + item->crop.bottom;
  251. return (crop_cy > height) ? 2 : (height - crop_cy);
  252. }
  253. static void update_item_transform(struct obs_scene_item *item)
  254. {
  255. uint32_t width = obs_source_get_width(item->source);
  256. uint32_t height = obs_source_get_height(item->source);
  257. uint32_t cx = calc_cx(item, width);
  258. uint32_t cy = calc_cy(item, height);
  259. struct vec2 base_origin;
  260. struct vec2 origin;
  261. struct vec2 scale = item->scale;
  262. struct calldata params;
  263. uint8_t stack[128];
  264. if (os_atomic_load_long(&item->defer_update) > 0)
  265. return;
  266. width = cx;
  267. height = cy;
  268. vec2_zero(&base_origin);
  269. vec2_zero(&origin);
  270. /* ----------------------- */
  271. if (item->bounds_type != OBS_BOUNDS_NONE) {
  272. calculate_bounds_data(item, &origin, &scale, &cx, &cy);
  273. } else {
  274. cx = (uint32_t)((float)cx * scale.x);
  275. cy = (uint32_t)((float)cy * scale.y);
  276. }
  277. add_alignment(&origin, item->align, (int)cx, (int)cy);
  278. matrix4_identity(&item->draw_transform);
  279. matrix4_scale3f(&item->draw_transform, &item->draw_transform,
  280. scale.x, scale.y, 1.0f);
  281. matrix4_translate3f(&item->draw_transform, &item->draw_transform,
  282. -origin.x, -origin.y, 0.0f);
  283. matrix4_rotate_aa4f(&item->draw_transform, &item->draw_transform,
  284. 0.0f, 0.0f, 1.0f, RAD(item->rot));
  285. matrix4_translate3f(&item->draw_transform, &item->draw_transform,
  286. item->pos.x, item->pos.y, 0.0f);
  287. item->output_scale = scale;
  288. /* ----------------------- */
  289. if (item->bounds_type != OBS_BOUNDS_NONE) {
  290. vec2_copy(&scale, &item->bounds);
  291. } else {
  292. scale.x = (float)width * item->scale.x;
  293. scale.y = (float)height * item->scale.y;
  294. }
  295. add_alignment(&base_origin, item->align, (int)scale.x, (int)scale.y);
  296. matrix4_identity(&item->box_transform);
  297. matrix4_scale3f(&item->box_transform, &item->box_transform,
  298. scale.x, scale.y, 1.0f);
  299. matrix4_translate3f(&item->box_transform, &item->box_transform,
  300. -base_origin.x, -base_origin.y, 0.0f);
  301. matrix4_rotate_aa4f(&item->box_transform, &item->box_transform,
  302. 0.0f, 0.0f, 1.0f, RAD(item->rot));
  303. matrix4_translate3f(&item->box_transform, &item->box_transform,
  304. item->pos.x, item->pos.y, 0.0f);
  305. /* ----------------------- */
  306. item->last_width = width;
  307. item->last_height = height;
  308. calldata_init_fixed(&params, stack, sizeof(stack));
  309. calldata_set_ptr(&params, "scene", item->parent);
  310. calldata_set_ptr(&params, "item", item);
  311. signal_handler_signal(item->parent->source->context.signals,
  312. "item_transform", &params);
  313. }
  314. static inline bool source_size_changed(struct obs_scene_item *item)
  315. {
  316. uint32_t width = obs_source_get_width(item->source);
  317. uint32_t height = obs_source_get_height(item->source);
  318. return item->last_width != width || item->last_height != height;
  319. }
  320. static inline bool crop_enabled(const struct obs_sceneitem_crop *crop)
  321. {
  322. return crop->left || crop->right || crop->top || crop->bottom;
  323. }
  324. static inline bool scale_filter_enabled(const struct obs_scene_item *item)
  325. {
  326. return item->scale_filter != OBS_SCALE_DISABLE;
  327. }
  328. static inline bool item_is_scene(const struct obs_scene_item *item)
  329. {
  330. return item->source && item->source->info.type == OBS_SOURCE_TYPE_SCENE;
  331. }
  332. static inline bool item_texture_enabled(const struct obs_scene_item *item)
  333. {
  334. return crop_enabled(&item->crop) || scale_filter_enabled(item) ||
  335. item_is_scene(item);
  336. }
  337. static void render_item_texture(struct obs_scene_item *item)
  338. {
  339. gs_texture_t *tex = gs_texrender_get_texture(item->item_render);
  340. gs_effect_t *effect = obs->video.default_effect;
  341. enum obs_scale_type type = item->scale_filter;
  342. uint32_t cx = gs_texture_get_width(tex);
  343. uint32_t cy = gs_texture_get_height(tex);
  344. if (type != OBS_SCALE_DISABLE) {
  345. if (type == OBS_SCALE_POINT) {
  346. gs_eparam_t *image = gs_effect_get_param_by_name(
  347. effect, "image");
  348. gs_effect_set_next_sampler(image,
  349. obs->video.point_sampler);
  350. } else if (!close_float(item->output_scale.x, 1.0f, EPSILON) ||
  351. !close_float(item->output_scale.y, 1.0f, EPSILON)) {
  352. gs_eparam_t *scale_param;
  353. if (item->output_scale.x < 0.5f ||
  354. item->output_scale.y < 0.5f) {
  355. effect = obs->video.bilinear_lowres_effect;
  356. } else if (type == OBS_SCALE_BICUBIC) {
  357. effect = obs->video.bicubic_effect;
  358. } else if (type == OBS_SCALE_LANCZOS) {
  359. effect = obs->video.lanczos_effect;
  360. }
  361. scale_param = gs_effect_get_param_by_name(effect,
  362. "base_dimension_i");
  363. if (scale_param) {
  364. struct vec2 base_res_i = {
  365. 1.0f / (float)cx,
  366. 1.0f / (float)cy
  367. };
  368. gs_effect_set_vec2(scale_param, &base_res_i);
  369. }
  370. }
  371. }
  372. while (gs_effect_loop(effect, "Draw"))
  373. obs_source_draw(tex, 0, 0, 0, 0, 0);
  374. }
  375. static inline void render_item(struct obs_scene_item *item)
  376. {
  377. if (item->item_render) {
  378. uint32_t width = obs_source_get_width(item->source);
  379. uint32_t height = obs_source_get_height(item->source);
  380. uint32_t cx = calc_cx(item, width);
  381. uint32_t cy = calc_cy(item, height);
  382. if (cx && cy && gs_texrender_begin(item->item_render, cx, cy)) {
  383. float cx_scale = (float)width / (float)cx;
  384. float cy_scale = (float)height / (float)cy;
  385. struct vec4 clear_color;
  386. vec4_zero(&clear_color);
  387. gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);
  388. gs_ortho(0.0f, (float)width, 0.0f, (float)height,
  389. -100.0f, 100.0f);
  390. gs_matrix_scale3f(cx_scale, cy_scale, 1.0f);
  391. gs_matrix_translate3f(
  392. -(float)item->crop.left,
  393. -(float)item->crop.top,
  394. 0.0f);
  395. gs_blend_state_push();
  396. gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
  397. obs_source_video_render(item->source);
  398. gs_blend_state_pop();
  399. gs_texrender_end(item->item_render);
  400. }
  401. }
  402. gs_matrix_push();
  403. gs_matrix_mul(&item->draw_transform);
  404. if (item->item_render) {
  405. render_item_texture(item);
  406. } else {
  407. obs_source_video_render(item->source);
  408. }
  409. gs_matrix_pop();
  410. }
  411. static void scene_video_tick(void *data, float seconds)
  412. {
  413. struct obs_scene *scene = data;
  414. struct obs_scene_item *item;
  415. video_lock(scene);
  416. item = scene->first_item;
  417. while (item) {
  418. if (item->item_render)
  419. gs_texrender_reset(item->item_render);
  420. item = item->next;
  421. }
  422. video_unlock(scene);
  423. UNUSED_PARAMETER(seconds);
  424. }
  425. static void scene_video_render(void *data, gs_effect_t *effect)
  426. {
  427. DARRAY(struct obs_scene_item*) remove_items;
  428. struct obs_scene *scene = data;
  429. struct obs_scene_item *item;
  430. da_init(remove_items);
  431. video_lock(scene);
  432. item = scene->first_item;
  433. gs_blend_state_push();
  434. gs_reset_blend_state();
  435. while (item) {
  436. if (obs_source_removed(item->source)) {
  437. struct obs_scene_item *del_item = item;
  438. item = item->next;
  439. remove_without_release(del_item);
  440. da_push_back(remove_items, &del_item);
  441. continue;
  442. }
  443. if (source_size_changed(item))
  444. update_item_transform(item);
  445. if (item->user_visible)
  446. render_item(item);
  447. item = item->next;
  448. }
  449. gs_blend_state_pop();
  450. video_unlock(scene);
  451. for (size_t i = 0; i < remove_items.num; i++)
  452. obs_sceneitem_release(remove_items.array[i]);
  453. da_free(remove_items);
  454. UNUSED_PARAMETER(effect);
  455. }
  456. static void set_visibility(struct obs_scene_item *item, bool vis)
  457. {
  458. pthread_mutex_lock(&item->actions_mutex);
  459. da_resize(item->audio_actions, 0);
  460. if (os_atomic_load_long(&item->active_refs) > 0) {
  461. if (!vis)
  462. obs_source_remove_active_child(item->parent->source,
  463. item->source);
  464. } else if (vis) {
  465. obs_source_add_active_child(item->parent->source, item->source);
  466. }
  467. os_atomic_set_long(&item->active_refs, vis ? 1 : 0);
  468. item->visible = vis;
  469. item->user_visible = vis;
  470. pthread_mutex_unlock(&item->actions_mutex);
  471. }
  472. static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
  473. {
  474. const char *name = obs_data_get_string(item_data, "name");
  475. obs_source_t *source = obs_get_source_by_name(name);
  476. const char *scale_filter_str;
  477. struct obs_scene_item *item;
  478. bool visible;
  479. bool lock;
  480. if (!source) {
  481. blog(LOG_WARNING, "[scene_load_item] Source %s not found!",
  482. name);
  483. return;
  484. }
  485. item = obs_scene_add(scene, source);
  486. if (!item) {
  487. blog(LOG_WARNING, "[scene_load_item] Could not add source '%s' "
  488. "to scene '%s'!",
  489. name, obs_source_get_name(scene->source));
  490. obs_source_release(source);
  491. return;
  492. }
  493. obs_data_set_default_int(item_data, "align",
  494. OBS_ALIGN_TOP | OBS_ALIGN_LEFT);
  495. if (obs_data_has_user_value(item_data, "id"))
  496. item->id = obs_data_get_int(item_data, "id");
  497. item->rot = (float)obs_data_get_double(item_data, "rot");
  498. item->align = (uint32_t)obs_data_get_int(item_data, "align");
  499. visible = obs_data_get_bool(item_data, "visible");
  500. lock = obs_data_get_bool(item_data, "locked");
  501. obs_data_get_vec2(item_data, "pos", &item->pos);
  502. obs_data_get_vec2(item_data, "scale", &item->scale);
  503. set_visibility(item, visible);
  504. obs_sceneitem_set_locked(item, lock);
  505. item->bounds_type =
  506. (enum obs_bounds_type)obs_data_get_int(item_data,
  507. "bounds_type");
  508. item->bounds_align =
  509. (uint32_t)obs_data_get_int(item_data, "bounds_align");
  510. obs_data_get_vec2(item_data, "bounds", &item->bounds);
  511. item->crop.left = (uint32_t)obs_data_get_int(item_data, "crop_left");
  512. item->crop.top = (uint32_t)obs_data_get_int(item_data, "crop_top");
  513. item->crop.right = (uint32_t)obs_data_get_int(item_data, "crop_right");
  514. item->crop.bottom = (uint32_t)obs_data_get_int(item_data, "crop_bottom");
  515. scale_filter_str = obs_data_get_string(item_data, "scale_filter");
  516. item->scale_filter = OBS_SCALE_DISABLE;
  517. if (scale_filter_str) {
  518. if (astrcmpi(scale_filter_str, "point") == 0)
  519. item->scale_filter = OBS_SCALE_POINT;
  520. else if (astrcmpi(scale_filter_str, "bilinear") == 0)
  521. item->scale_filter = OBS_SCALE_BILINEAR;
  522. else if (astrcmpi(scale_filter_str, "bicubic") == 0)
  523. item->scale_filter = OBS_SCALE_BICUBIC;
  524. else if (astrcmpi(scale_filter_str, "lanczos") == 0)
  525. item->scale_filter = OBS_SCALE_LANCZOS;
  526. }
  527. if (item->item_render && !item_texture_enabled(item)) {
  528. obs_enter_graphics();
  529. gs_texrender_destroy(item->item_render);
  530. item->item_render = NULL;
  531. obs_leave_graphics();
  532. } else if (!item->item_render && item_texture_enabled(item)) {
  533. obs_enter_graphics();
  534. item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
  535. obs_leave_graphics();
  536. }
  537. obs_source_release(source);
  538. update_item_transform(item);
  539. }
  540. static void scene_load(void *data, obs_data_t *settings)
  541. {
  542. struct obs_scene *scene = data;
  543. obs_data_array_t *items = obs_data_get_array(settings, "items");
  544. size_t count, i;
  545. remove_all_items(scene);
  546. if (!items) return;
  547. count = obs_data_array_count(items);
  548. for (i = 0; i < count; i++) {
  549. obs_data_t *item_data = obs_data_array_item(items, i);
  550. scene_load_item(scene, item_data);
  551. obs_data_release(item_data);
  552. }
  553. if (obs_data_has_user_value(settings, "id_counter"))
  554. scene->id_counter = obs_data_get_int(settings, "id_counter");
  555. obs_data_array_release(items);
  556. }
  557. static void scene_save_item(obs_data_array_t *array,
  558. struct obs_scene_item *item)
  559. {
  560. obs_data_t *item_data = obs_data_create();
  561. const char *name = obs_source_get_name(item->source);
  562. const char *scale_filter;
  563. obs_data_set_string(item_data, "name", name);
  564. obs_data_set_bool (item_data, "visible", item->user_visible);
  565. obs_data_set_bool (item_data, "locked", item->locked);
  566. obs_data_set_double(item_data, "rot", item->rot);
  567. obs_data_set_vec2 (item_data, "pos", &item->pos);
  568. obs_data_set_vec2 (item_data, "scale", &item->scale);
  569. obs_data_set_int (item_data, "align", (int)item->align);
  570. obs_data_set_int (item_data, "bounds_type", (int)item->bounds_type);
  571. obs_data_set_int (item_data, "bounds_align", (int)item->bounds_align);
  572. obs_data_set_vec2 (item_data, "bounds", &item->bounds);
  573. obs_data_set_int (item_data, "crop_left", (int)item->crop.left);
  574. obs_data_set_int (item_data, "crop_top", (int)item->crop.top);
  575. obs_data_set_int (item_data, "crop_right", (int)item->crop.right);
  576. obs_data_set_int (item_data, "crop_bottom", (int)item->crop.bottom);
  577. obs_data_set_int (item_data, "id", item->id);
  578. if (item->scale_filter == OBS_SCALE_POINT)
  579. scale_filter = "point";
  580. else if (item->scale_filter == OBS_SCALE_BILINEAR)
  581. scale_filter = "bilinear";
  582. else if (item->scale_filter == OBS_SCALE_BICUBIC)
  583. scale_filter = "bicubic";
  584. else if (item->scale_filter == OBS_SCALE_LANCZOS)
  585. scale_filter = "lanczos";
  586. else
  587. scale_filter = "disable";
  588. obs_data_set_string(item_data, "scale_filter", scale_filter);
  589. obs_data_array_push_back(array, item_data);
  590. obs_data_release(item_data);
  591. }
  592. static void scene_save(void *data, obs_data_t *settings)
  593. {
  594. struct obs_scene *scene = data;
  595. obs_data_array_t *array = obs_data_array_create();
  596. struct obs_scene_item *item;
  597. full_lock(scene);
  598. item = scene->first_item;
  599. while (item) {
  600. scene_save_item(array, item);
  601. item = item->next;
  602. }
  603. obs_data_set_int(settings, "id_counter", scene->id_counter);
  604. full_unlock(scene);
  605. obs_data_set_array(settings, "items", array);
  606. obs_data_array_release(array);
  607. }
  608. static uint32_t scene_getwidth(void *data)
  609. {
  610. UNUSED_PARAMETER(data);
  611. return obs->video.base_width;
  612. }
  613. static uint32_t scene_getheight(void *data)
  614. {
  615. UNUSED_PARAMETER(data);
  616. return obs->video.base_height;
  617. }
  618. static void apply_scene_item_audio_actions(struct obs_scene_item *item,
  619. float **p_buf, uint64_t ts, size_t sample_rate)
  620. {
  621. bool cur_visible = item->visible;
  622. uint64_t frame_num = 0;
  623. size_t deref_count = 0;
  624. float *buf = NULL;
  625. if (p_buf) {
  626. if (!*p_buf)
  627. *p_buf = malloc(AUDIO_OUTPUT_FRAMES * sizeof(float));
  628. buf = *p_buf;
  629. }
  630. pthread_mutex_lock(&item->actions_mutex);
  631. for (size_t i = 0; i < item->audio_actions.num; i++) {
  632. struct item_action action = item->audio_actions.array[i];
  633. uint64_t timestamp = action.timestamp;
  634. uint64_t new_frame_num;
  635. if (timestamp < ts)
  636. timestamp = ts;
  637. new_frame_num = (timestamp - ts) * (uint64_t)sample_rate /
  638. 1000000000ULL;
  639. if (ts && new_frame_num >= AUDIO_OUTPUT_FRAMES)
  640. break;
  641. da_erase(item->audio_actions, i--);
  642. item->visible = action.visible;
  643. if (!item->visible)
  644. deref_count++;
  645. if (buf && new_frame_num > frame_num) {
  646. for (; frame_num < new_frame_num; frame_num++)
  647. buf[frame_num] = cur_visible ? 1.0f : 0.0f;
  648. }
  649. cur_visible = item->visible;
  650. }
  651. if (buf) {
  652. for (; frame_num < AUDIO_OUTPUT_FRAMES; frame_num++)
  653. buf[frame_num] = cur_visible ? 1.0f : 0.0f;
  654. }
  655. pthread_mutex_unlock(&item->actions_mutex);
  656. while (deref_count--) {
  657. if (os_atomic_dec_long(&item->active_refs) == 0) {
  658. obs_source_remove_active_child(item->parent->source,
  659. item->source);
  660. }
  661. }
  662. }
  663. static bool apply_scene_item_volume(struct obs_scene_item *item,
  664. float **buf, uint64_t ts, size_t sample_rate)
  665. {
  666. bool actions_pending;
  667. struct item_action action;
  668. pthread_mutex_lock(&item->actions_mutex);
  669. actions_pending = item->audio_actions.num > 0;
  670. if (actions_pending)
  671. action = item->audio_actions.array[0];
  672. pthread_mutex_unlock(&item->actions_mutex);
  673. if (actions_pending) {
  674. uint64_t duration = (uint64_t)AUDIO_OUTPUT_FRAMES *
  675. 1000000000ULL / (uint64_t)sample_rate;
  676. if (!ts || action.timestamp < (ts + duration)) {
  677. apply_scene_item_audio_actions(item, buf, ts,
  678. sample_rate);
  679. return true;
  680. }
  681. }
  682. return false;
  683. }
  684. static void process_all_audio_actions(struct obs_scene_item *item,
  685. size_t sample_rate)
  686. {
  687. while (apply_scene_item_volume(item, NULL, 0, sample_rate));
  688. }
  689. static void mix_audio_with_buf(float *p_out, float *p_in, float *buf_in,
  690. size_t pos, size_t count)
  691. {
  692. register float *out = p_out;
  693. register float *buf = buf_in + pos;
  694. register float *in = p_in + pos;
  695. register float *end = in + count;
  696. while (in < end)
  697. *(out++) += *(in++) * *(buf++);
  698. }
  699. static inline void mix_audio(float *p_out, float *p_in,
  700. size_t pos, size_t count)
  701. {
  702. register float *out = p_out;
  703. register float *in = p_in + pos;
  704. register float *end = in + count;
  705. while (in < end)
  706. *(out++) += *(in++);
  707. }
  708. static bool scene_audio_render(void *data, uint64_t *ts_out,
  709. struct obs_source_audio_mix *audio_output, uint32_t mixers,
  710. size_t channels, size_t sample_rate)
  711. {
  712. uint64_t timestamp = 0;
  713. float *buf = NULL;
  714. struct obs_source_audio_mix child_audio;
  715. struct obs_scene *scene = data;
  716. struct obs_scene_item *item;
  717. audio_lock(scene);
  718. item = scene->first_item;
  719. while (item) {
  720. if (!obs_source_audio_pending(item->source)) {
  721. uint64_t source_ts =
  722. obs_source_get_audio_timestamp(item->source);
  723. if (source_ts && (!timestamp || source_ts < timestamp))
  724. timestamp = source_ts;
  725. }
  726. item = item->next;
  727. }
  728. if (!timestamp) {
  729. /* just process all pending audio actions if no audio playing,
  730. * otherwise audio actions will just never be processed */
  731. item = scene->first_item;
  732. while (item) {
  733. process_all_audio_actions(item, sample_rate);
  734. item = item->next;
  735. }
  736. audio_unlock(scene);
  737. return false;
  738. }
  739. item = scene->first_item;
  740. while (item) {
  741. uint64_t source_ts;
  742. size_t pos, count;
  743. bool apply_buf;
  744. apply_buf = apply_scene_item_volume(item, &buf, timestamp,
  745. sample_rate);
  746. if (obs_source_audio_pending(item->source)) {
  747. item = item->next;
  748. continue;
  749. }
  750. source_ts = obs_source_get_audio_timestamp(item->source);
  751. if (!source_ts) {
  752. item = item->next;
  753. continue;
  754. }
  755. pos = (size_t)ns_to_audio_frames(sample_rate,
  756. source_ts - timestamp);
  757. count = AUDIO_OUTPUT_FRAMES - pos;
  758. if (!apply_buf && !item->visible) {
  759. item = item->next;
  760. continue;
  761. }
  762. obs_source_get_audio_mix(item->source, &child_audio);
  763. for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
  764. if ((mixers & (1 << mix)) == 0)
  765. continue;
  766. for (size_t ch = 0; ch < channels; ch++) {
  767. float *out = audio_output->output[mix].data[ch];
  768. float *in = child_audio.output[mix].data[ch];
  769. if (apply_buf)
  770. mix_audio_with_buf(out, in, buf, pos,
  771. count);
  772. else
  773. mix_audio(out, in, pos, count);
  774. }
  775. }
  776. item = item->next;
  777. }
  778. *ts_out = timestamp;
  779. audio_unlock(scene);
  780. free(buf);
  781. return true;
  782. }
  783. const struct obs_source_info scene_info =
  784. {
  785. .id = "scene",
  786. .type = OBS_SOURCE_TYPE_SCENE,
  787. .output_flags = OBS_SOURCE_VIDEO |
  788. OBS_SOURCE_CUSTOM_DRAW |
  789. OBS_SOURCE_COMPOSITE,
  790. .get_name = scene_getname,
  791. .create = scene_create,
  792. .destroy = scene_destroy,
  793. .video_tick = scene_video_tick,
  794. .video_render = scene_video_render,
  795. .audio_render = scene_audio_render,
  796. .get_width = scene_getwidth,
  797. .get_height = scene_getheight,
  798. .load = scene_load,
  799. .save = scene_save,
  800. .enum_active_sources = scene_enum_active_sources,
  801. .enum_all_sources = scene_enum_all_sources
  802. };
  803. obs_scene_t *obs_scene_create(const char *name)
  804. {
  805. struct obs_source *source = obs_source_create("scene", name, NULL,
  806. NULL);
  807. return source->context.data;
  808. }
  809. obs_scene_t *obs_scene_create_private(const char *name)
  810. {
  811. struct obs_source *source = obs_source_create_private("scene", name,
  812. NULL);
  813. return source->context.data;
  814. }
  815. static obs_source_t *get_child_at_idx(obs_scene_t *scene, size_t idx)
  816. {
  817. struct obs_scene_item *item = scene->first_item;
  818. while (item && idx--)
  819. item = item->next;
  820. return item ? item->source : NULL;
  821. }
  822. static inline obs_source_t *dup_child(struct darray *array, size_t idx,
  823. obs_scene_t *new_scene, bool private)
  824. {
  825. DARRAY(struct obs_scene_item*) old_items;
  826. obs_source_t *source;
  827. old_items.da = *array;
  828. source = old_items.array[idx]->source;
  829. /* if the old item is referenced more than once in the old scene,
  830. * make sure they're referenced similarly in the new scene to reduce
  831. * load times */
  832. for (size_t i = 0; i < idx; i++) {
  833. struct obs_scene_item *item = old_items.array[i];
  834. if (item->source == source) {
  835. source = get_child_at_idx(new_scene, i);
  836. obs_source_addref(source);
  837. return source;
  838. }
  839. }
  840. return obs_source_duplicate(source, NULL, private);
  841. }
  842. static inline obs_source_t *new_ref(obs_source_t *source)
  843. {
  844. obs_source_addref(source);
  845. return source;
  846. }
  847. obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name,
  848. enum obs_scene_duplicate_type type)
  849. {
  850. bool make_unique = ((int)type & (1<<0)) != 0;
  851. bool make_private = ((int)type & (1<<1)) != 0;
  852. DARRAY(struct obs_scene_item*) items;
  853. struct obs_scene *new_scene;
  854. struct obs_scene_item *item;
  855. struct obs_source *source;
  856. da_init(items);
  857. if (!obs_ptr_valid(scene, "obs_scene_duplicate"))
  858. return NULL;
  859. /* --------------------------------- */
  860. full_lock(scene);
  861. item = scene->first_item;
  862. while (item) {
  863. da_push_back(items, &item);
  864. obs_sceneitem_addref(item);
  865. item = item->next;
  866. }
  867. full_unlock(scene);
  868. /* --------------------------------- */
  869. new_scene = make_private ?
  870. obs_scene_create_private(name) : obs_scene_create(name);
  871. for (size_t i = 0; i < items.num; i++) {
  872. item = items.array[i];
  873. source = make_unique ?
  874. dup_child(&items.da, i, new_scene, make_private) :
  875. new_ref(item->source);
  876. if (source) {
  877. struct obs_scene_item *new_item =
  878. obs_scene_add(new_scene, source);
  879. if (!new_item) {
  880. obs_source_release(source);
  881. continue;
  882. }
  883. if (!item->user_visible)
  884. set_visibility(new_item, false);
  885. new_item->selected = item->selected;
  886. new_item->pos = item->pos;
  887. new_item->rot = item->rot;
  888. new_item->scale = item->scale;
  889. new_item->align = item->align;
  890. new_item->last_width = item->last_width;
  891. new_item->last_height = item->last_height;
  892. new_item->output_scale = item->output_scale;
  893. new_item->scale_filter = item->scale_filter;
  894. new_item->box_transform = item->box_transform;
  895. new_item->draw_transform = item->draw_transform;
  896. new_item->bounds_type = item->bounds_type;
  897. new_item->bounds_align = item->bounds_align;
  898. new_item->bounds = item->bounds;
  899. obs_sceneitem_set_crop(new_item, &item->crop);
  900. if (!new_item->item_render &&
  901. item_texture_enabled(new_item)) {
  902. obs_enter_graphics();
  903. new_item->item_render = gs_texrender_create(
  904. GS_RGBA, GS_ZS_NONE);
  905. obs_leave_graphics();
  906. }
  907. obs_source_release(source);
  908. }
  909. }
  910. for (size_t i = 0; i < items.num; i++)
  911. obs_sceneitem_release(items.array[i]);
  912. da_free(items);
  913. return new_scene;
  914. }
  915. void obs_scene_addref(obs_scene_t *scene)
  916. {
  917. if (scene)
  918. obs_source_addref(scene->source);
  919. }
  920. void obs_scene_release(obs_scene_t *scene)
  921. {
  922. if (scene)
  923. obs_source_release(scene->source);
  924. }
  925. obs_source_t *obs_scene_get_source(const obs_scene_t *scene)
  926. {
  927. return scene ? scene->source : NULL;
  928. }
  929. obs_scene_t *obs_scene_from_source(const obs_source_t *source)
  930. {
  931. if (!source || source->info.id != scene_info.id)
  932. return NULL;
  933. return source->context.data;
  934. }
  935. obs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene, const char *name)
  936. {
  937. struct obs_scene_item *item;
  938. if (!scene)
  939. return NULL;
  940. full_lock(scene);
  941. item = scene->first_item;
  942. while (item) {
  943. if (strcmp(item->source->context.name, name) == 0)
  944. break;
  945. item = item->next;
  946. }
  947. full_unlock(scene);
  948. return item;
  949. }
  950. obs_sceneitem_t *obs_scene_find_sceneitem_by_id(obs_scene_t *scene, int64_t id)
  951. {
  952. struct obs_scene_item *item;
  953. if (!scene)
  954. return NULL;
  955. full_lock(scene);
  956. item = scene->first_item;
  957. while (item) {
  958. if (item->id == id)
  959. break;
  960. item = item->next;
  961. }
  962. full_unlock(scene);
  963. return item;
  964. }
  965. void obs_scene_enum_items(obs_scene_t *scene,
  966. bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*),
  967. void *param)
  968. {
  969. struct obs_scene_item *item;
  970. if (!scene || !callback)
  971. return;
  972. full_lock(scene);
  973. item = scene->first_item;
  974. while (item) {
  975. struct obs_scene_item *next = item->next;
  976. obs_sceneitem_addref(item);
  977. if (!callback(scene, item, param)) {
  978. obs_sceneitem_release(item);
  979. break;
  980. }
  981. obs_sceneitem_release(item);
  982. item = next;
  983. }
  984. full_unlock(scene);
  985. }
  986. static obs_sceneitem_t *sceneitem_get_ref(obs_sceneitem_t *si)
  987. {
  988. long owners = si->ref;
  989. while (owners > 0) {
  990. if (os_atomic_compare_swap_long(&si->ref, owners, owners + 1))
  991. return si;
  992. owners = si->ref;
  993. }
  994. return NULL;
  995. }
  996. static bool hotkey_show_sceneitem(void *data, obs_hotkey_pair_id id,
  997. obs_hotkey_t *hotkey, bool pressed)
  998. {
  999. UNUSED_PARAMETER(id);
  1000. UNUSED_PARAMETER(hotkey);
  1001. obs_sceneitem_t *si = sceneitem_get_ref(data);
  1002. if (pressed && si && !si->user_visible) {
  1003. obs_sceneitem_set_visible(si, true);
  1004. obs_sceneitem_release(si);
  1005. return true;
  1006. }
  1007. obs_sceneitem_release(si);
  1008. return false;
  1009. }
  1010. static bool hotkey_hide_sceneitem(void *data, obs_hotkey_pair_id id,
  1011. obs_hotkey_t *hotkey, bool pressed)
  1012. {
  1013. UNUSED_PARAMETER(id);
  1014. UNUSED_PARAMETER(hotkey);
  1015. obs_sceneitem_t *si = sceneitem_get_ref(data);
  1016. if (pressed && si && si->user_visible) {
  1017. obs_sceneitem_set_visible(si, false);
  1018. obs_sceneitem_release(si);
  1019. return true;
  1020. }
  1021. obs_sceneitem_release(si);
  1022. return false;
  1023. }
  1024. static void init_hotkeys(obs_scene_t *scene, obs_sceneitem_t *item,
  1025. const char *name)
  1026. {
  1027. struct dstr show = {0};
  1028. struct dstr hide = {0};
  1029. struct dstr show_desc = {0};
  1030. struct dstr hide_desc = {0};
  1031. dstr_copy(&show, "libobs.show_scene_item.%1");
  1032. dstr_replace(&show, "%1", name);
  1033. dstr_copy(&hide, "libobs.hide_scene_item.%1");
  1034. dstr_replace(&hide, "%1", name);
  1035. dstr_copy(&show_desc, obs->hotkeys.sceneitem_show);
  1036. dstr_replace(&show_desc, "%1", name);
  1037. dstr_copy(&hide_desc, obs->hotkeys.sceneitem_hide);
  1038. dstr_replace(&hide_desc, "%1", name);
  1039. item->toggle_visibility = obs_hotkey_pair_register_source(scene->source,
  1040. show.array, show_desc.array,
  1041. hide.array, hide_desc.array,
  1042. hotkey_show_sceneitem, hotkey_hide_sceneitem,
  1043. item, item);
  1044. dstr_free(&show);
  1045. dstr_free(&hide);
  1046. dstr_free(&show_desc);
  1047. dstr_free(&hide_desc);
  1048. }
  1049. static inline bool source_has_audio(obs_source_t *source)
  1050. {
  1051. return (source->info.output_flags &
  1052. (OBS_SOURCE_AUDIO | OBS_SOURCE_COMPOSITE)) != 0;
  1053. }
  1054. obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
  1055. {
  1056. struct obs_scene_item *last;
  1057. struct obs_scene_item *item;
  1058. struct calldata params;
  1059. uint8_t stack[128];
  1060. pthread_mutex_t mutex;
  1061. struct item_action action = {
  1062. .visible = true,
  1063. .timestamp = os_gettime_ns()
  1064. };
  1065. if (!scene)
  1066. return NULL;
  1067. if (!source) {
  1068. blog(LOG_ERROR, "Tried to add a NULL source to a scene");
  1069. return NULL;
  1070. }
  1071. if (pthread_mutex_init(&mutex, NULL) != 0) {
  1072. blog(LOG_WARNING, "Failed to create scene item mutex");
  1073. return NULL;
  1074. }
  1075. if (!obs_source_add_active_child(scene->source, source)) {
  1076. blog(LOG_WARNING, "Failed to add source to scene due to "
  1077. "infinite source recursion");
  1078. pthread_mutex_destroy(&mutex);
  1079. return NULL;
  1080. }
  1081. item = bzalloc(sizeof(struct obs_scene_item));
  1082. item->source = source;
  1083. item->id = ++scene->id_counter;
  1084. item->parent = scene;
  1085. item->ref = 1;
  1086. item->align = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
  1087. item->actions_mutex = mutex;
  1088. item->user_visible = true;
  1089. item->locked = false;
  1090. os_atomic_set_long(&item->active_refs, 1);
  1091. vec2_set(&item->scale, 1.0f, 1.0f);
  1092. matrix4_identity(&item->draw_transform);
  1093. matrix4_identity(&item->box_transform);
  1094. obs_source_addref(source);
  1095. if (source_has_audio(source)) {
  1096. item->visible = false;
  1097. da_push_back(item->audio_actions, &action);
  1098. } else {
  1099. item->visible = true;
  1100. }
  1101. if (item_texture_enabled(item)) {
  1102. obs_enter_graphics();
  1103. item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
  1104. obs_leave_graphics();
  1105. }
  1106. full_lock(scene);
  1107. last = scene->first_item;
  1108. if (!last) {
  1109. scene->first_item = item;
  1110. } else {
  1111. while (last->next)
  1112. last = last->next;
  1113. last->next = item;
  1114. item->prev = last;
  1115. }
  1116. full_unlock(scene);
  1117. if (!scene->source->context.private)
  1118. init_hotkeys(scene, item, obs_source_get_name(source));
  1119. calldata_init_fixed(&params, stack, sizeof(stack));
  1120. calldata_set_ptr(&params, "scene", scene);
  1121. calldata_set_ptr(&params, "item", item);
  1122. signal_handler_signal(scene->source->context.signals, "item_add",
  1123. &params);
  1124. return item;
  1125. }
  1126. static void obs_sceneitem_destroy(obs_sceneitem_t *item)
  1127. {
  1128. if (item) {
  1129. if (item->item_render) {
  1130. obs_enter_graphics();
  1131. gs_texrender_destroy(item->item_render);
  1132. obs_leave_graphics();
  1133. }
  1134. obs_hotkey_pair_unregister(item->toggle_visibility);
  1135. pthread_mutex_destroy(&item->actions_mutex);
  1136. if (item->source)
  1137. obs_source_release(item->source);
  1138. da_free(item->audio_actions);
  1139. bfree(item);
  1140. }
  1141. }
  1142. void obs_sceneitem_addref(obs_sceneitem_t *item)
  1143. {
  1144. if (item)
  1145. os_atomic_inc_long(&item->ref);
  1146. }
  1147. void obs_sceneitem_release(obs_sceneitem_t *item)
  1148. {
  1149. if (!item)
  1150. return;
  1151. if (os_atomic_dec_long(&item->ref) == 0)
  1152. obs_sceneitem_destroy(item);
  1153. }
  1154. void obs_sceneitem_remove(obs_sceneitem_t *item)
  1155. {
  1156. obs_scene_t *scene;
  1157. if (!item)
  1158. return;
  1159. scene = item->parent;
  1160. full_lock(scene);
  1161. if (item->removed) {
  1162. if (scene)
  1163. full_unlock(scene);
  1164. return;
  1165. }
  1166. item->removed = true;
  1167. assert(scene != NULL);
  1168. assert(scene->source != NULL);
  1169. set_visibility(item, false);
  1170. signal_item_remove(item);
  1171. detach_sceneitem(item);
  1172. full_unlock(scene);
  1173. obs_sceneitem_release(item);
  1174. }
  1175. obs_scene_t *obs_sceneitem_get_scene(const obs_sceneitem_t *item)
  1176. {
  1177. return item ? item->parent : NULL;
  1178. }
  1179. obs_source_t *obs_sceneitem_get_source(const obs_sceneitem_t *item)
  1180. {
  1181. return item ? item->source : NULL;
  1182. }
  1183. void obs_sceneitem_select(obs_sceneitem_t *item, bool select)
  1184. {
  1185. struct calldata params;
  1186. uint8_t stack[128];
  1187. const char *command = select ? "item_select" : "item_deselect";
  1188. if (!item || item->selected == select || !item->parent)
  1189. return;
  1190. item->selected = select;
  1191. calldata_init_fixed(&params, stack, sizeof(stack));
  1192. calldata_set_ptr(&params, "scene", item->parent);
  1193. calldata_set_ptr(&params, "item", item);
  1194. signal_handler_signal(item->parent->source->context.signals,
  1195. command, &params);
  1196. }
  1197. bool obs_sceneitem_selected(const obs_sceneitem_t *item)
  1198. {
  1199. return item ? item->selected : false;
  1200. }
  1201. void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos)
  1202. {
  1203. if (item) {
  1204. vec2_copy(&item->pos, pos);
  1205. update_item_transform(item);
  1206. }
  1207. }
  1208. void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot)
  1209. {
  1210. if (item) {
  1211. item->rot = rot;
  1212. update_item_transform(item);
  1213. }
  1214. }
  1215. void obs_sceneitem_set_scale(obs_sceneitem_t *item, const struct vec2 *scale)
  1216. {
  1217. if (item) {
  1218. vec2_copy(&item->scale, scale);
  1219. update_item_transform(item);
  1220. }
  1221. }
  1222. void obs_sceneitem_set_alignment(obs_sceneitem_t *item, uint32_t alignment)
  1223. {
  1224. if (item) {
  1225. item->align = alignment;
  1226. update_item_transform(item);
  1227. }
  1228. }
  1229. static inline void signal_reorder(struct obs_scene_item *item)
  1230. {
  1231. const char *command = NULL;
  1232. struct calldata params;
  1233. uint8_t stack[128];
  1234. command = "reorder";
  1235. calldata_init_fixed(&params, stack, sizeof(stack));
  1236. calldata_set_ptr(&params, "scene", item->parent);
  1237. signal_handler_signal(item->parent->source->context.signals,
  1238. command, &params);
  1239. }
  1240. void obs_sceneitem_set_order(obs_sceneitem_t *item,
  1241. enum obs_order_movement movement)
  1242. {
  1243. if (!item) return;
  1244. struct obs_scene_item *next, *prev;
  1245. struct obs_scene *scene = item->parent;
  1246. obs_scene_addref(scene);
  1247. full_lock(scene);
  1248. next = item->next;
  1249. prev = item->prev;
  1250. detach_sceneitem(item);
  1251. if (movement == OBS_ORDER_MOVE_DOWN) {
  1252. attach_sceneitem(scene, item, prev ? prev->prev : NULL);
  1253. } else if (movement == OBS_ORDER_MOVE_UP) {
  1254. attach_sceneitem(scene, item, next ? next : prev);
  1255. } else if (movement == OBS_ORDER_MOVE_TOP) {
  1256. struct obs_scene_item *last = next;
  1257. if (!last) {
  1258. last = prev;
  1259. } else {
  1260. while (last->next)
  1261. last = last->next;
  1262. }
  1263. attach_sceneitem(scene, item, last);
  1264. } else if (movement == OBS_ORDER_MOVE_BOTTOM) {
  1265. attach_sceneitem(scene, item, NULL);
  1266. }
  1267. signal_reorder(item);
  1268. full_unlock(scene);
  1269. obs_scene_release(scene);
  1270. }
  1271. void obs_sceneitem_set_order_position(obs_sceneitem_t *item,
  1272. int position)
  1273. {
  1274. if (!item) return;
  1275. struct obs_scene *scene = item->parent;
  1276. struct obs_scene_item *next;
  1277. obs_scene_addref(scene);
  1278. full_lock(scene);
  1279. detach_sceneitem(item);
  1280. next = scene->first_item;
  1281. if (position == 0) {
  1282. attach_sceneitem(scene, item, NULL);
  1283. } else {
  1284. for (int i = position; i > 1; --i) {
  1285. if (next->next == NULL)
  1286. break;
  1287. next = next->next;
  1288. }
  1289. attach_sceneitem(scene, item, next);
  1290. }
  1291. signal_reorder(item);
  1292. full_unlock(scene);
  1293. obs_scene_release(scene);
  1294. }
  1295. void obs_sceneitem_set_bounds_type(obs_sceneitem_t *item,
  1296. enum obs_bounds_type type)
  1297. {
  1298. if (item) {
  1299. item->bounds_type = type;
  1300. update_item_transform(item);
  1301. }
  1302. }
  1303. void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item,
  1304. uint32_t alignment)
  1305. {
  1306. if (item) {
  1307. item->bounds_align = alignment;
  1308. update_item_transform(item);
  1309. }
  1310. }
  1311. void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds)
  1312. {
  1313. if (item) {
  1314. item->bounds = *bounds;
  1315. update_item_transform(item);
  1316. }
  1317. }
  1318. void obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos)
  1319. {
  1320. if (item)
  1321. vec2_copy(pos, &item->pos);
  1322. }
  1323. float obs_sceneitem_get_rot(const obs_sceneitem_t *item)
  1324. {
  1325. return item ? item->rot : 0.0f;
  1326. }
  1327. void obs_sceneitem_get_scale(const obs_sceneitem_t *item, struct vec2 *scale)
  1328. {
  1329. if (item)
  1330. vec2_copy(scale, &item->scale);
  1331. }
  1332. uint32_t obs_sceneitem_get_alignment(const obs_sceneitem_t *item)
  1333. {
  1334. return item ? item->align : 0;
  1335. }
  1336. enum obs_bounds_type obs_sceneitem_get_bounds_type(const obs_sceneitem_t *item)
  1337. {
  1338. return item ? item->bounds_type : OBS_BOUNDS_NONE;
  1339. }
  1340. uint32_t obs_sceneitem_get_bounds_alignment(const obs_sceneitem_t *item)
  1341. {
  1342. return item ? item->bounds_align : 0;
  1343. }
  1344. void obs_sceneitem_get_bounds(const obs_sceneitem_t *item, struct vec2 *bounds)
  1345. {
  1346. if (item)
  1347. *bounds = item->bounds;
  1348. }
  1349. void obs_sceneitem_get_info(const obs_sceneitem_t *item,
  1350. struct obs_transform_info *info)
  1351. {
  1352. if (item && info) {
  1353. info->pos = item->pos;
  1354. info->rot = item->rot;
  1355. info->scale = item->scale;
  1356. info->alignment = item->align;
  1357. info->bounds_type = item->bounds_type;
  1358. info->bounds_alignment = item->bounds_align;
  1359. info->bounds = item->bounds;
  1360. }
  1361. }
  1362. void obs_sceneitem_set_info(obs_sceneitem_t *item,
  1363. const struct obs_transform_info *info)
  1364. {
  1365. if (item && info) {
  1366. item->pos = info->pos;
  1367. item->rot = info->rot;
  1368. item->scale = info->scale;
  1369. item->align = info->alignment;
  1370. item->bounds_type = info->bounds_type;
  1371. item->bounds_align = info->bounds_alignment;
  1372. item->bounds = info->bounds;
  1373. update_item_transform(item);
  1374. }
  1375. }
  1376. void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item,
  1377. struct matrix4 *transform)
  1378. {
  1379. if (item)
  1380. matrix4_copy(transform, &item->draw_transform);
  1381. }
  1382. void obs_sceneitem_get_box_transform(const obs_sceneitem_t *item,
  1383. struct matrix4 *transform)
  1384. {
  1385. if (item)
  1386. matrix4_copy(transform, &item->box_transform);
  1387. }
  1388. bool obs_sceneitem_visible(const obs_sceneitem_t *item)
  1389. {
  1390. return item ? item->user_visible : false;
  1391. }
  1392. bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible)
  1393. {
  1394. struct calldata cd;
  1395. uint8_t stack[256];
  1396. struct item_action action = {
  1397. .visible = visible,
  1398. .timestamp = os_gettime_ns()
  1399. };
  1400. if (!item)
  1401. return false;
  1402. if (item->user_visible == visible)
  1403. return false;
  1404. if (!item->parent)
  1405. return false;
  1406. if (visible) {
  1407. if (os_atomic_inc_long(&item->active_refs) == 1) {
  1408. if (!obs_source_add_active_child(item->parent->source,
  1409. item->source)) {
  1410. os_atomic_dec_long(&item->active_refs);
  1411. return false;
  1412. }
  1413. }
  1414. }
  1415. item->user_visible = visible;
  1416. calldata_init_fixed(&cd, stack, sizeof(stack));
  1417. calldata_set_ptr(&cd, "scene", item->parent);
  1418. calldata_set_ptr(&cd, "item", item);
  1419. calldata_set_bool(&cd, "visible", visible);
  1420. signal_handler_signal(item->parent->source->context.signals,
  1421. "item_visible", &cd);
  1422. if (source_has_audio(item->source)) {
  1423. pthread_mutex_lock(&item->actions_mutex);
  1424. da_push_back(item->audio_actions, &action);
  1425. pthread_mutex_unlock(&item->actions_mutex);
  1426. } else {
  1427. set_visibility(item, visible);
  1428. }
  1429. return true;
  1430. }
  1431. bool obs_sceneitem_locked(const obs_sceneitem_t *item)
  1432. {
  1433. return item ? item->locked : false;
  1434. }
  1435. bool obs_sceneitem_set_locked(obs_sceneitem_t *item, bool lock)
  1436. {
  1437. if (!item)
  1438. return false;
  1439. if (item->locked == lock)
  1440. return false;
  1441. if (!item->parent)
  1442. return false;
  1443. item->locked = lock;
  1444. return true;
  1445. }
  1446. static bool sceneitems_match(obs_scene_t *scene, obs_sceneitem_t * const *items,
  1447. size_t size, bool *order_matches)
  1448. {
  1449. obs_sceneitem_t *item = scene->first_item;
  1450. size_t count = 0;
  1451. while (item) {
  1452. bool found = false;
  1453. for (size_t i = 0; i < size; i++) {
  1454. if (items[i] != item)
  1455. continue;
  1456. if (count != i)
  1457. *order_matches = false;
  1458. found = true;
  1459. break;
  1460. }
  1461. if (!found)
  1462. return false;
  1463. item = item->next;
  1464. count += 1;
  1465. }
  1466. return count == size;
  1467. }
  1468. bool obs_scene_reorder_items(obs_scene_t *scene,
  1469. obs_sceneitem_t * const *item_order, size_t item_order_size)
  1470. {
  1471. if (!scene || !item_order_size)
  1472. return false;
  1473. obs_scene_addref(scene);
  1474. full_lock(scene);
  1475. bool order_matches = true;
  1476. if (!sceneitems_match(scene, item_order, item_order_size,
  1477. &order_matches) || order_matches) {
  1478. full_unlock(scene);
  1479. obs_scene_release(scene);
  1480. return false;
  1481. }
  1482. scene->first_item = item_order[0];
  1483. obs_sceneitem_t *prev = NULL;
  1484. for (size_t i = 0; i < item_order_size; i++) {
  1485. item_order[i]->prev = prev;
  1486. item_order[i]->next = NULL;
  1487. if (prev)
  1488. prev->next = item_order[i];
  1489. prev = item_order[i];
  1490. }
  1491. signal_reorder(scene->first_item);
  1492. full_unlock(scene);
  1493. obs_scene_release(scene);
  1494. return true;
  1495. }
  1496. void obs_scene_atomic_update(obs_scene_t *scene,
  1497. obs_scene_atomic_update_func func, void *data)
  1498. {
  1499. if (!scene)
  1500. return;
  1501. obs_scene_addref(scene);
  1502. full_lock(scene);
  1503. func(data, scene);
  1504. full_unlock(scene);
  1505. obs_scene_release(scene);
  1506. }
  1507. static inline bool crop_equal(const struct obs_sceneitem_crop *crop1,
  1508. const struct obs_sceneitem_crop *crop2)
  1509. {
  1510. return crop1->left == crop2->left &&
  1511. crop1->right == crop2->right &&
  1512. crop1->top == crop2->top &&
  1513. crop1->bottom == crop2->bottom;
  1514. }
  1515. void obs_sceneitem_set_crop(obs_sceneitem_t *item,
  1516. const struct obs_sceneitem_crop *crop)
  1517. {
  1518. bool item_tex_now_enabled;
  1519. if (!obs_ptr_valid(item, "obs_sceneitem_set_crop"))
  1520. return;
  1521. if (!obs_ptr_valid(crop, "obs_sceneitem_set_crop"))
  1522. return;
  1523. if (crop_equal(crop, &item->crop))
  1524. return;
  1525. item_tex_now_enabled = crop_enabled(crop) ||
  1526. scale_filter_enabled(item) || item_is_scene(item);
  1527. obs_enter_graphics();
  1528. if (!item_tex_now_enabled) {
  1529. gs_texrender_destroy(item->item_render);
  1530. item->item_render = NULL;
  1531. } else if (!item->item_render) {
  1532. item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
  1533. }
  1534. memcpy(&item->crop, crop, sizeof(*crop));
  1535. if (item->crop.left < 0) item->crop.left = 0;
  1536. if (item->crop.right < 0) item->crop.right = 0;
  1537. if (item->crop.top < 0) item->crop.top = 0;
  1538. if (item->crop.bottom < 0) item->crop.bottom = 0;
  1539. obs_leave_graphics();
  1540. update_item_transform(item);
  1541. }
  1542. void obs_sceneitem_get_crop(const obs_sceneitem_t *item,
  1543. struct obs_sceneitem_crop *crop)
  1544. {
  1545. if (!obs_ptr_valid(item, "obs_sceneitem_get_crop"))
  1546. return;
  1547. if (!obs_ptr_valid(crop, "obs_sceneitem_get_crop"))
  1548. return;
  1549. memcpy(crop, &item->crop, sizeof(*crop));
  1550. }
  1551. void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item,
  1552. enum obs_scale_type filter)
  1553. {
  1554. if (!obs_ptr_valid(item, "obs_sceneitem_set_scale_filter"))
  1555. return;
  1556. item->scale_filter = filter;
  1557. obs_enter_graphics();
  1558. if (!item_texture_enabled(item)) {
  1559. gs_texrender_destroy(item->item_render);
  1560. item->item_render = NULL;
  1561. } else if (!item->item_render) {
  1562. item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
  1563. }
  1564. obs_leave_graphics();
  1565. update_item_transform(item);
  1566. }
  1567. enum obs_scale_type obs_sceneitem_get_scale_filter(
  1568. obs_sceneitem_t *item)
  1569. {
  1570. return obs_ptr_valid(item, "obs_sceneitem_get_scale_filter") ?
  1571. item->scale_filter : OBS_SCALE_DISABLE;
  1572. }
  1573. void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item)
  1574. {
  1575. if (!obs_ptr_valid(item, "obs_sceneitem_defer_update_begin"))
  1576. return;
  1577. os_atomic_inc_long(&item->defer_update);
  1578. }
  1579. void obs_sceneitem_defer_update_end(obs_sceneitem_t *item)
  1580. {
  1581. if (!obs_ptr_valid(item, "obs_sceneitem_defer_update_end"))
  1582. return;
  1583. if (os_atomic_dec_long(&item->defer_update) == 0)
  1584. update_item_transform(item);
  1585. }
  1586. int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item)
  1587. {
  1588. if (!obs_ptr_valid(item, "obs_sceneitem_get_id"))
  1589. return 0;
  1590. return item->id;
  1591. }