1 /******************************************************************************
2 Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
3
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
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
17
18 #include <inttypes.h>
19 #include <math.h>
20
21 #include "media-io/format-conversion.h"
22 #include "media-io/video-frame.h"
23 #include "media-io/audio-io.h"
24 #include "util/threading.h"
25 #include "util/platform.h"
26 #include "util/util_uint64.h"
27 #include "callback/calldata.h"
28 #include "graphics/matrix3.h"
29 #include "graphics/vec3.h"
30
31 #include "obs.h"
32 #include "obs-internal.h"
33
34 static bool filter_compatible(obs_source_t *source, obs_source_t *filter);
35
data_valid(const struct obs_source * source,const char * f)36 static inline bool data_valid(const struct obs_source *source, const char *f)
37 {
38 return obs_source_valid(source, f) && source->context.data;
39 }
40
deinterlacing_enabled(const struct obs_source * source)41 static inline bool deinterlacing_enabled(const struct obs_source *source)
42 {
43 return source->deinterlace_mode != OBS_DEINTERLACE_MODE_DISABLE;
44 }
45
get_source_info(const char * id)46 struct obs_source_info *get_source_info(const char *id)
47 {
48 for (size_t i = 0; i < obs->source_types.num; i++) {
49 struct obs_source_info *info = &obs->source_types.array[i];
50 if (strcmp(info->id, id) == 0)
51 return info;
52 }
53
54 return NULL;
55 }
56
get_source_info2(const char * unversioned_id,uint32_t ver)57 struct obs_source_info *get_source_info2(const char *unversioned_id,
58 uint32_t ver)
59 {
60 for (size_t i = 0; i < obs->source_types.num; i++) {
61 struct obs_source_info *info = &obs->source_types.array[i];
62 if (strcmp(info->unversioned_id, unversioned_id) == 0 &&
63 info->version == ver)
64 return info;
65 }
66
67 return NULL;
68 }
69
70 static const char *source_signals[] = {
71 "void destroy(ptr source)",
72 "void remove(ptr source)",
73 "void save(ptr source)",
74 "void load(ptr source)",
75 "void activate(ptr source)",
76 "void deactivate(ptr source)",
77 "void show(ptr source)",
78 "void hide(ptr source)",
79 "void mute(ptr source, bool muted)",
80 "void push_to_mute_changed(ptr source, bool enabled)",
81 "void push_to_mute_delay(ptr source, int delay)",
82 "void push_to_talk_changed(ptr source, bool enabled)",
83 "void push_to_talk_delay(ptr source, int delay)",
84 "void enable(ptr source, bool enabled)",
85 "void rename(ptr source, string new_name, string prev_name)",
86 "void volume(ptr source, in out float volume)",
87 "void update_properties(ptr source)",
88 "void update_flags(ptr source, int flags)",
89 "void audio_sync(ptr source, int out int offset)",
90 "void audio_mixers(ptr source, in out int mixers)",
91 "void audio_activate(ptr source)",
92 "void audio_deactivate(ptr source)",
93 "void filter_add(ptr source, ptr filter)",
94 "void filter_remove(ptr source, ptr filter)",
95 "void reorder_filters(ptr source)",
96 "void transition_start(ptr source)",
97 "void transition_video_stop(ptr source)",
98 "void transition_stop(ptr source)",
99 "void media_play(ptr source)",
100 "void media_pause(ptr source)",
101 "void media_restart(ptr source)",
102 "void media_stopped(ptr source)",
103 "void media_next(ptr source)",
104 "void media_previous(ptr source)",
105 "void media_started(ptr source)",
106 "void media_ended(ptr source)",
107 NULL,
108 };
109
obs_source_init_context(struct obs_source * source,obs_data_t * settings,const char * name,obs_data_t * hotkey_data,bool private)110 bool obs_source_init_context(struct obs_source *source, obs_data_t *settings,
111 const char *name, obs_data_t *hotkey_data,
112 bool private)
113 {
114 if (!obs_context_data_init(&source->context, OBS_OBJ_TYPE_SOURCE,
115 settings, name, hotkey_data, private))
116 return false;
117
118 return signal_handler_add_array(source->context.signals,
119 source_signals);
120 }
121
obs_source_get_display_name(const char * id)122 const char *obs_source_get_display_name(const char *id)
123 {
124 const struct obs_source_info *info = get_source_info(id);
125 return (info != NULL) ? info->get_name(info->type_data) : NULL;
126 }
127
allocate_audio_output_buffer(struct obs_source * source)128 static void allocate_audio_output_buffer(struct obs_source *source)
129 {
130 size_t size = sizeof(float) * AUDIO_OUTPUT_FRAMES * MAX_AUDIO_CHANNELS *
131 MAX_AUDIO_MIXES;
132 float *ptr = bzalloc(size);
133
134 for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
135 size_t mix_pos = mix * AUDIO_OUTPUT_FRAMES * MAX_AUDIO_CHANNELS;
136
137 for (size_t i = 0; i < MAX_AUDIO_CHANNELS; i++) {
138 source->audio_output_buf[mix][i] =
139 ptr + mix_pos + AUDIO_OUTPUT_FRAMES * i;
140 }
141 }
142 }
143
allocate_audio_mix_buffer(struct obs_source * source)144 static void allocate_audio_mix_buffer(struct obs_source *source)
145 {
146 size_t size = sizeof(float) * AUDIO_OUTPUT_FRAMES * MAX_AUDIO_CHANNELS;
147 float *ptr = bzalloc(size);
148
149 for (size_t i = 0; i < MAX_AUDIO_CHANNELS; i++) {
150 source->audio_mix_buf[i] = ptr + AUDIO_OUTPUT_FRAMES * i;
151 }
152 }
153
is_async_video_source(const struct obs_source * source)154 static inline bool is_async_video_source(const struct obs_source *source)
155 {
156 return (source->info.output_flags & OBS_SOURCE_ASYNC_VIDEO) ==
157 OBS_SOURCE_ASYNC_VIDEO;
158 }
159
is_audio_source(const struct obs_source * source)160 static inline bool is_audio_source(const struct obs_source *source)
161 {
162 return source->info.output_flags & OBS_SOURCE_AUDIO;
163 }
164
is_composite_source(const struct obs_source * source)165 static inline bool is_composite_source(const struct obs_source *source)
166 {
167 return source->info.output_flags & OBS_SOURCE_COMPOSITE;
168 }
169
170 extern char *find_libobs_data_file(const char *file);
171
172 /* internal initialization */
obs_source_init(struct obs_source * source)173 static bool obs_source_init(struct obs_source *source)
174 {
175 source->user_volume = 1.0f;
176 source->volume = 1.0f;
177 source->sync_offset = 0;
178 source->balance = 0.5f;
179 source->audio_active = true;
180 pthread_mutex_init_value(&source->filter_mutex);
181 pthread_mutex_init_value(&source->async_mutex);
182 pthread_mutex_init_value(&source->audio_mutex);
183 pthread_mutex_init_value(&source->audio_buf_mutex);
184 pthread_mutex_init_value(&source->audio_cb_mutex);
185 pthread_mutex_init_value(&source->caption_cb_mutex);
186
187 if (pthread_mutex_init_recursive(&source->filter_mutex) != 0)
188 return false;
189 if (pthread_mutex_init(&source->audio_buf_mutex, NULL) != 0)
190 return false;
191 if (pthread_mutex_init(&source->audio_actions_mutex, NULL) != 0)
192 return false;
193 if (pthread_mutex_init(&source->audio_cb_mutex, NULL) != 0)
194 return false;
195 if (pthread_mutex_init(&source->audio_mutex, NULL) != 0)
196 return false;
197 if (pthread_mutex_init(&source->async_mutex, NULL) != 0)
198 return false;
199 if (pthread_mutex_init(&source->caption_cb_mutex, NULL) != 0)
200 return false;
201
202 if (is_audio_source(source) || is_composite_source(source))
203 allocate_audio_output_buffer(source);
204 if (source->info.audio_mix)
205 allocate_audio_mix_buffer(source);
206
207 if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) {
208 if (!obs_transition_init(source))
209 return false;
210 }
211
212 source->control = bzalloc(sizeof(obs_weak_source_t));
213 source->deinterlace_top_first = true;
214 source->control->source = source;
215 source->audio_mixers = 0xFF;
216
217 source->private_settings = obs_data_create();
218 return true;
219 }
220
obs_source_init_finalize(struct obs_source * source)221 static void obs_source_init_finalize(struct obs_source *source)
222 {
223 if (is_audio_source(source)) {
224 pthread_mutex_lock(&obs->data.audio_sources_mutex);
225
226 source->next_audio_source = obs->data.first_audio_source;
227 source->prev_next_audio_source = &obs->data.first_audio_source;
228 if (obs->data.first_audio_source)
229 obs->data.first_audio_source->prev_next_audio_source =
230 &source->next_audio_source;
231 obs->data.first_audio_source = source;
232
233 pthread_mutex_unlock(&obs->data.audio_sources_mutex);
234 }
235
236 obs_context_data_insert(&source->context, &obs->data.sources_mutex,
237 &obs->data.first_source);
238 }
239
obs_source_hotkey_mute(void * data,obs_hotkey_pair_id id,obs_hotkey_t * key,bool pressed)240 static bool obs_source_hotkey_mute(void *data, obs_hotkey_pair_id id,
241 obs_hotkey_t *key, bool pressed)
242 {
243 UNUSED_PARAMETER(id);
244 UNUSED_PARAMETER(key);
245
246 struct obs_source *source = data;
247
248 if (!pressed || obs_source_muted(source))
249 return false;
250
251 obs_source_set_muted(source, true);
252 return true;
253 }
254
obs_source_hotkey_unmute(void * data,obs_hotkey_pair_id id,obs_hotkey_t * key,bool pressed)255 static bool obs_source_hotkey_unmute(void *data, obs_hotkey_pair_id id,
256 obs_hotkey_t *key, bool pressed)
257 {
258 UNUSED_PARAMETER(id);
259 UNUSED_PARAMETER(key);
260
261 struct obs_source *source = data;
262
263 if (!pressed || !obs_source_muted(source))
264 return false;
265
266 obs_source_set_muted(source, false);
267 return true;
268 }
269
obs_source_hotkey_push_to_mute(void * data,obs_hotkey_id id,obs_hotkey_t * key,bool pressed)270 static void obs_source_hotkey_push_to_mute(void *data, obs_hotkey_id id,
271 obs_hotkey_t *key, bool pressed)
272 {
273 struct audio_action action = {.timestamp = os_gettime_ns(),
274 .type = AUDIO_ACTION_PTM,
275 .set = pressed};
276
277 UNUSED_PARAMETER(id);
278 UNUSED_PARAMETER(key);
279
280 struct obs_source *source = data;
281
282 pthread_mutex_lock(&source->audio_actions_mutex);
283 da_push_back(source->audio_actions, &action);
284 pthread_mutex_unlock(&source->audio_actions_mutex);
285
286 source->user_push_to_mute_pressed = pressed;
287 }
288
obs_source_hotkey_push_to_talk(void * data,obs_hotkey_id id,obs_hotkey_t * key,bool pressed)289 static void obs_source_hotkey_push_to_talk(void *data, obs_hotkey_id id,
290 obs_hotkey_t *key, bool pressed)
291 {
292 struct audio_action action = {.timestamp = os_gettime_ns(),
293 .type = AUDIO_ACTION_PTT,
294 .set = pressed};
295
296 UNUSED_PARAMETER(id);
297 UNUSED_PARAMETER(key);
298
299 struct obs_source *source = data;
300
301 pthread_mutex_lock(&source->audio_actions_mutex);
302 da_push_back(source->audio_actions, &action);
303 pthread_mutex_unlock(&source->audio_actions_mutex);
304
305 source->user_push_to_talk_pressed = pressed;
306 }
307
obs_source_init_audio_hotkeys(struct obs_source * source)308 static void obs_source_init_audio_hotkeys(struct obs_source *source)
309 {
310 if (!(source->info.output_flags & OBS_SOURCE_AUDIO) ||
311 source->info.type != OBS_SOURCE_TYPE_INPUT) {
312 source->mute_unmute_key = OBS_INVALID_HOTKEY_ID;
313 source->push_to_talk_key = OBS_INVALID_HOTKEY_ID;
314 return;
315 }
316
317 source->mute_unmute_key = obs_hotkey_pair_register_source(
318 source, "libobs.mute", obs->hotkeys.mute, "libobs.unmute",
319 obs->hotkeys.unmute, obs_source_hotkey_mute,
320 obs_source_hotkey_unmute, source, source);
321
322 source->push_to_mute_key = obs_hotkey_register_source(
323 source, "libobs.push-to-mute", obs->hotkeys.push_to_mute,
324 obs_source_hotkey_push_to_mute, source);
325
326 source->push_to_talk_key = obs_hotkey_register_source(
327 source, "libobs.push-to-talk", obs->hotkeys.push_to_talk,
328 obs_source_hotkey_push_to_talk, source);
329 }
330
331 static obs_source_t *
obs_source_create_internal(const char * id,const char * name,obs_data_t * settings,obs_data_t * hotkey_data,bool private,uint32_t last_obs_ver)332 obs_source_create_internal(const char *id, const char *name,
333 obs_data_t *settings, obs_data_t *hotkey_data,
334 bool private, uint32_t last_obs_ver)
335 {
336 struct obs_source *source = bzalloc(sizeof(struct obs_source));
337
338 const struct obs_source_info *info = get_source_info(id);
339 if (!info) {
340 blog(LOG_ERROR, "Source ID '%s' not found", id);
341
342 source->info.id = bstrdup(id);
343 source->owns_info_id = true;
344 source->info.unversioned_id = bstrdup(source->info.id);
345 } else {
346 source->info = *info;
347
348 /* Always mark filters as private so they aren't found by
349 * source enum/search functions.
350 *
351 * XXX: Fix design flaws with filters */
352 if (info->type == OBS_SOURCE_TYPE_FILTER)
353 private
354 = true;
355 }
356
357 source->mute_unmute_key = OBS_INVALID_HOTKEY_PAIR_ID;
358 source->push_to_mute_key = OBS_INVALID_HOTKEY_ID;
359 source->push_to_talk_key = OBS_INVALID_HOTKEY_ID;
360 source->last_obs_ver = last_obs_ver;
361
362 if (!obs_source_init_context(source, settings, name, hotkey_data,
363 private))
364 goto fail;
365
366 if (info) {
367 if (info->get_defaults) {
368 info->get_defaults(source->context.settings);
369 }
370 if (info->get_defaults2) {
371 info->get_defaults2(info->type_data,
372 source->context.settings);
373 }
374 }
375
376 if (!obs_source_init(source))
377 goto fail;
378
379 if (!private)
380 obs_source_init_audio_hotkeys(source);
381
382 /* allow the source to be created even if creation fails so that the
383 * user's data doesn't become lost */
384 if (info && info->create)
385 source->context.data =
386 info->create(source->context.settings, source);
387 if ((!info || info->create) && !source->context.data)
388 blog(LOG_ERROR, "Failed to create source '%s'!", name);
389
390 blog(LOG_DEBUG, "%ssource '%s' (%s) created", private ? "private " : "",
391 name, id);
392
393 source->flags = source->default_flags;
394 source->enabled = true;
395
396 if (!private) {
397 obs_source_dosignal(source, "source_create", NULL);
398 }
399
400 obs_source_init_finalize(source);
401 return source;
402
403 fail:
404 blog(LOG_ERROR, "obs_source_create failed");
405 obs_source_destroy(source);
406 return NULL;
407 }
408
obs_source_create(const char * id,const char * name,obs_data_t * settings,obs_data_t * hotkey_data)409 obs_source_t *obs_source_create(const char *id, const char *name,
410 obs_data_t *settings, obs_data_t *hotkey_data)
411 {
412 return obs_source_create_internal(id, name, settings, hotkey_data,
413 false, LIBOBS_API_VER);
414 }
415
obs_source_create_private(const char * id,const char * name,obs_data_t * settings)416 obs_source_t *obs_source_create_private(const char *id, const char *name,
417 obs_data_t *settings)
418 {
419 return obs_source_create_internal(id, name, settings, NULL, true,
420 LIBOBS_API_VER);
421 }
422
obs_source_create_set_last_ver(const char * id,const char * name,obs_data_t * settings,obs_data_t * hotkey_data,uint32_t last_obs_ver)423 obs_source_t *obs_source_create_set_last_ver(const char *id, const char *name,
424 obs_data_t *settings,
425 obs_data_t *hotkey_data,
426 uint32_t last_obs_ver)
427 {
428 return obs_source_create_internal(id, name, settings, hotkey_data,
429 false, last_obs_ver);
430 }
431
get_new_filter_name(obs_source_t * dst,const char * name)432 static char *get_new_filter_name(obs_source_t *dst, const char *name)
433 {
434 struct dstr new_name = {0};
435 int inc = 0;
436
437 dstr_copy(&new_name, name);
438
439 for (;;) {
440 obs_source_t *existing_filter =
441 obs_source_get_filter_by_name(dst, new_name.array);
442 if (!existing_filter)
443 break;
444
445 obs_source_release(existing_filter);
446
447 dstr_printf(&new_name, "%s %d", name, ++inc + 1);
448 }
449
450 return new_name.array;
451 }
452
duplicate_filters(obs_source_t * dst,obs_source_t * src,bool private)453 static void duplicate_filters(obs_source_t *dst, obs_source_t *src,
454 bool private)
455 {
456 DARRAY(obs_source_t *) filters;
457
458 da_init(filters);
459
460 pthread_mutex_lock(&src->filter_mutex);
461 for (size_t i = 0; i < src->filters.num; i++)
462 obs_source_addref(src->filters.array[i]);
463 da_copy(filters, src->filters);
464 pthread_mutex_unlock(&src->filter_mutex);
465
466 for (size_t i = filters.num; i > 0; i--) {
467 obs_source_t *src_filter = filters.array[i - 1];
468 char *new_name =
469 get_new_filter_name(dst, src_filter->context.name);
470 bool enabled = obs_source_enabled(src_filter);
471
472 obs_source_t *dst_filter =
473 obs_source_duplicate(src_filter, new_name, private);
474 obs_source_set_enabled(dst_filter, enabled);
475
476 bfree(new_name);
477 obs_source_filter_add(dst, dst_filter);
478 obs_source_release(dst_filter);
479 obs_source_release(src_filter);
480 }
481
482 da_free(filters);
483 }
484
obs_source_copy_filters(obs_source_t * dst,obs_source_t * src)485 void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src)
486 {
487 if (!obs_source_valid(dst, "obs_source_copy_filters"))
488 return;
489 if (!obs_source_valid(src, "obs_source_copy_filters"))
490 return;
491
492 duplicate_filters(dst, src, dst->context.private);
493 }
494
duplicate_filter(obs_source_t * dst,obs_source_t * filter)495 static void duplicate_filter(obs_source_t *dst, obs_source_t *filter)
496 {
497 if (!filter_compatible(dst, filter))
498 return;
499
500 char *new_name = get_new_filter_name(dst, filter->context.name);
501 bool enabled = obs_source_enabled(filter);
502
503 obs_source_t *dst_filter = obs_source_duplicate(filter, new_name, true);
504 obs_source_set_enabled(dst_filter, enabled);
505
506 bfree(new_name);
507 obs_source_filter_add(dst, dst_filter);
508 obs_source_release(dst_filter);
509 }
510
obs_source_copy_single_filter(obs_source_t * dst,obs_source_t * filter)511 void obs_source_copy_single_filter(obs_source_t *dst, obs_source_t *filter)
512 {
513 if (!obs_source_valid(dst, "obs_source_copy_single_filter"))
514 return;
515 if (!obs_source_valid(filter, "obs_source_copy_single_filter"))
516 return;
517
518 duplicate_filter(dst, filter);
519 }
520
obs_source_duplicate(obs_source_t * source,const char * new_name,bool create_private)521 obs_source_t *obs_source_duplicate(obs_source_t *source, const char *new_name,
522 bool create_private)
523 {
524 obs_source_t *new_source;
525 obs_data_t *settings;
526
527 if (!obs_source_valid(source, "obs_source_duplicate"))
528 return NULL;
529
530 if (source->info.type == OBS_SOURCE_TYPE_SCENE) {
531 obs_scene_t *scene = obs_scene_from_source(source);
532 if (scene && !create_private) {
533 obs_source_addref(source);
534 return source;
535 }
536 if (!scene)
537 scene = obs_group_from_source(source);
538 if (!scene)
539 return NULL;
540
541 obs_scene_t *new_scene = obs_scene_duplicate(
542 scene, new_name,
543 create_private ? OBS_SCENE_DUP_PRIVATE_COPY
544 : OBS_SCENE_DUP_COPY);
545 obs_source_t *new_source = obs_scene_get_source(new_scene);
546 return new_source;
547 }
548
549 if ((source->info.output_flags & OBS_SOURCE_DO_NOT_DUPLICATE) != 0) {
550 obs_source_addref(source);
551 return source;
552 }
553
554 settings = obs_data_create();
555 obs_data_apply(settings, source->context.settings);
556
557 new_source = create_private
558 ? obs_source_create_private(source->info.id,
559 new_name, settings)
560 : obs_source_create(source->info.id, new_name,
561 settings, NULL);
562
563 new_source->audio_mixers = source->audio_mixers;
564 new_source->sync_offset = source->sync_offset;
565 new_source->user_volume = source->user_volume;
566 new_source->user_muted = source->user_muted;
567 new_source->volume = source->volume;
568 new_source->muted = source->muted;
569 new_source->flags = source->flags;
570
571 obs_data_apply(new_source->private_settings, source->private_settings);
572
573 if (source->info.type != OBS_SOURCE_TYPE_FILTER)
574 duplicate_filters(new_source, source, create_private);
575
576 obs_data_release(settings);
577 return new_source;
578 }
579
obs_source_frame_init(struct obs_source_frame * frame,enum video_format format,uint32_t width,uint32_t height)580 void obs_source_frame_init(struct obs_source_frame *frame,
581 enum video_format format, uint32_t width,
582 uint32_t height)
583 {
584 struct video_frame vid_frame;
585
586 if (!obs_ptr_valid(frame, "obs_source_frame_init"))
587 return;
588
589 video_frame_init(&vid_frame, format, width, height);
590 frame->format = format;
591 frame->width = width;
592 frame->height = height;
593
594 for (size_t i = 0; i < MAX_AV_PLANES; i++) {
595 frame->data[i] = vid_frame.data[i];
596 frame->linesize[i] = vid_frame.linesize[i];
597 }
598 }
599
obs_source_frame_decref(struct obs_source_frame * frame)600 static inline void obs_source_frame_decref(struct obs_source_frame *frame)
601 {
602 if (os_atomic_dec_long(&frame->refs) == 0)
603 obs_source_frame_destroy(frame);
604 }
605
606 static bool obs_source_filter_remove_refless(obs_source_t *source,
607 obs_source_t *filter);
608
obs_source_destroy(struct obs_source * source)609 void obs_source_destroy(struct obs_source *source)
610 {
611 size_t i;
612
613 if (!obs_source_valid(source, "obs_source_destroy"))
614 return;
615
616 if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
617 obs_transition_clear(source);
618
619 pthread_mutex_lock(&obs->data.audio_sources_mutex);
620 if (source->prev_next_audio_source) {
621 *source->prev_next_audio_source = source->next_audio_source;
622 if (source->next_audio_source)
623 source->next_audio_source->prev_next_audio_source =
624 source->prev_next_audio_source;
625 }
626 pthread_mutex_unlock(&obs->data.audio_sources_mutex);
627
628 if (source->filter_parent)
629 obs_source_filter_remove_refless(source->filter_parent, source);
630
631 while (source->filters.num)
632 obs_source_filter_remove(source, source->filters.array[0]);
633
634 obs_context_data_remove(&source->context);
635
636 blog(LOG_DEBUG, "%ssource '%s' destroyed",
637 source->context.private ? "private " : "", source->context.name);
638
639 obs_source_dosignal(source, "source_destroy", "destroy");
640
641 if (source->context.data) {
642 source->info.destroy(source->context.data);
643 source->context.data = NULL;
644 }
645
646 audio_monitor_destroy(source->monitor);
647
648 obs_hotkey_unregister(source->push_to_talk_key);
649 obs_hotkey_unregister(source->push_to_mute_key);
650 obs_hotkey_pair_unregister(source->mute_unmute_key);
651
652 for (i = 0; i < source->async_cache.num; i++)
653 obs_source_frame_decref(source->async_cache.array[i].frame);
654
655 gs_enter_context(obs->video.graphics);
656 if (source->async_texrender)
657 gs_texrender_destroy(source->async_texrender);
658 if (source->async_prev_texrender)
659 gs_texrender_destroy(source->async_prev_texrender);
660 for (size_t c = 0; c < MAX_AV_PLANES; c++) {
661 gs_texture_destroy(source->async_textures[c]);
662 gs_texture_destroy(source->async_prev_textures[c]);
663 }
664 if (source->filter_texrender)
665 gs_texrender_destroy(source->filter_texrender);
666 gs_leave_context();
667
668 for (i = 0; i < MAX_AV_PLANES; i++)
669 bfree(source->audio_data.data[i]);
670 for (i = 0; i < MAX_AUDIO_CHANNELS; i++)
671 circlebuf_free(&source->audio_input_buf[i]);
672 audio_resampler_destroy(source->resampler);
673 bfree(source->audio_output_buf[0][0]);
674 bfree(source->audio_mix_buf[0]);
675
676 obs_source_frame_destroy(source->async_preload_frame);
677
678 if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
679 obs_transition_free(source);
680
681 da_free(source->audio_actions);
682 da_free(source->audio_cb_list);
683 da_free(source->caption_cb_list);
684 da_free(source->async_cache);
685 da_free(source->async_frames);
686 da_free(source->filters);
687 pthread_mutex_destroy(&source->filter_mutex);
688 pthread_mutex_destroy(&source->audio_actions_mutex);
689 pthread_mutex_destroy(&source->audio_buf_mutex);
690 pthread_mutex_destroy(&source->audio_cb_mutex);
691 pthread_mutex_destroy(&source->audio_mutex);
692 pthread_mutex_destroy(&source->caption_cb_mutex);
693 pthread_mutex_destroy(&source->async_mutex);
694 obs_data_release(source->private_settings);
695 obs_context_data_free(&source->context);
696
697 if (source->owns_info_id) {
698 bfree((void *)source->info.id);
699 bfree((void *)source->info.unversioned_id);
700 }
701
702 bfree(source);
703 }
704
obs_source_addref(obs_source_t * source)705 void obs_source_addref(obs_source_t *source)
706 {
707 if (!source)
708 return;
709
710 obs_ref_addref(&source->control->ref);
711 }
712
obs_source_release(obs_source_t * source)713 void obs_source_release(obs_source_t *source)
714 {
715 if (!obs) {
716 blog(LOG_WARNING, "Tried to release a source when the OBS "
717 "core is shut down!");
718 return;
719 }
720
721 if (!source)
722 return;
723
724 obs_weak_source_t *control = source->control;
725 if (obs_ref_release(&control->ref)) {
726 obs_source_destroy(source);
727 obs_weak_source_release(control);
728 }
729 }
730
obs_weak_source_addref(obs_weak_source_t * weak)731 void obs_weak_source_addref(obs_weak_source_t *weak)
732 {
733 if (!weak)
734 return;
735
736 obs_weak_ref_addref(&weak->ref);
737 }
738
obs_weak_source_release(obs_weak_source_t * weak)739 void obs_weak_source_release(obs_weak_source_t *weak)
740 {
741 if (!weak)
742 return;
743
744 if (obs_weak_ref_release(&weak->ref))
745 bfree(weak);
746 }
747
obs_source_get_ref(obs_source_t * source)748 obs_source_t *obs_source_get_ref(obs_source_t *source)
749 {
750 if (!source)
751 return NULL;
752
753 return obs_weak_source_get_source(source->control);
754 }
755
obs_source_get_weak_source(obs_source_t * source)756 obs_weak_source_t *obs_source_get_weak_source(obs_source_t *source)
757 {
758 if (!source)
759 return NULL;
760
761 obs_weak_source_t *weak = source->control;
762 obs_weak_source_addref(weak);
763 return weak;
764 }
765
obs_weak_source_get_source(obs_weak_source_t * weak)766 obs_source_t *obs_weak_source_get_source(obs_weak_source_t *weak)
767 {
768 if (!weak)
769 return NULL;
770
771 if (obs_weak_ref_get_ref(&weak->ref))
772 return weak->source;
773
774 return NULL;
775 }
776
obs_weak_source_references_source(obs_weak_source_t * weak,obs_source_t * source)777 bool obs_weak_source_references_source(obs_weak_source_t *weak,
778 obs_source_t *source)
779 {
780 return weak && source && weak->source == source;
781 }
782
obs_source_remove(obs_source_t * source)783 void obs_source_remove(obs_source_t *source)
784 {
785 if (!obs_source_valid(source, "obs_source_remove"))
786 return;
787
788 if (!source->removed) {
789 source->removed = true;
790 obs_source_dosignal(source, "source_remove", "remove");
791 }
792 }
793
obs_source_removed(const obs_source_t * source)794 bool obs_source_removed(const obs_source_t *source)
795 {
796 return obs_source_valid(source, "obs_source_removed") ? source->removed
797 : true;
798 }
799
get_defaults(const struct obs_source_info * info)800 static inline obs_data_t *get_defaults(const struct obs_source_info *info)
801 {
802 obs_data_t *settings = obs_data_create();
803 if (info->get_defaults2)
804 info->get_defaults2(info->type_data, settings);
805 else if (info->get_defaults)
806 info->get_defaults(settings);
807 return settings;
808 }
809
obs_source_settings(const char * id)810 obs_data_t *obs_source_settings(const char *id)
811 {
812 const struct obs_source_info *info = get_source_info(id);
813 return (info) ? get_defaults(info) : NULL;
814 }
815
obs_get_source_defaults(const char * id)816 obs_data_t *obs_get_source_defaults(const char *id)
817 {
818 const struct obs_source_info *info = get_source_info(id);
819 return info ? get_defaults(info) : NULL;
820 }
821
obs_get_source_properties(const char * id)822 obs_properties_t *obs_get_source_properties(const char *id)
823 {
824 const struct obs_source_info *info = get_source_info(id);
825 if (info && (info->get_properties || info->get_properties2)) {
826 obs_data_t *defaults = get_defaults(info);
827 obs_properties_t *props;
828
829 if (info->get_properties2)
830 props = info->get_properties2(NULL, info->type_data);
831 else
832 props = info->get_properties(NULL);
833
834 obs_properties_apply_settings(props, defaults);
835 obs_data_release(defaults);
836 return props;
837 }
838 return NULL;
839 }
840
obs_source_get_missing_files(const obs_source_t * source)841 obs_missing_files_t *obs_source_get_missing_files(const obs_source_t *source)
842 {
843 if (!data_valid(source, "obs_source_get_missing_files"))
844 return obs_missing_files_create();
845
846 if (source->info.missing_files) {
847 return source->info.missing_files(source->context.data);
848 }
849
850 return obs_missing_files_create();
851 }
852
obs_source_replace_missing_file(obs_missing_file_cb cb,obs_source_t * source,const char * new_path,void * data)853 void obs_source_replace_missing_file(obs_missing_file_cb cb,
854 obs_source_t *source, const char *new_path,
855 void *data)
856 {
857 if (!data_valid(source, "obs_source_replace_missing_file"))
858 return;
859
860 cb(source->context.data, new_path, data);
861 }
862
obs_is_source_configurable(const char * id)863 bool obs_is_source_configurable(const char *id)
864 {
865 const struct obs_source_info *info = get_source_info(id);
866 return info && (info->get_properties || info->get_properties2);
867 }
868
obs_source_configurable(const obs_source_t * source)869 bool obs_source_configurable(const obs_source_t *source)
870 {
871 return data_valid(source, "obs_source_configurable") &&
872 (source->info.get_properties || source->info.get_properties2);
873 }
874
obs_source_properties(const obs_source_t * source)875 obs_properties_t *obs_source_properties(const obs_source_t *source)
876 {
877 if (!data_valid(source, "obs_source_properties"))
878 return NULL;
879
880 if (source->info.get_properties2) {
881 obs_properties_t *props;
882 props = source->info.get_properties2(source->context.data,
883 source->info.type_data);
884 obs_properties_apply_settings(props, source->context.settings);
885 return props;
886
887 } else if (source->info.get_properties) {
888 obs_properties_t *props;
889 props = source->info.get_properties(source->context.data);
890 obs_properties_apply_settings(props, source->context.settings);
891 return props;
892 }
893
894 return NULL;
895 }
896
obs_source_get_output_flags(const obs_source_t * source)897 uint32_t obs_source_get_output_flags(const obs_source_t *source)
898 {
899 return obs_source_valid(source, "obs_source_get_output_flags")
900 ? source->info.output_flags
901 : 0;
902 }
903
obs_get_source_output_flags(const char * id)904 uint32_t obs_get_source_output_flags(const char *id)
905 {
906 const struct obs_source_info *info = get_source_info(id);
907 return info ? info->output_flags : 0;
908 }
909
obs_source_deferred_update(obs_source_t * source)910 static void obs_source_deferred_update(obs_source_t *source)
911 {
912 if (source->context.data && source->info.update) {
913 long count = os_atomic_load_long(&source->defer_update_count);
914 source->info.update(source->context.data,
915 source->context.settings);
916 os_atomic_compare_swap_long(&source->defer_update_count, count,
917 0);
918 }
919 }
920
obs_source_update(obs_source_t * source,obs_data_t * settings)921 void obs_source_update(obs_source_t *source, obs_data_t *settings)
922 {
923 if (!obs_source_valid(source, "obs_source_update"))
924 return;
925
926 if (settings) {
927 obs_data_apply(source->context.settings, settings);
928 }
929
930 if (source->info.output_flags & OBS_SOURCE_VIDEO) {
931 os_atomic_inc_long(&source->defer_update_count);
932 } else if (source->context.data && source->info.update) {
933 source->info.update(source->context.data,
934 source->context.settings);
935 }
936 }
937
obs_source_reset_settings(obs_source_t * source,obs_data_t * settings)938 void obs_source_reset_settings(obs_source_t *source, obs_data_t *settings)
939 {
940 if (!obs_source_valid(source, "obs_source_reset_settings"))
941 return;
942
943 obs_data_clear(source->context.settings);
944 obs_source_update(source, settings);
945 }
946
obs_source_update_properties(obs_source_t * source)947 void obs_source_update_properties(obs_source_t *source)
948 {
949 if (!obs_source_valid(source, "obs_source_update_properties"))
950 return;
951
952 obs_source_dosignal(source, NULL, "update_properties");
953 }
954
obs_source_send_mouse_click(obs_source_t * source,const struct obs_mouse_event * event,int32_t type,bool mouse_up,uint32_t click_count)955 void obs_source_send_mouse_click(obs_source_t *source,
956 const struct obs_mouse_event *event,
957 int32_t type, bool mouse_up,
958 uint32_t click_count)
959 {
960 if (!obs_source_valid(source, "obs_source_send_mouse_click"))
961 return;
962
963 if (source->info.output_flags & OBS_SOURCE_INTERACTION) {
964 if (source->info.mouse_click) {
965 source->info.mouse_click(source->context.data, event,
966 type, mouse_up, click_count);
967 }
968 }
969 }
970
obs_source_send_mouse_move(obs_source_t * source,const struct obs_mouse_event * event,bool mouse_leave)971 void obs_source_send_mouse_move(obs_source_t *source,
972 const struct obs_mouse_event *event,
973 bool mouse_leave)
974 {
975 if (!obs_source_valid(source, "obs_source_send_mouse_move"))
976 return;
977
978 if (source->info.output_flags & OBS_SOURCE_INTERACTION) {
979 if (source->info.mouse_move) {
980 source->info.mouse_move(source->context.data, event,
981 mouse_leave);
982 }
983 }
984 }
985
obs_source_send_mouse_wheel(obs_source_t * source,const struct obs_mouse_event * event,int x_delta,int y_delta)986 void obs_source_send_mouse_wheel(obs_source_t *source,
987 const struct obs_mouse_event *event,
988 int x_delta, int y_delta)
989 {
990 if (!obs_source_valid(source, "obs_source_send_mouse_wheel"))
991 return;
992
993 if (source->info.output_flags & OBS_SOURCE_INTERACTION) {
994 if (source->info.mouse_wheel) {
995 source->info.mouse_wheel(source->context.data, event,
996 x_delta, y_delta);
997 }
998 }
999 }
1000
obs_source_send_focus(obs_source_t * source,bool focus)1001 void obs_source_send_focus(obs_source_t *source, bool focus)
1002 {
1003 if (!obs_source_valid(source, "obs_source_send_focus"))
1004 return;
1005
1006 if (source->info.output_flags & OBS_SOURCE_INTERACTION) {
1007 if (source->info.focus) {
1008 source->info.focus(source->context.data, focus);
1009 }
1010 }
1011 }
1012
obs_source_send_key_click(obs_source_t * source,const struct obs_key_event * event,bool key_up)1013 void obs_source_send_key_click(obs_source_t *source,
1014 const struct obs_key_event *event, bool key_up)
1015 {
1016 if (!obs_source_valid(source, "obs_source_send_key_click"))
1017 return;
1018
1019 if (source->info.output_flags & OBS_SOURCE_INTERACTION) {
1020 if (source->info.key_click) {
1021 source->info.key_click(source->context.data, event,
1022 key_up);
1023 }
1024 }
1025 }
1026
obs_source_get_texcoords_centered(obs_source_t * source)1027 bool obs_source_get_texcoords_centered(obs_source_t *source)
1028 {
1029 return source->texcoords_centered;
1030 }
1031
obs_source_set_texcoords_centered(obs_source_t * source,bool centered)1032 void obs_source_set_texcoords_centered(obs_source_t *source, bool centered)
1033 {
1034 source->texcoords_centered = centered;
1035 }
1036
activate_source(obs_source_t * source)1037 static void activate_source(obs_source_t *source)
1038 {
1039 if (source->context.data && source->info.activate)
1040 source->info.activate(source->context.data);
1041 obs_source_dosignal(source, "source_activate", "activate");
1042 }
1043
deactivate_source(obs_source_t * source)1044 static void deactivate_source(obs_source_t *source)
1045 {
1046 if (source->context.data && source->info.deactivate)
1047 source->info.deactivate(source->context.data);
1048 obs_source_dosignal(source, "source_deactivate", "deactivate");
1049 }
1050
show_source(obs_source_t * source)1051 static void show_source(obs_source_t *source)
1052 {
1053 if (source->context.data && source->info.show)
1054 source->info.show(source->context.data);
1055 obs_source_dosignal(source, "source_show", "show");
1056 }
1057
hide_source(obs_source_t * source)1058 static void hide_source(obs_source_t *source)
1059 {
1060 if (source->context.data && source->info.hide)
1061 source->info.hide(source->context.data);
1062 obs_source_dosignal(source, "source_hide", "hide");
1063 }
1064
activate_tree(obs_source_t * parent,obs_source_t * child,void * param)1065 static void activate_tree(obs_source_t *parent, obs_source_t *child,
1066 void *param)
1067 {
1068 os_atomic_inc_long(&child->activate_refs);
1069
1070 UNUSED_PARAMETER(parent);
1071 UNUSED_PARAMETER(param);
1072 }
1073
deactivate_tree(obs_source_t * parent,obs_source_t * child,void * param)1074 static void deactivate_tree(obs_source_t *parent, obs_source_t *child,
1075 void *param)
1076 {
1077 os_atomic_dec_long(&child->activate_refs);
1078
1079 UNUSED_PARAMETER(parent);
1080 UNUSED_PARAMETER(param);
1081 }
1082
show_tree(obs_source_t * parent,obs_source_t * child,void * param)1083 static void show_tree(obs_source_t *parent, obs_source_t *child, void *param)
1084 {
1085 os_atomic_inc_long(&child->show_refs);
1086
1087 UNUSED_PARAMETER(parent);
1088 UNUSED_PARAMETER(param);
1089 }
1090
hide_tree(obs_source_t * parent,obs_source_t * child,void * param)1091 static void hide_tree(obs_source_t *parent, obs_source_t *child, void *param)
1092 {
1093 os_atomic_dec_long(&child->show_refs);
1094
1095 UNUSED_PARAMETER(parent);
1096 UNUSED_PARAMETER(param);
1097 }
1098
obs_source_activate(obs_source_t * source,enum view_type type)1099 void obs_source_activate(obs_source_t *source, enum view_type type)
1100 {
1101 if (!obs_source_valid(source, "obs_source_activate"))
1102 return;
1103
1104 os_atomic_inc_long(&source->show_refs);
1105 obs_source_enum_active_tree(source, show_tree, NULL);
1106
1107 if (type == MAIN_VIEW) {
1108 os_atomic_inc_long(&source->activate_refs);
1109 obs_source_enum_active_tree(source, activate_tree, NULL);
1110 }
1111 }
1112
obs_source_deactivate(obs_source_t * source,enum view_type type)1113 void obs_source_deactivate(obs_source_t *source, enum view_type type)
1114 {
1115 if (!obs_source_valid(source, "obs_source_deactivate"))
1116 return;
1117
1118 if (os_atomic_load_long(&source->show_refs) > 0) {
1119 os_atomic_dec_long(&source->show_refs);
1120 obs_source_enum_active_tree(source, hide_tree, NULL);
1121 }
1122
1123 if (type == MAIN_VIEW) {
1124 if (os_atomic_load_long(&source->activate_refs) > 0) {
1125 os_atomic_dec_long(&source->activate_refs);
1126 obs_source_enum_active_tree(source, deactivate_tree,
1127 NULL);
1128 }
1129 }
1130 }
1131
1132 static inline struct obs_source_frame *get_closest_frame(obs_source_t *source,
1133 uint64_t sys_time);
1134 bool set_async_texture_size(struct obs_source *source,
1135 const struct obs_source_frame *frame);
1136
async_tick(obs_source_t * source)1137 static void async_tick(obs_source_t *source)
1138 {
1139 uint64_t sys_time = obs->video.video_time;
1140
1141 pthread_mutex_lock(&source->async_mutex);
1142
1143 if (deinterlacing_enabled(source)) {
1144 deinterlace_process_last_frame(source, sys_time);
1145 } else {
1146 if (source->cur_async_frame) {
1147 remove_async_frame(source, source->cur_async_frame);
1148 source->cur_async_frame = NULL;
1149 }
1150
1151 source->cur_async_frame = get_closest_frame(source, sys_time);
1152 }
1153
1154 source->last_sys_timestamp = sys_time;
1155 pthread_mutex_unlock(&source->async_mutex);
1156
1157 if (source->cur_async_frame)
1158 source->async_update_texture =
1159 set_async_texture_size(source, source->cur_async_frame);
1160 }
1161
obs_source_video_tick(obs_source_t * source,float seconds)1162 void obs_source_video_tick(obs_source_t *source, float seconds)
1163 {
1164 bool now_showing, now_active;
1165
1166 if (!obs_source_valid(source, "obs_source_video_tick"))
1167 return;
1168
1169 if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
1170 obs_transition_tick(source, seconds);
1171
1172 if ((source->info.output_flags & OBS_SOURCE_ASYNC) != 0)
1173 async_tick(source);
1174
1175 if (os_atomic_load_long(&source->defer_update_count) > 0)
1176 obs_source_deferred_update(source);
1177
1178 /* reset the filter render texture information once every frame */
1179 if (source->filter_texrender)
1180 gs_texrender_reset(source->filter_texrender);
1181
1182 /* call show/hide if the reference changed */
1183 now_showing = !!source->show_refs;
1184 if (now_showing != source->showing) {
1185 if (now_showing) {
1186 show_source(source);
1187 } else {
1188 hide_source(source);
1189 }
1190
1191 if (source->filters.num) {
1192 for (size_t i = source->filters.num; i > 0; i--) {
1193 obs_source_t *filter =
1194 source->filters.array[i - 1];
1195 if (now_showing) {
1196 show_source(filter);
1197 } else {
1198 hide_source(filter);
1199 }
1200 }
1201 }
1202
1203 source->showing = now_showing;
1204 }
1205
1206 /* call activate/deactivate if the reference changed */
1207 now_active = !!source->activate_refs;
1208 if (now_active != source->active) {
1209 if (now_active) {
1210 activate_source(source);
1211 } else {
1212 deactivate_source(source);
1213 }
1214
1215 if (source->filters.num) {
1216 for (size_t i = source->filters.num; i > 0; i--) {
1217 obs_source_t *filter =
1218 source->filters.array[i - 1];
1219 if (now_active) {
1220 activate_source(filter);
1221 } else {
1222 deactivate_source(filter);
1223 }
1224 }
1225 }
1226
1227 source->active = now_active;
1228 }
1229
1230 if (source->context.data && source->info.video_tick)
1231 source->info.video_tick(source->context.data, seconds);
1232
1233 source->async_rendered = false;
1234 source->deinterlace_rendered = false;
1235 }
1236
1237 /* unless the value is 3+ hours worth of frames, this won't overflow */
conv_frames_to_time(const size_t sample_rate,const size_t frames)1238 static inline uint64_t conv_frames_to_time(const size_t sample_rate,
1239 const size_t frames)
1240 {
1241 if (!sample_rate)
1242 return 0;
1243
1244 return util_mul_div64(frames, 1000000000ULL, sample_rate);
1245 }
1246
conv_time_to_frames(const size_t sample_rate,const uint64_t duration)1247 static inline size_t conv_time_to_frames(const size_t sample_rate,
1248 const uint64_t duration)
1249 {
1250 return (size_t)util_mul_div64(duration, sample_rate, 1000000000ULL);
1251 }
1252
1253 /* maximum buffer size */
1254 #define MAX_BUF_SIZE (1000 * AUDIO_OUTPUT_FRAMES * sizeof(float))
1255
1256 /* time threshold in nanoseconds to ensure audio timing is as seamless as
1257 * possible */
1258 #define TS_SMOOTHING_THRESHOLD 70000000ULL
1259
reset_audio_timing(obs_source_t * source,uint64_t timestamp,uint64_t os_time)1260 static inline void reset_audio_timing(obs_source_t *source, uint64_t timestamp,
1261 uint64_t os_time)
1262 {
1263 source->timing_set = true;
1264 source->timing_adjust = os_time - timestamp;
1265 }
1266
reset_audio_data(obs_source_t * source,uint64_t os_time)1267 static void reset_audio_data(obs_source_t *source, uint64_t os_time)
1268 {
1269 for (size_t i = 0; i < MAX_AUDIO_CHANNELS; i++) {
1270 if (source->audio_input_buf[i].size)
1271 circlebuf_pop_front(&source->audio_input_buf[i], NULL,
1272 source->audio_input_buf[i].size);
1273 }
1274
1275 source->last_audio_input_buf_size = 0;
1276 source->audio_ts = os_time;
1277 source->next_audio_sys_ts_min = os_time;
1278 }
1279
handle_ts_jump(obs_source_t * source,uint64_t expected,uint64_t ts,uint64_t diff,uint64_t os_time)1280 static void handle_ts_jump(obs_source_t *source, uint64_t expected, uint64_t ts,
1281 uint64_t diff, uint64_t os_time)
1282 {
1283 blog(LOG_DEBUG,
1284 "Timestamp for source '%s' jumped by '%" PRIu64 "', "
1285 "expected value %" PRIu64 ", input value %" PRIu64,
1286 source->context.name, diff, expected, ts);
1287
1288 pthread_mutex_lock(&source->audio_buf_mutex);
1289 reset_audio_timing(source, ts, os_time);
1290 reset_audio_data(source, os_time);
1291 pthread_mutex_unlock(&source->audio_buf_mutex);
1292 }
1293
source_signal_audio_data(obs_source_t * source,const struct audio_data * in,bool muted)1294 static void source_signal_audio_data(obs_source_t *source,
1295 const struct audio_data *in, bool muted)
1296 {
1297 pthread_mutex_lock(&source->audio_cb_mutex);
1298
1299 for (size_t i = source->audio_cb_list.num; i > 0; i--) {
1300 struct audio_cb_info info = source->audio_cb_list.array[i - 1];
1301 info.callback(info.param, source, in, muted);
1302 }
1303
1304 pthread_mutex_unlock(&source->audio_cb_mutex);
1305 }
1306
uint64_diff(uint64_t ts1,uint64_t ts2)1307 static inline uint64_t uint64_diff(uint64_t ts1, uint64_t ts2)
1308 {
1309 return (ts1 < ts2) ? (ts2 - ts1) : (ts1 - ts2);
1310 }
1311
get_buf_placement(audio_t * audio,uint64_t offset)1312 static inline size_t get_buf_placement(audio_t *audio, uint64_t offset)
1313 {
1314 uint32_t sample_rate = audio_output_get_sample_rate(audio);
1315 return (size_t)util_mul_div64(offset, sample_rate, 1000000000ULL);
1316 }
1317
source_output_audio_place(obs_source_t * source,const struct audio_data * in)1318 static void source_output_audio_place(obs_source_t *source,
1319 const struct audio_data *in)
1320 {
1321 audio_t *audio = obs->audio.audio;
1322 size_t buf_placement;
1323 size_t channels = audio_output_get_channels(audio);
1324 size_t size = in->frames * sizeof(float);
1325
1326 if (!source->audio_ts || in->timestamp < source->audio_ts)
1327 reset_audio_data(source, in->timestamp);
1328
1329 buf_placement =
1330 get_buf_placement(audio, in->timestamp - source->audio_ts) *
1331 sizeof(float);
1332
1333 #if DEBUG_AUDIO == 1
1334 blog(LOG_DEBUG,
1335 "frames: %lu, size: %lu, placement: %lu, base_ts: %llu, ts: %llu",
1336 (unsigned long)in->frames,
1337 (unsigned long)source->audio_input_buf[0].size,
1338 (unsigned long)buf_placement, source->audio_ts, in->timestamp);
1339 #endif
1340
1341 /* do not allow the circular buffers to become too big */
1342 if ((buf_placement + size) > MAX_BUF_SIZE)
1343 return;
1344
1345 for (size_t i = 0; i < channels; i++) {
1346 circlebuf_place(&source->audio_input_buf[i], buf_placement,
1347 in->data[i], size);
1348 circlebuf_pop_back(&source->audio_input_buf[i], NULL,
1349 source->audio_input_buf[i].size -
1350 (buf_placement + size));
1351 }
1352
1353 source->last_audio_input_buf_size = 0;
1354 }
1355
source_output_audio_push_back(obs_source_t * source,const struct audio_data * in)1356 static inline void source_output_audio_push_back(obs_source_t *source,
1357 const struct audio_data *in)
1358 {
1359 audio_t *audio = obs->audio.audio;
1360 size_t channels = audio_output_get_channels(audio);
1361 size_t size = in->frames * sizeof(float);
1362
1363 /* do not allow the circular buffers to become too big */
1364 if ((source->audio_input_buf[0].size + size) > MAX_BUF_SIZE)
1365 return;
1366
1367 for (size_t i = 0; i < channels; i++)
1368 circlebuf_push_back(&source->audio_input_buf[i], in->data[i],
1369 size);
1370
1371 /* reset audio input buffer size to ensure that audio doesn't get
1372 * perpetually cut */
1373 source->last_audio_input_buf_size = 0;
1374 }
1375
source_muted(obs_source_t * source,uint64_t os_time)1376 static inline bool source_muted(obs_source_t *source, uint64_t os_time)
1377 {
1378 if (source->push_to_mute_enabled && source->user_push_to_mute_pressed)
1379 source->push_to_mute_stop_time =
1380 os_time + source->push_to_mute_delay * 1000000;
1381
1382 if (source->push_to_talk_enabled && source->user_push_to_talk_pressed)
1383 source->push_to_talk_stop_time =
1384 os_time + source->push_to_talk_delay * 1000000;
1385
1386 bool push_to_mute_active = source->user_push_to_mute_pressed ||
1387 os_time < source->push_to_mute_stop_time;
1388 bool push_to_talk_active = source->user_push_to_talk_pressed ||
1389 os_time < source->push_to_talk_stop_time;
1390
1391 return !source->enabled || source->user_muted ||
1392 (source->push_to_mute_enabled && push_to_mute_active) ||
1393 (source->push_to_talk_enabled && !push_to_talk_active);
1394 }
1395
source_output_audio_data(obs_source_t * source,const struct audio_data * data)1396 static void source_output_audio_data(obs_source_t *source,
1397 const struct audio_data *data)
1398 {
1399 size_t sample_rate = audio_output_get_sample_rate(obs->audio.audio);
1400 struct audio_data in = *data;
1401 uint64_t diff;
1402 uint64_t os_time = os_gettime_ns();
1403 int64_t sync_offset;
1404 bool using_direct_ts = false;
1405 bool push_back = false;
1406
1407 /* detects 'directly' set timestamps as long as they're within
1408 * a certain threshold */
1409 if (uint64_diff(in.timestamp, os_time) < MAX_TS_VAR) {
1410 source->timing_adjust = 0;
1411 source->timing_set = true;
1412 using_direct_ts = true;
1413 }
1414
1415 if (!source->timing_set) {
1416 reset_audio_timing(source, in.timestamp, os_time);
1417
1418 } else if (source->next_audio_ts_min != 0) {
1419 diff = uint64_diff(source->next_audio_ts_min, in.timestamp);
1420
1421 /* smooth audio if within threshold */
1422 if (diff > MAX_TS_VAR && !using_direct_ts)
1423 handle_ts_jump(source, source->next_audio_ts_min,
1424 in.timestamp, diff, os_time);
1425 else if (diff < TS_SMOOTHING_THRESHOLD) {
1426 if (source->async_unbuffered && source->async_decoupled)
1427 source->timing_adjust = os_time - in.timestamp;
1428 in.timestamp = source->next_audio_ts_min;
1429 }
1430 }
1431
1432 source->last_audio_ts = in.timestamp;
1433 source->next_audio_ts_min =
1434 in.timestamp + conv_frames_to_time(sample_rate, in.frames);
1435
1436 in.timestamp += source->timing_adjust;
1437
1438 pthread_mutex_lock(&source->audio_buf_mutex);
1439
1440 if (source->next_audio_sys_ts_min == in.timestamp) {
1441 push_back = true;
1442
1443 } else if (source->next_audio_sys_ts_min) {
1444 diff = uint64_diff(source->next_audio_sys_ts_min, in.timestamp);
1445
1446 if (diff < TS_SMOOTHING_THRESHOLD) {
1447 push_back = true;
1448
1449 /* This typically only happens if used with async video when
1450 * audio/video start transitioning in to a timestamp jump.
1451 * Audio will typically have a timestamp jump, and then video
1452 * will have a timestamp jump. If that case is encountered,
1453 * just clear the audio data in that small window and force a
1454 * resync. This handles all cases rather than just looping. */
1455 } else if (diff > MAX_TS_VAR) {
1456 reset_audio_timing(source, data->timestamp, os_time);
1457 in.timestamp = data->timestamp + source->timing_adjust;
1458 }
1459 }
1460
1461 sync_offset = source->sync_offset;
1462 in.timestamp += sync_offset;
1463 in.timestamp -= source->resample_offset;
1464
1465 source->next_audio_sys_ts_min =
1466 source->next_audio_ts_min + source->timing_adjust;
1467
1468 if (source->last_sync_offset != sync_offset) {
1469 if (source->last_sync_offset)
1470 push_back = false;
1471 source->last_sync_offset = sync_offset;
1472 }
1473
1474 if (source->monitoring_type != OBS_MONITORING_TYPE_MONITOR_ONLY) {
1475 if (push_back && source->audio_ts)
1476 source_output_audio_push_back(source, &in);
1477 else
1478 source_output_audio_place(source, &in);
1479 }
1480
1481 pthread_mutex_unlock(&source->audio_buf_mutex);
1482
1483 source_signal_audio_data(source, data, source_muted(source, os_time));
1484 }
1485
1486 enum convert_type {
1487 CONVERT_NONE,
1488 CONVERT_NV12,
1489 CONVERT_420,
1490 CONVERT_420_A,
1491 CONVERT_422,
1492 CONVERT_422_A,
1493 CONVERT_422_PACK,
1494 CONVERT_444,
1495 CONVERT_444_A,
1496 CONVERT_444_A_PACK,
1497 CONVERT_800,
1498 CONVERT_RGB_LIMITED,
1499 CONVERT_BGR3,
1500 };
1501
get_convert_type(enum video_format format,bool full_range)1502 static inline enum convert_type get_convert_type(enum video_format format,
1503 bool full_range)
1504 {
1505 switch (format) {
1506 case VIDEO_FORMAT_I420:
1507 return CONVERT_420;
1508 case VIDEO_FORMAT_NV12:
1509 return CONVERT_NV12;
1510 case VIDEO_FORMAT_I444:
1511 return CONVERT_444;
1512 case VIDEO_FORMAT_I422:
1513 return CONVERT_422;
1514
1515 case VIDEO_FORMAT_YVYU:
1516 case VIDEO_FORMAT_YUY2:
1517 case VIDEO_FORMAT_UYVY:
1518 return CONVERT_422_PACK;
1519
1520 case VIDEO_FORMAT_Y800:
1521 return CONVERT_800;
1522
1523 case VIDEO_FORMAT_NONE:
1524 case VIDEO_FORMAT_RGBA:
1525 case VIDEO_FORMAT_BGRA:
1526 case VIDEO_FORMAT_BGRX:
1527 return full_range ? CONVERT_NONE : CONVERT_RGB_LIMITED;
1528
1529 case VIDEO_FORMAT_BGR3:
1530 return CONVERT_BGR3;
1531
1532 case VIDEO_FORMAT_I40A:
1533 return CONVERT_420_A;
1534
1535 case VIDEO_FORMAT_I42A:
1536 return CONVERT_422_A;
1537
1538 case VIDEO_FORMAT_YUVA:
1539 return CONVERT_444_A;
1540
1541 case VIDEO_FORMAT_AYUV:
1542 return CONVERT_444_A_PACK;
1543 }
1544
1545 return CONVERT_NONE;
1546 }
1547
set_packed422_sizes(struct obs_source * source,const struct obs_source_frame * frame)1548 static inline bool set_packed422_sizes(struct obs_source *source,
1549 const struct obs_source_frame *frame)
1550 {
1551 const uint32_t width = frame->width;
1552 const uint32_t height = frame->height;
1553 const uint32_t half_width = (width + 1) / 2;
1554 source->async_convert_width[0] = half_width;
1555 source->async_convert_height[0] = height;
1556 source->async_texture_formats[0] = GS_BGRA;
1557 source->async_channel_count = 1;
1558 return true;
1559 }
1560
1561 static inline bool
set_packed444_alpha_sizes(struct obs_source * source,const struct obs_source_frame * frame)1562 set_packed444_alpha_sizes(struct obs_source *source,
1563 const struct obs_source_frame *frame)
1564 {
1565 source->async_convert_width[0] = frame->width;
1566 source->async_convert_height[0] = frame->height;
1567 source->async_texture_formats[0] = GS_BGRA;
1568 source->async_channel_count = 1;
1569 return true;
1570 }
1571
set_planar444_sizes(struct obs_source * source,const struct obs_source_frame * frame)1572 static inline bool set_planar444_sizes(struct obs_source *source,
1573 const struct obs_source_frame *frame)
1574 {
1575 source->async_convert_width[0] = frame->width;
1576 source->async_convert_width[1] = frame->width;
1577 source->async_convert_width[2] = frame->width;
1578 source->async_convert_height[0] = frame->height;
1579 source->async_convert_height[1] = frame->height;
1580 source->async_convert_height[2] = frame->height;
1581 source->async_texture_formats[0] = GS_R8;
1582 source->async_texture_formats[1] = GS_R8;
1583 source->async_texture_formats[2] = GS_R8;
1584 source->async_channel_count = 3;
1585 return true;
1586 }
1587
1588 static inline bool
set_planar444_alpha_sizes(struct obs_source * source,const struct obs_source_frame * frame)1589 set_planar444_alpha_sizes(struct obs_source *source,
1590 const struct obs_source_frame *frame)
1591 {
1592 source->async_convert_width[0] = frame->width;
1593 source->async_convert_width[1] = frame->width;
1594 source->async_convert_width[2] = frame->width;
1595 source->async_convert_width[3] = frame->width;
1596 source->async_convert_height[0] = frame->height;
1597 source->async_convert_height[1] = frame->height;
1598 source->async_convert_height[2] = frame->height;
1599 source->async_convert_height[3] = frame->height;
1600 source->async_texture_formats[0] = GS_R8;
1601 source->async_texture_formats[1] = GS_R8;
1602 source->async_texture_formats[2] = GS_R8;
1603 source->async_texture_formats[3] = GS_R8;
1604 source->async_channel_count = 4;
1605 return true;
1606 }
1607
set_planar420_sizes(struct obs_source * source,const struct obs_source_frame * frame)1608 static inline bool set_planar420_sizes(struct obs_source *source,
1609 const struct obs_source_frame *frame)
1610 {
1611 const uint32_t width = frame->width;
1612 const uint32_t height = frame->height;
1613 const uint32_t half_width = (width + 1) / 2;
1614 const uint32_t half_height = (height + 1) / 2;
1615 source->async_convert_width[0] = width;
1616 source->async_convert_width[1] = half_width;
1617 source->async_convert_width[2] = half_width;
1618 source->async_convert_height[0] = height;
1619 source->async_convert_height[1] = half_height;
1620 source->async_convert_height[2] = half_height;
1621 source->async_texture_formats[0] = GS_R8;
1622 source->async_texture_formats[1] = GS_R8;
1623 source->async_texture_formats[2] = GS_R8;
1624 source->async_channel_count = 3;
1625 return true;
1626 }
1627
1628 static inline bool
set_planar420_alpha_sizes(struct obs_source * source,const struct obs_source_frame * frame)1629 set_planar420_alpha_sizes(struct obs_source *source,
1630 const struct obs_source_frame *frame)
1631 {
1632 const uint32_t width = frame->width;
1633 const uint32_t height = frame->height;
1634 const uint32_t half_width = (width + 1) / 2;
1635 const uint32_t half_height = (height + 1) / 2;
1636 source->async_convert_width[0] = width;
1637 source->async_convert_width[1] = half_width;
1638 source->async_convert_width[2] = half_width;
1639 source->async_convert_width[3] = width;
1640 source->async_convert_height[0] = height;
1641 source->async_convert_height[1] = half_height;
1642 source->async_convert_height[2] = half_height;
1643 source->async_convert_height[3] = height;
1644 source->async_texture_formats[0] = GS_R8;
1645 source->async_texture_formats[1] = GS_R8;
1646 source->async_texture_formats[2] = GS_R8;
1647 source->async_texture_formats[3] = GS_R8;
1648 source->async_channel_count = 4;
1649 return true;
1650 }
1651
set_planar422_sizes(struct obs_source * source,const struct obs_source_frame * frame)1652 static inline bool set_planar422_sizes(struct obs_source *source,
1653 const struct obs_source_frame *frame)
1654 {
1655 const uint32_t width = frame->width;
1656 const uint32_t height = frame->height;
1657 const uint32_t half_width = (width + 1) / 2;
1658 source->async_convert_width[0] = width;
1659 source->async_convert_width[1] = half_width;
1660 source->async_convert_width[2] = half_width;
1661 source->async_convert_height[0] = height;
1662 source->async_convert_height[1] = height;
1663 source->async_convert_height[2] = height;
1664 source->async_texture_formats[0] = GS_R8;
1665 source->async_texture_formats[1] = GS_R8;
1666 source->async_texture_formats[2] = GS_R8;
1667 source->async_channel_count = 3;
1668 return true;
1669 }
1670
1671 static inline bool
set_planar422_alpha_sizes(struct obs_source * source,const struct obs_source_frame * frame)1672 set_planar422_alpha_sizes(struct obs_source *source,
1673 const struct obs_source_frame *frame)
1674 {
1675 const uint32_t width = frame->width;
1676 const uint32_t height = frame->height;
1677 const uint32_t half_width = (width + 1) / 2;
1678 source->async_convert_width[0] = width;
1679 source->async_convert_width[1] = half_width;
1680 source->async_convert_width[2] = half_width;
1681 source->async_convert_width[3] = width;
1682 source->async_convert_height[0] = height;
1683 source->async_convert_height[1] = height;
1684 source->async_convert_height[2] = height;
1685 source->async_convert_height[3] = height;
1686 source->async_texture_formats[0] = GS_R8;
1687 source->async_texture_formats[1] = GS_R8;
1688 source->async_texture_formats[2] = GS_R8;
1689 source->async_texture_formats[3] = GS_R8;
1690 source->async_channel_count = 4;
1691 return true;
1692 }
1693
set_nv12_sizes(struct obs_source * source,const struct obs_source_frame * frame)1694 static inline bool set_nv12_sizes(struct obs_source *source,
1695 const struct obs_source_frame *frame)
1696 {
1697 const uint32_t width = frame->width;
1698 const uint32_t height = frame->height;
1699 const uint32_t half_width = (width + 1) / 2;
1700 const uint32_t half_height = (height + 1) / 2;
1701 source->async_convert_width[0] = width;
1702 source->async_convert_width[1] = half_width;
1703 source->async_convert_height[0] = height;
1704 source->async_convert_height[1] = half_height;
1705 source->async_texture_formats[0] = GS_R8;
1706 source->async_texture_formats[1] = GS_R8G8;
1707 source->async_channel_count = 2;
1708 return true;
1709 }
1710
set_y800_sizes(struct obs_source * source,const struct obs_source_frame * frame)1711 static inline bool set_y800_sizes(struct obs_source *source,
1712 const struct obs_source_frame *frame)
1713 {
1714 source->async_convert_width[0] = frame->width;
1715 source->async_convert_height[0] = frame->height;
1716 source->async_texture_formats[0] = GS_R8;
1717 source->async_channel_count = 1;
1718 return true;
1719 }
1720
set_rgb_limited_sizes(struct obs_source * source,const struct obs_source_frame * frame)1721 static inline bool set_rgb_limited_sizes(struct obs_source *source,
1722 const struct obs_source_frame *frame)
1723 {
1724 source->async_convert_width[0] = frame->width;
1725 source->async_convert_height[0] = frame->height;
1726 source->async_texture_formats[0] = convert_video_format(frame->format);
1727 source->async_channel_count = 1;
1728 return true;
1729 }
1730
set_bgr3_sizes(struct obs_source * source,const struct obs_source_frame * frame)1731 static inline bool set_bgr3_sizes(struct obs_source *source,
1732 const struct obs_source_frame *frame)
1733 {
1734 source->async_convert_width[0] = frame->width * 3;
1735 source->async_convert_height[0] = frame->height;
1736 source->async_texture_formats[0] = GS_R8;
1737 source->async_channel_count = 1;
1738 return true;
1739 }
1740
init_gpu_conversion(struct obs_source * source,const struct obs_source_frame * frame)1741 static inline bool init_gpu_conversion(struct obs_source *source,
1742 const struct obs_source_frame *frame)
1743 {
1744 switch (get_convert_type(frame->format, frame->full_range)) {
1745 case CONVERT_422_PACK:
1746 return set_packed422_sizes(source, frame);
1747
1748 case CONVERT_420:
1749 return set_planar420_sizes(source, frame);
1750
1751 case CONVERT_422:
1752 return set_planar422_sizes(source, frame);
1753
1754 case CONVERT_NV12:
1755 return set_nv12_sizes(source, frame);
1756
1757 case CONVERT_444:
1758 return set_planar444_sizes(source, frame);
1759
1760 case CONVERT_800:
1761 return set_y800_sizes(source, frame);
1762
1763 case CONVERT_RGB_LIMITED:
1764 return set_rgb_limited_sizes(source, frame);
1765
1766 case CONVERT_BGR3:
1767 return set_bgr3_sizes(source, frame);
1768
1769 case CONVERT_420_A:
1770 return set_planar420_alpha_sizes(source, frame);
1771
1772 case CONVERT_422_A:
1773 return set_planar422_alpha_sizes(source, frame);
1774
1775 case CONVERT_444_A:
1776 return set_planar444_alpha_sizes(source, frame);
1777
1778 case CONVERT_444_A_PACK:
1779 return set_packed444_alpha_sizes(source, frame);
1780
1781 case CONVERT_NONE:
1782 assert(false && "No conversion requested");
1783 break;
1784 }
1785 return false;
1786 }
1787
set_async_texture_size(struct obs_source * source,const struct obs_source_frame * frame)1788 bool set_async_texture_size(struct obs_source *source,
1789 const struct obs_source_frame *frame)
1790 {
1791 enum convert_type cur =
1792 get_convert_type(frame->format, frame->full_range);
1793
1794 if (source->async_width == frame->width &&
1795 source->async_height == frame->height &&
1796 source->async_format == frame->format &&
1797 source->async_full_range == frame->full_range)
1798 return true;
1799
1800 source->async_width = frame->width;
1801 source->async_height = frame->height;
1802 source->async_format = frame->format;
1803 source->async_full_range = frame->full_range;
1804
1805 gs_enter_context(obs->video.graphics);
1806
1807 for (size_t c = 0; c < MAX_AV_PLANES; c++) {
1808 gs_texture_destroy(source->async_textures[c]);
1809 source->async_textures[c] = NULL;
1810 gs_texture_destroy(source->async_prev_textures[c]);
1811 source->async_prev_textures[c] = NULL;
1812 }
1813
1814 gs_texrender_destroy(source->async_texrender);
1815 gs_texrender_destroy(source->async_prev_texrender);
1816 source->async_texrender = NULL;
1817 source->async_prev_texrender = NULL;
1818
1819 const enum gs_color_format format = convert_video_format(frame->format);
1820 const bool async_gpu_conversion = (cur != CONVERT_NONE) &&
1821 init_gpu_conversion(source, frame);
1822 source->async_gpu_conversion = async_gpu_conversion;
1823 if (async_gpu_conversion) {
1824 source->async_texrender =
1825 gs_texrender_create(format, GS_ZS_NONE);
1826
1827 for (int c = 0; c < source->async_channel_count; ++c)
1828 source->async_textures[c] = gs_texture_create(
1829 source->async_convert_width[c],
1830 source->async_convert_height[c],
1831 source->async_texture_formats[c], 1, NULL,
1832 GS_DYNAMIC);
1833 } else {
1834 source->async_textures[0] =
1835 gs_texture_create(frame->width, frame->height, format,
1836 1, NULL, GS_DYNAMIC);
1837 }
1838
1839 if (deinterlacing_enabled(source))
1840 set_deinterlace_texture_size(source);
1841
1842 gs_leave_context();
1843
1844 return source->async_textures[0] != NULL;
1845 }
1846
upload_raw_frame(gs_texture_t * tex[MAX_AV_PLANES],const struct obs_source_frame * frame)1847 static void upload_raw_frame(gs_texture_t *tex[MAX_AV_PLANES],
1848 const struct obs_source_frame *frame)
1849 {
1850 switch (get_convert_type(frame->format, frame->full_range)) {
1851 case CONVERT_422_PACK:
1852 case CONVERT_800:
1853 case CONVERT_RGB_LIMITED:
1854 case CONVERT_BGR3:
1855 case CONVERT_420:
1856 case CONVERT_422:
1857 case CONVERT_NV12:
1858 case CONVERT_444:
1859 case CONVERT_420_A:
1860 case CONVERT_422_A:
1861 case CONVERT_444_A:
1862 case CONVERT_444_A_PACK:
1863 for (size_t c = 0; c < MAX_AV_PLANES; c++) {
1864 if (tex[c])
1865 gs_texture_set_image(tex[c], frame->data[c],
1866 frame->linesize[c], false);
1867 }
1868 break;
1869
1870 case CONVERT_NONE:
1871 assert(false && "No conversion requested");
1872 break;
1873 }
1874 }
1875
select_conversion_technique(enum video_format format,bool full_range)1876 static const char *select_conversion_technique(enum video_format format,
1877 bool full_range)
1878 {
1879 switch (format) {
1880 case VIDEO_FORMAT_UYVY:
1881 return "UYVY_Reverse";
1882
1883 case VIDEO_FORMAT_YUY2:
1884 return "YUY2_Reverse";
1885
1886 case VIDEO_FORMAT_YVYU:
1887 return "YVYU_Reverse";
1888
1889 case VIDEO_FORMAT_I420:
1890 return "I420_Reverse";
1891
1892 case VIDEO_FORMAT_NV12:
1893 return "NV12_Reverse";
1894
1895 case VIDEO_FORMAT_I444:
1896 return "I444_Reverse";
1897
1898 case VIDEO_FORMAT_Y800:
1899 return full_range ? "Y800_Full" : "Y800_Limited";
1900
1901 case VIDEO_FORMAT_BGR3:
1902 return full_range ? "BGR3_Full" : "BGR3_Limited";
1903
1904 case VIDEO_FORMAT_I422:
1905 return "I422_Reverse";
1906
1907 case VIDEO_FORMAT_I40A:
1908 return "I40A_Reverse";
1909
1910 case VIDEO_FORMAT_I42A:
1911 return "I42A_Reverse";
1912
1913 case VIDEO_FORMAT_YUVA:
1914 return "YUVA_Reverse";
1915
1916 case VIDEO_FORMAT_AYUV:
1917 return "AYUV_Reverse";
1918
1919 case VIDEO_FORMAT_BGRA:
1920 case VIDEO_FORMAT_BGRX:
1921 case VIDEO_FORMAT_RGBA:
1922 case VIDEO_FORMAT_NONE:
1923 if (full_range)
1924 assert(false && "No conversion requested");
1925 else
1926 return "RGB_Limited";
1927 break;
1928 }
1929 return NULL;
1930 }
1931
set_eparam(gs_effect_t * effect,const char * name,float val)1932 static inline void set_eparam(gs_effect_t *effect, const char *name, float val)
1933 {
1934 gs_eparam_t *param = gs_effect_get_param_by_name(effect, name);
1935 gs_effect_set_float(param, val);
1936 }
1937
set_eparami(gs_effect_t * effect,const char * name,int val)1938 static inline void set_eparami(gs_effect_t *effect, const char *name, int val)
1939 {
1940 gs_eparam_t *param = gs_effect_get_param_by_name(effect, name);
1941 gs_effect_set_int(param, val);
1942 }
1943
update_async_texrender(struct obs_source * source,const struct obs_source_frame * frame,gs_texture_t * tex[MAX_AV_PLANES],gs_texrender_t * texrender)1944 static bool update_async_texrender(struct obs_source *source,
1945 const struct obs_source_frame *frame,
1946 gs_texture_t *tex[MAX_AV_PLANES],
1947 gs_texrender_t *texrender)
1948 {
1949 GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_CONVERT_FORMAT, "Convert Format");
1950
1951 gs_texrender_reset(texrender);
1952
1953 upload_raw_frame(tex, frame);
1954
1955 uint32_t cx = source->async_width;
1956 uint32_t cy = source->async_height;
1957
1958 gs_effect_t *conv = obs->video.conversion_effect;
1959 const char *tech_name =
1960 select_conversion_technique(frame->format, frame->full_range);
1961 gs_technique_t *tech = gs_effect_get_technique(conv, tech_name);
1962
1963 const bool success = gs_texrender_begin(texrender, cx, cy);
1964
1965 if (success) {
1966 gs_enable_blending(false);
1967
1968 gs_technique_begin(tech);
1969 gs_technique_begin_pass(tech, 0);
1970
1971 if (tex[0])
1972 gs_effect_set_texture(
1973 gs_effect_get_param_by_name(conv, "image"),
1974 tex[0]);
1975 if (tex[1])
1976 gs_effect_set_texture(
1977 gs_effect_get_param_by_name(conv, "image1"),
1978 tex[1]);
1979 if (tex[2])
1980 gs_effect_set_texture(
1981 gs_effect_get_param_by_name(conv, "image2"),
1982 tex[2]);
1983 if (tex[3])
1984 gs_effect_set_texture(
1985 gs_effect_get_param_by_name(conv, "image3"),
1986 tex[3]);
1987 set_eparam(conv, "width", (float)cx);
1988 set_eparam(conv, "height", (float)cy);
1989 set_eparam(conv, "width_d2", (float)cx * 0.5f);
1990 set_eparam(conv, "height_d2", (float)cy * 0.5f);
1991 set_eparam(conv, "width_x2_i", 0.5f / (float)cx);
1992
1993 struct vec4 vec0, vec1, vec2;
1994 vec4_set(&vec0, frame->color_matrix[0], frame->color_matrix[1],
1995 frame->color_matrix[2], frame->color_matrix[3]);
1996 vec4_set(&vec1, frame->color_matrix[4], frame->color_matrix[5],
1997 frame->color_matrix[6], frame->color_matrix[7]);
1998 vec4_set(&vec2, frame->color_matrix[8], frame->color_matrix[9],
1999 frame->color_matrix[10], frame->color_matrix[11]);
2000 gs_effect_set_vec4(
2001 gs_effect_get_param_by_name(conv, "color_vec0"), &vec0);
2002 gs_effect_set_vec4(
2003 gs_effect_get_param_by_name(conv, "color_vec1"), &vec1);
2004 gs_effect_set_vec4(
2005 gs_effect_get_param_by_name(conv, "color_vec2"), &vec2);
2006 if (!frame->full_range) {
2007 gs_eparam_t *min_param = gs_effect_get_param_by_name(
2008 conv, "color_range_min");
2009 gs_effect_set_val(min_param, frame->color_range_min,
2010 sizeof(float) * 3);
2011 gs_eparam_t *max_param = gs_effect_get_param_by_name(
2012 conv, "color_range_max");
2013 gs_effect_set_val(max_param, frame->color_range_max,
2014 sizeof(float) * 3);
2015 }
2016
2017 gs_draw(GS_TRIS, 0, 3);
2018
2019 gs_technique_end_pass(tech);
2020 gs_technique_end(tech);
2021
2022 gs_enable_blending(true);
2023
2024 gs_texrender_end(texrender);
2025 }
2026
2027 GS_DEBUG_MARKER_END();
2028 return success;
2029 }
2030
update_async_texture(struct obs_source * source,const struct obs_source_frame * frame,gs_texture_t * tex,gs_texrender_t * texrender)2031 bool update_async_texture(struct obs_source *source,
2032 const struct obs_source_frame *frame,
2033 gs_texture_t *tex, gs_texrender_t *texrender)
2034 {
2035 gs_texture_t *tex3[MAX_AV_PLANES] = {tex, NULL, NULL, NULL,
2036 NULL, NULL, NULL, NULL};
2037 return update_async_textures(source, frame, tex3, texrender);
2038 }
2039
update_async_textures(struct obs_source * source,const struct obs_source_frame * frame,gs_texture_t * tex[MAX_AV_PLANES],gs_texrender_t * texrender)2040 bool update_async_textures(struct obs_source *source,
2041 const struct obs_source_frame *frame,
2042 gs_texture_t *tex[MAX_AV_PLANES],
2043 gs_texrender_t *texrender)
2044 {
2045 enum convert_type type;
2046
2047 source->async_flip = frame->flip;
2048 source->async_linear_alpha =
2049 (frame->flags & OBS_SOURCE_FRAME_LINEAR_ALPHA) != 0;
2050
2051 if (source->async_gpu_conversion && texrender)
2052 return update_async_texrender(source, frame, tex, texrender);
2053
2054 type = get_convert_type(frame->format, frame->full_range);
2055 if (type == CONVERT_NONE) {
2056 gs_texture_set_image(tex[0], frame->data[0], frame->linesize[0],
2057 false);
2058 return true;
2059 }
2060
2061 return false;
2062 }
2063
obs_source_draw_texture(struct obs_source * source,gs_effect_t * effect)2064 static inline void obs_source_draw_texture(struct obs_source *source,
2065 gs_effect_t *effect)
2066 {
2067 gs_texture_t *tex = source->async_textures[0];
2068 gs_eparam_t *param;
2069
2070 if (source->async_texrender)
2071 tex = gs_texrender_get_texture(source->async_texrender);
2072
2073 param = gs_effect_get_param_by_name(effect, "image");
2074
2075 const bool linear_srgb = gs_get_linear_srgb();
2076
2077 const bool previous = gs_framebuffer_srgb_enabled();
2078 gs_enable_framebuffer_srgb(linear_srgb);
2079
2080 if (linear_srgb) {
2081 gs_effect_set_texture_srgb(param, tex);
2082 } else {
2083 gs_effect_set_texture(param, tex);
2084 }
2085
2086 gs_draw_sprite(tex, source->async_flip ? GS_FLIP_V : 0, 0, 0);
2087
2088 gs_enable_framebuffer_srgb(previous);
2089 }
2090
obs_source_draw_async_texture(struct obs_source * source)2091 static void obs_source_draw_async_texture(struct obs_source *source)
2092 {
2093 gs_effect_t *effect = gs_get_effect();
2094 bool def_draw = (!effect);
2095 bool premultiplied = false;
2096 gs_technique_t *tech = NULL;
2097
2098 if (def_draw) {
2099 effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
2100 const bool nonlinear_alpha = gs_get_linear_srgb() &&
2101 !source->async_linear_alpha;
2102 const char *tech_name = nonlinear_alpha ? "DrawNonlinearAlpha"
2103 : "Draw";
2104 premultiplied = nonlinear_alpha;
2105 tech = gs_effect_get_technique(effect, tech_name);
2106 gs_technique_begin(tech);
2107 gs_technique_begin_pass(tech, 0);
2108 }
2109
2110 if (premultiplied) {
2111 gs_blend_state_push();
2112 gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
2113 }
2114
2115 obs_source_draw_texture(source, effect);
2116
2117 if (premultiplied) {
2118 gs_blend_state_pop();
2119 }
2120
2121 if (def_draw) {
2122 gs_technique_end_pass(tech);
2123 gs_technique_end(tech);
2124 }
2125 }
2126
recreate_async_texture(obs_source_t * source,enum gs_color_format format)2127 static void recreate_async_texture(obs_source_t *source,
2128 enum gs_color_format format)
2129 {
2130 uint32_t cx = gs_texture_get_width(source->async_textures[0]);
2131 uint32_t cy = gs_texture_get_height(source->async_textures[0]);
2132 gs_texture_destroy(source->async_textures[0]);
2133 source->async_textures[0] =
2134 gs_texture_create(cx, cy, format, 1, NULL, GS_DYNAMIC);
2135 }
2136
check_to_swap_bgrx_bgra(obs_source_t * source,struct obs_source_frame * frame)2137 static inline void check_to_swap_bgrx_bgra(obs_source_t *source,
2138 struct obs_source_frame *frame)
2139 {
2140 enum gs_color_format format =
2141 gs_texture_get_color_format(source->async_textures[0]);
2142 if (format == GS_BGRX && frame->format == VIDEO_FORMAT_BGRA) {
2143 recreate_async_texture(source, GS_BGRA);
2144 } else if (format == GS_BGRA && frame->format == VIDEO_FORMAT_BGRX) {
2145 recreate_async_texture(source, GS_BGRX);
2146 }
2147 }
2148
obs_source_update_async_video(obs_source_t * source)2149 static void obs_source_update_async_video(obs_source_t *source)
2150 {
2151 if (!source->async_rendered) {
2152 struct obs_source_frame *frame = obs_source_get_frame(source);
2153
2154 if (frame)
2155 frame = filter_async_video(source, frame);
2156
2157 source->async_rendered = true;
2158 if (frame) {
2159 check_to_swap_bgrx_bgra(source, frame);
2160
2161 if (!source->async_decoupled ||
2162 !source->async_unbuffered) {
2163 source->timing_adjust = obs->video.video_time -
2164 frame->timestamp;
2165 source->timing_set = true;
2166 }
2167
2168 if (source->async_update_texture) {
2169 update_async_textures(source, frame,
2170 source->async_textures,
2171 source->async_texrender);
2172 source->async_update_texture = false;
2173 }
2174
2175 obs_source_release_frame(source, frame);
2176 }
2177 }
2178 }
2179
rotate_async_video(obs_source_t * source,long rotation)2180 static void rotate_async_video(obs_source_t *source, long rotation)
2181 {
2182 float x = 0;
2183 float y = 0;
2184
2185 switch (rotation) {
2186 case 90:
2187 y = (float)source->async_width;
2188 break;
2189 case 270:
2190 case -90:
2191 x = (float)source->async_height;
2192 break;
2193 case 180:
2194 x = (float)source->async_width;
2195 y = (float)source->async_height;
2196 }
2197
2198 gs_matrix_translate3f(x, y, 0);
2199 gs_matrix_rotaa4f(0.0f, 0.0f, -1.0f, RAD((float)rotation));
2200 }
2201
obs_source_render_async_video(obs_source_t * source)2202 static inline void obs_source_render_async_video(obs_source_t *source)
2203 {
2204 if (source->async_textures[0] && source->async_active) {
2205 long rotation = source->async_rotation;
2206 if (rotation) {
2207 gs_matrix_push();
2208 rotate_async_video(source, rotation);
2209 }
2210 obs_source_draw_async_texture(source);
2211 if (rotation) {
2212 gs_matrix_pop();
2213 }
2214 }
2215 }
2216
obs_source_render_filters(obs_source_t * source)2217 static inline void obs_source_render_filters(obs_source_t *source)
2218 {
2219 obs_source_t *first_filter;
2220
2221 pthread_mutex_lock(&source->filter_mutex);
2222 first_filter = source->filters.array[0];
2223 obs_source_addref(first_filter);
2224 pthread_mutex_unlock(&source->filter_mutex);
2225
2226 source->rendering_filter = true;
2227 obs_source_video_render(first_filter);
2228 source->rendering_filter = false;
2229
2230 obs_source_release(first_filter);
2231 }
2232
obs_source_default_render(obs_source_t * source)2233 void obs_source_default_render(obs_source_t *source)
2234 {
2235 gs_effect_t *effect = obs->video.default_effect;
2236 gs_technique_t *tech = gs_effect_get_technique(effect, "Draw");
2237 size_t passes, i;
2238
2239 passes = gs_technique_begin(tech);
2240 for (i = 0; i < passes; i++) {
2241 gs_technique_begin_pass(tech, i);
2242 if (source->context.data)
2243 source->info.video_render(source->context.data, effect);
2244 gs_technique_end_pass(tech);
2245 }
2246 gs_technique_end(tech);
2247 }
2248
obs_source_main_render(obs_source_t * source)2249 static inline void obs_source_main_render(obs_source_t *source)
2250 {
2251 uint32_t flags = source->info.output_flags;
2252 bool custom_draw = (flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
2253 bool srgb_aware = (flags & OBS_SOURCE_SRGB) != 0;
2254 bool default_effect = !source->filter_parent &&
2255 source->filters.num == 0 && !custom_draw;
2256 bool previous_srgb = false;
2257
2258 if (!srgb_aware) {
2259 previous_srgb = gs_get_linear_srgb();
2260 gs_set_linear_srgb(false);
2261 }
2262
2263 if (default_effect)
2264 obs_source_default_render(source);
2265 else if (source->context.data)
2266 source->info.video_render(source->context.data,
2267 custom_draw ? NULL : gs_get_effect());
2268
2269 if (!srgb_aware)
2270 gs_set_linear_srgb(previous_srgb);
2271 }
2272
2273 static bool ready_async_frame(obs_source_t *source, uint64_t sys_time);
2274
2275 #if GS_USE_DEBUG_MARKERS
get_type_format(enum obs_source_type type)2276 static const char *get_type_format(enum obs_source_type type)
2277 {
2278 switch (type) {
2279 case OBS_SOURCE_TYPE_INPUT:
2280 return "Input: %s";
2281 case OBS_SOURCE_TYPE_FILTER:
2282 return "Filter: %s";
2283 case OBS_SOURCE_TYPE_TRANSITION:
2284 return "Transition: %s";
2285 case OBS_SOURCE_TYPE_SCENE:
2286 return "Scene: %s";
2287 default:
2288 return "[Unknown]: %s";
2289 }
2290 }
2291 #endif
2292
render_video(obs_source_t * source)2293 static inline void render_video(obs_source_t *source)
2294 {
2295 if (source->info.type != OBS_SOURCE_TYPE_FILTER &&
2296 (source->info.output_flags & OBS_SOURCE_VIDEO) == 0) {
2297 if (source->filter_parent)
2298 obs_source_skip_video_filter(source);
2299 return;
2300 }
2301
2302 if (source->info.type == OBS_SOURCE_TYPE_INPUT &&
2303 (source->info.output_flags & OBS_SOURCE_ASYNC) != 0 &&
2304 !source->rendering_filter) {
2305 if (deinterlacing_enabled(source))
2306 deinterlace_update_async_video(source);
2307 obs_source_update_async_video(source);
2308 }
2309
2310 if (!source->context.data || !source->enabled) {
2311 if (source->filter_parent)
2312 obs_source_skip_video_filter(source);
2313 return;
2314 }
2315
2316 GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_SOURCE,
2317 get_type_format(source->info.type),
2318 obs_source_get_name(source));
2319
2320 if (source->filters.num && !source->rendering_filter)
2321 obs_source_render_filters(source);
2322
2323 else if (source->info.video_render)
2324 obs_source_main_render(source);
2325
2326 else if (source->filter_target)
2327 obs_source_video_render(source->filter_target);
2328
2329 else if (deinterlacing_enabled(source))
2330 deinterlace_render(source);
2331
2332 else
2333 obs_source_render_async_video(source);
2334
2335 GS_DEBUG_MARKER_END();
2336 }
2337
obs_source_video_render(obs_source_t * source)2338 void obs_source_video_render(obs_source_t *source)
2339 {
2340 if (!obs_source_valid(source, "obs_source_video_render"))
2341 return;
2342
2343 obs_source_addref(source);
2344 render_video(source);
2345 obs_source_release(source);
2346 }
2347
get_async_width(const obs_source_t * source)2348 static inline uint32_t get_async_width(const obs_source_t *source)
2349 {
2350 return ((source->async_rotation % 180) == 0) ? source->async_width
2351 : source->async_height;
2352 }
2353
get_async_height(const obs_source_t * source)2354 static inline uint32_t get_async_height(const obs_source_t *source)
2355 {
2356 return ((source->async_rotation % 180) == 0) ? source->async_height
2357 : source->async_width;
2358 }
2359
get_base_width(const obs_source_t * source)2360 static uint32_t get_base_width(const obs_source_t *source)
2361 {
2362 bool is_filter = !!source->filter_parent;
2363 bool func_valid = source->context.data && source->info.get_width;
2364
2365 if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) {
2366 return source->enabled ? source->transition_actual_cx : 0;
2367
2368 } else if (func_valid && (!is_filter || source->enabled)) {
2369 return source->info.get_width(source->context.data);
2370
2371 } else if (is_filter) {
2372 return get_base_width(source->filter_target);
2373 }
2374
2375 return source->async_active ? get_async_width(source) : 0;
2376 }
2377
get_base_height(const obs_source_t * source)2378 static uint32_t get_base_height(const obs_source_t *source)
2379 {
2380 bool is_filter = !!source->filter_parent;
2381 bool func_valid = source->context.data && source->info.get_height;
2382
2383 if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) {
2384 return source->enabled ? source->transition_actual_cy : 0;
2385
2386 } else if (func_valid && (!is_filter || source->enabled)) {
2387 return source->info.get_height(source->context.data);
2388
2389 } else if (is_filter) {
2390 return get_base_height(source->filter_target);
2391 }
2392
2393 return source->async_active ? get_async_height(source) : 0;
2394 }
2395
get_recurse_width(obs_source_t * source)2396 static uint32_t get_recurse_width(obs_source_t *source)
2397 {
2398 uint32_t width;
2399
2400 pthread_mutex_lock(&source->filter_mutex);
2401
2402 width = (source->filters.num) ? get_base_width(source->filters.array[0])
2403 : get_base_width(source);
2404
2405 pthread_mutex_unlock(&source->filter_mutex);
2406
2407 return width;
2408 }
2409
get_recurse_height(obs_source_t * source)2410 static uint32_t get_recurse_height(obs_source_t *source)
2411 {
2412 uint32_t height;
2413
2414 pthread_mutex_lock(&source->filter_mutex);
2415
2416 height = (source->filters.num)
2417 ? get_base_height(source->filters.array[0])
2418 : get_base_height(source);
2419
2420 pthread_mutex_unlock(&source->filter_mutex);
2421
2422 return height;
2423 }
2424
obs_source_get_width(obs_source_t * source)2425 uint32_t obs_source_get_width(obs_source_t *source)
2426 {
2427 if (!data_valid(source, "obs_source_get_width"))
2428 return 0;
2429
2430 return (source->info.type != OBS_SOURCE_TYPE_FILTER)
2431 ? get_recurse_width(source)
2432 : get_base_width(source);
2433 }
2434
obs_source_get_height(obs_source_t * source)2435 uint32_t obs_source_get_height(obs_source_t *source)
2436 {
2437 if (!data_valid(source, "obs_source_get_height"))
2438 return 0;
2439
2440 return (source->info.type != OBS_SOURCE_TYPE_FILTER)
2441 ? get_recurse_height(source)
2442 : get_base_height(source);
2443 }
2444
obs_source_get_base_width(obs_source_t * source)2445 uint32_t obs_source_get_base_width(obs_source_t *source)
2446 {
2447 if (!data_valid(source, "obs_source_get_base_width"))
2448 return 0;
2449
2450 return get_base_width(source);
2451 }
2452
obs_source_get_base_height(obs_source_t * source)2453 uint32_t obs_source_get_base_height(obs_source_t *source)
2454 {
2455 if (!data_valid(source, "obs_source_get_base_height"))
2456 return 0;
2457
2458 return get_base_height(source);
2459 }
2460
obs_filter_get_parent(const obs_source_t * filter)2461 obs_source_t *obs_filter_get_parent(const obs_source_t *filter)
2462 {
2463 return obs_ptr_valid(filter, "obs_filter_get_parent")
2464 ? filter->filter_parent
2465 : NULL;
2466 }
2467
obs_filter_get_target(const obs_source_t * filter)2468 obs_source_t *obs_filter_get_target(const obs_source_t *filter)
2469 {
2470 return obs_ptr_valid(filter, "obs_filter_get_target")
2471 ? filter->filter_target
2472 : NULL;
2473 }
2474
2475 #define OBS_SOURCE_AV (OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO)
2476
filter_compatible(obs_source_t * source,obs_source_t * filter)2477 static bool filter_compatible(obs_source_t *source, obs_source_t *filter)
2478 {
2479 uint32_t s_caps = source->info.output_flags & OBS_SOURCE_AV;
2480 uint32_t f_caps = filter->info.output_flags & OBS_SOURCE_AV;
2481
2482 if ((f_caps & OBS_SOURCE_AUDIO) != 0 &&
2483 (f_caps & OBS_SOURCE_VIDEO) == 0)
2484 f_caps &= ~OBS_SOURCE_ASYNC;
2485
2486 return (s_caps & f_caps) == f_caps;
2487 }
2488
obs_source_filter_add(obs_source_t * source,obs_source_t * filter)2489 void obs_source_filter_add(obs_source_t *source, obs_source_t *filter)
2490 {
2491 struct calldata cd;
2492 uint8_t stack[128];
2493
2494 if (!obs_source_valid(source, "obs_source_filter_add"))
2495 return;
2496 if (!obs_ptr_valid(filter, "obs_source_filter_add"))
2497 return;
2498
2499 pthread_mutex_lock(&source->filter_mutex);
2500
2501 if (da_find(source->filters, &filter, 0) != DARRAY_INVALID) {
2502 blog(LOG_WARNING, "Tried to add a filter that was already "
2503 "present on the source");
2504 pthread_mutex_unlock(&source->filter_mutex);
2505 return;
2506 }
2507
2508 if (!source->owns_info_id && !filter_compatible(source, filter)) {
2509 pthread_mutex_unlock(&source->filter_mutex);
2510 return;
2511 }
2512
2513 obs_source_addref(filter);
2514
2515 filter->filter_parent = source;
2516 filter->filter_target = !source->filters.num ? source
2517 : source->filters.array[0];
2518
2519 da_insert(source->filters, 0, &filter);
2520
2521 pthread_mutex_unlock(&source->filter_mutex);
2522
2523 calldata_init_fixed(&cd, stack, sizeof(stack));
2524 calldata_set_ptr(&cd, "source", source);
2525 calldata_set_ptr(&cd, "filter", filter);
2526
2527 signal_handler_signal(source->context.signals, "filter_add", &cd);
2528
2529 blog(LOG_DEBUG, "- filter '%s' (%s) added to source '%s'",
2530 filter->context.name, filter->info.id, source->context.name);
2531 }
2532
obs_source_filter_remove_refless(obs_source_t * source,obs_source_t * filter)2533 static bool obs_source_filter_remove_refless(obs_source_t *source,
2534 obs_source_t *filter)
2535 {
2536 struct calldata cd;
2537 uint8_t stack[128];
2538 size_t idx;
2539
2540 pthread_mutex_lock(&source->filter_mutex);
2541
2542 idx = da_find(source->filters, &filter, 0);
2543 if (idx == DARRAY_INVALID) {
2544 pthread_mutex_unlock(&source->filter_mutex);
2545 return false;
2546 }
2547
2548 if (idx > 0) {
2549 obs_source_t *prev = source->filters.array[idx - 1];
2550 prev->filter_target = filter->filter_target;
2551 }
2552
2553 da_erase(source->filters, idx);
2554
2555 pthread_mutex_unlock(&source->filter_mutex);
2556
2557 calldata_init_fixed(&cd, stack, sizeof(stack));
2558 calldata_set_ptr(&cd, "source", source);
2559 calldata_set_ptr(&cd, "filter", filter);
2560
2561 signal_handler_signal(source->context.signals, "filter_remove", &cd);
2562
2563 blog(LOG_DEBUG, "- filter '%s' (%s) removed from source '%s'",
2564 filter->context.name, filter->info.id, source->context.name);
2565
2566 if (filter->info.filter_remove)
2567 filter->info.filter_remove(filter->context.data,
2568 filter->filter_parent);
2569
2570 filter->filter_parent = NULL;
2571 filter->filter_target = NULL;
2572 return true;
2573 }
2574
obs_source_filter_remove(obs_source_t * source,obs_source_t * filter)2575 void obs_source_filter_remove(obs_source_t *source, obs_source_t *filter)
2576 {
2577 if (!obs_source_valid(source, "obs_source_filter_remove"))
2578 return;
2579 if (!obs_ptr_valid(filter, "obs_source_filter_remove"))
2580 return;
2581
2582 if (obs_source_filter_remove_refless(source, filter))
2583 obs_source_release(filter);
2584 }
2585
find_next_filter(obs_source_t * source,obs_source_t * filter,size_t cur_idx)2586 static size_t find_next_filter(obs_source_t *source, obs_source_t *filter,
2587 size_t cur_idx)
2588 {
2589 bool curAsync = (filter->info.output_flags & OBS_SOURCE_ASYNC) != 0;
2590 bool nextAsync;
2591 obs_source_t *next;
2592
2593 if (cur_idx == source->filters.num - 1)
2594 return DARRAY_INVALID;
2595
2596 next = source->filters.array[cur_idx + 1];
2597 nextAsync = (next->info.output_flags & OBS_SOURCE_ASYNC);
2598
2599 if (nextAsync == curAsync)
2600 return cur_idx + 1;
2601 else
2602 return find_next_filter(source, filter, cur_idx + 1);
2603 }
2604
find_prev_filter(obs_source_t * source,obs_source_t * filter,size_t cur_idx)2605 static size_t find_prev_filter(obs_source_t *source, obs_source_t *filter,
2606 size_t cur_idx)
2607 {
2608 bool curAsync = (filter->info.output_flags & OBS_SOURCE_ASYNC) != 0;
2609 bool prevAsync;
2610 obs_source_t *prev;
2611
2612 if (cur_idx == 0)
2613 return DARRAY_INVALID;
2614
2615 prev = source->filters.array[cur_idx - 1];
2616 prevAsync = (prev->info.output_flags & OBS_SOURCE_ASYNC);
2617
2618 if (prevAsync == curAsync)
2619 return cur_idx - 1;
2620 else
2621 return find_prev_filter(source, filter, cur_idx - 1);
2622 }
2623
2624 /* moves filters above/below matching filter types */
move_filter_dir(obs_source_t * source,obs_source_t * filter,enum obs_order_movement movement)2625 static bool move_filter_dir(obs_source_t *source, obs_source_t *filter,
2626 enum obs_order_movement movement)
2627 {
2628 size_t idx;
2629
2630 idx = da_find(source->filters, &filter, 0);
2631 if (idx == DARRAY_INVALID)
2632 return false;
2633
2634 if (movement == OBS_ORDER_MOVE_UP) {
2635 size_t next_id = find_next_filter(source, filter, idx);
2636 if (next_id == DARRAY_INVALID)
2637 return false;
2638 da_move_item(source->filters, idx, next_id);
2639
2640 } else if (movement == OBS_ORDER_MOVE_DOWN) {
2641 size_t prev_id = find_prev_filter(source, filter, idx);
2642 if (prev_id == DARRAY_INVALID)
2643 return false;
2644 da_move_item(source->filters, idx, prev_id);
2645
2646 } else if (movement == OBS_ORDER_MOVE_TOP) {
2647 if (idx == source->filters.num - 1)
2648 return false;
2649 da_move_item(source->filters, idx, source->filters.num - 1);
2650
2651 } else if (movement == OBS_ORDER_MOVE_BOTTOM) {
2652 if (idx == 0)
2653 return false;
2654 da_move_item(source->filters, idx, 0);
2655 }
2656
2657 /* reorder filter targets, not the nicest way of dealing with things */
2658 for (size_t i = 0; i < source->filters.num; i++) {
2659 obs_source_t *next_filter =
2660 (i == source->filters.num - 1)
2661 ? source
2662 : source->filters.array[i + 1];
2663
2664 source->filters.array[i]->filter_target = next_filter;
2665 }
2666
2667 return true;
2668 }
2669
obs_source_filter_set_order(obs_source_t * source,obs_source_t * filter,enum obs_order_movement movement)2670 void obs_source_filter_set_order(obs_source_t *source, obs_source_t *filter,
2671 enum obs_order_movement movement)
2672 {
2673 bool success;
2674
2675 if (!obs_source_valid(source, "obs_source_filter_set_order"))
2676 return;
2677 if (!obs_ptr_valid(filter, "obs_source_filter_set_order"))
2678 return;
2679
2680 pthread_mutex_lock(&source->filter_mutex);
2681 success = move_filter_dir(source, filter, movement);
2682 pthread_mutex_unlock(&source->filter_mutex);
2683
2684 if (success)
2685 obs_source_dosignal(source, NULL, "reorder_filters");
2686 }
2687
obs_source_get_settings(const obs_source_t * source)2688 obs_data_t *obs_source_get_settings(const obs_source_t *source)
2689 {
2690 if (!obs_source_valid(source, "obs_source_get_settings"))
2691 return NULL;
2692
2693 obs_data_addref(source->context.settings);
2694 return source->context.settings;
2695 }
2696
filter_async_video(obs_source_t * source,struct obs_source_frame * in)2697 struct obs_source_frame *filter_async_video(obs_source_t *source,
2698 struct obs_source_frame *in)
2699 {
2700 size_t i;
2701
2702 pthread_mutex_lock(&source->filter_mutex);
2703
2704 for (i = source->filters.num; i > 0; i--) {
2705 struct obs_source *filter = source->filters.array[i - 1];
2706
2707 if (!filter->enabled)
2708 continue;
2709
2710 if (filter->context.data && filter->info.filter_video) {
2711 in = filter->info.filter_video(filter->context.data,
2712 in);
2713 if (!in)
2714 break;
2715 }
2716 }
2717
2718 pthread_mutex_unlock(&source->filter_mutex);
2719
2720 return in;
2721 }
2722
copy_frame_data_line(struct obs_source_frame * dst,const struct obs_source_frame * src,uint32_t plane,uint32_t y)2723 static inline void copy_frame_data_line(struct obs_source_frame *dst,
2724 const struct obs_source_frame *src,
2725 uint32_t plane, uint32_t y)
2726 {
2727 uint32_t pos_src = y * src->linesize[plane];
2728 uint32_t pos_dst = y * dst->linesize[plane];
2729 uint32_t bytes = dst->linesize[plane] < src->linesize[plane]
2730 ? dst->linesize[plane]
2731 : src->linesize[plane];
2732
2733 memcpy(dst->data[plane] + pos_dst, src->data[plane] + pos_src, bytes);
2734 }
2735
copy_frame_data_plane(struct obs_source_frame * dst,const struct obs_source_frame * src,uint32_t plane,uint32_t lines)2736 static inline void copy_frame_data_plane(struct obs_source_frame *dst,
2737 const struct obs_source_frame *src,
2738 uint32_t plane, uint32_t lines)
2739 {
2740 if (dst->linesize[plane] != src->linesize[plane]) {
2741 for (uint32_t y = 0; y < lines; y++)
2742 copy_frame_data_line(dst, src, plane, y);
2743 } else {
2744 memcpy(dst->data[plane], src->data[plane],
2745 (size_t)dst->linesize[plane] * (size_t)lines);
2746 }
2747 }
2748
copy_frame_data(struct obs_source_frame * dst,const struct obs_source_frame * src)2749 static void copy_frame_data(struct obs_source_frame *dst,
2750 const struct obs_source_frame *src)
2751 {
2752 dst->flip = src->flip;
2753 dst->flags = src->flags;
2754 dst->full_range = src->full_range;
2755 dst->timestamp = src->timestamp;
2756 memcpy(dst->color_matrix, src->color_matrix, sizeof(float) * 16);
2757 if (!dst->full_range) {
2758 size_t const size = sizeof(float) * 3;
2759 memcpy(dst->color_range_min, src->color_range_min, size);
2760 memcpy(dst->color_range_max, src->color_range_max, size);
2761 }
2762
2763 switch (src->format) {
2764 case VIDEO_FORMAT_I420: {
2765 const uint32_t height = dst->height;
2766 const uint32_t half_height = (height + 1) / 2;
2767 copy_frame_data_plane(dst, src, 0, height);
2768 copy_frame_data_plane(dst, src, 1, half_height);
2769 copy_frame_data_plane(dst, src, 2, half_height);
2770 break;
2771 }
2772
2773 case VIDEO_FORMAT_NV12: {
2774 const uint32_t height = dst->height;
2775 const uint32_t half_height = (height + 1) / 2;
2776 copy_frame_data_plane(dst, src, 0, height);
2777 copy_frame_data_plane(dst, src, 1, half_height);
2778 break;
2779 }
2780
2781 case VIDEO_FORMAT_I444:
2782 case VIDEO_FORMAT_I422:
2783 copy_frame_data_plane(dst, src, 0, dst->height);
2784 copy_frame_data_plane(dst, src, 1, dst->height);
2785 copy_frame_data_plane(dst, src, 2, dst->height);
2786 break;
2787
2788 case VIDEO_FORMAT_YVYU:
2789 case VIDEO_FORMAT_YUY2:
2790 case VIDEO_FORMAT_UYVY:
2791 case VIDEO_FORMAT_NONE:
2792 case VIDEO_FORMAT_RGBA:
2793 case VIDEO_FORMAT_BGRA:
2794 case VIDEO_FORMAT_BGRX:
2795 case VIDEO_FORMAT_Y800:
2796 case VIDEO_FORMAT_BGR3:
2797 case VIDEO_FORMAT_AYUV:
2798 copy_frame_data_plane(dst, src, 0, dst->height);
2799 break;
2800
2801 case VIDEO_FORMAT_I40A: {
2802 const uint32_t height = dst->height;
2803 const uint32_t half_height = (height + 1) / 2;
2804 copy_frame_data_plane(dst, src, 0, height);
2805 copy_frame_data_plane(dst, src, 1, half_height);
2806 copy_frame_data_plane(dst, src, 2, half_height);
2807 copy_frame_data_plane(dst, src, 3, height);
2808 break;
2809 }
2810
2811 case VIDEO_FORMAT_I42A:
2812 case VIDEO_FORMAT_YUVA:
2813 copy_frame_data_plane(dst, src, 0, dst->height);
2814 copy_frame_data_plane(dst, src, 1, dst->height);
2815 copy_frame_data_plane(dst, src, 2, dst->height);
2816 copy_frame_data_plane(dst, src, 3, dst->height);
2817 break;
2818 }
2819 }
2820
obs_source_frame_copy(struct obs_source_frame * dst,const struct obs_source_frame * src)2821 void obs_source_frame_copy(struct obs_source_frame *dst,
2822 const struct obs_source_frame *src)
2823 {
2824 copy_frame_data(dst, src);
2825 }
2826
async_texture_changed(struct obs_source * source,const struct obs_source_frame * frame)2827 static inline bool async_texture_changed(struct obs_source *source,
2828 const struct obs_source_frame *frame)
2829 {
2830 enum convert_type prev, cur;
2831 prev = get_convert_type(source->async_cache_format,
2832 source->async_cache_full_range);
2833 cur = get_convert_type(frame->format, frame->full_range);
2834
2835 return source->async_cache_width != frame->width ||
2836 source->async_cache_height != frame->height || prev != cur;
2837 }
2838
free_async_cache(struct obs_source * source)2839 static inline void free_async_cache(struct obs_source *source)
2840 {
2841 for (size_t i = 0; i < source->async_cache.num; i++)
2842 obs_source_frame_decref(source->async_cache.array[i].frame);
2843
2844 da_resize(source->async_cache, 0);
2845 da_resize(source->async_frames, 0);
2846 source->cur_async_frame = NULL;
2847 source->prev_async_frame = NULL;
2848 }
2849
2850 #define MAX_ASYNC_FRAMES 30
2851 //if return value is not null then do (os_atomic_dec_long(&output->refs) == 0) && obs_source_frame_destroy(output)
2852 static inline struct obs_source_frame *
cache_video(struct obs_source * source,const struct obs_source_frame * frame)2853 cache_video(struct obs_source *source, const struct obs_source_frame *frame)
2854 {
2855 struct obs_source_frame *new_frame = NULL;
2856
2857 pthread_mutex_lock(&source->async_mutex);
2858
2859 if (async_texture_changed(source, frame)) {
2860 free_async_cache(source);
2861 source->async_cache_width = frame->width;
2862 source->async_cache_height = frame->height;
2863 }
2864
2865 if (source->async_frames.num >= MAX_ASYNC_FRAMES) {
2866 bool available = false;
2867 for (size_t i = 0; i < source->async_cache.num; i++) {
2868 struct async_frame *af = &source->async_cache.array[i];
2869 if (!af->used) {
2870 available = true;
2871 break;
2872 }
2873 }
2874
2875 if (!available) {
2876 source->last_frame_ts = 0;
2877 pthread_mutex_unlock(&source->async_mutex);
2878 return NULL;
2879 }
2880 }
2881
2882 const enum video_format format = frame->format;
2883 source->async_cache_format = format;
2884 source->async_cache_full_range = frame->full_range;
2885
2886 for (size_t i = 0; i < source->async_cache.num; i++) {
2887 struct async_frame *af = &source->async_cache.array[i];
2888 if (!af->used) {
2889 new_frame = af->frame;
2890 new_frame->format = format;
2891 af->used = true;
2892 af->unused_count = 0;
2893 break;
2894 }
2895 }
2896
2897 if (!new_frame) {
2898 struct async_frame new_af;
2899
2900 new_frame = obs_source_frame_create(format, frame->width,
2901 frame->height);
2902 new_af.frame = new_frame;
2903 new_af.used = true;
2904 new_af.unused_count = 0;
2905 new_frame->refs = 1;
2906
2907 da_push_back(source->async_cache, &new_af);
2908 }
2909
2910 os_atomic_inc_long(&new_frame->refs);
2911
2912 pthread_mutex_unlock(&source->async_mutex);
2913
2914 copy_frame_data(new_frame, frame);
2915
2916 return new_frame;
2917 }
2918
2919 static void
obs_source_output_video_internal(obs_source_t * source,const struct obs_source_frame * frame)2920 obs_source_output_video_internal(obs_source_t *source,
2921 const struct obs_source_frame *frame)
2922 {
2923 if (!obs_source_valid(source, "obs_source_output_video"))
2924 return;
2925
2926 if (!frame) {
2927 source->async_active = false;
2928 return;
2929 }
2930
2931 struct obs_source_frame *output = cache_video(source, frame);
2932
2933 /* ------------------------------------------- */
2934 pthread_mutex_lock(&source->async_mutex);
2935 if (output) {
2936 if (os_atomic_dec_long(&output->refs) == 0) {
2937 obs_source_frame_destroy(output);
2938 output = NULL;
2939 } else {
2940 da_push_back(source->async_frames, &output);
2941 source->async_active = true;
2942 }
2943 }
2944 pthread_mutex_unlock(&source->async_mutex);
2945 }
2946
obs_source_output_video(obs_source_t * source,const struct obs_source_frame * frame)2947 void obs_source_output_video(obs_source_t *source,
2948 const struct obs_source_frame *frame)
2949 {
2950 if (!frame) {
2951 obs_source_output_video_internal(source, NULL);
2952 return;
2953 }
2954
2955 struct obs_source_frame new_frame = *frame;
2956 new_frame.full_range =
2957 format_is_yuv(frame->format) ? new_frame.full_range : true;
2958
2959 obs_source_output_video_internal(source, &new_frame);
2960 }
2961
obs_source_output_video2(obs_source_t * source,const struct obs_source_frame2 * frame)2962 void obs_source_output_video2(obs_source_t *source,
2963 const struct obs_source_frame2 *frame)
2964 {
2965 if (!frame) {
2966 obs_source_output_video_internal(source, NULL);
2967 return;
2968 }
2969
2970 struct obs_source_frame new_frame;
2971 enum video_range_type range =
2972 resolve_video_range(frame->format, frame->range);
2973
2974 for (size_t i = 0; i < MAX_AV_PLANES; i++) {
2975 new_frame.data[i] = frame->data[i];
2976 new_frame.linesize[i] = frame->linesize[i];
2977 }
2978
2979 new_frame.width = frame->width;
2980 new_frame.height = frame->height;
2981 new_frame.timestamp = frame->timestamp;
2982 new_frame.format = frame->format;
2983 new_frame.full_range = range == VIDEO_RANGE_FULL;
2984 new_frame.flip = frame->flip;
2985 new_frame.flags = frame->flags;
2986
2987 memcpy(&new_frame.color_matrix, &frame->color_matrix,
2988 sizeof(frame->color_matrix));
2989 memcpy(&new_frame.color_range_min, &frame->color_range_min,
2990 sizeof(frame->color_range_min));
2991 memcpy(&new_frame.color_range_max, &frame->color_range_max,
2992 sizeof(frame->color_range_max));
2993
2994 obs_source_output_video_internal(source, &new_frame);
2995 }
2996
obs_source_set_async_rotation(obs_source_t * source,long rotation)2997 void obs_source_set_async_rotation(obs_source_t *source, long rotation)
2998 {
2999 if (source)
3000 source->async_rotation = rotation;
3001 }
3002
obs_source_output_cea708(obs_source_t * source,const struct obs_source_cea_708 * captions)3003 void obs_source_output_cea708(obs_source_t *source,
3004 const struct obs_source_cea_708 *captions)
3005 {
3006 if (!captions) {
3007 return;
3008 }
3009
3010 pthread_mutex_lock(&source->caption_cb_mutex);
3011
3012 for (size_t i = source->caption_cb_list.num; i > 0; i--) {
3013 struct caption_cb_info info =
3014 source->caption_cb_list.array[i - 1];
3015 info.callback(info.param, source, captions);
3016 }
3017
3018 pthread_mutex_unlock(&source->caption_cb_mutex);
3019 }
3020
obs_source_add_caption_callback(obs_source_t * source,obs_source_caption_t callback,void * param)3021 void obs_source_add_caption_callback(obs_source_t *source,
3022 obs_source_caption_t callback, void *param)
3023 {
3024 struct caption_cb_info info = {callback, param};
3025
3026 if (!obs_source_valid(source, "obs_source_add_caption_callback"))
3027 return;
3028
3029 pthread_mutex_lock(&source->caption_cb_mutex);
3030 da_push_back(source->caption_cb_list, &info);
3031 pthread_mutex_unlock(&source->caption_cb_mutex);
3032 }
3033
obs_source_remove_caption_callback(obs_source_t * source,obs_source_caption_t callback,void * param)3034 void obs_source_remove_caption_callback(obs_source_t *source,
3035 obs_source_caption_t callback,
3036 void *param)
3037 {
3038 struct caption_cb_info info = {callback, param};
3039
3040 if (!obs_source_valid(source, "obs_source_remove_caption_callback"))
3041 return;
3042
3043 pthread_mutex_lock(&source->caption_cb_mutex);
3044 da_erase_item(source->caption_cb_list, &info);
3045 pthread_mutex_unlock(&source->caption_cb_mutex);
3046 }
3047
preload_frame_changed(obs_source_t * source,const struct obs_source_frame * in)3048 static inline bool preload_frame_changed(obs_source_t *source,
3049 const struct obs_source_frame *in)
3050 {
3051 if (!source->async_preload_frame)
3052 return true;
3053
3054 return in->width != source->async_preload_frame->width ||
3055 in->height != source->async_preload_frame->height ||
3056 in->format != source->async_preload_frame->format;
3057 }
3058
3059 static void
obs_source_preload_video_internal(obs_source_t * source,const struct obs_source_frame * frame)3060 obs_source_preload_video_internal(obs_source_t *source,
3061 const struct obs_source_frame *frame)
3062 {
3063 if (!obs_source_valid(source, "obs_source_preload_video"))
3064 return;
3065 if (!frame)
3066 return;
3067
3068 if (preload_frame_changed(source, frame)) {
3069 obs_source_frame_destroy(source->async_preload_frame);
3070 source->async_preload_frame = obs_source_frame_create(
3071 frame->format, frame->width, frame->height);
3072 }
3073
3074 copy_frame_data(source->async_preload_frame, frame);
3075
3076 source->last_frame_ts = frame->timestamp;
3077 }
3078
obs_source_preload_video(obs_source_t * source,const struct obs_source_frame * frame)3079 void obs_source_preload_video(obs_source_t *source,
3080 const struct obs_source_frame *frame)
3081 {
3082 if (!frame) {
3083 obs_source_preload_video_internal(source, NULL);
3084 return;
3085 }
3086
3087 struct obs_source_frame new_frame = *frame;
3088 new_frame.full_range =
3089 format_is_yuv(frame->format) ? new_frame.full_range : true;
3090
3091 obs_source_preload_video_internal(source, &new_frame);
3092 }
3093
obs_source_preload_video2(obs_source_t * source,const struct obs_source_frame2 * frame)3094 void obs_source_preload_video2(obs_source_t *source,
3095 const struct obs_source_frame2 *frame)
3096 {
3097 if (!frame) {
3098 obs_source_preload_video_internal(source, NULL);
3099 return;
3100 }
3101
3102 struct obs_source_frame new_frame;
3103 enum video_range_type range =
3104 resolve_video_range(frame->format, frame->range);
3105
3106 for (size_t i = 0; i < MAX_AV_PLANES; i++) {
3107 new_frame.data[i] = frame->data[i];
3108 new_frame.linesize[i] = frame->linesize[i];
3109 }
3110
3111 new_frame.width = frame->width;
3112 new_frame.height = frame->height;
3113 new_frame.timestamp = frame->timestamp;
3114 new_frame.format = frame->format;
3115 new_frame.full_range = range == VIDEO_RANGE_FULL;
3116 new_frame.flip = frame->flip;
3117 new_frame.flags = frame->flags;
3118
3119 memcpy(&new_frame.color_matrix, &frame->color_matrix,
3120 sizeof(frame->color_matrix));
3121 memcpy(&new_frame.color_range_min, &frame->color_range_min,
3122 sizeof(frame->color_range_min));
3123 memcpy(&new_frame.color_range_max, &frame->color_range_max,
3124 sizeof(frame->color_range_max));
3125
3126 obs_source_preload_video_internal(source, &new_frame);
3127 }
3128
obs_source_show_preloaded_video(obs_source_t * source)3129 void obs_source_show_preloaded_video(obs_source_t *source)
3130 {
3131 uint64_t sys_ts;
3132
3133 if (!obs_source_valid(source, "obs_source_show_preloaded_video"))
3134 return;
3135
3136 if (!source->async_preload_frame)
3137 return;
3138
3139 obs_enter_graphics();
3140
3141 set_async_texture_size(source, source->async_preload_frame);
3142 update_async_textures(source, source->async_preload_frame,
3143 source->async_textures, source->async_texrender);
3144 source->async_active = true;
3145
3146 obs_leave_graphics();
3147
3148 pthread_mutex_lock(&source->audio_buf_mutex);
3149 sys_ts = (source->monitoring_type != OBS_MONITORING_TYPE_MONITOR_ONLY)
3150 ? os_gettime_ns()
3151 : 0;
3152 reset_audio_timing(source, source->last_frame_ts, sys_ts);
3153 reset_audio_data(source, sys_ts);
3154 pthread_mutex_unlock(&source->audio_buf_mutex);
3155 }
3156
3157 static void
obs_source_set_video_frame_internal(obs_source_t * source,const struct obs_source_frame * frame)3158 obs_source_set_video_frame_internal(obs_source_t *source,
3159 const struct obs_source_frame *frame)
3160 {
3161 if (!obs_source_valid(source, "obs_source_set_video_frame"))
3162 return;
3163 if (!frame)
3164 return;
3165
3166 obs_enter_graphics();
3167
3168 if (preload_frame_changed(source, frame)) {
3169 obs_source_frame_destroy(source->async_preload_frame);
3170 source->async_preload_frame = obs_source_frame_create(
3171 frame->format, frame->width, frame->height);
3172 }
3173
3174 copy_frame_data(source->async_preload_frame, frame);
3175 set_async_texture_size(source, source->async_preload_frame);
3176 update_async_textures(source, source->async_preload_frame,
3177 source->async_textures, source->async_texrender);
3178
3179 source->last_frame_ts = frame->timestamp;
3180
3181 obs_leave_graphics();
3182 }
3183
obs_source_set_video_frame(obs_source_t * source,const struct obs_source_frame * frame)3184 void obs_source_set_video_frame(obs_source_t *source,
3185 const struct obs_source_frame *frame)
3186 {
3187 if (!frame) {
3188 obs_source_preload_video_internal(source, NULL);
3189 return;
3190 }
3191
3192 struct obs_source_frame new_frame = *frame;
3193 new_frame.full_range =
3194 format_is_yuv(frame->format) ? new_frame.full_range : true;
3195
3196 obs_source_set_video_frame_internal(source, &new_frame);
3197 }
3198
obs_source_set_video_frame2(obs_source_t * source,const struct obs_source_frame2 * frame)3199 void obs_source_set_video_frame2(obs_source_t *source,
3200 const struct obs_source_frame2 *frame)
3201 {
3202 if (!frame) {
3203 obs_source_preload_video_internal(source, NULL);
3204 return;
3205 }
3206
3207 struct obs_source_frame new_frame;
3208 enum video_range_type range =
3209 resolve_video_range(frame->format, frame->range);
3210
3211 for (size_t i = 0; i < MAX_AV_PLANES; i++) {
3212 new_frame.data[i] = frame->data[i];
3213 new_frame.linesize[i] = frame->linesize[i];
3214 }
3215
3216 new_frame.width = frame->width;
3217 new_frame.height = frame->height;
3218 new_frame.timestamp = frame->timestamp;
3219 new_frame.format = frame->format;
3220 new_frame.full_range = range == VIDEO_RANGE_FULL;
3221 new_frame.flip = frame->flip;
3222 new_frame.flags = frame->flags;
3223
3224 memcpy(&new_frame.color_matrix, &frame->color_matrix,
3225 sizeof(frame->color_matrix));
3226 memcpy(&new_frame.color_range_min, &frame->color_range_min,
3227 sizeof(frame->color_range_min));
3228 memcpy(&new_frame.color_range_max, &frame->color_range_max,
3229 sizeof(frame->color_range_max));
3230
3231 obs_source_set_video_frame_internal(source, &new_frame);
3232 }
3233
3234 static inline struct obs_audio_data *
filter_async_audio(obs_source_t * source,struct obs_audio_data * in)3235 filter_async_audio(obs_source_t *source, struct obs_audio_data *in)
3236 {
3237 size_t i;
3238 for (i = source->filters.num; i > 0; i--) {
3239 struct obs_source *filter = source->filters.array[i - 1];
3240
3241 if (!filter->enabled)
3242 continue;
3243
3244 if (filter->context.data && filter->info.filter_audio) {
3245 in = filter->info.filter_audio(filter->context.data,
3246 in);
3247 if (!in)
3248 return NULL;
3249 }
3250 }
3251
3252 return in;
3253 }
3254
reset_resampler(obs_source_t * source,const struct obs_source_audio * audio)3255 static inline void reset_resampler(obs_source_t *source,
3256 const struct obs_source_audio *audio)
3257 {
3258 const struct audio_output_info *obs_info;
3259 struct resample_info output_info;
3260
3261 obs_info = audio_output_get_info(obs->audio.audio);
3262
3263 output_info.format = obs_info->format;
3264 output_info.samples_per_sec = obs_info->samples_per_sec;
3265 output_info.speakers = obs_info->speakers;
3266
3267 source->sample_info.format = audio->format;
3268 source->sample_info.samples_per_sec = audio->samples_per_sec;
3269 source->sample_info.speakers = audio->speakers;
3270
3271 audio_resampler_destroy(source->resampler);
3272 source->resampler = NULL;
3273 source->resample_offset = 0;
3274
3275 if (source->sample_info.samples_per_sec == obs_info->samples_per_sec &&
3276 source->sample_info.format == obs_info->format &&
3277 source->sample_info.speakers == obs_info->speakers) {
3278 source->audio_failed = false;
3279 return;
3280 }
3281
3282 source->resampler =
3283 audio_resampler_create(&output_info, &source->sample_info);
3284
3285 source->audio_failed = source->resampler == NULL;
3286 if (source->resampler == NULL)
3287 blog(LOG_ERROR, "creation of resampler failed");
3288 }
3289
copy_audio_data(obs_source_t * source,const uint8_t * const data[],uint32_t frames,uint64_t ts)3290 static void copy_audio_data(obs_source_t *source, const uint8_t *const data[],
3291 uint32_t frames, uint64_t ts)
3292 {
3293 size_t planes = audio_output_get_planes(obs->audio.audio);
3294 size_t blocksize = audio_output_get_block_size(obs->audio.audio);
3295 size_t size = (size_t)frames * blocksize;
3296 bool resize = source->audio_storage_size < size;
3297
3298 source->audio_data.frames = frames;
3299 source->audio_data.timestamp = ts;
3300
3301 for (size_t i = 0; i < planes; i++) {
3302 /* ensure audio storage capacity */
3303 if (resize) {
3304 bfree(source->audio_data.data[i]);
3305 source->audio_data.data[i] = bmalloc(size);
3306 }
3307
3308 memcpy(source->audio_data.data[i], data[i], size);
3309 }
3310
3311 if (resize)
3312 source->audio_storage_size = size;
3313 }
3314
3315 /* TODO: SSE optimization */
downmix_to_mono_planar(struct obs_source * source,uint32_t frames)3316 static void downmix_to_mono_planar(struct obs_source *source, uint32_t frames)
3317 {
3318 size_t channels = audio_output_get_channels(obs->audio.audio);
3319 const float channels_i = 1.0f / (float)channels;
3320 float **data = (float **)source->audio_data.data;
3321
3322 for (size_t channel = 1; channel < channels; channel++) {
3323 for (uint32_t frame = 0; frame < frames; frame++)
3324 data[0][frame] += data[channel][frame];
3325 }
3326
3327 for (uint32_t frame = 0; frame < frames; frame++)
3328 data[0][frame] *= channels_i;
3329
3330 for (size_t channel = 1; channel < channels; channel++) {
3331 for (uint32_t frame = 0; frame < frames; frame++)
3332 data[channel][frame] = data[0][frame];
3333 }
3334 }
3335
process_audio_balancing(struct obs_source * source,uint32_t frames,float balance,enum obs_balance_type type)3336 static void process_audio_balancing(struct obs_source *source, uint32_t frames,
3337 float balance, enum obs_balance_type type)
3338 {
3339 float **data = (float **)source->audio_data.data;
3340
3341 switch (type) {
3342 case OBS_BALANCE_TYPE_SINE_LAW:
3343 for (uint32_t frame = 0; frame < frames; frame++) {
3344 data[0][frame] = data[0][frame] *
3345 sinf((1.0f - balance) * (M_PI / 2.0f));
3346 data[1][frame] =
3347 data[1][frame] * sinf(balance * (M_PI / 2.0f));
3348 }
3349 break;
3350 case OBS_BALANCE_TYPE_SQUARE_LAW:
3351 for (uint32_t frame = 0; frame < frames; frame++) {
3352 data[0][frame] = data[0][frame] * sqrtf(1.0f - balance);
3353 data[1][frame] = data[1][frame] * sqrtf(balance);
3354 }
3355 break;
3356 case OBS_BALANCE_TYPE_LINEAR:
3357 for (uint32_t frame = 0; frame < frames; frame++) {
3358 data[0][frame] = data[0][frame] * (1.0f - balance);
3359 data[1][frame] = data[1][frame] * balance;
3360 }
3361 break;
3362 default:
3363 break;
3364 }
3365 }
3366
3367 /* resamples/remixes new audio to the designated main audio output format */
process_audio(obs_source_t * source,const struct obs_source_audio * audio)3368 static void process_audio(obs_source_t *source,
3369 const struct obs_source_audio *audio)
3370 {
3371 uint32_t frames = audio->frames;
3372 bool mono_output;
3373
3374 if (source->sample_info.samples_per_sec != audio->samples_per_sec ||
3375 source->sample_info.format != audio->format ||
3376 source->sample_info.speakers != audio->speakers)
3377 reset_resampler(source, audio);
3378
3379 if (source->audio_failed)
3380 return;
3381
3382 if (source->resampler) {
3383 uint8_t *output[MAX_AV_PLANES];
3384
3385 memset(output, 0, sizeof(output));
3386
3387 audio_resampler_resample(source->resampler, output, &frames,
3388 &source->resample_offset, audio->data,
3389 audio->frames);
3390
3391 copy_audio_data(source, (const uint8_t *const *)output, frames,
3392 audio->timestamp);
3393 } else {
3394 copy_audio_data(source, audio->data, audio->frames,
3395 audio->timestamp);
3396 }
3397
3398 mono_output = audio_output_get_channels(obs->audio.audio) == 1;
3399
3400 if (!mono_output && source->sample_info.speakers == SPEAKERS_STEREO &&
3401 (source->balance > 0.51f || source->balance < 0.49f)) {
3402 process_audio_balancing(source, frames, source->balance,
3403 OBS_BALANCE_TYPE_SINE_LAW);
3404 }
3405
3406 if (!mono_output && (source->flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0)
3407 downmix_to_mono_planar(source, frames);
3408 }
3409
obs_source_output_audio(obs_source_t * source,const struct obs_source_audio * audio)3410 void obs_source_output_audio(obs_source_t *source,
3411 const struct obs_source_audio *audio)
3412 {
3413 struct obs_audio_data *output;
3414
3415 if (!obs_source_valid(source, "obs_source_output_audio"))
3416 return;
3417 if (!obs_ptr_valid(audio, "obs_source_output_audio"))
3418 return;
3419
3420 process_audio(source, audio);
3421
3422 pthread_mutex_lock(&source->filter_mutex);
3423 output = filter_async_audio(source, &source->audio_data);
3424
3425 if (output) {
3426 struct audio_data data;
3427
3428 for (int i = 0; i < MAX_AV_PLANES; i++)
3429 data.data[i] = output->data[i];
3430
3431 data.frames = output->frames;
3432 data.timestamp = output->timestamp;
3433
3434 pthread_mutex_lock(&source->audio_mutex);
3435 source_output_audio_data(source, &data);
3436 pthread_mutex_unlock(&source->audio_mutex);
3437 }
3438
3439 pthread_mutex_unlock(&source->filter_mutex);
3440 }
3441
remove_async_frame(obs_source_t * source,struct obs_source_frame * frame)3442 void remove_async_frame(obs_source_t *source, struct obs_source_frame *frame)
3443 {
3444 if (frame)
3445 frame->prev_frame = false;
3446
3447 for (size_t i = 0; i < source->async_cache.num; i++) {
3448 struct async_frame *f = &source->async_cache.array[i];
3449
3450 if (f->frame == frame) {
3451 f->used = false;
3452 break;
3453 }
3454 }
3455 }
3456
3457 /* #define DEBUG_ASYNC_FRAMES 1 */
3458
ready_async_frame(obs_source_t * source,uint64_t sys_time)3459 static bool ready_async_frame(obs_source_t *source, uint64_t sys_time)
3460 {
3461 struct obs_source_frame *next_frame = source->async_frames.array[0];
3462 struct obs_source_frame *frame = NULL;
3463 uint64_t sys_offset = sys_time - source->last_sys_timestamp;
3464 uint64_t frame_time = next_frame->timestamp;
3465 uint64_t frame_offset = 0;
3466
3467 if (source->async_unbuffered) {
3468 while (source->async_frames.num > 1) {
3469 da_erase(source->async_frames, 0);
3470 remove_async_frame(source, next_frame);
3471 next_frame = source->async_frames.array[0];
3472 }
3473
3474 source->last_frame_ts = next_frame->timestamp;
3475 return true;
3476 }
3477
3478 #if DEBUG_ASYNC_FRAMES
3479 blog(LOG_DEBUG,
3480 "source->last_frame_ts: %llu, frame_time: %llu, "
3481 "sys_offset: %llu, frame_offset: %llu, "
3482 "number of frames: %lu",
3483 source->last_frame_ts, frame_time, sys_offset,
3484 frame_time - source->last_frame_ts,
3485 (unsigned long)source->async_frames.num);
3486 #endif
3487
3488 /* account for timestamp invalidation */
3489 if (frame_out_of_bounds(source, frame_time)) {
3490 #if DEBUG_ASYNC_FRAMES
3491 blog(LOG_DEBUG, "timing jump");
3492 #endif
3493 source->last_frame_ts = next_frame->timestamp;
3494 return true;
3495 } else {
3496 frame_offset = frame_time - source->last_frame_ts;
3497 source->last_frame_ts += sys_offset;
3498 }
3499
3500 while (source->last_frame_ts > next_frame->timestamp) {
3501
3502 /* this tries to reduce the needless frame duplication, also
3503 * helps smooth out async rendering to frame boundaries. In
3504 * other words, tries to keep the framerate as smooth as
3505 * possible */
3506 if ((source->last_frame_ts - next_frame->timestamp) < 2000000)
3507 break;
3508
3509 if (frame)
3510 da_erase(source->async_frames, 0);
3511
3512 #if DEBUG_ASYNC_FRAMES
3513 blog(LOG_DEBUG,
3514 "new frame, "
3515 "source->last_frame_ts: %llu, "
3516 "next_frame->timestamp: %llu",
3517 source->last_frame_ts, next_frame->timestamp);
3518 #endif
3519
3520 remove_async_frame(source, frame);
3521
3522 if (source->async_frames.num == 1)
3523 return true;
3524
3525 frame = next_frame;
3526 next_frame = source->async_frames.array[1];
3527
3528 /* more timestamp checking and compensating */
3529 if ((next_frame->timestamp - frame_time) > MAX_TS_VAR) {
3530 #if DEBUG_ASYNC_FRAMES
3531 blog(LOG_DEBUG, "timing jump");
3532 #endif
3533 source->last_frame_ts =
3534 next_frame->timestamp - frame_offset;
3535 }
3536
3537 frame_time = next_frame->timestamp;
3538 frame_offset = frame_time - source->last_frame_ts;
3539 }
3540
3541 #if DEBUG_ASYNC_FRAMES
3542 if (!frame)
3543 blog(LOG_DEBUG, "no frame!");
3544 #endif
3545
3546 return frame != NULL;
3547 }
3548
get_closest_frame(obs_source_t * source,uint64_t sys_time)3549 static inline struct obs_source_frame *get_closest_frame(obs_source_t *source,
3550 uint64_t sys_time)
3551 {
3552 if (!source->async_frames.num)
3553 return NULL;
3554
3555 if (!source->last_frame_ts || ready_async_frame(source, sys_time)) {
3556 struct obs_source_frame *frame = source->async_frames.array[0];
3557 da_erase(source->async_frames, 0);
3558
3559 if (!source->last_frame_ts)
3560 source->last_frame_ts = frame->timestamp;
3561
3562 return frame;
3563 }
3564
3565 return NULL;
3566 }
3567
3568 /*
3569 * Ensures that cached frames are displayed on time. If multiple frames
3570 * were cached between renders, then releases the unnecessary frames and uses
3571 * the frame with the closest timing to ensure sync. Also ensures that timing
3572 * with audio is synchronized.
3573 */
obs_source_get_frame(obs_source_t * source)3574 struct obs_source_frame *obs_source_get_frame(obs_source_t *source)
3575 {
3576 struct obs_source_frame *frame = NULL;
3577
3578 if (!obs_source_valid(source, "obs_source_get_frame"))
3579 return NULL;
3580
3581 pthread_mutex_lock(&source->async_mutex);
3582
3583 frame = source->cur_async_frame;
3584 source->cur_async_frame = NULL;
3585
3586 if (frame) {
3587 os_atomic_inc_long(&frame->refs);
3588 }
3589
3590 pthread_mutex_unlock(&source->async_mutex);
3591
3592 return frame;
3593 }
3594
obs_source_release_frame(obs_source_t * source,struct obs_source_frame * frame)3595 void obs_source_release_frame(obs_source_t *source,
3596 struct obs_source_frame *frame)
3597 {
3598 if (!frame)
3599 return;
3600
3601 if (!source) {
3602 obs_source_frame_destroy(frame);
3603 } else {
3604 pthread_mutex_lock(&source->async_mutex);
3605
3606 if (os_atomic_dec_long(&frame->refs) == 0)
3607 obs_source_frame_destroy(frame);
3608 else
3609 remove_async_frame(source, frame);
3610
3611 pthread_mutex_unlock(&source->async_mutex);
3612 }
3613 }
3614
obs_source_get_name(const obs_source_t * source)3615 const char *obs_source_get_name(const obs_source_t *source)
3616 {
3617 return obs_source_valid(source, "obs_source_get_name")
3618 ? source->context.name
3619 : NULL;
3620 }
3621
obs_source_set_name(obs_source_t * source,const char * name)3622 void obs_source_set_name(obs_source_t *source, const char *name)
3623 {
3624 if (!obs_source_valid(source, "obs_source_set_name"))
3625 return;
3626
3627 if (!name || !*name || !source->context.name ||
3628 strcmp(name, source->context.name) != 0) {
3629 struct calldata data;
3630 char *prev_name = bstrdup(source->context.name);
3631 obs_context_data_setname(&source->context, name);
3632
3633 calldata_init(&data);
3634 calldata_set_ptr(&data, "source", source);
3635 calldata_set_string(&data, "new_name", source->context.name);
3636 calldata_set_string(&data, "prev_name", prev_name);
3637 if (!source->context.private)
3638 signal_handler_signal(obs->signals, "source_rename",
3639 &data);
3640 signal_handler_signal(source->context.signals, "rename", &data);
3641 calldata_free(&data);
3642 bfree(prev_name);
3643 }
3644 }
3645
obs_source_get_type(const obs_source_t * source)3646 enum obs_source_type obs_source_get_type(const obs_source_t *source)
3647 {
3648 return obs_source_valid(source, "obs_source_get_type")
3649 ? source->info.type
3650 : OBS_SOURCE_TYPE_INPUT;
3651 }
3652
obs_source_get_id(const obs_source_t * source)3653 const char *obs_source_get_id(const obs_source_t *source)
3654 {
3655 return obs_source_valid(source, "obs_source_get_id") ? source->info.id
3656 : NULL;
3657 }
3658
obs_source_get_unversioned_id(const obs_source_t * source)3659 const char *obs_source_get_unversioned_id(const obs_source_t *source)
3660 {
3661 return obs_source_valid(source, "obs_source_get_unversioned_id")
3662 ? source->info.unversioned_id
3663 : NULL;
3664 }
3665
render_filter_bypass(obs_source_t * target,gs_effect_t * effect,const char * tech_name)3666 static inline void render_filter_bypass(obs_source_t *target,
3667 gs_effect_t *effect,
3668 const char *tech_name)
3669 {
3670 gs_technique_t *tech = gs_effect_get_technique(effect, tech_name);
3671 size_t passes, i;
3672
3673 passes = gs_technique_begin(tech);
3674 for (i = 0; i < passes; i++) {
3675 gs_technique_begin_pass(tech, i);
3676 obs_source_video_render(target);
3677 gs_technique_end_pass(tech);
3678 }
3679 gs_technique_end(tech);
3680 }
3681
render_filter_tex(gs_texture_t * tex,gs_effect_t * effect,uint32_t width,uint32_t height,const char * tech_name)3682 static inline void render_filter_tex(gs_texture_t *tex, gs_effect_t *effect,
3683 uint32_t width, uint32_t height,
3684 const char *tech_name)
3685 {
3686 gs_technique_t *tech = gs_effect_get_technique(effect, tech_name);
3687 gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
3688 size_t passes, i;
3689
3690 const bool linear_srgb = gs_get_linear_srgb();
3691
3692 const bool previous = gs_framebuffer_srgb_enabled();
3693 gs_enable_framebuffer_srgb(linear_srgb);
3694
3695 if (linear_srgb)
3696 gs_effect_set_texture_srgb(image, tex);
3697 else
3698 gs_effect_set_texture(image, tex);
3699
3700 passes = gs_technique_begin(tech);
3701 for (i = 0; i < passes; i++) {
3702 gs_technique_begin_pass(tech, i);
3703 gs_draw_sprite(tex, 0, width, height);
3704 gs_technique_end_pass(tech);
3705 }
3706 gs_technique_end(tech);
3707
3708 gs_enable_framebuffer_srgb(previous);
3709 }
3710
can_bypass(obs_source_t * target,obs_source_t * parent,uint32_t filter_flags,uint32_t parent_flags,enum obs_allow_direct_render allow_direct)3711 static inline bool can_bypass(obs_source_t *target, obs_source_t *parent,
3712 uint32_t filter_flags, uint32_t parent_flags,
3713 enum obs_allow_direct_render allow_direct)
3714 {
3715 return (target == parent) &&
3716 (allow_direct == OBS_ALLOW_DIRECT_RENDERING) &&
3717 ((parent_flags & OBS_SOURCE_CUSTOM_DRAW) == 0) &&
3718 ((parent_flags & OBS_SOURCE_ASYNC) == 0) &&
3719 ((filter_flags & OBS_SOURCE_SRGB) ==
3720 (parent_flags & OBS_SOURCE_SRGB));
3721 }
3722
obs_source_process_filter_begin(obs_source_t * filter,enum gs_color_format format,enum obs_allow_direct_render allow_direct)3723 bool obs_source_process_filter_begin(obs_source_t *filter,
3724 enum gs_color_format format,
3725 enum obs_allow_direct_render allow_direct)
3726 {
3727 obs_source_t *target, *parent;
3728 uint32_t filter_flags, parent_flags;
3729 int cx, cy;
3730
3731 if (!obs_ptr_valid(filter, "obs_source_process_filter_begin"))
3732 return false;
3733
3734 target = obs_filter_get_target(filter);
3735 parent = obs_filter_get_parent(filter);
3736
3737 if (!target) {
3738 blog(LOG_INFO, "filter '%s' being processed with no target!",
3739 filter->context.name);
3740 return false;
3741 }
3742 if (!parent) {
3743 blog(LOG_INFO, "filter '%s' being processed with no parent!",
3744 filter->context.name);
3745 return false;
3746 }
3747
3748 filter_flags = filter->info.output_flags;
3749 parent_flags = parent->info.output_flags;
3750 cx = get_base_width(target);
3751 cy = get_base_height(target);
3752
3753 filter->allow_direct = allow_direct;
3754
3755 /* if the parent does not use any custom effects, and this is the last
3756 * filter in the chain for the parent, then render the parent directly
3757 * using the filter effect instead of rendering to texture to reduce
3758 * the total number of passes */
3759 if (can_bypass(target, parent, filter_flags, parent_flags,
3760 allow_direct)) {
3761 return true;
3762 }
3763
3764 if (!cx || !cy) {
3765 obs_source_skip_video_filter(filter);
3766 return false;
3767 }
3768
3769 if (!filter->filter_texrender)
3770 filter->filter_texrender =
3771 gs_texrender_create(format, GS_ZS_NONE);
3772
3773 if (gs_texrender_begin(filter->filter_texrender, cx, cy)) {
3774 gs_blend_state_push();
3775 gs_blend_function_separate(GS_BLEND_SRCALPHA,
3776 GS_BLEND_INVSRCALPHA, GS_BLEND_ONE,
3777 GS_BLEND_INVSRCALPHA);
3778
3779 bool custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
3780 bool async = (parent_flags & OBS_SOURCE_ASYNC) != 0;
3781 struct vec4 clear_color;
3782
3783 vec4_zero(&clear_color);
3784 gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);
3785 gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
3786
3787 if (target == parent && !custom_draw && !async)
3788 obs_source_default_render(target);
3789 else
3790 obs_source_video_render(target);
3791
3792 gs_blend_state_pop();
3793
3794 gs_texrender_end(filter->filter_texrender);
3795 }
3796 return true;
3797 }
3798
obs_source_process_filter_tech_end(obs_source_t * filter,gs_effect_t * effect,uint32_t width,uint32_t height,const char * tech_name)3799 void obs_source_process_filter_tech_end(obs_source_t *filter,
3800 gs_effect_t *effect, uint32_t width,
3801 uint32_t height, const char *tech_name)
3802 {
3803 obs_source_t *target, *parent;
3804 gs_texture_t *texture;
3805 uint32_t filter_flags, parent_flags;
3806
3807 if (!filter)
3808 return;
3809
3810 target = obs_filter_get_target(filter);
3811 parent = obs_filter_get_parent(filter);
3812
3813 if (!target || !parent)
3814 return;
3815
3816 filter_flags = filter->info.output_flags;
3817 parent_flags = parent->info.output_flags;
3818
3819 const bool previous =
3820 gs_set_linear_srgb((filter_flags & OBS_SOURCE_SRGB) != 0);
3821
3822 const char *tech = tech_name ? tech_name : "Draw";
3823
3824 if (can_bypass(target, parent, filter_flags, parent_flags,
3825 filter->allow_direct)) {
3826 render_filter_bypass(target, effect, tech);
3827 } else {
3828 texture = gs_texrender_get_texture(filter->filter_texrender);
3829 if (texture) {
3830 render_filter_tex(texture, effect, width, height, tech);
3831 }
3832 }
3833
3834 gs_set_linear_srgb(previous);
3835 }
3836
obs_source_process_filter_end(obs_source_t * filter,gs_effect_t * effect,uint32_t width,uint32_t height)3837 void obs_source_process_filter_end(obs_source_t *filter, gs_effect_t *effect,
3838 uint32_t width, uint32_t height)
3839 {
3840 if (!obs_ptr_valid(filter, "obs_source_process_filter_end"))
3841 return;
3842
3843 obs_source_process_filter_tech_end(filter, effect, width, height,
3844 "Draw");
3845 }
3846
obs_source_skip_video_filter(obs_source_t * filter)3847 void obs_source_skip_video_filter(obs_source_t *filter)
3848 {
3849 obs_source_t *target, *parent;
3850 bool custom_draw, async;
3851 uint32_t parent_flags;
3852
3853 if (!obs_ptr_valid(filter, "obs_source_skip_video_filter"))
3854 return;
3855
3856 target = obs_filter_get_target(filter);
3857 parent = obs_filter_get_parent(filter);
3858 parent_flags = parent->info.output_flags;
3859 custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
3860 async = (parent_flags & OBS_SOURCE_ASYNC) != 0;
3861
3862 if (target == parent) {
3863 if (!custom_draw && !async)
3864 obs_source_default_render(target);
3865 else if (target->info.video_render)
3866 obs_source_main_render(target);
3867 else if (deinterlacing_enabled(target))
3868 deinterlace_render(target);
3869 else
3870 obs_source_render_async_video(target);
3871
3872 } else {
3873 obs_source_video_render(target);
3874 }
3875 }
3876
obs_source_get_signal_handler(const obs_source_t * source)3877 signal_handler_t *obs_source_get_signal_handler(const obs_source_t *source)
3878 {
3879 return obs_source_valid(source, "obs_source_get_signal_handler")
3880 ? source->context.signals
3881 : NULL;
3882 }
3883
obs_source_get_proc_handler(const obs_source_t * source)3884 proc_handler_t *obs_source_get_proc_handler(const obs_source_t *source)
3885 {
3886 return obs_source_valid(source, "obs_source_get_proc_handler")
3887 ? source->context.procs
3888 : NULL;
3889 }
3890
obs_source_set_volume(obs_source_t * source,float volume)3891 void obs_source_set_volume(obs_source_t *source, float volume)
3892 {
3893 if (obs_source_valid(source, "obs_source_set_volume")) {
3894 struct audio_action action = {.timestamp = os_gettime_ns(),
3895 .type = AUDIO_ACTION_VOL,
3896 .vol = volume};
3897
3898 struct calldata data;
3899 uint8_t stack[128];
3900
3901 calldata_init_fixed(&data, stack, sizeof(stack));
3902 calldata_set_ptr(&data, "source", source);
3903 calldata_set_float(&data, "volume", volume);
3904
3905 signal_handler_signal(source->context.signals, "volume", &data);
3906 if (!source->context.private)
3907 signal_handler_signal(obs->signals, "source_volume",
3908 &data);
3909
3910 volume = (float)calldata_float(&data, "volume");
3911
3912 pthread_mutex_lock(&source->audio_actions_mutex);
3913 da_push_back(source->audio_actions, &action);
3914 pthread_mutex_unlock(&source->audio_actions_mutex);
3915
3916 source->user_volume = volume;
3917 }
3918 }
3919
obs_source_get_volume(const obs_source_t * source)3920 float obs_source_get_volume(const obs_source_t *source)
3921 {
3922 return obs_source_valid(source, "obs_source_get_volume")
3923 ? source->user_volume
3924 : 0.0f;
3925 }
3926
obs_source_set_sync_offset(obs_source_t * source,int64_t offset)3927 void obs_source_set_sync_offset(obs_source_t *source, int64_t offset)
3928 {
3929 if (obs_source_valid(source, "obs_source_set_sync_offset")) {
3930 struct calldata data;
3931 uint8_t stack[128];
3932
3933 calldata_init_fixed(&data, stack, sizeof(stack));
3934 calldata_set_ptr(&data, "source", source);
3935 calldata_set_int(&data, "offset", offset);
3936
3937 signal_handler_signal(source->context.signals, "audio_sync",
3938 &data);
3939
3940 source->sync_offset = calldata_int(&data, "offset");
3941 }
3942 }
3943
obs_source_get_sync_offset(const obs_source_t * source)3944 int64_t obs_source_get_sync_offset(const obs_source_t *source)
3945 {
3946 return obs_source_valid(source, "obs_source_get_sync_offset")
3947 ? source->sync_offset
3948 : 0;
3949 }
3950
3951 struct source_enum_data {
3952 obs_source_enum_proc_t enum_callback;
3953 void *param;
3954 };
3955
enum_source_active_tree_callback(obs_source_t * parent,obs_source_t * child,void * param)3956 static void enum_source_active_tree_callback(obs_source_t *parent,
3957 obs_source_t *child, void *param)
3958 {
3959 struct source_enum_data *data = param;
3960 bool is_transition = child->info.type == OBS_SOURCE_TYPE_TRANSITION;
3961
3962 if (is_transition)
3963 obs_transition_enum_sources(
3964 child, enum_source_active_tree_callback, param);
3965 if (child->info.enum_active_sources) {
3966 if (child->context.data) {
3967 child->info.enum_active_sources(
3968 child->context.data,
3969 enum_source_active_tree_callback, data);
3970 }
3971 }
3972
3973 data->enum_callback(parent, child, data->param);
3974 }
3975
obs_source_enum_active_sources(obs_source_t * source,obs_source_enum_proc_t enum_callback,void * param)3976 void obs_source_enum_active_sources(obs_source_t *source,
3977 obs_source_enum_proc_t enum_callback,
3978 void *param)
3979 {
3980 bool is_transition;
3981 if (!data_valid(source, "obs_source_enum_active_sources"))
3982 return;
3983
3984 is_transition = source->info.type == OBS_SOURCE_TYPE_TRANSITION;
3985 if (!is_transition && !source->info.enum_active_sources)
3986 return;
3987
3988 obs_source_addref(source);
3989
3990 if (is_transition)
3991 obs_transition_enum_sources(source, enum_callback, param);
3992 if (source->info.enum_active_sources)
3993 source->info.enum_active_sources(source->context.data,
3994 enum_callback, param);
3995
3996 obs_source_release(source);
3997 }
3998
obs_source_enum_active_tree(obs_source_t * source,obs_source_enum_proc_t enum_callback,void * param)3999 void obs_source_enum_active_tree(obs_source_t *source,
4000 obs_source_enum_proc_t enum_callback,
4001 void *param)
4002 {
4003 struct source_enum_data data = {enum_callback, param};
4004 bool is_transition;
4005
4006 if (!data_valid(source, "obs_source_enum_active_tree"))
4007 return;
4008
4009 is_transition = source->info.type == OBS_SOURCE_TYPE_TRANSITION;
4010 if (!is_transition && !source->info.enum_active_sources)
4011 return;
4012
4013 obs_source_addref(source);
4014
4015 if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
4016 obs_transition_enum_sources(
4017 source, enum_source_active_tree_callback, &data);
4018 if (source->info.enum_active_sources)
4019 source->info.enum_active_sources(
4020 source->context.data, enum_source_active_tree_callback,
4021 &data);
4022
4023 obs_source_release(source);
4024 }
4025
enum_source_full_tree_callback(obs_source_t * parent,obs_source_t * child,void * param)4026 static void enum_source_full_tree_callback(obs_source_t *parent,
4027 obs_source_t *child, void *param)
4028 {
4029 struct source_enum_data *data = param;
4030 bool is_transition = child->info.type == OBS_SOURCE_TYPE_TRANSITION;
4031
4032 if (is_transition)
4033 obs_transition_enum_sources(
4034 child, enum_source_full_tree_callback, param);
4035 if (child->info.enum_all_sources) {
4036 if (child->context.data) {
4037 child->info.enum_all_sources(
4038 child->context.data,
4039 enum_source_full_tree_callback, data);
4040 }
4041 } else if (child->info.enum_active_sources) {
4042 if (child->context.data) {
4043 child->info.enum_active_sources(
4044 child->context.data,
4045 enum_source_full_tree_callback, data);
4046 }
4047 }
4048
4049 data->enum_callback(parent, child, data->param);
4050 }
4051
obs_source_enum_full_tree(obs_source_t * source,obs_source_enum_proc_t enum_callback,void * param)4052 void obs_source_enum_full_tree(obs_source_t *source,
4053 obs_source_enum_proc_t enum_callback,
4054 void *param)
4055 {
4056 struct source_enum_data data = {enum_callback, param};
4057 bool is_transition;
4058
4059 if (!data_valid(source, "obs_source_enum_full_tree"))
4060 return;
4061
4062 is_transition = source->info.type == OBS_SOURCE_TYPE_TRANSITION;
4063 if (!is_transition && !source->info.enum_active_sources)
4064 return;
4065
4066 obs_source_addref(source);
4067
4068 if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
4069 obs_transition_enum_sources(
4070 source, enum_source_full_tree_callback, &data);
4071
4072 if (source->info.enum_all_sources) {
4073 source->info.enum_all_sources(source->context.data,
4074 enum_source_full_tree_callback,
4075 &data);
4076
4077 } else if (source->info.enum_active_sources) {
4078 source->info.enum_active_sources(source->context.data,
4079 enum_source_full_tree_callback,
4080 &data);
4081 }
4082
4083 obs_source_release(source);
4084 }
4085
4086 struct descendant_info {
4087 bool exists;
4088 obs_source_t *target;
4089 };
4090
check_descendant(obs_source_t * parent,obs_source_t * child,void * param)4091 static void check_descendant(obs_source_t *parent, obs_source_t *child,
4092 void *param)
4093 {
4094 struct descendant_info *info = param;
4095 if (child == info->target || parent == info->target)
4096 info->exists = true;
4097 }
4098
obs_source_add_active_child(obs_source_t * parent,obs_source_t * child)4099 bool obs_source_add_active_child(obs_source_t *parent, obs_source_t *child)
4100 {
4101 struct descendant_info info = {false, parent};
4102
4103 if (!obs_ptr_valid(parent, "obs_source_add_active_child"))
4104 return false;
4105 if (!obs_ptr_valid(child, "obs_source_add_active_child"))
4106 return false;
4107 if (parent == child) {
4108 blog(LOG_WARNING, "obs_source_add_active_child: "
4109 "parent == child");
4110 return false;
4111 }
4112
4113 obs_source_enum_full_tree(child, check_descendant, &info);
4114 if (info.exists)
4115 return false;
4116
4117 for (int i = 0; i < parent->show_refs; i++) {
4118 enum view_type type;
4119 type = (i < parent->activate_refs) ? MAIN_VIEW : AUX_VIEW;
4120 obs_source_activate(child, type);
4121 }
4122
4123 return true;
4124 }
4125
obs_source_remove_active_child(obs_source_t * parent,obs_source_t * child)4126 void obs_source_remove_active_child(obs_source_t *parent, obs_source_t *child)
4127 {
4128 if (!obs_ptr_valid(parent, "obs_source_remove_active_child"))
4129 return;
4130 if (!obs_ptr_valid(child, "obs_source_remove_active_child"))
4131 return;
4132
4133 for (int i = 0; i < parent->show_refs; i++) {
4134 enum view_type type;
4135 type = (i < parent->activate_refs) ? MAIN_VIEW : AUX_VIEW;
4136 obs_source_deactivate(child, type);
4137 }
4138 }
4139
obs_source_save(obs_source_t * source)4140 void obs_source_save(obs_source_t *source)
4141 {
4142 if (!data_valid(source, "obs_source_save"))
4143 return;
4144
4145 obs_source_dosignal(source, "source_save", "save");
4146
4147 if (source->info.save)
4148 source->info.save(source->context.data,
4149 source->context.settings);
4150 }
4151
obs_source_load(obs_source_t * source)4152 void obs_source_load(obs_source_t *source)
4153 {
4154 if (!data_valid(source, "obs_source_load"))
4155 return;
4156 if (source->info.load)
4157 source->info.load(source->context.data,
4158 source->context.settings);
4159
4160 obs_source_dosignal(source, "source_load", "load");
4161 }
4162
obs_source_load2(obs_source_t * source)4163 void obs_source_load2(obs_source_t *source)
4164 {
4165 if (!data_valid(source, "obs_source_load2"))
4166 return;
4167
4168 obs_source_load(source);
4169
4170 for (size_t i = source->filters.num; i > 0; i--) {
4171 obs_source_t *filter = source->filters.array[i - 1];
4172 obs_source_load(filter);
4173 }
4174 }
4175
obs_source_active(const obs_source_t * source)4176 bool obs_source_active(const obs_source_t *source)
4177 {
4178 return obs_source_valid(source, "obs_source_active")
4179 ? source->activate_refs != 0
4180 : false;
4181 }
4182
obs_source_showing(const obs_source_t * source)4183 bool obs_source_showing(const obs_source_t *source)
4184 {
4185 return obs_source_valid(source, "obs_source_showing")
4186 ? source->show_refs != 0
4187 : false;
4188 }
4189
signal_flags_updated(obs_source_t * source)4190 static inline void signal_flags_updated(obs_source_t *source)
4191 {
4192 struct calldata data;
4193 uint8_t stack[128];
4194
4195 calldata_init_fixed(&data, stack, sizeof(stack));
4196 calldata_set_ptr(&data, "source", source);
4197 calldata_set_int(&data, "flags", source->flags);
4198
4199 signal_handler_signal(source->context.signals, "update_flags", &data);
4200 }
4201
obs_source_set_flags(obs_source_t * source,uint32_t flags)4202 void obs_source_set_flags(obs_source_t *source, uint32_t flags)
4203 {
4204 if (!obs_source_valid(source, "obs_source_set_flags"))
4205 return;
4206
4207 if (flags != source->flags) {
4208 source->flags = flags;
4209 signal_flags_updated(source);
4210 }
4211 }
4212
obs_source_set_default_flags(obs_source_t * source,uint32_t flags)4213 void obs_source_set_default_flags(obs_source_t *source, uint32_t flags)
4214 {
4215 if (!obs_source_valid(source, "obs_source_set_default_flags"))
4216 return;
4217
4218 source->default_flags = flags;
4219 }
4220
obs_source_get_flags(const obs_source_t * source)4221 uint32_t obs_source_get_flags(const obs_source_t *source)
4222 {
4223 return obs_source_valid(source, "obs_source_get_flags") ? source->flags
4224 : 0;
4225 }
4226
obs_source_set_audio_mixers(obs_source_t * source,uint32_t mixers)4227 void obs_source_set_audio_mixers(obs_source_t *source, uint32_t mixers)
4228 {
4229 struct calldata data;
4230 uint8_t stack[128];
4231
4232 if (!obs_source_valid(source, "obs_source_set_audio_mixers"))
4233 return;
4234 if ((source->info.output_flags & OBS_SOURCE_AUDIO) == 0)
4235 return;
4236
4237 if (source->audio_mixers == mixers)
4238 return;
4239
4240 calldata_init_fixed(&data, stack, sizeof(stack));
4241 calldata_set_ptr(&data, "source", source);
4242 calldata_set_int(&data, "mixers", mixers);
4243
4244 signal_handler_signal(source->context.signals, "audio_mixers", &data);
4245
4246 mixers = (uint32_t)calldata_int(&data, "mixers");
4247
4248 source->audio_mixers = mixers;
4249 }
4250
obs_source_get_audio_mixers(const obs_source_t * source)4251 uint32_t obs_source_get_audio_mixers(const obs_source_t *source)
4252 {
4253 if (!obs_source_valid(source, "obs_source_get_audio_mixers"))
4254 return 0;
4255 if ((source->info.output_flags & OBS_SOURCE_AUDIO) == 0)
4256 return 0;
4257
4258 return source->audio_mixers;
4259 }
4260
obs_source_draw_set_color_matrix(const struct matrix4 * color_matrix,const struct vec3 * color_range_min,const struct vec3 * color_range_max)4261 void obs_source_draw_set_color_matrix(const struct matrix4 *color_matrix,
4262 const struct vec3 *color_range_min,
4263 const struct vec3 *color_range_max)
4264 {
4265 struct vec3 color_range_min_def;
4266 struct vec3 color_range_max_def;
4267
4268 vec3_set(&color_range_min_def, 0.0f, 0.0f, 0.0f);
4269 vec3_set(&color_range_max_def, 1.0f, 1.0f, 1.0f);
4270
4271 gs_effect_t *effect = gs_get_effect();
4272 gs_eparam_t *matrix;
4273 gs_eparam_t *range_min;
4274 gs_eparam_t *range_max;
4275
4276 if (!effect) {
4277 blog(LOG_WARNING, "obs_source_draw_set_color_matrix: no "
4278 "active effect!");
4279 return;
4280 }
4281
4282 if (!obs_ptr_valid(color_matrix, "obs_source_draw_set_color_matrix"))
4283 return;
4284
4285 if (!color_range_min)
4286 color_range_min = &color_range_min_def;
4287 if (!color_range_max)
4288 color_range_max = &color_range_max_def;
4289
4290 matrix = gs_effect_get_param_by_name(effect, "color_matrix");
4291 range_min = gs_effect_get_param_by_name(effect, "color_range_min");
4292 range_max = gs_effect_get_param_by_name(effect, "color_range_max");
4293
4294 gs_effect_set_matrix4(matrix, color_matrix);
4295 gs_effect_set_val(range_min, color_range_min, sizeof(float) * 3);
4296 gs_effect_set_val(range_max, color_range_max, sizeof(float) * 3);
4297 }
4298
obs_source_draw(gs_texture_t * texture,int x,int y,uint32_t cx,uint32_t cy,bool flip)4299 void obs_source_draw(gs_texture_t *texture, int x, int y, uint32_t cx,
4300 uint32_t cy, bool flip)
4301 {
4302 if (!obs_ptr_valid(texture, "obs_source_draw"))
4303 return;
4304
4305 gs_effect_t *effect = gs_get_effect();
4306 if (!effect) {
4307 blog(LOG_WARNING, "obs_source_draw: no active effect!");
4308 return;
4309 }
4310
4311 const bool linear_srgb = gs_get_linear_srgb();
4312
4313 const bool previous = gs_framebuffer_srgb_enabled();
4314 gs_enable_framebuffer_srgb(linear_srgb);
4315
4316 gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
4317 if (linear_srgb)
4318 gs_effect_set_texture_srgb(image, texture);
4319 else
4320 gs_effect_set_texture(image, texture);
4321
4322 const bool change_pos = (x != 0 || y != 0);
4323 if (change_pos) {
4324 gs_matrix_push();
4325 gs_matrix_translate3f((float)x, (float)y, 0.0f);
4326 }
4327
4328 gs_draw_sprite(texture, flip ? GS_FLIP_V : 0, cx, cy);
4329
4330 if (change_pos)
4331 gs_matrix_pop();
4332
4333 gs_enable_framebuffer_srgb(previous);
4334 }
4335
obs_source_inc_showing(obs_source_t * source)4336 void obs_source_inc_showing(obs_source_t *source)
4337 {
4338 if (obs_source_valid(source, "obs_source_inc_showing"))
4339 obs_source_activate(source, AUX_VIEW);
4340 }
4341
obs_source_inc_active(obs_source_t * source)4342 void obs_source_inc_active(obs_source_t *source)
4343 {
4344 if (obs_source_valid(source, "obs_source_inc_active"))
4345 obs_source_activate(source, MAIN_VIEW);
4346 }
4347
obs_source_dec_showing(obs_source_t * source)4348 void obs_source_dec_showing(obs_source_t *source)
4349 {
4350 if (obs_source_valid(source, "obs_source_dec_showing"))
4351 obs_source_deactivate(source, AUX_VIEW);
4352 }
4353
obs_source_dec_active(obs_source_t * source)4354 void obs_source_dec_active(obs_source_t *source)
4355 {
4356 if (obs_source_valid(source, "obs_source_dec_active"))
4357 obs_source_deactivate(source, MAIN_VIEW);
4358 }
4359
obs_source_enum_filters(obs_source_t * source,obs_source_enum_proc_t callback,void * param)4360 void obs_source_enum_filters(obs_source_t *source,
4361 obs_source_enum_proc_t callback, void *param)
4362 {
4363 if (!obs_source_valid(source, "obs_source_enum_filters"))
4364 return;
4365 if (!obs_ptr_valid(callback, "obs_source_enum_filters"))
4366 return;
4367
4368 pthread_mutex_lock(&source->filter_mutex);
4369
4370 for (size_t i = source->filters.num; i > 0; i--) {
4371 struct obs_source *filter = source->filters.array[i - 1];
4372 callback(source, filter, param);
4373 }
4374
4375 pthread_mutex_unlock(&source->filter_mutex);
4376 }
4377
obs_source_set_hidden(obs_source_t * source,bool hidden)4378 void obs_source_set_hidden(obs_source_t *source, bool hidden)
4379 {
4380 source->temp_removed = hidden;
4381 }
4382
obs_source_is_hidden(obs_source_t * source)4383 bool obs_source_is_hidden(obs_source_t *source)
4384 {
4385 return source->temp_removed;
4386 }
4387
obs_source_get_filter_by_name(obs_source_t * source,const char * name)4388 obs_source_t *obs_source_get_filter_by_name(obs_source_t *source,
4389 const char *name)
4390 {
4391 obs_source_t *filter = NULL;
4392
4393 if (!obs_source_valid(source, "obs_source_get_filter_by_name"))
4394 return NULL;
4395 if (!obs_ptr_valid(name, "obs_source_get_filter_by_name"))
4396 return NULL;
4397
4398 pthread_mutex_lock(&source->filter_mutex);
4399
4400 for (size_t i = 0; i < source->filters.num; i++) {
4401 struct obs_source *cur_filter = source->filters.array[i];
4402 if (strcmp(cur_filter->context.name, name) == 0) {
4403 filter = cur_filter;
4404 obs_source_addref(filter);
4405 break;
4406 }
4407 }
4408
4409 pthread_mutex_unlock(&source->filter_mutex);
4410
4411 return filter;
4412 }
4413
obs_source_filter_count(const obs_source_t * source)4414 size_t obs_source_filter_count(const obs_source_t *source)
4415 {
4416 return obs_source_valid(source, "obs_source_filter_count")
4417 ? source->filters.num
4418 : 0;
4419 }
4420
obs_source_enabled(const obs_source_t * source)4421 bool obs_source_enabled(const obs_source_t *source)
4422 {
4423 return obs_source_valid(source, "obs_source_enabled") ? source->enabled
4424 : false;
4425 }
4426
obs_source_set_enabled(obs_source_t * source,bool enabled)4427 void obs_source_set_enabled(obs_source_t *source, bool enabled)
4428 {
4429 struct calldata data;
4430 uint8_t stack[128];
4431
4432 if (!obs_source_valid(source, "obs_source_set_enabled"))
4433 return;
4434
4435 source->enabled = enabled;
4436
4437 calldata_init_fixed(&data, stack, sizeof(stack));
4438 calldata_set_ptr(&data, "source", source);
4439 calldata_set_bool(&data, "enabled", enabled);
4440
4441 signal_handler_signal(source->context.signals, "enable", &data);
4442 }
4443
obs_source_muted(const obs_source_t * source)4444 bool obs_source_muted(const obs_source_t *source)
4445 {
4446 return obs_source_valid(source, "obs_source_muted") ? source->user_muted
4447 : false;
4448 }
4449
obs_source_set_muted(obs_source_t * source,bool muted)4450 void obs_source_set_muted(obs_source_t *source, bool muted)
4451 {
4452 struct calldata data;
4453 uint8_t stack[128];
4454 struct audio_action action = {.timestamp = os_gettime_ns(),
4455 .type = AUDIO_ACTION_MUTE,
4456 .set = muted};
4457
4458 if (!obs_source_valid(source, "obs_source_set_muted"))
4459 return;
4460
4461 source->user_muted = muted;
4462
4463 calldata_init_fixed(&data, stack, sizeof(stack));
4464 calldata_set_ptr(&data, "source", source);
4465 calldata_set_bool(&data, "muted", muted);
4466
4467 signal_handler_signal(source->context.signals, "mute", &data);
4468
4469 pthread_mutex_lock(&source->audio_actions_mutex);
4470 da_push_back(source->audio_actions, &action);
4471 pthread_mutex_unlock(&source->audio_actions_mutex);
4472 }
4473
source_signal_push_to_changed(obs_source_t * source,const char * signal,bool enabled)4474 static void source_signal_push_to_changed(obs_source_t *source,
4475 const char *signal, bool enabled)
4476 {
4477 struct calldata data;
4478 uint8_t stack[128];
4479
4480 calldata_init_fixed(&data, stack, sizeof(stack));
4481 calldata_set_ptr(&data, "source", source);
4482 calldata_set_bool(&data, "enabled", enabled);
4483
4484 signal_handler_signal(source->context.signals, signal, &data);
4485 }
4486
source_signal_push_to_delay(obs_source_t * source,const char * signal,uint64_t delay)4487 static void source_signal_push_to_delay(obs_source_t *source,
4488 const char *signal, uint64_t delay)
4489 {
4490 struct calldata data;
4491 uint8_t stack[128];
4492
4493 calldata_init_fixed(&data, stack, sizeof(stack));
4494 calldata_set_ptr(&data, "source", source);
4495 calldata_set_bool(&data, "delay", delay);
4496
4497 signal_handler_signal(source->context.signals, signal, &data);
4498 }
4499
obs_source_push_to_mute_enabled(obs_source_t * source)4500 bool obs_source_push_to_mute_enabled(obs_source_t *source)
4501 {
4502 bool enabled;
4503 if (!obs_source_valid(source, "obs_source_push_to_mute_enabled"))
4504 return false;
4505
4506 pthread_mutex_lock(&source->audio_mutex);
4507 enabled = source->push_to_mute_enabled;
4508 pthread_mutex_unlock(&source->audio_mutex);
4509
4510 return enabled;
4511 }
4512
obs_source_enable_push_to_mute(obs_source_t * source,bool enabled)4513 void obs_source_enable_push_to_mute(obs_source_t *source, bool enabled)
4514 {
4515 if (!obs_source_valid(source, "obs_source_enable_push_to_mute"))
4516 return;
4517
4518 pthread_mutex_lock(&source->audio_mutex);
4519 bool changed = source->push_to_mute_enabled != enabled;
4520 if (obs_source_get_output_flags(source) & OBS_SOURCE_AUDIO && changed)
4521 blog(LOG_INFO, "source '%s' %s push-to-mute",
4522 obs_source_get_name(source),
4523 enabled ? "enabled" : "disabled");
4524
4525 source->push_to_mute_enabled = enabled;
4526
4527 if (changed)
4528 source_signal_push_to_changed(source, "push_to_mute_changed",
4529 enabled);
4530 pthread_mutex_unlock(&source->audio_mutex);
4531 }
4532
obs_source_get_push_to_mute_delay(obs_source_t * source)4533 uint64_t obs_source_get_push_to_mute_delay(obs_source_t *source)
4534 {
4535 uint64_t delay;
4536 if (!obs_source_valid(source, "obs_source_get_push_to_mute_delay"))
4537 return 0;
4538
4539 pthread_mutex_lock(&source->audio_mutex);
4540 delay = source->push_to_mute_delay;
4541 pthread_mutex_unlock(&source->audio_mutex);
4542
4543 return delay;
4544 }
4545
obs_source_set_push_to_mute_delay(obs_source_t * source,uint64_t delay)4546 void obs_source_set_push_to_mute_delay(obs_source_t *source, uint64_t delay)
4547 {
4548 if (!obs_source_valid(source, "obs_source_set_push_to_mute_delay"))
4549 return;
4550
4551 pthread_mutex_lock(&source->audio_mutex);
4552 source->push_to_mute_delay = delay;
4553
4554 source_signal_push_to_delay(source, "push_to_mute_delay", delay);
4555 pthread_mutex_unlock(&source->audio_mutex);
4556 }
4557
obs_source_push_to_talk_enabled(obs_source_t * source)4558 bool obs_source_push_to_talk_enabled(obs_source_t *source)
4559 {
4560 bool enabled;
4561 if (!obs_source_valid(source, "obs_source_push_to_talk_enabled"))
4562 return false;
4563
4564 pthread_mutex_lock(&source->audio_mutex);
4565 enabled = source->push_to_talk_enabled;
4566 pthread_mutex_unlock(&source->audio_mutex);
4567
4568 return enabled;
4569 }
4570
obs_source_enable_push_to_talk(obs_source_t * source,bool enabled)4571 void obs_source_enable_push_to_talk(obs_source_t *source, bool enabled)
4572 {
4573 if (!obs_source_valid(source, "obs_source_enable_push_to_talk"))
4574 return;
4575
4576 pthread_mutex_lock(&source->audio_mutex);
4577 bool changed = source->push_to_talk_enabled != enabled;
4578 if (obs_source_get_output_flags(source) & OBS_SOURCE_AUDIO && changed)
4579 blog(LOG_INFO, "source '%s' %s push-to-talk",
4580 obs_source_get_name(source),
4581 enabled ? "enabled" : "disabled");
4582
4583 source->push_to_talk_enabled = enabled;
4584
4585 if (changed)
4586 source_signal_push_to_changed(source, "push_to_talk_changed",
4587 enabled);
4588 pthread_mutex_unlock(&source->audio_mutex);
4589 }
4590
obs_source_get_push_to_talk_delay(obs_source_t * source)4591 uint64_t obs_source_get_push_to_talk_delay(obs_source_t *source)
4592 {
4593 uint64_t delay;
4594 if (!obs_source_valid(source, "obs_source_get_push_to_talk_delay"))
4595 return 0;
4596
4597 pthread_mutex_lock(&source->audio_mutex);
4598 delay = source->push_to_talk_delay;
4599 pthread_mutex_unlock(&source->audio_mutex);
4600
4601 return delay;
4602 }
4603
obs_source_set_push_to_talk_delay(obs_source_t * source,uint64_t delay)4604 void obs_source_set_push_to_talk_delay(obs_source_t *source, uint64_t delay)
4605 {
4606 if (!obs_source_valid(source, "obs_source_set_push_to_talk_delay"))
4607 return;
4608
4609 pthread_mutex_lock(&source->audio_mutex);
4610 source->push_to_talk_delay = delay;
4611
4612 source_signal_push_to_delay(source, "push_to_talk_delay", delay);
4613 pthread_mutex_unlock(&source->audio_mutex);
4614 }
4615
obs_source_get_type_data(obs_source_t * source)4616 void *obs_source_get_type_data(obs_source_t *source)
4617 {
4618 return obs_source_valid(source, "obs_source_get_type_data")
4619 ? source->info.type_data
4620 : NULL;
4621 }
4622
get_source_volume(obs_source_t * source,uint64_t os_time)4623 static float get_source_volume(obs_source_t *source, uint64_t os_time)
4624 {
4625 if (source->push_to_mute_enabled && source->push_to_mute_pressed)
4626 source->push_to_mute_stop_time =
4627 os_time + source->push_to_mute_delay * 1000000;
4628
4629 if (source->push_to_talk_enabled && source->push_to_talk_pressed)
4630 source->push_to_talk_stop_time =
4631 os_time + source->push_to_talk_delay * 1000000;
4632
4633 bool push_to_mute_active = source->push_to_mute_pressed ||
4634 os_time < source->push_to_mute_stop_time;
4635 bool push_to_talk_active = source->push_to_talk_pressed ||
4636 os_time < source->push_to_talk_stop_time;
4637
4638 bool muted = !source->enabled || source->muted ||
4639 (source->push_to_mute_enabled && push_to_mute_active) ||
4640 (source->push_to_talk_enabled && !push_to_talk_active);
4641
4642 if (muted || close_float(source->volume, 0.0f, 0.0001f))
4643 return 0.0f;
4644 if (close_float(source->volume, 1.0f, 0.0001f))
4645 return 1.0f;
4646
4647 return source->volume;
4648 }
4649
multiply_output_audio(obs_source_t * source,size_t mix,size_t channels,float vol)4650 static inline void multiply_output_audio(obs_source_t *source, size_t mix,
4651 size_t channels, float vol)
4652 {
4653 register float *out = source->audio_output_buf[mix][0];
4654 register float *end = out + AUDIO_OUTPUT_FRAMES * channels;
4655
4656 while (out < end)
4657 *(out++) *= vol;
4658 }
4659
multiply_vol_data(obs_source_t * source,size_t mix,size_t channels,float * vol_data)4660 static inline void multiply_vol_data(obs_source_t *source, size_t mix,
4661 size_t channels, float *vol_data)
4662 {
4663 for (size_t ch = 0; ch < channels; ch++) {
4664 register float *out = source->audio_output_buf[mix][ch];
4665 register float *end = out + AUDIO_OUTPUT_FRAMES;
4666 register float *vol = vol_data;
4667
4668 while (out < end)
4669 *(out++) *= *(vol++);
4670 }
4671 }
4672
apply_audio_action(obs_source_t * source,const struct audio_action * action)4673 static inline void apply_audio_action(obs_source_t *source,
4674 const struct audio_action *action)
4675 {
4676 switch (action->type) {
4677 case AUDIO_ACTION_VOL:
4678 source->volume = action->vol;
4679 break;
4680 case AUDIO_ACTION_MUTE:
4681 source->muted = action->set;
4682 break;
4683 case AUDIO_ACTION_PTT:
4684 source->push_to_talk_pressed = action->set;
4685 break;
4686 case AUDIO_ACTION_PTM:
4687 source->push_to_mute_pressed = action->set;
4688 break;
4689 }
4690 }
4691
apply_audio_actions(obs_source_t * source,size_t channels,size_t sample_rate)4692 static void apply_audio_actions(obs_source_t *source, size_t channels,
4693 size_t sample_rate)
4694 {
4695 float vol_data[AUDIO_OUTPUT_FRAMES];
4696 float cur_vol = get_source_volume(source, source->audio_ts);
4697 size_t frame_num = 0;
4698
4699 pthread_mutex_lock(&source->audio_actions_mutex);
4700
4701 for (size_t i = 0; i < source->audio_actions.num; i++) {
4702 struct audio_action action = source->audio_actions.array[i];
4703 uint64_t timestamp = action.timestamp;
4704 size_t new_frame_num;
4705
4706 if (timestamp < source->audio_ts)
4707 timestamp = source->audio_ts;
4708
4709 new_frame_num = conv_time_to_frames(
4710 sample_rate, timestamp - source->audio_ts);
4711
4712 if (new_frame_num >= AUDIO_OUTPUT_FRAMES)
4713 break;
4714
4715 da_erase(source->audio_actions, i--);
4716
4717 apply_audio_action(source, &action);
4718
4719 if (new_frame_num > frame_num) {
4720 for (; frame_num < new_frame_num; frame_num++)
4721 vol_data[frame_num] = cur_vol;
4722 }
4723
4724 cur_vol = get_source_volume(source, timestamp);
4725 }
4726
4727 for (; frame_num < AUDIO_OUTPUT_FRAMES; frame_num++)
4728 vol_data[frame_num] = cur_vol;
4729
4730 pthread_mutex_unlock(&source->audio_actions_mutex);
4731
4732 for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
4733 if ((source->audio_mixers & (1 << mix)) != 0)
4734 multiply_vol_data(source, mix, channels, vol_data);
4735 }
4736 }
4737
apply_audio_volume(obs_source_t * source,uint32_t mixers,size_t channels,size_t sample_rate)4738 static void apply_audio_volume(obs_source_t *source, uint32_t mixers,
4739 size_t channels, size_t sample_rate)
4740 {
4741 struct audio_action action;
4742 bool actions_pending;
4743 float vol;
4744
4745 pthread_mutex_lock(&source->audio_actions_mutex);
4746
4747 actions_pending = source->audio_actions.num > 0;
4748 if (actions_pending)
4749 action = source->audio_actions.array[0];
4750
4751 pthread_mutex_unlock(&source->audio_actions_mutex);
4752
4753 if (actions_pending) {
4754 uint64_t duration =
4755 conv_frames_to_time(sample_rate, AUDIO_OUTPUT_FRAMES);
4756
4757 if (action.timestamp < (source->audio_ts + duration)) {
4758 apply_audio_actions(source, channels, sample_rate);
4759 return;
4760 }
4761 }
4762
4763 vol = get_source_volume(source, source->audio_ts);
4764 if (vol == 1.0f)
4765 return;
4766
4767 if (vol == 0.0f || mixers == 0) {
4768 memset(source->audio_output_buf[0][0], 0,
4769 AUDIO_OUTPUT_FRAMES * sizeof(float) *
4770 MAX_AUDIO_CHANNELS * MAX_AUDIO_MIXES);
4771 return;
4772 }
4773
4774 for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
4775 uint32_t mix_and_val = (1 << mix);
4776 if ((source->audio_mixers & mix_and_val) != 0 &&
4777 (mixers & mix_and_val) != 0)
4778 multiply_output_audio(source, mix, channels, vol);
4779 }
4780 }
4781
custom_audio_render(obs_source_t * source,uint32_t mixers,size_t channels,size_t sample_rate)4782 static void custom_audio_render(obs_source_t *source, uint32_t mixers,
4783 size_t channels, size_t sample_rate)
4784 {
4785 struct obs_source_audio_mix audio_data;
4786 bool success;
4787 uint64_t ts;
4788
4789 for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
4790 for (size_t ch = 0; ch < channels; ch++) {
4791 audio_data.output[mix].data[ch] =
4792 source->audio_output_buf[mix][ch];
4793 }
4794
4795 if ((source->audio_mixers & mixers & (1 << mix)) != 0) {
4796 memset(source->audio_output_buf[mix][0], 0,
4797 sizeof(float) * AUDIO_OUTPUT_FRAMES * channels);
4798 }
4799 }
4800
4801 success = source->info.audio_render(source->context.data, &ts,
4802 &audio_data, mixers, channels,
4803 sample_rate);
4804 source->audio_ts = success ? ts : 0;
4805 source->audio_pending = !success;
4806
4807 if (!success || !source->audio_ts || !mixers)
4808 return;
4809
4810 for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
4811 uint32_t mix_bit = 1 << mix;
4812
4813 if ((mixers & mix_bit) == 0)
4814 continue;
4815
4816 if ((source->audio_mixers & mix_bit) == 0) {
4817 memset(source->audio_output_buf[mix][0], 0,
4818 sizeof(float) * AUDIO_OUTPUT_FRAMES * channels);
4819 }
4820 }
4821
4822 apply_audio_volume(source, mixers, channels, sample_rate);
4823 }
4824
audio_submix(obs_source_t * source,size_t channels,size_t sample_rate)4825 static void audio_submix(obs_source_t *source, size_t channels,
4826 size_t sample_rate)
4827 {
4828 struct audio_output_data audio_data;
4829 struct obs_source_audio audio = {0};
4830 bool success;
4831 uint64_t ts;
4832
4833 for (size_t ch = 0; ch < channels; ch++) {
4834 audio_data.data[ch] = source->audio_mix_buf[ch];
4835 }
4836
4837 memset(source->audio_mix_buf[0], 0,
4838 sizeof(float) * AUDIO_OUTPUT_FRAMES * channels);
4839
4840 success = source->info.audio_mix(source->context.data, &ts, &audio_data,
4841 channels, sample_rate);
4842
4843 if (!success)
4844 return;
4845
4846 for (size_t i = 0; i < channels; i++)
4847 audio.data[i] = (const uint8_t *)audio_data.data[i];
4848
4849 audio.samples_per_sec = (uint32_t)sample_rate;
4850 audio.frames = AUDIO_OUTPUT_FRAMES;
4851 audio.format = AUDIO_FORMAT_FLOAT_PLANAR;
4852 audio.speakers = (enum speaker_layout)channels;
4853 audio.timestamp = ts;
4854
4855 obs_source_output_audio(source, &audio);
4856 }
4857
process_audio_source_tick(obs_source_t * source,uint32_t mixers,size_t channels,size_t sample_rate,size_t size)4858 static inline void process_audio_source_tick(obs_source_t *source,
4859 uint32_t mixers, size_t channels,
4860 size_t sample_rate, size_t size)
4861 {
4862 bool audio_submix = !!(source->info.output_flags & OBS_SOURCE_SUBMIX);
4863
4864 pthread_mutex_lock(&source->audio_buf_mutex);
4865
4866 if (source->audio_input_buf[0].size < size) {
4867 source->audio_pending = true;
4868 pthread_mutex_unlock(&source->audio_buf_mutex);
4869 return;
4870 }
4871
4872 for (size_t ch = 0; ch < channels; ch++)
4873 circlebuf_peek_front(&source->audio_input_buf[ch],
4874 source->audio_output_buf[0][ch], size);
4875
4876 pthread_mutex_unlock(&source->audio_buf_mutex);
4877
4878 for (size_t mix = 1; mix < MAX_AUDIO_MIXES; mix++) {
4879 uint32_t mix_and_val = (1 << mix);
4880
4881 if (audio_submix) {
4882 if (mix > 1)
4883 break;
4884
4885 mixers = 1;
4886 mix_and_val = 1;
4887 }
4888
4889 if ((source->audio_mixers & mix_and_val) == 0 ||
4890 (mixers & mix_and_val) == 0) {
4891 memset(source->audio_output_buf[mix][0], 0,
4892 size * channels);
4893 continue;
4894 }
4895
4896 for (size_t ch = 0; ch < channels; ch++)
4897 memcpy(source->audio_output_buf[mix][ch],
4898 source->audio_output_buf[0][ch], size);
4899 }
4900
4901 if (audio_submix) {
4902 source->audio_pending = false;
4903 return;
4904 }
4905
4906 if ((source->audio_mixers & 1) == 0 || (mixers & 1) == 0)
4907 memset(source->audio_output_buf[0][0], 0, size * channels);
4908
4909 apply_audio_volume(source, mixers, channels, sample_rate);
4910 source->audio_pending = false;
4911 }
4912
obs_source_audio_render(obs_source_t * source,uint32_t mixers,size_t channels,size_t sample_rate,size_t size)4913 void obs_source_audio_render(obs_source_t *source, uint32_t mixers,
4914 size_t channels, size_t sample_rate, size_t size)
4915 {
4916 if (!source->audio_output_buf[0][0]) {
4917 source->audio_pending = true;
4918 return;
4919 }
4920
4921 if (source->info.audio_render) {
4922 if (!source->context.data) {
4923 source->audio_pending = true;
4924 return;
4925 }
4926 custom_audio_render(source, mixers, channels, sample_rate);
4927 return;
4928 }
4929
4930 if (source->info.audio_mix) {
4931 audio_submix(source, channels, sample_rate);
4932 }
4933
4934 if (!source->audio_ts) {
4935 source->audio_pending = true;
4936 return;
4937 }
4938
4939 process_audio_source_tick(source, mixers, channels, sample_rate, size);
4940 }
4941
obs_source_audio_pending(const obs_source_t * source)4942 bool obs_source_audio_pending(const obs_source_t *source)
4943 {
4944 if (!obs_source_valid(source, "obs_source_audio_pending"))
4945 return true;
4946
4947 return (is_composite_source(source) || is_audio_source(source))
4948 ? source->audio_pending
4949 : true;
4950 }
4951
obs_source_get_audio_timestamp(const obs_source_t * source)4952 uint64_t obs_source_get_audio_timestamp(const obs_source_t *source)
4953 {
4954 return obs_source_valid(source, "obs_source_get_audio_timestamp")
4955 ? source->audio_ts
4956 : 0;
4957 }
4958
obs_source_get_audio_mix(const obs_source_t * source,struct obs_source_audio_mix * audio)4959 void obs_source_get_audio_mix(const obs_source_t *source,
4960 struct obs_source_audio_mix *audio)
4961 {
4962 if (!obs_source_valid(source, "obs_source_get_audio_mix"))
4963 return;
4964 if (!obs_ptr_valid(audio, "audio"))
4965 return;
4966
4967 for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
4968 for (size_t ch = 0; ch < MAX_AUDIO_CHANNELS; ch++) {
4969 audio->output[mix].data[ch] =
4970 source->audio_output_buf[mix][ch];
4971 }
4972 }
4973 }
4974
obs_source_add_audio_capture_callback(obs_source_t * source,obs_source_audio_capture_t callback,void * param)4975 void obs_source_add_audio_capture_callback(obs_source_t *source,
4976 obs_source_audio_capture_t callback,
4977 void *param)
4978 {
4979 struct audio_cb_info info = {callback, param};
4980
4981 if (!obs_source_valid(source, "obs_source_add_audio_capture_callback"))
4982 return;
4983
4984 pthread_mutex_lock(&source->audio_cb_mutex);
4985 da_push_back(source->audio_cb_list, &info);
4986 pthread_mutex_unlock(&source->audio_cb_mutex);
4987 }
4988
obs_source_remove_audio_capture_callback(obs_source_t * source,obs_source_audio_capture_t callback,void * param)4989 void obs_source_remove_audio_capture_callback(
4990 obs_source_t *source, obs_source_audio_capture_t callback, void *param)
4991 {
4992 struct audio_cb_info info = {callback, param};
4993
4994 if (!obs_source_valid(source,
4995 "obs_source_remove_audio_capture_callback"))
4996 return;
4997
4998 pthread_mutex_lock(&source->audio_cb_mutex);
4999 da_erase_item(source->audio_cb_list, &info);
5000 pthread_mutex_unlock(&source->audio_cb_mutex);
5001 }
5002
obs_source_set_monitoring_type(obs_source_t * source,enum obs_monitoring_type type)5003 void obs_source_set_monitoring_type(obs_source_t *source,
5004 enum obs_monitoring_type type)
5005 {
5006 bool was_on;
5007 bool now_on;
5008
5009 if (!obs_source_valid(source, "obs_source_set_monitoring_type"))
5010 return;
5011 if (source->monitoring_type == type)
5012 return;
5013
5014 was_on = source->monitoring_type != OBS_MONITORING_TYPE_NONE;
5015 now_on = type != OBS_MONITORING_TYPE_NONE;
5016
5017 if (was_on != now_on) {
5018 if (!was_on) {
5019 source->monitor = audio_monitor_create(source);
5020 } else {
5021 audio_monitor_destroy(source->monitor);
5022 source->monitor = NULL;
5023 }
5024 }
5025
5026 source->monitoring_type = type;
5027 }
5028
5029 enum obs_monitoring_type
obs_source_get_monitoring_type(const obs_source_t * source)5030 obs_source_get_monitoring_type(const obs_source_t *source)
5031 {
5032 return obs_source_valid(source, "obs_source_get_monitoring_type")
5033 ? source->monitoring_type
5034 : OBS_MONITORING_TYPE_NONE;
5035 }
5036
obs_source_set_async_unbuffered(obs_source_t * source,bool unbuffered)5037 void obs_source_set_async_unbuffered(obs_source_t *source, bool unbuffered)
5038 {
5039 if (!obs_source_valid(source, "obs_source_set_async_unbuffered"))
5040 return;
5041
5042 source->async_unbuffered = unbuffered;
5043 }
5044
obs_source_async_unbuffered(const obs_source_t * source)5045 bool obs_source_async_unbuffered(const obs_source_t *source)
5046 {
5047 return obs_source_valid(source, "obs_source_async_unbuffered")
5048 ? source->async_unbuffered
5049 : false;
5050 }
5051
obs_source_get_private_settings(obs_source_t * source)5052 obs_data_t *obs_source_get_private_settings(obs_source_t *source)
5053 {
5054 if (!obs_ptr_valid(source, "obs_source_get_private_settings"))
5055 return NULL;
5056
5057 obs_data_addref(source->private_settings);
5058 return source->private_settings;
5059 }
5060
obs_source_set_async_decoupled(obs_source_t * source,bool decouple)5061 void obs_source_set_async_decoupled(obs_source_t *source, bool decouple)
5062 {
5063 if (!obs_ptr_valid(source, "obs_source_set_async_decoupled"))
5064 return;
5065
5066 source->async_decoupled = decouple;
5067 if (decouple) {
5068 pthread_mutex_lock(&source->audio_buf_mutex);
5069 source->timing_set = false;
5070 reset_audio_data(source, 0);
5071 pthread_mutex_unlock(&source->audio_buf_mutex);
5072 }
5073 }
5074
obs_source_async_decoupled(const obs_source_t * source)5075 bool obs_source_async_decoupled(const obs_source_t *source)
5076 {
5077 return obs_source_valid(source, "obs_source_async_decoupled")
5078 ? source->async_decoupled
5079 : false;
5080 }
5081
5082 /* hidden/undocumented export to allow source type redefinition for scripts */
obs_enable_source_type(const char * name,bool enable)5083 EXPORT void obs_enable_source_type(const char *name, bool enable)
5084 {
5085 struct obs_source_info *info = get_source_info(name);
5086 if (!info)
5087 return;
5088
5089 if (enable)
5090 info->output_flags &= ~OBS_SOURCE_CAP_DISABLED;
5091 else
5092 info->output_flags |= OBS_SOURCE_CAP_DISABLED;
5093 }
5094
obs_source_get_speaker_layout(obs_source_t * source)5095 enum speaker_layout obs_source_get_speaker_layout(obs_source_t *source)
5096 {
5097 if (!obs_source_valid(source, "obs_source_get_audio_channels"))
5098 return SPEAKERS_UNKNOWN;
5099
5100 return source->sample_info.speakers;
5101 }
5102
obs_source_set_balance_value(obs_source_t * source,float balance)5103 void obs_source_set_balance_value(obs_source_t *source, float balance)
5104 {
5105 if (!obs_source_valid(source, "obs_source_set_balance_value"))
5106 return;
5107
5108 source->balance = balance;
5109 }
5110
obs_source_get_balance_value(const obs_source_t * source)5111 float obs_source_get_balance_value(const obs_source_t *source)
5112 {
5113 return obs_source_valid(source, "obs_source_get_balance_value")
5114 ? source->balance
5115 : 0.5f;
5116 }
5117
obs_source_set_audio_active(obs_source_t * source,bool active)5118 void obs_source_set_audio_active(obs_source_t *source, bool active)
5119 {
5120 if (!obs_source_valid(source, "obs_source_set_audio_active"))
5121 return;
5122
5123 if (os_atomic_set_bool(&source->audio_active, active) == active)
5124 return;
5125
5126 if (active)
5127 obs_source_dosignal(source, "source_audio_activate",
5128 "audio_activate");
5129 else
5130 obs_source_dosignal(source, "source_audio_deactivate",
5131 "audio_deactivate");
5132 }
5133
obs_source_audio_active(const obs_source_t * source)5134 bool obs_source_audio_active(const obs_source_t *source)
5135 {
5136 return obs_source_valid(source, "obs_source_audio_active")
5137 ? os_atomic_load_bool(&source->audio_active)
5138 : false;
5139 }
5140
obs_source_get_last_obs_version(const obs_source_t * source)5141 uint32_t obs_source_get_last_obs_version(const obs_source_t *source)
5142 {
5143 return obs_source_valid(source, "obs_source_get_last_obs_version")
5144 ? source->last_obs_ver
5145 : 0;
5146 }
5147
obs_source_get_icon_type(const char * id)5148 enum obs_icon_type obs_source_get_icon_type(const char *id)
5149 {
5150 const struct obs_source_info *info = get_source_info(id);
5151 return (info) ? info->icon_type : OBS_ICON_TYPE_UNKNOWN;
5152 }
5153
obs_source_media_play_pause(obs_source_t * source,bool pause)5154 void obs_source_media_play_pause(obs_source_t *source, bool pause)
5155 {
5156 if (!data_valid(source, "obs_source_media_play_pause"))
5157 return;
5158
5159 if (!source->info.media_play_pause)
5160 return;
5161
5162 source->info.media_play_pause(source->context.data, pause);
5163
5164 if (pause)
5165 obs_source_dosignal(source, NULL, "media_pause");
5166 else
5167 obs_source_dosignal(source, NULL, "media_play");
5168 }
5169
obs_source_media_restart(obs_source_t * source)5170 void obs_source_media_restart(obs_source_t *source)
5171 {
5172 if (!data_valid(source, "obs_source_media_restart"))
5173 return;
5174
5175 if (!source->info.media_restart)
5176 return;
5177
5178 source->info.media_restart(source->context.data);
5179
5180 obs_source_dosignal(source, NULL, "media_restart");
5181 }
5182
obs_source_media_stop(obs_source_t * source)5183 void obs_source_media_stop(obs_source_t *source)
5184 {
5185 if (!data_valid(source, "obs_source_media_stop"))
5186 return;
5187
5188 if (!source->info.media_stop)
5189 return;
5190
5191 source->info.media_stop(source->context.data);
5192
5193 obs_source_dosignal(source, NULL, "media_stopped");
5194 }
5195
obs_source_media_next(obs_source_t * source)5196 void obs_source_media_next(obs_source_t *source)
5197 {
5198 if (!data_valid(source, "obs_source_media_next"))
5199 return;
5200
5201 if (!source->info.media_next)
5202 return;
5203
5204 source->info.media_next(source->context.data);
5205
5206 obs_source_dosignal(source, NULL, "media_next");
5207 }
5208
obs_source_media_previous(obs_source_t * source)5209 void obs_source_media_previous(obs_source_t *source)
5210 {
5211 if (!data_valid(source, "obs_source_media_previous"))
5212 return;
5213
5214 if (!source->info.media_previous)
5215 return;
5216
5217 source->info.media_previous(source->context.data);
5218
5219 obs_source_dosignal(source, NULL, "media_previous");
5220 }
5221
obs_source_media_get_duration(obs_source_t * source)5222 int64_t obs_source_media_get_duration(obs_source_t *source)
5223 {
5224 if (!data_valid(source, "obs_source_media_get_duration"))
5225 return 0;
5226
5227 if (source->info.media_get_duration)
5228 return source->info.media_get_duration(source->context.data);
5229 else
5230 return 0;
5231 }
5232
obs_source_media_get_time(obs_source_t * source)5233 int64_t obs_source_media_get_time(obs_source_t *source)
5234 {
5235 if (!data_valid(source, "obs_source_media_get_time"))
5236 return 0;
5237
5238 if (source->info.media_get_time)
5239 return source->info.media_get_time(source->context.data);
5240 else
5241 return 0;
5242 }
5243
obs_source_media_set_time(obs_source_t * source,int64_t ms)5244 void obs_source_media_set_time(obs_source_t *source, int64_t ms)
5245 {
5246 if (!data_valid(source, "obs_source_media_set_time"))
5247 return;
5248
5249 if (source->info.media_set_time)
5250 source->info.media_set_time(source->context.data, ms);
5251 }
5252
obs_source_media_get_state(obs_source_t * source)5253 enum obs_media_state obs_source_media_get_state(obs_source_t *source)
5254 {
5255 if (!data_valid(source, "obs_source_media_get_state"))
5256 return OBS_MEDIA_STATE_NONE;
5257
5258 if (source->info.media_get_state)
5259 return source->info.media_get_state(source->context.data);
5260 else
5261 return OBS_MEDIA_STATE_NONE;
5262 }
5263
obs_source_media_started(obs_source_t * source)5264 void obs_source_media_started(obs_source_t *source)
5265 {
5266 if (!obs_source_valid(source, "obs_source_media_started"))
5267 return;
5268
5269 obs_source_dosignal(source, NULL, "media_started");
5270 }
5271
obs_source_media_ended(obs_source_t * source)5272 void obs_source_media_ended(obs_source_t *source)
5273 {
5274 if (!obs_source_valid(source, "obs_source_media_ended"))
5275 return;
5276
5277 obs_source_dosignal(source, NULL, "media_ended");
5278 }
5279
obs_source_backup_filters(obs_source_t * source)5280 obs_data_array_t *obs_source_backup_filters(obs_source_t *source)
5281 {
5282 if (!obs_source_valid(source, "obs_source_backup_filters"))
5283 return NULL;
5284
5285 obs_data_array_t *array = obs_data_array_create();
5286
5287 pthread_mutex_lock(&source->filter_mutex);
5288 for (size_t i = 0; i < source->filters.num; i++) {
5289 struct obs_source *filter = source->filters.array[i];
5290 obs_data_t *data = obs_save_source(filter);
5291 obs_data_array_push_back(array, data);
5292 obs_data_release(data);
5293 }
5294 pthread_mutex_unlock(&source->filter_mutex);
5295
5296 return array;
5297 }
5298
obs_source_restore_filters(obs_source_t * source,obs_data_array_t * array)5299 void obs_source_restore_filters(obs_source_t *source, obs_data_array_t *array)
5300 {
5301 if (!obs_source_valid(source, "obs_source_restore_filters"))
5302 return;
5303 if (!obs_ptr_valid(array, "obs_source_restore_filters"))
5304 return;
5305
5306 DARRAY(obs_source_t *) cur_filters;
5307 DARRAY(obs_source_t *) new_filters;
5308 obs_source_t *prev = NULL;
5309
5310 da_init(cur_filters);
5311 da_init(new_filters);
5312
5313 pthread_mutex_lock(&source->filter_mutex);
5314
5315 /* clear filter list */
5316 da_reserve(cur_filters, source->filters.num);
5317 da_reserve(new_filters, source->filters.num);
5318 for (size_t i = 0; i < source->filters.num; i++) {
5319 obs_source_t *filter = source->filters.array[i];
5320 da_push_back(cur_filters, &filter);
5321 filter->filter_parent = NULL;
5322 filter->filter_target = NULL;
5323 }
5324
5325 da_free(source->filters);
5326 pthread_mutex_unlock(&source->filter_mutex);
5327
5328 /* add backed up filters */
5329 size_t count = obs_data_array_count(array);
5330 for (size_t i = 0; i < count; i++) {
5331 obs_data_t *data = obs_data_array_item(array, i);
5332 const char *name = obs_data_get_string(data, "name");
5333 obs_source_t *filter = NULL;
5334
5335 /* if backed up filter already exists, don't create */
5336 for (size_t j = 0; j < cur_filters.num; j++) {
5337 obs_source_t *cur = cur_filters.array[j];
5338 const char *cur_name = cur->context.name;
5339 if (cur_name && strcmp(cur_name, name) == 0) {
5340 filter = cur;
5341 obs_source_addref(cur);
5342 break;
5343 }
5344 }
5345
5346 if (!filter)
5347 filter = obs_load_source(data);
5348
5349 /* add filter */
5350 if (prev)
5351 prev->filter_target = filter;
5352 prev = filter;
5353 filter->filter_parent = source;
5354 da_push_back(new_filters, &filter);
5355
5356 obs_data_release(data);
5357 }
5358
5359 if (prev)
5360 prev->filter_target = source;
5361
5362 pthread_mutex_lock(&source->filter_mutex);
5363 da_move(source->filters, new_filters);
5364 pthread_mutex_unlock(&source->filter_mutex);
5365
5366 /* release filters */
5367 for (size_t i = 0; i < cur_filters.num; i++) {
5368 obs_source_t *filter = cur_filters.array[i];
5369 obs_source_release(filter);
5370 }
5371
5372 da_free(cur_filters);
5373 }
5374