obs-source.c 49.2 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 33 34 35 36
static inline bool source_valid(struct obs_source *source)
{
	return source && source->context.data;
}

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

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

	return NULL;
}

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

	switch (type) {
J
jp9000 已提交
58 59 60 61 62 63 64 65 66 67 68
	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;
69 70 71 72 73
	}

	return find_source(list, id);
}

74 75 76 77 78 79 80 81 82
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)",
83 84
	"void volume_level(ptr source, float level, float magnitude, "
		"float peak)",
85 86 87
	NULL
};

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

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

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

104
/* internal initialization */
J
jp9000 已提交
105 106
bool obs_source_init(struct obs_source *source,
		const struct obs_source_info *info)
J
jp9000 已提交
107
{
108
	source->refs = 1;
J
jp9000 已提交
109
	source->user_volume = 1.0f;
110
	source->present_volume = 0.0f;
J
jp9000 已提交
111
	source->sync_offset = 0;
112 113 114
	pthread_mutex_init_value(&source->filter_mutex);
	pthread_mutex_init_value(&source->video_mutex);
	pthread_mutex_init_value(&source->audio_mutex);
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 166
	source       = bzalloc(sizeof(struct obs_source));
	source->info = *info;
167

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

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

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

J
jp9000 已提交
180
	if (!obs_source_init(source, info))
181
		goto fail;
J
jp9000 已提交
182

183
	obs_source_dosignal(source, "source_create", NULL);
J
jp9000 已提交
184
	return source;
185 186 187 188 189

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

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

	if (!frame)
		return;

200
	video_frame_init(&vid_frame, format, width, height);
201 202 203
	frame->format = format;
	frame->width  = width;
	frame->height = height;
204

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

211
void obs_source_destroy(struct obs_source *source)
J
jp9000 已提交
212
{
213
	size_t i;
214

J
jp9000 已提交
215 216 217
	if (!source)
		return;

218 219
	obs_context_data_remove(&source->context);

220
	obs_source_dosignal(source, "source_destroy", "destroy");
221

222
	if (source->context.data) {
223
		source->info.destroy(source->context.data);
224 225
		source->context.data = NULL;
	}
226

227 228
	if (source->filter_parent)
		obs_source_filter_remove(source->filter_parent, source);
229

230 231
	for (i = 0; i < source->filters.num; i++)
		obs_source_release(source->filters.array[i]);
232

233 234
	for (i = 0; i < source->video_frames.num; i++)
		source_frame_destroy(source->video_frames.array[i]);
235

236
	gs_entercontext(obs->video.graphics);
P
Palana 已提交
237
	texrender_destroy(source->async_convert_texrender);
238
	texture_destroy(source->async_texture);
239
	gs_leavecontext();
J
jp9000 已提交
240

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

244 245 246
	audio_line_destroy(source->audio_line);
	audio_resampler_destroy(source->resampler);

J
jp9000 已提交
247
	texrender_destroy(source->filter_texrender);
248 249 250 251 252
	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);
253
	obs_context_data_free(&source->context);
254 255 256
	bfree(source);
}

P
Palana 已提交
257
void obs_source_addref(obs_source_t source)
258
{
P
Palana 已提交
259
	if (source)
J
jp9000 已提交
260
		os_atomic_inc_long(&source->refs);
261 262
}

P
Palana 已提交
263
void obs_source_release(obs_source_t source)
264
{
P
Palana 已提交
265 266
	if (!source)
		return;
267

J
jp9000 已提交
268
	if (os_atomic_dec_long(&source->refs) == 0)
P
Palana 已提交
269
		obs_source_destroy(source);
270 271 272 273
}

void obs_source_remove(obs_source_t source)
{
274
	struct obs_core_data *data = &obs->data;
275
	size_t id;
276
	bool   exists;
277 278 279

	pthread_mutex_lock(&data->sources_mutex);

J
jp9000 已提交
280 281
	if (!source || source->removed) {
		pthread_mutex_unlock(&data->sources_mutex);
J
jp9000 已提交
282
		return;
J
jp9000 已提交
283
	}
J
jp9000 已提交
284

J
jp9000 已提交
285
	source->removed = true;
J
jp9000 已提交
286

J
jp9000 已提交
287 288
	obs_source_addref(source);

289 290 291 292
	id = da_find(data->user_sources, &source, 0);
	exists = (id != DARRAY_INVALID);
	if (exists) {
		da_erase(data->user_sources, id);
J
jp9000 已提交
293
		obs_source_release(source);
294 295 296
	}

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

298 299 300
	if (exists)
		obs_source_dosignal(source, "source_remove", "remove");

J
jp9000 已提交
301
	obs_source_release(source);
302 303 304 305
}

bool obs_source_removed(obs_source_t source)
{
J
jp9000 已提交
306
	return source ? source->removed : true;
J
jp9000 已提交
307 308
}

J
jp9000 已提交
309 310 311 312 313 314 315 316
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 已提交
317 318 319
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 已提交
320
	return (info) ? get_defaults(info) : NULL;
J
jp9000 已提交
321 322
}

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

331
		properties = info->properties();
J
jp9000 已提交
332 333 334 335
		obs_properties_apply_settings(properties, defaults);
		obs_data_release(defaults);
		return properties;
	}
J
jp9000 已提交
336 337 338
	return NULL;
}

339
obs_properties_t obs_source_properties(obs_source_t source)
340
{
341
	if (source_valid(source) && source->info.properties) {
J
jp9000 已提交
342
		obs_properties_t props;
343
		props = source->info.properties();
344
		obs_properties_apply_settings(props, source->context.settings);
J
jp9000 已提交
345 346 347
		return props;
	}

348 349 350
	return NULL;
}

351
uint32_t obs_source_get_output_flags(obs_source_t source)
J
jp9000 已提交
352
{
J
jp9000 已提交
353
	return source ? source->info.output_flags : 0;
J
jp9000 已提交
354 355
}

356 357
static void obs_source_deferred_update(obs_source_t source)
{
358 359 360 361
	if (source->context.data && source->info.update)
		source->info.update(source->context.data,
				source->context.settings);

362 363 364
	source->defer_update = false;
}

365
void obs_source_update(obs_source_t source, obs_data_t settings)
J
jp9000 已提交
366
{
J
jp9000 已提交
367 368
	if (!source) return;

369 370 371 372 373 374 375 376
	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);
377
	}
J
jp9000 已提交
378 379
}

380
static void activate_source(obs_source_t source)
J
jp9000 已提交
381
{
382
	if (source->context.data && source->info.activate)
383
		source->info.activate(source->context.data);
384
	obs_source_dosignal(source, "source_activate", "activate");
J
jp9000 已提交
385 386
}

387
static void deactivate_source(obs_source_t source)
J
jp9000 已提交
388
{
389
	if (source->context.data && source->info.deactivate)
390
		source->info.deactivate(source->context.data);
391
	obs_source_dosignal(source, "source_deactivate", "deactivate");
392
}
393

394 395
static void show_source(obs_source_t source)
{
396
	if (source->context.data && source->info.show)
397
		source->info.show(source->context.data);
398
	obs_source_dosignal(source, "source_show", "show");
399 400 401 402
}

static void hide_source(obs_source_t source)
{
403
	if (source->context.data && source->info.hide)
404
		source->info.hide(source->context.data);
405
	obs_source_dosignal(source, "source_hide", "hide");
406 407 408 409
}

static void activate_tree(obs_source_t parent, obs_source_t child, void *param)
{
J
jp9000 已提交
410
	if (os_atomic_inc_long(&child->activate_refs) == 1)
411
		activate_source(child);
J
jp9000 已提交
412 413 414

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
415 416 417 418 419
}

static void deactivate_tree(obs_source_t parent, obs_source_t child,
		void *param)
{
J
jp9000 已提交
420
	if (os_atomic_dec_long(&child->activate_refs) == 0)
421
		deactivate_source(child);
J
jp9000 已提交
422 423 424

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
425 426
}

427 428
static void show_tree(obs_source_t parent, obs_source_t child, void *param)
{
J
jp9000 已提交
429
	if (os_atomic_inc_long(&child->show_refs) == 1)
430 431 432 433 434 435 436 437
		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 已提交
438
	if (os_atomic_dec_long(&child->show_refs) == 0)
439 440 441 442 443 444 445
		hide_source(child);

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

void obs_source_activate(obs_source_t source, enum view_type type)
446 447 448
{
	if (!source) return;

J
jp9000 已提交
449
	if (os_atomic_inc_long(&source->show_refs) == 1) {
450 451 452 453 454
		show_source(source);
		obs_source_enum_tree(source, show_tree, NULL);
	}

	if (type == MAIN_VIEW) {
J
jp9000 已提交
455
		if (os_atomic_inc_long(&source->activate_refs) == 1) {
456 457 458 459
			activate_source(source);
			obs_source_enum_tree(source, activate_tree, NULL);
			obs_source_set_present_volume(source, 1.0f);
		}
460 461 462
	}
}

463
void obs_source_deactivate(obs_source_t source, enum view_type type)
464 465 466
{
	if (!source) return;

J
jp9000 已提交
467
	if (os_atomic_dec_long(&source->show_refs) == 0) {
468 469 470 471 472
		hide_source(source);
		obs_source_enum_tree(source, hide_tree, NULL);
	}

	if (type == MAIN_VIEW) {
J
jp9000 已提交
473
		if (os_atomic_dec_long(&source->activate_refs) == 0) {
474 475 476 477
			deactivate_source(source);
			obs_source_enum_tree(source, deactivate_tree, NULL);
			obs_source_set_present_volume(source, 0.0f);
		}
478
	}
J
jp9000 已提交
479 480
}

481
void obs_source_video_tick(obs_source_t source, float seconds)
J
jp9000 已提交
482
{
J
jp9000 已提交
483 484
	if (!source) return;

485 486 487
	if (source->defer_update)
		obs_source_deferred_update(source);

J
jp9000 已提交
488 489 490 491
	/* reset the filter render texture information once every frame */
	if (source->filter_texrender)
		texrender_reset(source->filter_texrender);

492
	if (source->context.data && source->info.video_tick)
493
		source->info.video_tick(source->context.data, seconds);
J
jp9000 已提交
494 495
}

496
/* unless the value is 3+ hours worth of frames, this won't overflow */
J
jp9000 已提交
497
static inline uint64_t conv_frames_to_time(size_t frames)
498
{
J
jp9000 已提交
499 500
	const struct audio_output_info *info;
	info = audio_output_getinfo(obs->audio.audio);
501 502 503

	return (uint64_t)frames * 1000000000ULL /
		(uint64_t)info->samples_per_sec;
504 505
}

506
/* maximum "direct" timestamp variance in nanoseconds */
507
#define MAX_TS_VAR          5000000000ULL
508
/* maximum time that timestamp can jump in nanoseconds */
509 510 511 512
#define MAX_TIMESTAMP_JUMP  2000000000ULL
/* time threshold in nanoseconds to ensure audio timing is as seamless as
 * possible */
#define TS_SMOOTHING_THRESHOLD 70000000ULL
513 514 515 516 517 518

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

520 521
static inline void handle_ts_jump(obs_source_t source, uint64_t expected,
		uint64_t ts, uint64_t diff)
522
{
J
jp9000 已提交
523
	blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%"PRIu64"', "
524
	                "expected value %"PRIu64", input value %"PRIu64,
525
	                source->context.name, diff, expected, ts);
526 527

	/* if has video, ignore audio data until reset */
528
	if (source->info.output_flags & OBS_SOURCE_ASYNC)
529
		os_atomic_dec_long(&source->av_sync_ref);
530
	else
531 532 533
		reset_audio_timing(source, ts);
}

534 535 536 537 538 539 540 541 542 543
#define VOL_MIN -96.0f
#define VOL_MAX  0.0f

static inline float to_db(float val)
{
	float db = 20.0f * log10f(val);
	return isfinite(db) ? db : VOL_MIN;
}

static void calc_volume_levels(struct obs_source *source, float *array,
544
		size_t frames, float volume)
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
{
	float sum_val = 0.0f;
	float max_val = 0.0f;
	float rms_val = 0.0f;

	const uint32_t sample_rate    = audio_output_samplerate(obs_audio());
	const size_t   channels       = audio_output_channels(obs_audio());
	const size_t   count          = frames * channels;
	const size_t   vol_peak_delay = sample_rate * 3;
	const float    alpha          = 0.15f;

	for (size_t i = 0; i < count; i++) {
		float val      = array[i];
		float val_pow2 = val * val;

		sum_val += val_pow2;
		max_val  = fmaxf(max_val, val_pow2);
	}

564 565
	rms_val = to_db(sqrtf(sum_val / (float)count * volume));
	max_val = to_db(sqrtf(max_val * volume));
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590

	if (max_val > source->vol_max)
		source->vol_max = max_val;
	else
		source->vol_max = alpha * source->vol_max +
			(1.0f - alpha) * max_val;

	if (source->vol_max > source->vol_peak ||
	    source->vol_update_count > vol_peak_delay) {
		source->vol_peak         = source->vol_max;
		source->vol_update_count = 0;
	} else {
		source->vol_update_count += count;
	}

	source->vol_mag = alpha * rms_val + source->vol_mag * (1.0f - alpha);
}

/* TODO update peak/etc later */
static void obs_source_update_volume_level(obs_source_t source,
		struct audio_data *in)
{
	if (source && in) {
		struct calldata data = {0};

591 592
		calc_volume_levels(source, (float*)in->data[0], in->frames,
				in->volume);
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607

		calldata_setptr  (&data, "source",    source);
		calldata_setfloat(&data, "level",     source->vol_max);
		calldata_setfloat(&data, "magnitude", source->vol_mag);
		calldata_setfloat(&data, "peak",      source->vol_peak);

		signal_handler_signal(source->context.signals, "volume_level",
				&data);
		signal_handler_signal(obs->signals, "source_volume_level",
				&data);

		calldata_free(&data);
	}
}

608 609 610 611
static void source_output_audio_line(obs_source_t source,
		const struct audio_data *data)
{
	struct audio_data in = *data;
612
	uint64_t diff;
613 614

	if (!source->timing_set) {
615
		reset_audio_timing(source, in.timestamp);
616 617

		/* detects 'directly' set timestamps as long as they're within
618
		 * a certain threshold */
619
		if ((source->timing_adjust + MAX_TS_VAR) < MAX_TS_VAR * 2)
620
			source->timing_adjust = 0;
621

622
	} else {
623
		bool ts_under = (in.timestamp < source->next_audio_ts_min);
624

625 626 627 628 629
		diff = ts_under ?
			(source->next_audio_ts_min - in.timestamp) :
			(in.timestamp - source->next_audio_ts_min);

		/* smooth audio if lower or within threshold */
630
		if (diff > MAX_TIMESTAMP_JUMP)
631 632 633 634
			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;
635 636
	}

637
	source->next_audio_ts_min = in.timestamp +
J
jp9000 已提交
638
		conv_frames_to_time(in.frames);
639

640
	if (source->av_sync_ref != 0)
641 642
		return;

J
jp9000 已提交
643
	in.timestamp += source->timing_adjust + source->sync_offset;
J
jp9000 已提交
644 645
	in.volume = source->user_volume * source->present_volume *
		obs->audio.user_volume * obs->audio.present_volume;
646

647
	audio_line_output(source->audio_line, &in);
648
	obs_source_update_volume_level(source, &in);
649 650
}

651 652 653 654 655 656 657 658
enum convert_type {
	CONVERT_NONE,
	CONVERT_NV12,
	CONVERT_420,
	CONVERT_422_U,
	CONVERT_422_Y,
};

659
static inline enum convert_type get_convert_type(enum video_format format)
660
{
661
	switch (format) {
662 663 664 665 666 667 668 669 670 671 672
	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;

673
	case VIDEO_FORMAT_NONE:
674 675 676 677 678 679 680 681 682
	case VIDEO_FORMAT_RGBA:
	case VIDEO_FORMAT_BGRA:
	case VIDEO_FORMAT_BGRX:
		return CONVERT_NONE;
	}

	return CONVERT_NONE;
}

683 684 685 686
static inline bool set_packed422_sizes(struct obs_source *source,
		struct source_frame *frame)
{
	source->async_convert_height = frame->height;
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
	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,
		struct 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;
	source->async_plane_offset[1] = source->async_plane_offset[0] +
		frame->width * frame->height / 4;
704 705 706 707 708 709 710 711 712 713 714 715
	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_420:
716 717 718 719
			return set_planar420_sizes(source, frame);

		case CONVERT_NV12:
			assert(false && "NV12 not yet implemented");
720 721 722 723 724 725 726 727 728 729 730
			/* TODO: implement conversion */
			break;

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

	}
	return false;
}

731 732 733 734 735 736 737 738 739 740 741
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;
}

742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
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 =
763
			texrender_create(GS_BGRX, GS_ZS_NONE);
764 765 766 767

		source->async_texture = gs_create_texture(
				source->async_convert_width,
				source->async_convert_height,
768 769
				source->async_texture_format,
				1, NULL, GS_DYNAMIC);
770 771

	} else {
772 773
		enum gs_color_format format = convert_video_format(
				frame->format);
774 775 776 777
		source->async_gpu_conversion = false;

		source->async_texture = gs_create_texture(
				frame->width, frame->height,
778
				format, 1, NULL, GS_DYNAMIC);
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
	}

	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_420:
799 800 801 802 803
			texture_setimage(tex, frame->data[0],
					frame->width, false);
			break;

		case CONVERT_NV12:
804 805 806 807 808 809 810 811 812 813 814 815 816
			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:
817
			return "UYVY_Reverse";
818 819 820 821 822 823 824 825

		case VIDEO_FORMAT_YUY2:
			return "YUY2_Reverse";

		case VIDEO_FORMAT_YVYU:
			return "YVYU_Reverse";

		case VIDEO_FORMAT_I420:
826 827 828
			return "I420_Reverse";

		case VIDEO_FORMAT_NV12:
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
			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);
845
	effect_setfloat(param, val);
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
}

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;

861 862 863
	float convert_width  = (float)source->async_convert_width;
	float convert_height = (float)source->async_convert_height;

864 865 866 867 868 869 870 871 872 873
	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);

874
	effect_settexture(effect_getparambyname(conv, "image"), tex);
875 876 877 878
	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);
879
	set_eparam(conv, "width_d2",  cx * 0.5f);
880
	set_eparam(conv, "height_d2", cy * 0.5f);
881
	set_eparam(conv, "width_d2_i",  1.0f / (cx * 0.5f));
882
	set_eparam(conv, "height_d2_i", 1.0f / (cy * 0.5f));
883 884 885 886 887 888 889 890 891 892
	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]);
893 894 895 896 897 898 899 900 901 902 903 904 905

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

906 907
static bool update_async_texture(struct obs_source *source,
		const struct source_frame *frame)
908
{
909 910 911
	texture_t         tex       = source->async_texture;
	texrender_t       texrender = source->async_convert_texrender;
	enum convert_type type      = get_convert_type(frame->format);
912 913 914
	void              *ptr;
	uint32_t          linesize;

915 916 917
	source->async_format     = frame->format;
	source->async_flip       = frame->flip;
	source->async_full_range = frame->full_range;
918 919
	memcpy(source->async_color_matrix, frame->color_matrix,
			sizeof(frame->color_matrix));
920 921 922 923
	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);
924

925 926 927
	if (source->async_gpu_conversion && texrender)
		return update_async_texrender(source, frame);

928
	if (type == CONVERT_NONE) {
929
		texture_setimage(tex, frame->data[0], frame->linesize[0],
930
				false);
931 932 933
		return true;
	}

934
	if (!texture_map(tex, &ptr, &linesize))
935 936 937
		return false;

	if (type == CONVERT_420)
J
jp9000 已提交
938 939 940
		decompress_420((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
941 942

	else if (type == CONVERT_NV12)
J
jp9000 已提交
943 944 945
		decompress_nv12((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
946 947

	else if (type == CONVERT_422_Y)
948
		decompress_422(frame->data[0], frame->linesize[0],
J
jp9000 已提交
949
				0, frame->height, ptr, linesize, true);
950 951

	else if (type == CONVERT_422_U)
952
		decompress_422(frame->data[0], frame->linesize[0],
J
jp9000 已提交
953
				0, frame->height, ptr, linesize, false);
954 955 956 957 958

	texture_unmap(tex);
	return true;
}

959
static inline void obs_source_draw_texture(struct obs_source *source,
960 961
		effect_t effect, float *color_matrix,
		float const *color_range_min, float const *color_range_max)
962
{
963 964
	texture_t tex = source->async_texture;
	eparam_t  param;
965

966 967 968
	if (source->async_convert_texrender)
		tex = texrender_gettexture(source->async_convert_texrender);

P
Palana 已提交
969
	if (color_range_min) {
970 971
		size_t const size = sizeof(float) * 3;
		param = effect_getparambyname(effect, "color_range_min");
972
		effect_setval(param, color_range_min, size);
P
Palana 已提交
973
	}
974

P
Palana 已提交
975 976
	if (color_range_max) {
		size_t const size = sizeof(float) * 3;
977
		param = effect_getparambyname(effect, "color_range_max");
978
		effect_setval(param, color_range_max, size);
P
Palana 已提交
979
	}
980

P
Palana 已提交
981
	if (color_matrix) {
982
		param = effect_getparambyname(effect, "color_matrix");
983
		effect_setval(param, color_matrix, sizeof(float) * 16);
984 985
	}

J
jp9000 已提交
986
	param = effect_getparambyname(effect, "image");
987
	effect_settexture(param, tex);
988

989 990
	gs_draw_sprite(tex, source->async_flip ? GS_FLIP_V : 0, 0, 0);
}
991

992 993
static void obs_source_draw_async_texture(struct obs_source *source)
{
994 995 996 997 998
	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);
999 1000 1001 1002 1003 1004 1005 1006 1007 1008
	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,
1009 1010 1011
			yuv ? source->async_color_matrix : NULL,
			limited_range ? source->async_color_range_min : NULL,
			limited_range ? source->async_color_range_max : NULL);
1012 1013 1014 1015 1016

	if (def_draw) {
		technique_endpass(tech);
		technique_end(tech);
	}
1017 1018
}

1019 1020 1021
static void obs_source_render_async_video(obs_source_t source)
{
	struct source_frame *frame = obs_source_getframe(source);
1022 1023 1024 1025 1026 1027
	if (frame) {
		if (!set_async_texture_size(source, frame))
			return;
		if (!update_async_texture(source, frame))
			return;
	}
1028

1029 1030
	if (source->async_texture)
		obs_source_draw_async_texture(source);
1031 1032 1033 1034

	obs_source_releaseframe(source, frame);
}

1035 1036 1037 1038 1039 1040 1041
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 已提交
1042 1043
static inline void obs_source_default_render(obs_source_t source,
		bool color_matrix)
1044 1045
{
	effect_t    effect     = obs->video.default_effect;
J
jp9000 已提交
1046
	const char  *tech_name = color_matrix ? "DrawMatrix" : "Draw";
1047 1048 1049 1050 1051 1052
	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);
1053 1054
		if (source->context.data)
			source->info.video_render(source->context.data, effect);
1055 1056 1057 1058 1059 1060 1061
		technique_endpass(tech);
	}
	technique_end(tech);
}

static inline void obs_source_main_render(obs_source_t source)
{
1062 1063 1064
	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;
1065 1066
	bool default_effect = !source->filter_parent &&
	                      source->filters.num == 0 &&
1067
	                      !custom_draw;
1068 1069

	if (default_effect)
J
jp9000 已提交
1070
		obs_source_default_render(source, color_matrix);
1071
	else if (source->context.data)
1072
		source->info.video_render(source->context.data,
1073
				custom_draw ? NULL : gs_geteffect());
1074 1075
}

1076
void obs_source_video_render(obs_source_t source)
J
jp9000 已提交
1077
{
1078
	if (!source_valid(source)) return;
J
jp9000 已提交
1079

1080 1081
	if (source->filters.num && !source->rendering_filter)
		obs_source_render_filters(source);
1082

1083 1084 1085 1086
	else if (source->info.video_render)
		obs_source_main_render(source);

	else if (source->filter_target)
1087 1088
		obs_source_video_render(source->filter_target);

1089
	else
1090
		obs_source_render_async_video(source);
J
jp9000 已提交
1091 1092
}

J
jp9000 已提交
1093
uint32_t obs_source_getwidth(obs_source_t source)
J
jp9000 已提交
1094
{
1095
	if (!source_valid(source)) return 0;
1096 1097

	if (source->info.getwidth)
1098
		return source->info.getwidth(source->context.data);
1099
	return source->async_width;
J
jp9000 已提交
1100 1101
}

J
jp9000 已提交
1102
uint32_t obs_source_getheight(obs_source_t source)
J
jp9000 已提交
1103
{
1104
	if (!source_valid(source)) return 0;
1105 1106

	if (source->info.getheight)
1107
		return source->info.getheight(source->context.data);
1108
	return source->async_height;
J
jp9000 已提交
1109 1110
}

1111 1112
obs_source_t obs_filter_getparent(obs_source_t filter)
{
J
jp9000 已提交
1113
	return filter ? filter->filter_parent : NULL;
1114 1115
}

1116
obs_source_t obs_filter_gettarget(obs_source_t filter)
J
jp9000 已提交
1117
{
J
jp9000 已提交
1118
	return filter ? filter->filter_target : NULL;
J
jp9000 已提交
1119 1120
}

1121
void obs_source_filter_add(obs_source_t source, obs_source_t filter)
J
jp9000 已提交
1122
{
J
jp9000 已提交
1123 1124 1125
	if (!source || !filter)
		return;

1126 1127
	pthread_mutex_lock(&source->filter_mutex);

J
jp9000 已提交
1128
	if (da_find(source->filters, &filter, 0) != DARRAY_INVALID) {
J
jp9000 已提交
1129 1130 1131 1132 1133 1134
		blog(LOG_WARNING, "Tried to add a filter that was already "
		                  "present on the source");
		return;
	}

	if (source->filters.num) {
1135
		obs_source_t *back = da_end(source->filters);
J
jp9000 已提交
1136 1137 1138 1139
		(*back)->filter_target = filter;
	}

	da_push_back(source->filters, &filter);
1140 1141 1142 1143

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = source;
J
jp9000 已提交
1144 1145 1146
	filter->filter_target = source;
}

1147
void obs_source_filter_remove(obs_source_t source, obs_source_t filter)
J
jp9000 已提交
1148
{
1149 1150
	size_t idx;

J
jp9000 已提交
1151 1152 1153
	if (!source || !filter)
		return;

1154 1155 1156
	pthread_mutex_lock(&source->filter_mutex);

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
1157
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
1158 1159 1160
		return;

	if (idx > 0) {
1161
		obs_source_t prev = source->filters.array[idx-1];
J
jp9000 已提交
1162 1163 1164 1165
		prev->filter_target = filter->filter_target;
	}

	da_erase(source->filters, idx);
1166 1167 1168 1169

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = NULL;
J
jp9000 已提交
1170 1171 1172
	filter->filter_target = NULL;
}

1173
void obs_source_filter_setorder(obs_source_t source, obs_source_t filter,
J
jp9000 已提交
1174 1175
		enum order_movement movement)
{
J
jp9000 已提交
1176 1177 1178 1179 1180 1181
	size_t idx, i;

	if (!source || !filter)
		return;

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
1182
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
		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);
	}

1206
	/* reorder filter targets, not the nicest way of dealing with things */
J
jp9000 已提交
1207
	for (i = 0; i < source->filters.num; i++) {
1208
		obs_source_t next_filter = (i == source->filters.num-1) ?
J
jp9000 已提交
1209 1210 1211 1212 1213
			source : source->filters.array[idx+1];
		source->filters.array[i]->filter_target = next_filter;
	}
}

1214
obs_data_t obs_source_getsettings(obs_source_t source)
J
jp9000 已提交
1215
{
J
jp9000 已提交
1216 1217
	if (!source) return NULL;

1218 1219
	obs_data_addref(source->context.settings);
	return source->context.settings;
J
jp9000 已提交
1220 1221
}

1222 1223
static inline struct source_frame *filter_async_video(obs_source_t source,
		struct source_frame *in)
1224 1225 1226 1227
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
1228 1229

		if (filter->context.data && filter->info.filter_video) {
1230 1231
			in = filter->info.filter_video(filter->context.data,
					in);
1232 1233 1234 1235 1236 1237 1238 1239
			if (!in)
				return NULL;
		}
	}

	return in;
}

1240 1241 1242
static inline void copy_frame_data_line(struct source_frame *dst,
		const struct source_frame *src, uint32_t plane, uint32_t y)
{
1243 1244 1245 1246
	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];
1247 1248 1249 1250 1251 1252 1253

	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)
{
1254
	if (dst->linesize[plane] != src->linesize[plane])
1255 1256 1257 1258
		for (uint32_t y = 0; y < lines; y++)
			copy_frame_data_line(dst, src, plane, y);
	else
		memcpy(dst->data[plane], src->data[plane],
1259
				dst->linesize[plane] * lines);
1260 1261 1262 1263 1264 1265
}

static void copy_frame_data(struct source_frame *dst,
		const struct source_frame *src)
{
	dst->flip         = src->flip;
1266
	dst->full_range   = src->full_range;
1267 1268
	dst->timestamp    = src->timestamp;
	memcpy(dst->color_matrix, src->color_matrix, sizeof(float) * 16);
1269 1270 1271 1272 1273
	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);
	}
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297

	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 已提交
1298
static inline struct source_frame *cache_video(const struct source_frame *frame)
1299
{
1300
	/* TODO: use an actual cache */
1301
	struct source_frame *new_frame = source_frame_create(frame->format,
1302
			frame->width, frame->height);
1303

1304
	copy_frame_data(new_frame, frame);
1305
	return new_frame;
1306 1307
}

1308
static bool ready_async_frame(obs_source_t source, uint64_t sys_time);
1309 1310 1311 1312

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

1316
void obs_source_output_video(obs_source_t source,
1317
		const struct source_frame *frame)
1318
{
J
jp9000 已提交
1319 1320 1321
	if (!source || !frame)
		return;

J
jp9000 已提交
1322
	struct source_frame *output = cache_video(frame);
1323 1324 1325 1326 1327

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

1328 1329
	if (output) {
		pthread_mutex_lock(&source->video_mutex);
1330
		cycle_frames(source);
1331 1332 1333
		da_push_back(source->video_frames, &output);
		pthread_mutex_unlock(&source->video_mutex);
	}
1334 1335
}

1336 1337
static inline struct filtered_audio *filter_async_audio(obs_source_t source,
		struct filtered_audio *in)
1338 1339 1340 1341
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
1342 1343

		if (filter->context.data && filter->info.filter_audio) {
1344 1345
			in = filter->info.filter_audio(filter->context.data,
					in);
1346 1347 1348 1349 1350 1351 1352 1353
			if (!in)
				return NULL;
		}
	}

	return in;
}

1354
static inline void reset_resampler(obs_source_t source,
1355 1356
		const struct source_audio *audio)
{
J
jp9000 已提交
1357
	const struct audio_output_info *obs_info;
1358 1359
	struct resample_info output_info;

1360 1361
	obs_info = audio_output_getinfo(obs->audio.audio);

1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386
	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 已提交
1387
		const uint8_t *const data[], uint32_t frames, uint64_t ts)
1388
{
1389
	size_t planes    = audio_output_planes(obs->audio.audio);
1390
	size_t blocksize = audio_output_blocksize(obs->audio.audio);
1391 1392
	size_t size      = (size_t)frames * blocksize;
	bool   resize    = source->audio_storage_size < size;
1393

J
jp9000 已提交
1394 1395
	source->audio_data.frames    = frames;
	source->audio_data.timestamp = ts;
1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408

	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;
1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422
}

/* 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 已提交
1423
		uint8_t  *output[MAX_AV_PLANES];
1424 1425 1426
		uint32_t frames;
		uint64_t offset;

1427 1428 1429 1430 1431
		memset(output, 0, sizeof(output));

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

J
jp9000 已提交
1433
		copy_audio_data(source, (const uint8_t *const *)output, frames,
1434 1435 1436 1437 1438
				audio->timestamp - offset);
	} else {
		copy_audio_data(source, audio->data, audio->frames,
				audio->timestamp);
	}
1439 1440 1441 1442 1443
}

void obs_source_output_audio(obs_source_t source,
		const struct source_audio *audio)
{
J
jp9000 已提交
1444
	uint32_t flags;
1445
	struct filtered_audio *output;
1446

J
jp9000 已提交
1447 1448 1449 1450
	if (!source || !audio)
		return;

	flags = source->info.output_flags;
1451
	process_audio(source, audio);
1452 1453

	pthread_mutex_lock(&source->filter_mutex);
1454
	output = filter_async_audio(source, &source->audio_data);
1455 1456

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

1459 1460
		pthread_mutex_lock(&source->audio_mutex);

1461 1462
		/* wait for video to start before outputting any audio so we
		 * have a base for sync */
1463
		if (source->timing_set || !async) {
1464
			struct audio_data data;
1465

J
jp9000 已提交
1466
			for (int i = 0; i < MAX_AV_PLANES; i++)
1467 1468
				data.data[i] = output->data[i];

1469 1470 1471
			data.frames    = output->frames;
			data.timestamp = output->timestamp;
			source_output_audio_line(source, &data);
1472 1473 1474 1475 1476 1477 1478 1479
		}

		pthread_mutex_unlock(&source->audio_mutex);
	}

	pthread_mutex_unlock(&source->filter_mutex);
}

1480 1481 1482 1483 1484
static inline bool frame_out_of_bounds(obs_source_t source, uint64_t ts)
{
	return ((ts - source->last_frame_ts) > MAX_TIMESTAMP_JUMP);
}

1485
static bool ready_async_frame(obs_source_t source, uint64_t sys_time)
1486 1487 1488 1489 1490 1491 1492 1493 1494 1495
{
	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;
1496
		os_atomic_inc_long(&source->av_sync_ref);
1497 1498
	} else {
		frame_offset = frame_time - source->last_frame_ts;
1499
		source->last_frame_ts += frame_offset;
1500 1501 1502 1503 1504
	}

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

1505 1506 1507
		if (source->video_frames.num == 1)
			return true;

1508 1509 1510 1511 1512 1513 1514 1515
		frame = next_frame;
		da_erase(source->video_frames, 0);
		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;
1516
			os_atomic_inc_long(&source->av_sync_ref);
1517 1518 1519 1520 1521 1522
		}

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

1523 1524
	source_frame_destroy(frame);

1525 1526 1527 1528 1529 1530
	return frame != NULL;
}

static inline struct source_frame *get_closest_frame(obs_source_t source,
		uint64_t sys_time)
{
1531
	if (ready_async_frame(source, sys_time)) {
1532 1533 1534 1535 1536 1537
		struct source_frame *frame = source->video_frames.array[0];
		da_erase(source->video_frames, 0);
		return frame;
	}

	return NULL;
1538 1539
}

1540
/*
1541 1542
 * Ensures that cached frames are displayed on time.  If multiple frames
 * were cached between renders, then releases the unnecessary frames and uses
1543 1544
 * the frame with the closest timing to ensure sync.  Also ensures that timing
 * with audio is synchronized.
1545
 */
1546
struct source_frame *obs_source_getframe(obs_source_t source)
J
jp9000 已提交
1547
{
1548 1549
	struct source_frame *frame = NULL;
	uint64_t sys_time;
1550

J
jp9000 已提交
1551 1552 1553
	if (!source)
		return NULL;

1554 1555 1556 1557 1558
	pthread_mutex_lock(&source->video_mutex);

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

1559
	sys_time = os_gettime_ns();
1560

1561 1562
	if (!source->last_frame_ts) {
		frame = source->video_frames.array[0];
1563 1564
		da_erase(source->video_frames, 0);

1565
		source->last_frame_ts = frame->timestamp;
1566
	} else {
1567
		frame = get_closest_frame(source, sys_time);
J
jp9000 已提交
1568 1569 1570 1571 1572 1573
	}

	/* reset timing to current system time */
	if (frame) {
		source->timing_adjust = sys_time - frame->timestamp;
		source->timing_set = true;
1574 1575 1576 1577 1578 1579
	}

	source->last_sys_timestamp = sys_time;

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

1581
	if (frame)
1582 1583
		obs_source_addref(source);

1584
	return frame;
J
jp9000 已提交
1585 1586
}

1587
void obs_source_releaseframe(obs_source_t source, struct source_frame *frame)
J
jp9000 已提交
1588
{
J
jp9000 已提交
1589
	if (source && frame) {
1590 1591 1592
		source_frame_destroy(frame);
		obs_source_release(source);
	}
J
jp9000 已提交
1593
}
1594 1595 1596

const char *obs_source_getname(obs_source_t source)
{
1597
	return source ? source->context.name : NULL;
1598 1599 1600 1601
}

void obs_source_setname(obs_source_t source, const char *name)
{
J
jp9000 已提交
1602
	if (!source) return;
1603
	obs_context_data_setname(&source->context, name);
1604 1605
}

1606
void obs_source_gettype(obs_source_t source, enum obs_source_type *type,
1607 1608
		const char **id)
{
J
jp9000 已提交
1609 1610
	if (!source) return;

J
jp9000 已提交
1611
	if (type) *type = source->info.type;
J
jp9000 已提交
1612
	if (id)   *id   = source->info.id;
1613
}
1614 1615

static inline void render_filter_bypass(obs_source_t target, effect_t effect,
J
jp9000 已提交
1616
		bool use_matrix)
1617
{
J
jp9000 已提交
1618
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631
	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 已提交
1632
		uint32_t width, uint32_t height, bool use_matrix)
1633
{
J
jp9000 已提交
1634
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1635
	technique_t tech       = effect_gettechnique(effect, tech_name);
J
jp9000 已提交
1636
	eparam_t    image      = effect_getparambyname(effect, "image");
1637 1638
	size_t      passes, i;

1639
	effect_settexture(image, tex);
1640 1641 1642 1643 1644 1645 1646 1647 1648 1649

	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 已提交
1650 1651
void obs_source_process_filter(obs_source_t filter, effect_t effect,
		uint32_t width, uint32_t height, enum gs_color_format format,
1652
		enum allow_direct_render allow_direct)
1653
{
J
jp9000 已提交
1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669
	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;
1670 1671 1672 1673 1674

	/* 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 */
1675
	if (can_directly && expects_def && target == parent) {
J
jp9000 已提交
1676
		render_filter_bypass(target, effect, use_matrix);
1677 1678 1679
		return;
	}

J
jp9000 已提交
1680 1681 1682 1683 1684
	if (!filter->filter_texrender)
		filter->filter_texrender = texrender_create(format,
				GS_ZS_NONE);

	if (texrender_begin(filter->filter_texrender, cx, cy)) {
1685
		gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
1686
		if (expects_def && parent == target)
J
jp9000 已提交
1687
			obs_source_default_render(parent, use_matrix);
1688 1689
		else
			obs_source_video_render(target);
J
jp9000 已提交
1690
		texrender_end(filter->filter_texrender);
1691 1692 1693 1694
	}

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

J
jp9000 已提交
1695 1696
	render_filter_tex(texrender_gettexture(filter->filter_texrender),
			effect, width, height, use_matrix);
1697
}
1698 1699 1700

signal_handler_t obs_source_signalhandler(obs_source_t source)
{
1701
	return source ? source->context.signals : NULL;
1702 1703 1704 1705
}

proc_handler_t obs_source_prochandler(obs_source_t source)
{
1706
	return source ? source->context.procs : NULL;
1707
}
J
jp9000 已提交
1708 1709 1710

void obs_source_setvolume(obs_source_t source, float volume)
{
J
jp9000 已提交
1711 1712 1713 1714 1715
	if (source) {
		struct calldata data = {0};
		calldata_setptr(&data, "source", source);
		calldata_setfloat(&data, "volume", volume);

1716
		signal_handler_signal(source->context.signals, "volume", &data);
1717
		signal_handler_signal(obs->signals, "source_volume", &data);
J
jp9000 已提交
1718

1719
		volume = (float)calldata_float(&data, "volume");
J
jp9000 已提交
1720 1721
		calldata_free(&data);

J
jp9000 已提交
1722
		source->user_volume = volume;
J
jp9000 已提交
1723
	}
J
jp9000 已提交
1724 1725
}

J
jp9000 已提交
1726 1727 1728 1729 1730 1731 1732 1733 1734
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 已提交
1735 1736
void obs_source_set_present_volume(obs_source_t source, float volume)
{
J
jp9000 已提交
1737
	if (source) {
J
jp9000 已提交
1738
		source->present_volume = volume;
J
jp9000 已提交
1739 1740 1741 1742 1743 1744 1745 1746

		/* 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 已提交
1747 1748 1749 1750
}

float obs_source_getvolume(obs_source_t source)
{
J
jp9000 已提交
1751
	return source ? source->user_volume : 0.0f;
J
jp9000 已提交
1752 1753 1754 1755
}

float obs_source_get_present_volume(obs_source_t source)
{
J
jp9000 已提交
1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767
	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 已提交
1768
}
1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780

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 已提交
1781
		os_atomic_inc_long(&child->enum_refs);
1782

1783 1784 1785
		if (child->context.data)
			child->info.enum_sources(child->context.data,
					enum_source_tree_callback, data);
1786

J
jp9000 已提交
1787
		os_atomic_dec_long(&child->enum_refs);
1788 1789 1790 1791 1792 1793 1794 1795 1796
	}

	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)
{
1797 1798 1799
	if (!source_valid(source)      ||
	    !source->info.enum_sources ||
	    source->enum_refs)
1800 1801 1802 1803
		return;

	obs_source_addref(source);

J
jp9000 已提交
1804
	os_atomic_inc_long(&source->enum_refs);
1805
	source->info.enum_sources(source->context.data, enum_callback, param);
J
jp9000 已提交
1806
	os_atomic_dec_long(&source->enum_refs);
1807 1808 1809 1810 1811 1812 1813 1814 1815 1816

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

1817 1818 1819
	if (!source_valid(source)      ||
	    !source->info.enum_sources ||
	    source->enum_refs)
1820 1821 1822 1823
		return;

	obs_source_addref(source);

J
jp9000 已提交
1824
	os_atomic_inc_long(&source->enum_refs);
1825 1826
	source->info.enum_sources(source->context.data,
			enum_source_tree_callback,
1827
			&data);
J
jp9000 已提交
1828
	os_atomic_dec_long(&source->enum_refs);
1829 1830 1831

	obs_source_release(source);
}
1832 1833 1834 1835 1836

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

1837 1838 1839 1840 1841
	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);
	}
1842 1843 1844 1845 1846 1847
}

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

1848 1849 1850 1851 1852
	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);
	}
1853
}
J
jp9000 已提交
1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900

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);
}
1901 1902 1903

void obs_source_save(obs_source_t source)
{
1904
	if (!source_valid(source) || !source->info.save) return;
1905 1906 1907 1908 1909
	source->info.save(source->context.data, source->context.settings);
}

void obs_source_load(obs_source_t source)
{
1910
	if (!source_valid(source) || !source->info.load) return;
1911 1912
	source->info.load(source->context.data, source->context.settings);
}