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

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
6
    the Free Software Foundation, either version 2 of the License, or
J
jp9000 已提交
7 8 9 10 11 12 13 14 15 16 17
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/

18 19
#include <inttypes.h>

20
#include "media-io/format-conversion.h"
21
#include "media-io/video-frame.h"
22
#include "util/platform.h"
23
#include "callback/calldata.h"
24 25
#include "graphics/matrix3.h"
#include "graphics/vec3.h"
26

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

30 31
static void obs_source_destroy(obs_source_t source);

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

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

	return NULL;
}

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

	switch (type) {
J
jp9000 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65
	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:
66
	default:
J
jp9000 已提交
67
		blog(LOG_ERROR, "get_source_info: invalid source type");
68 69 70 71 72 73
		return NULL;
	}

	return find_source(list, id);
}

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

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

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

91
/* internal initialization */
J
jp9000 已提交
92 93
bool obs_source_init(struct obs_source *source,
		const struct obs_source_info *info)
J
jp9000 已提交
94
{
95
	source->refs = 1;
J
jp9000 已提交
96 97
	source->user_volume = 1.0f;
	source->present_volume = 1.0f;
J
jp9000 已提交
98
	source->sync_offset = 0;
99 100 101
	pthread_mutex_init_value(&source->filter_mutex);
	pthread_mutex_init_value(&source->video_mutex);
	pthread_mutex_init_value(&source->audio_mutex);
102

J
jp9000 已提交
103
	memcpy(&source->info, info, sizeof(struct obs_source_info));
104 105 106 107 108 109 110

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

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

122
	return true;
J
jp9000 已提交
123 124
}

125
static inline void obs_source_dosignal(struct obs_source *source,
126
		const char *signal_obs, const char *signal_source)
127 128 129 130 131
{
	struct calldata data;

	calldata_init(&data);
	calldata_setptr(&data, "source", source);
132 133 134 135
	if (signal_obs)
		signal_handler_signal(obs->signals, signal_obs, &data);
	if (signal_source)
		signal_handler_signal(source->signals, signal_source, &data);
136 137 138
	calldata_free(&data);
}

139
obs_source_t obs_source_create(enum obs_source_type type, const char *id,
140
		const char *name, obs_data_t settings)
J
jp9000 已提交
141 142 143
{
	struct obs_source *source;

J
jp9000 已提交
144
	const struct obs_source_info *info = get_source_info(type, id);
J
jp9000 已提交
145
	if (!info) {
J
jp9000 已提交
146
		blog(LOG_ERROR, "Source '%s' not found", id);
J
jp9000 已提交
147 148 149
		return NULL;
	}

150
	source = bzalloc(sizeof(struct obs_source));
151

152 153 154
	if (!obs_source_init_handlers(source))
		goto fail;

J
jp9000 已提交
155 156 157 158
	source->name     = bstrdup(name);
	source->settings = obs_data_newref(settings);
	source->data     = info->create(source->settings, source);

159 160 161
	if (!source->data)
		goto fail;

J
jp9000 已提交
162
	if (!obs_source_init(source, info))
163
		goto fail;
J
jp9000 已提交
164

165
	obs_source_dosignal(source, "source-create", NULL);
J
jp9000 已提交
166
	return source;
167 168 169 170 171

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

174 175
void source_frame_init(struct source_frame *frame, enum video_format format,
		uint32_t width, uint32_t height)
176
{
177
	struct video_frame vid_frame;
J
jp9000 已提交
178 179 180 181

	if (!frame)
		return;

182
	video_frame_init(&vid_frame, format, width, height);
183 184 185
	frame->format = format;
	frame->width  = width;
	frame->height = height;
186

187 188 189
	for (size_t i = 0; i < MAX_AV_PLANES; i++) {
		frame->data[i]     = vid_frame.data[i];
		frame->linesize[i] = vid_frame.linesize[i];
190 191 192
	}
}

193
static void obs_source_destroy(struct obs_source *source)
J
jp9000 已提交
194
{
195
	size_t i;
196

J
jp9000 已提交
197 198 199
	if (!source)
		return;

200
	obs_source_dosignal(source, "source-destroy", "destroy");
201

202 203
	if (source->filter_parent)
		obs_source_filter_remove(source->filter_parent, source);
204

205 206
	for (i = 0; i < source->filters.num; i++)
		obs_source_release(source->filters.array[i]);
207

208 209
	for (i = 0; i < source->video_frames.num; i++)
		source_frame_destroy(source->video_frames.array[i]);
210

211 212 213
	gs_entercontext(obs->video.graphics);
	texture_destroy(source->output_texture);
	gs_leavecontext();
J
jp9000 已提交
214

215
	if (source->data)
J
jp9000 已提交
216
		source->info.destroy(source->data);
217

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

221 222 223
	audio_line_destroy(source->audio_line);
	audio_resampler_destroy(source->resampler);

224 225 226
	proc_handler_destroy(source->procs);
	signal_handler_destroy(source->signals);

J
jp9000 已提交
227
	texrender_destroy(source->filter_texrender);
228 229 230 231 232
	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);
233
	obs_data_release(source->settings);
234
	bfree(source->name);
235 236 237
	bfree(source);
}

P
Palana 已提交
238
void obs_source_addref(obs_source_t source)
239
{
P
Palana 已提交
240 241
	if (source)
		++source->refs;
242 243
}

P
Palana 已提交
244
void obs_source_release(obs_source_t source)
245
{
P
Palana 已提交
246 247
	if (!source)
		return;
248

P
Palana 已提交
249 250
	if (--source->refs == 0)
		obs_source_destroy(source);
251 252 253 254
}

void obs_source_remove(obs_source_t source)
{
255
	struct obs_core_data *data = &obs->data;
256 257 258 259
	size_t id;

	pthread_mutex_lock(&data->sources_mutex);

J
jp9000 已提交
260 261
	if (!source || source->removed) {
		pthread_mutex_unlock(&data->sources_mutex);
J
jp9000 已提交
262
		return;
J
jp9000 已提交
263
	}
J
jp9000 已提交
264

J
jp9000 已提交
265
	source->removed = true;
J
jp9000 已提交
266

J
jp9000 已提交
267 268 269 270 271 272
	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);
273 274 275
	}

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

277
	obs_source_dosignal(source, "source-remove", "remove");
J
jp9000 已提交
278
	obs_source_release(source);
279 280 281 282
}

bool obs_source_removed(obs_source_t source)
{
J
jp9000 已提交
283
	return source ? source->removed : true;
J
jp9000 已提交
284 285
}

J
jp9000 已提交
286 287 288
obs_properties_t obs_source_properties(enum obs_source_type type,
		const char *id, const char *locale)
{
J
jp9000 已提交
289 290 291
	const struct obs_source_info *info = get_source_info(type, id);
	if (info && info->get_properties)
	       return info->get_properties(locale);
J
jp9000 已提交
292 293 294
	return NULL;
}

295
uint32_t obs_source_get_output_flags(obs_source_t source)
J
jp9000 已提交
296
{
J
jp9000 已提交
297
	return source ? source->info.output_flags : 0;
J
jp9000 已提交
298 299
}

300
void obs_source_update(obs_source_t source, obs_data_t settings)
J
jp9000 已提交
301
{
J
jp9000 已提交
302 303
	if (!source) return;

304
	obs_data_apply(source->settings, settings);
305

J
jp9000 已提交
306 307
	if (source->info.update)
		source->info.update(source->data, source->settings);
J
jp9000 已提交
308 309
}

310
static void activate_source(obs_source_t source)
J
jp9000 已提交
311
{
J
jp9000 已提交
312 313
	if (source->info.activate)
		source->info.activate(source->data);
314
	obs_source_dosignal(source, "source-activate", "activate");
J
jp9000 已提交
315 316
}

317
static void deactivate_source(obs_source_t source)
J
jp9000 已提交
318
{
J
jp9000 已提交
319 320
	if (source->info.deactivate)
		source->info.deactivate(source->data);
321 322
	obs_source_dosignal(source, "source-deactivate", "deactivate");
}
323

324 325 326 327 328 329 330 331 332 333 334 335
static void show_source(obs_source_t source)
{
	if (source->info.show)
		source->info.show(source->data);
	obs_source_dosignal(source, "source-show", "show");
}

static void hide_source(obs_source_t source)
{
	if (source->info.hide)
		source->info.hide(source->data);
	obs_source_dosignal(source, "source-hide", "hide");
336 337 338 339 340 341
}

static void activate_tree(obs_source_t parent, obs_source_t child, void *param)
{
	if (++child->activate_refs == 1)
		activate_source(child);
J
jp9000 已提交
342 343 344

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
345 346 347 348 349 350 351
}

static void deactivate_tree(obs_source_t parent, obs_source_t child,
		void *param)
{
	if (--child->activate_refs == 0)
		deactivate_source(child);
J
jp9000 已提交
352 353 354

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
355 356
}

357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
static void show_tree(obs_source_t parent, obs_source_t child, void *param)
{
	if (++child->show_refs == 1)
		show_source(child);

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

static void hide_tree(obs_source_t parent, obs_source_t child, void *param)
{
	if (--child->show_refs == 0)
		hide_source(child);

	UNUSED_PARAMETER(parent);
	UNUSED_PARAMETER(param);
}

void obs_source_activate(obs_source_t source, enum view_type type)
376 377 378
{
	if (!source) return;

379 380 381 382 383 384 385 386 387 388 389
	if (++source->show_refs == 1) {
		show_source(source);
		obs_source_enum_tree(source, show_tree, NULL);
	}

	if (type == MAIN_VIEW) {
		if (++source->activate_refs == 1) {
			activate_source(source);
			obs_source_enum_tree(source, activate_tree, NULL);
			obs_source_set_present_volume(source, 1.0f);
		}
390 391 392
	}
}

393
void obs_source_deactivate(obs_source_t source, enum view_type type)
394 395 396
{
	if (!source) return;

397 398 399 400 401 402 403 404 405 406 407
	if (--source->show_refs == 0) {
		hide_source(source);
		obs_source_enum_tree(source, hide_tree, NULL);
	}

	if (type == MAIN_VIEW) {
		if (--source->activate_refs == 0) {
			deactivate_source(source);
			obs_source_enum_tree(source, deactivate_tree, NULL);
			obs_source_set_present_volume(source, 0.0f);
		}
408
	}
J
jp9000 已提交
409 410
}

411
void obs_source_video_tick(obs_source_t source, float seconds)
J
jp9000 已提交
412
{
J
jp9000 已提交
413 414
	if (!source) return;

J
jp9000 已提交
415 416 417 418 419 420
	/* 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 已提交
421 422
}

423
/* unless the value is 3+ hours worth of frames, this won't overflow */
J
jp9000 已提交
424
static inline uint64_t conv_frames_to_time(size_t frames)
425
{
J
jp9000 已提交
426 427
	const struct audio_output_info *info;
	info = audio_output_getinfo(obs->audio.audio);
428 429 430

	return (uint64_t)frames * 1000000000ULL /
		(uint64_t)info->samples_per_sec;
431 432
}

433
/* maximum "direct" timestamp variance in nanoseconds */
434
#define MAX_TS_VAR          5000000000ULL
435
/* maximum time that timestamp can jump in nanoseconds */
436 437 438 439
#define MAX_TIMESTAMP_JUMP  2000000000ULL
/* time threshold in nanoseconds to ensure audio timing is as seamless as
 * possible */
#define TS_SMOOTHING_THRESHOLD 70000000ULL
440 441 442 443 444 445

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

447 448
static inline void handle_ts_jump(obs_source_t source, uint64_t expected,
		uint64_t ts, uint64_t diff)
449
{
J
jp9000 已提交
450
	blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%"PRIu64"', "
451 452
	                "expected value %"PRIu64", input value %"PRIu64,
	                source->name, diff, expected, ts);
453 454

	/* if has video, ignore audio data until reset */
J
jp9000 已提交
455
	if (source->info.output_flags & OBS_SOURCE_ASYNC_VIDEO)
456 457 458 459 460
		source->audio_reset_ref--;
	else 
		reset_audio_timing(source, ts);
}

461 462 463 464
static void source_output_audio_line(obs_source_t source,
		const struct audio_data *data)
{
	struct audio_data in = *data;
465
	uint64_t diff;
466 467

	if (!source->timing_set) {
468
		reset_audio_timing(source, in.timestamp);
469 470

		/* detects 'directly' set timestamps as long as they're within
471
		 * a certain threshold */
472
		if ((source->timing_adjust + MAX_TS_VAR) < MAX_TS_VAR * 2)
473
			source->timing_adjust = 0;
474

475
	} else {
476
		bool ts_under = (in.timestamp < source->next_audio_ts_min);
477

478 479 480 481 482
		diff = ts_under ?
			(source->next_audio_ts_min - in.timestamp) :
			(in.timestamp - source->next_audio_ts_min);

		/* smooth audio if lower or within threshold */
483
		if (diff > MAX_TIMESTAMP_JUMP)
484 485 486 487
			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;
488 489
	}

490
	source->next_audio_ts_min = in.timestamp +
J
jp9000 已提交
491
		conv_frames_to_time(in.frames);
492

493 494 495
	if (source->audio_reset_ref != 0)
		return;

J
jp9000 已提交
496
	in.timestamp += source->timing_adjust + source->sync_offset;
J
jp9000 已提交
497 498
	in.volume = source->user_volume * source->present_volume *
		obs->audio.user_volume * obs->audio.present_volume;
499

500 501 502
	audio_line_output(source->audio_line, &in);
}

503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
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;
}

520 521 522 523 524 525 526 527
enum convert_type {
	CONVERT_NONE,
	CONVERT_NV12,
	CONVERT_420,
	CONVERT_422_U,
	CONVERT_422_Y,
};

528
static inline enum convert_type get_convert_type(enum video_format format)
529
{
530
	switch (format) {
531 532 533 534 535 536 537 538 539 540 541
	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;

542
	case VIDEO_FORMAT_NONE:
543 544 545 546 547 548 549 550 551 552 553 554
	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;
555
	uint32_t linesize;
556
	enum convert_type type = get_convert_type(frame->format);
557 558

	if (type == CONVERT_NONE) {
559
		texture_setimage(tex, frame->data[0], frame->linesize[0],
560
				false);
561 562 563
		return true;
	}

564
	if (!texture_map(tex, &ptr, &linesize))
565 566 567
		return false;

	if (type == CONVERT_420)
J
jp9000 已提交
568 569 570
		decompress_420((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
571 572

	else if (type == CONVERT_NV12)
J
jp9000 已提交
573 574 575
		decompress_nv12((const uint8_t* const*)frame->data,
				frame->linesize,
				0, frame->height, ptr, linesize);
576 577

	else if (type == CONVERT_422_Y)
578
		decompress_422(frame->data[0], frame->linesize[0],
J
jp9000 已提交
579
				0, frame->height, ptr, linesize, true);
580 581

	else if (type == CONVERT_422_U)
582
		decompress_422(frame->data[0], frame->linesize[0],
J
jp9000 已提交
583
				0, frame->height, ptr, linesize, false);
584 585 586 587 588

	texture_unmap(tex);
	return true;
}

589 590
static void obs_source_draw_texture(texture_t tex, struct source_frame *frame)
{
591
	effect_t    effect = obs->video.default_effect;
592
	bool        yuv    = format_is_yuv(frame->format);
593
	const char  *type  = yuv ? "DrawMatrix" : "Draw";
594
	technique_t tech;
595
	eparam_t    param;
596

597 598
	if (!upload_frame(tex, frame))
		return;
599 600 601 602 603

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

604
	if (yuv) {
605 606
		param = effect_getparambyname(effect, "color_matrix");
		effect_setval(effect, param, frame->color_matrix,
607 608 609
				sizeof(float) * 16);
	}

J
jp9000 已提交
610
	param = effect_getparambyname(effect, "image");
611 612
	effect_settexture(effect, param, tex);

613
	gs_draw_sprite(tex, frame->flip ? GS_FLIP_V : 0, 0, 0);
614 615 616 617 618

	technique_endpass(tech);
	technique_end(tech);
}

619 620 621 622 623 624
static void obs_source_render_async_video(obs_source_t source)
{
	struct source_frame *frame = obs_source_getframe(source);
	if (!frame)
		return;

J
jp9000 已提交
625
	if (set_texture_size(source, frame))
626
		obs_source_draw_texture(source->output_texture, frame);
627 628 629 630

	obs_source_releaseframe(source, frame);
}

631 632 633 634 635 636 637
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 已提交
638 639
static inline void obs_source_default_render(obs_source_t source,
		bool color_matrix)
640 641
{
	effect_t    effect     = obs->video.default_effect;
J
jp9000 已提交
642
	const char  *tech_name = color_matrix ? "DrawMatrix" : "Draw";
643 644 645 646 647 648
	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 已提交
649
		source->info.video_render(source->data, effect);
650 651 652 653 654 655 656
		technique_endpass(tech);
	}
	technique_end(tech);
}

static inline void obs_source_main_render(obs_source_t source)
{
J
jp9000 已提交
657 658
	uint32_t flags = source->info.output_flags;
	bool color_matrix = (flags & OBS_SOURCE_COLOR_MATRIX) != 0;
659 660
	bool default_effect = !source->filter_parent &&
	                      source->filters.num == 0 &&
J
jp9000 已提交
661
	                      (flags & OBS_SOURCE_CUSTOM_DRAW) == 0;
662 663

	if (default_effect)
J
jp9000 已提交
664
		obs_source_default_render(source, color_matrix);
665
	else
J
jp9000 已提交
666
		source->info.video_render(source->data, NULL);
667 668
}

669
void obs_source_video_render(obs_source_t source)
J
jp9000 已提交
670
{
J
jp9000 已提交
671 672
	if (!source) return;

J
jp9000 已提交
673
	if (source->info.video_render) {
674 675 676 677
		if (source->filters.num && !source->rendering_filter)
			obs_source_render_filters(source);
		else
			obs_source_main_render(source);
678 679 680 681 682 683

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

	} else {
		obs_source_render_async_video(source);
J
jp9000 已提交
684 685 686
	}
}

J
jp9000 已提交
687
uint32_t obs_source_getwidth(obs_source_t source)
J
jp9000 已提交
688
{
J
jp9000 已提交
689
	if (source && source->info.getwidth)
J
jp9000 已提交
690
		return source->info.getwidth(source->data);
J
jp9000 已提交
691 692 693
	return 0;
}

J
jp9000 已提交
694
uint32_t obs_source_getheight(obs_source_t source)
J
jp9000 已提交
695
{
J
jp9000 已提交
696
	if (source && source->info.getheight)
J
jp9000 已提交
697
		return source->info.getheight(source->data);
J
jp9000 已提交
698 699 700
	return 0;
}

701 702
obs_source_t obs_filter_getparent(obs_source_t filter)
{
J
jp9000 已提交
703
	return filter ? filter->filter_parent : NULL;
704 705
}

706
obs_source_t obs_filter_gettarget(obs_source_t filter)
J
jp9000 已提交
707
{
J
jp9000 已提交
708
	return filter ? filter->filter_target : NULL;
J
jp9000 已提交
709 710
}

711
void obs_source_filter_add(obs_source_t source, obs_source_t filter)
J
jp9000 已提交
712
{
J
jp9000 已提交
713 714 715
	if (!source || !filter)
		return;

716 717
	pthread_mutex_lock(&source->filter_mutex);

J
jp9000 已提交
718
	if (da_find(source->filters, &filter, 0) != DARRAY_INVALID) {
J
jp9000 已提交
719 720 721 722 723 724
		blog(LOG_WARNING, "Tried to add a filter that was already "
		                  "present on the source");
		return;
	}

	if (source->filters.num) {
725
		obs_source_t *back = da_end(source->filters);
J
jp9000 已提交
726 727 728 729
		(*back)->filter_target = filter;
	}

	da_push_back(source->filters, &filter);
730 731 732 733

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = source;
J
jp9000 已提交
734 735 736
	filter->filter_target = source;
}

737
void obs_source_filter_remove(obs_source_t source, obs_source_t filter)
J
jp9000 已提交
738
{
739 740
	size_t idx;

J
jp9000 已提交
741 742 743
	if (!source || !filter)
		return;

744 745 746
	pthread_mutex_lock(&source->filter_mutex);

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
747
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
748 749 750
		return;

	if (idx > 0) {
751
		obs_source_t prev = source->filters.array[idx-1];
J
jp9000 已提交
752 753 754 755
		prev->filter_target = filter->filter_target;
	}

	da_erase(source->filters, idx);
756 757 758 759

	pthread_mutex_unlock(&source->filter_mutex);

	filter->filter_parent = NULL;
J
jp9000 已提交
760 761 762
	filter->filter_target = NULL;
}

763
void obs_source_filter_setorder(obs_source_t source, obs_source_t filter,
J
jp9000 已提交
764 765
		enum order_movement movement)
{
J
jp9000 已提交
766 767 768 769 770 771
	size_t idx, i;

	if (!source || !filter)
		return;

	idx = da_find(source->filters, &filter, 0);
J
jp9000 已提交
772
	if (idx == DARRAY_INVALID)
J
jp9000 已提交
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
		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);
	}

796
	/* reorder filter targets, not the nicest way of dealing with things */
J
jp9000 已提交
797
	for (i = 0; i < source->filters.num; i++) {
798
		obs_source_t next_filter = (i == source->filters.num-1) ?
J
jp9000 已提交
799 800 801 802 803
			source : source->filters.array[idx+1];
		source->filters.array[i]->filter_target = next_filter;
	}
}

804
obs_data_t obs_source_getsettings(obs_source_t source)
J
jp9000 已提交
805
{
J
jp9000 已提交
806 807
	if (!source) return NULL;

808 809
	obs_data_addref(source->settings);
	return source->settings;
J
jp9000 已提交
810 811
}

812 813
static inline struct source_frame *filter_async_video(obs_source_t source,
		struct source_frame *in)
814 815 816 817
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
J
jp9000 已提交
818 819
		if (filter->info.filter_video) {
			in = filter->info.filter_video(filter->data, in);
820 821 822 823 824 825 826 827
			if (!in)
				return NULL;
		}
	}

	return in;
}

828 829 830
static inline void copy_frame_data_line(struct source_frame *dst,
		const struct source_frame *src, uint32_t plane, uint32_t y)
{
831 832 833 834
	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];
835 836 837 838 839 840 841

	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)
{
842
	if (dst->linesize[plane] != src->linesize[plane])
843 844 845 846
		for (uint32_t y = 0; y < lines; y++)
			copy_frame_data_line(dst, src, plane, y);
	else
		memcpy(dst->data[plane], src->data[plane],
847
				dst->linesize[plane] * lines);
848 849 850 851 852 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 878 879
}

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

J
jp9000 已提交
880
static inline struct source_frame *cache_video(const struct source_frame *frame)
881
{
882
	/* TODO: use an actual cache */
883
	struct source_frame *new_frame = source_frame_create(frame->format,
884
			frame->width, frame->height);
885

886
	copy_frame_data(new_frame, frame);
887
	return new_frame;
888 889 890
}

void obs_source_output_video(obs_source_t source,
891
		const struct source_frame *frame)
892
{
J
jp9000 已提交
893 894 895
	if (!source || !frame)
		return;

J
jp9000 已提交
896
	struct source_frame *output = cache_video(frame);
897 898 899 900 901

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

902 903 904 905 906
	if (output) {
		pthread_mutex_lock(&source->video_mutex);
		da_push_back(source->video_frames, &output);
		pthread_mutex_unlock(&source->video_mutex);
	}
907 908
}

909 910
static inline struct filtered_audio *filter_async_audio(obs_source_t source,
		struct filtered_audio *in)
911 912 913 914
{
	size_t i;
	for (i = source->filters.num; i > 0; i--) {
		struct obs_source *filter = source->filters.array[i-1];
J
jp9000 已提交
915 916
		if (filter->info.filter_audio) {
			in = filter->info.filter_audio(filter->data, in);
917 918 919 920 921 922 923 924
			if (!in)
				return NULL;
		}
	}

	return in;
}

925
static inline void reset_resampler(obs_source_t source,
926 927
		const struct source_audio *audio)
{
J
jp9000 已提交
928
	const struct audio_output_info *obs_info;
929 930
	struct resample_info output_info;

931 932
	obs_info = audio_output_getinfo(obs->audio.audio);

933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
	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 已提交
958
		const uint8_t *const data[], uint32_t frames, uint64_t ts)
959
{
960
	size_t planes    = audio_output_planes(obs->audio.audio);
961
	size_t blocksize = audio_output_blocksize(obs->audio.audio);
962 963
	size_t size      = (size_t)frames * blocksize;
	bool   resize    = source->audio_storage_size < size;
964

J
jp9000 已提交
965 966
	source->audio_data.frames    = frames;
	source->audio_data.timestamp = ts;
967 968 969 970 971 972 973 974 975 976 977 978 979

	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;
980 981 982 983 984 985 986 987 988 989 990 991 992 993
}

/* resamples/remixes new audio to the designated main audio output format */
static void process_audio(obs_source_t source, const struct source_audio *audio)
{
	if (source->sample_info.samples_per_sec != audio->samples_per_sec ||
	    source->sample_info.format          != audio->format          ||
	    source->sample_info.speakers        != audio->speakers)
		reset_resampler(source, audio);

	if (source->audio_failed)
		return;

	if (source->resampler) {
J
jp9000 已提交
994
		uint8_t  *output[MAX_AV_PLANES];
995 996 997
		uint32_t frames;
		uint64_t offset;

998 999 1000 1001 1002
		memset(output, 0, sizeof(output));

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

J
jp9000 已提交
1004
		copy_audio_data(source, (const uint8_t *const *)output, frames,
1005 1006 1007 1008 1009
				audio->timestamp - offset);
	} else {
		copy_audio_data(source, audio->data, audio->frames,
				audio->timestamp);
	}
1010 1011 1012 1013 1014
}

void obs_source_output_audio(obs_source_t source,
		const struct source_audio *audio)
{
J
jp9000 已提交
1015
	uint32_t flags;
1016
	struct filtered_audio *output;
1017

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

	flags = source->info.output_flags;
1022
	process_audio(source, audio);
1023 1024

	pthread_mutex_lock(&source->filter_mutex);
1025
	output = filter_async_audio(source, &source->audio_data);
1026 1027

	if (output) {
1028
		bool async = (flags & OBS_SOURCE_ASYNC_VIDEO) != 0;
J
jp9000 已提交
1029

1030 1031
		pthread_mutex_lock(&source->audio_mutex);

1032 1033
		/* wait for video to start before outputting any audio so we
		 * have a base for sync */
1034
		if (source->timing_set || !async) {
1035
			struct audio_data data;
1036

J
jp9000 已提交
1037
			for (int i = 0; i < MAX_AV_PLANES; i++)
1038 1039
				data.data[i] = output->data[i];

1040 1041 1042
			data.frames    = output->frames;
			data.timestamp = output->timestamp;
			source_output_audio_line(source, &data);
1043 1044 1045 1046 1047 1048 1049 1050
		}

		pthread_mutex_unlock(&source->audio_mutex);
	}

	pthread_mutex_unlock(&source->filter_mutex);
}

1051 1052 1053 1054 1055 1056
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 已提交
1057
		uint64_t sys_time, int *audio_time_refs)
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
{
	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 已提交
1068
		(*audio_time_refs)++;
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088
	} 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 已提交
1089
			(*audio_time_refs)++;
1090 1091 1092 1093 1094 1095 1096 1097 1098
		}

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

	return frame;
}

1099
/*
1100 1101
 * Ensures that cached frames are displayed on time.  If multiple frames
 * were cached between renders, then releases the unnecessary frames and uses
1102 1103
 * the frame with the closest timing to ensure sync.  Also ensures that timing
 * with audio is synchronized.
1104
 */
1105
struct source_frame *obs_source_getframe(obs_source_t source)
J
jp9000 已提交
1106
{
1107
	struct source_frame *frame = NULL;
J
jp9000 已提交
1108
	int      audio_time_refs = 0;
1109
	uint64_t sys_time;
1110

J
jp9000 已提交
1111 1112 1113
	if (!source)
		return NULL;

1114 1115 1116 1117 1118
	pthread_mutex_lock(&source->video_mutex);

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

1119
	sys_time = os_gettime_ns();
1120

1121 1122
	if (!source->last_frame_ts) {
		frame = source->video_frames.array[0];
1123 1124
		da_erase(source->video_frames, 0);

1125
		source->last_frame_ts = frame->timestamp;
1126
	} else {
J
jp9000 已提交
1127 1128 1129 1130 1131 1132 1133 1134
		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;
1135 1136 1137 1138 1139 1140
	}

	source->last_sys_timestamp = sys_time;

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

1142
	if (frame)
1143 1144
		obs_source_addref(source);

1145
	return frame;
J
jp9000 已提交
1146 1147
}

1148
void obs_source_releaseframe(obs_source_t source, struct source_frame *frame)
J
jp9000 已提交
1149
{
J
jp9000 已提交
1150
	if (source && frame) {
1151 1152 1153
		source_frame_destroy(frame);
		obs_source_release(source);
	}
J
jp9000 已提交
1154
}
1155 1156 1157

const char *obs_source_getname(obs_source_t source)
{
J
jp9000 已提交
1158
	return source ? source->name : NULL;
1159 1160 1161 1162
}

void obs_source_setname(obs_source_t source, const char *name)
{
J
jp9000 已提交
1163 1164
	if (!source) return;

1165 1166 1167 1168
	bfree(source->name);
	source->name = bstrdup(name);
}

1169
void obs_source_gettype(obs_source_t source, enum obs_source_type *type,
1170 1171
		const char **id)
{
J
jp9000 已提交
1172 1173
	if (!source) return;

J
jp9000 已提交
1174
	if (type) *type = source->info.type;
J
jp9000 已提交
1175
	if (id)   *id   = source->info.id;
1176
}
1177 1178

static inline void render_filter_bypass(obs_source_t target, effect_t effect,
J
jp9000 已提交
1179
		bool use_matrix)
1180
{
J
jp9000 已提交
1181
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
	technique_t tech       = effect_gettechnique(effect, tech_name);
	size_t      passes, i;

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

static inline void render_filter_tex(texture_t tex, effect_t effect,
J
jp9000 已提交
1195
		uint32_t width, uint32_t height, bool use_matrix)
1196
{
J
jp9000 已提交
1197
	const char  *tech_name = use_matrix ? "DrawMatrix" : "Draw";
1198
	technique_t tech       = effect_gettechnique(effect, tech_name);
J
jp9000 已提交
1199
	eparam_t    image      = effect_getparambyname(effect, "image");
1200 1201
	size_t      passes, i;

J
jp9000 已提交
1202
	effect_settexture(effect, image, tex);
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212

	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 已提交
1213 1214
void obs_source_process_filter(obs_source_t filter, effect_t effect,
		uint32_t width, uint32_t height, enum gs_color_format format,
1215
		enum allow_direct_render allow_direct)
1216
{
J
jp9000 已提交
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232
	obs_source_t target, parent;
	uint32_t     target_flags, parent_flags;
	int          cx, cy;
	bool         use_matrix, expects_def, can_directly;

	if (!filter) return;

	target       = obs_filter_gettarget(filter);
	parent       = obs_filter_getparent(filter);
	target_flags = target->info.output_flags;
	parent_flags = parent->info.output_flags;
	cx           = obs_source_getwidth(target);
	cy           = obs_source_getheight(target);
	use_matrix   = !!(target_flags & OBS_SOURCE_COLOR_MATRIX);
	expects_def  = !(parent_flags & OBS_SOURCE_CUSTOM_DRAW);
	can_directly = allow_direct == ALLOW_DIRECT_RENDERING;
1233 1234 1235 1236 1237

	/* 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 */
1238
	if (can_directly && expects_def && target == parent) {
J
jp9000 已提交
1239
		render_filter_bypass(target, effect, use_matrix);
1240 1241 1242
		return;
	}

J
jp9000 已提交
1243 1244 1245 1246 1247
	if (!filter->filter_texrender)
		filter->filter_texrender = texrender_create(format,
				GS_ZS_NONE);

	if (texrender_begin(filter->filter_texrender, cx, cy)) {
1248
		gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
1249
		if (expects_def && parent == target)
J
jp9000 已提交
1250
			obs_source_default_render(parent, use_matrix);
1251 1252
		else
			obs_source_video_render(target);
J
jp9000 已提交
1253
		texrender_end(filter->filter_texrender);
1254 1255 1256 1257
	}

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

J
jp9000 已提交
1258 1259
	render_filter_tex(texrender_gettexture(filter->filter_texrender),
			effect, width, height, use_matrix);
1260
}
1261 1262 1263

signal_handler_t obs_source_signalhandler(obs_source_t source)
{
J
jp9000 已提交
1264
	return source ? source->signals : NULL;
1265 1266 1267 1268
}

proc_handler_t obs_source_prochandler(obs_source_t source)
{
J
jp9000 已提交
1269
	return source ? source->procs : NULL;
1270
}
J
jp9000 已提交
1271 1272 1273

void obs_source_setvolume(obs_source_t source, float volume)
{
J
jp9000 已提交
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
	if (source) {
		struct calldata data = {0};
		calldata_setptr(&data, "source", source);
		calldata_setfloat(&data, "volume", volume);

		signal_handler_signal(source->signals, "volume", &data);
		signal_handler_signal(obs->signals, "source-volume", &data);

		volume = calldata_float(&data, "volume");
		calldata_free(&data);

J
jp9000 已提交
1285
		source->user_volume = volume;
J
jp9000 已提交
1286
	}
J
jp9000 已提交
1287 1288
}

J
jp9000 已提交
1289 1290 1291 1292 1293 1294 1295 1296 1297
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 已提交
1298 1299
void obs_source_set_present_volume(obs_source_t source, float volume)
{
J
jp9000 已提交
1300
	if (source) {
J
jp9000 已提交
1301
		source->present_volume = volume;
J
jp9000 已提交
1302 1303 1304 1305 1306 1307 1308 1309

		/* 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 已提交
1310 1311 1312 1313
}

float obs_source_getvolume(obs_source_t source)
{
J
jp9000 已提交
1314
	return source ? source->user_volume : 0.0f;
J
jp9000 已提交
1315 1316 1317 1318
}

float obs_source_get_present_volume(obs_source_t source)
{
J
jp9000 已提交
1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330
	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 已提交
1331
}
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388

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) {
		child->enum_refs++;

		child->info.enum_sources(child->data,
				enum_source_tree_callback, data);

		child->enum_refs--;
	}

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

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

	obs_source_addref(source);

	source->enum_refs++;
	source->info.enum_sources(source->data, enum_callback, param);
	source->enum_refs--;

	obs_source_release(source);
}

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

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

	obs_source_addref(source);

	source->enum_refs++;
	source->info.enum_sources(source->data, enum_source_tree_callback,
			&data);
	source->enum_refs--;

	obs_source_release(source);
}
1389 1390 1391 1392 1393

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

1394 1395 1396 1397 1398
	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);
	}
1399 1400 1401 1402 1403 1404
}

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

1405 1406 1407 1408 1409
	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);
	}
1410
}
J
jp9000 已提交
1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457

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