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