obs.c 43.5 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 "graphics/matrix4.h"
21 22
#include "callback/calldata.h"

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

26
struct obs_core *obs = NULL;
27

J
jp9000 已提交
28
extern void add_default_module_paths(void);
J
jp9000 已提交
29 30
extern char *find_libobs_data_file(const char *file);

J
jp9000 已提交
31
static inline void make_video_info(struct video_output_info *vi,
32 33 34
		struct obs_video_info *ovi)
{
	vi->name    = "video";
35
	vi->format  = ovi->output_format;
36 37 38 39
	vi->fps_num = ovi->fps_num;
	vi->fps_den = ovi->fps_den;
	vi->width   = ovi->output_width;
	vi->height  = ovi->output_height;
40 41
	vi->range   = ovi->range;
	vi->colorspace = ovi->colorspace;
42
	vi->cache_size = 6;
43 44
}

J
jp9000 已提交
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
#define PIXEL_SIZE 4

#define GET_ALIGN(val, align) \
	(((val) + (align-1)) & ~(align-1))

static inline void set_420p_sizes(const struct obs_video_info *ovi)
{
	struct obs_core_video *video = &obs->video;
	uint32_t chroma_pixels;
	uint32_t total_bytes;

	chroma_pixels = (ovi->output_width * ovi->output_height / 4);
	chroma_pixels = GET_ALIGN(chroma_pixels, PIXEL_SIZE);

	video->plane_offsets[0] = 0;
	video->plane_offsets[1] = ovi->output_width * ovi->output_height;
	video->plane_offsets[2] = video->plane_offsets[1] + chroma_pixels;

	video->plane_linewidth[0] = ovi->output_width;
	video->plane_linewidth[1] = ovi->output_width/2;
	video->plane_linewidth[2] = ovi->output_width/2;

	video->plane_sizes[0] = video->plane_offsets[1];
	video->plane_sizes[1] = video->plane_sizes[0]/4;
	video->plane_sizes[2] = video->plane_sizes[1];

	total_bytes = video->plane_offsets[2] + chroma_pixels;

	video->conversion_height =
		(total_bytes/PIXEL_SIZE + ovi->output_width-1) /
		ovi->output_width;

	video->conversion_height = GET_ALIGN(video->conversion_height, 2);
	video->conversion_tech = "Planar420";
}

P
Palana 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
static inline void set_nv12_sizes(const struct obs_video_info *ovi)
{
	struct obs_core_video *video = &obs->video;
	uint32_t chroma_pixels;
	uint32_t total_bytes;

	chroma_pixels = (ovi->output_width * ovi->output_height / 2);
	chroma_pixels = GET_ALIGN(chroma_pixels, PIXEL_SIZE);

	video->plane_offsets[0] = 0;
	video->plane_offsets[1] = ovi->output_width * ovi->output_height;

	video->plane_linewidth[0] = ovi->output_width;
	video->plane_linewidth[1] = ovi->output_width;

	video->plane_sizes[0] = video->plane_offsets[1];
	video->plane_sizes[1] = video->plane_sizes[0]/2;

	total_bytes = video->plane_offsets[1] + chroma_pixels;

	video->conversion_height =
		(total_bytes/PIXEL_SIZE + ovi->output_width-1) /
		ovi->output_width;

	video->conversion_height = GET_ALIGN(video->conversion_height, 2);
	video->conversion_tech = "NV12";
}

J
jp9000 已提交
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
static inline void set_444p_sizes(const struct obs_video_info *ovi)
{
	struct obs_core_video *video = &obs->video;
	uint32_t chroma_pixels;
	uint32_t total_bytes;

	chroma_pixels = (ovi->output_width * ovi->output_height);
	chroma_pixels = GET_ALIGN(chroma_pixels, PIXEL_SIZE);

	video->plane_offsets[0] = 0;
	video->plane_offsets[1] = chroma_pixels;
	video->plane_offsets[2] = chroma_pixels + chroma_pixels;

	video->plane_linewidth[0] = ovi->output_width;
	video->plane_linewidth[1] = ovi->output_width;
	video->plane_linewidth[2] = ovi->output_width;

	video->plane_sizes[0] = chroma_pixels;
	video->plane_sizes[1] = chroma_pixels;
	video->plane_sizes[2] = chroma_pixels;

	total_bytes = video->plane_offsets[2] + chroma_pixels;

	video->conversion_height =
		(total_bytes/PIXEL_SIZE + ovi->output_width-1) /
		ovi->output_width;

	video->conversion_height = GET_ALIGN(video->conversion_height, 2);
	video->conversion_tech = "Planar444";
}

J
jp9000 已提交
140 141 142 143 144 145 146 147 148 149 150
static inline void calc_gpu_conversion_sizes(const struct obs_video_info *ovi)
{
	obs->video.conversion_height = 0;
	memset(obs->video.plane_offsets, 0, sizeof(obs->video.plane_offsets));
	memset(obs->video.plane_sizes, 0, sizeof(obs->video.plane_sizes));
	memset(obs->video.plane_linewidth, 0,
		sizeof(obs->video.plane_linewidth));

	switch ((uint32_t)ovi->output_format) {
	case VIDEO_FORMAT_I420:
		set_420p_sizes(ovi);
P
Palana 已提交
151 152 153 154
		break;
	case VIDEO_FORMAT_NV12:
		set_nv12_sizes(ovi);
		break;
J
jp9000 已提交
155 156 157
	case VIDEO_FORMAT_I444:
		set_444p_sizes(ovi);
		break;
J
jp9000 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
	}
}

static bool obs_init_gpu_conversion(struct obs_video_info *ovi)
{
	struct obs_core_video *video = &obs->video;

	calc_gpu_conversion_sizes(ovi);

	if (!video->conversion_height) {
		blog(LOG_INFO, "GPU conversion not available for format: %u",
				(unsigned int)ovi->output_format);
		video->gpu_conversion = false;
		return true;
	}

	for (size_t i = 0; i < NUM_TEXTURES; i++) {
175
		video->convert_textures[i] = gs_texture_create(
J
jp9000 已提交
176
				ovi->output_width, video->conversion_height,
177
				GS_RGBA, 1, NULL, GS_RENDER_TARGET);
J
jp9000 已提交
178 179 180 181 182 183 184 185

		if (!video->convert_textures[i])
			return false;
	}

	return true;
}

186 187
static bool obs_init_textures(struct obs_video_info *ovi)
{
188
	struct obs_core_video *video = &obs->video;
J
jp9000 已提交
189 190
	uint32_t output_height = video->gpu_conversion ?
		video->conversion_height : ovi->output_height;
191 192 193
	size_t i;

	for (i = 0; i < NUM_TEXTURES; i++) {
194
		video->copy_surfaces[i] = gs_stagesurface_create(
J
jp9000 已提交
195
				ovi->output_width, output_height, GS_RGBA);
196 197 198 199

		if (!video->copy_surfaces[i])
			return false;

200
		video->render_textures[i] = gs_texture_create(
201
				ovi->base_width, ovi->base_height,
202
				GS_RGBA, 1, NULL, GS_RENDER_TARGET);
203 204 205 206

		if (!video->render_textures[i])
			return false;

207
		video->output_textures[i] = gs_texture_create(
208
				ovi->output_width, ovi->output_height,
209
				GS_RGBA, 1, NULL, GS_RENDER_TARGET);
210 211 212 213 214 215 216 217

		if (!video->output_textures[i])
			return false;
	}

	return true;
}

218
static int obs_init_graphics(struct obs_video_info *ovi)
219
{
220
	struct obs_core_video *video = &obs->video;
221 222
	uint8_t transparent_tex_data[2*2*4] = {0};
	const uint8_t *transparent_tex = transparent_tex_data;
223
	bool success = true;
224
	int errorcode;
J
jp9000 已提交
225

226
	errorcode = gs_create(&video->graphics, ovi->graphics_module,
227
			ovi->adapter);
J
jp9000 已提交
228
	if (errorcode != GS_SUCCESS) {
229 230 231 232 233 234 235 236
		switch (errorcode) {
		case GS_ERROR_MODULE_NOT_FOUND:
			return OBS_VIDEO_MODULE_NOT_FOUND;
		case GS_ERROR_NOT_SUPPORTED:
			return OBS_VIDEO_NOT_SUPPORTED;
		default:
			return OBS_VIDEO_FAIL;
		}
J
jp9000 已提交
237 238
	}

239
	gs_enter_context(video->graphics);
J
jp9000 已提交
240

241
	char *filename = find_libobs_data_file("default.effect");
242
	video->default_effect = gs_effect_create_from_file(filename,
243 244 245
			NULL);
	bfree(filename);

246 247 248 249 250 251
	if (gs_get_device_type() == GS_DEVICE_OPENGL) {
		filename = find_libobs_data_file("default_rect.effect");
		video->default_rect_effect = gs_effect_create_from_file(
				filename, NULL);
		bfree(filename);
	}
P
Palana 已提交
252

253 254 255 256 257
	filename = find_libobs_data_file("opaque.effect");
	video->opaque_effect = gs_effect_create_from_file(filename,
			NULL);
	bfree(filename);

258
	filename = find_libobs_data_file("solid.effect");
259
	video->solid_effect = gs_effect_create_from_file(filename,
260 261 262 263
			NULL);
	bfree(filename);

	filename = find_libobs_data_file("format_conversion.effect");
264
	video->conversion_effect = gs_effect_create_from_file(filename,
265 266 267
			NULL);
	bfree(filename);

268 269 270 271 272 273 274 275 276 277
	filename = find_libobs_data_file("bicubic_scale.effect");
	video->bicubic_effect = gs_effect_create_from_file(filename,
			NULL);
	bfree(filename);

	filename = find_libobs_data_file("lanczos_scale.effect");
	video->lanczos_effect = gs_effect_create_from_file(filename,
			NULL);
	bfree(filename);

278 279 280 281 282
	filename = find_libobs_data_file("bilinear_lowres_scale.effect");
	video->bilinear_lowres_effect = gs_effect_create_from_file(filename,
			NULL);
	bfree(filename);

283 284 285
	obs->video.transparent_texture = gs_texture_create(2, 2, GS_RGBA, 1,
			&transparent_tex, 0);

286 287
	if (!video->default_effect)
		success = false;
288 289 290 291
	if (gs_get_device_type() == GS_DEVICE_OPENGL) {
		if (!video->default_rect_effect)
			success = false;
	}
292 293
	if (!video->opaque_effect)
		success = false;
294 295 296 297
	if (!video->solid_effect)
		success = false;
	if (!video->conversion_effect)
		success = false;
298 299
	if (!video->transparent_texture)
		success = false;
J
jp9000 已提交
300

301
	gs_leave_context();
302
	return success ? OBS_VIDEO_SUCCESS : OBS_VIDEO_FAIL;
J
jp9000 已提交
303 304
}

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
static inline void set_video_matrix(struct obs_core_video *video,
		struct obs_video_info *ovi)
{
	struct matrix4 mat;
	struct vec4 r_row;

	if (format_is_yuv(ovi->output_format)) {
		video_format_get_parameters(ovi->colorspace, ovi->range,
				(float*)&mat, NULL, NULL);
		matrix4_inv(&mat, &mat);

		/* swap R and G */
		r_row = mat.x;
		mat.x = mat.y;
		mat.y = r_row;
	} else {
		matrix4_identity(&mat);
	}

	memcpy(video->color_matrix, &mat, sizeof(float) * 16);
}

327
static int obs_init_video(struct obs_video_info *ovi)
J
jp9000 已提交
328
{
329
	struct obs_core_video *video = &obs->video;
J
jp9000 已提交
330
	struct video_output_info vi;
331 332 333
	int errorcode;

	make_video_info(&vi, ovi);
334 335 336 337 338
	video->base_width     = ovi->base_width;
	video->base_height    = ovi->base_height;
	video->output_width   = ovi->output_width;
	video->output_height  = ovi->output_height;
	video->gpu_conversion = ovi->gpu_conversion;
339
	video->scale_type     = ovi->scale_type;
340

341 342
	set_video_matrix(video, ovi);

J
jp9000 已提交
343
	errorcode = video_output_open(&video->video, &vi);
344 345

	if (errorcode != VIDEO_OUTPUT_SUCCESS) {
346
		if (errorcode == VIDEO_OUTPUT_INVALIDPARAM) {
347
			blog(LOG_ERROR, "Invalid video parameters specified");
348 349
			return OBS_VIDEO_INVALID_PARAM;
		} else {
350
			blog(LOG_ERROR, "Could not open video output");
351 352
		}
		return OBS_VIDEO_FAIL;
353 354
	}

355
	gs_enter_context(video->graphics);
356 357

	if (ovi->gpu_conversion && !obs_init_gpu_conversion(ovi))
358
		return OBS_VIDEO_FAIL;
359
	if (!obs_init_textures(ovi))
360
		return OBS_VIDEO_FAIL;
361

362
	gs_leave_context();
363

364 365 366
	errorcode = pthread_create(&video->video_thread, NULL,
			obs_video_thread, obs);
	if (errorcode != 0)
367
		return OBS_VIDEO_FAIL;
J
jp9000 已提交
368

369
	video->thread_initialized = true;
370
	return OBS_VIDEO_SUCCESS;
J
jp9000 已提交
371 372
}

373
static void stop_video(void)
J
jp9000 已提交
374
{
375
	struct obs_core_video *video = &obs->video;
376
	void *thread_retval;
J
jp9000 已提交
377

378 379
	if (video->video) {
		video_output_stop(video->video);
380
		if (video->thread_initialized) {
381
			pthread_join(video->video_thread, &thread_retval);
382 383
			video->thread_initialized = false;
		}
384
	}
385

386 387 388 389 390 391 392
}

static void obs_free_video(void)
{
	struct obs_core_video *video = &obs->video;

	if (video->video) {
393
		video_output_close(video->video);
394 395
		video->video = NULL;

396 397
		if (!video->graphics)
			return;
398

399
		gs_enter_context(video->graphics);
400

401
		if (video->mapped_surface) {
402
			gs_stagesurface_unmap(video->mapped_surface);
403 404
			video->mapped_surface = NULL;
		}
405

406
		for (size_t i = 0; i < NUM_TEXTURES; i++) {
407 408 409 410
			gs_stagesurface_destroy(video->copy_surfaces[i]);
			gs_texture_destroy(video->render_textures[i]);
			gs_texture_destroy(video->convert_textures[i]);
			gs_texture_destroy(video->output_textures[i]);
411

J
jp9000 已提交
412 413 414 415
			video->copy_surfaces[i]    = NULL;
			video->render_textures[i]  = NULL;
			video->convert_textures[i] = NULL;
			video->output_textures[i]  = NULL;
416
		}
417

418
		gs_leave_context();
419

420
		circlebuf_free(&video->vframe_info_buffer);
421

422 423 424 425 426 427 428 429 430
		memset(&video->textures_rendered, 0,
				sizeof(video->textures_rendered));
		memset(&video->textures_output, 0,
				sizeof(video->textures_output));
		memset(&video->textures_copied, 0,
				sizeof(video->textures_copied));
		memset(&video->textures_converted, 0,
				sizeof(video->textures_converted));

431 432 433 434 435 436 437 438 439
		video->cur_texture = 0;
	}
}

static void obs_free_graphics(void)
{
	struct obs_core_video *video = &obs->video;

	if (video->graphics) {
440
		gs_enter_context(video->graphics);
441

442 443
		gs_texture_destroy(video->transparent_texture);

444
		gs_effect_destroy(video->default_effect);
P
Palana 已提交
445
		gs_effect_destroy(video->default_rect_effect);
446
		gs_effect_destroy(video->opaque_effect);
447 448
		gs_effect_destroy(video->solid_effect);
		gs_effect_destroy(video->conversion_effect);
449 450
		gs_effect_destroy(video->bicubic_effect);
		gs_effect_destroy(video->lanczos_effect);
451
		gs_effect_destroy(video->bilinear_lowres_effect);
452
		video->default_effect = NULL;
453

454
		gs_leave_context();
455 456

		gs_destroy(video->graphics);
457
		video->graphics = NULL;
458
	}
J
jp9000 已提交
459 460
}

J
jp9000 已提交
461
static bool obs_init_audio(struct audio_output_info *ai)
J
jp9000 已提交
462
{
463
	struct obs_core_audio *audio = &obs->audio;
464
	int errorcode;
J
jp9000 已提交
465

466
	/* TODO: sound subsystem */
J
jp9000 已提交
467

J
jp9000 已提交
468 469 470
	audio->user_volume    = 1.0f;
	audio->present_volume = 1.0f;

J
jp9000 已提交
471
	errorcode = audio_output_open(&audio->audio, ai);
472 473
	if (errorcode == AUDIO_OUTPUT_SUCCESS)
		return true;
P
Palana 已提交
474
	else if (errorcode == AUDIO_OUTPUT_INVALIDPARAM)
475 476 477 478 479 480 481 482 483
		blog(LOG_ERROR, "Invalid audio parameters specified");
	else
		blog(LOG_ERROR, "Could not open audio output");

	return false;
}

static void obs_free_audio(void)
{
484
	struct obs_core_audio *audio = &obs->audio;
485 486 487
	if (audio->audio)
		audio_output_close(audio->audio);

488
	memset(audio, 0, sizeof(struct obs_core_audio));
489 490 491 492
}

static bool obs_init_data(void)
{
493
	struct obs_core_data *data = &obs->data;
494
	pthread_mutexattr_t attr;
495

496 497
	assert(data != NULL);

498 499
	pthread_mutex_init_value(&obs->data.displays_mutex);

500
	if (pthread_mutexattr_init(&attr) != 0)
501
		return false;
502 503 504 505
	if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
		goto fail;
	if (pthread_mutex_init(&data->sources_mutex, &attr) != 0)
		goto fail;
506 507
	if (pthread_mutex_init(&data->audio_sources_mutex, &attr) != 0)
		goto fail;
508 509
	if (pthread_mutex_init(&data->displays_mutex, &attr) != 0)
		goto fail;
510 511 512 513
	if (pthread_mutex_init(&data->outputs_mutex, &attr) != 0)
		goto fail;
	if (pthread_mutex_init(&data->encoders_mutex, &attr) != 0)
		goto fail;
514 515
	if (pthread_mutex_init(&data->services_mutex, &attr) != 0)
		goto fail;
J
jp9000 已提交
516
	if (!obs_view_init(&data->main_view))
517
		goto fail;
J
jp9000 已提交
518

519
	data->valid = true;
520 521 522

fail:
	pthread_mutexattr_destroy(&attr);
523
	return data->valid;
J
jp9000 已提交
524 525
}

526 527 528 529 530 531 532 533 534 535 536
void obs_main_view_free(struct obs_view *view)
{
	if (!view) return;

	for (size_t i = 0; i < MAX_CHANNELS; i++)
		obs_source_release(view->channels[i]);

	memset(view->channels, 0, sizeof(view->channels));
	pthread_mutex_destroy(&view->channels_mutex);
}

537 538 539 540 541 542 543 544 545 546 547 548
#define FREE_OBS_LINKED_LIST(type) \
	do { \
		int unfreed = 0; \
		while (data->first_ ## type ) { \
			obs_ ## type ## _destroy(data->first_ ## type ); \
			unfreed++; \
		} \
		if (unfreed) \
			blog(LOG_INFO, "\t%d " #type "(s) were remaining", \
					unfreed); \
	} while (false)

549
static void obs_free_data(void)
J
jp9000 已提交
550
{
551
	struct obs_core_data *data = &obs->data;
J
jp9000 已提交
552

553 554
	data->valid = false;

555
	obs_main_view_free(&data->main_view);
556

557
	blog(LOG_INFO, "Freeing OBS context data");
558

559 560 561 562 563 564
	FREE_OBS_LINKED_LIST(source);
	FREE_OBS_LINKED_LIST(output);
	FREE_OBS_LINKED_LIST(encoder);
	FREE_OBS_LINKED_LIST(display);
	FREE_OBS_LINKED_LIST(service);

565
	pthread_mutex_destroy(&data->sources_mutex);
566
	pthread_mutex_destroy(&data->audio_sources_mutex);
567 568 569
	pthread_mutex_destroy(&data->displays_mutex);
	pthread_mutex_destroy(&data->outputs_mutex);
	pthread_mutex_destroy(&data->encoders_mutex);
570
	pthread_mutex_destroy(&data->services_mutex);
571
}
J
jp9000 已提交
572

573 574 575 576
static const char *obs_signals[] = {
	"void source_create(ptr source)",
	"void source_destroy(ptr source)",
	"void source_remove(ptr source)",
J
jp9000 已提交
577 578
	"void source_save(ptr source)",
	"void source_load(ptr source)",
579 580 581 582
	"void source_activate(ptr source)",
	"void source_deactivate(ptr source)",
	"void source_show(ptr source)",
	"void source_hide(ptr source)",
J
jp9000 已提交
583
	"void source_rename(ptr source, string new_name, string prev_name)",
584
	"void source_volume(ptr source, in out float volume)",
585 586
	"void source_volume_level(ptr source, float level, float magnitude, "
		"float peak)",
587 588 589 590

	"void channel_change(int channel, in out ptr source, ptr prev_source)",
	"void master_volume(in out float volume)",

P
Palana 已提交
591 592 593 594 595
	"void hotkey_layout_change()",
	"void hotkey_register(ptr hotkey)",
	"void hotkey_unregister(ptr hotkey)",
	"void hotkey_bindings_changed(ptr hotkey)",

596 597 598
	NULL
};

599 600 601 602 603 604 605
static inline bool obs_init_handlers(void)
{
	obs->signals = signal_handler_create();
	if (!obs->signals)
		return false;

	obs->procs   = proc_handler_create();
606 607 608 609
	if (!obs->procs)
		return false;

	return signal_handler_add_array(obs->signals, obs_signals);
610 611
}

P
Palana 已提交
612 613 614 615 616 617 618 619 620 621 622 623
static pthread_once_t obs_pthread_once_init_token = PTHREAD_ONCE_INIT;
static inline bool obs_init_hotkeys(void)
{
	struct obs_core_hotkeys *hotkeys = &obs->hotkeys;
	pthread_mutexattr_t attr;
	bool success = false;

	assert(hotkeys != NULL);

	da_init(hotkeys->hotkeys);
	hotkeys->signals = obs->signals;
	hotkeys->name_map_init_token = obs_pthread_once_init_token;
P
Palana 已提交
624 625 626 627
	hotkeys->mute = bstrdup("Mute");
	hotkeys->unmute = bstrdup("Unmute");
	hotkeys->push_to_mute = bstrdup("Push-to-mute");
	hotkeys->push_to_talk = bstrdup("Push-to-talk");
P
Palana 已提交
628 629
	hotkeys->sceneitem_show = bstrdup("Show '%1'");
	hotkeys->sceneitem_hide = bstrdup("Hide '%1'");
P
Palana 已提交
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674

	if (!obs_hotkeys_platform_init(hotkeys))
		return false;

	if (pthread_mutexattr_init(&attr) != 0)
		return false;
	if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
		goto fail;
	if (pthread_mutex_init(&hotkeys->mutex, &attr) != 0)
		goto fail;

	if (os_event_init(&hotkeys->stop_event, OS_EVENT_TYPE_MANUAL) != 0)
		goto fail;
	if (pthread_create(&hotkeys->hotkey_thread, NULL,
			obs_hotkey_thread, NULL))
		goto fail;

	hotkeys->hotkey_thread_initialized = true;

	success = true;

fail:
	pthread_mutexattr_destroy(&attr);
	return success;
}

static inline void stop_hotkeys(void)
{
	struct obs_core_hotkeys *hotkeys = &obs->hotkeys;
	void *thread_ret;

	if (hotkeys->hotkey_thread_initialized) {
		os_event_signal(hotkeys->stop_event);
		pthread_join(hotkeys->hotkey_thread, &thread_ret);
		hotkeys->hotkey_thread_initialized = false;
	}

	os_event_destroy(hotkeys->stop_event);
	obs_hotkeys_free();
}

static inline void obs_free_hotkeys(void)
{
	struct obs_core_hotkeys *hotkeys = &obs->hotkeys;

P
Palana 已提交
675 676 677 678
	bfree(hotkeys->mute);
	bfree(hotkeys->unmute);
	bfree(hotkeys->push_to_mute);
	bfree(hotkeys->push_to_talk);
P
Palana 已提交
679 680
	bfree(hotkeys->sceneitem_show);
	bfree(hotkeys->sceneitem_hide);
P
Palana 已提交
681

P
Palana 已提交
682 683 684 685 686 687
	obs_hotkey_name_map_free();

	obs_hotkeys_platform_free(hotkeys);
	pthread_mutex_destroy(&hotkeys->mutex);
}

688 689
extern const struct obs_source_info scene_info;

J
jp9000 已提交
690 691
extern void log_system_info(void);

692 693
static bool obs_init(const char *locale, const char *module_config_path,
		profiler_name_store_t *store)
694
{
695
	obs = bzalloc(sizeof(struct obs_core));
696

697 698 699 700 701 702 703
	obs->name_store_owned = !store;
	obs->name_store = store ? store : profiler_name_store_create();
	if (!obs->name_store) {
		blog(LOG_ERROR, "Couldn't create profiler name store");
		return false;
	}

J
jp9000 已提交
704 705
	log_system_info();

706 707 708 709
	if (!obs_init_data())
		return false;
	if (!obs_init_handlers())
		return false;
P
Palana 已提交
710 711
	if (!obs_init_hotkeys())
		return false;
712

713 714
	if (module_config_path)
		obs->module_config_path = bstrdup(module_config_path);
715
	obs->locale = bstrdup(locale);
716
	obs_register_source(&scene_info);
J
jp9000 已提交
717
	add_default_module_paths();
718
	return true;
J
jp9000 已提交
719 720
}

J
jp9000 已提交
721 722
#ifdef _WIN32
extern void initialize_crash_handler(void);
J
jp9000 已提交
723 724
extern void initialize_com(void);
extern void uninitialize_com(void);
J
jp9000 已提交
725 726
#endif

P
Palana 已提交
727
static const char *obs_startup_name = "obs_startup";
728 729
bool obs_startup(const char *locale, const char *module_config_path,
		profiler_name_store_t *store)
J
jp9000 已提交
730
{
731
	bool success;
J
jp9000 已提交
732

P
Palana 已提交
733 734
	profile_start(obs_startup_name);

735
	if (obs) {
J
jp9000 已提交
736
		blog(LOG_WARNING, "Tried to call obs_startup more than once");
737 738 739
		return false;
	}

J
jp9000 已提交
740 741
#ifdef _WIN32
	initialize_crash_handler();
J
jp9000 已提交
742
	initialize_com();
J
jp9000 已提交
743 744
#endif

745
	success = obs_init(locale, module_config_path, store);
P
Palana 已提交
746
	profile_end(obs_startup_name);
747 748 749 750
	if (!success)
		obs_shutdown();

	return success;
J
jp9000 已提交
751 752
}

753
void obs_shutdown(void)
J
jp9000 已提交
754
{
J
jp9000 已提交
755 756
	struct obs_module *module;

J
jp9000 已提交
757 758 759
	if (!obs)
		return;

760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
#define FREE_REGISTERED_TYPES(structure, list) \
	do { \
		for (size_t i = 0; i < list.num; i++) { \
			struct structure *item = &list.array[i]; \
			if (item->type_data && item->free_type_data) \
				item->free_type_data(item->type_data); \
		} \
		da_free(list); \
	} while (false)

	FREE_REGISTERED_TYPES(obs_source_info, obs->input_types);
	FREE_REGISTERED_TYPES(obs_source_info, obs->filter_types);
	FREE_REGISTERED_TYPES(obs_source_info, obs->transition_types);
	FREE_REGISTERED_TYPES(obs_output_info, obs->output_types);
	FREE_REGISTERED_TYPES(obs_encoder_info, obs->encoder_types);
	FREE_REGISTERED_TYPES(obs_service_info, obs->service_types);
	FREE_REGISTERED_TYPES(obs_modal_ui, obs->modal_ui_callbacks);
	FREE_REGISTERED_TYPES(obs_modeless_ui, obs->modeless_ui_callbacks);

#undef FREE_REGISTERED_TYPES
J
jp9000 已提交
780

781
	stop_video();
P
Palana 已提交
782
	stop_hotkeys();
783

784 785
	obs_free_data();
	obs_free_video();
P
Palana 已提交
786
	obs_free_hotkeys();
787
	obs_free_graphics();
788
	obs_free_audio();
789 790
	proc_handler_destroy(obs->procs);
	signal_handler_destroy(obs->signals);
J
jp9000 已提交
791

J
jp9000 已提交
792 793 794 795 796 797 798 799 800 801 802
	module = obs->first_module;
	while (module) {
		struct obs_module *next = module->next;
		free_module(module);
		module = next;
	}
	obs->first_module = NULL;

	for (size_t i = 0; i < obs->module_paths.num; i++)
		free_module_path(obs->module_paths.array+i);
	da_free(obs->module_paths);
J
jp9000 已提交
803

804 805 806
	if (obs->name_store_owned)
		profiler_name_store_free(obs->name_store);

807
	bfree(obs->module_config_path);
808
	bfree(obs->locale);
J
jp9000 已提交
809
	bfree(obs);
810
	obs = NULL;
J
jp9000 已提交
811 812 813 814

#ifdef _WIN32
	uninitialize_com();
#endif
815 816
}

817 818 819 820 821
bool obs_initialized(void)
{
	return obs != NULL;
}

822 823 824 825 826
uint32_t obs_get_version(void)
{
	return LIBOBS_API_VER;
}

827 828
void obs_set_locale(const char *locale)
{
J
jp9000 已提交
829
	struct obs_module *module;
830 831 832 833 834 835
	if (!obs)
		return;

	if (obs->locale)
		bfree(obs->locale);
	obs->locale = bstrdup(locale);
836

J
jp9000 已提交
837 838
	module = obs->first_module;
	while (module) {
839 840
		if (module->set_locale)
			module->set_locale(locale);
J
jp9000 已提交
841 842

		module = module->next;
843
	}
844 845 846 847 848 849 850
}

const char *obs_get_locale(void)
{
	return obs ? obs->locale : NULL;
}

851 852 853 854
#define OBS_SIZE_MIN 2
#define OBS_SIZE_MAX (32 * 1024)

static inline bool size_valid(uint32_t width, uint32_t height)
855
{
856 857 858 859 860 861 862
	return (width >= OBS_SIZE_MIN && height >= OBS_SIZE_MIN &&
	        width <= OBS_SIZE_MAX && height <= OBS_SIZE_MAX);
}

int obs_reset_video(struct obs_video_info *ovi)
{
	if (!obs) return OBS_VIDEO_FAIL;
863 864 865

	/* don't allow changing of video settings if active. */
	if (obs->video.video && video_output_active(obs->video.video))
866 867 868 869 870
		return OBS_VIDEO_CURRENTLY_ACTIVE;

	if (!size_valid(ovi->output_width, ovi->output_height) ||
	    !size_valid(ovi->base_width,   ovi->base_height))
		return OBS_VIDEO_INVALID_PARAM;
871

872
	struct obs_core_video *video = &obs->video;
873

874
	stop_video();
875
	obs_free_video();
876

877 878
	if (!ovi) {
		obs_free_graphics();
879
		return OBS_VIDEO_SUCCESS;
880 881
	}

882 883 884 885
	/* align to multiple-of-two and SSE alignment sizes */
	ovi->output_width  &= 0xFFFFFFFC;
	ovi->output_height &= 0xFFFFFFFE;

886 887
	if (!video->graphics) {
		int errorcode = obs_init_graphics(ovi);
888 889
		if (errorcode != OBS_VIDEO_SUCCESS) {
			obs_free_graphics();
890
			return errorcode;
891
		}
892
	}
893

894
	blog(LOG_INFO, "---------------------------------");
J
jp9000 已提交
895 896 897
	blog(LOG_INFO, "video settings reset:\n"
	               "\tbase resolution:   %dx%d\n"
	               "\toutput resolution: %dx%d\n"
J
jp9000 已提交
898 899
	               "\tfps:               %d/%d\n"
	               "\tformat:            %s",
J
jp9000 已提交
900 901
	               ovi->base_width, ovi->base_height,
	               ovi->output_width, ovi->output_height,
J
jp9000 已提交
902 903
	               ovi->fps_num, ovi->fps_den,
		       get_video_format_name(ovi->output_format));
J
jp9000 已提交
904

905
	return obs_init_video(ovi);
906 907
}

908
bool obs_reset_audio(const struct obs_audio_info *oai)
J
jp9000 已提交
909
{
910 911
	struct audio_output_info ai;

912 913 914 915 916 917
	if (!obs) return false;

	/* don't allow changing of audio settings if active. */
	if (obs->audio.audio && audio_output_active(obs->audio.audio))
		return false;

J
jp9000 已提交
918
	obs_free_audio();
919
	if (!oai)
920 921
		return true;

922 923 924 925 926 927
	ai.name = "Audio";
	ai.samples_per_sec = oai->samples_per_sec;
	ai.format = AUDIO_FORMAT_FLOAT_PLANAR;
	ai.speakers = oai->speakers;
	ai.buffer_ms = oai->buffer_ms;

928
	blog(LOG_INFO, "---------------------------------");
J
jp9000 已提交
929 930 931
	blog(LOG_INFO, "audio settings reset:\n"
	               "\tsamples per sec: %d\n"
	               "\tspeakers:        %d\n"
932
	               "\tbuffering (ms):  %d",
933 934 935
	               (int)ai.samples_per_sec,
	               (int)ai.speakers,
	               (int)ai.buffer_ms);
J
jp9000 已提交
936

937
	return obs_init_audio(&ai);
J
jp9000 已提交
938 939
}

940 941
bool obs_get_video_info(struct obs_video_info *ovi)
{
942
	struct obs_core_video *video = &obs->video;
J
jp9000 已提交
943
	const struct video_output_info *info;
944

J
jp9000 已提交
945
	if (!obs || !video->graphics)
946 947
		return false;

948
	info = video_output_get_info(video->video);
949 950
	if (!info)
		return false;
951 952 953 954

	memset(ovi, 0, sizeof(struct obs_video_info));
	ovi->base_width    = video->base_width;
	ovi->base_height   = video->base_height;
955
	ovi->gpu_conversion= video->gpu_conversion;
956
	ovi->scale_type    = video->scale_type;
957 958
	ovi->colorspace    = info->colorspace;
	ovi->range         = info->range;
959 960
	ovi->output_width  = info->width;
	ovi->output_height = info->height;
961
	ovi->output_format = info->format;
962 963 964 965 966 967
	ovi->fps_num       = info->fps_num;
	ovi->fps_den       = info->fps_den;

	return true;
}

968
bool obs_get_audio_info(struct obs_audio_info *oai)
969
{
970
	struct obs_core_audio *audio = &obs->audio;
J
jp9000 已提交
971
	const struct audio_output_info *info;
972

973
	if (!obs || !oai || !audio->audio)
974 975
		return false;

976
	info = audio_output_get_info(audio->audio);
977

978 979 980
	oai->samples_per_sec = info->samples_per_sec;
	oai->speakers = info->speakers;
	oai->buffer_ms = info->buffer_ms;
981 982 983
	return true;
}

984
bool obs_enum_input_types(size_t idx, const char **id)
J
jp9000 已提交
985
{
J
jp9000 已提交
986 987
	if (!obs) return false;

J
jp9000 已提交
988 989
	if (idx >= obs->input_types.num)
		return false;
990
	*id = obs->input_types.array[idx].id;
J
jp9000 已提交
991 992 993
	return true;
}

994
bool obs_enum_filter_types(size_t idx, const char **id)
J
jp9000 已提交
995
{
J
jp9000 已提交
996 997
	if (!obs) return false;

J
jp9000 已提交
998 999
	if (idx >= obs->filter_types.num)
		return false;
1000
	*id = obs->filter_types.array[idx].id;
J
jp9000 已提交
1001 1002 1003
	return true;
}

1004
bool obs_enum_transition_types(size_t idx, const char **id)
J
jp9000 已提交
1005
{
J
jp9000 已提交
1006 1007
	if (!obs) return false;

J
jp9000 已提交
1008 1009
	if (idx >= obs->transition_types.num)
		return false;
1010
	*id = obs->transition_types.array[idx].id;
J
jp9000 已提交
1011 1012 1013
	return true;
}

1014
bool obs_enum_output_types(size_t idx, const char **id)
J
jp9000 已提交
1015
{
J
jp9000 已提交
1016 1017
	if (!obs) return false;

J
jp9000 已提交
1018 1019
	if (idx >= obs->output_types.num)
		return false;
1020
	*id = obs->output_types.array[idx].id;
J
jp9000 已提交
1021 1022 1023
	return true;
}

1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
bool obs_enum_encoder_types(size_t idx, const char **id)
{
	if (!obs) return false;

	if (idx >= obs->encoder_types.num)
		return false;
	*id = obs->encoder_types.array[idx].id;
	return true;
}

bool obs_enum_service_types(size_t idx, const char **id)
{
	if (!obs) return false;

	if (idx >= obs->service_types.num)
		return false;
	*id = obs->service_types.array[idx].id;
	return true;
}

J
jp9000 已提交
1044
void obs_enter_graphics(void)
J
jp9000 已提交
1045
{
J
jp9000 已提交
1046
	if (obs && obs->video.graphics)
1047
		gs_enter_context(obs->video.graphics);
J
jp9000 已提交
1048 1049 1050 1051 1052
}

void obs_leave_graphics(void)
{
	if (obs && obs->video.graphics)
1053
		gs_leave_context();
J
jp9000 已提交
1054 1055
}

1056
audio_t *obs_get_audio(void)
J
jp9000 已提交
1057 1058 1059 1060
{
	return (obs != NULL) ? obs->audio.audio : NULL;
}

1061
video_t *obs_get_video(void)
J
jp9000 已提交
1062
{
J
jp9000 已提交
1063
	return (obs != NULL) ? obs->video.video : NULL;
J
jp9000 已提交
1064 1065
}

J
jp9000 已提交
1066
/* TODO: optimize this later so it's not just O(N) string lookups */
J
jp9000 已提交
1067
static inline struct obs_modal_ui *get_modal_ui_callback(const char *id,
J
jp9000 已提交
1068 1069
		const char *task, const char *target)
{
J
jp9000 已提交
1070 1071
	for (size_t i = 0; i < obs->modal_ui_callbacks.num; i++) {
		struct obs_modal_ui *callback = obs->modal_ui_callbacks.array+i;
J
jp9000 已提交
1072

J
jp9000 已提交
1073
		if (strcmp(callback->id,     id)     == 0 &&
1074 1075
		    strcmp(callback->task,   task)   == 0 &&
		    strcmp(callback->target, target) == 0)
J
jp9000 已提交
1076 1077 1078 1079 1080 1081
			return callback;
	}

	return NULL;
}

J
jp9000 已提交
1082
static inline struct obs_modeless_ui *get_modeless_ui_callback(const char *id,
J
jp9000 已提交
1083 1084
		const char *task, const char *target)
{
J
jp9000 已提交
1085
	for (size_t i = 0; i < obs->modeless_ui_callbacks.num; i++) {
1086
		struct obs_modeless_ui *callback;
J
jp9000 已提交
1087
		callback = obs->modeless_ui_callbacks.array+i;
J
jp9000 已提交
1088

J
jp9000 已提交
1089
		if (strcmp(callback->id,     id)     == 0 &&
1090 1091
		    strcmp(callback->task,   task)   == 0 &&
		    strcmp(callback->target, target) == 0)
J
jp9000 已提交
1092 1093 1094 1095 1096 1097
			return callback;
	}

	return NULL;
}

1098
int obs_exec_ui(const char *name, const char *task, const char *target,
J
jp9000 已提交
1099 1100
		void *data, void *ui_data)
{
1101
	struct obs_modal_ui *callback;
J
jp9000 已提交
1102 1103
	int errorcode = OBS_UI_NOTFOUND;

J
jp9000 已提交
1104 1105
	if (!obs) return errorcode;

1106
	callback = get_modal_ui_callback(name, task, target);
J
jp9000 已提交
1107
	if (callback) {
J
jp9000 已提交
1108
		bool success = callback->exec(data, ui_data);
J
jp9000 已提交
1109 1110 1111 1112 1113 1114
		errorcode = success ? OBS_UI_SUCCESS : OBS_UI_CANCEL;
	}

	return errorcode;
}

J
jp9000 已提交
1115 1116 1117
void *obs_create_ui(const char *name, const char *task, const char *target,
		void *data, void *ui_data)
{
1118
	struct obs_modeless_ui *callback;
J
jp9000 已提交
1119

J
jp9000 已提交
1120 1121
	if (!obs) return NULL;

J
jp9000 已提交
1122
	callback = get_modeless_ui_callback(name, task, target);
J
jp9000 已提交
1123
	return callback ? callback->create(data, ui_data) : NULL;
J
jp9000 已提交
1124 1125
}

1126
obs_source_t *obs_get_output_source(uint32_t channel)
1127
{
J
jp9000 已提交
1128
	if (!obs) return NULL;
1129
	return obs_view_get_source(&obs->data.main_view, channel);
J
jp9000 已提交
1130 1131
}

1132
void obs_set_output_source(uint32_t channel, obs_source_t *source)
J
jp9000 已提交
1133
{
1134 1135 1136 1137 1138
	assert(channel < MAX_CHANNELS);

	if (!obs) return;
	if (channel >= MAX_CHANNELS) return;

1139
	struct obs_source *prev_source;
J
jp9000 已提交
1140
	struct obs_view *view = &obs->data.main_view;
J
jp9000 已提交
1141
	struct calldata params = {0};
1142

J
jp9000 已提交
1143
	pthread_mutex_lock(&view->channels_mutex);
1144

J
jp9000 已提交
1145 1146
	obs_source_addref(source);

J
jp9000 已提交
1147
	prev_source = view->channels[channel];
J
jp9000 已提交
1148

1149 1150 1151
	calldata_set_int(&params, "channel", channel);
	calldata_set_ptr(&params, "prev_source", prev_source);
	calldata_set_ptr(&params, "source", source);
1152
	signal_handler_signal(obs->signals, "channel_change", &params);
1153
	calldata_get_ptr(&params, "source", &source);
J
jp9000 已提交
1154 1155
	calldata_free(&params);

J
jp9000 已提交
1156
	view->channels[channel] = source;
1157

J
jp9000 已提交
1158 1159 1160
	pthread_mutex_unlock(&view->channels_mutex);

	if (source)
1161
		obs_source_activate(source, MAIN_VIEW);
1162 1163

	if (prev_source) {
1164
		obs_source_deactivate(prev_source, MAIN_VIEW);
1165
		obs_source_release(prev_source);
1166
	}
J
jp9000 已提交
1167
}
1168

1169
void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param)
1170
{
1171 1172
	obs_source_t *source;

J
jp9000 已提交
1173 1174
	if (!obs) return;

1175 1176
	pthread_mutex_lock(&obs->data.sources_mutex);
	source = obs->data.first_source;
1177

1178 1179 1180
	while (source) {
		obs_source_t *next_source =
			(obs_source_t*)source->context.next;
1181

1182 1183
		if ((source->info.type == OBS_SOURCE_TYPE_INPUT) != 0 &&
		    !enum_proc(param, source))
1184
			break;
1185

1186
		source = next_source;
1187
	}
1188

1189
	pthread_mutex_unlock(&obs->data.sources_mutex);
1190 1191
}

1192 1193
static inline void obs_enum(void *pstart, pthread_mutex_t *mutex, void *proc,
		void *param)
1194
{
1195 1196
	struct obs_context_data **start = pstart, *context;
	bool (*enum_proc)(void*, void*) = proc;
1197

1198 1199 1200
	assert(start);
	assert(mutex);
	assert(enum_proc);
J
jp9000 已提交
1201

1202
	pthread_mutex_lock(mutex);
1203

1204 1205 1206
	context = *start;
	while (context) {
		if (!enum_proc(param, context))
1207 1208
			break;

1209 1210 1211 1212
		context = context->next;
	}

	pthread_mutex_unlock(mutex);
1213 1214
}

1215
void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t*), void *param)
1216
{
J
jp9000 已提交
1217
	if (!obs) return;
1218 1219 1220
	obs_enum(&obs->data.first_output, &obs->data.outputs_mutex,
			enum_proc, param);
}
J
jp9000 已提交
1221

1222
void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t*), void *param)
1223 1224 1225 1226 1227
{
	if (!obs) return;
	obs_enum(&obs->data.first_encoder, &obs->data.encoders_mutex,
			enum_proc, param);
}
1228

1229
void obs_enum_services(bool (*enum_proc)(void*, obs_service_t*), void *param)
1230 1231 1232 1233
{
	if (!obs) return;
	obs_enum(&obs->data.first_service, &obs->data.services_mutex,
			enum_proc, param);
1234
}
1235

1236
obs_source_t *obs_get_source_by_name(const char *name)
1237
{
1238
	struct obs_core_data *data = &obs->data;
1239
	struct obs_source *source;
1240

J
jp9000 已提交
1241 1242
	if (!obs) return NULL;

1243 1244
	pthread_mutex_lock(&data->sources_mutex);
	source = data->first_source;
1245

1246 1247
	while (source) {
		if (strcmp(source->context.name, name) == 0) {
1248
			obs_source_addref(source);
1249 1250
			break;
		}
1251 1252

		source = (struct obs_source*)source->context.next;
1253 1254
	}

1255
	pthread_mutex_unlock(&data->sources_mutex);
1256 1257
	return source;
}
1258

1259
static inline void *get_context_by_name(void *vfirst, const char *name,
P
Palana 已提交
1260
		pthread_mutex_t *mutex, void *(*addref)(void*))
1261 1262 1263 1264 1265 1266 1267 1268
{
	struct obs_context_data **first = vfirst;
	struct obs_context_data *context;

	pthread_mutex_lock(mutex);

	context = *first;
	while (context) {
P
Palana 已提交
1269 1270
		if (strcmp(context->name, name) == 0) {
			context = addref(context);
1271
			break;
P
Palana 已提交
1272
		}
1273 1274 1275 1276 1277 1278 1279
		context = context->next;
	}

	pthread_mutex_unlock(mutex);
	return context;
}

1280 1281 1282 1283 1284
static inline void *obs_output_addref_safe_(void *ref)
{
	return obs_output_get_ref(ref);
}

1285 1286 1287 1288 1289
static inline void *obs_encoder_addref_safe_(void *ref)
{
	return obs_encoder_get_ref(ref);
}

1290 1291 1292 1293 1294
static inline void *obs_service_addref_safe_(void *ref)
{
	return obs_service_get_ref(ref);
}

P
Palana 已提交
1295 1296 1297 1298 1299
static inline void *obs_id_(void *data)
{
	return data;
}

1300
obs_output_t *obs_get_output_by_name(const char *name)
1301 1302 1303
{
	if (!obs) return NULL;
	return get_context_by_name(&obs->data.first_output, name,
1304
			&obs->data.outputs_mutex, obs_output_addref_safe_);
1305 1306
}

1307
obs_encoder_t *obs_get_encoder_by_name(const char *name)
1308 1309 1310
{
	if (!obs) return NULL;
	return get_context_by_name(&obs->data.first_encoder, name,
1311
			&obs->data.encoders_mutex, obs_encoder_addref_safe_);
1312 1313
}

1314
obs_service_t *obs_get_service_by_name(const char *name)
1315 1316 1317
{
	if (!obs) return NULL;
	return get_context_by_name(&obs->data.first_service, name,
1318
			&obs->data.services_mutex, obs_service_addref_safe_);
1319 1320
}

1321
gs_effect_t *obs_get_base_effect(enum obs_base_effect effect)
1322
{
1323
	if (!obs) return NULL;
1324

1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340
	switch (effect) {
	case OBS_EFFECT_DEFAULT:
		return obs->video.default_effect;
	case OBS_EFFECT_DEFAULT_RECT:
		return obs->video.default_rect_effect;
	case OBS_EFFECT_OPAQUE:
		return obs->video.opaque_effect;
	case OBS_EFFECT_SOLID:
		return obs->video.solid_effect;
	case OBS_EFFECT_BICUBIC:
		return obs->video.bicubic_effect;
	case OBS_EFFECT_LANCZOS:
		return obs->video.lanczos_effect;
	case OBS_EFFECT_BILINEAR_LOWRES:
		return obs->video.bilinear_lowres_effect;
	}
1341

1342
	return NULL;
1343 1344
}

1345 1346 1347 1348 1349 1350 1351
/* DEPRECATED */
gs_effect_t *obs_get_default_rect_effect(void)
{
	if (!obs) return NULL;
	return obs->video.default_rect_effect;
}

1352
signal_handler_t *obs_get_signal_handler(void)
1353
{
1354
	if (!obs) return NULL;
1355 1356 1357
	return obs->signals;
}

1358
proc_handler_t *obs_get_proc_handler(void)
1359
{
1360
	if (!obs) return NULL;
1361 1362
	return obs->procs;
}
1363

J
jp9000 已提交
1364
void obs_render_main_view(void)
1365 1366
{
	if (!obs) return;
J
jp9000 已提交
1367
	obs_view_render(&obs->data.main_view);
1368
}
J
jp9000 已提交
1369 1370 1371

void obs_set_master_volume(float volume)
{
J
jp9000 已提交
1372
	struct calldata data = {0};
J
jp9000 已提交
1373

J
jp9000 已提交
1374
	if (!obs) return;
J
jp9000 已提交
1375

1376
	calldata_set_float(&data, "volume", volume);
1377 1378
	signal_handler_signal(obs->signals, "master_volume", &data);
	volume = (float)calldata_float(&data, "volume");
J
jp9000 已提交
1379 1380
	calldata_free(&data);

J
jp9000 已提交
1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
	obs->audio.user_volume = volume;
}

void obs_set_present_volume(float volume)
{
	if (!obs) return;
	obs->audio.present_volume = volume;
}

float obs_get_master_volume(void)
{
	return obs ? obs->audio.user_volume : 0.0f;
}

float obs_get_present_volume(void)
{
	return obs ? obs->audio.present_volume : 0.0f;
}
1399

1400 1401
static obs_source_t *obs_load_source_type(obs_data_t *source_data,
		enum obs_source_type type)
1402
{
1403
	obs_data_array_t *filters = obs_data_get_array(source_data, "filters");
1404
	obs_source_t *source;
J
jp9000 已提交
1405 1406
	const char   *name    = obs_data_get_string(source_data, "name");
	const char   *id      = obs_data_get_string(source_data, "id");
1407
	obs_data_t   *settings = obs_data_get_obj(source_data, "settings");
P
Palana 已提交
1408
	obs_data_t   *hotkeys  = obs_data_get_obj(source_data, "hotkeys");
1409
	double       volume;
1410 1411
	int64_t      sync;
	uint32_t     flags;
1412
	uint32_t     mixers;
1413

1414
	source = obs_source_create(type, id, name, settings, hotkeys);
1415

1416
	obs_data_release(hotkeys);
P
Palana 已提交
1417

1418
	obs_data_set_default_double(source_data, "volume", 1.0);
J
jp9000 已提交
1419
	volume = obs_data_get_double(source_data, "volume");
1420
	obs_source_set_volume(source, (float)volume);
1421

1422 1423 1424
	sync = obs_data_get_int(source_data, "sync");
	obs_source_set_sync_offset(source, sync);

1425 1426 1427 1428
	obs_data_set_default_int(source_data, "mixers", 0xF);
	mixers = (uint32_t)obs_data_get_int(source_data, "mixers");
	obs_source_set_audio_mixers(source, mixers);

1429
	obs_data_set_default_int(source_data, "flags", source->default_flags);
1430 1431 1432
	flags = (uint32_t)obs_data_get_int(source_data, "flags");
	obs_source_set_flags(source, flags);

1433 1434 1435 1436
	obs_data_set_default_bool(source_data, "enabled", true);
	obs_source_set_enabled(source,
			obs_data_get_bool(source_data, "enabled"));

J
jp9000 已提交
1437 1438 1439
	obs_data_set_default_bool(source_data, "muted", false);
	obs_source_set_muted(source, obs_data_get_bool(source_data, "muted"));

1440 1441 1442 1443 1444 1445 1446 1447
	obs_data_set_default_bool(source_data, "push-to-mute", false);
	obs_source_enable_push_to_mute(source,
			obs_data_get_bool(source_data, "push-to-mute"));

	obs_data_set_default_int(source_data, "push-to-mute-delay", 0);
	obs_source_set_push_to_mute_delay(source,
			obs_data_get_int(source_data, "push-to-mute-delay"));

P
Palana 已提交
1448 1449 1450 1451 1452 1453 1454 1455
	obs_data_set_default_bool(source_data, "push-to-talk", false);
	obs_source_enable_push_to_talk(source,
			obs_data_get_bool(source_data, "push-to-talk"));

	obs_data_set_default_int(source_data, "push-to-talk-delay", 0);
	obs_source_set_push_to_talk_delay(source,
			obs_data_get_int(source_data, "push-to-talk-delay"));

1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475
	if (filters) {
		size_t count = obs_data_array_count(filters);

		for (size_t i = 0; i < count; i++) {
			obs_data_t *filter_data =
				obs_data_array_item(filters, i);

			obs_source_t *filter = obs_load_source_type(
					filter_data, OBS_SOURCE_TYPE_FILTER);
			if (filter) {
				obs_source_filter_add(source, filter);
				obs_source_release(filter);
			}

			obs_data_release(filter_data);
		}

		obs_data_array_release(filters);
	}

1476 1477 1478 1479 1480
	obs_data_release(settings);

	return source;
}

1481 1482 1483 1484 1485
obs_source_t *obs_load_source(obs_data_t *source_data)
{
	return obs_load_source_type(source_data, OBS_SOURCE_TYPE_INPUT);
}

1486
void obs_load_sources(obs_data_array_t *array)
1487
{
1488 1489
	if (!obs) return;

1490 1491
	struct obs_core_data *data = &obs->data;
	DARRAY(obs_source_t*) sources;
1492 1493 1494
	size_t count;
	size_t i;

1495 1496
	da_init(sources);

1497
	count = obs_data_array_count(array);
1498
	da_reserve(sources, count);
1499

1500
	pthread_mutex_lock(&data->sources_mutex);
1501 1502

	for (i = 0; i < count; i++) {
1503 1504
		obs_data_t   *source_data = obs_data_array_item(array, i);
		obs_source_t *source      = obs_load_source(source_data);
1505

1506
		da_push_back(sources, &source);
1507 1508 1509 1510 1511

		obs_data_release(source_data);
	}

	/* tell sources that we want to load */
1512 1513 1514 1515
	for (i = 0; i < sources.num; i++)
		obs_source_load(sources.array[i]);
	for (i = 0; i < sources.num; i++)
		obs_source_release(sources.array[i]);
1516

1517 1518 1519
	pthread_mutex_unlock(&data->sources_mutex);

	da_free(sources);
1520 1521
}

1522
obs_data_t *obs_save_source(obs_source_t *source)
1523
{
1524
	obs_data_array_t *filters = obs_data_array_create();
1525 1526
	obs_data_t *source_data = obs_data_create();
	obs_data_t *settings    = obs_source_get_settings(source);
P
Palana 已提交
1527 1528
	obs_data_t *hotkey_data = source->context.hotkey_data;
	obs_data_t *hotkeys;
1529
	float      volume      = obs_source_get_volume(source);
1530
	uint32_t   mixers      = obs_source_get_audio_mixers(source);
1531 1532
	int64_t    sync        = obs_source_get_sync_offset(source);
	uint32_t   flags       = obs_source_get_flags(source);
1533
	const char *name       = obs_source_get_name(source);
J
jp9000 已提交
1534
	const char *id         = obs_source_get_id(source);
1535
	bool       enabled     = obs_source_enabled(source);
J
jp9000 已提交
1536
	bool       muted       = obs_source_muted(source);
1537 1538
	bool       push_to_mute= obs_source_push_to_mute_enabled(source);
	uint64_t   ptm_delay   = obs_source_get_push_to_mute_delay(source);
P
Palana 已提交
1539 1540
	bool       push_to_talk= obs_source_push_to_talk_enabled(source);
	uint64_t   ptt_delay   = obs_source_get_push_to_talk_delay(source);
1541 1542

	obs_source_save(source);
P
Palana 已提交
1543 1544 1545 1546 1547 1548 1549
	hotkeys = obs_hotkeys_save_source(source);

	if (hotkeys) {
		obs_data_release(hotkey_data);
		source->context.hotkey_data = hotkeys;
		hotkey_data = hotkeys;
	}
1550

J
jp9000 已提交
1551 1552 1553
	obs_data_set_string(source_data, "name",     name);
	obs_data_set_string(source_data, "id",       id);
	obs_data_set_obj   (source_data, "settings", settings);
1554
	obs_data_set_int   (source_data, "mixers",   mixers);
1555 1556
	obs_data_set_int   (source_data, "sync",     sync);
	obs_data_set_int   (source_data, "flags",    flags);
J
jp9000 已提交
1557
	obs_data_set_double(source_data, "volume",   volume);
1558
	obs_data_set_bool  (source_data, "enabled",  enabled);
J
jp9000 已提交
1559
	obs_data_set_bool  (source_data, "muted",    muted);
1560 1561
	obs_data_set_bool  (source_data, "push-to-mute", push_to_mute);
	obs_data_set_int   (source_data, "push-to-mute-delay", ptm_delay);
P
Palana 已提交
1562 1563
	obs_data_set_bool  (source_data, "push-to-talk", push_to_talk);
	obs_data_set_int   (source_data, "push-to-talk-delay", ptt_delay);
P
Palana 已提交
1564
	obs_data_set_obj   (source_data, "hotkeys",  hotkey_data);
1565

1566 1567 1568
	pthread_mutex_lock(&source->filter_mutex);

	if (source->filters.num) {
1569 1570
		for (size_t i = source->filters.num; i > 0; i--) {
			obs_source_t *filter = source->filters.array[i - 1];
1571 1572 1573 1574 1575 1576 1577 1578 1579 1580
			obs_data_t *filter_data = obs_save_source(filter);
			obs_data_array_push_back(filters, filter_data);
			obs_data_release(filter_data);
		}

		obs_data_set_array(source_data, "filters", filters);
	}

	pthread_mutex_unlock(&source->filter_mutex);

1581
	obs_data_release(settings);
1582
	obs_data_array_release(filters);
1583 1584

	return source_data;
1585 1586
}

1587 1588
obs_data_array_t *obs_save_sources_filtered(obs_save_source_filter_cb cb,
		void *data_)
1589
{
1590 1591
	if (!obs) return NULL;

1592
	struct obs_core_data *data = &obs->data;
1593
	obs_data_array_t *array;
1594
	obs_source_t *source;
1595 1596 1597

	array = obs_data_array_create();

1598
	pthread_mutex_lock(&data->sources_mutex);
1599

1600
	source = data->first_source;
1601

1602
	while (source) {
1603 1604
		if ((source->info.type == OBS_SOURCE_TYPE_INPUT) != 0 &&
				cb(data_, source)) {
1605 1606 1607 1608 1609 1610 1611
			obs_data_t *source_data = obs_save_source(source);

			obs_data_array_push_back(array, source_data);
			obs_data_release(source_data);
		}

		source = (obs_source_t*)source->context.next;
1612 1613
	}

1614
	pthread_mutex_unlock(&data->sources_mutex);
1615 1616 1617 1618

	return array;
}

1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630
static bool save_source_filter(void *data, obs_source_t *source)
{
	UNUSED_PARAMETER(data);
	UNUSED_PARAMETER(source);
	return true;
}

obs_data_array_t *obs_save_sources(void)
{
	return obs_save_sources_filtered(save_source_filter, NULL);
}

1631 1632 1633 1634 1635
/* ensures that names are never blank */
static inline char *dup_name(const char *name)
{
	if (!name || !*name) {
		struct dstr unnamed = {0};
1636
		dstr_printf(&unnamed, "__unnamed%04lld",
1637 1638 1639 1640 1641 1642 1643 1644 1645 1646
				obs->data.unnamed_index++);

		return unnamed.array;
	} else {
		return bstrdup(name);
	}
}

static inline bool obs_context_data_init_wrap(
		struct obs_context_data *context,
1647
		obs_data_t              *settings,
P
Palana 已提交
1648 1649
		const char              *name,
		obs_data_t              *hotkey_data)
1650 1651
{
	assert(context);
1652
	memset(context, 0, sizeof(*context));
1653

J
jp9000 已提交
1654 1655 1656 1657
	pthread_mutex_init_value(&context->rename_cache_mutex);
	if (pthread_mutex_init(&context->rename_cache_mutex, NULL) < 0)
		return false;

1658
	context->signals = signal_handler_create();
P
Palana 已提交
1659
	if (!context->signals)
1660 1661 1662 1663 1664 1665
		return false;

	context->procs = proc_handler_create();
	if (!context->procs)
		return false;

P
Palana 已提交
1666 1667 1668
	context->name        = dup_name(name);
	context->settings    = obs_data_newref(settings);
	context->hotkey_data = obs_data_newref(hotkey_data);
1669 1670 1671 1672 1673
	return true;
}

bool obs_context_data_init(
		struct obs_context_data *context,
1674
		obs_data_t              *settings,
P
Palana 已提交
1675 1676
		const char              *name,
		obs_data_t              *hotkey_data)
1677
{
P
Palana 已提交
1678
	if (obs_context_data_init_wrap(context, settings, name, hotkey_data)) {
1679 1680 1681 1682 1683 1684 1685 1686 1687
		return true;
	} else {
		obs_context_data_free(context);
		return false;
	}
}

void obs_context_data_free(struct obs_context_data *context)
{
P
Palana 已提交
1688
	obs_hotkeys_context_release(context);
1689 1690 1691 1692
	signal_handler_destroy(context->signals);
	proc_handler_destroy(context->procs);
	obs_data_release(context->settings);
	obs_context_data_remove(context);
J
jp9000 已提交
1693
	pthread_mutex_destroy(&context->rename_cache_mutex);
1694 1695
	bfree(context->name);

J
jp9000 已提交
1696 1697 1698 1699
	for (size_t i = 0; i < context->rename_cache.num; i++)
		bfree(context->rename_cache.array[i]);
	da_free(context->rename_cache);

1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726
	memset(context, 0, sizeof(*context));
}

void obs_context_data_insert(struct obs_context_data *context,
		pthread_mutex_t *mutex, void *pfirst)
{
	struct obs_context_data **first = pfirst;

	assert(context);
	assert(mutex);
	assert(first);

	context->mutex = mutex;

	pthread_mutex_lock(mutex);
	context->prev_next  = first;
	context->next       = *first;
	*first              = context;
	if (context->next)
		context->next->prev_next = &context->next;
	pthread_mutex_unlock(mutex);
}

void obs_context_data_remove(struct obs_context_data *context)
{
	if (context && context->mutex) {
		pthread_mutex_lock(context->mutex);
1727 1728
		if (context->prev_next)
			*context->prev_next = context->next;
1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739
		if (context->next)
			context->next->prev_next = context->prev_next;
		pthread_mutex_unlock(context->mutex);

		context->mutex = NULL;
	}
}

void obs_context_data_setname(struct obs_context_data *context,
		const char *name)
{
J
jp9000 已提交
1740 1741 1742 1743
	pthread_mutex_lock(&context->rename_cache_mutex);

	if (context->name)
		da_push_back(context->rename_cache, &context->name);
1744
	context->name = dup_name(name);
J
jp9000 已提交
1745 1746

	pthread_mutex_unlock(&context->rename_cache_mutex);
1747
}
1748 1749 1750 1751 1752 1753 1754 1755

profiler_name_store_t *obs_get_profiler_name_store(void)
{
	if (!obs)
		return NULL;

	return obs->name_store;
}