obs-source.c 55.4 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
	source->present_volume = 0.0f;
J
jp9000 已提交
114
	source->sync_offset = 0;
115 116 117
	pthread_mutex_init_value(&source->filter_mutex);
	pthread_mutex_init_value(&source->video_mutex);
	pthread_mutex_init_value(&source->audio_mutex);
118

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	if (!frame)
		return;

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

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

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

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

226 227
	obs_context_data_remove(&source->context);

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

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

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

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

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

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

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

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

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

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

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

268 269 270
	bfree(source);
}

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

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

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

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

	pthread_mutex_lock(&data->sources_mutex);

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

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

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

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

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

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

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

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

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

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

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

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

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

362 363 364
	return NULL;
}

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

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

376 377 378
	source->defer_update = false;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

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

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

635
	calldata_init(&data);
636

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

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

642
	calldata_free(&data);
643 644
}

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

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

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

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

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

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

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

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

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

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

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

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

	return CONVERT_NONE;
}

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

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

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

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

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

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

	}
	return false;
}

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

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

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

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

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

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

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

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

	if (!source->async_texture)
		return false;

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

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

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

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

		case VIDEO_FORMAT_YUY2:
			return "YUY2_Reverse";

		case VIDEO_FORMAT_YVYU:
			return "YVYU_Reverse";

		case VIDEO_FORMAT_I420:
877 878 879
			return "I420_Reverse";

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

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

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

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

	upload_raw_frame(tex, frame);

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

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

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

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

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

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

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

	gs_draw_sprite(tex, 0, cx, cy);

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

952
	gs_texrender_end(texrender);
953 954 955 956

	return true;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1181 1182
	pthread_mutex_lock(&source->filter_mutex);

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

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

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

	pthread_mutex_unlock(&source->filter_mutex);

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

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

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

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

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

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

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

	pthread_mutex_unlock(&source->filter_mutex);

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

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

	if (!source || !filter)
		return;

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

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

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

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

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

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

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

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

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

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

	return in;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return in;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1517 1518
		pthread_mutex_lock(&source->audio_mutex);

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

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

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

		pthread_mutex_unlock(&source->audio_mutex);
	}

	pthread_mutex_unlock(&source->filter_mutex);
}

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

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

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

J
jp9000 已提交
1556 1557 1558 1559 1560 1561 1562 1563 1564 1565
	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 已提交
1566 1567 1568 1569 1570 1571 1572 1573 1574
#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

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

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

1607
		obs_source_frame_destroy(frame);
1608

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

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

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

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

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

1633 1634 1635
	return frame != NULL;
}

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

	return NULL;
1646 1647
}

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

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

1662 1663
	pthread_mutex_lock(&source->video_mutex);

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

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

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

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

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

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

	pthread_mutex_unlock(&source->video_mutex);
1688

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

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

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

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

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

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

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

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

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

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

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

1763
	gs_effect_set_texture(image, tex);
1764

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

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

	if (!filter) return;

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

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

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

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

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

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

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

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

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

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

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

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

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

	UNUSED_PARAMETER(parent);
}

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

		/* 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 已提交
1871 1872
}

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

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

1883
void obs_source_set_sync_offset(obs_source_t *source, int64_t offset)
J
jp9000 已提交
1884
{
J
jp9000 已提交
1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896
	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 已提交
1897 1898
}

1899
int64_t obs_source_get_sync_offset(const obs_source_t *source)
J
jp9000 已提交
1900 1901
{
	return source ? source->sync_offset : 0;
J
jp9000 已提交
1902
}
1903 1904 1905 1906 1907 1908

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

1909
static void enum_source_tree_callback(obs_source_t *parent, obs_source_t *child,
1910 1911 1912 1913
		void *param)
{
	struct source_enum_data *data = param;

J
jp9000 已提交
1914 1915
	if (child->info.enum_sources) {
		if (child->context.data) {
1916 1917
			child->info.enum_sources(child->context.data,
					enum_source_tree_callback, data);
J
jp9000 已提交
1918
		}
1919 1920 1921 1922 1923
	}

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

1924
void obs_source_enum_sources(obs_source_t *source,
1925 1926 1927
		obs_source_enum_proc_t enum_callback,
		void *param)
{
J
jp9000 已提交
1928
	if (!source_valid(source) || !source->info.enum_sources)
1929 1930 1931 1932
		return;

	obs_source_addref(source);

1933
	source->info.enum_sources(source->context.data, enum_callback, param);
1934 1935 1936 1937

	obs_source_release(source);
}

1938
void obs_source_enum_tree(obs_source_t *source,
1939 1940 1941 1942 1943
		obs_source_enum_proc_t enum_callback,
		void *param)
{
	struct source_enum_data data = {enum_callback, param};

J
jp9000 已提交
1944
	if (!source_valid(source) || !source->info.enum_sources)
1945 1946 1947 1948
		return;

	obs_source_addref(source);

1949 1950
	source->info.enum_sources(source->context.data,
			enum_source_tree_callback,
1951 1952 1953 1954
			&data);

	obs_source_release(source);
}
1955

J
jp9000 已提交
1956 1957 1958 1959 1960 1961 1962
struct descendant_info {
	bool exists;
	obs_source_t *target;
};

static void check_descendant(obs_source_t *parent, obs_source_t *child,
		void *param)
1963
{
J
jp9000 已提交
1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976
	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;
1977

1978 1979 1980 1981 1982
	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 已提交
1983 1984

	return true;
1985 1986
}

1987
void obs_source_remove_child(obs_source_t *parent, obs_source_t *child)
1988 1989 1990
{
	if (!parent || !child) return;

1991 1992 1993 1994 1995
	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);
	}
1996
}
J
jp9000 已提交
1997

1998
static void reset_transition_vol(obs_source_t *parent, obs_source_t *child,
J
jp9000 已提交
1999 2000 2001 2002 2003 2004 2005 2006
		void *param)
{
	child->transition_volume = 0.0f;

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

2007
static void add_transition_vol(obs_source_t *parent, obs_source_t *child,
J
jp9000 已提交
2008 2009 2010 2011 2012 2013 2014 2015
		void *param)
{
	float *vol = param;
	child->transition_volume += *vol;

	UNUSED_PARAMETER(parent);
}

2016
static void apply_transition_vol(obs_source_t *parent, obs_source_t *child,
J
jp9000 已提交
2017 2018 2019 2020 2021 2022 2023 2024
		void *param)
{
	child->present_volume = child->transition_volume;

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

2025
void obs_transition_begin_frame(obs_source_t *transition)
J
jp9000 已提交
2026 2027 2028 2029 2030
{
	if (!transition) return;
	obs_source_enum_tree(transition, reset_transition_vol, NULL);
}

2031
void obs_source_set_transition_vol(obs_source_t *source, float vol)
J
jp9000 已提交
2032 2033 2034 2035 2036 2037 2038
{
	if (!source) return;

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

2039
void obs_transition_end_frame(obs_source_t *transition)
J
jp9000 已提交
2040 2041 2042 2043
{
	if (!transition) return;
	obs_source_enum_tree(transition, apply_transition_vol, NULL);
}
2044

2045
void obs_source_save(obs_source_t *source)
2046
{
2047
	if (!source_valid(source) || !source->info.save) return;
2048 2049 2050
	source->info.save(source->context.data, source->context.settings);
}

2051
void obs_source_load(obs_source_t *source)
2052
{
2053
	if (!source_valid(source) || !source->info.load) return;
2054 2055
	source->info.load(source->context.data, source->context.settings);
}
J
jp9000 已提交
2056

J
jp9000 已提交
2057 2058 2059 2060 2061
bool obs_source_active(const obs_source_t *source)
{
	return source->activate_refs != 0;
}

J
jp9000 已提交
2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087
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 已提交
2088 2089 2090 2091 2092

void obs_source_draw_set_color_matrix(const struct matrix4 *color_matrix,
		const struct vec3 *color_range_min,
		const struct vec3 *color_range_max)
{
2093 2094 2095 2096 2097 2098
	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 已提交
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 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159
	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();
}