obs.c 43.4 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
	audio->user_volume    = 1.0f;

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

	return false;
}

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

J
jp9000 已提交
487 488 489 490
	circlebuf_free(&audio->buffered_timestamps);
	da_free(audio->render_order);
	da_free(audio->root_nodes);

491
	memset(audio, 0, sizeof(struct obs_core_audio));
492 493 494 495
}

static bool obs_init_data(void)
{
496
	struct obs_core_data *data = &obs->data;
497
	pthread_mutexattr_t attr;
498

499 500
	assert(data != NULL);

501 502
	pthread_mutex_init_value(&obs->data.displays_mutex);

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

522
	data->valid = true;
523 524 525

fail:
	pthread_mutexattr_destroy(&attr);
526
	return data->valid;
J
jp9000 已提交
527 528
}

529 530 531 532 533 534 535 536 537 538 539
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);
}

540 541 542 543 544 545 546 547 548 549 550 551
#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)

552
static void obs_free_data(void)
J
jp9000 已提交
553
{
554
	struct obs_core_data *data = &obs->data;
J
jp9000 已提交
555

556 557
	data->valid = false;

558
	obs_main_view_free(&data->main_view);
559

560
	blog(LOG_INFO, "Freeing OBS context data");
561

562 563 564 565 566 567
	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);

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

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

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

P
Palana 已提交
594 595 596 597 598
	"void hotkey_layout_change()",
	"void hotkey_register(ptr hotkey)",
	"void hotkey_unregister(ptr hotkey)",
	"void hotkey_bindings_changed(ptr hotkey)",

599 600 601
	NULL
};

602 603 604 605 606 607 608
static inline bool obs_init_handlers(void)
{
	obs->signals = signal_handler_create();
	if (!obs->signals)
		return false;

	obs->procs   = proc_handler_create();
609 610 611 612
	if (!obs->procs)
		return false;

	return signal_handler_add_array(obs->signals, obs_signals);
613 614
}

P
Palana 已提交
615 616 617 618 619 620 621 622 623 624 625 626
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 已提交
627 628 629 630
	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 已提交
631 632
	hotkeys->sceneitem_show = bstrdup("Show '%1'");
	hotkeys->sceneitem_hide = bstrdup("Hide '%1'");
P
Palana 已提交
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 675 676 677

	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 已提交
678 679 680 681
	bfree(hotkeys->mute);
	bfree(hotkeys->unmute);
	bfree(hotkeys->push_to_mute);
	bfree(hotkeys->push_to_talk);
P
Palana 已提交
682 683
	bfree(hotkeys->sceneitem_show);
	bfree(hotkeys->sceneitem_hide);
P
Palana 已提交
684

P
Palana 已提交
685 686 687 688 689 690
	obs_hotkey_name_map_free();

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

691 692
extern const struct obs_source_info scene_info;

J
jp9000 已提交
693 694
extern void log_system_info(void);

695 696
static bool obs_init(const char *locale, const char *module_config_path,
		profiler_name_store_t *store)
697
{
698
	obs = bzalloc(sizeof(struct obs_core));
699

700 701 702 703 704 705 706
	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 已提交
707 708
	log_system_info();

709 710 711 712
	if (!obs_init_data())
		return false;
	if (!obs_init_handlers())
		return false;
P
Palana 已提交
713 714
	if (!obs_init_hotkeys())
		return false;
715

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

J
jp9000 已提交
724 725
#ifdef _WIN32
extern void initialize_crash_handler(void);
J
jp9000 已提交
726 727
extern void initialize_com(void);
extern void uninitialize_com(void);
J
jp9000 已提交
728 729
#endif

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

P
Palana 已提交
736 737
	profile_start(obs_startup_name);

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

J
jp9000 已提交
743 744
#ifdef _WIN32
	initialize_crash_handler();
J
jp9000 已提交
745
	initialize_com();
J
jp9000 已提交
746 747
#endif

748
	success = obs_init(locale, module_config_path, store);
P
Palana 已提交
749
	profile_end(obs_startup_name);
750 751 752 753
	if (!success)
		obs_shutdown();

	return success;
J
jp9000 已提交
754 755
}

756
void obs_shutdown(void)
J
jp9000 已提交
757
{
J
jp9000 已提交
758 759
	struct obs_module *module;

J
jp9000 已提交
760 761 762
	if (!obs)
		return;

763 764 765 766 767 768 769 770 771 772
#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)

773
	FREE_REGISTERED_TYPES(obs_source_info, obs->source_types);
774 775 776 777 778 779 780 781 782 783
	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 已提交
784

785
	stop_video();
P
Palana 已提交
786
	stop_hotkeys();
787

J
jp9000 已提交
788
	obs_free_audio();
789 790
	obs_free_data();
	obs_free_video();
P
Palana 已提交
791
	obs_free_hotkeys();
792
	obs_free_graphics();
793 794
	proc_handler_destroy(obs->procs);
	signal_handler_destroy(obs->signals);
J
jp9000 已提交
795

J
jp9000 已提交
796 797 798 799 800 801 802 803 804 805 806
	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 已提交
807

808 809 810
	if (obs->name_store_owned)
		profiler_name_store_free(obs->name_store);

811
	bfree(obs->module_config_path);
812
	bfree(obs->locale);
J
jp9000 已提交
813
	bfree(obs);
814
	obs = NULL;
J
jp9000 已提交
815 816 817 818

#ifdef _WIN32
	uninitialize_com();
#endif
819 820
}

821 822 823 824 825
bool obs_initialized(void)
{
	return obs != NULL;
}

826 827 828 829 830
uint32_t obs_get_version(void)
{
	return LIBOBS_API_VER;
}

831 832
void obs_set_locale(const char *locale)
{
J
jp9000 已提交
833
	struct obs_module *module;
834 835 836 837 838 839
	if (!obs)
		return;

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

J
jp9000 已提交
841 842
	module = obs->first_module;
	while (module) {
843 844
		if (module->set_locale)
			module->set_locale(locale);
J
jp9000 已提交
845 846

		module = module->next;
847
	}
848 849 850 851 852 853 854
}

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

855 856 857 858
#define OBS_SIZE_MIN 2
#define OBS_SIZE_MAX (32 * 1024)

static inline bool size_valid(uint32_t width, uint32_t height)
859
{
860 861 862 863 864 865 866
	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;
867 868 869

	/* don't allow changing of video settings if active. */
	if (obs->video.video && video_output_active(obs->video.video))
870 871 872 873 874
		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;
875

876
	struct obs_core_video *video = &obs->video;
877

878
	stop_video();
879
	obs_free_video();
880

881 882
	if (!ovi) {
		obs_free_graphics();
883
		return OBS_VIDEO_SUCCESS;
884 885
	}

886 887 888 889
	/* align to multiple-of-two and SSE alignment sizes */
	ovi->output_width  &= 0xFFFFFFFC;
	ovi->output_height &= 0xFFFFFFFE;

890 891
	if (!video->graphics) {
		int errorcode = obs_init_graphics(ovi);
892 893
		if (errorcode != OBS_VIDEO_SUCCESS) {
			obs_free_graphics();
894
			return errorcode;
895
		}
896
	}
897

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

909
	return obs_init_video(ovi);
910 911
}

912
bool obs_reset_audio(const struct obs_audio_info *oai)
J
jp9000 已提交
913
{
914 915
	struct audio_output_info ai;

916 917 918 919 920 921
	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 已提交
922
	obs_free_audio();
923
	if (!oai)
924 925
		return true;

926 927 928 929
	ai.name = "Audio";
	ai.samples_per_sec = oai->samples_per_sec;
	ai.format = AUDIO_FORMAT_FLOAT_PLANAR;
	ai.speakers = oai->speakers;
J
jp9000 已提交
930
	ai.input_callback = audio_callback;
931

932
	blog(LOG_INFO, "---------------------------------");
J
jp9000 已提交
933 934
	blog(LOG_INFO, "audio settings reset:\n"
	               "\tsamples per sec: %d\n"
J
jp9000 已提交
935
	               "\tspeakers:        %d",
936
	               (int)ai.samples_per_sec,
J
jp9000 已提交
937
	               (int)ai.speakers);
J
jp9000 已提交
938

939
	return obs_init_audio(&ai);
J
jp9000 已提交
940 941
}

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

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

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

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

	return true;
}

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

975
	if (!obs || !oai || !audio->audio)
976 977
		return false;

978
	info = audio_output_get_info(audio->audio);
979

980 981
	oai->samples_per_sec = info->samples_per_sec;
	oai->speakers = info->speakers;
982 983 984
	return true;
}

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

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

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

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

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

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

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

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

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
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 已提交
1045
void obs_enter_graphics(void)
J
jp9000 已提交
1046
{
J
jp9000 已提交
1047
	if (obs && obs->video.graphics)
1048
		gs_enter_context(obs->video.graphics);
J
jp9000 已提交
1049 1050 1051 1052 1053
}

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

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

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

J
jp9000 已提交
1067
/* TODO: optimize this later so it's not just O(N) string lookups */
J
jp9000 已提交
1068
static inline struct obs_modal_ui *get_modal_ui_callback(const char *id,
J
jp9000 已提交
1069 1070
		const char *task, const char *target)
{
J
jp9000 已提交
1071 1072
	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 已提交
1073

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

	return NULL;
}

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

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

	return NULL;
}

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

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

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

	return errorcode;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1187
		source = next_source;
1188
	}
1189

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

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

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

1203
	pthread_mutex_lock(mutex);
1204

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

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

	pthread_mutex_unlock(mutex);
1214 1215
}

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

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

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

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

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

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

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

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

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

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

	pthread_mutex_lock(mutex);

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

	pthread_mutex_unlock(mutex);
	return context;
}

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

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

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

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

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

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

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

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

1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341
	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;
	}
1342

1343
	return NULL;
1344 1345
}

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

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

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

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

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

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

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

J
jp9000 已提交
1382 1383 1384 1385 1386 1387 1388 1389
	obs->audio.user_volume = volume;
}

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

1390
static obs_source_t *obs_load_source_type(obs_data_t *source_data)
1391
{
1392
	obs_data_array_t *filters = obs_data_get_array(source_data, "filters");
1393
	obs_source_t *source;
J
jp9000 已提交
1394 1395
	const char   *name    = obs_data_get_string(source_data, "name");
	const char   *id      = obs_data_get_string(source_data, "id");
1396
	obs_data_t   *settings = obs_data_get_obj(source_data, "settings");
P
Palana 已提交
1397
	obs_data_t   *hotkeys  = obs_data_get_obj(source_data, "hotkeys");
1398
	double       volume;
1399 1400
	int64_t      sync;
	uint32_t     flags;
1401
	uint32_t     mixers;
1402

1403
	source = obs_source_create(id, name, settings, hotkeys);
1404

1405
	obs_data_release(hotkeys);
P
Palana 已提交
1406

1407
	obs_data_set_default_double(source_data, "volume", 1.0);
J
jp9000 已提交
1408
	volume = obs_data_get_double(source_data, "volume");
1409
	obs_source_set_volume(source, (float)volume);
1410

1411 1412 1413
	sync = obs_data_get_int(source_data, "sync");
	obs_source_set_sync_offset(source, sync);

1414 1415 1416 1417
	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);

1418
	obs_data_set_default_int(source_data, "flags", source->default_flags);
1419 1420 1421
	flags = (uint32_t)obs_data_get_int(source_data, "flags");
	obs_source_set_flags(source, flags);

1422 1423 1424 1425
	obs_data_set_default_bool(source_data, "enabled", true);
	obs_source_set_enabled(source,
			obs_data_get_bool(source_data, "enabled"));

J
jp9000 已提交
1426 1427 1428
	obs_data_set_default_bool(source_data, "muted", false);
	obs_source_set_muted(source, obs_data_get_bool(source_data, "muted"));

1429 1430 1431 1432 1433 1434 1435 1436
	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 已提交
1437 1438 1439 1440 1441 1442 1443 1444
	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"));

1445 1446 1447 1448 1449 1450 1451 1452
	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(
1453
					filter_data);
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464
			if (filter) {
				obs_source_filter_add(source, filter);
				obs_source_release(filter);
			}

			obs_data_release(filter_data);
		}

		obs_data_array_release(filters);
	}

1465 1466 1467 1468 1469
	obs_data_release(settings);

	return source;
}

1470 1471
obs_source_t *obs_load_source(obs_data_t *source_data)
{
1472
	return obs_load_source_type(source_data);
1473 1474
}

1475
void obs_load_sources(obs_data_array_t *array)
1476
{
1477 1478
	if (!obs) return;

1479 1480
	struct obs_core_data *data = &obs->data;
	DARRAY(obs_source_t*) sources;
1481 1482 1483
	size_t count;
	size_t i;

1484 1485
	da_init(sources);

1486
	count = obs_data_array_count(array);
1487
	da_reserve(sources, count);
1488

1489
	pthread_mutex_lock(&data->sources_mutex);
1490 1491

	for (i = 0; i < count; i++) {
1492 1493
		obs_data_t   *source_data = obs_data_array_item(array, i);
		obs_source_t *source      = obs_load_source(source_data);
1494

1495
		da_push_back(sources, &source);
1496 1497 1498 1499 1500

		obs_data_release(source_data);
	}

	/* tell sources that we want to load */
1501 1502 1503 1504
	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]);
1505

1506 1507 1508
	pthread_mutex_unlock(&data->sources_mutex);

	da_free(sources);
1509 1510
}

1511
obs_data_t *obs_save_source(obs_source_t *source)
1512
{
1513
	obs_data_array_t *filters = obs_data_array_create();
1514 1515
	obs_data_t *source_data = obs_data_create();
	obs_data_t *settings    = obs_source_get_settings(source);
P
Palana 已提交
1516 1517
	obs_data_t *hotkey_data = source->context.hotkey_data;
	obs_data_t *hotkeys;
1518
	float      volume      = obs_source_get_volume(source);
1519
	uint32_t   mixers      = obs_source_get_audio_mixers(source);
1520 1521
	int64_t    sync        = obs_source_get_sync_offset(source);
	uint32_t   flags       = obs_source_get_flags(source);
1522
	const char *name       = obs_source_get_name(source);
J
jp9000 已提交
1523
	const char *id         = obs_source_get_id(source);
1524
	bool       enabled     = obs_source_enabled(source);
J
jp9000 已提交
1525
	bool       muted       = obs_source_muted(source);
1526 1527
	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 已提交
1528 1529
	bool       push_to_talk= obs_source_push_to_talk_enabled(source);
	uint64_t   ptt_delay   = obs_source_get_push_to_talk_delay(source);
1530 1531

	obs_source_save(source);
P
Palana 已提交
1532 1533 1534 1535 1536 1537 1538
	hotkeys = obs_hotkeys_save_source(source);

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

J
jp9000 已提交
1540 1541 1542
	obs_data_set_string(source_data, "name",     name);
	obs_data_set_string(source_data, "id",       id);
	obs_data_set_obj   (source_data, "settings", settings);
1543
	obs_data_set_int   (source_data, "mixers",   mixers);
1544 1545
	obs_data_set_int   (source_data, "sync",     sync);
	obs_data_set_int   (source_data, "flags",    flags);
J
jp9000 已提交
1546
	obs_data_set_double(source_data, "volume",   volume);
1547
	obs_data_set_bool  (source_data, "enabled",  enabled);
J
jp9000 已提交
1548
	obs_data_set_bool  (source_data, "muted",    muted);
1549 1550
	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 已提交
1551 1552
	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 已提交
1553
	obs_data_set_obj   (source_data, "hotkeys",  hotkey_data);
1554

1555 1556 1557
	pthread_mutex_lock(&source->filter_mutex);

	if (source->filters.num) {
1558 1559
		for (size_t i = source->filters.num; i > 0; i--) {
			obs_source_t *filter = source->filters.array[i - 1];
1560 1561 1562 1563 1564 1565 1566 1567 1568 1569
			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);

1570
	obs_data_release(settings);
1571
	obs_data_array_release(filters);
1572 1573

	return source_data;
1574 1575
}

1576 1577
obs_data_array_t *obs_save_sources_filtered(obs_save_source_filter_cb cb,
		void *data_)
1578
{
1579 1580
	if (!obs) return NULL;

1581
	struct obs_core_data *data = &obs->data;
1582
	obs_data_array_t *array;
1583
	obs_source_t *source;
1584 1585 1586

	array = obs_data_array_create();

1587
	pthread_mutex_lock(&data->sources_mutex);
1588

1589
	source = data->first_source;
1590

1591
	while (source) {
1592
		if ((source->info.type != OBS_SOURCE_TYPE_FILTER) != 0 &&
1593
				cb(data_, source)) {
1594 1595 1596 1597 1598 1599 1600
			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;
1601 1602
	}

1603
	pthread_mutex_unlock(&data->sources_mutex);
1604 1605 1606 1607

	return array;
}

1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619
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);
}

1620 1621 1622 1623 1624
/* ensures that names are never blank */
static inline char *dup_name(const char *name)
{
	if (!name || !*name) {
		struct dstr unnamed = {0};
1625
		dstr_printf(&unnamed, "__unnamed%04lld",
1626 1627 1628 1629 1630 1631 1632 1633 1634 1635
				obs->data.unnamed_index++);

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

static inline bool obs_context_data_init_wrap(
		struct obs_context_data *context,
1636
		obs_data_t              *settings,
P
Palana 已提交
1637 1638
		const char              *name,
		obs_data_t              *hotkey_data)
1639 1640
{
	assert(context);
1641
	memset(context, 0, sizeof(*context));
1642

J
jp9000 已提交
1643 1644 1645 1646
	pthread_mutex_init_value(&context->rename_cache_mutex);
	if (pthread_mutex_init(&context->rename_cache_mutex, NULL) < 0)
		return false;

1647
	context->signals = signal_handler_create();
P
Palana 已提交
1648
	if (!context->signals)
1649 1650 1651 1652 1653 1654
		return false;

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

P
Palana 已提交
1655 1656 1657
	context->name        = dup_name(name);
	context->settings    = obs_data_newref(settings);
	context->hotkey_data = obs_data_newref(hotkey_data);
1658 1659 1660 1661 1662
	return true;
}

bool obs_context_data_init(
		struct obs_context_data *context,
1663
		obs_data_t              *settings,
P
Palana 已提交
1664 1665
		const char              *name,
		obs_data_t              *hotkey_data)
1666
{
P
Palana 已提交
1667
	if (obs_context_data_init_wrap(context, settings, name, hotkey_data)) {
1668 1669 1670 1671 1672 1673 1674 1675 1676
		return true;
	} else {
		obs_context_data_free(context);
		return false;
	}
}

void obs_context_data_free(struct obs_context_data *context)
{
P
Palana 已提交
1677
	obs_hotkeys_context_release(context);
1678 1679 1680 1681
	signal_handler_destroy(context->signals);
	proc_handler_destroy(context->procs);
	obs_data_release(context->settings);
	obs_context_data_remove(context);
J
jp9000 已提交
1682
	pthread_mutex_destroy(&context->rename_cache_mutex);
1683 1684
	bfree(context->name);

J
jp9000 已提交
1685 1686 1687 1688
	for (size_t i = 0; i < context->rename_cache.num; i++)
		bfree(context->rename_cache.array[i]);
	da_free(context->rename_cache);

1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715
	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);
1716 1717
		if (context->prev_next)
			*context->prev_next = context->next;
1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728
		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 已提交
1729 1730 1731 1732
	pthread_mutex_lock(&context->rename_cache_mutex);

	if (context->name)
		da_push_back(context->rename_cache, &context->name);
1733
	context->name = dup_name(name);
J
jp9000 已提交
1734 1735

	pthread_mutex_unlock(&context->rename_cache_mutex);
1736
}
1737 1738 1739 1740 1741 1742 1743 1744

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

	return obs->name_store;
}
1745 1746 1747 1748 1749

uint64_t obs_get_video_frame_time(void)
{
	return obs ? obs->video.video_time : 0;
}