obs-source.c 30.5 KB
Newer Older
J
jp9000 已提交
1 2 3 4 5
/******************************************************************************
    Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>

    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
#include "media-io/format-conversion.h"
19
#include "util/platform.h"
20
#include "callback/calldata.h"
21 22
#include "graphics/matrix3.h"
#include "graphics/vec3.h"
23

J
jp9000 已提交
24
#include "obs.h"
J
jp9000 已提交
25
#include "obs-internal.h"
J
jp9000 已提交
26

27 28
static void obs_source_destroy(obs_source_t source);

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

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

	return NULL;
}

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

	switch (type) {
J
jp9000 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62
	case OBS_SOURCE_TYPE_INPUT:
		list = &obs->input_types.da;
		break;

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

	case OBS_SOURCE_TYPE_TRANSITION:
		list = &obs->transition_types.da;
		break;

	case OBS_SOURCE_TYPE_SCENE:
63 64 65 66 67 68 69 70
	default:
		blog(LOG_WARNING, "get_source_info: invalid source type");
		return NULL;
	}

	return find_source(list, id);
}

J
jp9000 已提交
71
bool obs_source_init_handlers(struct obs_source *source)
72 73 74 75 76 77 78 79 80
{
	source->signals = signal_handler_create();
	if (!source->signals)
		return false;

	source->procs = proc_handler_create();
	return (source->procs != NULL);
}

81 82 83
const char *obs_source_getdisplayname(enum obs_source_type type,
		const char *id, const char *locale)
{
J
jp9000 已提交
84
	const struct obs_source_info *info = get_source_info(type, id);
85 86 87
	return (info != NULL) ? info->getname(locale) : NULL;
}

88
/* internal initialization */
J
jp9000 已提交
89 90
bool obs_source_init(struct obs_source *source,
		const struct obs_source_info *info)
J
jp9000 已提交
91
{
92
	source->refs = 1;
J
jp9000 已提交
93
	source->volume = 1.0f;
94 95 96
	pthread_mutex_init_value(&source->filter_mutex);
	pthread_mutex_init_value(&source->video_mutex);
	pthread_mutex_init_value(&source->audio_mutex);
97

J
jp9000 已提交
98
	memcpy(&source->info, info, sizeof(struct obs_source_info));
99 100 101 102 103 104 105

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

J
jp9000 已提交
107
	if (info->output_flags & OBS_SOURCE_AUDIO) {
J
jp9000 已提交
108 109
		source->audio_line = audio_output_createline(obs->audio.audio,
				source->name);
110 111
		if (!source->audio_line) {
			blog(LOG_ERROR, "Failed to create audio line for "
J
jp9000 已提交
112
			                "source '%s'", source->name);
113 114 115
			return false;
		}
	}
116

117
	return true;
J
jp9000 已提交
118 119
}

120 121 122 123 124 125 126 127 128 129 130
static inline void obs_source_dosignal(struct obs_source *source,
		const char *signal)
{
	struct calldata data;

	calldata_init(&data);
	calldata_setptr(&data, "source", source);
	signal_handler_signal(obs->signals, signal, &data);
	calldata_free(&data);
}

131
obs_source_t obs_source_create(enum obs_source_type type, const char *id,
132
		const char *name, obs_data_t settings)
J
jp9000 已提交
133 134 135
{
	struct obs_source *source;

J
jp9000 已提交
136
	const struct obs_source_info *info = get_source_info(type, id);
J
jp9000 已提交
137
	if (!info) {
138
		blog(LOG_WARNING, "Source '%s' not found", id);
J
jp9000 已提交
139 140 141
		return NULL;
	}

142
	source = bzalloc(sizeof(struct obs_source));
143

144 145 146
	if (!obs_source_init_handlers(source))
		goto fail;

J
jp9000 已提交
147 148 149 150 151
	source->name     = bstrdup(name);
	source->type     = type;
	source->settings = obs_data_newref(settings);
	source->data     = info->create(source->settings, source);

152 153 154
	if (!source->data)
		goto fail;

J
jp9000 已提交
155
	if (!obs_source_init(source, info))
156
		goto fail;
J
jp9000 已提交
157

J
jp9000 已提交
158
	obs_source_dosignal(source, "source-create");
J
jp9000 已提交
159
	return source;
160 161 162 163 164

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

167 168 169
#define ALIGN_SIZE(size, align) \
	size = (((size)+(align-1)) & (~(align-1)))

170 171
/* messy code alarm */
void source_frame_init(struct source_frame *frame,
172 173 174 175
		enum video_format format, uint32_t width, uint32_t height)
{
	size_t size;
	size_t offsets[MAX_VIDEO_PLANES];
176 177
	int    alignment = base_get_alignment();

178
	memset(offsets, 0, sizeof(offsets));
179 180 181
	frame->format = format;
	frame->width  = width;
	frame->height = height;
182 183 184 185 186 187 188

	switch (format) {
	case VIDEO_FORMAT_NONE:
		return;

	case VIDEO_FORMAT_I420:
		size = width * height;
189
		ALIGN_SIZE(size, alignment);
190 191
		offsets[0] = size;
		size += (width/2) * (height/2);
192
		ALIGN_SIZE(size, alignment);
193 194
		offsets[1] = size;
		size += (width/2) * (height/2);
195
		ALIGN_SIZE(size, alignment);
196 197 198
		frame->data[0] = bmalloc(size);
		frame->data[1] = (uint8_t*)frame->data[0] + offsets[0];
		frame->data[2] = (uint8_t*)frame->data[0] + offsets[1];
199 200 201
		frame->linesize[0] = width;
		frame->linesize[1] = width/2;
		frame->linesize[2] = width/2;
202 203 204 205
		break;

	case VIDEO_FORMAT_NV12:
		size = width * height;
206
		ALIGN_SIZE(size, alignment);
207 208
		offsets[0] = size;
		size += (width/2) * (height/2) * 2;
209
		ALIGN_SIZE(size, alignment);
210 211
		frame->data[0] = bmalloc(size);
		frame->data[1] = (uint8_t*)frame->data[0] + offsets[0];
212 213
		frame->linesize[0] = width;
		frame->linesize[1] = width;
214 215 216 217 218 219
		break;

	case VIDEO_FORMAT_YVYU:
	case VIDEO_FORMAT_YUY2:
	case VIDEO_FORMAT_UYVY:
		size = width * height * 2;
220
		ALIGN_SIZE(size, alignment);
221
		frame->data[0] = bmalloc(size);
222
		frame->linesize[0] = width*2;
223 224 225 226 227 228
		break;

	case VIDEO_FORMAT_RGBA:
	case VIDEO_FORMAT_BGRA:
	case VIDEO_FORMAT_BGRX:
		size = width * height * 4;
229
		ALIGN_SIZE(size, alignment);
230
		frame->data[0] = bmalloc(size);
231
		frame->linesize[0] = width*4;
232 233 234 235
		break;
	}
}

236
static void obs_source_destroy(obs_source_t source)
J
jp9000 已提交
237
{
238
	size_t i;
239

240 241
	obs_source_dosignal(source, "source-destroy");

242 243
	if (source->filter_parent)
		obs_source_filter_remove(source->filter_parent, source);
244

245 246
	for (i = 0; i < source->filters.num; i++)
		obs_source_release(source->filters.array[i]);
247

248 249
	for (i = 0; i < source->video_frames.num; i++)
		source_frame_destroy(source->video_frames.array[i]);
250

251 252 253
	gs_entercontext(obs->video.graphics);
	texture_destroy(source->output_texture);
	gs_leavecontext();
J
jp9000 已提交
254

255
	if (source->data)
J
jp9000 已提交
256
		source->info.destroy(source->data);
257

258 259 260
	for (i = 0; i < MAX_AUDIO_PLANES; i++)
		bfree(source->audio_data.data[i]);

261 262 263
	audio_line_destroy(source->audio_line);
	audio_resampler_destroy(source->resampler);

264 265 266
	proc_handler_destroy(source->procs);
	signal_handler_destroy(source->signals);

J
jp9000 已提交
267
	texrender_destroy(source->filter_texrender);
268 269 270 271 272
	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);
273
	obs_data_release(source->settings);
274
	bfree(source->name);
275 276 277
	bfree(source);
}

P
Palana 已提交
278
void obs_source_addref(obs_source_t source)
279
{
P
Palana 已提交
280 281
	if (source)
		++source->refs;
282 283
}

P
Palana 已提交
284
void obs_source_release(obs_source_t source)
285
{
P
Palana 已提交
286 287
	if (!source)
		return;
288

P
Palana 已提交
289 290
	if (--source->refs == 0)
		obs_source_destroy(source);
291 292 293 294
}

void obs_source_remove(obs_source_t source)
{
295
	struct obs_core_data *data = &obs->data;
296 297 298 299
	size_t id;

	pthread_mutex_lock(&data->sources_mutex);

J
jp9000 已提交
300
	if (!source || source->removed)
J
jp9000 已提交
301 302
		return;

J
jp9000 已提交
303
	source->removed = true;
J
jp9000 已提交
304

J
jp9000 已提交
305 306 307 308 309 310
	obs_source_addref(source);

	id = da_find(data->sources, &source, 0);
	if (id != DARRAY_INVALID) {
		da_erase_item(data->sources, &source);
		obs_source_release(source);
311 312 313
	}

	pthread_mutex_unlock(&data->sources_mutex);
J
jp9000 已提交
314 315 316

	obs_source_dosignal(source, "source-remove");
	obs_source_release(source);
317 318 319 320 321
}

bool obs_source_removed(obs_source_t source)
{
	return source->removed;
J
jp9000 已提交
322 323
}

J
jp9000 已提交
324 325 326
obs_properties_t obs_source_properties(enum obs_source_type type,
		const char *id, const char *locale)
{
J
jp9000 已提交
327 328 329
	const struct obs_source_info *info = get_source_info(type, id);
	if (info && info->get_properties)
	       return info->get_properties(locale);
J
jp9000 已提交
330 331 332
	return NULL;
}

333
uint32_t obs_source_get_output_flags(obs_source_t source)
J
jp9000 已提交
334
{
J
jp9000 已提交
335
	return source->info.output_flags;
J
jp9000 已提交
336 337
}

338
void obs_source_update(obs_source_t source, obs_data_t settings)
J
jp9000 已提交
339
{
340 341
	obs_data_replace(&source->settings, settings);

J
jp9000 已提交
342 343
	if (source->info.update)
		source->info.update(source->data, source->settings);
J
jp9000 已提交
344 345
}

346
void obs_source_activate(obs_source_t source)
J
jp9000 已提交
347
{
J
jp9000 已提交
348 349
	if (source->info.activate)
		source->info.activate(source->data);
J
jp9000 已提交
350 351
}

352
void obs_source_deactivate(obs_source_t source)
J
jp9000 已提交
353
{
J
jp9000 已提交
354 355
	if (source->info.deactivate)
		source->info.deactivate(source->data);
J
jp9000 已提交
356 357
}

358
void obs_source_video_tick(obs_source_t source, float seconds)
J
jp9000 已提交
359
{
J
jp9000 已提交
360 361 362 363 364 365
	/* reset the filter render texture information once every frame */
	if (source->filter_texrender)
		texrender_reset(source->filter_texrender);

	if (source->info.video_tick)
		source->info.video_tick(source->data, seconds);
J
jp9000 已提交
366 367
}

368
/* unless the value is 3+ hours worth of frames, this won't overflow */
369 370
static inline uint64_t conv_frames_to_time(obs_source_t source, size_t frames)
{
J
jp9000 已提交
371 372
	const struct audio_output_info *info;
	info = audio_output_getinfo(obs->audio.audio);
373 374 375

	return (uint64_t)frames * 1000000000ULL /
		(uint64_t)info->samples_per_sec;
376 377
}

378
/* maximum "direct" timestamp variance in nanoseconds */
379
#define MAX_TS_VAR         5000000000ULL
380
/* maximum time that timestamp can jump in nanoseconds */
381
#define MAX_TIMESTAMP_JUMP 2000000000ULL
382 383 384 385 386 387

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

389 390 391 392 393 394 395
static inline void handle_ts_jump(obs_source_t source, uint64_t ts,
		uint64_t diff)
{
	blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%lld', "
	                "resetting audio timing", source->name, diff);

	/* if has video, ignore audio data until reset */
J
jp9000 已提交
396
	if (source->info.output_flags & OBS_SOURCE_ASYNC_VIDEO)
397 398 399 400 401
		source->audio_reset_ref--;
	else 
		reset_audio_timing(source, ts);
}

402 403 404 405
static void source_output_audio_line(obs_source_t source,
		const struct audio_data *data)
{
	struct audio_data in = *data;
406
	uint64_t diff;
407 408

	if (!source->timing_set) {
409
		reset_audio_timing(source, in.timestamp);
410 411

		/* detects 'directly' set timestamps as long as they're within
412
		 * a certain threshold */
413
		if ((source->timing_adjust + MAX_TS_VAR) < MAX_TS_VAR * 2)
414
			source->timing_adjust = 0;
415

416
	} else {
417
		diff = in.timestamp - source->next_audio_ts_min;
418 419 420

		/* don't need signed because negative will trigger it
		 * regardless, which is what we want */
421 422
		if (diff > MAX_TIMESTAMP_JUMP)
			handle_ts_jump(source, in.timestamp, diff);
423 424
	}

425
	source->next_audio_ts_min = in.timestamp +
426 427
		conv_frames_to_time(source, in.frames);

428 429 430
	if (source->audio_reset_ref != 0)
		return;

431
	in.timestamp += source->timing_adjust;
J
jp9000 已提交
432
	in.volume = source->volume;
433

434 435 436
	audio_line_output(source->audio_line, &in);
}

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
static bool set_texture_size(obs_source_t source, struct source_frame *frame)
{
	if (source->output_texture) {
		uint32_t width  = texture_getwidth(source->output_texture);
		uint32_t height = texture_getheight(source->output_texture);

		if (width == frame->width && height == frame->height)
			return true;
	}

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

	return source->output_texture != NULL;
}

454 455 456 457 458 459 460 461
enum convert_type {
	CONVERT_NONE,
	CONVERT_NV12,
	CONVERT_420,
	CONVERT_422_U,
	CONVERT_422_Y,
};

462
static inline enum convert_type get_convert_type(enum video_format format)
463
{
464
	switch (format) {
465 466 467 468 469 470 471 472 473 474 475
	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;

476
	case VIDEO_FORMAT_NONE:
477 478 479 480 481 482 483 484 485 486 487 488
	case VIDEO_FORMAT_RGBA:
	case VIDEO_FORMAT_BGRA:
	case VIDEO_FORMAT_BGRX:
		return CONVERT_NONE;
	}

	return CONVERT_NONE;
}

static bool upload_frame(texture_t tex, const struct source_frame *frame)
{
	void *ptr;
489
	uint32_t linesize;
490
	enum convert_type type = get_convert_type(frame->format);
491 492

	if (type == CONVERT_NONE) {
493
		texture_setimage(tex, frame->data[0], frame->linesize[0],
494
				false);
495 496 497
		return true;
	}

498
	if (!texture_map(tex, &ptr, &linesize))
499 500 501
		return false;

	if (type == CONVERT_420)
502
		decompress_420(frame->data, frame->linesize,
503
				frame->width, frame->height, 0, frame->height,
504
				ptr, linesize);
505 506

	else if (type == CONVERT_NV12)
507
		decompress_nv12(frame->data, frame->linesize,
508
				frame->width, frame->height, 0, frame->height,
509
				ptr, linesize);
510 511

	else if (type == CONVERT_422_Y)
512
		decompress_422(frame->data[0], frame->linesize[0],
513
				frame->width, frame->height, 0, frame->height,
514
				ptr, linesize, true);
515 516

	else if (type == CONVERT_422_U)
517
		decompress_422(frame->data[0], frame->linesize[0],
518
				frame->width, frame->height, 0, frame->height,
519
				ptr, linesize, false);
520 521 522 523 524

	texture_unmap(tex);
	return true;
}

525 526
static void obs_source_draw_texture(texture_t tex, struct source_frame *frame)
{
527
	effect_t    effect = obs->video.default_effect;
528
	bool        yuv    = format_is_yuv(frame->format);
529
	const char  *type  = yuv ? "DrawMatrix" : "Draw";
530
	technique_t tech;
531
	eparam_t    param;
532

533 534
	if (!upload_frame(tex, frame))
		return;
535 536 537 538 539

	tech = effect_gettechnique(effect, type);
	technique_begin(tech);
	technique_beginpass(tech, 0);

540
	if (yuv) {
541 542
		param = effect_getparambyname(effect, "color_matrix");
		effect_setval(effect, param, frame->color_matrix,
543 544 545
				sizeof(float) * 16);
	}

J
jp9000 已提交
546
	param = effect_getparambyname(effect, "image");
547 548
	effect_settexture(effect, param, tex);

549
	gs_draw_sprite(tex, frame->flip ? GS_FLIP_V : 0, 0, 0);
550 551 552 553 554

	technique_endpass(tech);
	technique_end(tech);
}

555 556 557 558 559 560
static void obs_source_render_async_video(obs_source_t source)
{
	struct source_frame *frame = obs_source_getframe(source);
	if (!frame)
		return;

J
jp9000 已提交
561
	if (set_texture_size(source, frame))
562
		obs_source_draw_texture(source->output_texture, frame);
563 564 565 566

	obs_source_releaseframe(source, frame);
}

567 568 569 570 571 572 573
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 已提交
574 575
static inline void obs_source_default_render(obs_source_t source,
		bool color_matrix)
576 577
{
	effect_t    effect     = obs->video.default_effect;
J
jp9000 已提交
578
	const char  *tech_name = color_matrix ? "DrawMatrix" : "Draw";
579 580 581 582 583 584
	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);
J
jp9000 已提交
585
		source->info.video_render(source->data, effect);
586 587 588 589 590 591 592
		technique_endpass(tech);
	}
	technique_end(tech);
}

static inline void obs_source_main_render(obs_source_t source)
{
J
jp9000 已提交
593 594
	uint32_t flags = source->info.output_flags;
	bool color_matrix = (flags & OBS_SOURCE_COLOR_MATRIX) != 0;
595 596
	bool default_effect = !source->filter_parent &&
	                      source->filters.num == 0 &&
J
jp9000 已提交
597
	                      (flags & OBS_SOURCE_CUSTOM_DRAW) == 0;
598 599

	if (default_effect)
J
jp9000 已提交
600
		obs_source_default_render(source, color_matrix);
601
	else
J
jp9000 已提交
602
		source->info.video_render(source->data, NULL);
603 604
}

605
void obs_source_video_render(obs_source_t source)
J
jp9000 已提交
606
{
J
jp9000 已提交
607
	if (source->info.video_render) {
608 609 610 611
		if (source->filters.num && !source->rendering_filter)
			obs_source_render_filters(source);
		else
			obs_source_main_render(source);
612 613 614 615 616 617

	} else if (source->filter_target) {
		obs_source_video_render(source->filter_target);

	} else {
		obs_source_render_async_video(source);
J
jp9000 已提交
618 619 620
	}
}

J
jp9000 已提交
621
uint32_t obs_source_getwidth(obs_source_t source)
J
jp9000 已提交
622
{
J
jp9000 已提交
623 624
	if (source->info.getwidth)
		return source->info.getwidth(source->data);
J
jp9000 已提交
625 626 627
	return 0;
}

J
jp9000 已提交
628
uint32_t obs_source_getheight(obs_source_t source)
J
jp9000 已提交
629
{
J
jp9000 已提交
630 631
	if (source->info.getheight)
		return source->info.getheight(source->data);
J
jp9000 已提交
632 633 634
	return 0;
}

635 636 637 638 639
obs_source_t obs_filter_getparent(obs_source_t filter)
{
	return filter->filter_parent;
}

640
obs_source_t obs_filter_gettarget(obs_source_t filter)
J
jp9000 已提交
641 642 643 644
{
	return filter->filter_target;
}

645
void obs_source_filter_add(obs_source_t source, obs_source_t filter)
J
jp9000 已提交
646
{
647 648
	pthread_mutex_lock(&source->filter_mutex);

J
jp9000 已提交
649
	if (da_find(source->filters, &filter, 0) != DARRAY_INVALID) {
J
jp9000 已提交
650 651 652 653 654 655
		blog(LOG_WARNING, "Tried to add a filter that was already "
		                  "present on the source");
		return;
	}

	if (source->filters.num) {
656
		obs_source_t *back = da_end(source->filters);
J
jp9000 已提交
657 658 659 660
		(*back)->filter_target = filter;
	}

	da_push_back(source->filters, &filter);
661 662 663 664

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = source;
J
jp9000 已提交
665 666 667
	filter->filter_target = source;
}

668
void obs_source_filter_remove(obs_source_t source, obs_source_t filter)
J
jp9000 已提交
669
{
670 671 672 673 674
	size_t idx;

	pthread_mutex_lock(&source->filter_mutex);

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
675
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
676 677 678
		return;

	if (idx > 0) {
679
		obs_source_t prev = source->filters.array[idx-1];
J
jp9000 已提交
680 681 682 683
		prev->filter_target = filter->filter_target;
	}

	da_erase(source->filters, idx);
684 685 686 687

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = NULL;
J
jp9000 已提交
688 689 690
	filter->filter_target = NULL;
}

691
void obs_source_filter_setorder(obs_source_t source, obs_source_t filter,
J
jp9000 已提交
692 693 694 695
		enum order_movement movement)
{
	size_t idx = da_find(source->filters, &filter, 0);
	size_t i;
J
jp9000 已提交
696
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
		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);
	}

720
	/* reorder filter targets, not the nicest way of dealing with things */
J
jp9000 已提交
721
	for (i = 0; i < source->filters.num; i++) {
722
		obs_source_t next_filter = (i == source->filters.num-1) ?
J
jp9000 已提交
723 724 725 726 727
			source : source->filters.array[idx+1];
		source->filters.array[i]->filter_target = next_filter;
	}
}

728
obs_data_t obs_source_getsettings(obs_source_t source)
J
jp9000 已提交
729
{
730 731
	obs_data_addref(source->settings);
	return source->settings;
J
jp9000 已提交
732 733
}

734 735
static inline struct source_frame *filter_async_video(obs_source_t source,
		struct source_frame *in)
736 737 738 739
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
J
jp9000 已提交
740 741
		if (filter->info.filter_video) {
			in = filter->info.filter_video(filter->data, in);
742 743 744 745 746 747 748 749
			if (!in)
				return NULL;
		}
	}

	return in;
}

750 751 752
static inline void copy_frame_data_line(struct source_frame *dst,
		const struct source_frame *src, uint32_t plane, uint32_t y)
{
753 754 755 756
	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];
757 758 759 760 761 762 763

	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)
{
764
	if (dst->linesize[plane] != src->linesize[plane])
765 766 767 768
		for (uint32_t y = 0; y < lines; y++)
			copy_frame_data_line(dst, src, plane, y);
	else
		memcpy(dst->data[plane], src->data[plane],
769
				dst->linesize[plane] * lines);
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
}

static void copy_frame_data(struct source_frame *dst,
		const struct source_frame *src)
{
	dst->flip         = src->flip;
	dst->timestamp    = src->timestamp;
	memcpy(dst->color_matrix, src->color_matrix, sizeof(float) * 16);

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

802 803
static inline struct source_frame *cache_video(obs_source_t source,
		const struct source_frame *frame)
804
{
805
	/* TODO: use an actual cache */
806
	struct source_frame *new_frame = source_frame_create(frame->format,
807
			frame->width, frame->height);
808

809
	copy_frame_data(new_frame, frame);
810
	return new_frame;
811 812 813
}

void obs_source_output_video(obs_source_t source,
814
		const struct source_frame *frame)
815
{
816
	struct source_frame *output = cache_video(source, frame);
817 818 819 820 821

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

822 823 824 825 826
	if (output) {
		pthread_mutex_lock(&source->video_mutex);
		da_push_back(source->video_frames, &output);
		pthread_mutex_unlock(&source->video_mutex);
	}
827 828
}

829 830
static inline struct filtered_audio *filter_async_audio(obs_source_t source,
		struct filtered_audio *in)
831 832 833 834
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
J
jp9000 已提交
835 836
		if (filter->info.filter_audio) {
			in = filter->info.filter_audio(filter->data, in);
837 838 839 840 841 842 843 844
			if (!in)
				return NULL;
		}
	}

	return in;
}

845
static inline void reset_resampler(obs_source_t source,
846 847
		const struct source_audio *audio)
{
J
jp9000 已提交
848
	const struct audio_output_info *obs_info;
849 850
	struct resample_info output_info;

851 852
	obs_info = audio_output_getinfo(obs->audio.audio);

853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877
	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,
878
		const void *const data[], uint32_t frames, uint64_t timestamp)
879
{
880
	size_t planes    = audio_output_planes(obs->audio.audio);
881
	size_t blocksize = audio_output_blocksize(obs->audio.audio);
882 883
	size_t size      = (size_t)frames * blocksize;
	bool   resize    = source->audio_storage_size < size;
884 885 886

	source->audio_data.frames = frames;
	source->audio_data.timestamp = timestamp;
887 888 889 890 891 892 893 894 895 896 897 898 899

	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;
900 901 902 903 904 905 906 907 908 909 910 911 912 913
}

/* 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) {
914
		uint8_t  *output[MAX_AUDIO_PLANES];
915 916 917
		uint32_t frames;
		uint64_t offset;

918 919 920 921 922
		memset(output, 0, sizeof(output));

		audio_resampler_resample(source->resampler,
				output, &frames, &offset,
				audio->data, audio->frames);
923 924 925 926 927 928 929

		copy_audio_data(source, output, frames,
				audio->timestamp - offset);
	} else {
		copy_audio_data(source, audio->data, audio->frames,
				audio->timestamp);
	}
930 931 932 933 934 935
}

void obs_source_output_audio(obs_source_t source,
		const struct source_audio *audio)
{
	uint32_t flags = obs_source_get_output_flags(source);
936
	size_t blocksize = audio_output_blocksize(obs->audio.audio);
937
	struct filtered_audio *output;
938

939
	process_audio(source, audio);
940 941

	pthread_mutex_lock(&source->filter_mutex);
942
	output = filter_async_audio(source, &source->audio_data);
943 944

	if (output) {
J
jp9000 已提交
945 946
		bool async = (flags & OBS_SOURCE_ASYNC_VIDEO) == 0;

947 948
		pthread_mutex_lock(&source->audio_mutex);

949 950
		/* wait for video to start before outputting any audio so we
		 * have a base for sync */
J
jp9000 已提交
951
		if (source->timing_set || async) {
952
			struct audio_data data;
953 954 955 956

			for (int i = 0; i < MAX_AUDIO_PLANES; i++)
				data.data[i] = output->data[i];

957 958 959
			data.frames    = output->frames;
			data.timestamp = output->timestamp;
			source_output_audio_line(source, &data);
960 961 962 963 964 965 966 967
		}

		pthread_mutex_unlock(&source->audio_mutex);
	}

	pthread_mutex_unlock(&source->filter_mutex);
}

968 969 970 971 972 973
static inline bool frame_out_of_bounds(obs_source_t source, uint64_t ts)
{
	return ((ts - source->last_frame_ts) > MAX_TIMESTAMP_JUMP);
}

static inline struct source_frame *get_closest_frame(obs_source_t source,
J
jp9000 已提交
974
		uint64_t sys_time, int *audio_time_refs)
975 976 977 978 979 980 981 982 983 984
{
	struct source_frame *next_frame = source->video_frames.array[0];
	struct source_frame *frame      = NULL;
	uint64_t sys_offset = sys_time - source->last_sys_timestamp;
	uint64_t frame_time = next_frame->timestamp;
	uint64_t frame_offset = 0;

	/* account for timestamp invalidation */
	if (frame_out_of_bounds(source, frame_time)) {
		source->last_frame_ts = next_frame->timestamp;
J
jp9000 已提交
985
		(*audio_time_refs)++;
986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
	} else {
		frame_offset = frame_time - source->last_frame_ts;
		source->last_frame_ts += sys_offset;
	}

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

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

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

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

		/* more timestamp checking and compensating */
		if ((next_frame->timestamp - frame_time) > MAX_TIMESTAMP_JUMP) {
			source->last_frame_ts =
				next_frame->timestamp - frame_offset;
J
jp9000 已提交
1006
			(*audio_time_refs)++;
1007 1008 1009 1010 1011 1012 1013 1014 1015
		}

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

	return frame;
}

1016
/*
1017 1018
 * Ensures that cached frames are displayed on time.  If multiple frames
 * were cached between renders, then releases the unnecessary frames and uses
1019 1020
 * the frame with the closest timing to ensure sync.  Also ensures that timing
 * with audio is synchronized.
1021
 */
1022
struct source_frame *obs_source_getframe(obs_source_t source)
J
jp9000 已提交
1023
{
1024
	struct source_frame *frame = NULL;
J
jp9000 已提交
1025 1026
	uint64_t last_frame_time = source->last_frame_ts;
	int      audio_time_refs = 0;
1027
	uint64_t sys_time;
1028 1029 1030 1031 1032 1033

	pthread_mutex_lock(&source->video_mutex);

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

1034
	sys_time = os_gettime_ns();
1035

1036 1037
	if (!source->last_frame_ts) {
		frame = source->video_frames.array[0];
1038 1039
		da_erase(source->video_frames, 0);

1040
		source->last_frame_ts = frame->timestamp;
1041
	} else {
J
jp9000 已提交
1042 1043 1044 1045 1046 1047 1048 1049
		frame = get_closest_frame(source, sys_time, &audio_time_refs);
	}

	/* reset timing to current system time */
	if (frame) {
		source->audio_reset_ref += audio_time_refs;
		source->timing_adjust = sys_time - frame->timestamp;
		source->timing_set = true;
1050 1051 1052 1053 1054 1055
	}

	source->last_sys_timestamp = sys_time;

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

1057
	if (frame)
1058 1059
		obs_source_addref(source);

1060
	return frame;
J
jp9000 已提交
1061 1062
}

1063
void obs_source_releaseframe(obs_source_t source, struct source_frame *frame)
J
jp9000 已提交
1064
{
1065 1066 1067 1068
	if (frame) {
		source_frame_destroy(frame);
		obs_source_release(source);
	}
J
jp9000 已提交
1069
}
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081

const char *obs_source_getname(obs_source_t source)
{
	return source->name;
}

void obs_source_setname(obs_source_t source, const char *name)
{
	bfree(source->name);
	source->name = bstrdup(name);
}

1082
void obs_source_gettype(obs_source_t source, enum obs_source_type *type,
1083 1084
		const char **id)
{
1085
	if (type) *type = source->type;
J
jp9000 已提交
1086
	if (id)   *id   = source->info.id;
1087
}
1088 1089

static inline void render_filter_bypass(obs_source_t target, effect_t effect,
J
jp9000 已提交
1090
		uint32_t width, uint32_t height, bool use_matrix)
1091
{
J
jp9000 已提交
1092
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1093
	technique_t tech       = effect_gettechnique(effect, tech_name);
J
jp9000 已提交
1094
	eparam_t    image      = effect_getparambyname(effect, "image");
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
	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 已提交
1107
		uint32_t width, uint32_t height, bool use_matrix)
1108
{
J
jp9000 已提交
1109
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1110
	technique_t tech       = effect_gettechnique(effect, tech_name);
J
jp9000 已提交
1111
	eparam_t    image      = effect_getparambyname(effect, "image");
1112 1113
	size_t      passes, i;

J
jp9000 已提交
1114
	effect_settexture(effect, image, tex);
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124

	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 已提交
1125 1126
void obs_source_process_filter(obs_source_t filter, effect_t effect,
		uint32_t width, uint32_t height, enum gs_color_format format,
1127
		enum allow_direct_render allow_direct)
1128 1129 1130 1131 1132 1133 1134
{
	obs_source_t target       = obs_filter_gettarget(filter);
	obs_source_t parent       = obs_filter_getparent(filter);
	uint32_t     target_flags = obs_source_get_output_flags(target);
	uint32_t     parent_flags = obs_source_get_output_flags(parent);
	int          cx           = obs_source_getwidth(target);
	int          cy           = obs_source_getheight(target);
J
jp9000 已提交
1135 1136
	bool         use_matrix   = !!(target_flags & OBS_SOURCE_COLOR_MATRIX);
	bool         expects_def  = !(parent_flags & OBS_SOURCE_CUSTOM_DRAW);
1137
	bool         can_directly = allow_direct == ALLOW_DIRECT_RENDERING;
1138 1139 1140 1141 1142

	/* 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 */
1143
	if (can_directly && expects_def && target == parent) {
J
jp9000 已提交
1144
		render_filter_bypass(target, effect, width, height, use_matrix);
1145 1146 1147
		return;
	}

J
jp9000 已提交
1148 1149 1150 1151 1152
	if (!filter->filter_texrender)
		filter->filter_texrender = texrender_create(format,
				GS_ZS_NONE);

	if (texrender_begin(filter->filter_texrender, cx, cy)) {
1153
		gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
1154
		if (expects_def && parent == target)
J
jp9000 已提交
1155
			obs_source_default_render(parent, use_matrix);
1156 1157
		else
			obs_source_video_render(target);
J
jp9000 已提交
1158
		texrender_end(filter->filter_texrender);
1159 1160 1161 1162
	}

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

J
jp9000 已提交
1163 1164
	render_filter_tex(texrender_gettexture(filter->filter_texrender),
			effect, width, height, use_matrix);
1165
}
1166 1167 1168 1169 1170 1171 1172 1173 1174 1175

signal_handler_t obs_source_signalhandler(obs_source_t source)
{
	return source->signals;
}

proc_handler_t obs_source_prochandler(obs_source_t source)
{
	return source->procs;
}
J
jp9000 已提交
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185

void obs_source_setvolume(obs_source_t source, float volume)
{
	source->volume = volume;
}

float obs_source_getvolume(obs_source_t source)
{
	return source->volume;
}