obs-source.c 55.1 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)",
85
	"void audio_data(ptr source, ptr data)",
86 87 88
	NULL
};

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

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

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

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

118 119 120 121 122 123
	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 已提交
124

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

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

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

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

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

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

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

172
	if (!obs_source_init_context(source, settings, name))
173 174
		goto fail;

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

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

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

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

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

199 200
void obs_source_frame_init(struct obs_source_frame *frame,
		enum video_format format, uint32_t width, uint32_t height)
201
{
202
	struct video_frame vid_frame;
J
jp9000 已提交
203 204 205 206

	if (!frame)
		return;

207
	video_frame_init(&vid_frame, format, width, height);
208 209 210
	frame->format = format;
	frame->width  = width;
	frame->height = height;
211

212 213 214
	for (size_t i = 0; i < MAX_AV_PLANES; i++) {
		frame->data[i]     = vid_frame.data[i];
		frame->linesize[i] = vid_frame.linesize[i];
215 216 217
	}
}

218
void obs_source_destroy(struct obs_source *source)
J
jp9000 已提交
219
{
220
	size_t i;
221

J
jp9000 已提交
222 223 224
	if (!source)
		return;

225 226
	obs_context_data_remove(&source->context);

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

229
	obs_source_dosignal(source, "source_destroy", "destroy");
230

231
	if (source->context.data) {
232
		source->info.destroy(source->context.data);
233 234
		source->context.data = NULL;
	}
235

236 237
	if (source->filter_parent)
		obs_source_filter_remove(source->filter_parent, source);
238

239 240
	for (i = 0; i < source->filters.num; i++)
		obs_source_release(source->filters.array[i]);
241

242
	for (i = 0; i < source->video_frames.num; i++)
243
		obs_source_frame_destroy(source->video_frames.array[i]);
244

245 246 247 248
	gs_enter_context(obs->video.graphics);
	gs_texrender_destroy(source->async_convert_texrender);
	gs_texture_destroy(source->async_texture);
	gs_leave_context();
J
jp9000 已提交
249

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

253 254 255
	audio_line_destroy(source->audio_line);
	audio_resampler_destroy(source->resampler);

256
	gs_texrender_destroy(source->filter_texrender);
257 258 259 260 261
	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);
262
	obs_context_data_free(&source->context);
263

264 265 266
	if (source->owns_info_id)
		bfree((void*)source->info.id);

267 268 269
	bfree(source);
}

270
void obs_source_addref(obs_source_t *source)
271
{
P
Palana 已提交
272
	if (source)
J
jp9000 已提交
273
		os_atomic_inc_long(&source->refs);
274 275
}

276
void obs_source_release(obs_source_t *source)
277
{
P
Palana 已提交
278 279
	if (!source)
		return;
280

J
jp9000 已提交
281
	if (os_atomic_dec_long(&source->refs) == 0)
P
Palana 已提交
282
		obs_source_destroy(source);
283 284
}

285
void obs_source_remove(obs_source_t *source)
286
{
287
	struct obs_core_data *data = &obs->data;
288
	size_t id;
289
	bool   exists;
290 291 292

	pthread_mutex_lock(&data->sources_mutex);

J
jp9000 已提交
293 294
	if (!source || source->removed) {
		pthread_mutex_unlock(&data->sources_mutex);
J
jp9000 已提交
295
		return;
J
jp9000 已提交
296
	}
J
jp9000 已提交
297

J
jp9000 已提交
298
	source->removed = true;
J
jp9000 已提交
299

J
jp9000 已提交
300 301
	obs_source_addref(source);

302 303 304 305
	id = da_find(data->user_sources, &source, 0);
	exists = (id != DARRAY_INVALID);
	if (exists) {
		da_erase(data->user_sources, id);
J
jp9000 已提交
306
		obs_source_release(source);
307 308 309
	}

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

311 312 313
	if (exists)
		obs_source_dosignal(source, "source_remove", "remove");

J
jp9000 已提交
314
	obs_source_release(source);
315 316
}

317
bool obs_source_removed(const obs_source_t *source)
318
{
J
jp9000 已提交
319
	return source ? source->removed : true;
J
jp9000 已提交
320 321
}

322
static inline obs_data_t *get_defaults(const struct obs_source_info *info)
J
jp9000 已提交
323
{
324
	obs_data_t *settings = obs_data_create();
325 326
	if (info->get_defaults)
		info->get_defaults(settings);
J
jp9000 已提交
327 328 329
	return settings;
}

330
obs_data_t *obs_source_settings(enum obs_source_type type, const char *id)
J
jp9000 已提交
331 332
{
	const struct obs_source_info *info = get_source_info(type, id);
J
jp9000 已提交
333
	return (info) ? get_defaults(info) : NULL;
J
jp9000 已提交
334 335
}

336
obs_properties_t *obs_get_source_properties(enum obs_source_type type,
337
		const char *id)
J
jp9000 已提交
338
{
J
jp9000 已提交
339
	const struct obs_source_info *info = get_source_info(type, id);
340
	if (info && info->get_properties) {
341 342
		obs_data_t       *defaults = get_defaults(info);
		obs_properties_t *properties;
J
jp9000 已提交
343

344
		properties = info->get_properties(NULL);
J
jp9000 已提交
345 346 347 348
		obs_properties_apply_settings(properties, defaults);
		obs_data_release(defaults);
		return properties;
	}
J
jp9000 已提交
349 350 351
	return NULL;
}

352
obs_properties_t *obs_source_properties(const obs_source_t *source)
353
{
354
	if (source_valid(source) && source->info.get_properties) {
355
		obs_properties_t *props;
356
		props = source->info.get_properties(source->context.data);
357
		obs_properties_apply_settings(props, source->context.settings);
J
jp9000 已提交
358 359 360
		return props;
	}

361 362 363
	return NULL;
}

364
uint32_t obs_source_get_output_flags(const obs_source_t *source)
J
jp9000 已提交
365
{
J
jp9000 已提交
366
	return source ? source->info.output_flags : 0;
J
jp9000 已提交
367 368
}

369
static void obs_source_deferred_update(obs_source_t *source)
370
{
371 372 373 374
	if (source->context.data && source->info.update)
		source->info.update(source->context.data,
				source->context.settings);

375 376 377
	source->defer_update = false;
}

378
void obs_source_update(obs_source_t *source, obs_data_t *settings)
J
jp9000 已提交
379
{
J
jp9000 已提交
380 381
	if (!source) return;

382 383 384 385 386 387 388 389
	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);
390
	}
J
jp9000 已提交
391 392
}

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
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);
}

408
void obs_source_send_mouse_click(obs_source_t *source,
K
kc5nra 已提交
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
		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);
		}
	}
}

424
void obs_source_send_mouse_move(obs_source_t *source,
K
kc5nra 已提交
425 426 427 428 429 430 431 432 433 434 435 436 437
		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);
		}
	}
}

438
void obs_source_send_mouse_wheel(obs_source_t *source,
K
kc5nra 已提交
439 440 441 442 443 444 445 446 447 448 449 450 451
		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);
		}
	}
}

452
void obs_source_send_focus(obs_source_t *source, bool focus)
K
kc5nra 已提交
453 454 455 456 457 458 459 460 461 462 463
{
	if (!source)
		return;

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

464
void obs_source_send_key_click(obs_source_t *source,
K
kc5nra 已提交
465 466 467 468 469 470 471 472 473 474 475 476 477
		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);
		}
	}
}

478
static void activate_source(obs_source_t *source)
J
jp9000 已提交
479
{
480
	if (source->context.data && source->info.activate)
481
		source->info.activate(source->context.data);
482
	obs_source_dosignal(source, "source_activate", "activate");
J
jp9000 已提交
483 484
}

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

492
static void show_source(obs_source_t *source)
493
{
494
	if (source->context.data && source->info.show)
495
		source->info.show(source->context.data);
496
	obs_source_dosignal(source, "source_show", "show");
497 498
}

499
static void hide_source(obs_source_t *source)
500
{
501
	if (source->context.data && source->info.hide)
502
		source->info.hide(source->context.data);
503
	obs_source_dosignal(source, "source_hide", "hide");
504 505
}

506 507
static void activate_tree(obs_source_t *parent, obs_source_t *child,
		void *param)
508
{
J
jp9000 已提交
509
	if (os_atomic_inc_long(&child->activate_refs) == 1)
510
		activate_source(child);
J
jp9000 已提交
511 512 513

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
514 515
}

516
static void deactivate_tree(obs_source_t *parent, obs_source_t *child,
517 518
		void *param)
{
J
jp9000 已提交
519
	if (os_atomic_dec_long(&child->activate_refs) == 0)
520
		deactivate_source(child);
J
jp9000 已提交
521 522 523

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
524 525
}

526
static void show_tree(obs_source_t *parent, obs_source_t *child, void *param)
527
{
J
jp9000 已提交
528
	if (os_atomic_inc_long(&child->show_refs) == 1)
529 530 531 532 533 534
		show_source(child);

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

535
static void hide_tree(obs_source_t *parent, obs_source_t *child, void *param)
536
{
J
jp9000 已提交
537
	if (os_atomic_dec_long(&child->show_refs) == 0)
538 539 540 541 542 543
		hide_source(child);

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

544
void obs_source_activate(obs_source_t *source, enum view_type type)
545 546 547
{
	if (!source) return;

J
jp9000 已提交
548
	if (os_atomic_inc_long(&source->show_refs) == 1) {
549 550 551 552 553
		show_source(source);
		obs_source_enum_tree(source, show_tree, NULL);
	}

	if (type == MAIN_VIEW) {
J
jp9000 已提交
554
		if (os_atomic_inc_long(&source->activate_refs) == 1) {
555 556 557 558
			activate_source(source);
			obs_source_enum_tree(source, activate_tree, NULL);
			obs_source_set_present_volume(source, 1.0f);
		}
559 560 561
	}
}

562
void obs_source_deactivate(obs_source_t *source, enum view_type type)
563 564 565
{
	if (!source) return;

J
jp9000 已提交
566
	if (os_atomic_dec_long(&source->show_refs) == 0) {
567 568 569 570 571
		hide_source(source);
		obs_source_enum_tree(source, hide_tree, NULL);
	}

	if (type == MAIN_VIEW) {
J
jp9000 已提交
572
		if (os_atomic_dec_long(&source->activate_refs) == 0) {
573 574 575 576
			deactivate_source(source);
			obs_source_enum_tree(source, deactivate_tree, NULL);
			obs_source_set_present_volume(source, 0.0f);
		}
577
	}
J
jp9000 已提交
578 579
}

580
void obs_source_video_tick(obs_source_t *source, float seconds)
J
jp9000 已提交
581
{
J
jp9000 已提交
582 583
	if (!source) return;

584 585 586
	if (source->defer_update)
		obs_source_deferred_update(source);

J
jp9000 已提交
587 588
	/* reset the filter render texture information once every frame */
	if (source->filter_texrender)
589
		gs_texrender_reset(source->filter_texrender);
J
jp9000 已提交
590

591
	if (source->context.data && source->info.video_tick)
592
		source->info.video_tick(source->context.data, seconds);
593 594

	source->async_rendered = false;
J
jp9000 已提交
595 596
}

597
/* unless the value is 3+ hours worth of frames, this won't overflow */
J
jp9000 已提交
598
static inline uint64_t conv_frames_to_time(size_t frames)
599
{
J
jp9000 已提交
600
	const struct audio_output_info *info;
601
	info = audio_output_get_info(obs->audio.audio);
602 603 604

	return (uint64_t)frames * 1000000000ULL /
		(uint64_t)info->samples_per_sec;
605 606
}

607 608
/* maximum timestamp variance in nanoseconds */
#define MAX_TS_VAR          2000000000ULL
609

610 611
static inline void reset_audio_timing(obs_source_t *source, uint64_t timestamp,
		uint64_t os_time)
612 613
{
	source->timing_set    = true;
614
	source->timing_adjust = os_time - timestamp;
615
}
616

617
static inline void handle_ts_jump(obs_source_t *source, uint64_t expected,
618
		uint64_t ts, uint64_t diff, uint64_t os_time)
619
{
J
jp9000 已提交
620
	blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%"PRIu64"', "
621
	                "expected value %"PRIu64", input value %"PRIu64,
622
	                source->context.name, diff, expected, ts);
623 624

	/* if has video, ignore audio data until reset */
625
	if (!(source->info.output_flags & OBS_SOURCE_ASYNC))
626
		reset_audio_timing(source, ts, os_time);
627 628
}

629
static void source_signal_audio_data(obs_source_t *source,
630 631
		struct audio_data *in)
{
632
	struct calldata data;
633

634
	calldata_init(&data);
635

636 637
	calldata_set_ptr(&data, "source", source);
	calldata_set_ptr(&data, "data",   in);
638

639
	signal_handler_signal(source->context.signals, "audio_data", &data);
640

641
	calldata_free(&data);
642 643
}

644 645 646 647 648
static inline uint64_t uint64_diff(uint64_t ts1, uint64_t ts2)
{
	return (ts1 < ts2) ?  (ts2 - ts1) : (ts1 - ts2);
}

649
static void source_output_audio_line(obs_source_t *source,
650 651 652
		const struct audio_data *data)
{
	struct audio_data in = *data;
653
	uint64_t diff;
654
	uint64_t os_time = os_gettime_ns();
655

656 657 658 659 660
	/* 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;
661

662 663
	} else if (!source->timing_set) {
		reset_audio_timing(source, in.timestamp, os_time);
664

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

668
		/* smooth audio if within threshold */
669
		if (diff > MAX_TS_VAR)
670
			handle_ts_jump(source, source->next_audio_ts_min,
671
					in.timestamp, diff, os_time);
672
		else if (diff < TS_SMOOTHING_THRESHOLD)
673
			in.timestamp = source->next_audio_ts_min;
674 675
	}

676
	source->next_audio_ts_min = in.timestamp +
J
jp9000 已提交
677
		conv_frames_to_time(in.frames);
678

J
jp9000 已提交
679
	in.timestamp += source->timing_adjust + source->sync_offset;
J
jp9000 已提交
680 681
	in.volume = source->user_volume * source->present_volume *
		obs->audio.user_volume * obs->audio.present_volume;
682

683
	audio_line_output(source->audio_line, &in);
684
	source_signal_audio_data(source, &in);
685 686
}

687 688 689 690 691 692 693 694
enum convert_type {
	CONVERT_NONE,
	CONVERT_NV12,
	CONVERT_420,
	CONVERT_422_U,
	CONVERT_422_Y,
};

695
static inline enum convert_type get_convert_type(enum video_format format)
696
{
697
	switch (format) {
698 699 700 701 702 703 704 705 706 707 708
	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;

709
	case VIDEO_FORMAT_NONE:
710 711 712 713 714 715 716 717 718
	case VIDEO_FORMAT_RGBA:
	case VIDEO_FORMAT_BGRA:
	case VIDEO_FORMAT_BGRX:
		return CONVERT_NONE;
	}

	return CONVERT_NONE;
}

719
static inline bool set_packed422_sizes(struct obs_source *source,
720
		struct obs_source_frame *frame)
721 722
{
	source->async_convert_height = frame->height;
723 724 725 726 727 728
	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,
729
		struct obs_source_frame *frame)
730 731 732 733 734 735 736 737 738 739
{
	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;
740 741 742
	return true;
}

J
jp9000 已提交
743 744 745 746 747 748 749 750 751 752 753 754 755
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;
}

756
static inline bool init_gpu_conversion(struct obs_source *source,
757
		struct obs_source_frame *frame)
758 759 760 761 762 763 764
{
	switch (get_convert_type(frame->format)) {
		case CONVERT_422_Y:
		case CONVERT_422_U:
			return set_packed422_sizes(source, frame);

		case CONVERT_420:
765 766 767
			return set_planar420_sizes(source, frame);

		case CONVERT_NV12:
J
jp9000 已提交
768
			return set_nv12_sizes(source, frame);
769 770 771 772 773 774 775 776 777 778
			break;

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

	}
	return false;
}

779 780 781 782 783 784 785 786 787 788 789
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;
}

790
static inline bool set_async_texture_size(struct obs_source *source,
791
		struct obs_source_frame *frame)
792 793 794 795 796 797 798 799 800 801 802
{
	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;
	}

803 804
	gs_texture_destroy(source->async_texture);
	gs_texrender_destroy(source->async_convert_texrender);
805 806 807 808 809 810
	source->async_convert_texrender = NULL;

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

		source->async_convert_texrender =
811
			gs_texrender_create(GS_BGRX, GS_ZS_NONE);
812

813
		source->async_texture = gs_texture_create(
814 815
				source->async_convert_width,
				source->async_convert_height,
816 817
				source->async_texture_format,
				1, NULL, GS_DYNAMIC);
818 819

	} else {
820 821
		enum gs_color_format format = convert_video_format(
				frame->format);
822 823
		source->async_gpu_conversion = false;

824
		source->async_texture = gs_texture_create(
825
				frame->width, frame->height,
826
				format, 1, NULL, GS_DYNAMIC);
827 828 829 830 831 832 833 834 835 836
	}

	if (!source->async_texture)
		return false;

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

837
static void upload_raw_frame(gs_texture_t *tex,
838
		const struct obs_source_frame *frame)
839 840 841 842
{
	switch (get_convert_type(frame->format)) {
		case CONVERT_422_U:
		case CONVERT_422_Y:
843
			gs_texture_set_image(tex, frame->data[0],
844 845 846 847
					frame->linesize[0], false);
			break;

		case CONVERT_420:
848
			gs_texture_set_image(tex, frame->data[0],
849 850 851 852
					frame->width, false);
			break;

		case CONVERT_NV12:
J
jp9000 已提交
853 854
			gs_texture_set_image(tex, frame->data[0],
					frame->width, false);
855 856 857 858 859 860 861 862 863 864 865 866
			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:
867
			return "UYVY_Reverse";
868 869 870 871 872 873 874 875

		case VIDEO_FORMAT_YUY2:
			return "YUY2_Reverse";

		case VIDEO_FORMAT_YVYU:
			return "YVYU_Reverse";

		case VIDEO_FORMAT_I420:
876 877 878
			return "I420_Reverse";

		case VIDEO_FORMAT_NV12:
J
jp9000 已提交
879
			return "NV12_Reverse";
880 881 882 883 884 885 886 887 888 889 890 891
			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;
}

892
static inline void set_eparam(gs_effect_t *effect, const char *name, float val)
893
{
894
	gs_eparam_t *param = gs_effect_get_param_by_name(effect, name);
895
	gs_effect_set_float(param, val);
896 897 898
}

static bool update_async_texrender(struct obs_source *source,
899
		const struct obs_source_frame *frame)
900
{
901 902
	gs_texture_t   *tex       = source->async_texture;
	gs_texrender_t *texrender = source->async_convert_texrender;
903

904
	gs_texrender_reset(texrender);
905 906 907 908 909 910

	upload_raw_frame(tex, frame);

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

911 912 913
	float convert_width  = (float)source->async_convert_width;
	float convert_height = (float)source->async_convert_height;

914 915
	gs_effect_t *conv = obs->video.conversion_effect;
	gs_technique_t *tech = gs_effect_get_technique(conv,
916 917
			select_conversion_technique(frame->format));

918
	if (!gs_texrender_begin(texrender, cx, cy))
919 920
		return false;

921 922
	gs_technique_begin(tech);
	gs_technique_begin_pass(tech, 0);
923

924
	gs_effect_set_texture(gs_effect_get_param_by_name(conv, "image"), tex);
925 926 927 928
	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);
929
	set_eparam(conv, "width_d2",  cx * 0.5f);
930
	set_eparam(conv, "height_d2", cy * 0.5f);
931
	set_eparam(conv, "width_d2_i",  1.0f / (cx * 0.5f));
932
	set_eparam(conv, "height_d2_i", 1.0f / (cy * 0.5f));
933 934 935 936 937 938 939 940 941 942
	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]);
943 944 945 946 947

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

	gs_draw_sprite(tex, 0, cx, cy);

948 949
	gs_technique_end_pass(tech);
	gs_technique_end(tech);
950

951
	gs_texrender_end(texrender);
952 953 954 955

	return true;
}

956
static bool update_async_texture(struct obs_source *source,
957
		const struct obs_source_frame *frame)
958
{
959 960
	gs_texture_t      *tex       = source->async_texture;
	gs_texrender_t    *texrender = source->async_convert_texrender;
961
	enum convert_type type      = get_convert_type(frame->format);
962
	uint8_t           *ptr;
963 964
	uint32_t          linesize;

965 966 967
	source->async_format     = frame->format;
	source->async_flip       = frame->flip;
	source->async_full_range = frame->full_range;
968 969
	memcpy(source->async_color_matrix, frame->color_matrix,
			sizeof(frame->color_matrix));
970 971 972 973
	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);
974

975 976 977
	if (source->async_gpu_conversion && texrender)
		return update_async_texrender(source, frame);

978
	if (type == CONVERT_NONE) {
979
		gs_texture_set_image(tex, frame->data[0], frame->linesize[0],
980
				false);
981 982 983
		return true;
	}

984
	if (!gs_texture_map(tex, &ptr, &linesize))
985 986 987
		return false;

	if (type == CONVERT_420)
J
jp9000 已提交
988 989 990
		decompress_420((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
991 992

	else if (type == CONVERT_NV12)
J
jp9000 已提交
993 994 995
		decompress_nv12((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
996 997

	else if (type == CONVERT_422_Y)
998
		decompress_422(frame->data[0], frame->linesize[0],
J
jp9000 已提交
999
				0, frame->height, ptr, linesize, true);
1000 1001

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

1005
	gs_texture_unmap(tex);
1006 1007 1008
	return true;
}

1009
static inline void obs_source_draw_texture(struct obs_source *source,
1010
		gs_effect_t *effect, float *color_matrix,
1011
		float const *color_range_min, float const *color_range_max)
1012
{
1013 1014
	gs_texture_t *tex = source->async_texture;
	gs_eparam_t  *param;
1015

1016
	if (source->async_convert_texrender)
1017
		tex = gs_texrender_get_texture(source->async_convert_texrender);
1018

P
Palana 已提交
1019
	if (color_range_min) {
1020
		size_t const size = sizeof(float) * 3;
1021 1022
		param = gs_effect_get_param_by_name(effect, "color_range_min");
		gs_effect_set_val(param, color_range_min, size);
P
Palana 已提交
1023
	}
1024

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

P
Palana 已提交
1031
	if (color_matrix) {
1032 1033
		param = gs_effect_get_param_by_name(effect, "color_matrix");
		gs_effect_set_val(param, color_matrix, sizeof(float) * 16);
1034 1035
	}

1036 1037
	param = gs_effect_get_param_by_name(effect, "image");
	gs_effect_set_texture(param, tex);
1038

1039 1040
	gs_draw_sprite(tex, source->async_flip ? GS_FLIP_V : 0, 0, 0);
}
1041

1042 1043
static void obs_source_draw_async_texture(struct obs_source *source)
{
1044
	gs_effect_t    *effect        = gs_get_effect();
1045 1046 1047 1048
	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);
1049
	gs_technique_t *tech          = NULL;
1050 1051 1052

	if (def_draw) {
		effect = obs_get_default_effect();
1053 1054 1055
		tech = gs_effect_get_technique(effect, type);
		gs_technique_begin(tech);
		gs_technique_begin_pass(tech, 0);
1056 1057 1058
	}

	obs_source_draw_texture(source, effect,
1059 1060 1061
			yuv ? source->async_color_matrix : NULL,
			limited_range ? source->async_color_range_min : NULL,
			limited_range ? source->async_color_range_max : NULL);
1062 1063

	if (def_draw) {
1064 1065
		gs_technique_end_pass(tech);
		gs_technique_end(tech);
1066
	}
1067 1068
}

1069
static void obs_source_render_async_video(obs_source_t *source)
1070
{
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
	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);
1083
	}
1084

1085 1086
	if (source->async_texture)
		obs_source_draw_async_texture(source);
1087 1088
}

1089
static inline void obs_source_render_filters(obs_source_t *source)
1090 1091 1092 1093 1094 1095
{
	source->rendering_filter = true;
	obs_source_video_render(source->filters.array[0]);
	source->rendering_filter = false;
}

1096
static inline void obs_source_default_render(obs_source_t *source,
J
jp9000 已提交
1097
		bool color_matrix)
1098
{
1099
	gs_effect_t    *effect     = obs->video.default_effect;
1100
	const char     *tech_name = color_matrix ? "DrawMatrix" : "Draw";
1101
	gs_technique_t *tech       = gs_effect_get_technique(effect, tech_name);
1102
	size_t         passes, i;
1103

1104
	passes = gs_technique_begin(tech);
1105
	for (i = 0; i < passes; i++) {
1106
		gs_technique_begin_pass(tech, i);
1107 1108
		if (source->context.data)
			source->info.video_render(source->context.data, effect);
1109
		gs_technique_end_pass(tech);
1110
	}
1111
	gs_technique_end(tech);
1112 1113
}

1114
static inline void obs_source_main_render(obs_source_t *source)
1115
{
1116 1117 1118
	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;
1119 1120
	bool default_effect = !source->filter_parent &&
	                      source->filters.num == 0 &&
1121
	                      !custom_draw;
1122 1123

	if (default_effect)
J
jp9000 已提交
1124
		obs_source_default_render(source, color_matrix);
1125
	else if (source->context.data)
1126
		source->info.video_render(source->context.data,
1127
				custom_draw ? NULL : gs_get_effect());
1128 1129
}

1130
void obs_source_video_render(obs_source_t *source)
J
jp9000 已提交
1131
{
1132
	if (!source_valid(source)) return;
J
jp9000 已提交
1133

1134 1135
	if (source->filters.num && !source->rendering_filter)
		obs_source_render_filters(source);
1136

1137 1138 1139 1140
	else if (source->info.video_render)
		obs_source_main_render(source);

	else if (source->filter_target)
1141 1142
		obs_source_video_render(source->filter_target);

1143
	else
1144
		obs_source_render_async_video(source);
J
jp9000 已提交
1145 1146
}

1147
uint32_t obs_source_get_width(const obs_source_t *source)
J
jp9000 已提交
1148
{
1149
	if (!source_valid(source)) return 0;
1150

1151 1152
	if (source->info.get_width)
		return source->info.get_width(source->context.data);
1153
	return source->async_width;
J
jp9000 已提交
1154 1155
}

1156
uint32_t obs_source_get_height(const obs_source_t *source)
J
jp9000 已提交
1157
{
1158
	if (!source_valid(source)) return 0;
1159

1160 1161
	if (source->info.get_height)
		return source->info.get_height(source->context.data);
1162
	return source->async_height;
J
jp9000 已提交
1163 1164
}

1165
obs_source_t *obs_filter_get_parent(const obs_source_t *filter)
1166
{
J
jp9000 已提交
1167
	return filter ? filter->filter_parent : NULL;
1168 1169
}

1170
obs_source_t *obs_filter_get_target(const obs_source_t *filter)
J
jp9000 已提交
1171
{
J
jp9000 已提交
1172
	return filter ? filter->filter_target : NULL;
J
jp9000 已提交
1173 1174
}

1175
void obs_source_filter_add(obs_source_t *source, obs_source_t *filter)
J
jp9000 已提交
1176
{
J
jp9000 已提交
1177 1178 1179
	if (!source || !filter)
		return;

1180 1181
	pthread_mutex_lock(&source->filter_mutex);

J
jp9000 已提交
1182
	if (da_find(source->filters, &filter, 0) != DARRAY_INVALID) {
J
jp9000 已提交
1183 1184 1185 1186 1187 1188
		blog(LOG_WARNING, "Tried to add a filter that was already "
		                  "present on the source");
		return;
	}

	if (source->filters.num) {
1189
		obs_source_t **back = da_end(source->filters);
J
jp9000 已提交
1190 1191 1192 1193
		(*back)->filter_target = filter;
	}

	da_push_back(source->filters, &filter);
1194 1195 1196 1197

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = source;
J
jp9000 已提交
1198 1199 1200
	filter->filter_target = source;
}

1201
void obs_source_filter_remove(obs_source_t *source, obs_source_t *filter)
J
jp9000 已提交
1202
{
1203 1204
	size_t idx;

J
jp9000 已提交
1205 1206 1207
	if (!source || !filter)
		return;

1208 1209 1210
	pthread_mutex_lock(&source->filter_mutex);

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
1211
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
1212 1213 1214
		return;

	if (idx > 0) {
1215
		obs_source_t *prev = source->filters.array[idx-1];
J
jp9000 已提交
1216 1217 1218 1219
		prev->filter_target = filter->filter_target;
	}

	da_erase(source->filters, idx);
1220 1221 1222 1223

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = NULL;
J
jp9000 已提交
1224 1225 1226
	filter->filter_target = NULL;
}

1227
void obs_source_filter_set_order(obs_source_t *source, obs_source_t *filter,
J
jp9000 已提交
1228
		enum obs_order_movement movement)
J
jp9000 已提交
1229
{
J
jp9000 已提交
1230 1231 1232 1233 1234 1235
	size_t idx, i;

	if (!source || !filter)
		return;

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
1236
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
1237 1238
		return;

J
jp9000 已提交
1239
	if (movement == OBS_ORDER_MOVE_UP) {
J
jp9000 已提交
1240 1241 1242 1243
		if (idx == source->filters.num-1)
			return;
		da_move_item(source->filters, idx, idx+1);

J
jp9000 已提交
1244
	} else if (movement == OBS_ORDER_MOVE_DOWN) {
J
jp9000 已提交
1245 1246 1247 1248
		if (idx == 0)
			return;
		da_move_item(source->filters, idx, idx-1);

J
jp9000 已提交
1249
	} else if (movement == OBS_ORDER_MOVE_TOP) {
J
jp9000 已提交
1250 1251 1252 1253
		if (idx == source->filters.num-1)
			return;
		da_move_item(source->filters, idx, source->filters.num-1);

J
jp9000 已提交
1254
	} else if (movement == OBS_ORDER_MOVE_BOTTOM) {
J
jp9000 已提交
1255 1256 1257 1258 1259
		if (idx == 0)
			return;
		da_move_item(source->filters, idx, 0);
	}

1260
	/* reorder filter targets, not the nicest way of dealing with things */
J
jp9000 已提交
1261
	for (i = 0; i < source->filters.num; i++) {
1262
		obs_source_t *next_filter = (i == source->filters.num-1) ?
J
jp9000 已提交
1263 1264 1265 1266 1267
			source : source->filters.array[idx+1];
		source->filters.array[i]->filter_target = next_filter;
	}
}

1268
obs_data_t *obs_source_get_settings(const obs_source_t *source)
J
jp9000 已提交
1269
{
J
jp9000 已提交
1270 1271
	if (!source) return NULL;

1272 1273
	obs_data_addref(source->context.settings);
	return source->context.settings;
J
jp9000 已提交
1274 1275
}

1276
static inline struct obs_source_frame *filter_async_video(obs_source_t *source,
1277
		struct obs_source_frame *in)
1278 1279 1280 1281
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
1282 1283

		if (filter->context.data && filter->info.filter_video) {
1284 1285
			in = filter->info.filter_video(filter->context.data,
					in);
1286 1287 1288 1289 1290 1291 1292 1293
			if (!in)
				return NULL;
		}
	}

	return in;
}

1294 1295
static inline void copy_frame_data_line(struct obs_source_frame *dst,
		const struct obs_source_frame *src, uint32_t plane, uint32_t y)
1296
{
1297 1298 1299 1300
	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];
1301 1302 1303 1304

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

1305 1306 1307
static inline void copy_frame_data_plane(struct obs_source_frame *dst,
		const struct obs_source_frame *src,
		uint32_t plane, uint32_t lines)
1308
{
1309
	if (dst->linesize[plane] != src->linesize[plane])
1310 1311 1312 1313
		for (uint32_t y = 0; y < lines; y++)
			copy_frame_data_line(dst, src, plane, y);
	else
		memcpy(dst->data[plane], src->data[plane],
1314
				dst->linesize[plane] * lines);
1315 1316
}

1317 1318
static void copy_frame_data(struct obs_source_frame *dst,
		const struct obs_source_frame *src)
1319 1320
{
	dst->flip         = src->flip;
1321
	dst->full_range   = src->full_range;
1322 1323
	dst->timestamp    = src->timestamp;
	memcpy(dst->color_matrix, src->color_matrix, sizeof(float) * 16);
1324 1325 1326 1327 1328
	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);
	}
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352

	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);
	}
}

1353 1354
static inline struct obs_source_frame *cache_video(
		const struct obs_source_frame *frame)
1355
{
1356
	/* TODO: use an actual cache */
1357 1358
	struct obs_source_frame *new_frame = obs_source_frame_create(
			frame->format, frame->width, frame->height);
1359

1360
	copy_frame_data(new_frame, frame);
1361
	return new_frame;
1362 1363
}

1364
static bool ready_async_frame(obs_source_t *source, uint64_t sys_time);
1365 1366 1367 1368

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

1372
void obs_source_output_video(obs_source_t *source,
1373
		const struct obs_source_frame *frame)
1374
{
J
jp9000 已提交
1375 1376 1377
	if (!source || !frame)
		return;

1378
	struct obs_source_frame *output = cache_video(frame);
1379 1380 1381 1382 1383

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

1384 1385
	if (output) {
		pthread_mutex_lock(&source->video_mutex);
1386
		cycle_frames(source);
1387 1388 1389
		da_push_back(source->video_frames, &output);
		pthread_mutex_unlock(&source->video_mutex);
	}
1390 1391
}

1392
static inline struct obs_audio_data *filter_async_audio(obs_source_t *source,
1393
		struct obs_audio_data *in)
1394 1395 1396 1397
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
1398 1399

		if (filter->context.data && filter->info.filter_audio) {
1400 1401
			in = filter->info.filter_audio(filter->context.data,
					in);
1402 1403 1404 1405 1406 1407 1408 1409
			if (!in)
				return NULL;
		}
	}

	return in;
}

1410
static inline void reset_resampler(obs_source_t *source,
1411
		const struct obs_source_audio *audio)
1412
{
J
jp9000 已提交
1413
	const struct audio_output_info *obs_info;
1414 1415
	struct resample_info output_info;

1416
	obs_info = audio_output_get_info(obs->audio.audio);
1417

1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441
	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");
}

1442
static inline void copy_audio_data(obs_source_t *source,
J
jp9000 已提交
1443
		const uint8_t *const data[], uint32_t frames, uint64_t ts)
1444
{
1445 1446
	size_t planes    = audio_output_get_planes(obs->audio.audio);
	size_t blocksize = audio_output_get_block_size(obs->audio.audio);
1447 1448
	size_t size      = (size_t)frames * blocksize;
	bool   resize    = source->audio_storage_size < size;
1449

J
jp9000 已提交
1450 1451
	source->audio_data.frames    = frames;
	source->audio_data.timestamp = ts;
1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464

	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;
1465 1466 1467
}

/* resamples/remixes new audio to the designated main audio output format */
1468
static void process_audio(obs_source_t *source,
1469
		const struct obs_source_audio *audio)
1470 1471 1472 1473 1474 1475 1476 1477 1478 1479
{
	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 已提交
1480
		uint8_t  *output[MAX_AV_PLANES];
1481 1482 1483
		uint32_t frames;
		uint64_t offset;

1484 1485 1486 1487 1488
		memset(output, 0, sizeof(output));

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

J
jp9000 已提交
1490
		copy_audio_data(source, (const uint8_t *const *)output, frames,
1491 1492 1493 1494 1495
				audio->timestamp - offset);
	} else {
		copy_audio_data(source, audio->data, audio->frames,
				audio->timestamp);
	}
1496 1497
}

1498
void obs_source_output_audio(obs_source_t *source,
1499
		const struct obs_source_audio *audio)
1500
{
J
jp9000 已提交
1501
	uint32_t flags;
1502
	struct obs_audio_data *output;
1503

J
jp9000 已提交
1504 1505 1506 1507
	if (!source || !audio)
		return;

	flags = source->info.output_flags;
1508
	process_audio(source, audio);
1509 1510

	pthread_mutex_lock(&source->filter_mutex);
1511
	output = filter_async_audio(source, &source->audio_data);
1512 1513

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

1516 1517
		pthread_mutex_lock(&source->audio_mutex);

1518 1519
		/* wait for video to start before outputting any audio so we
		 * have a base for sync */
1520
		if (source->timing_set || !async) {
1521
			struct audio_data data;
1522

J
jp9000 已提交
1523
			for (int i = 0; i < MAX_AV_PLANES; i++)
1524 1525
				data.data[i] = output->data[i];

1526 1527 1528
			data.frames    = output->frames;
			data.timestamp = output->timestamp;
			source_output_audio_line(source, &data);
1529 1530 1531 1532 1533 1534 1535 1536
		}

		pthread_mutex_unlock(&source->audio_mutex);
	}

	pthread_mutex_unlock(&source->filter_mutex);
}

1537
static inline bool frame_out_of_bounds(const obs_source_t *source, uint64_t ts)
1538
{
J
jp9000 已提交
1539
	if (ts < source->last_frame_ts)
1540
		return ((source->last_frame_ts - ts) > MAX_TS_VAR);
J
jp9000 已提交
1541
	else
1542
		return ((ts - source->last_frame_ts) > MAX_TS_VAR);
1543 1544
}

J
jp9000 已提交
1545 1546
/* #define DEBUG_ASYNC_FRAMES 1 */

1547
static bool ready_async_frame(obs_source_t *source, uint64_t sys_time)
1548
{
1549 1550
	struct obs_source_frame *next_frame = source->video_frames.array[0];
	struct obs_source_frame *frame      = NULL;
1551 1552 1553 1554
	uint64_t sys_offset = sys_time - source->last_sys_timestamp;
	uint64_t frame_time = next_frame->timestamp;
	uint64_t frame_offset = 0;

J
jp9000 已提交
1555 1556 1557 1558 1559 1560 1561 1562 1563 1564
	if ((source->flags & OBS_SOURCE_UNBUFFERED) != 0) {
		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 已提交
1565 1566 1567 1568 1569 1570 1571 1572 1573
#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

1574 1575
	/* account for timestamp invalidation */
	if (frame_out_of_bounds(source, frame_time)) {
J
jp9000 已提交
1576 1577 1578
#if DEBUG_ASYNC_FRAMES
		blog(LOG_DEBUG, "timing jump");
#endif
1579
		source->last_frame_ts = next_frame->timestamp;
J
jp9000 已提交
1580
		return true;
1581 1582
	} else {
		frame_offset = frame_time - source->last_frame_ts;
J
jp9000 已提交
1583
		source->last_frame_ts += sys_offset;
1584 1585
	}

J
jp9000 已提交
1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605
	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

1606
		obs_source_frame_destroy(frame);
1607

1608 1609 1610
		if (source->video_frames.num == 1)
			return true;

1611
		frame = next_frame;
J
jp9000 已提交
1612
		next_frame = source->video_frames.array[1];
1613 1614

		/* more timestamp checking and compensating */
1615
		if ((next_frame->timestamp - frame_time) > MAX_TS_VAR) {
J
jp9000 已提交
1616 1617 1618
#if DEBUG_ASYNC_FRAMES
			blog(LOG_DEBUG, "timing jump");
#endif
1619 1620 1621 1622 1623 1624 1625 1626
			source->last_frame_ts =
				next_frame->timestamp - frame_offset;
		}

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

J
jp9000 已提交
1627 1628 1629 1630
#if DEBUG_ASYNC_FRAMES
	if (!frame)
		blog(LOG_DEBUG, "no frame!");
#endif
1631

1632 1633 1634
	return frame != NULL;
}

1635
static inline struct obs_source_frame *get_closest_frame(obs_source_t *source,
1636 1637
		uint64_t sys_time)
{
1638
	if (ready_async_frame(source, sys_time)) {
1639
		struct obs_source_frame *frame = source->video_frames.array[0];
1640 1641 1642 1643 1644
		da_erase(source->video_frames, 0);
		return frame;
	}

	return NULL;
1645 1646
}

1647
/*
1648 1649
 * Ensures that cached frames are displayed on time.  If multiple frames
 * were cached between renders, then releases the unnecessary frames and uses
1650 1651
 * the frame with the closest timing to ensure sync.  Also ensures that timing
 * with audio is synchronized.
1652
 */
1653
struct obs_source_frame *obs_source_get_frame(obs_source_t *source)
J
jp9000 已提交
1654
{
1655
	struct obs_source_frame *frame = NULL;
1656
	uint64_t sys_time;
1657

J
jp9000 已提交
1658 1659 1660
	if (!source)
		return NULL;

1661 1662
	pthread_mutex_lock(&source->video_mutex);

J
jp9000 已提交
1663 1664
	sys_time = os_gettime_ns();

1665 1666 1667
	if (!source->video_frames.num)
		goto unlock;

1668 1669
	if (!source->last_frame_ts) {
		frame = source->video_frames.array[0];
1670 1671
		da_erase(source->video_frames, 0);

1672
		source->last_frame_ts = frame->timestamp;
1673
	} else {
1674
		frame = get_closest_frame(source, sys_time);
J
jp9000 已提交
1675 1676 1677 1678 1679 1680
	}

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

J
jp9000 已提交
1683
unlock:
1684 1685 1686
	source->last_sys_timestamp = sys_time;

	pthread_mutex_unlock(&source->video_mutex);
1687

1688
	if (frame)
1689 1690
		obs_source_addref(source);

1691
	return frame;
J
jp9000 已提交
1692 1693
}

1694
void obs_source_release_frame(obs_source_t *source,
1695
		struct obs_source_frame *frame)
J
jp9000 已提交
1696
{
J
jp9000 已提交
1697
	if (source && frame) {
1698
		obs_source_frame_destroy(frame);
1699 1700
		obs_source_release(source);
	}
J
jp9000 已提交
1701
}
1702

1703
const char *obs_source_get_name(const obs_source_t *source)
1704
{
1705
	return source ? source->context.name : NULL;
1706 1707
}

1708
void obs_source_set_name(obs_source_t *source, const char *name)
1709
{
J
jp9000 已提交
1710
	if (!source) return;
J
jp9000 已提交
1711 1712 1713 1714 1715 1716 1717

	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);
1718 1719 1720
		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 已提交
1721 1722 1723 1724 1725
		signal_handler_signal(obs->signals, "source_rename", &data);
		signal_handler_signal(source->context.signals, "rename", &data);
		calldata_free(&data);
		bfree(prev_name);
	}
1726 1727
}

1728
enum obs_source_type obs_source_get_type(const obs_source_t *source)
1729
{
J
jp9000 已提交
1730 1731
	return source ? source->info.type : OBS_SOURCE_TYPE_INPUT;
}
J
jp9000 已提交
1732

1733
const char *obs_source_get_id(const obs_source_t *source)
J
jp9000 已提交
1734 1735
{
	return source ? source->info.id : NULL;
1736
}
1737

1738 1739
static inline void render_filter_bypass(obs_source_t *target,
		gs_effect_t *effect, bool use_matrix)
1740
{
J
jp9000 已提交
1741
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1742
	gs_technique_t *tech    = gs_effect_get_technique(effect, tech_name);
1743 1744
	size_t      passes, i;

1745
	passes = gs_technique_begin(tech);
1746
	for (i = 0; i < passes; i++) {
1747
		gs_technique_begin_pass(tech, i);
1748
		obs_source_video_render(target);
1749
		gs_technique_end_pass(tech);
1750
	}
1751
	gs_technique_end(tech);
1752 1753
}

1754
static inline void render_filter_tex(gs_texture_t *tex, gs_effect_t *effect,
J
jp9000 已提交
1755
		uint32_t width, uint32_t height, bool use_matrix)
1756
{
J
jp9000 已提交
1757
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1758 1759
	gs_technique_t *tech    = gs_effect_get_technique(effect, tech_name);
	gs_eparam_t    *image   = gs_effect_get_param_by_name(effect, "image");
1760 1761
	size_t      passes, i;

1762
	gs_effect_set_texture(image, tex);
1763

1764
	passes = gs_technique_begin(tech);
1765
	for (i = 0; i < passes; i++) {
1766
		gs_technique_begin_pass(tech, i);
1767
		gs_draw_sprite(tex, width, height, 0);
1768
		gs_technique_end_pass(tech);
1769
	}
1770
	gs_technique_end(tech);
1771 1772
}

1773
void obs_source_process_filter(obs_source_t *filter, gs_effect_t *effect,
J
jp9000 已提交
1774
		uint32_t width, uint32_t height, enum gs_color_format format,
1775
		enum obs_allow_direct_render allow_direct)
1776
{
1777
	obs_source_t *target, *parent;
J
jp9000 已提交
1778 1779 1780 1781 1782 1783
	uint32_t     target_flags, parent_flags;
	int          cx, cy;
	bool         use_matrix, expects_def, can_directly;

	if (!filter) return;

1784 1785
	target       = obs_filter_get_target(filter);
	parent       = obs_filter_get_parent(filter);
J
jp9000 已提交
1786 1787
	target_flags = target->info.output_flags;
	parent_flags = parent->info.output_flags;
1788 1789
	cx           = obs_source_get_width(target);
	cy           = obs_source_get_height(target);
J
jp9000 已提交
1790 1791
	use_matrix   = !!(target_flags & OBS_SOURCE_COLOR_MATRIX);
	expects_def  = !(parent_flags & OBS_SOURCE_CUSTOM_DRAW);
1792
	can_directly = allow_direct == OBS_ALLOW_DIRECT_RENDERING;
1793 1794 1795 1796 1797

	/* 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 */
1798
	if (can_directly && expects_def && target == parent) {
J
jp9000 已提交
1799
		render_filter_bypass(target, effect, use_matrix);
1800 1801 1802
		return;
	}

J
jp9000 已提交
1803
	if (!filter->filter_texrender)
1804
		filter->filter_texrender = gs_texrender_create(format,
J
jp9000 已提交
1805 1806
				GS_ZS_NONE);

1807
	if (gs_texrender_begin(filter->filter_texrender, cx, cy)) {
1808
		gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
1809
		if (expects_def && parent == target)
J
jp9000 已提交
1810
			obs_source_default_render(parent, use_matrix);
1811 1812
		else
			obs_source_video_render(target);
1813
		gs_texrender_end(filter->filter_texrender);
1814 1815 1816 1817
	}

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

1818
	render_filter_tex(gs_texrender_get_texture(filter->filter_texrender),
J
jp9000 已提交
1819
			effect, width, height, use_matrix);
1820
}
1821

1822
signal_handler_t *obs_source_get_signal_handler(const obs_source_t *source)
1823
{
1824
	return source ? source->context.signals : NULL;
1825 1826
}

1827
proc_handler_t *obs_source_get_proc_handler(const obs_source_t *source)
1828
{
1829
	return source ? source->context.procs : NULL;
1830
}
J
jp9000 已提交
1831

1832
void obs_source_set_volume(obs_source_t *source, float volume)
J
jp9000 已提交
1833
{
J
jp9000 已提交
1834 1835
	if (source) {
		struct calldata data = {0};
1836 1837
		calldata_set_ptr(&data, "source", source);
		calldata_set_float(&data, "volume", volume);
J
jp9000 已提交
1838

1839
		signal_handler_signal(source->context.signals, "volume", &data);
1840
		signal_handler_signal(obs->signals, "source_volume", &data);
J
jp9000 已提交
1841

1842
		volume = (float)calldata_float(&data, "volume");
J
jp9000 已提交
1843 1844
		calldata_free(&data);

J
jp9000 已提交
1845
		source->user_volume = volume;
J
jp9000 已提交
1846
	}
J
jp9000 已提交
1847 1848
}

1849
static void set_tree_preset_vol(obs_source_t *parent, obs_source_t *child,
J
jp9000 已提交
1850 1851 1852 1853 1854 1855 1856 1857
		void *param)
{
	float *vol = param;
	child->present_volume = *vol;

	UNUSED_PARAMETER(parent);
}

1858
void obs_source_set_present_volume(obs_source_t *source, float volume)
J
jp9000 已提交
1859
{
J
jp9000 已提交
1860
	if (source) {
J
jp9000 已提交
1861
		source->present_volume = volume;
J
jp9000 已提交
1862 1863 1864 1865 1866 1867 1868 1869

		/* don't set the presentation volume of the tree if a
		 * transition source, let the transition handle presentation
		 * volume for the child sources itself. */
		if (source->info.type != OBS_SOURCE_TYPE_TRANSITION)
			obs_source_enum_tree(source, set_tree_preset_vol,
					&volume);
	}
J
jp9000 已提交
1870 1871
}

1872
float obs_source_get_volume(const obs_source_t *source)
J
jp9000 已提交
1873
{
J
jp9000 已提交
1874
	return source ? source->user_volume : 0.0f;
J
jp9000 已提交
1875 1876
}

1877
float obs_source_get_present_volume(const obs_source_t *source)
J
jp9000 已提交
1878
{
J
jp9000 已提交
1879 1880 1881
	return source ? source->present_volume : 0.0f;
}

1882
void obs_source_set_sync_offset(obs_source_t *source, int64_t offset)
J
jp9000 已提交
1883 1884 1885 1886 1887
{
	if (source)
		source->sync_offset = offset;
}

1888
int64_t obs_source_get_sync_offset(const obs_source_t *source)
J
jp9000 已提交
1889 1890
{
	return source ? source->sync_offset : 0;
J
jp9000 已提交
1891
}
1892 1893 1894 1895 1896 1897

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

1898
static void enum_source_tree_callback(obs_source_t *parent, obs_source_t *child,
1899 1900 1901 1902
		void *param)
{
	struct source_enum_data *data = param;

J
jp9000 已提交
1903 1904
	if (child->info.enum_sources) {
		if (child->context.data) {
1905 1906
			child->info.enum_sources(child->context.data,
					enum_source_tree_callback, data);
J
jp9000 已提交
1907
		}
1908 1909 1910 1911 1912
	}

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

1913
void obs_source_enum_sources(obs_source_t *source,
1914 1915 1916
		obs_source_enum_proc_t enum_callback,
		void *param)
{
J
jp9000 已提交
1917
	if (!source_valid(source) || !source->info.enum_sources)
1918 1919 1920 1921
		return;

	obs_source_addref(source);

1922
	source->info.enum_sources(source->context.data, enum_callback, param);
1923 1924 1925 1926

	obs_source_release(source);
}

1927
void obs_source_enum_tree(obs_source_t *source,
1928 1929 1930 1931 1932
		obs_source_enum_proc_t enum_callback,
		void *param)
{
	struct source_enum_data data = {enum_callback, param};

J
jp9000 已提交
1933
	if (!source_valid(source) || !source->info.enum_sources)
1934 1935 1936 1937
		return;

	obs_source_addref(source);

1938 1939
	source->info.enum_sources(source->context.data,
			enum_source_tree_callback,
1940 1941 1942 1943
			&data);

	obs_source_release(source);
}
1944

J
jp9000 已提交
1945 1946 1947 1948 1949 1950 1951
struct descendant_info {
	bool exists;
	obs_source_t *target;
};

static void check_descendant(obs_source_t *parent, obs_source_t *child,
		void *param)
1952
{
J
jp9000 已提交
1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965
	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;
1966

1967 1968 1969 1970 1971
	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 已提交
1972 1973

	return true;
1974 1975
}

1976
void obs_source_remove_child(obs_source_t *parent, obs_source_t *child)
1977 1978 1979
{
	if (!parent || !child) return;

1980 1981 1982 1983 1984
	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);
	}
1985
}
J
jp9000 已提交
1986

1987
static void reset_transition_vol(obs_source_t *parent, obs_source_t *child,
J
jp9000 已提交
1988 1989 1990 1991 1992 1993 1994 1995
		void *param)
{
	child->transition_volume = 0.0f;

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

1996
static void add_transition_vol(obs_source_t *parent, obs_source_t *child,
J
jp9000 已提交
1997 1998 1999 2000 2001 2002 2003 2004
		void *param)
{
	float *vol = param;
	child->transition_volume += *vol;

	UNUSED_PARAMETER(parent);
}

2005
static void apply_transition_vol(obs_source_t *parent, obs_source_t *child,
J
jp9000 已提交
2006 2007 2008 2009 2010 2011 2012 2013
		void *param)
{
	child->present_volume = child->transition_volume;

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

2014
void obs_transition_begin_frame(obs_source_t *transition)
J
jp9000 已提交
2015 2016 2017 2018 2019
{
	if (!transition) return;
	obs_source_enum_tree(transition, reset_transition_vol, NULL);
}

2020
void obs_source_set_transition_vol(obs_source_t *source, float vol)
J
jp9000 已提交
2021 2022 2023 2024 2025 2026 2027
{
	if (!source) return;

	add_transition_vol(NULL, source, &vol);
	obs_source_enum_tree(source, add_transition_vol, &vol);
}

2028
void obs_transition_end_frame(obs_source_t *transition)
J
jp9000 已提交
2029 2030 2031 2032
{
	if (!transition) return;
	obs_source_enum_tree(transition, apply_transition_vol, NULL);
}
2033

2034
void obs_source_save(obs_source_t *source)
2035
{
2036
	if (!source_valid(source) || !source->info.save) return;
2037 2038 2039
	source->info.save(source->context.data, source->context.settings);
}

2040
void obs_source_load(obs_source_t *source)
2041
{
2042
	if (!source_valid(source) || !source->info.load) return;
2043 2044
	source->info.load(source->context.data, source->context.settings);
}
J
jp9000 已提交
2045

J
jp9000 已提交
2046 2047 2048 2049 2050
bool obs_source_active(const obs_source_t *source)
{
	return source->activate_refs != 0;
}

J
jp9000 已提交
2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076
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 已提交
2077 2078 2079 2080 2081

void obs_source_draw_set_color_matrix(const struct matrix4 *color_matrix,
		const struct vec3 *color_range_min,
		const struct vec3 *color_range_max)
{
2082 2083 2084 2085 2086 2087
	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 已提交
2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 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
	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();
}