obs-source.c 57.6 KB
Newer Older
J
jp9000 已提交
1
/******************************************************************************
2
    Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
J
jp9000 已提交
3 4 5

    This program is free software: you can redistribute it and/or modify
    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
J
jp9000 已提交
7 8 9 10 11 12 13 14 15 16 17
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/

18 19
#include <inttypes.h>

20
#include "media-io/format-conversion.h"
21
#include "media-io/video-frame.h"
22
#include "media-io/audio-io.h"
J
jp9000 已提交
23
#include "util/threading.h"
24
#include "util/platform.h"
25
#include "callback/calldata.h"
26 27
#include "graphics/matrix3.h"
#include "graphics/vec3.h"
28

J
jp9000 已提交
29
#include "obs.h"
J
jp9000 已提交
30
#include "obs-internal.h"
J
jp9000 已提交
31

32
static inline bool source_valid(const struct obs_source *source)
33 34 35 36
{
	return source && source->context.data;
}

37
const struct obs_source_info *find_source(struct darray *list, const char *id)
J
jp9000 已提交
38 39
{
	size_t i;
J
jp9000 已提交
40
	struct obs_source_info *array = list->array;
J
jp9000 已提交
41 42

	for (i = 0; i < list->num; i++) {
J
jp9000 已提交
43
		struct obs_source_info *info = array+i;
44
		if (strcmp(info->id, id) == 0)
J
jp9000 已提交
45 46 47 48 49 50
			return info;
	}

	return NULL;
}

J
jp9000 已提交
51
static const struct obs_source_info *get_source_info(enum obs_source_type type,
52 53 54 55 56
		const char *id)
{
	struct darray *list = NULL;

	switch (type) {
J
jp9000 已提交
57 58 59 60 61 62 63 64 65 66 67
	case OBS_SOURCE_TYPE_INPUT:
		list = &obs->input_types.da;
		break;

	case OBS_SOURCE_TYPE_FILTER:
		list = &obs->filter_types.da;
		break;

	case OBS_SOURCE_TYPE_TRANSITION:
		list = &obs->transition_types.da;
		break;
68 69 70 71 72
	}

	return find_source(list, id);
}

73 74 75 76 77 78 79 80
static const char *source_signals[] = {
	"void destroy(ptr source)",
	"void add(ptr source)",
	"void remove(ptr source)",
	"void activate(ptr source)",
	"void deactivate(ptr source)",
	"void show(ptr source)",
	"void hide(ptr source)",
J
jp9000 已提交
81
	"void rename(ptr source, string new_name, string prev_name)",
82
	"void volume(ptr source, in out float volume)",
83
	"void update_properties(ptr source)",
J
jp9000 已提交
84
	"void update_flags(ptr source, int flags)",
J
jp9000 已提交
85
	"void audio_sync(ptr source, int out int offset)",
86
	"void audio_data(ptr source, ptr data)",
87 88 89
	NULL
};

90
bool obs_source_init_context(struct obs_source *source,
91
		obs_data_t *settings, const char *name)
92
{
93
	if (!obs_context_data_init(&source->context, settings, name))
94 95
		return false;

96 97
	return signal_handler_add_array(source->context.signals,
			source_signals);
98 99
}

100 101
const char *obs_source_get_display_name(enum obs_source_type type,
		const char *id)
102
{
J
jp9000 已提交
103
	const struct obs_source_info *info = get_source_info(type, id);
104
	return (info != NULL) ? info->get_name() : NULL;
105 106
}

107
/* internal initialization */
J
jp9000 已提交
108 109
bool obs_source_init(struct obs_source *source,
		const struct obs_source_info *info)
J
jp9000 已提交
110
{
111
	source->refs = 1;
J
jp9000 已提交
112
	source->user_volume = 1.0f;
113 114
	source->present_volume = 1.0f;
	source->base_volume = 0.0f;
J
jp9000 已提交
115
	source->sync_offset = 0;
116 117 118
	pthread_mutex_init_value(&source->filter_mutex);
	pthread_mutex_init_value(&source->video_mutex);
	pthread_mutex_init_value(&source->audio_mutex);
119

120 121 122 123 124 125
	if (pthread_mutex_init(&source->filter_mutex, NULL) != 0)
		return false;
	if (pthread_mutex_init(&source->audio_mutex, NULL) != 0)
		return false;
	if (pthread_mutex_init(&source->video_mutex, NULL) != 0)
		return false;
J
jp9000 已提交
126

127
	if (info && info->output_flags & OBS_SOURCE_AUDIO) {
128
		source->audio_line = audio_output_create_line(obs->audio.audio,
129
				source->context.name);
130 131
		if (!source->audio_line) {
			blog(LOG_ERROR, "Failed to create audio line for "
132
			                "source '%s'", source->context.name);
133 134 135
			return false;
		}
	}
136

137 138 139
	obs_context_data_insert(&source->context,
			&obs->data.sources_mutex,
			&obs->data.first_source);
140
	return true;
J
jp9000 已提交
141 142
}

143
static inline void obs_source_dosignal(struct obs_source *source,
144
		const char *signal_obs, const char *signal_source)
145 146 147 148
{
	struct calldata data;

	calldata_init(&data);
149
	calldata_set_ptr(&data, "source", source);
150 151 152
	if (signal_obs)
		signal_handler_signal(obs->signals, signal_obs, &data);
	if (signal_source)
153 154
		signal_handler_signal(source->context.signals, signal_source,
				&data);
155 156 157
	calldata_free(&data);
}

158 159
obs_source_t *obs_source_create(enum obs_source_type type, const char *id,
		const char *name, obs_data_t *settings)
J
jp9000 已提交
160
{
161
	struct obs_source *source = bzalloc(sizeof(struct obs_source));
J
jp9000 已提交
162

J
jp9000 已提交
163
	const struct obs_source_info *info = get_source_info(type, id);
J
jp9000 已提交
164
	if (!info) {
P
Palana 已提交
165
		blog(LOG_ERROR, "Source ID '%s' not found", id);
J
jp9000 已提交
166

167 168 169 170 171 172
		source->info.id      = bstrdup(id);
		source->info.type    = type;
		source->owns_info_id = true;
	} else {
		source->info = *info;
	}
173

174
	if (!obs_source_init_context(source, settings, name))
175 176
		goto fail;

177 178
	if (info && info->get_defaults)
		info->get_defaults(source->context.settings);
J
jp9000 已提交
179

180 181
	/* allow the source to be created even if creation fails so that the
	 * user's data doesn't become lost */
182 183 184
	if (info)
		source->context.data = info->create(source->context.settings,
				source);
185
	if (!source->context.data)
186
		blog(LOG_ERROR, "Failed to create source '%s'!", name);
187

J
jp9000 已提交
188
	if (!obs_source_init(source, info))
189
		goto fail;
J
jp9000 已提交
190

191
	blog(LOG_INFO, "source '%s' (%s) created", name, id);
192
	obs_source_dosignal(source, "source_create", NULL);
J
jp9000 已提交
193 194 195

	if (info->type == OBS_SOURCE_TYPE_TRANSITION)
		os_atomic_inc_long(&obs->data.active_transitions);
J
jp9000 已提交
196
	return source;
197 198 199 200 201

fail:
	blog(LOG_ERROR, "obs_source_create failed");
	obs_source_destroy(source);
	return NULL;
J
jp9000 已提交
202 203
}

204 205
void obs_source_frame_init(struct obs_source_frame *frame,
		enum video_format format, uint32_t width, uint32_t height)
206
{
207
	struct video_frame vid_frame;
J
jp9000 已提交
208 209 210 211

	if (!frame)
		return;

212
	video_frame_init(&vid_frame, format, width, height);
213 214 215
	frame->format = format;
	frame->width  = width;
	frame->height = height;
216

217 218 219
	for (size_t i = 0; i < MAX_AV_PLANES; i++) {
		frame->data[i]     = vid_frame.data[i];
		frame->linesize[i] = vid_frame.linesize[i];
220 221 222
	}
}

223
void obs_source_destroy(struct obs_source *source)
J
jp9000 已提交
224
{
225
	size_t i;
226

J
jp9000 已提交
227 228 229
	if (!source)
		return;

J
jp9000 已提交
230 231 232
	if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
		os_atomic_dec_long(&obs->data.active_transitions);

233 234
	obs_context_data_remove(&source->context);

235 236
	blog(LOG_INFO, "source '%s' destroyed", source->context.name);

237
	obs_source_dosignal(source, "source_destroy", "destroy");
238

239
	if (source->context.data) {
240
		source->info.destroy(source->context.data);
241 242
		source->context.data = NULL;
	}
243

244 245
	if (source->filter_parent)
		obs_source_filter_remove(source->filter_parent, source);
246

247 248
	for (i = 0; i < source->filters.num; i++)
		obs_source_release(source->filters.array[i]);
249

250
	for (i = 0; i < source->video_frames.num; i++)
251
		obs_source_frame_destroy(source->video_frames.array[i]);
252

253 254 255 256
	gs_enter_context(obs->video.graphics);
	gs_texrender_destroy(source->async_convert_texrender);
	gs_texture_destroy(source->async_texture);
	gs_leave_context();
J
jp9000 已提交
257

J
jp9000 已提交
258
	for (i = 0; i < MAX_AV_PLANES; i++)
259 260
		bfree(source->audio_data.data[i]);

261 262 263
	audio_line_destroy(source->audio_line);
	audio_resampler_destroy(source->resampler);

264
	gs_texrender_destroy(source->filter_texrender);
265 266 267 268 269
	da_free(source->video_frames);
	da_free(source->filters);
	pthread_mutex_destroy(&source->filter_mutex);
	pthread_mutex_destroy(&source->audio_mutex);
	pthread_mutex_destroy(&source->video_mutex);
270
	obs_context_data_free(&source->context);
271

272 273 274
	if (source->owns_info_id)
		bfree((void*)source->info.id);

275 276 277
	bfree(source);
}

278
void obs_source_addref(obs_source_t *source)
279
{
P
Palana 已提交
280
	if (source)
J
jp9000 已提交
281
		os_atomic_inc_long(&source->refs);
282 283
}

284
void obs_source_release(obs_source_t *source)
285
{
P
Palana 已提交
286 287
	if (!source)
		return;
288

J
jp9000 已提交
289
	if (os_atomic_dec_long(&source->refs) == 0)
P
Palana 已提交
290
		obs_source_destroy(source);
291 292
}

293
void obs_source_remove(obs_source_t *source)
294
{
295
	struct obs_core_data *data = &obs->data;
296
	size_t id;
297
	bool   exists;
298 299 300

	pthread_mutex_lock(&data->sources_mutex);

J
jp9000 已提交
301 302
	if (!source || source->removed) {
		pthread_mutex_unlock(&data->sources_mutex);
J
jp9000 已提交
303
		return;
J
jp9000 已提交
304
	}
J
jp9000 已提交
305

J
jp9000 已提交
306
	source->removed = true;
J
jp9000 已提交
307

J
jp9000 已提交
308 309
	obs_source_addref(source);

310 311 312 313
	id = da_find(data->user_sources, &source, 0);
	exists = (id != DARRAY_INVALID);
	if (exists) {
		da_erase(data->user_sources, id);
J
jp9000 已提交
314
		obs_source_release(source);
315 316 317
	}

	pthread_mutex_unlock(&data->sources_mutex);
J
jp9000 已提交
318

319 320 321
	if (exists)
		obs_source_dosignal(source, "source_remove", "remove");

J
jp9000 已提交
322
	obs_source_release(source);
323 324
}

325
bool obs_source_removed(const obs_source_t *source)
326
{
J
jp9000 已提交
327
	return source ? source->removed : true;
J
jp9000 已提交
328 329
}

330
static inline obs_data_t *get_defaults(const struct obs_source_info *info)
J
jp9000 已提交
331
{
332
	obs_data_t *settings = obs_data_create();
333 334
	if (info->get_defaults)
		info->get_defaults(settings);
J
jp9000 已提交
335 336 337
	return settings;
}

338
obs_data_t *obs_source_settings(enum obs_source_type type, const char *id)
J
jp9000 已提交
339 340
{
	const struct obs_source_info *info = get_source_info(type, id);
J
jp9000 已提交
341
	return (info) ? get_defaults(info) : NULL;
J
jp9000 已提交
342 343
}

344
obs_properties_t *obs_get_source_properties(enum obs_source_type type,
345
		const char *id)
J
jp9000 已提交
346
{
J
jp9000 已提交
347
	const struct obs_source_info *info = get_source_info(type, id);
348
	if (info && info->get_properties) {
349 350
		obs_data_t       *defaults = get_defaults(info);
		obs_properties_t *properties;
J
jp9000 已提交
351

352
		properties = info->get_properties(NULL);
J
jp9000 已提交
353 354 355 356
		obs_properties_apply_settings(properties, defaults);
		obs_data_release(defaults);
		return properties;
	}
J
jp9000 已提交
357 358 359
	return NULL;
}

360
obs_properties_t *obs_source_properties(const obs_source_t *source)
361
{
362
	if (source_valid(source) && source->info.get_properties) {
363
		obs_properties_t *props;
364
		props = source->info.get_properties(source->context.data);
365
		obs_properties_apply_settings(props, source->context.settings);
J
jp9000 已提交
366 367 368
		return props;
	}

369 370 371
	return NULL;
}

372
uint32_t obs_source_get_output_flags(const obs_source_t *source)
J
jp9000 已提交
373
{
J
jp9000 已提交
374
	return source ? source->info.output_flags : 0;
J
jp9000 已提交
375 376
}

377
static void obs_source_deferred_update(obs_source_t *source)
378
{
379 380 381 382
	if (source->context.data && source->info.update)
		source->info.update(source->context.data,
				source->context.settings);

383 384 385
	source->defer_update = false;
}

386
void obs_source_update(obs_source_t *source, obs_data_t *settings)
J
jp9000 已提交
387
{
J
jp9000 已提交
388 389
	if (!source) return;

390 391 392 393 394 395 396 397
	if (settings)
		obs_data_apply(source->context.settings, settings);

	if (source->info.output_flags & OBS_SOURCE_VIDEO) {
		source->defer_update = true;
	} else if (source->context.data && source->info.update) {
		source->info.update(source->context.data,
				source->context.settings);
398
	}
J
jp9000 已提交
399 400
}

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
void obs_source_update_properties(obs_source_t *source)
{
	calldata_t calldata;

	if (!source) return;

	calldata_init(&calldata);
	calldata_set_ptr(&calldata, "source", source);

	signal_handler_signal(obs_source_get_signal_handler(source),
			"update_properties", &calldata);

	calldata_free(&calldata);
}

416
void obs_source_send_mouse_click(obs_source_t *source,
K
kc5nra 已提交
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
		const struct obs_mouse_event *event,
		int32_t type, bool mouse_up,
		uint32_t click_count)
{
	if (!source)
		return;

	if (source->info.output_flags & OBS_SOURCE_INTERACTION) {
		if (source->info.mouse_click) {
			source->info.mouse_click(source->context.data,
					event, type, mouse_up, click_count);
		}
	}
}

432
void obs_source_send_mouse_move(obs_source_t *source,
K
kc5nra 已提交
433 434 435 436 437 438 439 440 441 442 443 444 445
		const struct obs_mouse_event *event, bool mouse_leave)
{
	if (!source)
		return;

	if (source->info.output_flags & OBS_SOURCE_INTERACTION) {
		if (source->info.mouse_move) {
			source->info.mouse_move(source->context.data,
					event, mouse_leave);
		}
	}
}

446
void obs_source_send_mouse_wheel(obs_source_t *source,
K
kc5nra 已提交
447 448 449 450 451 452 453 454 455 456 457 458 459
		const struct obs_mouse_event *event, int x_delta, int y_delta)
{
	if (!source)
		return;

	if (source->info.output_flags & OBS_SOURCE_INTERACTION) {
		if (source->info.mouse_wheel) {
			source->info.mouse_wheel(source->context.data,
					event, x_delta, y_delta);
		}
	}
}

460
void obs_source_send_focus(obs_source_t *source, bool focus)
K
kc5nra 已提交
461 462 463 464 465 466 467 468 469 470 471
{
	if (!source)
		return;

	if (source->info.output_flags & OBS_SOURCE_INTERACTION) {
		if (source->info.focus) {
			source->info.focus(source->context.data, focus);
		}
	}
}

472
void obs_source_send_key_click(obs_source_t *source,
K
kc5nra 已提交
473 474 475 476 477 478 479 480 481 482 483 484 485
		const struct obs_key_event *event, bool key_up)
{
	if (!source)
		return;

	if (source->info.output_flags & OBS_SOURCE_INTERACTION) {
		if (source->info.key_click) {
			source->info.key_click(source->context.data, event,
					key_up);
		}
	}
}

486
static void activate_source(obs_source_t *source)
J
jp9000 已提交
487
{
488
	if (source->context.data && source->info.activate)
489
		source->info.activate(source->context.data);
490
	obs_source_dosignal(source, "source_activate", "activate");
J
jp9000 已提交
491 492
}

493
static void deactivate_source(obs_source_t *source)
J
jp9000 已提交
494
{
495
	if (source->context.data && source->info.deactivate)
496
		source->info.deactivate(source->context.data);
497
	obs_source_dosignal(source, "source_deactivate", "deactivate");
498
}
499

500
static void show_source(obs_source_t *source)
501
{
502
	if (source->context.data && source->info.show)
503
		source->info.show(source->context.data);
504
	obs_source_dosignal(source, "source_show", "show");
505 506
}

507
static void hide_source(obs_source_t *source)
508
{
509
	if (source->context.data && source->info.hide)
510
		source->info.hide(source->context.data);
511
	obs_source_dosignal(source, "source_hide", "hide");
512 513
}

514 515
static void activate_tree(obs_source_t *parent, obs_source_t *child,
		void *param)
516
{
J
jp9000 已提交
517
	if (os_atomic_inc_long(&child->activate_refs) == 1)
518
		activate_source(child);
J
jp9000 已提交
519 520 521

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
522 523
}

524
static void deactivate_tree(obs_source_t *parent, obs_source_t *child,
525 526
		void *param)
{
J
jp9000 已提交
527
	if (os_atomic_dec_long(&child->activate_refs) == 0)
528
		deactivate_source(child);
J
jp9000 已提交
529 530 531

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
532 533
}

534
static void show_tree(obs_source_t *parent, obs_source_t *child, void *param)
535
{
J
jp9000 已提交
536
	if (os_atomic_inc_long(&child->show_refs) == 1)
537 538 539 540 541 542
		show_source(child);

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

543
static void hide_tree(obs_source_t *parent, obs_source_t *child, void *param)
544
{
J
jp9000 已提交
545
	if (os_atomic_dec_long(&child->show_refs) == 0)
546 547 548 549 550 551
		hide_source(child);

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

552
void obs_source_activate(obs_source_t *source, enum view_type type)
553 554 555
{
	if (!source) return;

J
jp9000 已提交
556
	if (os_atomic_inc_long(&source->show_refs) == 1) {
557 558 559 560 561
		show_source(source);
		obs_source_enum_tree(source, show_tree, NULL);
	}

	if (type == MAIN_VIEW) {
J
jp9000 已提交
562
		if (os_atomic_inc_long(&source->activate_refs) == 1) {
563 564 565
			activate_source(source);
			obs_source_enum_tree(source, activate_tree, NULL);
		}
566 567 568
	}
}

569
void obs_source_deactivate(obs_source_t *source, enum view_type type)
570 571 572
{
	if (!source) return;

J
jp9000 已提交
573
	if (os_atomic_dec_long(&source->show_refs) == 0) {
574 575 576 577 578
		hide_source(source);
		obs_source_enum_tree(source, hide_tree, NULL);
	}

	if (type == MAIN_VIEW) {
J
jp9000 已提交
579
		if (os_atomic_dec_long(&source->activate_refs) == 0) {
580 581 582
			deactivate_source(source);
			obs_source_enum_tree(source, deactivate_tree, NULL);
		}
583
	}
J
jp9000 已提交
584 585
}

586
void obs_source_video_tick(obs_source_t *source, float seconds)
J
jp9000 已提交
587
{
J
jp9000 已提交
588 589
	if (!source) return;

590 591 592
	if (source->defer_update)
		obs_source_deferred_update(source);

J
jp9000 已提交
593 594
	/* reset the filter render texture information once every frame */
	if (source->filter_texrender)
595
		gs_texrender_reset(source->filter_texrender);
J
jp9000 已提交
596

597
	if (source->context.data && source->info.video_tick)
598
		source->info.video_tick(source->context.data, seconds);
599 600

	source->async_rendered = false;
J
jp9000 已提交
601 602
}

603
/* unless the value is 3+ hours worth of frames, this won't overflow */
J
jp9000 已提交
604
static inline uint64_t conv_frames_to_time(size_t frames)
605
{
J
jp9000 已提交
606
	const struct audio_output_info *info;
607
	info = audio_output_get_info(obs->audio.audio);
608 609 610

	return (uint64_t)frames * 1000000000ULL /
		(uint64_t)info->samples_per_sec;
611 612
}

613 614
/* maximum timestamp variance in nanoseconds */
#define MAX_TS_VAR          2000000000ULL
615

616 617
static inline void reset_audio_timing(obs_source_t *source, uint64_t timestamp,
		uint64_t os_time)
618 619
{
	source->timing_set    = true;
620
	source->timing_adjust = os_time - timestamp;
621
}
622

623
static inline void handle_ts_jump(obs_source_t *source, uint64_t expected,
624
		uint64_t ts, uint64_t diff, uint64_t os_time)
625
{
J
jp9000 已提交
626
	blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%"PRIu64"', "
627
	                "expected value %"PRIu64", input value %"PRIu64,
628
	                source->context.name, diff, expected, ts);
629 630

	/* if has video, ignore audio data until reset */
631
	if (!(source->info.output_flags & OBS_SOURCE_ASYNC))
632
		reset_audio_timing(source, ts, os_time);
633 634
}

635
static void source_signal_audio_data(obs_source_t *source,
636 637
		struct audio_data *in)
{
638
	struct calldata data;
639

640
	calldata_init(&data);
641

642 643
	calldata_set_ptr(&data, "source", source);
	calldata_set_ptr(&data, "data",   in);
644

645
	signal_handler_signal(source->context.signals, "audio_data", &data);
646

647
	calldata_free(&data);
648 649
}

650 651 652 653 654
static inline uint64_t uint64_diff(uint64_t ts1, uint64_t ts2)
{
	return (ts1 < ts2) ?  (ts2 - ts1) : (ts1 - ts2);
}

655
static void source_output_audio_line(obs_source_t *source,
656 657 658
		const struct audio_data *data)
{
	struct audio_data in = *data;
659
	uint64_t diff;
660
	uint64_t os_time = os_gettime_ns();
661

662 663 664 665 666
	/* detects 'directly' set timestamps as long as they're within
	 * a certain threshold */
	if (uint64_diff(in.timestamp, os_time) < MAX_TS_VAR) {
		source->timing_adjust = 0;
		source->timing_set = true;
667

668 669
	} else if (!source->timing_set) {
		reset_audio_timing(source, in.timestamp, os_time);
670

J
jp9000 已提交
671
	} else if (source->next_audio_ts_min != 0) {
672
		diff = uint64_diff(source->next_audio_ts_min, in.timestamp);
673

674
		/* smooth audio if within threshold */
675
		if (diff > MAX_TS_VAR)
676
			handle_ts_jump(source, source->next_audio_ts_min,
677
					in.timestamp, diff, os_time);
678
		else if (diff < TS_SMOOTHING_THRESHOLD)
679
			in.timestamp = source->next_audio_ts_min;
680 681
	}

682
	source->next_audio_ts_min = in.timestamp +
J
jp9000 已提交
683
		conv_frames_to_time(in.frames);
684

J
jp9000 已提交
685
	in.timestamp += source->timing_adjust + source->sync_offset;
686 687 688
	in.volume = source->base_volume * source->user_volume *
		source->present_volume * obs->audio.user_volume *
		obs->audio.present_volume;
689

690
	audio_line_output(source->audio_line, &in);
691
	source_signal_audio_data(source, &in);
692 693
}

694 695 696 697 698 699 700 701
enum convert_type {
	CONVERT_NONE,
	CONVERT_NV12,
	CONVERT_420,
	CONVERT_422_U,
	CONVERT_422_Y,
};

702
static inline enum convert_type get_convert_type(enum video_format format)
703
{
704
	switch (format) {
705 706 707 708 709 710 711 712 713 714 715
	case VIDEO_FORMAT_I420:
		return CONVERT_420;
	case VIDEO_FORMAT_NV12:
		return CONVERT_NV12;

	case VIDEO_FORMAT_YVYU:
	case VIDEO_FORMAT_YUY2:
		return CONVERT_422_Y;
	case VIDEO_FORMAT_UYVY:
		return CONVERT_422_U;

716
	case VIDEO_FORMAT_NONE:
717 718 719 720 721 722 723 724 725
	case VIDEO_FORMAT_RGBA:
	case VIDEO_FORMAT_BGRA:
	case VIDEO_FORMAT_BGRX:
		return CONVERT_NONE;
	}

	return CONVERT_NONE;
}

726
static inline bool set_packed422_sizes(struct obs_source *source,
727
		struct obs_source_frame *frame)
728 729
{
	source->async_convert_height = frame->height;
730 731 732 733 734 735
	source->async_convert_width  = frame->width / 2;
	source->async_texture_format = GS_BGRA;
	return true;
}

static inline bool set_planar420_sizes(struct obs_source *source,
736
		struct obs_source_frame *frame)
737 738 739 740 741 742 743 744 745 746
{
	uint32_t size = frame->width * frame->height;
	size += size/2;

	source->async_convert_width   = frame->width;
	source->async_convert_height  = (size / frame->width + 1) & 0xFFFFFFFE;
	source->async_texture_format  = GS_R8;
	source->async_plane_offset[0] = frame->width * frame->height;
	source->async_plane_offset[1] = source->async_plane_offset[0] +
		frame->width * frame->height / 4;
747 748 749
	return true;
}

J
jp9000 已提交
750 751 752 753 754 755 756 757 758 759 760 761 762
static inline bool set_nv12_sizes(struct obs_source *source,
		struct obs_source_frame *frame)
{
	uint32_t size = frame->width * frame->height;
	size += size/2;

	source->async_convert_width   = frame->width;
	source->async_convert_height  = (size / frame->width + 1) & 0xFFFFFFFE;
	source->async_texture_format  = GS_R8;
	source->async_plane_offset[0] = frame->width * frame->height;
	return true;
}

763
static inline bool init_gpu_conversion(struct obs_source *source,
764
		struct obs_source_frame *frame)
765 766 767 768 769 770 771
{
	switch (get_convert_type(frame->format)) {
		case CONVERT_422_Y:
		case CONVERT_422_U:
			return set_packed422_sizes(source, frame);

		case CONVERT_420:
772 773 774
			return set_planar420_sizes(source, frame);

		case CONVERT_NV12:
J
jp9000 已提交
775
			return set_nv12_sizes(source, frame);
776 777 778 779 780 781 782 783 784 785
			break;

		case CONVERT_NONE:
			assert(false && "No conversion requested");
			break;

	}
	return false;
}

786 787 788 789 790 791 792 793 794 795 796
static inline enum gs_color_format convert_video_format(
		enum video_format format)
{
	if (format == VIDEO_FORMAT_RGBA)
		return GS_RGBA;
	else if (format == VIDEO_FORMAT_BGRA)
		return GS_BGRA;

	return GS_BGRX;
}

797
static inline bool set_async_texture_size(struct obs_source *source,
798
		struct obs_source_frame *frame)
799 800 801 802 803 804 805 806 807 808 809
{
	enum convert_type prev, cur;
	prev = get_convert_type(source->async_format);
	cur  = get_convert_type(frame->format);
	if (source->async_texture) {
		if (source->async_width  == frame->width &&
		    source->async_height == frame->height &&
		    prev == cur)
			return true;
	}

810 811
	gs_texture_destroy(source->async_texture);
	gs_texrender_destroy(source->async_convert_texrender);
812 813 814 815 816 817
	source->async_convert_texrender = NULL;

	if (cur != CONVERT_NONE && init_gpu_conversion(source, frame)) {
		source->async_gpu_conversion = true;

		source->async_convert_texrender =
818
			gs_texrender_create(GS_BGRX, GS_ZS_NONE);
819

820
		source->async_texture = gs_texture_create(
821 822
				source->async_convert_width,
				source->async_convert_height,
823 824
				source->async_texture_format,
				1, NULL, GS_DYNAMIC);
825 826

	} else {
827 828
		enum gs_color_format format = convert_video_format(
				frame->format);
829 830
		source->async_gpu_conversion = false;

831
		source->async_texture = gs_texture_create(
832
				frame->width, frame->height,
833
				format, 1, NULL, GS_DYNAMIC);
834 835 836 837 838 839 840 841 842 843
	}

	if (!source->async_texture)
		return false;

	source->async_width  = frame->width;
	source->async_height = frame->height;
	return true;
}

844
static void upload_raw_frame(gs_texture_t *tex,
845
		const struct obs_source_frame *frame)
846 847 848 849
{
	switch (get_convert_type(frame->format)) {
		case CONVERT_422_U:
		case CONVERT_422_Y:
850
			gs_texture_set_image(tex, frame->data[0],
851 852 853 854
					frame->linesize[0], false);
			break;

		case CONVERT_420:
855
			gs_texture_set_image(tex, frame->data[0],
856 857 858 859
					frame->width, false);
			break;

		case CONVERT_NV12:
J
jp9000 已提交
860 861
			gs_texture_set_image(tex, frame->data[0],
					frame->width, false);
862 863 864 865 866 867 868 869 870 871 872 873
			break;

		case CONVERT_NONE:
			assert(false && "No conversion requested");
			break;
	}
}

static const char *select_conversion_technique(enum video_format format)
{
	switch (format) {
		case VIDEO_FORMAT_UYVY:
874
			return "UYVY_Reverse";
875 876 877 878 879 880 881 882

		case VIDEO_FORMAT_YUY2:
			return "YUY2_Reverse";

		case VIDEO_FORMAT_YVYU:
			return "YVYU_Reverse";

		case VIDEO_FORMAT_I420:
883 884 885
			return "I420_Reverse";

		case VIDEO_FORMAT_NV12:
J
jp9000 已提交
886
			return "NV12_Reverse";
887 888 889 890 891 892 893 894 895 896 897 898
			break;

		case VIDEO_FORMAT_BGRA:
		case VIDEO_FORMAT_BGRX:
		case VIDEO_FORMAT_RGBA:
		case VIDEO_FORMAT_NONE:
			assert(false && "No conversion requested");
			break;
	}
	return NULL;
}

899
static inline void set_eparam(gs_effect_t *effect, const char *name, float val)
900
{
901
	gs_eparam_t *param = gs_effect_get_param_by_name(effect, name);
902
	gs_effect_set_float(param, val);
903 904 905
}

static bool update_async_texrender(struct obs_source *source,
906
		const struct obs_source_frame *frame)
907
{
908 909
	gs_texture_t   *tex       = source->async_texture;
	gs_texrender_t *texrender = source->async_convert_texrender;
910

911
	gs_texrender_reset(texrender);
912 913 914 915 916 917

	upload_raw_frame(tex, frame);

	uint32_t cx = source->async_width;
	uint32_t cy = source->async_height;

918 919 920
	float convert_width  = (float)source->async_convert_width;
	float convert_height = (float)source->async_convert_height;

921 922
	gs_effect_t *conv = obs->video.conversion_effect;
	gs_technique_t *tech = gs_effect_get_technique(conv,
923 924
			select_conversion_technique(frame->format));

925
	if (!gs_texrender_begin(texrender, cx, cy))
926 927
		return false;

928 929
	gs_technique_begin(tech);
	gs_technique_begin_pass(tech, 0);
930

931
	gs_effect_set_texture(gs_effect_get_param_by_name(conv, "image"), tex);
932 933 934 935
	set_eparam(conv, "width",  (float)cx);
	set_eparam(conv, "height", (float)cy);
	set_eparam(conv, "width_i",  1.0f / cx);
	set_eparam(conv, "height_i", 1.0f / cy);
936
	set_eparam(conv, "width_d2",  cx * 0.5f);
937
	set_eparam(conv, "height_d2", cy * 0.5f);
938
	set_eparam(conv, "width_d2_i",  1.0f / (cx * 0.5f));
939
	set_eparam(conv, "height_d2_i", 1.0f / (cy * 0.5f));
940 941 942 943 944 945 946 947 948 949
	set_eparam(conv, "input_width",  convert_width);
	set_eparam(conv, "input_height", convert_height);
	set_eparam(conv, "input_width_i",  1.0f / convert_width);
	set_eparam(conv, "input_height_i", 1.0f / convert_height);
	set_eparam(conv, "input_width_i_d2",  (1.0f / convert_width)  * 0.5f);
	set_eparam(conv, "input_height_i_d2", (1.0f / convert_height) * 0.5f);
	set_eparam(conv, "u_plane_offset",
			(float)source->async_plane_offset[0]);
	set_eparam(conv, "v_plane_offset",
			(float)source->async_plane_offset[1]);
950 951 952 953 954

	gs_ortho(0.f, (float)cx, 0.f, (float)cy, -100.f, 100.f);

	gs_draw_sprite(tex, 0, cx, cy);

955 956
	gs_technique_end_pass(tech);
	gs_technique_end(tech);
957

958
	gs_texrender_end(texrender);
959 960 961 962

	return true;
}

963
static bool update_async_texture(struct obs_source *source,
964
		const struct obs_source_frame *frame)
965
{
966 967
	gs_texture_t      *tex       = source->async_texture;
	gs_texrender_t    *texrender = source->async_convert_texrender;
968
	enum convert_type type      = get_convert_type(frame->format);
969
	uint8_t           *ptr;
970 971
	uint32_t          linesize;

972 973 974
	source->async_format     = frame->format;
	source->async_flip       = frame->flip;
	source->async_full_range = frame->full_range;
975 976
	memcpy(source->async_color_matrix, frame->color_matrix,
			sizeof(frame->color_matrix));
977 978 979 980
	memcpy(source->async_color_range_min, frame->color_range_min,
			sizeof frame->color_range_min);
	memcpy(source->async_color_range_max, frame->color_range_max,
			sizeof frame->color_range_max);
981

982 983 984
	if (source->async_gpu_conversion && texrender)
		return update_async_texrender(source, frame);

985
	if (type == CONVERT_NONE) {
986
		gs_texture_set_image(tex, frame->data[0], frame->linesize[0],
987
				false);
988 989 990
		return true;
	}

991
	if (!gs_texture_map(tex, &ptr, &linesize))
992 993 994
		return false;

	if (type == CONVERT_420)
J
jp9000 已提交
995 996 997
		decompress_420((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
998 999

	else if (type == CONVERT_NV12)
J
jp9000 已提交
1000 1001 1002
		decompress_nv12((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
1003 1004

	else if (type == CONVERT_422_Y)
1005
		decompress_422(frame->data[0], frame->linesize[0],
J
jp9000 已提交
1006
				0, frame->height, ptr, linesize, true);
1007 1008

	else if (type == CONVERT_422_U)
1009
		decompress_422(frame->data[0], frame->linesize[0],
J
jp9000 已提交
1010
				0, frame->height, ptr, linesize, false);
1011

1012
	gs_texture_unmap(tex);
1013 1014 1015
	return true;
}

1016
static inline void obs_source_draw_texture(struct obs_source *source,
1017
		gs_effect_t *effect, float *color_matrix,
1018
		float const *color_range_min, float const *color_range_max)
1019
{
1020 1021
	gs_texture_t *tex = source->async_texture;
	gs_eparam_t  *param;
1022

1023
	if (source->async_convert_texrender)
1024
		tex = gs_texrender_get_texture(source->async_convert_texrender);
1025

P
Palana 已提交
1026
	if (color_range_min) {
1027
		size_t const size = sizeof(float) * 3;
1028 1029
		param = gs_effect_get_param_by_name(effect, "color_range_min");
		gs_effect_set_val(param, color_range_min, size);
P
Palana 已提交
1030
	}
1031

P
Palana 已提交
1032 1033
	if (color_range_max) {
		size_t const size = sizeof(float) * 3;
1034 1035
		param = gs_effect_get_param_by_name(effect, "color_range_max");
		gs_effect_set_val(param, color_range_max, size);
P
Palana 已提交
1036
	}
1037

P
Palana 已提交
1038
	if (color_matrix) {
1039 1040
		param = gs_effect_get_param_by_name(effect, "color_matrix");
		gs_effect_set_val(param, color_matrix, sizeof(float) * 16);
1041 1042
	}

1043 1044
	param = gs_effect_get_param_by_name(effect, "image");
	gs_effect_set_texture(param, tex);
1045

1046 1047
	gs_draw_sprite(tex, source->async_flip ? GS_FLIP_V : 0, 0, 0);
}
1048

1049 1050
static void obs_source_draw_async_texture(struct obs_source *source)
{
1051
	gs_effect_t    *effect        = gs_get_effect();
1052 1053 1054 1055
	bool           yuv           = format_is_yuv(source->async_format);
	bool           limited_range = yuv && !source->async_full_range;
	const char     *type         = yuv ? "DrawMatrix" : "Draw";
	bool           def_draw      = (!effect);
1056
	gs_technique_t *tech          = NULL;
1057 1058 1059

	if (def_draw) {
		effect = obs_get_default_effect();
1060 1061 1062
		tech = gs_effect_get_technique(effect, type);
		gs_technique_begin(tech);
		gs_technique_begin_pass(tech, 0);
1063 1064 1065
	}

	obs_source_draw_texture(source, effect,
1066 1067 1068
			yuv ? source->async_color_matrix : NULL,
			limited_range ? source->async_color_range_min : NULL,
			limited_range ? source->async_color_range_max : NULL);
1069 1070

	if (def_draw) {
1071 1072
		gs_technique_end_pass(tech);
		gs_technique_end(tech);
1073
	}
1074 1075
}

1076
static void obs_source_render_async_video(obs_source_t *source)
1077
{
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
	if (!source->async_rendered) {
		struct obs_source_frame *frame = obs_source_get_frame(source);

		source->async_rendered = true;
		if (frame) {
			if (!set_async_texture_size(source, frame))
				return;
			if (!update_async_texture(source, frame))
				return;
		}

		obs_source_release_frame(source, frame);
1090
	}
1091

1092 1093
	if (source->async_texture)
		obs_source_draw_async_texture(source);
1094 1095
}

1096
static inline void obs_source_render_filters(obs_source_t *source)
1097 1098 1099 1100 1101 1102
{
	source->rendering_filter = true;
	obs_source_video_render(source->filters.array[0]);
	source->rendering_filter = false;
}

1103
static inline void obs_source_default_render(obs_source_t *source,
J
jp9000 已提交
1104
		bool color_matrix)
1105
{
1106
	gs_effect_t    *effect     = obs->video.default_effect;
1107
	const char     *tech_name = color_matrix ? "DrawMatrix" : "Draw";
1108
	gs_technique_t *tech       = gs_effect_get_technique(effect, tech_name);
1109
	size_t         passes, i;
1110

1111
	passes = gs_technique_begin(tech);
1112
	for (i = 0; i < passes; i++) {
1113
		gs_technique_begin_pass(tech, i);
1114 1115
		if (source->context.data)
			source->info.video_render(source->context.data, effect);
1116
		gs_technique_end_pass(tech);
1117
	}
1118
	gs_technique_end(tech);
1119 1120
}

1121
static inline void obs_source_main_render(obs_source_t *source)
1122
{
1123 1124 1125
	uint32_t flags      = source->info.output_flags;
	bool color_matrix   = (flags & OBS_SOURCE_COLOR_MATRIX) != 0;
	bool custom_draw    = (flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
1126 1127
	bool default_effect = !source->filter_parent &&
	                      source->filters.num == 0 &&
1128
	                      !custom_draw;
1129 1130

	if (default_effect)
J
jp9000 已提交
1131
		obs_source_default_render(source, color_matrix);
1132
	else if (source->context.data)
1133
		source->info.video_render(source->context.data,
1134
				custom_draw ? NULL : gs_get_effect());
1135 1136
}

1137
void obs_source_video_render(obs_source_t *source)
J
jp9000 已提交
1138
{
1139
	if (!source_valid(source)) return;
J
jp9000 已提交
1140

1141 1142
	if (source->filters.num && !source->rendering_filter)
		obs_source_render_filters(source);
1143

1144 1145 1146 1147
	else if (source->info.video_render)
		obs_source_main_render(source);

	else if (source->filter_target)
1148 1149
		obs_source_video_render(source->filter_target);

1150
	else
1151
		obs_source_render_async_video(source);
J
jp9000 已提交
1152 1153
}

1154
uint32_t obs_source_get_width(const obs_source_t *source)
J
jp9000 已提交
1155
{
1156
	if (!source_valid(source)) return 0;
1157

1158 1159
	if (source->info.get_width)
		return source->info.get_width(source->context.data);
1160
	return source->async_width;
J
jp9000 已提交
1161 1162
}

1163
uint32_t obs_source_get_height(const obs_source_t *source)
J
jp9000 已提交
1164
{
1165
	if (!source_valid(source)) return 0;
1166

1167 1168
	if (source->info.get_height)
		return source->info.get_height(source->context.data);
1169
	return source->async_height;
J
jp9000 已提交
1170 1171
}

1172
obs_source_t *obs_filter_get_parent(const obs_source_t *filter)
1173
{
J
jp9000 已提交
1174
	return filter ? filter->filter_parent : NULL;
1175 1176
}

1177
obs_source_t *obs_filter_get_target(const obs_source_t *filter)
J
jp9000 已提交
1178
{
J
jp9000 已提交
1179
	return filter ? filter->filter_target : NULL;
J
jp9000 已提交
1180 1181
}

1182
void obs_source_filter_add(obs_source_t *source, obs_source_t *filter)
J
jp9000 已提交
1183
{
J
jp9000 已提交
1184 1185 1186
	if (!source || !filter)
		return;

1187 1188
	pthread_mutex_lock(&source->filter_mutex);

J
jp9000 已提交
1189
	if (da_find(source->filters, &filter, 0) != DARRAY_INVALID) {
J
jp9000 已提交
1190 1191 1192 1193 1194 1195
		blog(LOG_WARNING, "Tried to add a filter that was already "
		                  "present on the source");
		return;
	}

	if (source->filters.num) {
1196
		obs_source_t **back = da_end(source->filters);
J
jp9000 已提交
1197 1198 1199 1200
		(*back)->filter_target = filter;
	}

	da_push_back(source->filters, &filter);
1201 1202 1203 1204

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = source;
J
jp9000 已提交
1205 1206 1207
	filter->filter_target = source;
}

1208
void obs_source_filter_remove(obs_source_t *source, obs_source_t *filter)
J
jp9000 已提交
1209
{
1210 1211
	size_t idx;

J
jp9000 已提交
1212 1213 1214
	if (!source || !filter)
		return;

1215 1216 1217
	pthread_mutex_lock(&source->filter_mutex);

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
1218
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
1219 1220 1221
		return;

	if (idx > 0) {
1222
		obs_source_t *prev = source->filters.array[idx-1];
J
jp9000 已提交
1223 1224 1225 1226
		prev->filter_target = filter->filter_target;
	}

	da_erase(source->filters, idx);
1227 1228 1229 1230

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = NULL;
J
jp9000 已提交
1231 1232 1233
	filter->filter_target = NULL;
}

1234
void obs_source_filter_set_order(obs_source_t *source, obs_source_t *filter,
J
jp9000 已提交
1235
		enum obs_order_movement movement)
J
jp9000 已提交
1236
{
J
jp9000 已提交
1237 1238 1239 1240 1241 1242
	size_t idx, i;

	if (!source || !filter)
		return;

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
1243
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
1244 1245
		return;

J
jp9000 已提交
1246
	if (movement == OBS_ORDER_MOVE_UP) {
J
jp9000 已提交
1247 1248 1249 1250
		if (idx == source->filters.num-1)
			return;
		da_move_item(source->filters, idx, idx+1);

J
jp9000 已提交
1251
	} else if (movement == OBS_ORDER_MOVE_DOWN) {
J
jp9000 已提交
1252 1253 1254 1255
		if (idx == 0)
			return;
		da_move_item(source->filters, idx, idx-1);

J
jp9000 已提交
1256
	} else if (movement == OBS_ORDER_MOVE_TOP) {
J
jp9000 已提交
1257 1258 1259 1260
		if (idx == source->filters.num-1)
			return;
		da_move_item(source->filters, idx, source->filters.num-1);

J
jp9000 已提交
1261
	} else if (movement == OBS_ORDER_MOVE_BOTTOM) {
J
jp9000 已提交
1262 1263 1264 1265 1266
		if (idx == 0)
			return;
		da_move_item(source->filters, idx, 0);
	}

1267
	/* reorder filter targets, not the nicest way of dealing with things */
J
jp9000 已提交
1268
	for (i = 0; i < source->filters.num; i++) {
1269
		obs_source_t *next_filter = (i == source->filters.num-1) ?
J
jp9000 已提交
1270 1271 1272 1273 1274
			source : source->filters.array[idx+1];
		source->filters.array[i]->filter_target = next_filter;
	}
}

1275
obs_data_t *obs_source_get_settings(const obs_source_t *source)
J
jp9000 已提交
1276
{
J
jp9000 已提交
1277 1278
	if (!source) return NULL;

1279 1280
	obs_data_addref(source->context.settings);
	return source->context.settings;
J
jp9000 已提交
1281 1282
}

1283
static inline struct obs_source_frame *filter_async_video(obs_source_t *source,
1284
		struct obs_source_frame *in)
1285 1286 1287 1288
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
1289 1290

		if (filter->context.data && filter->info.filter_video) {
1291 1292
			in = filter->info.filter_video(filter->context.data,
					in);
1293 1294 1295 1296 1297 1298 1299 1300
			if (!in)
				return NULL;
		}
	}

	return in;
}

1301 1302
static inline void copy_frame_data_line(struct obs_source_frame *dst,
		const struct obs_source_frame *src, uint32_t plane, uint32_t y)
1303
{
1304 1305 1306 1307
	uint32_t pos_src = y * src->linesize[plane];
	uint32_t pos_dst = y * dst->linesize[plane];
	uint32_t bytes = dst->linesize[plane] < src->linesize[plane] ?
		dst->linesize[plane] : src->linesize[plane];
1308 1309 1310 1311

	memcpy(dst->data[plane] + pos_dst, src->data[plane] + pos_src, bytes);
}

1312 1313 1314
static inline void copy_frame_data_plane(struct obs_source_frame *dst,
		const struct obs_source_frame *src,
		uint32_t plane, uint32_t lines)
1315
{
1316
	if (dst->linesize[plane] != src->linesize[plane])
1317 1318 1319 1320
		for (uint32_t y = 0; y < lines; y++)
			copy_frame_data_line(dst, src, plane, y);
	else
		memcpy(dst->data[plane], src->data[plane],
1321
				dst->linesize[plane] * lines);
1322 1323
}

1324 1325
static void copy_frame_data(struct obs_source_frame *dst,
		const struct obs_source_frame *src)
1326 1327
{
	dst->flip         = src->flip;
1328
	dst->full_range   = src->full_range;
1329 1330
	dst->timestamp    = src->timestamp;
	memcpy(dst->color_matrix, src->color_matrix, sizeof(float) * 16);
1331 1332 1333 1334 1335
	if (!dst->full_range) {
		size_t const size = sizeof(float) * 3;
		memcpy(dst->color_range_min, src->color_range_min, size);
		memcpy(dst->color_range_max, src->color_range_max, size);
	}
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359

	switch (dst->format) {
	case VIDEO_FORMAT_I420:
		copy_frame_data_plane(dst, src, 0, dst->height);
		copy_frame_data_plane(dst, src, 1, dst->height/2);
		copy_frame_data_plane(dst, src, 2, dst->height/2);
		break;

	case VIDEO_FORMAT_NV12:
		copy_frame_data_plane(dst, src, 0, dst->height);
		copy_frame_data_plane(dst, src, 1, dst->height/2);
		break;

	case VIDEO_FORMAT_YVYU:
	case VIDEO_FORMAT_YUY2:
	case VIDEO_FORMAT_UYVY:
	case VIDEO_FORMAT_NONE:
	case VIDEO_FORMAT_RGBA:
	case VIDEO_FORMAT_BGRA:
	case VIDEO_FORMAT_BGRX:
		copy_frame_data_plane(dst, src, 0, dst->height);
	}
}

1360 1361
static inline struct obs_source_frame *cache_video(
		const struct obs_source_frame *frame)
1362
{
1363
	/* TODO: use an actual cache */
1364 1365
	struct obs_source_frame *new_frame = obs_source_frame_create(
			frame->format, frame->width, frame->height);
1366

1367
	copy_frame_data(new_frame, frame);
1368
	return new_frame;
1369 1370
}

1371
static bool ready_async_frame(obs_source_t *source, uint64_t sys_time);
1372 1373 1374 1375

static inline void cycle_frames(struct obs_source *source)
{
	if (source->video_frames.num && !source->activate_refs)
1376
		ready_async_frame(source, os_gettime_ns());
1377 1378
}

1379
void obs_source_output_video(obs_source_t *source,
1380
		const struct obs_source_frame *frame)
1381
{
J
jp9000 已提交
1382 1383 1384
	if (!source || !frame)
		return;

1385
	struct obs_source_frame *output = cache_video(frame);
1386 1387 1388 1389 1390

	pthread_mutex_lock(&source->filter_mutex);
	output = filter_async_video(source, output);
	pthread_mutex_unlock(&source->filter_mutex);

1391 1392
	if (output) {
		pthread_mutex_lock(&source->video_mutex);
1393
		cycle_frames(source);
1394 1395 1396
		da_push_back(source->video_frames, &output);
		pthread_mutex_unlock(&source->video_mutex);
	}
1397 1398
}

1399
static inline struct obs_audio_data *filter_async_audio(obs_source_t *source,
1400
		struct obs_audio_data *in)
1401 1402 1403 1404
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
1405 1406

		if (filter->context.data && filter->info.filter_audio) {
1407 1408
			in = filter->info.filter_audio(filter->context.data,
					in);
1409 1410 1411 1412 1413 1414 1415 1416
			if (!in)
				return NULL;
		}
	}

	return in;
}

1417
static inline void reset_resampler(obs_source_t *source,
1418
		const struct obs_source_audio *audio)
1419
{
J
jp9000 已提交
1420
	const struct audio_output_info *obs_info;
1421 1422
	struct resample_info output_info;

1423
	obs_info = audio_output_get_info(obs->audio.audio);
1424

1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448
	output_info.format           = obs_info->format;
	output_info.samples_per_sec  = obs_info->samples_per_sec;
	output_info.speakers         = obs_info->speakers;

	source->sample_info.format          = audio->format;
	source->sample_info.samples_per_sec = audio->samples_per_sec;
	source->sample_info.speakers        = audio->speakers;

	if (source->sample_info.samples_per_sec == obs_info->samples_per_sec &&
	    source->sample_info.format          == obs_info->format          &&
	    source->sample_info.speakers        == obs_info->speakers) {
		source->audio_failed = false;
		return;
	}

	audio_resampler_destroy(source->resampler);
	source->resampler = audio_resampler_create(&output_info,
			&source->sample_info);

	source->audio_failed = source->resampler == NULL;
	if (source->resampler == NULL)
		blog(LOG_ERROR, "creation of resampler failed");
}

J
jp9000 已提交
1449
static void copy_audio_data(obs_source_t *source,
J
jp9000 已提交
1450
		const uint8_t *const data[], uint32_t frames, uint64_t ts)
1451
{
1452 1453
	size_t planes    = audio_output_get_planes(obs->audio.audio);
	size_t blocksize = audio_output_get_block_size(obs->audio.audio);
1454 1455
	size_t size      = (size_t)frames * blocksize;
	bool   resize    = source->audio_storage_size < size;
1456

J
jp9000 已提交
1457 1458
	source->audio_data.frames    = frames;
	source->audio_data.timestamp = ts;
1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471

	for (size_t i = 0; i < planes; i++) {
		/* ensure audio storage capacity */
		if (resize) {
			bfree(source->audio_data.data[i]);
			source->audio_data.data[i] = bmalloc(size);
		}

		memcpy(source->audio_data.data[i], data[i], size);
	}

	if (resize)
		source->audio_storage_size = size;
1472 1473
}

1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519
/* TODO: SSE optimization */
static void downmix_to_mono_planar(struct obs_source *source, uint32_t frames)
{
	uint32_t channels = get_audio_channels(source->sample_info.speakers);
	const float channels_i = 1.0f / (float)channels;
	float **data = (float**)source->audio_data.data;

	for (uint32_t channel = 1; channel < channels; channel++) {
		for (uint32_t frame = 0; frame < frames; frame++)
			data[0][frame] += data[channel][frame];
	}

	for (uint32_t frame = 0; frame < frames; frame++)
		data[0][frame] *= channels_i;

	for (uint32_t channel = 1; channel < channels; channel++) {
		for (uint32_t frame = 0; frame < frames; frame++)
			data[channel][frame] = data[0][frame];
	}
}

static void downmix_to_mono_interleaved(struct obs_source *source,
		uint32_t frames)
{
	uint32_t channels = get_audio_channels(source->sample_info.speakers);
	const float channels_i = 1.0f / (float)channels;
	float *data = (float*)source->audio_data.data[0];

	for (uint32_t frame = 0; frame < frames; frame++) {
		uint32_t pos = frame * channels;

		for (uint32_t channel = 1; channel < channels; channel++)
			data[pos] += data[pos + channel];
	}

	for (uint32_t frame = 0; frame < frames; frame++)
		data[frame * channels] *= channels_i;

	for (uint32_t frame = 0; frame < frames; frame++) {
		uint32_t pos = frame * channels;

		for (uint32_t channel = 1; channel < channels; channel++)
			data[pos + channel] = data[pos];
	}
}

1520
/* resamples/remixes new audio to the designated main audio output format */
1521
static void process_audio(obs_source_t *source,
1522
		const struct obs_source_audio *audio)
1523
{
1524 1525
	uint32_t frames = audio->frames;

1526 1527 1528 1529 1530 1531 1532 1533 1534
	if (source->sample_info.samples_per_sec != audio->samples_per_sec ||
	    source->sample_info.format          != audio->format          ||
	    source->sample_info.speakers        != audio->speakers)
		reset_resampler(source, audio);

	if (source->audio_failed)
		return;

	if (source->resampler) {
J
jp9000 已提交
1535
		uint8_t  *output[MAX_AV_PLANES];
1536 1537
		uint64_t offset;

1538 1539 1540 1541 1542
		memset(output, 0, sizeof(output));

		audio_resampler_resample(source->resampler,
				output, &frames, &offset,
				audio->data, audio->frames);
1543

J
jp9000 已提交
1544
		copy_audio_data(source, (const uint8_t *const *)output, frames,
1545 1546 1547 1548 1549
				audio->timestamp - offset);
	} else {
		copy_audio_data(source, audio->data, audio->frames,
				audio->timestamp);
	}
1550 1551 1552 1553 1554 1555 1556

	if ((source->flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0) {
		if (is_audio_planar(source->sample_info.format))
			downmix_to_mono_planar(source, frames);
		else
			downmix_to_mono_interleaved(source, frames);
	}
1557 1558
}

1559
void obs_source_output_audio(obs_source_t *source,
1560
		const struct obs_source_audio *audio)
1561
{
J
jp9000 已提交
1562
	uint32_t flags;
1563
	struct obs_audio_data *output;
1564

J
jp9000 已提交
1565 1566 1567 1568
	if (!source || !audio)
		return;

	flags = source->info.output_flags;
1569
	process_audio(source, audio);
1570 1571

	pthread_mutex_lock(&source->filter_mutex);
1572
	output = filter_async_audio(source, &source->audio_data);
1573 1574

	if (output) {
1575
		bool async = (flags & OBS_SOURCE_ASYNC) != 0;
J
jp9000 已提交
1576

1577 1578
		pthread_mutex_lock(&source->audio_mutex);

1579 1580
		/* wait for video to start before outputting any audio so we
		 * have a base for sync */
1581
		if (source->timing_set || !async) {
1582
			struct audio_data data;
1583

J
jp9000 已提交
1584
			for (int i = 0; i < MAX_AV_PLANES; i++)
1585 1586
				data.data[i] = output->data[i];

1587 1588 1589
			data.frames    = output->frames;
			data.timestamp = output->timestamp;
			source_output_audio_line(source, &data);
1590 1591 1592 1593 1594 1595 1596 1597
		}

		pthread_mutex_unlock(&source->audio_mutex);
	}

	pthread_mutex_unlock(&source->filter_mutex);
}

1598
static inline bool frame_out_of_bounds(const obs_source_t *source, uint64_t ts)
1599
{
J
jp9000 已提交
1600
	if (ts < source->last_frame_ts)
1601
		return ((source->last_frame_ts - ts) > MAX_TS_VAR);
J
jp9000 已提交
1602
	else
1603
		return ((ts - source->last_frame_ts) > MAX_TS_VAR);
1604 1605
}

J
jp9000 已提交
1606 1607
/* #define DEBUG_ASYNC_FRAMES 1 */

1608
static bool ready_async_frame(obs_source_t *source, uint64_t sys_time)
1609
{
1610 1611
	struct obs_source_frame *next_frame = source->video_frames.array[0];
	struct obs_source_frame *frame      = NULL;
1612 1613 1614 1615
	uint64_t sys_offset = sys_time - source->last_sys_timestamp;
	uint64_t frame_time = next_frame->timestamp;
	uint64_t frame_offset = 0;

1616
	if ((source->flags & OBS_SOURCE_FLAG_UNBUFFERED) != 0) {
J
jp9000 已提交
1617 1618 1619 1620 1621 1622 1623 1624 1625
		while (source->video_frames.num > 1) {
			da_erase(source->video_frames, 0);
			obs_source_frame_destroy(next_frame);
			next_frame = source->video_frames.array[0];
		}

		return true;
	}

J
jp9000 已提交
1626 1627 1628 1629 1630 1631 1632 1633 1634
#if DEBUG_ASYNC_FRAMES
	blog(LOG_DEBUG, "source->last_frame_ts: %llu, frame_time: %llu, "
			"sys_offset: %llu, frame_offset: %llu, "
			"number of frames: %lu",
			source->last_frame_ts, frame_time, sys_offset,
			frame_time - source->last_frame_ts,
			(unsigned long)source->video_frames.num);
#endif

1635 1636
	/* account for timestamp invalidation */
	if (frame_out_of_bounds(source, frame_time)) {
J
jp9000 已提交
1637 1638 1639
#if DEBUG_ASYNC_FRAMES
		blog(LOG_DEBUG, "timing jump");
#endif
1640
		source->last_frame_ts = next_frame->timestamp;
J
jp9000 已提交
1641
		return true;
1642 1643
	} else {
		frame_offset = frame_time - source->last_frame_ts;
J
jp9000 已提交
1644
		source->last_frame_ts += sys_offset;
1645 1646
	}

J
jp9000 已提交
1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666
	while (source->last_frame_ts > next_frame->timestamp) {

		/* this tries to reduce the needless frame duplication, also
		 * helps smooth out async rendering to frame boundaries.  In
		 * other words, tries to keep the framerate as smooth as
		 * possible */
		if ((source->last_frame_ts - next_frame->timestamp) < 1000000)
			break;

		if (frame)
			da_erase(source->video_frames, 0);

#if DEBUG_ASYNC_FRAMES
		blog(LOG_DEBUG, "new frame, "
				"source->last_frame_ts: %llu, "
				"next_frame->timestamp: %llu",
				source->last_frame_ts,
				next_frame->timestamp);
#endif

1667
		obs_source_frame_destroy(frame);
1668

1669 1670 1671
		if (source->video_frames.num == 1)
			return true;

1672
		frame = next_frame;
J
jp9000 已提交
1673
		next_frame = source->video_frames.array[1];
1674 1675

		/* more timestamp checking and compensating */
1676
		if ((next_frame->timestamp - frame_time) > MAX_TS_VAR) {
J
jp9000 已提交
1677 1678 1679
#if DEBUG_ASYNC_FRAMES
			blog(LOG_DEBUG, "timing jump");
#endif
1680 1681 1682 1683 1684 1685 1686 1687
			source->last_frame_ts =
				next_frame->timestamp - frame_offset;
		}

		frame_time   = next_frame->timestamp;
		frame_offset = frame_time - source->last_frame_ts;
	}

J
jp9000 已提交
1688 1689 1690 1691
#if DEBUG_ASYNC_FRAMES
	if (!frame)
		blog(LOG_DEBUG, "no frame!");
#endif
1692

1693 1694 1695
	return frame != NULL;
}

1696
static inline struct obs_source_frame *get_closest_frame(obs_source_t *source,
1697 1698
		uint64_t sys_time)
{
1699
	if (ready_async_frame(source, sys_time)) {
1700
		struct obs_source_frame *frame = source->video_frames.array[0];
1701 1702 1703 1704 1705
		da_erase(source->video_frames, 0);
		return frame;
	}

	return NULL;
1706 1707
}

1708
/*
1709 1710
 * Ensures that cached frames are displayed on time.  If multiple frames
 * were cached between renders, then releases the unnecessary frames and uses
1711 1712
 * the frame with the closest timing to ensure sync.  Also ensures that timing
 * with audio is synchronized.
1713
 */
1714
struct obs_source_frame *obs_source_get_frame(obs_source_t *source)
J
jp9000 已提交
1715
{
1716
	struct obs_source_frame *frame = NULL;
1717
	uint64_t sys_time;
1718

J
jp9000 已提交
1719 1720 1721
	if (!source)
		return NULL;

1722 1723
	pthread_mutex_lock(&source->video_mutex);

J
jp9000 已提交
1724 1725
	sys_time = os_gettime_ns();

1726 1727 1728
	if (!source->video_frames.num)
		goto unlock;

1729 1730
	if (!source->last_frame_ts) {
		frame = source->video_frames.array[0];
1731 1732
		da_erase(source->video_frames, 0);

1733
		source->last_frame_ts = frame->timestamp;
1734
	} else {
1735
		frame = get_closest_frame(source, sys_time);
J
jp9000 已提交
1736 1737 1738 1739 1740 1741
	}

	/* reset timing to current system time */
	if (frame) {
		source->timing_adjust = sys_time - frame->timestamp;
		source->timing_set = true;
1742 1743
	}

J
jp9000 已提交
1744
unlock:
1745 1746 1747
	source->last_sys_timestamp = sys_time;

	pthread_mutex_unlock(&source->video_mutex);
1748

1749
	if (frame)
1750 1751
		obs_source_addref(source);

1752
	return frame;
J
jp9000 已提交
1753 1754
}

1755
void obs_source_release_frame(obs_source_t *source,
1756
		struct obs_source_frame *frame)
J
jp9000 已提交
1757
{
J
jp9000 已提交
1758
	if (source && frame) {
1759
		obs_source_frame_destroy(frame);
1760 1761
		obs_source_release(source);
	}
J
jp9000 已提交
1762
}
1763

1764
const char *obs_source_get_name(const obs_source_t *source)
1765
{
1766
	return source ? source->context.name : NULL;
1767 1768
}

1769
void obs_source_set_name(obs_source_t *source, const char *name)
1770
{
J
jp9000 已提交
1771
	if (!source) return;
J
jp9000 已提交
1772 1773 1774 1775 1776 1777 1778

	if (!name || !*name || strcmp(name, source->context.name) != 0) {
		struct calldata data;
		char *prev_name = bstrdup(source->context.name);
		obs_context_data_setname(&source->context, name);

		calldata_init(&data);
1779 1780 1781
		calldata_set_ptr(&data, "source", source);
		calldata_set_string(&data, "new_name", source->context.name);
		calldata_set_string(&data, "prev_name", prev_name);
J
jp9000 已提交
1782 1783 1784 1785 1786
		signal_handler_signal(obs->signals, "source_rename", &data);
		signal_handler_signal(source->context.signals, "rename", &data);
		calldata_free(&data);
		bfree(prev_name);
	}
1787 1788
}

1789
enum obs_source_type obs_source_get_type(const obs_source_t *source)
1790
{
J
jp9000 已提交
1791 1792
	return source ? source->info.type : OBS_SOURCE_TYPE_INPUT;
}
J
jp9000 已提交
1793

1794
const char *obs_source_get_id(const obs_source_t *source)
J
jp9000 已提交
1795 1796
{
	return source ? source->info.id : NULL;
1797
}
1798

1799 1800
static inline void render_filter_bypass(obs_source_t *target,
		gs_effect_t *effect, bool use_matrix)
1801
{
J
jp9000 已提交
1802
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1803
	gs_technique_t *tech    = gs_effect_get_technique(effect, tech_name);
1804 1805
	size_t      passes, i;

1806
	passes = gs_technique_begin(tech);
1807
	for (i = 0; i < passes; i++) {
1808
		gs_technique_begin_pass(tech, i);
1809
		obs_source_video_render(target);
1810
		gs_technique_end_pass(tech);
1811
	}
1812
	gs_technique_end(tech);
1813 1814
}

1815
static inline void render_filter_tex(gs_texture_t *tex, gs_effect_t *effect,
J
jp9000 已提交
1816
		uint32_t width, uint32_t height, bool use_matrix)
1817
{
J
jp9000 已提交
1818
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1819 1820
	gs_technique_t *tech    = gs_effect_get_technique(effect, tech_name);
	gs_eparam_t    *image   = gs_effect_get_param_by_name(effect, "image");
1821 1822
	size_t      passes, i;

1823
	gs_effect_set_texture(image, tex);
1824

1825
	passes = gs_technique_begin(tech);
1826
	for (i = 0; i < passes; i++) {
1827
		gs_technique_begin_pass(tech, i);
1828
		gs_draw_sprite(tex, width, height, 0);
1829
		gs_technique_end_pass(tech);
1830
	}
1831
	gs_technique_end(tech);
1832 1833
}

1834
void obs_source_process_filter(obs_source_t *filter, gs_effect_t *effect,
J
jp9000 已提交
1835
		uint32_t width, uint32_t height, enum gs_color_format format,
1836
		enum obs_allow_direct_render allow_direct)
1837
{
1838
	obs_source_t *target, *parent;
J
jp9000 已提交
1839 1840 1841 1842 1843 1844
	uint32_t     target_flags, parent_flags;
	int          cx, cy;
	bool         use_matrix, expects_def, can_directly;

	if (!filter) return;

1845 1846
	target       = obs_filter_get_target(filter);
	parent       = obs_filter_get_parent(filter);
J
jp9000 已提交
1847 1848
	target_flags = target->info.output_flags;
	parent_flags = parent->info.output_flags;
1849 1850
	cx           = obs_source_get_width(target);
	cy           = obs_source_get_height(target);
J
jp9000 已提交
1851 1852
	use_matrix   = !!(target_flags & OBS_SOURCE_COLOR_MATRIX);
	expects_def  = !(parent_flags & OBS_SOURCE_CUSTOM_DRAW);
1853
	can_directly = allow_direct == OBS_ALLOW_DIRECT_RENDERING;
1854 1855 1856 1857 1858

	/* if the parent does not use any custom effects, and this is the last
	 * filter in the chain for the parent, then render the parent directly
	 * using the filter effect instead of rendering to texture to reduce
	 * the total number of passes */
1859
	if (can_directly && expects_def && target == parent) {
J
jp9000 已提交
1860
		render_filter_bypass(target, effect, use_matrix);
1861 1862 1863
		return;
	}

J
jp9000 已提交
1864
	if (!filter->filter_texrender)
1865
		filter->filter_texrender = gs_texrender_create(format,
J
jp9000 已提交
1866 1867
				GS_ZS_NONE);

1868
	if (gs_texrender_begin(filter->filter_texrender, cx, cy)) {
1869
		gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
1870
		if (expects_def && parent == target)
J
jp9000 已提交
1871
			obs_source_default_render(parent, use_matrix);
1872 1873
		else
			obs_source_video_render(target);
1874
		gs_texrender_end(filter->filter_texrender);
1875 1876 1877 1878
	}

	/* --------------------------- */

1879
	render_filter_tex(gs_texrender_get_texture(filter->filter_texrender),
J
jp9000 已提交
1880
			effect, width, height, use_matrix);
1881
}
1882

1883
signal_handler_t *obs_source_get_signal_handler(const obs_source_t *source)
1884
{
1885
	return source ? source->context.signals : NULL;
1886 1887
}

1888
proc_handler_t *obs_source_get_proc_handler(const obs_source_t *source)
1889
{
1890
	return source ? source->context.procs : NULL;
1891
}
J
jp9000 已提交
1892

1893
void obs_source_set_volume(obs_source_t *source, float volume)
J
jp9000 已提交
1894
{
J
jp9000 已提交
1895 1896
	if (source) {
		struct calldata data = {0};
1897 1898
		calldata_set_ptr(&data, "source", source);
		calldata_set_float(&data, "volume", volume);
J
jp9000 已提交
1899

1900
		signal_handler_signal(source->context.signals, "volume", &data);
1901
		signal_handler_signal(obs->signals, "source_volume", &data);
J
jp9000 已提交
1902

1903
		volume = (float)calldata_float(&data, "volume");
J
jp9000 已提交
1904 1905
		calldata_free(&data);

J
jp9000 已提交
1906
		source->user_volume = volume;
J
jp9000 已提交
1907
	}
J
jp9000 已提交
1908 1909
}

1910
static void set_tree_preset_vol(obs_source_t *parent, obs_source_t *child,
J
jp9000 已提交
1911 1912 1913 1914 1915 1916 1917 1918
		void *param)
{
	float *vol = param;
	child->present_volume = *vol;

	UNUSED_PARAMETER(parent);
}

1919
void obs_source_set_present_volume(obs_source_t *source, float volume)
J
jp9000 已提交
1920
{
1921
	if (source)
J
jp9000 已提交
1922
		source->present_volume = volume;
J
jp9000 已提交
1923 1924
}

1925
float obs_source_get_volume(const obs_source_t *source)
J
jp9000 已提交
1926
{
J
jp9000 已提交
1927
	return source ? source->user_volume : 0.0f;
J
jp9000 已提交
1928 1929
}

1930
float obs_source_get_present_volume(const obs_source_t *source)
J
jp9000 已提交
1931
{
J
jp9000 已提交
1932 1933 1934
	return source ? source->present_volume : 0.0f;
}

1935
void obs_source_set_sync_offset(obs_source_t *source, int64_t offset)
J
jp9000 已提交
1936
{
J
jp9000 已提交
1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948
	if (source) {
		struct calldata data = {0};

		calldata_set_ptr(&data, "source", source);
		calldata_set_int(&data, "offset", offset);

		signal_handler_signal(source->context.signals, "audio_sync",
				&data);

		source->sync_offset = calldata_int(&data, "offset");
		calldata_free(&data);
	}
J
jp9000 已提交
1949 1950
}

1951
int64_t obs_source_get_sync_offset(const obs_source_t *source)
J
jp9000 已提交
1952 1953
{
	return source ? source->sync_offset : 0;
J
jp9000 已提交
1954
}
1955 1956 1957 1958 1959 1960

struct source_enum_data {
	obs_source_enum_proc_t enum_callback;
	void *param;
};

1961
static void enum_source_tree_callback(obs_source_t *parent, obs_source_t *child,
1962 1963 1964 1965
		void *param)
{
	struct source_enum_data *data = param;

J
jp9000 已提交
1966 1967
	if (child->info.enum_sources) {
		if (child->context.data) {
1968 1969
			child->info.enum_sources(child->context.data,
					enum_source_tree_callback, data);
J
jp9000 已提交
1970
		}
1971 1972 1973 1974 1975
	}

	data->enum_callback(parent, child, data->param);
}

1976
void obs_source_enum_sources(obs_source_t *source,
1977 1978 1979
		obs_source_enum_proc_t enum_callback,
		void *param)
{
J
jp9000 已提交
1980
	if (!source_valid(source) || !source->info.enum_sources)
1981 1982 1983 1984
		return;

	obs_source_addref(source);

1985
	source->info.enum_sources(source->context.data, enum_callback, param);
1986 1987 1988 1989

	obs_source_release(source);
}

1990
void obs_source_enum_tree(obs_source_t *source,
1991 1992 1993 1994 1995
		obs_source_enum_proc_t enum_callback,
		void *param)
{
	struct source_enum_data data = {enum_callback, param};

J
jp9000 已提交
1996
	if (!source_valid(source) || !source->info.enum_sources)
1997 1998 1999 2000
		return;

	obs_source_addref(source);

2001 2002
	source->info.enum_sources(source->context.data,
			enum_source_tree_callback,
2003 2004 2005 2006
			&data);

	obs_source_release(source);
}
2007

J
jp9000 已提交
2008 2009 2010 2011 2012 2013 2014
struct descendant_info {
	bool exists;
	obs_source_t *target;
};

static void check_descendant(obs_source_t *parent, obs_source_t *child,
		void *param)
2015
{
J
jp9000 已提交
2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028
	struct descendant_info *info = param;
	if (child == info->target || parent == info->target)
		info->exists = true;
}

bool obs_source_add_child(obs_source_t *parent, obs_source_t *child)
{
	struct descendant_info info = {false, child};
	if (!parent || !child) return false;

	obs_source_enum_tree(parent, check_descendant, &info);
	if (info.exists)
		return false;
2029

2030 2031 2032 2033 2034
	for (int i = 0; i < parent->show_refs; i++) {
		enum view_type type;
		type = (i < parent->activate_refs) ? MAIN_VIEW : AUX_VIEW;
		obs_source_activate(child, type);
	}
J
jp9000 已提交
2035 2036

	return true;
2037 2038
}

2039
void obs_source_remove_child(obs_source_t *parent, obs_source_t *child)
2040 2041 2042
{
	if (!parent || !child) return;

2043 2044 2045 2046 2047
	for (int i = 0; i < parent->show_refs; i++) {
		enum view_type type;
		type = (i < parent->activate_refs) ? MAIN_VIEW : AUX_VIEW;
		obs_source_deactivate(child, type);
	}
2048
}
J
jp9000 已提交
2049

2050
void obs_source_save(obs_source_t *source)
2051
{
2052
	if (!source_valid(source) || !source->info.save) return;
2053 2054 2055
	source->info.save(source->context.data, source->context.settings);
}

2056
void obs_source_load(obs_source_t *source)
2057
{
2058
	if (!source_valid(source) || !source->info.load) return;
2059 2060
	source->info.load(source->context.data, source->context.settings);
}
J
jp9000 已提交
2061

J
jp9000 已提交
2062 2063 2064 2065 2066
bool obs_source_active(const obs_source_t *source)
{
	return source->activate_refs != 0;
}

J
jp9000 已提交
2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092
static inline void signal_flags_updated(obs_source_t *source)
{
	struct calldata data = {0};

	calldata_set_ptr(&data, "source", source);
	calldata_set_int(&data, "flags", source->flags);

	signal_handler_signal(source->context.signals, "update_flags", &data);

	calldata_free(&data);
}

void obs_source_set_flags(obs_source_t *source, uint32_t flags)
{
	if (!source) return;

	if (flags != source->flags) {
		source->flags = flags;
		signal_flags_updated(source);
	}
}

uint32_t obs_source_get_flags(const obs_source_t *source)
{
	return source ? source->flags : 0;
}
J
jp9000 已提交
2093 2094 2095 2096 2097

void obs_source_draw_set_color_matrix(const struct matrix4 *color_matrix,
		const struct vec3 *color_range_min,
		const struct vec3 *color_range_max)
{
2098 2099 2100 2101 2102 2103
	struct vec3 color_range_min_def;
	struct vec3 color_range_max_def;

	vec3_set(&color_range_min_def, 0.0f, 0.0f, 0.0f);
	vec3_set(&color_range_max_def, 1.0f, 1.0f, 1.0f);

J
jp9000 已提交
2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164
	gs_effect_t *effect = gs_get_effect();
	gs_eparam_t *matrix;
	gs_eparam_t *range_min;
	gs_eparam_t *range_max;

	if (!effect) {
		blog(LOG_WARNING, "obs_source_draw_set_color_matrix: NULL "
				"effect");
		return;
	}

	if (!color_matrix) {
		blog(LOG_WARNING, "obs_source_draw_set_color_matrix: NULL "
				"color_matrix");
		return;
	}

	if (!color_range_min)
		color_range_min = &color_range_min_def;
	if (!color_range_max)
		color_range_max = &color_range_max_def;

	matrix = gs_effect_get_param_by_name(effect, "color_matrix");
	range_min = gs_effect_get_param_by_name(effect, "color_range_min");
	range_max = gs_effect_get_param_by_name(effect, "color_range_max");

	gs_effect_set_matrix4(matrix, color_matrix);
	gs_effect_set_val(range_min, color_range_min, sizeof(float)*3);
	gs_effect_set_val(range_max, color_range_max, sizeof(float)*3);
}

void obs_source_draw(gs_texture_t *texture, int x, int y, uint32_t cx,
		uint32_t cy, bool flip)
{
	gs_effect_t *effect = gs_get_effect();
	bool change_pos = (x != 0 || y != 0);
	gs_eparam_t *image;

	if (!effect) {
		blog(LOG_WARNING, "obs_source_draw: NULL effect");
		return;
	}

	if (!texture) {
		blog(LOG_WARNING, "obs_source_draw: NULL texture");
		return;
	}

	image = gs_effect_get_param_by_name(effect, "image");
	gs_effect_set_texture(image, texture);

	if (change_pos) {
		gs_matrix_push();
		gs_matrix_translate3f((float)x, (float)y, 0.0f);
	}

	gs_draw_sprite(texture, flip ? GS_FLIP_V : 0, cx, cy);

	if (change_pos)
		gs_matrix_pop();
}
2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228

static inline float get_transition_volume(obs_source_t *source,
		obs_source_t *child)
{
	if (source && child && source->info.get_transition_volume)
		return source->info.get_transition_volume(source->context.data,
				child);
	return 0.0f;
}

static float obs_source_get_target_volume_refs(obs_source_t *source,
		obs_source_t *target, int refs);

struct base_vol_enum_info {
	obs_source_t *target;
	float vol;
};

static void get_transition_child_vol(obs_source_t *parent, obs_source_t *child,
		void *param)
{
	struct base_vol_enum_info *info = param;
	float vol = obs_source_get_target_volume(child, info->target);

	info->vol += vol * get_transition_volume(parent, child);
}

static void get_source_base_vol(obs_source_t *parent, obs_source_t *child,
		void *param)
{
	struct base_vol_enum_info *info = param;
	float vol = obs_source_get_target_volume(child, info->target);

	if (vol > info->vol)
		info->vol = vol;

	UNUSED_PARAMETER(parent);
}

/*
 * This traverses a source tree for any references to a particular source.
 * If the source is found, it'll just return 1.0.  However, if the source
 * exists within some transition somewhere, the transition source will be able
 * to control what the volume of the source will be.  If the source is also
 * active outside the transition, then it'll just use 1.0.
 */
float obs_source_get_target_volume(obs_source_t *source, obs_source_t *target)
{
	struct base_vol_enum_info info = {target, 0.0f};
	bool transition = source->info.type == OBS_SOURCE_TYPE_TRANSITION;

	if (source == target)
		return 1.0f;

	if (source->info.enum_sources) {
		source->info.enum_sources(source->context.data,
				transition ?
					get_transition_child_vol :
					get_source_base_vol,
				&info);
	}

	return info.vol;
}