obs-source.c 44.3 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"
J
jp9000 已提交
22
#include "util/threading.h"
23
#include "util/platform.h"
24
#include "callback/calldata.h"
25 26
#include "graphics/matrix3.h"
#include "graphics/vec3.h"
27

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

J
jp9000 已提交
31
static inline const struct obs_source_info *find_source(struct darray *list,
32
		const char *id)
J
jp9000 已提交
33 34
{
	size_t i;
J
jp9000 已提交
35
	struct obs_source_info *array = list->array;
J
jp9000 已提交
36 37

	for (i = 0; i < list->num; i++) {
J
jp9000 已提交
38
		struct obs_source_info *info = array+i;
39
		if (strcmp(info->id, id) == 0)
J
jp9000 已提交
40 41 42 43 44 45
			return info;
	}

	return NULL;
}

J
jp9000 已提交
46
static const struct obs_source_info *get_source_info(enum obs_source_type type,
47 48 49 50 51
		const char *id)
{
	struct darray *list = NULL;

	switch (type) {
J
jp9000 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64
	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;

	case OBS_SOURCE_TYPE_SCENE:
65
	default:
J
jp9000 已提交
66
		blog(LOG_ERROR, "get_source_info: invalid source type");
67 68 69 70 71 72
		return NULL;
	}

	return find_source(list, id);
}

73 74 75 76 77 78 79 80 81 82 83 84
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)",
	"void volume(ptr source, in out float volume)",
	NULL
};

85 86
bool obs_source_init_context(struct obs_source *source,
		obs_data_t settings, const char *name)
87
{
88
	if (!obs_context_data_init(&source->context, settings, name))
89 90
		return false;

91 92
	return signal_handler_add_array(source->context.signals,
			source_signals);
93 94
}

95 96 97
const char *obs_source_getdisplayname(enum obs_source_type type,
		const char *id, const char *locale)
{
J
jp9000 已提交
98
	const struct obs_source_info *info = get_source_info(type, id);
99 100 101
	return (info != NULL) ? info->getname(locale) : NULL;
}

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

J
jp9000 已提交
114
	memcpy(&source->info, info, sizeof(struct obs_source_info));
115 116 117 118 119 120 121

	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 已提交
122

J
jp9000 已提交
123
	if (info->output_flags & OBS_SOURCE_AUDIO) {
J
jp9000 已提交
124
		source->audio_line = audio_output_createline(obs->audio.audio,
125
				source->context.name);
126 127
		if (!source->audio_line) {
			blog(LOG_ERROR, "Failed to create audio line for "
128
			                "source '%s'", source->context.name);
129 130 131
			return false;
		}
	}
132

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

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

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

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

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

165
	source = bzalloc(sizeof(struct obs_source));
166

167
	if (!obs_source_init_context(source, settings, name))
168 169
		goto fail;

J
jp9000 已提交
170
	if (info->defaults)
171
		info->defaults(source->context.settings);
J
jp9000 已提交
172

173 174
	source->context.data = info->create(source->context.settings, source);
	if (!source->context.data)
175 176
		goto fail;

J
jp9000 已提交
177
	if (!obs_source_init(source, info))
178
		goto fail;
J
jp9000 已提交
179

180
	obs_source_dosignal(source, "source_create", NULL);
J
jp9000 已提交
181
	return source;
182 183 184 185 186

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

189 190
void source_frame_init(struct source_frame *frame, enum video_format format,
		uint32_t width, uint32_t height)
191
{
192
	struct video_frame vid_frame;
J
jp9000 已提交
193 194 195 196

	if (!frame)
		return;

197
	video_frame_init(&vid_frame, format, width, height);
198 199 200
	frame->format = format;
	frame->width  = width;
	frame->height = height;
201

202 203 204
	for (size_t i = 0; i < MAX_AV_PLANES; i++) {
		frame->data[i]     = vid_frame.data[i];
		frame->linesize[i] = vid_frame.linesize[i];
205 206 207
	}
}

208
void obs_source_destroy(struct obs_source *source)
J
jp9000 已提交
209
{
210
	size_t i;
211

J
jp9000 已提交
212 213 214
	if (!source)
		return;

215 216
	obs_context_data_remove(&source->context);

217
	obs_source_dosignal(source, "source_destroy", "destroy");
218

219 220
	if (source->filter_parent)
		obs_source_filter_remove(source->filter_parent, source);
221

222 223
	for (i = 0; i < source->filters.num; i++)
		obs_source_release(source->filters.array[i]);
224

225 226
	for (i = 0; i < source->video_frames.num; i++)
		source_frame_destroy(source->video_frames.array[i]);
227

228
	gs_entercontext(obs->video.graphics);
229
	texture_destroy(source->async_texture);
230
	gs_leavecontext();
J
jp9000 已提交
231

232 233
	if (source->context.data)
		source->info.destroy(source->context.data);
234

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

238 239 240
	audio_line_destroy(source->audio_line);
	audio_resampler_destroy(source->resampler);

J
jp9000 已提交
241
	texrender_destroy(source->filter_texrender);
242 243 244 245 246
	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);
247
	obs_context_data_free(&source->context);
248 249 250
	bfree(source);
}

P
Palana 已提交
251
void obs_source_addref(obs_source_t source)
252
{
P
Palana 已提交
253
	if (source)
J
jp9000 已提交
254
		os_atomic_inc_long(&source->refs);
255 256
}

P
Palana 已提交
257
void obs_source_release(obs_source_t source)
258
{
P
Palana 已提交
259 260
	if (!source)
		return;
261

J
jp9000 已提交
262
	if (os_atomic_dec_long(&source->refs) == 0)
P
Palana 已提交
263
		obs_source_destroy(source);
264 265 266 267
}

void obs_source_remove(obs_source_t source)
{
268
	struct obs_core_data *data = &obs->data;
269
	size_t id;
270
	bool   exists;
271 272 273

	pthread_mutex_lock(&data->sources_mutex);

J
jp9000 已提交
274 275
	if (!source || source->removed) {
		pthread_mutex_unlock(&data->sources_mutex);
J
jp9000 已提交
276
		return;
J
jp9000 已提交
277
	}
J
jp9000 已提交
278

J
jp9000 已提交
279
	source->removed = true;
J
jp9000 已提交
280

J
jp9000 已提交
281 282
	obs_source_addref(source);

283 284 285 286
	id = da_find(data->user_sources, &source, 0);
	exists = (id != DARRAY_INVALID);
	if (exists) {
		da_erase(data->user_sources, id);
J
jp9000 已提交
287
		obs_source_release(source);
288 289 290
	}

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

292 293 294
	if (exists)
		obs_source_dosignal(source, "source_remove", "remove");

J
jp9000 已提交
295
	obs_source_release(source);
296 297 298 299
}

bool obs_source_removed(obs_source_t source)
{
J
jp9000 已提交
300
	return source ? source->removed : true;
J
jp9000 已提交
301 302
}

J
jp9000 已提交
303 304 305 306 307 308 309 310
static inline obs_data_t get_defaults(const struct obs_source_info *info)
{
	obs_data_t settings = obs_data_create();
	if (info->defaults)
		info->defaults(settings);
	return settings;
}

J
jp9000 已提交
311 312 313
obs_data_t obs_source_settings(enum obs_source_type type, const char *id)
{
	const struct obs_source_info *info = get_source_info(type, id);
J
jp9000 已提交
314
	return (info) ? get_defaults(info) : NULL;
J
jp9000 已提交
315 316
}

317
obs_properties_t obs_get_source_properties(enum obs_source_type type,
J
jp9000 已提交
318 319
		const char *id, const char *locale)
{
J
jp9000 已提交
320
	const struct obs_source_info *info = get_source_info(type, id);
J
jp9000 已提交
321 322 323 324 325 326 327 328 329
	if (info && info->properties) {
		obs_data_t       defaults = get_defaults(info);
		obs_properties_t properties;

		properties = info->properties(locale);
		obs_properties_apply_settings(properties, defaults);
		obs_data_release(defaults);
		return properties;
	}
J
jp9000 已提交
330 331 332
	return NULL;
}

333 334
obs_properties_t obs_source_properties(obs_source_t source, const char *locale)
{
J
jp9000 已提交
335 336 337
	if (source && source->info.properties) {
		obs_properties_t props;
		props = source->info.properties(locale);
338
		obs_properties_apply_settings(props, source->context.settings);
J
jp9000 已提交
339 340 341
		return props;
	}

342 343 344
	return NULL;
}

345
uint32_t obs_source_get_output_flags(obs_source_t source)
J
jp9000 已提交
346
{
J
jp9000 已提交
347
	return source ? source->info.output_flags : 0;
J
jp9000 已提交
348 349
}

350 351
static void obs_source_deferred_update(obs_source_t source)
{
352
	source->info.update(source->context.data, source->context.settings);
353 354 355
	source->defer_update = false;
}

356
void obs_source_update(obs_source_t source, obs_data_t settings)
J
jp9000 已提交
357
{
J
jp9000 已提交
358 359
	if (!source) return;

360
	obs_data_apply(source->context.settings, settings);
361

362 363 364 365
	if (source->info.update) {
		if (source->info.output_flags & OBS_SOURCE_VIDEO)
			source->defer_update = true;
		else
366 367
			source->info.update(source->context.data,
					source->context.settings);
368
	}
J
jp9000 已提交
369 370
}

371
static void activate_source(obs_source_t source)
J
jp9000 已提交
372
{
J
jp9000 已提交
373
	if (source->info.activate)
374
		source->info.activate(source->context.data);
375
	obs_source_dosignal(source, "source_activate", "activate");
J
jp9000 已提交
376 377
}

378
static void deactivate_source(obs_source_t source)
J
jp9000 已提交
379
{
J
jp9000 已提交
380
	if (source->info.deactivate)
381
		source->info.deactivate(source->context.data);
382
	obs_source_dosignal(source, "source_deactivate", "deactivate");
383
}
384

385 386 387
static void show_source(obs_source_t source)
{
	if (source->info.show)
388
		source->info.show(source->context.data);
389
	obs_source_dosignal(source, "source_show", "show");
390 391 392 393 394
}

static void hide_source(obs_source_t source)
{
	if (source->info.hide)
395
		source->info.hide(source->context.data);
396
	obs_source_dosignal(source, "source_hide", "hide");
397 398 399 400
}

static void activate_tree(obs_source_t parent, obs_source_t child, void *param)
{
J
jp9000 已提交
401
	if (os_atomic_inc_long(&child->activate_refs) == 1)
402
		activate_source(child);
J
jp9000 已提交
403 404 405

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
406 407 408 409 410
}

static void deactivate_tree(obs_source_t parent, obs_source_t child,
		void *param)
{
J
jp9000 已提交
411
	if (os_atomic_dec_long(&child->activate_refs) == 0)
412
		deactivate_source(child);
J
jp9000 已提交
413 414 415

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
416 417
}

418 419
static void show_tree(obs_source_t parent, obs_source_t child, void *param)
{
J
jp9000 已提交
420
	if (os_atomic_inc_long(&child->show_refs) == 1)
421 422 423 424 425 426 427 428
		show_source(child);

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

static void hide_tree(obs_source_t parent, obs_source_t child, void *param)
{
J
jp9000 已提交
429
	if (os_atomic_dec_long(&child->show_refs) == 0)
430 431 432 433 434 435 436
		hide_source(child);

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

void obs_source_activate(obs_source_t source, enum view_type type)
437 438 439
{
	if (!source) return;

J
jp9000 已提交
440
	if (os_atomic_inc_long(&source->show_refs) == 1) {
441 442 443 444 445
		show_source(source);
		obs_source_enum_tree(source, show_tree, NULL);
	}

	if (type == MAIN_VIEW) {
J
jp9000 已提交
446
		if (os_atomic_inc_long(&source->activate_refs) == 1) {
447 448 449 450
			activate_source(source);
			obs_source_enum_tree(source, activate_tree, NULL);
			obs_source_set_present_volume(source, 1.0f);
		}
451 452 453
	}
}

454
void obs_source_deactivate(obs_source_t source, enum view_type type)
455 456 457
{
	if (!source) return;

J
jp9000 已提交
458
	if (os_atomic_dec_long(&source->show_refs) == 0) {
459 460 461 462 463
		hide_source(source);
		obs_source_enum_tree(source, hide_tree, NULL);
	}

	if (type == MAIN_VIEW) {
J
jp9000 已提交
464
		if (os_atomic_dec_long(&source->activate_refs) == 0) {
465 466 467 468
			deactivate_source(source);
			obs_source_enum_tree(source, deactivate_tree, NULL);
			obs_source_set_present_volume(source, 0.0f);
		}
469
	}
J
jp9000 已提交
470 471
}

472
void obs_source_video_tick(obs_source_t source, float seconds)
J
jp9000 已提交
473
{
J
jp9000 已提交
474 475
	if (!source) return;

476 477 478
	if (source->defer_update)
		obs_source_deferred_update(source);

J
jp9000 已提交
479 480 481 482 483
	/* reset the filter render texture information once every frame */
	if (source->filter_texrender)
		texrender_reset(source->filter_texrender);

	if (source->info.video_tick)
484
		source->info.video_tick(source->context.data, seconds);
J
jp9000 已提交
485 486
}

487
/* unless the value is 3+ hours worth of frames, this won't overflow */
J
jp9000 已提交
488
static inline uint64_t conv_frames_to_time(size_t frames)
489
{
J
jp9000 已提交
490 491
	const struct audio_output_info *info;
	info = audio_output_getinfo(obs->audio.audio);
492 493 494

	return (uint64_t)frames * 1000000000ULL /
		(uint64_t)info->samples_per_sec;
495 496
}

497
/* maximum "direct" timestamp variance in nanoseconds */
498
#define MAX_TS_VAR          5000000000ULL
499
/* maximum time that timestamp can jump in nanoseconds */
500 501 502 503
#define MAX_TIMESTAMP_JUMP  2000000000ULL
/* time threshold in nanoseconds to ensure audio timing is as seamless as
 * possible */
#define TS_SMOOTHING_THRESHOLD 70000000ULL
504 505 506 507 508 509

static inline void reset_audio_timing(obs_source_t source, uint64_t timetamp)
{
	source->timing_set    = true;
	source->timing_adjust = os_gettime_ns() - timetamp;
}
510

511 512
static inline void handle_ts_jump(obs_source_t source, uint64_t expected,
		uint64_t ts, uint64_t diff)
513
{
J
jp9000 已提交
514
	blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%"PRIu64"', "
515
	                "expected value %"PRIu64", input value %"PRIu64,
516
	                source->context.name, diff, expected, ts);
517 518

	/* if has video, ignore audio data until reset */
519
	if (source->info.output_flags & OBS_SOURCE_ASYNC)
J
jp9000 已提交
520
		os_atomic_dec_long(&source->audio_reset_ref);
521 522 523 524
	else 
		reset_audio_timing(source, ts);
}

525 526 527 528
static void source_output_audio_line(obs_source_t source,
		const struct audio_data *data)
{
	struct audio_data in = *data;
529
	uint64_t diff;
530 531

	if (!source->timing_set) {
532
		reset_audio_timing(source, in.timestamp);
533 534

		/* detects 'directly' set timestamps as long as they're within
535
		 * a certain threshold */
536
		if ((source->timing_adjust + MAX_TS_VAR) < MAX_TS_VAR * 2)
537
			source->timing_adjust = 0;
538

539
	} else {
540
		bool ts_under = (in.timestamp < source->next_audio_ts_min);
541

542 543 544 545 546
		diff = ts_under ?
			(source->next_audio_ts_min - in.timestamp) :
			(in.timestamp - source->next_audio_ts_min);

		/* smooth audio if lower or within threshold */
547
		if (diff > MAX_TIMESTAMP_JUMP)
548 549 550 551
			handle_ts_jump(source, source->next_audio_ts_min,
					in.timestamp, diff);
		else if (ts_under || diff < TS_SMOOTHING_THRESHOLD)
			in.timestamp = source->next_audio_ts_min;
552 553
	}

554
	source->next_audio_ts_min = in.timestamp +
J
jp9000 已提交
555
		conv_frames_to_time(in.frames);
556

557 558 559
	if (source->audio_reset_ref != 0)
		return;

J
jp9000 已提交
560
	in.timestamp += source->timing_adjust + source->sync_offset;
J
jp9000 已提交
561 562
	in.volume = source->user_volume * source->present_volume *
		obs->audio.user_volume * obs->audio.present_volume;
563

564 565 566
	audio_line_output(source->audio_line, &in);
}

567 568 569 570 571 572 573 574
enum convert_type {
	CONVERT_NONE,
	CONVERT_NV12,
	CONVERT_420,
	CONVERT_422_U,
	CONVERT_422_Y,
};

575
static inline enum convert_type get_convert_type(enum video_format format)
576
{
577
	switch (format) {
578 579 580 581 582 583 584 585 586 587 588
	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;

589
	case VIDEO_FORMAT_NONE:
590 591 592 593 594 595 596 597 598
	case VIDEO_FORMAT_RGBA:
	case VIDEO_FORMAT_BGRA:
	case VIDEO_FORMAT_BGRX:
		return CONVERT_NONE;
	}

	return CONVERT_NONE;
}

599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
static inline bool set_packed422_sizes(struct obs_source *source,
		struct source_frame *frame)
{
	source->async_convert_height = frame->height;
	source->async_convert_width = frame->width / 2;
	return true;
}

static inline bool init_gpu_conversion(struct obs_source *source,
		struct source_frame *frame)
{
	switch (get_convert_type(frame->format)) {
		case CONVERT_422_Y:
		case CONVERT_422_U:
			return set_packed422_sizes(source, frame);

		case CONVERT_NV12:
		case CONVERT_420:
			/* TODO: implement conversion */
			break;

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

	}
	return false;
}

static inline bool set_async_texture_size(struct obs_source *source,
		struct source_frame *frame)
{
	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;
	}

	texture_destroy(source->async_texture);
	texrender_destroy(source->async_convert_texrender);
	source->async_convert_texrender = NULL;

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

		source->async_convert_texrender =
			texrender_create(GS_RGBA, GS_ZS_NONE);

		source->async_texture = gs_create_texture(
				source->async_convert_width,
				source->async_convert_height,
				GS_RGBA, 1, NULL, GS_DYNAMIC);

	} else {
		source->async_gpu_conversion = false;

		source->async_texture = gs_create_texture(
				frame->width, frame->height,
				GS_RGBA, 1, NULL, GS_DYNAMIC);
	}

	if (!source->async_texture)
		return false;

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

static void upload_raw_frame(texture_t tex, const struct source_frame *frame)
{
	switch (get_convert_type(frame->format)) {
		case CONVERT_422_U:
		case CONVERT_422_Y:
			texture_setimage(tex, frame->data[0],
					frame->linesize[0], false);
			break;

		case CONVERT_NV12:
		case CONVERT_420:
			assert(false && "Conversion not yet implemented");
			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:
			return "UYUV_Reverse";

		case VIDEO_FORMAT_YUY2:
			return "YUY2_Reverse";

		case VIDEO_FORMAT_YVYU:
			return "YVYU_Reverse";

		case VIDEO_FORMAT_NV12:
		case VIDEO_FORMAT_I420:
			assert(false && "Conversion not yet implemented");
			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;
}

static inline void set_eparam(effect_t effect, const char *name, float val)
{
	eparam_t param = effect_getparambyname(effect, name);
	effect_setfloat(effect, param, val);
}

static bool update_async_texrender(struct obs_source *source,
		const struct source_frame *frame)
{
	texture_t   tex       = source->async_texture;
	texrender_t texrender = source->async_convert_texrender;

	texrender_reset(texrender);

	upload_raw_frame(tex, frame);

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

	effect_t conv = obs->video.conversion_effect;
	technique_t tech = effect_gettechnique(conv,
			select_conversion_technique(frame->format));

	if (!texrender_begin(texrender, cx, cy))
		return false;

	technique_begin(tech);
	technique_beginpass(tech, 0);

	effect_settexture(conv, effect_getparambyname(conv, "image"),
			tex);
	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);
	set_eparam(conv, "width_d2",  cx  * 0.5f);
	set_eparam(conv, "height_d2", cy * 0.5f);
	set_eparam(conv, "width_d2_i",  1.0f / (cx  * 0.5f));
	set_eparam(conv, "height_d2_i", 1.0f / (cy * 0.5f));
	set_eparam(conv, "input_height", (float)cy);

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

	gs_draw_sprite(tex, 0, cx, cy);

	technique_endpass(tech);
	technique_end(tech);

	texrender_end(texrender);

	return true;
}

772 773
static bool update_async_texture(struct obs_source *source,
		const struct source_frame *frame)
774
{
775 776 777
	texture_t         tex       = source->async_texture;
	texrender_t       texrender = source->async_convert_texrender;
	enum convert_type type      = get_convert_type(frame->format);
778 779 780
	void              *ptr;
	uint32_t          linesize;

781 782 783
	source->async_format     = frame->format;
	source->async_flip       = frame->flip;
	source->async_full_range = frame->full_range;
784 785
	memcpy(source->async_color_matrix, frame->color_matrix,
			sizeof(frame->color_matrix));
786 787 788 789
	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);
790

791 792 793
	if (source->async_gpu_conversion && texrender)
		return update_async_texrender(source, frame);

794
	if (type == CONVERT_NONE) {
795
		texture_setimage(tex, frame->data[0], frame->linesize[0],
796
				false);
797 798 799
		return true;
	}

800
	if (!texture_map(tex, &ptr, &linesize))
801 802 803
		return false;

	if (type == CONVERT_420)
J
jp9000 已提交
804 805 806
		decompress_420((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
807 808

	else if (type == CONVERT_NV12)
J
jp9000 已提交
809 810 811
		decompress_nv12((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
812 813

	else if (type == CONVERT_422_Y)
814
		decompress_422(frame->data[0], frame->linesize[0],
J
jp9000 已提交
815
				0, frame->height, ptr, linesize, true);
816 817

	else if (type == CONVERT_422_U)
818
		decompress_422(frame->data[0], frame->linesize[0],
J
jp9000 已提交
819
				0, frame->height, ptr, linesize, false);
820 821 822 823 824

	texture_unmap(tex);
	return true;
}

825 826 827
static float const full_range_min[] = {0., 0., 0.};
static float const full_range_max[] = {1., 1., 1.};

828
static inline void obs_source_draw_texture(struct obs_source *source,
829 830
		effect_t effect, float *color_matrix,
		float const *color_range_min, float const *color_range_max)
831
{
832 833
	texture_t tex = source->async_texture;
	eparam_t  param;
834

835 836 837
	if (source->async_convert_texrender)
		tex = texrender_gettexture(source->async_convert_texrender);

838
	if (color_matrix) {
839 840 841 842 843 844 845 846 847 848 849 850
		size_t const size = sizeof(float) * 3;
		
		if (!color_range_min)
			color_range_min = full_range_min;
		if (!color_range_max)
			color_range_max = full_range_max;
		
		param = effect_getparambyname(effect, "color_range_min");
		effect_setval(effect, param, color_range_min, size);
		param = effect_getparambyname(effect, "color_range_max");
		effect_setval(effect, param, color_range_max, size);

851
		param = effect_getparambyname(effect, "color_matrix");
852
		effect_setval(effect, param, color_matrix, sizeof(float) * 16);
853 854
	}

J
jp9000 已提交
855
	param = effect_getparambyname(effect, "image");
856 857
	effect_settexture(effect, param, tex);

858 859
	gs_draw_sprite(tex, source->async_flip ? GS_FLIP_V : 0, 0, 0);
}
860

861 862
static void obs_source_draw_async_texture(struct obs_source *source)
{
863 864 865 866 867
	effect_t    effect        = gs_geteffect();
	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);
868 869 870 871 872 873 874 875 876 877
	technique_t tech;

	if (def_draw) {
		effect = obs_get_default_effect();
		tech = effect_gettechnique(effect, type);
		technique_begin(tech);
		technique_beginpass(tech, 0);
	}

	obs_source_draw_texture(source, effect,
878 879 880
			yuv ? source->async_color_matrix : NULL,
			limited_range ? source->async_color_range_min : NULL,
			limited_range ? source->async_color_range_max : NULL);
881 882 883 884 885

	if (def_draw) {
		technique_endpass(tech);
		technique_end(tech);
	}
886 887
}

888 889 890
static void obs_source_render_async_video(obs_source_t source)
{
	struct source_frame *frame = obs_source_getframe(source);
891 892 893 894 895 896
	if (frame) {
		if (!set_async_texture_size(source, frame))
			return;
		if (!update_async_texture(source, frame))
			return;
	}
897

898 899
	if (source->async_texture)
		obs_source_draw_async_texture(source);
900 901 902 903

	obs_source_releaseframe(source, frame);
}

904 905 906 907 908 909 910
static inline void obs_source_render_filters(obs_source_t source)
{
	source->rendering_filter = true;
	obs_source_video_render(source->filters.array[0]);
	source->rendering_filter = false;
}

J
jp9000 已提交
911 912
static inline void obs_source_default_render(obs_source_t source,
		bool color_matrix)
913 914
{
	effect_t    effect     = obs->video.default_effect;
J
jp9000 已提交
915
	const char  *tech_name = color_matrix ? "DrawMatrix" : "Draw";
916 917 918 919 920 921
	technique_t tech       = effect_gettechnique(effect, tech_name);
	size_t      passes, i;

	passes = technique_begin(tech);
	for (i = 0; i < passes; i++) {
		technique_beginpass(tech, i);
922
		source->info.video_render(source->context.data, effect);
923 924 925 926 927 928 929
		technique_endpass(tech);
	}
	technique_end(tech);
}

static inline void obs_source_main_render(obs_source_t source)
{
930 931 932
	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;
933 934
	bool default_effect = !source->filter_parent &&
	                      source->filters.num == 0 &&
935
	                      !custom_draw;
936 937

	if (default_effect)
J
jp9000 已提交
938
		obs_source_default_render(source, color_matrix);
939
	else
940
		source->info.video_render(source->context.data,
941
				custom_draw ? NULL : gs_geteffect());
942 943
}

944
void obs_source_video_render(obs_source_t source)
J
jp9000 已提交
945
{
J
jp9000 已提交
946 947
	if (!source) return;

948 949
	if (source->filters.num && !source->rendering_filter)
		obs_source_render_filters(source);
950

951 952 953 954
	else if (source->info.video_render)
		obs_source_main_render(source);

	else if (source->filter_target)
955 956
		obs_source_video_render(source->filter_target);

957
	else
958
		obs_source_render_async_video(source);
J
jp9000 已提交
959 960
}

J
jp9000 已提交
961
uint32_t obs_source_getwidth(obs_source_t source)
J
jp9000 已提交
962
{
963 964 965
	if (!source) return 0;

	if (source->info.getwidth)
966
		return source->info.getwidth(source->context.data);
967
	return source->async_width;
J
jp9000 已提交
968 969
}

J
jp9000 已提交
970
uint32_t obs_source_getheight(obs_source_t source)
J
jp9000 已提交
971
{
972 973 974
	if (!source) return 0;

	if (source->info.getheight)
975
		return source->info.getheight(source->context.data);
976
	return source->async_height;
J
jp9000 已提交
977 978
}

979 980
obs_source_t obs_filter_getparent(obs_source_t filter)
{
J
jp9000 已提交
981
	return filter ? filter->filter_parent : NULL;
982 983
}

984
obs_source_t obs_filter_gettarget(obs_source_t filter)
J
jp9000 已提交
985
{
J
jp9000 已提交
986
	return filter ? filter->filter_target : NULL;
J
jp9000 已提交
987 988
}

989
void obs_source_filter_add(obs_source_t source, obs_source_t filter)
J
jp9000 已提交
990
{
J
jp9000 已提交
991 992 993
	if (!source || !filter)
		return;

994 995
	pthread_mutex_lock(&source->filter_mutex);

J
jp9000 已提交
996
	if (da_find(source->filters, &filter, 0) != DARRAY_INVALID) {
J
jp9000 已提交
997 998 999 1000 1001 1002
		blog(LOG_WARNING, "Tried to add a filter that was already "
		                  "present on the source");
		return;
	}

	if (source->filters.num) {
1003
		obs_source_t *back = da_end(source->filters);
J
jp9000 已提交
1004 1005 1006 1007
		(*back)->filter_target = filter;
	}

	da_push_back(source->filters, &filter);
1008 1009 1010 1011

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = source;
J
jp9000 已提交
1012 1013 1014
	filter->filter_target = source;
}

1015
void obs_source_filter_remove(obs_source_t source, obs_source_t filter)
J
jp9000 已提交
1016
{
1017 1018
	size_t idx;

J
jp9000 已提交
1019 1020 1021
	if (!source || !filter)
		return;

1022 1023 1024
	pthread_mutex_lock(&source->filter_mutex);

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
1025
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
1026 1027 1028
		return;

	if (idx > 0) {
1029
		obs_source_t prev = source->filters.array[idx-1];
J
jp9000 已提交
1030 1031 1032 1033
		prev->filter_target = filter->filter_target;
	}

	da_erase(source->filters, idx);
1034 1035 1036 1037

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = NULL;
J
jp9000 已提交
1038 1039 1040
	filter->filter_target = NULL;
}

1041
void obs_source_filter_setorder(obs_source_t source, obs_source_t filter,
J
jp9000 已提交
1042 1043
		enum order_movement movement)
{
J
jp9000 已提交
1044 1045 1046 1047 1048 1049
	size_t idx, i;

	if (!source || !filter)
		return;

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
1050
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
		return;

	if (movement == ORDER_MOVE_UP) {
		if (idx == source->filters.num-1)
			return;
		da_move_item(source->filters, idx, idx+1);

	} else if (movement == ORDER_MOVE_DOWN) {
		if (idx == 0)
			return;
		da_move_item(source->filters, idx, idx-1);

	} else if (movement == ORDER_MOVE_TOP) {
		if (idx == source->filters.num-1)
			return;
		da_move_item(source->filters, idx, source->filters.num-1);

	} else if (movement == ORDER_MOVE_BOTTOM) {
		if (idx == 0)
			return;
		da_move_item(source->filters, idx, 0);
	}

1074
	/* reorder filter targets, not the nicest way of dealing with things */
J
jp9000 已提交
1075
	for (i = 0; i < source->filters.num; i++) {
1076
		obs_source_t next_filter = (i == source->filters.num-1) ?
J
jp9000 已提交
1077 1078 1079 1080 1081
			source : source->filters.array[idx+1];
		source->filters.array[i]->filter_target = next_filter;
	}
}

1082
obs_data_t obs_source_getsettings(obs_source_t source)
J
jp9000 已提交
1083
{
J
jp9000 已提交
1084 1085
	if (!source) return NULL;

1086 1087
	obs_data_addref(source->context.settings);
	return source->context.settings;
J
jp9000 已提交
1088 1089
}

1090 1091
static inline struct source_frame *filter_async_video(obs_source_t source,
		struct source_frame *in)
1092 1093 1094 1095
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
J
jp9000 已提交
1096
		if (filter->info.filter_video) {
1097 1098
			in = filter->info.filter_video(filter->context.data,
					in);
1099 1100 1101 1102 1103 1104 1105 1106
			if (!in)
				return NULL;
		}
	}

	return in;
}

1107 1108 1109
static inline void copy_frame_data_line(struct source_frame *dst,
		const struct source_frame *src, uint32_t plane, uint32_t y)
{
1110 1111 1112 1113
	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];
1114 1115 1116 1117 1118 1119 1120

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

static inline void copy_frame_data_plane(struct source_frame *dst,
		const struct source_frame *src, uint32_t plane, uint32_t lines)
{
1121
	if (dst->linesize[plane] != src->linesize[plane])
1122 1123 1124 1125
		for (uint32_t y = 0; y < lines; y++)
			copy_frame_data_line(dst, src, plane, y);
	else
		memcpy(dst->data[plane], src->data[plane],
1126
				dst->linesize[plane] * lines);
1127 1128 1129 1130 1131 1132
}

static void copy_frame_data(struct source_frame *dst,
		const struct source_frame *src)
{
	dst->flip         = src->flip;
1133
	dst->full_range   = src->full_range;
1134 1135
	dst->timestamp    = src->timestamp;
	memcpy(dst->color_matrix, src->color_matrix, sizeof(float) * 16);
1136 1137 1138 1139 1140
	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);
	}
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164

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

J
jp9000 已提交
1165
static inline struct source_frame *cache_video(const struct source_frame *frame)
1166
{
1167
	/* TODO: use an actual cache */
1168
	struct source_frame *new_frame = source_frame_create(frame->format,
1169
			frame->width, frame->height);
1170

1171
	copy_frame_data(new_frame, frame);
1172
	return new_frame;
1173 1174 1175
}

void obs_source_output_video(obs_source_t source,
1176
		const struct source_frame *frame)
1177
{
J
jp9000 已提交
1178 1179 1180
	if (!source || !frame)
		return;

J
jp9000 已提交
1181
	struct source_frame *output = cache_video(frame);
1182 1183 1184 1185 1186

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

1187 1188 1189 1190 1191
	if (output) {
		pthread_mutex_lock(&source->video_mutex);
		da_push_back(source->video_frames, &output);
		pthread_mutex_unlock(&source->video_mutex);
	}
1192 1193
}

1194 1195
static inline struct filtered_audio *filter_async_audio(obs_source_t source,
		struct filtered_audio *in)
1196 1197 1198 1199
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
J
jp9000 已提交
1200
		if (filter->info.filter_audio) {
1201 1202
			in = filter->info.filter_audio(filter->context.data,
					in);
1203 1204 1205 1206 1207 1208 1209 1210
			if (!in)
				return NULL;
		}
	}

	return in;
}

1211
static inline void reset_resampler(obs_source_t source,
1212 1213
		const struct source_audio *audio)
{
J
jp9000 已提交
1214
	const struct audio_output_info *obs_info;
1215 1216
	struct resample_info output_info;

1217 1218
	obs_info = audio_output_getinfo(obs->audio.audio);

1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
	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");
}

static inline void copy_audio_data(obs_source_t source,
J
jp9000 已提交
1244
		const uint8_t *const data[], uint32_t frames, uint64_t ts)
1245
{
1246
	size_t planes    = audio_output_planes(obs->audio.audio);
1247
	size_t blocksize = audio_output_blocksize(obs->audio.audio);
1248 1249
	size_t size      = (size_t)frames * blocksize;
	bool   resize    = source->audio_storage_size < size;
1250

J
jp9000 已提交
1251 1252
	source->audio_data.frames    = frames;
	source->audio_data.timestamp = ts;
1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265

	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;
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279
}

/* resamples/remixes new audio to the designated main audio output format */
static void process_audio(obs_source_t source, const struct source_audio *audio)
{
	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 已提交
1280
		uint8_t  *output[MAX_AV_PLANES];
1281 1282 1283
		uint32_t frames;
		uint64_t offset;

1284 1285 1286 1287 1288
		memset(output, 0, sizeof(output));

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

J
jp9000 已提交
1290
		copy_audio_data(source, (const uint8_t *const *)output, frames,
1291 1292 1293 1294 1295
				audio->timestamp - offset);
	} else {
		copy_audio_data(source, audio->data, audio->frames,
				audio->timestamp);
	}
1296 1297 1298 1299 1300
}

void obs_source_output_audio(obs_source_t source,
		const struct source_audio *audio)
{
J
jp9000 已提交
1301
	uint32_t flags;
1302
	struct filtered_audio *output;
1303

J
jp9000 已提交
1304 1305 1306 1307
	if (!source || !audio)
		return;

	flags = source->info.output_flags;
1308
	process_audio(source, audio);
1309 1310

	pthread_mutex_lock(&source->filter_mutex);
1311
	output = filter_async_audio(source, &source->audio_data);
1312 1313

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

1316 1317
		pthread_mutex_lock(&source->audio_mutex);

1318 1319
		/* wait for video to start before outputting any audio so we
		 * have a base for sync */
1320
		if (source->timing_set || !async) {
1321
			struct audio_data data;
1322

J
jp9000 已提交
1323
			for (int i = 0; i < MAX_AV_PLANES; i++)
1324 1325
				data.data[i] = output->data[i];

1326 1327 1328
			data.frames    = output->frames;
			data.timestamp = output->timestamp;
			source_output_audio_line(source, &data);
1329 1330 1331 1332 1333 1334 1335 1336
		}

		pthread_mutex_unlock(&source->audio_mutex);
	}

	pthread_mutex_unlock(&source->filter_mutex);
}

1337 1338 1339 1340 1341 1342
static inline bool frame_out_of_bounds(obs_source_t source, uint64_t ts)
{
	return ((ts - source->last_frame_ts) > MAX_TIMESTAMP_JUMP);
}

static inline struct source_frame *get_closest_frame(obs_source_t source,
J
jp9000 已提交
1343
		uint64_t sys_time, int *audio_time_refs)
1344 1345 1346 1347 1348 1349 1350 1351 1352 1353
{
	struct source_frame *next_frame = source->video_frames.array[0];
	struct source_frame *frame      = NULL;
	uint64_t sys_offset = sys_time - source->last_sys_timestamp;
	uint64_t frame_time = next_frame->timestamp;
	uint64_t frame_offset = 0;

	/* account for timestamp invalidation */
	if (frame_out_of_bounds(source, frame_time)) {
		source->last_frame_ts = next_frame->timestamp;
J
jp9000 已提交
1354
		(*audio_time_refs)++;
1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374
	} else {
		frame_offset = frame_time - source->last_frame_ts;
		source->last_frame_ts += sys_offset;
	}

	while (frame_offset <= sys_offset) {
		source_frame_destroy(frame);

		frame = next_frame;
		da_erase(source->video_frames, 0);

		if (!source->video_frames.num)
			break;

		next_frame = source->video_frames.array[0];

		/* more timestamp checking and compensating */
		if ((next_frame->timestamp - frame_time) > MAX_TIMESTAMP_JUMP) {
			source->last_frame_ts =
				next_frame->timestamp - frame_offset;
J
jp9000 已提交
1375
			(*audio_time_refs)++;
1376 1377 1378 1379 1380 1381 1382 1383 1384
		}

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

	return frame;
}

1385
/*
1386 1387
 * Ensures that cached frames are displayed on time.  If multiple frames
 * were cached between renders, then releases the unnecessary frames and uses
1388 1389
 * the frame with the closest timing to ensure sync.  Also ensures that timing
 * with audio is synchronized.
1390
 */
1391
struct source_frame *obs_source_getframe(obs_source_t source)
J
jp9000 已提交
1392
{
1393
	struct source_frame *frame = NULL;
J
jp9000 已提交
1394
	int      audio_time_refs = 0;
1395
	uint64_t sys_time;
1396

J
jp9000 已提交
1397 1398 1399
	if (!source)
		return NULL;

1400 1401 1402 1403 1404
	pthread_mutex_lock(&source->video_mutex);

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

1405
	sys_time = os_gettime_ns();
1406

1407 1408
	if (!source->last_frame_ts) {
		frame = source->video_frames.array[0];
1409 1410
		da_erase(source->video_frames, 0);

1411
		source->last_frame_ts = frame->timestamp;
1412
	} else {
J
jp9000 已提交
1413 1414 1415 1416 1417 1418 1419 1420
		frame = get_closest_frame(source, sys_time, &audio_time_refs);
	}

	/* reset timing to current system time */
	if (frame) {
		source->audio_reset_ref += audio_time_refs;
		source->timing_adjust = sys_time - frame->timestamp;
		source->timing_set = true;
1421 1422 1423 1424 1425 1426
	}

	source->last_sys_timestamp = sys_time;

unlock:
	pthread_mutex_unlock(&source->video_mutex);
1427

1428
	if (frame)
1429 1430
		obs_source_addref(source);

1431
	return frame;
J
jp9000 已提交
1432 1433
}

1434
void obs_source_releaseframe(obs_source_t source, struct source_frame *frame)
J
jp9000 已提交
1435
{
J
jp9000 已提交
1436
	if (source && frame) {
1437 1438 1439
		source_frame_destroy(frame);
		obs_source_release(source);
	}
J
jp9000 已提交
1440
}
1441 1442 1443

const char *obs_source_getname(obs_source_t source)
{
1444
	return source ? source->context.name : NULL;
1445 1446 1447 1448
}

void obs_source_setname(obs_source_t source, const char *name)
{
J
jp9000 已提交
1449
	if (!source) return;
1450
	obs_context_data_setname(&source->context, name);
1451 1452
}

1453
void obs_source_gettype(obs_source_t source, enum obs_source_type *type,
1454 1455
		const char **id)
{
J
jp9000 已提交
1456 1457
	if (!source) return;

J
jp9000 已提交
1458
	if (type) *type = source->info.type;
J
jp9000 已提交
1459
	if (id)   *id   = source->info.id;
1460
}
1461 1462

static inline void render_filter_bypass(obs_source_t target, effect_t effect,
J
jp9000 已提交
1463
		bool use_matrix)
1464
{
J
jp9000 已提交
1465
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478
	technique_t tech       = effect_gettechnique(effect, tech_name);
	size_t      passes, i;

	passes = technique_begin(tech);
	for (i = 0; i < passes; i++) {
		technique_beginpass(tech, i);
		obs_source_video_render(target);
		technique_endpass(tech);
	}
	technique_end(tech);
}

static inline void render_filter_tex(texture_t tex, effect_t effect,
J
jp9000 已提交
1479
		uint32_t width, uint32_t height, bool use_matrix)
1480
{
J
jp9000 已提交
1481
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1482
	technique_t tech       = effect_gettechnique(effect, tech_name);
J
jp9000 已提交
1483
	eparam_t    image      = effect_getparambyname(effect, "image");
1484 1485
	size_t      passes, i;

J
jp9000 已提交
1486
	effect_settexture(effect, image, tex);
1487 1488 1489 1490 1491 1492 1493 1494 1495 1496

	passes = technique_begin(tech);
	for (i = 0; i < passes; i++) {
		technique_beginpass(tech, i);
		gs_draw_sprite(tex, width, height, 0);
		technique_endpass(tech);
	}
	technique_end(tech);
}

J
jp9000 已提交
1497 1498
void obs_source_process_filter(obs_source_t filter, effect_t effect,
		uint32_t width, uint32_t height, enum gs_color_format format,
1499
		enum allow_direct_render allow_direct)
1500
{
J
jp9000 已提交
1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516
	obs_source_t target, parent;
	uint32_t     target_flags, parent_flags;
	int          cx, cy;
	bool         use_matrix, expects_def, can_directly;

	if (!filter) return;

	target       = obs_filter_gettarget(filter);
	parent       = obs_filter_getparent(filter);
	target_flags = target->info.output_flags;
	parent_flags = parent->info.output_flags;
	cx           = obs_source_getwidth(target);
	cy           = obs_source_getheight(target);
	use_matrix   = !!(target_flags & OBS_SOURCE_COLOR_MATRIX);
	expects_def  = !(parent_flags & OBS_SOURCE_CUSTOM_DRAW);
	can_directly = allow_direct == ALLOW_DIRECT_RENDERING;
1517 1518 1519 1520 1521

	/* 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 */
1522
	if (can_directly && expects_def && target == parent) {
J
jp9000 已提交
1523
		render_filter_bypass(target, effect, use_matrix);
1524 1525 1526
		return;
	}

J
jp9000 已提交
1527 1528 1529 1530 1531
	if (!filter->filter_texrender)
		filter->filter_texrender = texrender_create(format,
				GS_ZS_NONE);

	if (texrender_begin(filter->filter_texrender, cx, cy)) {
1532
		gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
1533
		if (expects_def && parent == target)
J
jp9000 已提交
1534
			obs_source_default_render(parent, use_matrix);
1535 1536
		else
			obs_source_video_render(target);
J
jp9000 已提交
1537
		texrender_end(filter->filter_texrender);
1538 1539 1540 1541
	}

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

J
jp9000 已提交
1542 1543
	render_filter_tex(texrender_gettexture(filter->filter_texrender),
			effect, width, height, use_matrix);
1544
}
1545 1546 1547

signal_handler_t obs_source_signalhandler(obs_source_t source)
{
1548
	return source ? source->context.signals : NULL;
1549 1550 1551 1552
}

proc_handler_t obs_source_prochandler(obs_source_t source)
{
1553
	return source ? source->context.procs : NULL;
1554
}
J
jp9000 已提交
1555 1556 1557

void obs_source_setvolume(obs_source_t source, float volume)
{
J
jp9000 已提交
1558 1559 1560 1561 1562
	if (source) {
		struct calldata data = {0};
		calldata_setptr(&data, "source", source);
		calldata_setfloat(&data, "volume", volume);

1563
		signal_handler_signal(source->context.signals, "volume", &data);
1564
		signal_handler_signal(obs->signals, "source_volume", &data);
J
jp9000 已提交
1565

1566
		volume = (float)calldata_float(&data, "volume");
J
jp9000 已提交
1567 1568
		calldata_free(&data);

J
jp9000 已提交
1569
		source->user_volume = volume;
J
jp9000 已提交
1570
	}
J
jp9000 已提交
1571 1572
}

J
jp9000 已提交
1573 1574 1575 1576 1577 1578 1579 1580 1581
static void set_tree_preset_vol(obs_source_t parent, obs_source_t child,
		void *param)
{
	float *vol = param;
	child->present_volume = *vol;

	UNUSED_PARAMETER(parent);
}

J
jp9000 已提交
1582 1583
void obs_source_set_present_volume(obs_source_t source, float volume)
{
J
jp9000 已提交
1584
	if (source) {
J
jp9000 已提交
1585
		source->present_volume = volume;
J
jp9000 已提交
1586 1587 1588 1589 1590 1591 1592 1593

		/* 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 已提交
1594 1595 1596 1597
}

float obs_source_getvolume(obs_source_t source)
{
J
jp9000 已提交
1598
	return source ? source->user_volume : 0.0f;
J
jp9000 已提交
1599 1600 1601 1602
}

float obs_source_get_present_volume(obs_source_t source)
{
J
jp9000 已提交
1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614
	return source ? source->present_volume : 0.0f;
}

void obs_source_set_sync_offset(obs_source_t source, int64_t offset)
{
	if (source)
		source->sync_offset = offset;
}

int64_t obs_source_get_sync_offset(obs_source_t source)
{
	return source ? source->sync_offset : 0;
J
jp9000 已提交
1615
}
1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627

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

static void enum_source_tree_callback(obs_source_t parent, obs_source_t child,
		void *param)
{
	struct source_enum_data *data = param;

	if (child->info.enum_sources && !child->enum_refs) {
J
jp9000 已提交
1628
		os_atomic_inc_long(&child->enum_refs);
1629

1630
		child->info.enum_sources(child->context.data,
1631 1632
				enum_source_tree_callback, data);

J
jp9000 已提交
1633
		os_atomic_dec_long(&child->enum_refs);
1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647
	}

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

void obs_source_enum_sources(obs_source_t source,
		obs_source_enum_proc_t enum_callback,
		void *param)
{
	if (!source || !source->info.enum_sources || source->enum_refs)
		return;

	obs_source_addref(source);

J
jp9000 已提交
1648
	os_atomic_inc_long(&source->enum_refs);
1649
	source->info.enum_sources(source->context.data, enum_callback, param);
J
jp9000 已提交
1650
	os_atomic_dec_long(&source->enum_refs);
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665

	obs_source_release(source);
}

void obs_source_enum_tree(obs_source_t source,
		obs_source_enum_proc_t enum_callback,
		void *param)
{
	struct source_enum_data data = {enum_callback, param};

	if (!source || !source->info.enum_sources || source->enum_refs)
		return;

	obs_source_addref(source);

J
jp9000 已提交
1666
	os_atomic_inc_long(&source->enum_refs);
1667 1668
	source->info.enum_sources(source->context.data,
			enum_source_tree_callback,
1669
			&data);
J
jp9000 已提交
1670
	os_atomic_dec_long(&source->enum_refs);
1671 1672 1673

	obs_source_release(source);
}
1674 1675 1676 1677 1678

void obs_source_add_child(obs_source_t parent, obs_source_t child)
{
	if (!parent || !child) return;

1679 1680 1681 1682 1683
	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);
	}
1684 1685 1686 1687 1688 1689
}

void obs_source_remove_child(obs_source_t parent, obs_source_t child)
{
	if (!parent || !child) return;

1690 1691 1692 1693 1694
	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);
	}
1695
}
J
jp9000 已提交
1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742

static void reset_transition_vol(obs_source_t parent, obs_source_t child,
		void *param)
{
	child->transition_volume = 0.0f;

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

static void add_transition_vol(obs_source_t parent, obs_source_t child,
		void *param)
{
	float *vol = param;
	child->transition_volume += *vol;

	UNUSED_PARAMETER(parent);
}

static void apply_transition_vol(obs_source_t parent, obs_source_t child,
		void *param)
{
	child->present_volume = child->transition_volume;

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

void obs_transition_begin_frame(obs_source_t transition)
{
	if (!transition) return;
	obs_source_enum_tree(transition, reset_transition_vol, NULL);
}

void obs_source_set_transition_vol(obs_source_t source, float vol)
{
	if (!source) return;

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

void obs_transition_end_frame(obs_source_t transition)
{
	if (!transition) return;
	obs_source_enum_tree(transition, apply_transition_vol, NULL);
}