obs-source.c 50.7 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;
}

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

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

	return NULL;
}

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

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

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

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

	return find_source(list, id);
}

73 74 75 76 77 78 79 80
static const char *source_signals[] = {
	"void destroy(ptr source)",
	"void add(ptr source)",
	"void remove(ptr source)",
	"void activate(ptr source)",
	"void deactivate(ptr source)",
	"void show(ptr source)",
	"void hide(ptr source)",
J
jp9000 已提交
81
	"void rename(ptr source, string new_name, string prev_name)",
82
	"void volume(ptr source, in out float volume)",
83 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 99
const char *obs_source_get_display_name(enum obs_source_type type,
		const char *id)
100
{
J
jp9000 已提交
101
	const struct obs_source_info *info = get_source_info(type, id);
102
	return (info != NULL) ? info->get_name() : NULL;
103 104
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	if (!frame)
		return;

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

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

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

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

224 225
	obs_context_data_remove(&source->context);

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

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

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

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

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

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

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

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

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

255
	gs_texrender_destroy(source->filter_texrender);
256 257 258 259 260
	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);
261
	obs_context_data_free(&source->context);
262 263 264 265
	
	if (source->owns_info_id)
		bfree((void*)source->info.id);

266 267 268
	bfree(source);
}

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

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

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

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

	pthread_mutex_lock(&data->sources_mutex);

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

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

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

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

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

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

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

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

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

J
jp9000 已提交
329 330 331
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 已提交
332
	return (info) ? get_defaults(info) : NULL;
J
jp9000 已提交
333 334
}

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

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

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

360 361 362
	return NULL;
}

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

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

374 375 376
	source->defer_update = false;
}

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

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

392
static void activate_source(obs_source_t source)
J
jp9000 已提交
393
{
394
	if (source->context.data && source->info.activate)
395
		source->info.activate(source->context.data);
396
	obs_source_dosignal(source, "source_activate", "activate");
J
jp9000 已提交
397 398
}

399
static void deactivate_source(obs_source_t source)
J
jp9000 已提交
400
{
401
	if (source->context.data && source->info.deactivate)
402
		source->info.deactivate(source->context.data);
403
	obs_source_dosignal(source, "source_deactivate", "deactivate");
404
}
405

406 407
static void show_source(obs_source_t source)
{
408
	if (source->context.data && source->info.show)
409
		source->info.show(source->context.data);
410
	obs_source_dosignal(source, "source_show", "show");
411 412 413 414
}

static void hide_source(obs_source_t source)
{
415
	if (source->context.data && source->info.hide)
416
		source->info.hide(source->context.data);
417
	obs_source_dosignal(source, "source_hide", "hide");
418 419 420 421
}

static void activate_tree(obs_source_t parent, obs_source_t child, void *param)
{
J
jp9000 已提交
422
	if (os_atomic_inc_long(&child->activate_refs) == 1)
423
		activate_source(child);
J
jp9000 已提交
424 425 426

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
427 428 429 430 431
}

static void deactivate_tree(obs_source_t parent, obs_source_t child,
		void *param)
{
J
jp9000 已提交
432
	if (os_atomic_dec_long(&child->activate_refs) == 0)
433
		deactivate_source(child);
J
jp9000 已提交
434 435 436

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
437 438
}

439 440
static void show_tree(obs_source_t parent, obs_source_t child, void *param)
{
J
jp9000 已提交
441
	if (os_atomic_inc_long(&child->show_refs) == 1)
442 443 444 445 446 447 448 449
		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 已提交
450
	if (os_atomic_dec_long(&child->show_refs) == 0)
451 452 453 454 455 456 457
		hide_source(child);

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

void obs_source_activate(obs_source_t source, enum view_type type)
458 459 460
{
	if (!source) return;

J
jp9000 已提交
461
	if (os_atomic_inc_long(&source->show_refs) == 1) {
462 463 464 465 466
		show_source(source);
		obs_source_enum_tree(source, show_tree, NULL);
	}

	if (type == MAIN_VIEW) {
J
jp9000 已提交
467
		if (os_atomic_inc_long(&source->activate_refs) == 1) {
468 469 470 471
			activate_source(source);
			obs_source_enum_tree(source, activate_tree, NULL);
			obs_source_set_present_volume(source, 1.0f);
		}
472 473 474
	}
}

475
void obs_source_deactivate(obs_source_t source, enum view_type type)
476 477 478
{
	if (!source) return;

J
jp9000 已提交
479
	if (os_atomic_dec_long(&source->show_refs) == 0) {
480 481 482 483 484
		hide_source(source);
		obs_source_enum_tree(source, hide_tree, NULL);
	}

	if (type == MAIN_VIEW) {
J
jp9000 已提交
485
		if (os_atomic_dec_long(&source->activate_refs) == 0) {
486 487 488 489
			deactivate_source(source);
			obs_source_enum_tree(source, deactivate_tree, NULL);
			obs_source_set_present_volume(source, 0.0f);
		}
490
	}
J
jp9000 已提交
491 492
}

493
void obs_source_video_tick(obs_source_t source, float seconds)
J
jp9000 已提交
494
{
J
jp9000 已提交
495 496
	if (!source) return;

497 498 499
	if (source->defer_update)
		obs_source_deferred_update(source);

J
jp9000 已提交
500 501
	/* reset the filter render texture information once every frame */
	if (source->filter_texrender)
502
		gs_texrender_reset(source->filter_texrender);
J
jp9000 已提交
503

504
	if (source->context.data && source->info.video_tick)
505
		source->info.video_tick(source->context.data, seconds);
J
jp9000 已提交
506 507
}

508
/* unless the value is 3+ hours worth of frames, this won't overflow */
J
jp9000 已提交
509
static inline uint64_t conv_frames_to_time(size_t frames)
510
{
J
jp9000 已提交
511
	const struct audio_output_info *info;
512
	info = audio_output_get_info(obs->audio.audio);
513 514 515

	return (uint64_t)frames * 1000000000ULL /
		(uint64_t)info->samples_per_sec;
516 517
}

518
/* maximum "direct" timestamp variance in nanoseconds */
519
#define MAX_TS_VAR          5000000000ULL
520
/* maximum time that timestamp can jump in nanoseconds */
521
#define MAX_TIMESTAMP_JUMP  2000000000ULL
522 523 524 525 526 527

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

529 530
static inline void handle_ts_jump(obs_source_t source, uint64_t expected,
		uint64_t ts, uint64_t diff)
531
{
J
jp9000 已提交
532
	blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%"PRIu64"', "
533
	                "expected value %"PRIu64", input value %"PRIu64,
534
	                source->context.name, diff, expected, ts);
535 536

	/* if has video, ignore audio data until reset */
537
	if (source->info.output_flags & OBS_SOURCE_ASYNC)
538
		os_atomic_dec_long(&source->av_sync_ref);
539
	else
540 541 542
		reset_audio_timing(source, ts);
}

543 544 545 546 547 548 549 550 551 552
#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,
553
		size_t frames, float volume)
554 555 556 557 558
{
	float sum_val = 0.0f;
	float max_val = 0.0f;
	float rms_val = 0.0f;

559
	audio_t        audio          = obs_get_audio();
560 561
	const uint32_t sample_rate    = audio_output_get_sample_rate(audio);
	const size_t   channels       = audio_output_get_channels(audio);
562 563 564 565 566 567 568 569 570 571 572 573
	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);
	}

574 575 576 577 578 579 580 581 582
	/*
	  We want the volume meters scale linearly in respect to current
	  volume, so, no need to apply volume here.
	*/

	UNUSED_PARAMETER(volume);

	rms_val = to_db(sqrtf(sum_val / (float)count));
	max_val = to_db(sqrtf(max_val));
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607

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

608 609
		calc_volume_levels(source, (float*)in->data[0], in->frames,
				in->volume);
610

611 612 613 614
		calldata_set_ptr  (&data, "source",    source);
		calldata_set_float(&data, "level",     source->vol_max);
		calldata_set_float(&data, "magnitude", source->vol_mag);
		calldata_set_float(&data, "peak",      source->vol_peak);
615 616 617 618 619 620 621 622 623 624

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

		calldata_free(&data);
	}
}

625 626 627 628
static void source_output_audio_line(obs_source_t source,
		const struct audio_data *data)
{
	struct audio_data in = *data;
629
	uint64_t diff;
630 631

	if (!source->timing_set) {
632
		reset_audio_timing(source, in.timestamp);
633 634

		/* detects 'directly' set timestamps as long as they're within
635
		 * a certain threshold */
636
		if ((source->timing_adjust + MAX_TS_VAR) < MAX_TS_VAR * 2)
637
			source->timing_adjust = 0;
638

J
jp9000 已提交
639
	} else if (source->next_audio_ts_min != 0) {
640
		bool ts_under = (in.timestamp < source->next_audio_ts_min);
641

642 643 644 645 646
		diff = ts_under ?
			(source->next_audio_ts_min - in.timestamp) :
			(in.timestamp - source->next_audio_ts_min);

		/* smooth audio if lower or within threshold */
647
		if (diff > MAX_TIMESTAMP_JUMP)
648 649 650 651
			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;
652 653
	}

654
	source->next_audio_ts_min = in.timestamp +
J
jp9000 已提交
655
		conv_frames_to_time(in.frames);
656

657
	if (source->av_sync_ref != 0)
658 659
		return;

J
jp9000 已提交
660
	in.timestamp += source->timing_adjust + source->sync_offset;
J
jp9000 已提交
661 662
	in.volume = source->user_volume * source->present_volume *
		obs->audio.user_volume * obs->audio.present_volume;
663

664
	audio_line_output(source->audio_line, &in);
665
	obs_source_update_volume_level(source, &in);
666 667
}

668 669 670 671 672 673 674 675
enum convert_type {
	CONVERT_NONE,
	CONVERT_NV12,
	CONVERT_420,
	CONVERT_422_U,
	CONVERT_422_Y,
};

676
static inline enum convert_type get_convert_type(enum video_format format)
677
{
678
	switch (format) {
679 680 681 682 683 684 685 686 687 688 689
	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;

690
	case VIDEO_FORMAT_NONE:
691 692 693 694 695 696 697 698 699
	case VIDEO_FORMAT_RGBA:
	case VIDEO_FORMAT_BGRA:
	case VIDEO_FORMAT_BGRX:
		return CONVERT_NONE;
	}

	return CONVERT_NONE;
}

700
static inline bool set_packed422_sizes(struct obs_source *source,
701
		struct obs_source_frame *frame)
702 703
{
	source->async_convert_height = frame->height;
704 705 706 707 708 709
	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,
710
		struct obs_source_frame *frame)
711 712 713 714 715 716 717 718 719 720
{
	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;
721 722 723 724
	return true;
}

static inline bool init_gpu_conversion(struct obs_source *source,
725
		struct obs_source_frame *frame)
726 727 728 729 730 731 732
{
	switch (get_convert_type(frame->format)) {
		case CONVERT_422_Y:
		case CONVERT_422_U:
			return set_packed422_sizes(source, frame);

		case CONVERT_420:
733 734 735 736
			return set_planar420_sizes(source, frame);

		case CONVERT_NV12:
			assert(false && "NV12 not yet implemented");
737 738 739 740 741 742 743 744 745 746 747
			/* TODO: implement conversion */
			break;

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

	}
	return false;
}

748 749 750 751 752 753 754 755 756 757 758
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;
}

759
static inline bool set_async_texture_size(struct obs_source *source,
760
		struct obs_source_frame *frame)
761 762 763 764 765 766 767 768 769 770 771
{
	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;
	}

772 773
	gs_texture_destroy(source->async_texture);
	gs_texrender_destroy(source->async_convert_texrender);
774 775 776 777 778 779
	source->async_convert_texrender = NULL;

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

		source->async_convert_texrender =
780
			gs_texrender_create(GS_BGRX, GS_ZS_NONE);
781

782
		source->async_texture = gs_texture_create(
783 784
				source->async_convert_width,
				source->async_convert_height,
785 786
				source->async_texture_format,
				1, NULL, GS_DYNAMIC);
787 788

	} else {
789 790
		enum gs_color_format format = convert_video_format(
				frame->format);
791 792
		source->async_gpu_conversion = false;

793
		source->async_texture = gs_texture_create(
794
				frame->width, frame->height,
795
				format, 1, NULL, GS_DYNAMIC);
796 797 798 799 800 801 802 803 804 805
	}

	if (!source->async_texture)
		return false;

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

806
static void upload_raw_frame(gs_texture_t tex,
807
		const struct obs_source_frame *frame)
808 809 810 811
{
	switch (get_convert_type(frame->format)) {
		case CONVERT_422_U:
		case CONVERT_422_Y:
812
			gs_texture_set_image(tex, frame->data[0],
813 814 815 816
					frame->linesize[0], false);
			break;

		case CONVERT_420:
817
			gs_texture_set_image(tex, frame->data[0],
818 819 820 821
					frame->width, false);
			break;

		case CONVERT_NV12:
822 823 824 825 826 827 828 829 830 831 832 833 834
			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:
835
			return "UYVY_Reverse";
836 837 838 839 840 841 842 843

		case VIDEO_FORMAT_YUY2:
			return "YUY2_Reverse";

		case VIDEO_FORMAT_YVYU:
			return "YVYU_Reverse";

		case VIDEO_FORMAT_I420:
844 845 846
			return "I420_Reverse";

		case VIDEO_FORMAT_NV12:
847 848 849 850 851 852 853 854 855 856 857 858 859
			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;
}

860
static inline void set_eparam(gs_effect_t effect, const char *name, float val)
861
{
862 863
	gs_eparam_t param = gs_effect_get_param_by_name(effect, name);
	gs_effect_set_float(param, val);
864 865 866
}

static bool update_async_texrender(struct obs_source *source,
867
		const struct obs_source_frame *frame)
868
{
869 870
	gs_texture_t   tex       = source->async_texture;
	gs_texrender_t texrender = source->async_convert_texrender;
871

872
	gs_texrender_reset(texrender);
873 874 875 876 877 878

	upload_raw_frame(tex, frame);

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

879 880 881
	float convert_width  = (float)source->async_convert_width;
	float convert_height = (float)source->async_convert_height;

882 883
	gs_effect_t conv = obs->video.conversion_effect;
	gs_technique_t tech = gs_effect_get_technique(conv,
884 885
			select_conversion_technique(frame->format));

886
	if (!gs_texrender_begin(texrender, cx, cy))
887 888
		return false;

889 890
	gs_technique_begin(tech);
	gs_technique_begin_pass(tech, 0);
891

892
	gs_effect_set_texture(gs_effect_get_param_by_name(conv, "image"), tex);
893 894 895 896
	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);
897
	set_eparam(conv, "width_d2",  cx * 0.5f);
898
	set_eparam(conv, "height_d2", cy * 0.5f);
899
	set_eparam(conv, "width_d2_i",  1.0f / (cx * 0.5f));
900
	set_eparam(conv, "height_d2_i", 1.0f / (cy * 0.5f));
901 902 903 904 905 906 907 908 909 910
	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]);
911 912 913 914 915

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

	gs_draw_sprite(tex, 0, cx, cy);

916 917
	gs_technique_end_pass(tech);
	gs_technique_end(tech);
918

919
	gs_texrender_end(texrender);
920 921 922 923

	return true;
}

924
static bool update_async_texture(struct obs_source *source,
925
		const struct obs_source_frame *frame)
926
{
927 928
	gs_texture_t      tex       = source->async_texture;
	gs_texrender_t    texrender = source->async_convert_texrender;
929
	enum convert_type type      = get_convert_type(frame->format);
930
	uint8_t           *ptr;
931 932
	uint32_t          linesize;

933 934 935
	source->async_format     = frame->format;
	source->async_flip       = frame->flip;
	source->async_full_range = frame->full_range;
936 937
	memcpy(source->async_color_matrix, frame->color_matrix,
			sizeof(frame->color_matrix));
938 939 940 941
	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);
942

943 944 945
	if (source->async_gpu_conversion && texrender)
		return update_async_texrender(source, frame);

946
	if (type == CONVERT_NONE) {
947
		gs_texture_set_image(tex, frame->data[0], frame->linesize[0],
948
				false);
949 950 951
		return true;
	}

952
	if (!gs_texture_map(tex, &ptr, &linesize))
953 954 955
		return false;

	if (type == CONVERT_420)
J
jp9000 已提交
956 957 958
		decompress_420((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
959 960

	else if (type == CONVERT_NV12)
J
jp9000 已提交
961 962 963
		decompress_nv12((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
964 965

	else if (type == CONVERT_422_Y)
966
		decompress_422(frame->data[0], frame->linesize[0],
J
jp9000 已提交
967
				0, frame->height, ptr, linesize, true);
968 969

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

973
	gs_texture_unmap(tex);
974 975 976
	return true;
}

977
static inline void obs_source_draw_texture(struct obs_source *source,
978
		gs_effect_t effect, float *color_matrix,
979
		float const *color_range_min, float const *color_range_max)
980
{
981 982
	gs_texture_t tex = source->async_texture;
	gs_eparam_t  param;
983

984
	if (source->async_convert_texrender)
985
		tex = gs_texrender_get_texture(source->async_convert_texrender);
986

P
Palana 已提交
987
	if (color_range_min) {
988
		size_t const size = sizeof(float) * 3;
989 990
		param = gs_effect_get_param_by_name(effect, "color_range_min");
		gs_effect_set_val(param, color_range_min, size);
P
Palana 已提交
991
	}
992

P
Palana 已提交
993 994
	if (color_range_max) {
		size_t const size = sizeof(float) * 3;
995 996
		param = gs_effect_get_param_by_name(effect, "color_range_max");
		gs_effect_set_val(param, color_range_max, size);
P
Palana 已提交
997
	}
998

P
Palana 已提交
999
	if (color_matrix) {
1000 1001
		param = gs_effect_get_param_by_name(effect, "color_matrix");
		gs_effect_set_val(param, color_matrix, sizeof(float) * 16);
1002 1003
	}

1004 1005
	param = gs_effect_get_param_by_name(effect, "image");
	gs_effect_set_texture(param, tex);
1006

1007 1008
	gs_draw_sprite(tex, source->async_flip ? GS_FLIP_V : 0, 0, 0);
}
1009

1010 1011
static void obs_source_draw_async_texture(struct obs_source *source)
{
1012 1013 1014 1015 1016 1017
	gs_effect_t    effect        = gs_get_effect();
	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);
	gs_technique_t tech          = NULL;
1018 1019 1020

	if (def_draw) {
		effect = obs_get_default_effect();
1021 1022 1023
		tech = gs_effect_get_technique(effect, type);
		gs_technique_begin(tech);
		gs_technique_begin_pass(tech, 0);
1024 1025 1026
	}

	obs_source_draw_texture(source, effect,
1027 1028 1029
			yuv ? source->async_color_matrix : NULL,
			limited_range ? source->async_color_range_min : NULL,
			limited_range ? source->async_color_range_max : NULL);
1030 1031

	if (def_draw) {
1032 1033
		gs_technique_end_pass(tech);
		gs_technique_end(tech);
1034
	}
1035 1036
}

1037 1038
static void obs_source_render_async_video(obs_source_t source)
{
1039
	struct obs_source_frame *frame = obs_source_get_frame(source);
1040 1041 1042 1043 1044 1045
	if (frame) {
		if (!set_async_texture_size(source, frame))
			return;
		if (!update_async_texture(source, frame))
			return;
	}
1046

1047 1048
	if (source->async_texture)
		obs_source_draw_async_texture(source);
1049

1050
	obs_source_release_frame(source, frame);
1051 1052
}

1053 1054 1055 1056 1057 1058 1059
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 已提交
1060 1061
static inline void obs_source_default_render(obs_source_t source,
		bool color_matrix)
1062
{
1063 1064 1065 1066
	gs_effect_t    effect     = obs->video.default_effect;
	const char     *tech_name = color_matrix ? "DrawMatrix" : "Draw";
	gs_technique_t tech       = gs_effect_get_technique(effect, tech_name);
	size_t         passes, i;
1067

1068
	passes = gs_technique_begin(tech);
1069
	for (i = 0; i < passes; i++) {
1070
		gs_technique_begin_pass(tech, i);
1071 1072
		if (source->context.data)
			source->info.video_render(source->context.data, effect);
1073
		gs_technique_end_pass(tech);
1074
	}
1075
	gs_technique_end(tech);
1076 1077 1078 1079
}

static inline void obs_source_main_render(obs_source_t source)
{
1080 1081 1082
	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;
1083 1084
	bool default_effect = !source->filter_parent &&
	                      source->filters.num == 0 &&
1085
	                      !custom_draw;
1086 1087

	if (default_effect)
J
jp9000 已提交
1088
		obs_source_default_render(source, color_matrix);
1089
	else if (source->context.data)
1090
		source->info.video_render(source->context.data,
1091
				custom_draw ? NULL : gs_get_effect());
1092 1093
}

1094
void obs_source_video_render(obs_source_t source)
J
jp9000 已提交
1095
{
1096
	if (!source_valid(source)) return;
J
jp9000 已提交
1097

1098 1099
	if (source->filters.num && !source->rendering_filter)
		obs_source_render_filters(source);
1100

1101 1102 1103 1104
	else if (source->info.video_render)
		obs_source_main_render(source);

	else if (source->filter_target)
1105 1106
		obs_source_video_render(source->filter_target);

1107
	else
1108
		obs_source_render_async_video(source);
J
jp9000 已提交
1109 1110
}

1111
uint32_t obs_source_get_width(obs_source_t source)
J
jp9000 已提交
1112
{
1113
	if (!source_valid(source)) return 0;
1114

1115 1116
	if (source->info.get_width)
		return source->info.get_width(source->context.data);
1117
	return source->async_width;
J
jp9000 已提交
1118 1119
}

1120
uint32_t obs_source_get_height(obs_source_t source)
J
jp9000 已提交
1121
{
1122
	if (!source_valid(source)) return 0;
1123

1124 1125
	if (source->info.get_height)
		return source->info.get_height(source->context.data);
1126
	return source->async_height;
J
jp9000 已提交
1127 1128
}

1129
obs_source_t obs_filter_get_parent(obs_source_t filter)
1130
{
J
jp9000 已提交
1131
	return filter ? filter->filter_parent : NULL;
1132 1133
}

1134
obs_source_t obs_filter_get_target(obs_source_t filter)
J
jp9000 已提交
1135
{
J
jp9000 已提交
1136
	return filter ? filter->filter_target : NULL;
J
jp9000 已提交
1137 1138
}

1139
void obs_source_filter_add(obs_source_t source, obs_source_t filter)
J
jp9000 已提交
1140
{
J
jp9000 已提交
1141 1142 1143
	if (!source || !filter)
		return;

1144 1145
	pthread_mutex_lock(&source->filter_mutex);

J
jp9000 已提交
1146
	if (da_find(source->filters, &filter, 0) != DARRAY_INVALID) {
J
jp9000 已提交
1147 1148 1149 1150 1151 1152
		blog(LOG_WARNING, "Tried to add a filter that was already "
		                  "present on the source");
		return;
	}

	if (source->filters.num) {
1153
		obs_source_t *back = da_end(source->filters);
J
jp9000 已提交
1154 1155 1156 1157
		(*back)->filter_target = filter;
	}

	da_push_back(source->filters, &filter);
1158 1159 1160 1161

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = source;
J
jp9000 已提交
1162 1163 1164
	filter->filter_target = source;
}

1165
void obs_source_filter_remove(obs_source_t source, obs_source_t filter)
J
jp9000 已提交
1166
{
1167 1168
	size_t idx;

J
jp9000 已提交
1169 1170 1171
	if (!source || !filter)
		return;

1172 1173 1174
	pthread_mutex_lock(&source->filter_mutex);

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
1175
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
1176 1177 1178
		return;

	if (idx > 0) {
1179
		obs_source_t prev = source->filters.array[idx-1];
J
jp9000 已提交
1180 1181 1182 1183
		prev->filter_target = filter->filter_target;
	}

	da_erase(source->filters, idx);
1184 1185 1186 1187

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = NULL;
J
jp9000 已提交
1188 1189 1190
	filter->filter_target = NULL;
}

1191
void obs_source_filter_set_order(obs_source_t source, obs_source_t filter,
J
jp9000 已提交
1192
		enum obs_order_movement movement)
J
jp9000 已提交
1193
{
J
jp9000 已提交
1194 1195 1196 1197 1198 1199
	size_t idx, i;

	if (!source || !filter)
		return;

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
1200
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
1201 1202
		return;

J
jp9000 已提交
1203
	if (movement == OBS_ORDER_MOVE_UP) {
J
jp9000 已提交
1204 1205 1206 1207
		if (idx == source->filters.num-1)
			return;
		da_move_item(source->filters, idx, idx+1);

J
jp9000 已提交
1208
	} else if (movement == OBS_ORDER_MOVE_DOWN) {
J
jp9000 已提交
1209 1210 1211 1212
		if (idx == 0)
			return;
		da_move_item(source->filters, idx, idx-1);

J
jp9000 已提交
1213
	} else if (movement == OBS_ORDER_MOVE_TOP) {
J
jp9000 已提交
1214 1215 1216 1217
		if (idx == source->filters.num-1)
			return;
		da_move_item(source->filters, idx, source->filters.num-1);

J
jp9000 已提交
1218
	} else if (movement == OBS_ORDER_MOVE_BOTTOM) {
J
jp9000 已提交
1219 1220 1221 1222 1223
		if (idx == 0)
			return;
		da_move_item(source->filters, idx, 0);
	}

1224
	/* reorder filter targets, not the nicest way of dealing with things */
J
jp9000 已提交
1225
	for (i = 0; i < source->filters.num; i++) {
1226
		obs_source_t next_filter = (i == source->filters.num-1) ?
J
jp9000 已提交
1227 1228 1229 1230 1231
			source : source->filters.array[idx+1];
		source->filters.array[i]->filter_target = next_filter;
	}
}

1232
obs_data_t obs_source_get_settings(obs_source_t source)
J
jp9000 已提交
1233
{
J
jp9000 已提交
1234 1235
	if (!source) return NULL;

1236 1237
	obs_data_addref(source->context.settings);
	return source->context.settings;
J
jp9000 已提交
1238 1239
}

1240 1241
static inline struct obs_source_frame *filter_async_video(obs_source_t source,
		struct obs_source_frame *in)
1242 1243 1244 1245
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
1246 1247

		if (filter->context.data && filter->info.filter_video) {
1248 1249
			in = filter->info.filter_video(filter->context.data,
					in);
1250 1251 1252 1253 1254 1255 1256 1257
			if (!in)
				return NULL;
		}
	}

	return in;
}

1258 1259
static inline void copy_frame_data_line(struct obs_source_frame *dst,
		const struct obs_source_frame *src, uint32_t plane, uint32_t y)
1260
{
1261 1262 1263 1264
	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];
1265 1266 1267 1268

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

1269 1270 1271
static inline void copy_frame_data_plane(struct obs_source_frame *dst,
		const struct obs_source_frame *src,
		uint32_t plane, uint32_t lines)
1272
{
1273
	if (dst->linesize[plane] != src->linesize[plane])
1274 1275 1276 1277
		for (uint32_t y = 0; y < lines; y++)
			copy_frame_data_line(dst, src, plane, y);
	else
		memcpy(dst->data[plane], src->data[plane],
1278
				dst->linesize[plane] * lines);
1279 1280
}

1281 1282
static void copy_frame_data(struct obs_source_frame *dst,
		const struct obs_source_frame *src)
1283 1284
{
	dst->flip         = src->flip;
1285
	dst->full_range   = src->full_range;
1286 1287
	dst->timestamp    = src->timestamp;
	memcpy(dst->color_matrix, src->color_matrix, sizeof(float) * 16);
1288 1289 1290 1291 1292
	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);
	}
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316

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

1317 1318
static inline struct obs_source_frame *cache_video(
		const struct obs_source_frame *frame)
1319
{
1320
	/* TODO: use an actual cache */
1321 1322
	struct obs_source_frame *new_frame = obs_source_frame_create(
			frame->format, frame->width, frame->height);
1323

1324
	copy_frame_data(new_frame, frame);
1325
	return new_frame;
1326 1327
}

1328
static bool ready_async_frame(obs_source_t source, uint64_t sys_time);
1329 1330 1331 1332

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

1336
void obs_source_output_video(obs_source_t source,
1337
		const struct obs_source_frame *frame)
1338
{
J
jp9000 已提交
1339 1340 1341
	if (!source || !frame)
		return;

1342
	struct obs_source_frame *output = cache_video(frame);
1343 1344 1345 1346 1347

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

1348 1349
	if (output) {
		pthread_mutex_lock(&source->video_mutex);
1350
		cycle_frames(source);
1351 1352 1353
		da_push_back(source->video_frames, &output);
		pthread_mutex_unlock(&source->video_mutex);
	}
1354 1355
}

1356 1357
static inline struct obs_audio_data *filter_async_audio(obs_source_t source,
		struct obs_audio_data *in)
1358 1359 1360 1361
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
1362 1363

		if (filter->context.data && filter->info.filter_audio) {
1364 1365
			in = filter->info.filter_audio(filter->context.data,
					in);
1366 1367 1368 1369 1370 1371 1372 1373
			if (!in)
				return NULL;
		}
	}

	return in;
}

1374
static inline void reset_resampler(obs_source_t source,
1375
		const struct obs_source_audio *audio)
1376
{
J
jp9000 已提交
1377
	const struct audio_output_info *obs_info;
1378 1379
	struct resample_info output_info;

1380
	obs_info = audio_output_get_info(obs->audio.audio);
1381

1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
	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 已提交
1407
		const uint8_t *const data[], uint32_t frames, uint64_t ts)
1408
{
1409 1410
	size_t planes    = audio_output_get_planes(obs->audio.audio);
	size_t blocksize = audio_output_get_block_size(obs->audio.audio);
1411 1412
	size_t size      = (size_t)frames * blocksize;
	bool   resize    = source->audio_storage_size < size;
1413

J
jp9000 已提交
1414 1415
	source->audio_data.frames    = frames;
	source->audio_data.timestamp = ts;
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428

	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;
1429 1430 1431
}

/* resamples/remixes new audio to the designated main audio output format */
1432 1433
static void process_audio(obs_source_t source,
		const struct obs_source_audio *audio)
1434 1435 1436 1437 1438 1439 1440 1441 1442 1443
{
	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 已提交
1444
		uint8_t  *output[MAX_AV_PLANES];
1445 1446 1447
		uint32_t frames;
		uint64_t offset;

1448 1449 1450 1451 1452
		memset(output, 0, sizeof(output));

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

J
jp9000 已提交
1454
		copy_audio_data(source, (const uint8_t *const *)output, frames,
1455 1456 1457 1458 1459
				audio->timestamp - offset);
	} else {
		copy_audio_data(source, audio->data, audio->frames,
				audio->timestamp);
	}
1460 1461 1462
}

void obs_source_output_audio(obs_source_t source,
1463
		const struct obs_source_audio *audio)
1464
{
J
jp9000 已提交
1465
	uint32_t flags;
1466
	struct obs_audio_data *output;
1467

J
jp9000 已提交
1468 1469 1470 1471
	if (!source || !audio)
		return;

	flags = source->info.output_flags;
1472
	process_audio(source, audio);
1473 1474

	pthread_mutex_lock(&source->filter_mutex);
1475
	output = filter_async_audio(source, &source->audio_data);
1476 1477

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

1480 1481
		pthread_mutex_lock(&source->audio_mutex);

1482 1483
		/* wait for video to start before outputting any audio so we
		 * have a base for sync */
1484
		if (source->timing_set || !async) {
1485
			struct audio_data data;
1486

J
jp9000 已提交
1487
			for (int i = 0; i < MAX_AV_PLANES; i++)
1488 1489
				data.data[i] = output->data[i];

1490 1491 1492
			data.frames    = output->frames;
			data.timestamp = output->timestamp;
			source_output_audio_line(source, &data);
1493 1494 1495 1496 1497 1498 1499 1500
		}

		pthread_mutex_unlock(&source->audio_mutex);
	}

	pthread_mutex_unlock(&source->filter_mutex);
}

1501 1502 1503 1504 1505
static inline bool frame_out_of_bounds(obs_source_t source, uint64_t ts)
{
	return ((ts - source->last_frame_ts) > MAX_TIMESTAMP_JUMP);
}

1506
static bool ready_async_frame(obs_source_t source, uint64_t sys_time)
1507
{
1508 1509
	struct obs_source_frame *next_frame = source->video_frames.array[0];
	struct obs_source_frame *frame      = NULL;
1510 1511 1512 1513 1514 1515 1516
	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;
1517
		os_atomic_inc_long(&source->av_sync_ref);
1518 1519
	} else {
		frame_offset = frame_time - source->last_frame_ts;
1520
		source->last_frame_ts += frame_offset;
1521 1522 1523
	}

	while (frame_offset <= sys_offset) {
1524
		obs_source_frame_destroy(frame);
1525

1526 1527 1528
		if (source->video_frames.num == 1)
			return true;

1529 1530 1531 1532 1533 1534 1535 1536
		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;
1537
			os_atomic_inc_long(&source->av_sync_ref);
1538 1539 1540 1541 1542 1543
		}

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

1544
	obs_source_frame_destroy(frame);
1545

1546 1547 1548
	return frame != NULL;
}

1549
static inline struct obs_source_frame *get_closest_frame(obs_source_t source,
1550 1551
		uint64_t sys_time)
{
1552
	if (ready_async_frame(source, sys_time)) {
1553
		struct obs_source_frame *frame = source->video_frames.array[0];
1554 1555 1556 1557 1558
		da_erase(source->video_frames, 0);
		return frame;
	}

	return NULL;
1559 1560
}

1561
/*
1562 1563
 * Ensures that cached frames are displayed on time.  If multiple frames
 * were cached between renders, then releases the unnecessary frames and uses
1564 1565
 * the frame with the closest timing to ensure sync.  Also ensures that timing
 * with audio is synchronized.
1566
 */
1567
struct obs_source_frame *obs_source_get_frame(obs_source_t source)
J
jp9000 已提交
1568
{
1569
	struct obs_source_frame *frame = NULL;
1570
	uint64_t sys_time;
1571

J
jp9000 已提交
1572 1573 1574
	if (!source)
		return NULL;

1575 1576 1577 1578 1579
	pthread_mutex_lock(&source->video_mutex);

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

1580
	sys_time = os_gettime_ns();
1581

1582 1583
	if (!source->last_frame_ts) {
		frame = source->video_frames.array[0];
1584 1585
		da_erase(source->video_frames, 0);

1586
		source->last_frame_ts = frame->timestamp;
1587
	} else {
1588
		frame = get_closest_frame(source, sys_time);
J
jp9000 已提交
1589 1590 1591 1592 1593 1594
	}

	/* reset timing to current system time */
	if (frame) {
		source->timing_adjust = sys_time - frame->timestamp;
		source->timing_set = true;
1595 1596 1597 1598 1599 1600
	}

	source->last_sys_timestamp = sys_time;

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

1602
	if (frame)
1603 1604
		obs_source_addref(source);

1605
	return frame;
J
jp9000 已提交
1606 1607
}

1608 1609
void obs_source_release_frame(obs_source_t source,
		struct obs_source_frame *frame)
J
jp9000 已提交
1610
{
J
jp9000 已提交
1611
	if (source && frame) {
1612
		obs_source_frame_destroy(frame);
1613 1614
		obs_source_release(source);
	}
J
jp9000 已提交
1615
}
1616

1617
const char *obs_source_get_name(obs_source_t source)
1618
{
1619
	return source ? source->context.name : NULL;
1620 1621
}

1622
void obs_source_set_name(obs_source_t source, const char *name)
1623
{
J
jp9000 已提交
1624
	if (!source) return;
J
jp9000 已提交
1625 1626 1627 1628 1629 1630 1631

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

		calldata_init(&data);
1632 1633 1634
		calldata_set_ptr(&data, "source", source);
		calldata_set_string(&data, "new_name", source->context.name);
		calldata_set_string(&data, "prev_name", prev_name);
J
jp9000 已提交
1635 1636 1637 1638 1639
		signal_handler_signal(obs->signals, "source_rename", &data);
		signal_handler_signal(source->context.signals, "rename", &data);
		calldata_free(&data);
		bfree(prev_name);
	}
1640 1641
}

J
jp9000 已提交
1642
enum obs_source_type obs_source_get_type(obs_source_t source)
1643
{
J
jp9000 已提交
1644 1645
	return source ? source->info.type : OBS_SOURCE_TYPE_INPUT;
}
J
jp9000 已提交
1646

J
jp9000 已提交
1647 1648 1649
const char *obs_source_get_id(obs_source_t source)
{
	return source ? source->info.id : NULL;
1650
}
1651

1652
static inline void render_filter_bypass(obs_source_t target, gs_effect_t effect,
J
jp9000 已提交
1653
		bool use_matrix)
1654
{
J
jp9000 已提交
1655
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1656
	gs_technique_t tech    = gs_effect_get_technique(effect, tech_name);
1657 1658
	size_t      passes, i;

1659
	passes = gs_technique_begin(tech);
1660
	for (i = 0; i < passes; i++) {
1661
		gs_technique_begin_pass(tech, i);
1662
		obs_source_video_render(target);
1663
		gs_technique_end_pass(tech);
1664
	}
1665
	gs_technique_end(tech);
1666 1667
}

1668
static inline void render_filter_tex(gs_texture_t tex, gs_effect_t effect,
J
jp9000 已提交
1669
		uint32_t width, uint32_t height, bool use_matrix)
1670
{
J
jp9000 已提交
1671
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1672 1673
	gs_technique_t tech    = gs_effect_get_technique(effect, tech_name);
	gs_eparam_t    image   = gs_effect_get_param_by_name(effect, "image");
1674 1675
	size_t      passes, i;

1676
	gs_effect_set_texture(image, tex);
1677

1678
	passes = gs_technique_begin(tech);
1679
	for (i = 0; i < passes; i++) {
1680
		gs_technique_begin_pass(tech, i);
1681
		gs_draw_sprite(tex, width, height, 0);
1682
		gs_technique_end_pass(tech);
1683
	}
1684
	gs_technique_end(tech);
1685 1686
}

1687
void obs_source_process_filter(obs_source_t filter, gs_effect_t effect,
J
jp9000 已提交
1688
		uint32_t width, uint32_t height, enum gs_color_format format,
1689
		enum obs_allow_direct_render allow_direct)
1690
{
J
jp9000 已提交
1691 1692 1693 1694 1695 1696 1697
	obs_source_t target, parent;
	uint32_t     target_flags, parent_flags;
	int          cx, cy;
	bool         use_matrix, expects_def, can_directly;

	if (!filter) return;

1698 1699
	target       = obs_filter_get_target(filter);
	parent       = obs_filter_get_parent(filter);
J
jp9000 已提交
1700 1701
	target_flags = target->info.output_flags;
	parent_flags = parent->info.output_flags;
1702 1703
	cx           = obs_source_get_width(target);
	cy           = obs_source_get_height(target);
J
jp9000 已提交
1704 1705
	use_matrix   = !!(target_flags & OBS_SOURCE_COLOR_MATRIX);
	expects_def  = !(parent_flags & OBS_SOURCE_CUSTOM_DRAW);
1706
	can_directly = allow_direct == OBS_ALLOW_DIRECT_RENDERING;
1707 1708 1709 1710 1711

	/* 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 */
1712
	if (can_directly && expects_def && target == parent) {
J
jp9000 已提交
1713
		render_filter_bypass(target, effect, use_matrix);
1714 1715 1716
		return;
	}

J
jp9000 已提交
1717
	if (!filter->filter_texrender)
1718
		filter->filter_texrender = gs_texrender_create(format,
J
jp9000 已提交
1719 1720
				GS_ZS_NONE);

1721
	if (gs_texrender_begin(filter->filter_texrender, cx, cy)) {
1722
		gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
1723
		if (expects_def && parent == target)
J
jp9000 已提交
1724
			obs_source_default_render(parent, use_matrix);
1725 1726
		else
			obs_source_video_render(target);
1727
		gs_texrender_end(filter->filter_texrender);
1728 1729 1730 1731
	}

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

1732
	render_filter_tex(gs_texrender_get_texture(filter->filter_texrender),
J
jp9000 已提交
1733
			effect, width, height, use_matrix);
1734
}
1735

1736
signal_handler_t obs_source_get_signal_handler(obs_source_t source)
1737
{
1738
	return source ? source->context.signals : NULL;
1739 1740
}

1741
proc_handler_t obs_source_get_proc_handler(obs_source_t source)
1742
{
1743
	return source ? source->context.procs : NULL;
1744
}
J
jp9000 已提交
1745

1746
void obs_source_set_volume(obs_source_t source, float volume)
J
jp9000 已提交
1747
{
J
jp9000 已提交
1748 1749
	if (source) {
		struct calldata data = {0};
1750 1751
		calldata_set_ptr(&data, "source", source);
		calldata_set_float(&data, "volume", volume);
J
jp9000 已提交
1752

1753
		signal_handler_signal(source->context.signals, "volume", &data);
1754
		signal_handler_signal(obs->signals, "source_volume", &data);
J
jp9000 已提交
1755

1756
		volume = (float)calldata_float(&data, "volume");
J
jp9000 已提交
1757 1758
		calldata_free(&data);

J
jp9000 已提交
1759
		source->user_volume = volume;
J
jp9000 已提交
1760
	}
J
jp9000 已提交
1761 1762
}

J
jp9000 已提交
1763 1764 1765 1766 1767 1768 1769 1770 1771
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 已提交
1772 1773
void obs_source_set_present_volume(obs_source_t source, float volume)
{
J
jp9000 已提交
1774
	if (source) {
J
jp9000 已提交
1775
		source->present_volume = volume;
J
jp9000 已提交
1776 1777 1778 1779 1780 1781 1782 1783

		/* 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 已提交
1784 1785
}

1786
float obs_source_get_volume(obs_source_t source)
J
jp9000 已提交
1787
{
J
jp9000 已提交
1788
	return source ? source->user_volume : 0.0f;
J
jp9000 已提交
1789 1790 1791 1792
}

float obs_source_get_present_volume(obs_source_t source)
{
J
jp9000 已提交
1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804
	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 已提交
1805
}
1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817

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

1820 1821 1822
		if (child->context.data)
			child->info.enum_sources(child->context.data,
					enum_source_tree_callback, data);
1823

J
jp9000 已提交
1824
		os_atomic_dec_long(&child->enum_refs);
1825 1826 1827 1828 1829 1830 1831 1832 1833
	}

	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)
{
1834 1835 1836
	if (!source_valid(source)      ||
	    !source->info.enum_sources ||
	    source->enum_refs)
1837 1838 1839 1840
		return;

	obs_source_addref(source);

J
jp9000 已提交
1841
	os_atomic_inc_long(&source->enum_refs);
1842
	source->info.enum_sources(source->context.data, enum_callback, param);
J
jp9000 已提交
1843
	os_atomic_dec_long(&source->enum_refs);
1844 1845 1846 1847 1848 1849 1850 1851 1852 1853

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

1854 1855 1856
	if (!source_valid(source)      ||
	    !source->info.enum_sources ||
	    source->enum_refs)
1857 1858 1859 1860
		return;

	obs_source_addref(source);

J
jp9000 已提交
1861
	os_atomic_inc_long(&source->enum_refs);
1862 1863
	source->info.enum_sources(source->context.data,
			enum_source_tree_callback,
1864
			&data);
J
jp9000 已提交
1865
	os_atomic_dec_long(&source->enum_refs);
1866 1867 1868

	obs_source_release(source);
}
1869 1870 1871 1872 1873

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

1874 1875 1876 1877 1878
	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);
	}
1879 1880 1881 1882 1883 1884
}

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

1885 1886 1887 1888 1889
	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);
	}
1890
}
J
jp9000 已提交
1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937

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);
}
1938 1939 1940

void obs_source_save(obs_source_t source)
{
1941
	if (!source_valid(source) || !source->info.save) return;
1942 1943 1944 1945 1946
	source->info.save(source->context.data, source->context.settings);
}

void obs_source_load(obs_source_t source)
{
1947
	if (!source_valid(source) || !source->info.load) return;
1948 1949
	source->info.load(source->context.data, source->context.settings);
}