obs.c 31.8 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 21
#include "callback/calldata.h"

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

25
struct obs_core *obs = NULL;
26

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

30 31
static inline void make_gs_init_data(struct gs_init_data *gid,
		struct obs_video_info *ovi)
J
jp9000 已提交
32
{
33
	memcpy(&gid->window, &ovi->window, sizeof(struct gs_window));
34 35
	gid->cx              = ovi->window_width;
	gid->cy              = ovi->window_height;
36 37 38 39 40 41
	gid->num_backbuffers = 2;
	gid->format          = GS_RGBA;
	gid->zsformat        = GS_ZS_NONE;
	gid->adapter         = ovi->adapter;
}

J
jp9000 已提交
42
static inline void make_video_info(struct video_output_info *vi,
43 44 45
		struct obs_video_info *ovi)
{
	vi->name    = "video";
46
	vi->format  = ovi->output_format;
47 48 49 50 51 52
	vi->fps_num = ovi->fps_num;
	vi->fps_den = ovi->fps_den;
	vi->width   = ovi->output_width;
	vi->height  = ovi->output_height;
}

J
jp9000 已提交
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 81 82 83 84 85 86 87 88
#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 已提交
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
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 已提交
117 118 119 120 121 122 123 124 125 126 127
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 已提交
128 129 130 131
		break;
	case VIDEO_FORMAT_NV12:
		set_nv12_sizes(ovi);
		break;
J
jp9000 已提交
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
	}
}

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++) {
149
		video->convert_textures[i] = gs_texture_create(
J
jp9000 已提交
150
				ovi->output_width, video->conversion_height,
151
				GS_RGBA, 1, NULL, GS_RENDER_TARGET);
J
jp9000 已提交
152 153 154 155 156 157 158 159

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

	return true;
}

160 161
static bool obs_init_textures(struct obs_video_info *ovi)
{
162
	struct obs_core_video *video = &obs->video;
163
	bool yuv = format_is_yuv(ovi->output_format);
J
jp9000 已提交
164 165
	uint32_t output_height = video->gpu_conversion ?
		video->conversion_height : ovi->output_height;
166 167 168
	size_t i;

	for (i = 0; i < NUM_TEXTURES; i++) {
169
		video->copy_surfaces[i] = gs_stagesurface_create(
J
jp9000 已提交
170
				ovi->output_width, output_height, GS_RGBA);
171 172 173 174

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

175
		video->render_textures[i] = gs_texture_create(
176
				ovi->base_width, ovi->base_height,
177
				GS_RGBA, 1, NULL, GS_RENDER_TARGET);
178 179 180 181

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

182
		video->output_textures[i] = gs_texture_create(
183
				ovi->output_width, ovi->output_height,
184
				GS_RGBA, 1, NULL, GS_RENDER_TARGET);
185 186 187

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

		if (yuv)
190
			obs_source_frame_init(&video->convert_frames[i],
191
					ovi->output_format,
192
					ovi->output_width,ovi->output_height);
193 194 195 196 197
	}

	return true;
}

198
static int obs_init_graphics(struct obs_video_info *ovi)
199
{
200
	struct obs_core_video *video = &obs->video;
201
	struct gs_init_data graphics_data;
202
	bool success = true;
203
	int errorcode;
J
jp9000 已提交
204

205 206 207 208
	make_gs_init_data(&graphics_data, ovi);

	errorcode = gs_create(&video->graphics, ovi->graphics_module,
			&graphics_data);
J
jp9000 已提交
209
	if (errorcode != GS_SUCCESS) {
210 211 212 213 214 215 216 217
		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 已提交
218 219
	}

220
	gs_enter_context(video->graphics);
J
jp9000 已提交
221

222
	char *filename = find_libobs_data_file("default.effect");
223
	video->default_effect = gs_effect_create_from_file(filename,
224 225 226 227
			NULL);
	bfree(filename);

	filename = find_libobs_data_file("solid.effect");
228
	video->solid_effect = gs_effect_create_from_file(filename,
229 230 231 232
			NULL);
	bfree(filename);

	filename = find_libobs_data_file("format_conversion.effect");
233
	video->conversion_effect = gs_effect_create_from_file(filename,
234 235 236 237 238 239 240 241 242
			NULL);
	bfree(filename);

	if (!video->default_effect)
		success = false;
	if (!video->solid_effect)
		success = false;
	if (!video->conversion_effect)
		success = false;
J
jp9000 已提交
243

244
	gs_leave_context();
245
	return success ? OBS_VIDEO_SUCCESS : OBS_VIDEO_FAIL;
J
jp9000 已提交
246 247
}

248
static int obs_init_video(struct obs_video_info *ovi)
J
jp9000 已提交
249
{
250
	struct obs_core_video *video = &obs->video;
J
jp9000 已提交
251
	struct video_output_info vi;
252 253 254
	int errorcode;

	make_video_info(&vi, ovi);
255 256 257 258 259 260
	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;

J
jp9000 已提交
261
	errorcode = video_output_open(&video->video, &vi);
262 263

	if (errorcode != VIDEO_OUTPUT_SUCCESS) {
264
		if (errorcode == VIDEO_OUTPUT_INVALIDPARAM) {
265
			blog(LOG_ERROR, "Invalid video parameters specified");
266 267
			return OBS_VIDEO_INVALID_PARAM;
		} else {
268
			blog(LOG_ERROR, "Could not open video output");
269 270
		}
		return OBS_VIDEO_FAIL;
271 272
	}

273
	if (!obs_display_init(&video->main_display, NULL))
274
		return OBS_VIDEO_FAIL;
275 276 277 278

	video->main_display.cx = ovi->window_width;
	video->main_display.cy = ovi->window_height;

279
	gs_enter_context(video->graphics);
280 281

	if (ovi->gpu_conversion && !obs_init_gpu_conversion(ovi))
282
		return OBS_VIDEO_FAIL;
283
	if (!obs_init_textures(ovi))
284
		return OBS_VIDEO_FAIL;
285

286
	gs_leave_context();
287

288 289 290
	errorcode = pthread_create(&video->video_thread, NULL,
			obs_video_thread, obs);
	if (errorcode != 0)
291
		return OBS_VIDEO_FAIL;
J
jp9000 已提交
292

293
	video->thread_initialized = true;
294
	return OBS_VIDEO_SUCCESS;
J
jp9000 已提交
295 296
}

297
static void stop_video(void)
J
jp9000 已提交
298
{
299
	struct obs_core_video *video = &obs->video;
300
	void *thread_retval;
J
jp9000 已提交
301

302 303
	if (video->video) {
		video_output_stop(video->video);
304
		if (video->thread_initialized) {
305
			pthread_join(video->video_thread, &thread_retval);
306 307
			video->thread_initialized = false;
		}
308
	}
309

310 311 312 313 314 315 316 317
}

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

	if (video->video) {
		obs_display_free(&video->main_display);
318

319
		video_output_close(video->video);
320 321
		video->video = NULL;

322 323
		if (!video->graphics)
			return;
324

325
		gs_enter_context(video->graphics);
326

327
		if (video->mapped_surface) {
328
			gs_stagesurface_unmap(video->mapped_surface);
329 330
			video->mapped_surface = NULL;
		}
331

332
		for (size_t i = 0; i < NUM_TEXTURES; i++) {
333 334 335 336
			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]);
337
			obs_source_frame_free(&video->convert_frames[i]);
338

J
jp9000 已提交
339 340 341 342
			video->copy_surfaces[i]    = NULL;
			video->render_textures[i]  = NULL;
			video->convert_textures[i] = NULL;
			video->output_textures[i]  = NULL;
343
		}
344

345
		gs_leave_context();
346 347 348 349 350 351 352 353 354 355

		video->cur_texture = 0;
	}
}

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

	if (video->graphics) {
356
		gs_enter_context(video->graphics);
357

358 359 360
		gs_effect_destroy(video->default_effect);
		gs_effect_destroy(video->solid_effect);
		gs_effect_destroy(video->conversion_effect);
361
		video->default_effect = NULL;
362

363
		gs_leave_context();
364 365

		gs_destroy(video->graphics);
366
		video->graphics = NULL;
367
	}
J
jp9000 已提交
368 369
}

J
jp9000 已提交
370
static bool obs_init_audio(struct audio_output_info *ai)
J
jp9000 已提交
371
{
372
	struct obs_core_audio *audio = &obs->audio;
373
	int errorcode;
J
jp9000 已提交
374

375
	/* TODO: sound subsystem */
J
jp9000 已提交
376

J
jp9000 已提交
377 378 379
	audio->user_volume    = 1.0f;
	audio->present_volume = 1.0f;

J
jp9000 已提交
380
	errorcode = audio_output_open(&audio->audio, ai);
381 382
	if (errorcode == AUDIO_OUTPUT_SUCCESS)
		return true;
P
Palana 已提交
383
	else if (errorcode == AUDIO_OUTPUT_INVALIDPARAM)
384 385 386 387 388 389 390 391 392
		blog(LOG_ERROR, "Invalid audio parameters specified");
	else
		blog(LOG_ERROR, "Could not open audio output");

	return false;
}

static void obs_free_audio(void)
{
393
	struct obs_core_audio *audio = &obs->audio;
394 395 396
	if (audio->audio)
		audio_output_close(audio->audio);

397
	memset(audio, 0, sizeof(struct obs_core_audio));
398 399 400 401
}

static bool obs_init_data(void)
{
402
	struct obs_core_data *data = &obs->data;
403
	pthread_mutexattr_t attr;
404

405 406
	assert(data != NULL);

407 408
	pthread_mutex_init_value(&obs->data.displays_mutex);

409
	if (pthread_mutexattr_init(&attr) != 0)
410
		return false;
411 412
	if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
		goto fail;
413 414
	if (pthread_mutex_init(&data->user_sources_mutex, &attr) != 0)
		goto fail;
415 416 417 418
	if (pthread_mutex_init(&data->sources_mutex, &attr) != 0)
		goto fail;
	if (pthread_mutex_init(&data->displays_mutex, &attr) != 0)
		goto fail;
419 420 421 422
	if (pthread_mutex_init(&data->outputs_mutex, &attr) != 0)
		goto fail;
	if (pthread_mutex_init(&data->encoders_mutex, &attr) != 0)
		goto fail;
423 424
	if (pthread_mutex_init(&data->services_mutex, &attr) != 0)
		goto fail;
J
jp9000 已提交
425
	if (!obs_view_init(&data->main_view))
426
		goto fail;
J
jp9000 已提交
427

428
	data->valid = true;
429 430 431

fail:
	pthread_mutexattr_destroy(&attr);
432
	return data->valid;
J
jp9000 已提交
433 434
}

435 436 437 438 439 440 441 442 443 444 445 446
#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)

447
static void obs_free_data(void)
J
jp9000 已提交
448
{
449
	struct obs_core_data *data = &obs->data;
J
jp9000 已提交
450

451 452
	data->valid = false;

J
jp9000 已提交
453
	obs_view_free(&data->main_view);
454

455
	blog(LOG_INFO, "Freeing OBS context data");
456

457 458 459 460 461 462 463
	if (data->user_sources.num)
		blog(LOG_INFO, "\t%d user source(s) were remaining",
				(int)data->user_sources.num);

	while (data->user_sources.num)
		obs_source_remove(data->user_sources.array[0]);
	da_free(data->user_sources);
464

465 466 467 468 469 470 471
	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);

	pthread_mutex_destroy(&data->user_sources_mutex);
472 473 474 475
	pthread_mutex_destroy(&data->sources_mutex);
	pthread_mutex_destroy(&data->displays_mutex);
	pthread_mutex_destroy(&data->outputs_mutex);
	pthread_mutex_destroy(&data->encoders_mutex);
476
	pthread_mutex_destroy(&data->services_mutex);
477
}
J
jp9000 已提交
478

479 480 481 482 483 484 485 486 487
static const char *obs_signals[] = {
	"void source_create(ptr source)",
	"void source_destroy(ptr source)",
	"void source_add(ptr source)",
	"void source_remove(ptr source)",
	"void source_activate(ptr source)",
	"void source_deactivate(ptr source)",
	"void source_show(ptr source)",
	"void source_hide(ptr source)",
J
jp9000 已提交
488
	"void source_rename(ptr source, string new_name, string prev_name)",
489
	"void source_volume(ptr source, in out float volume)",
490 491
	"void source_volume_level(ptr source, float level, float magnitude, "
		"float peak)",
492 493 494 495 496 497 498

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

	NULL
};

499 500 501 502 503 504 505
static inline bool obs_init_handlers(void)
{
	obs->signals = signal_handler_create();
	if (!obs->signals)
		return false;

	obs->procs   = proc_handler_create();
506 507 508 509
	if (!obs->procs)
		return false;

	return signal_handler_add_array(obs->signals, obs_signals);
510 511
}

512 513
extern const struct obs_source_info scene_info;

J
jp9000 已提交
514 515
extern void log_system_info(void);

516
static bool obs_init(const char *locale)
517
{
518
	obs = bzalloc(sizeof(struct obs_core));
519

J
jp9000 已提交
520 521
	log_system_info();

522 523 524 525 526
	if (!obs_init_data())
		return false;
	if (!obs_init_handlers())
		return false;

527
	obs->locale = bstrdup(locale);
528
	obs_register_source(&scene_info);
J
jp9000 已提交
529
	add_default_module_paths();
530
	return true;
J
jp9000 已提交
531 532
}

533
bool obs_startup(const char *locale)
J
jp9000 已提交
534
{
535
	bool success;
J
jp9000 已提交
536

537
	if (obs) {
J
jp9000 已提交
538
		blog(LOG_WARNING, "Tried to call obs_startup more than once");
539 540 541
		return false;
	}

542
	success = obs_init(locale);
543 544 545 546
	if (!success)
		obs_shutdown();

	return success;
J
jp9000 已提交
547 548
}

549
void obs_shutdown(void)
J
jp9000 已提交
550
{
J
jp9000 已提交
551 552
	struct obs_module *module;

J
jp9000 已提交
553 554 555 556 557
	if (!obs)
		return;

	da_free(obs->input_types);
	da_free(obs->filter_types);
558
	da_free(obs->encoder_types);
J
jp9000 已提交
559 560
	da_free(obs->transition_types);
	da_free(obs->output_types);
561
	da_free(obs->service_types);
J
jp9000 已提交
562 563
	da_free(obs->modal_ui_callbacks);
	da_free(obs->modeless_ui_callbacks);
J
jp9000 已提交
564

565 566
	stop_video();

567 568
	obs_free_data();
	obs_free_video();
569
	obs_free_graphics();
570
	obs_free_audio();
571 572
	proc_handler_destroy(obs->procs);
	signal_handler_destroy(obs->signals);
J
jp9000 已提交
573

J
jp9000 已提交
574 575 576 577 578 579 580 581 582 583 584
	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 已提交
585

586
	bfree(obs->locale);
J
jp9000 已提交
587
	bfree(obs);
588 589 590
	obs = NULL;
}

591 592 593 594 595
bool obs_initialized(void)
{
	return obs != NULL;
}

596 597 598 599 600
uint32_t obs_get_version(void)
{
	return LIBOBS_API_VER;
}

601 602
void obs_set_locale(const char *locale)
{
J
jp9000 已提交
603
	struct obs_module *module;
604 605 606 607 608 609
	if (!obs)
		return;

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

J
jp9000 已提交
611 612
	module = obs->first_module;
	while (module) {
613 614
		if (module->set_locale)
			module->set_locale(locale);
J
jp9000 已提交
615 616

		module = module->next;
617
	}
618 619 620 621 622 623 624
}

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

625 626 627 628
#define OBS_SIZE_MIN 2
#define OBS_SIZE_MAX (32 * 1024)

static inline bool size_valid(uint32_t width, uint32_t height)
629
{
630 631 632 633 634 635 636
	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;
637 638 639

	/* don't allow changing of video settings if active. */
	if (obs->video.video && video_output_active(obs->video.video))
640 641 642 643 644
		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;
645

646
	struct obs_core_video *video = &obs->video;
647

648
	stop_video();
649
	obs_free_video();
650

651 652
	if (!ovi) {
		obs_free_graphics();
653
		return OBS_VIDEO_SUCCESS;
654 655
	}

656 657 658 659
	/* align to multiple-of-two and SSE alignment sizes */
	ovi->output_width  &= 0xFFFFFFFC;
	ovi->output_height &= 0xFFFFFFFE;

660 661 662 663 664
	if (!video->graphics) {
		int errorcode = obs_init_graphics(ovi);
		if (errorcode != OBS_VIDEO_SUCCESS)
			return errorcode;
	}
665

J
jp9000 已提交
666 667 668 669 670 671 672 673
	blog(LOG_INFO, "video settings reset:\n"
	               "\tbase resolution:   %dx%d\n"
	               "\toutput resolution: %dx%d\n"
	               "\tfps:               %d/%d",
	               ovi->base_width, ovi->base_height,
	               ovi->output_width, ovi->output_height,
	               ovi->fps_num, ovi->fps_den);

674
	return obs_init_video(ovi);
675 676
}

J
jp9000 已提交
677
bool obs_reset_audio(struct audio_output_info *ai)
J
jp9000 已提交
678
{
679 680 681 682 683 684
	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 已提交
685
	obs_free_audio();
686 687 688
	if(!ai)
		return true;

J
jp9000 已提交
689 690 691 692 693 694 695 696
	blog(LOG_INFO, "audio settings reset:\n"
	               "\tsamples per sec: %d\n"
	               "\tspeakers:        %d\n"
	               "\tbuffering (ms):  %d\n",
	               (int)ai->samples_per_sec,
	               (int)ai->speakers,
	               (int)ai->buffer_ms);

J
jp9000 已提交
697
	return obs_init_audio(ai);
J
jp9000 已提交
698 699
}

700 701
bool obs_get_video_info(struct obs_video_info *ovi)
{
702
	struct obs_core_video *video = &obs->video;
J
jp9000 已提交
703
	const struct video_output_info *info;
704

J
jp9000 已提交
705
	if (!obs || !video->graphics)
706 707
		return false;

708
	info = video_output_get_info(video->video);
709 710 711 712 713 714

	memset(ovi, 0, sizeof(struct obs_video_info));
	ovi->base_width    = video->base_width;
	ovi->base_height   = video->base_height;
	ovi->output_width  = info->width;
	ovi->output_height = info->height;
715
	ovi->output_format = info->format;
716 717 718 719 720 721
	ovi->fps_num       = info->fps_num;
	ovi->fps_den       = info->fps_den;

	return true;
}

J
jp9000 已提交
722
bool obs_get_audio_info(struct audio_output_info *aoi)
723
{
724
	struct obs_core_audio *audio = &obs->audio;
J
jp9000 已提交
725
	const struct audio_output_info *info;
726

J
jp9000 已提交
727
	if (!obs || !audio->audio)
728 729
		return false;

730
	info = audio_output_get_info(audio->audio);
J
jp9000 已提交
731
	memcpy(aoi, info, sizeof(struct audio_output_info));
732 733 734 735

	return true;
}

736
bool obs_enum_input_types(size_t idx, const char **id)
J
jp9000 已提交
737
{
J
jp9000 已提交
738 739
	if (!obs) return false;

J
jp9000 已提交
740 741
	if (idx >= obs->input_types.num)
		return false;
742
	*id = obs->input_types.array[idx].id;
J
jp9000 已提交
743 744 745
	return true;
}

746
bool obs_enum_filter_types(size_t idx, const char **id)
J
jp9000 已提交
747
{
J
jp9000 已提交
748 749
	if (!obs) return false;

J
jp9000 已提交
750 751
	if (idx >= obs->filter_types.num)
		return false;
752
	*id = obs->filter_types.array[idx].id;
J
jp9000 已提交
753 754 755
	return true;
}

756
bool obs_enum_transition_types(size_t idx, const char **id)
J
jp9000 已提交
757
{
J
jp9000 已提交
758 759
	if (!obs) return false;

J
jp9000 已提交
760 761
	if (idx >= obs->transition_types.num)
		return false;
762
	*id = obs->transition_types.array[idx].id;
J
jp9000 已提交
763 764 765
	return true;
}

766
bool obs_enum_output_types(size_t idx, const char **id)
J
jp9000 已提交
767
{
J
jp9000 已提交
768 769
	if (!obs) return false;

J
jp9000 已提交
770 771
	if (idx >= obs->output_types.num)
		return false;
772
	*id = obs->output_types.array[idx].id;
J
jp9000 已提交
773 774 775
	return true;
}

776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
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 已提交
796
void obs_enter_graphics(void)
J
jp9000 已提交
797
{
J
jp9000 已提交
798
	if (obs && obs->video.graphics)
799
		gs_enter_context(obs->video.graphics);
J
jp9000 已提交
800 801 802 803 804
}

void obs_leave_graphics(void)
{
	if (obs && obs->video.graphics)
805
		gs_leave_context();
J
jp9000 已提交
806 807
}

808
audio_t *obs_get_audio(void)
J
jp9000 已提交
809 810 811 812
{
	return (obs != NULL) ? obs->audio.audio : NULL;
}

813
video_t *obs_get_video(void)
J
jp9000 已提交
814
{
J
jp9000 已提交
815
	return (obs != NULL) ? obs->video.video : NULL;
J
jp9000 已提交
816 817
}

J
jp9000 已提交
818
/* TODO: optimize this later so it's not just O(N) string lookups */
J
jp9000 已提交
819
static inline struct obs_modal_ui *get_modal_ui_callback(const char *id,
J
jp9000 已提交
820 821
		const char *task, const char *target)
{
J
jp9000 已提交
822 823
	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 已提交
824

J
jp9000 已提交
825
		if (strcmp(callback->id,     id)     == 0 &&
826 827
		    strcmp(callback->task,   task)   == 0 &&
		    strcmp(callback->target, target) == 0)
J
jp9000 已提交
828 829 830 831 832 833
			return callback;
	}

	return NULL;
}

J
jp9000 已提交
834
static inline struct obs_modeless_ui *get_modeless_ui_callback(const char *id,
J
jp9000 已提交
835 836
		const char *task, const char *target)
{
J
jp9000 已提交
837
	for (size_t i = 0; i < obs->modeless_ui_callbacks.num; i++) {
838
		struct obs_modeless_ui *callback;
J
jp9000 已提交
839
		callback = obs->modeless_ui_callbacks.array+i;
J
jp9000 已提交
840

J
jp9000 已提交
841
		if (strcmp(callback->id,     id)     == 0 &&
842 843
		    strcmp(callback->task,   task)   == 0 &&
		    strcmp(callback->target, target) == 0)
J
jp9000 已提交
844 845 846 847 848 849
			return callback;
	}

	return NULL;
}

850
int obs_exec_ui(const char *name, const char *task, const char *target,
J
jp9000 已提交
851 852
		void *data, void *ui_data)
{
853
	struct obs_modal_ui *callback;
J
jp9000 已提交
854 855
	int errorcode = OBS_UI_NOTFOUND;

J
jp9000 已提交
856 857
	if (!obs) return errorcode;

858
	callback = get_modal_ui_callback(name, task, target);
J
jp9000 已提交
859
	if (callback) {
J
jp9000 已提交
860
		bool success = callback->exec(data, ui_data);
J
jp9000 已提交
861 862 863 864 865 866
		errorcode = success ? OBS_UI_SUCCESS : OBS_UI_CANCEL;
	}

	return errorcode;
}

J
jp9000 已提交
867 868 869
void *obs_create_ui(const char *name, const char *task, const char *target,
		void *data, void *ui_data)
{
870
	struct obs_modeless_ui *callback;
J
jp9000 已提交
871

J
jp9000 已提交
872 873
	if (!obs) return NULL;

J
jp9000 已提交
874
	callback = get_modeless_ui_callback(name, task, target);
J
jp9000 已提交
875
	return callback ? callback->create(data, ui_data) : NULL;
J
jp9000 已提交
876 877
}

878
bool obs_add_source(obs_source_t *source)
879
{
880 881
	struct calldata params = {0};

J
jp9000 已提交
882
	if (!obs) return false;
883
	if (!source) return false;
J
jp9000 已提交
884

885
	pthread_mutex_lock(&obs->data.sources_mutex);
886
	da_push_back(obs->data.user_sources, &source);
887 888 889
	obs_source_addref(source);
	pthread_mutex_unlock(&obs->data.sources_mutex);

890
	calldata_set_ptr(&params, "source", source);
891
	signal_handler_signal(obs->signals, "source_add", &params);
892 893
	calldata_free(&params);

894 895 896
	return true;
}

897
obs_source_t *obs_get_output_source(uint32_t channel)
898
{
J
jp9000 已提交
899
	if (!obs) return NULL;
900
	return obs_view_get_source(&obs->data.main_view, channel);
J
jp9000 已提交
901 902
}

903
void obs_set_output_source(uint32_t channel, obs_source_t *source)
J
jp9000 已提交
904
{
905 906 907 908 909
	assert(channel < MAX_CHANNELS);

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

910
	struct obs_source *prev_source;
J
jp9000 已提交
911
	struct obs_view *view = &obs->data.main_view;
J
jp9000 已提交
912
	struct calldata params = {0};
913

J
jp9000 已提交
914
	pthread_mutex_lock(&view->channels_mutex);
915

J
jp9000 已提交
916 917
	obs_source_addref(source);

J
jp9000 已提交
918
	prev_source = view->channels[channel];
J
jp9000 已提交
919

920 921 922
	calldata_set_int(&params, "channel", channel);
	calldata_set_ptr(&params, "prev_source", prev_source);
	calldata_set_ptr(&params, "source", source);
923
	signal_handler_signal(obs->signals, "channel_change", &params);
924
	calldata_get_ptr(&params, "source", &source);
J
jp9000 已提交
925 926
	calldata_free(&params);

J
jp9000 已提交
927
	view->channels[channel] = source;
928

J
jp9000 已提交
929 930 931
	pthread_mutex_unlock(&view->channels_mutex);

	if (source)
932
		obs_source_activate(source, MAIN_VIEW);
933 934

	if (prev_source) {
935
		obs_source_deactivate(prev_source, MAIN_VIEW);
936
		obs_source_release(prev_source);
937
	}
J
jp9000 已提交
938
}
939

940
void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param)
941
{
J
jp9000 已提交
942 943
	if (!obs) return;

944
	pthread_mutex_lock(&obs->data.user_sources_mutex);
945

946 947 948
	for (size_t i = 0; i < obs->data.user_sources.num; i++) {
		struct obs_source *source = obs->data.user_sources.array[i];
		if (!enum_proc(param, source))
949
			break;
950
	}
951

952
	pthread_mutex_unlock(&obs->data.user_sources_mutex);
953 954
}

955 956
static inline void obs_enum(void *pstart, pthread_mutex_t *mutex, void *proc,
		void *param)
957
{
958 959
	struct obs_context_data **start = pstart, *context;
	bool (*enum_proc)(void*, void*) = proc;
960

961 962 963
	assert(start);
	assert(mutex);
	assert(enum_proc);
J
jp9000 已提交
964

965
	pthread_mutex_lock(mutex);
966

967 968 969
	context = *start;
	while (context) {
		if (!enum_proc(param, context))
970 971
			break;

972 973 974 975
		context = context->next;
	}

	pthread_mutex_unlock(mutex);
976 977
}

978
void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t*), void *param)
979
{
J
jp9000 已提交
980
	if (!obs) return;
981 982 983
	obs_enum(&obs->data.first_output, &obs->data.outputs_mutex,
			enum_proc, param);
}
J
jp9000 已提交
984

985
void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t*), void *param)
986 987 988 989 990
{
	if (!obs) return;
	obs_enum(&obs->data.first_encoder, &obs->data.encoders_mutex,
			enum_proc, param);
}
991

992
void obs_enum_services(bool (*enum_proc)(void*, obs_service_t*), void *param)
993 994 995 996
{
	if (!obs) return;
	obs_enum(&obs->data.first_service, &obs->data.services_mutex,
			enum_proc, param);
997
}
998

999
obs_source_t *obs_get_source_by_name(const char *name)
1000
{
1001
	struct obs_core_data *data = &obs->data;
1002 1003 1004
	struct obs_source *source = NULL;
	size_t i;

J
jp9000 已提交
1005 1006
	if (!obs) return NULL;

1007
	pthread_mutex_lock(&data->user_sources_mutex);
1008

1009 1010 1011
	for (i = 0; i < data->user_sources.num; i++) {
		struct obs_source *cur_source = data->user_sources.array[i];
		if (strcmp(cur_source->context.name, name) == 0) {
1012
			source = cur_source;
1013
			obs_source_addref(source);
1014 1015 1016 1017
			break;
		}
	}

1018
	pthread_mutex_unlock(&data->user_sources_mutex);
1019 1020
	return source;
}
1021

1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
static inline void *get_context_by_name(void *vfirst, const char *name,
		pthread_mutex_t *mutex)
{
	struct obs_context_data **first = vfirst;
	struct obs_context_data *context;

	pthread_mutex_lock(mutex);

	context = *first;
	while (context) {
		if (strcmp(context->name, name) == 0)
			break;
		context = context->next;
	}

	pthread_mutex_unlock(mutex);
	return context;
}

1041
obs_output_t *obs_get_output_by_name(const char *name)
1042 1043 1044 1045 1046 1047
{
	if (!obs) return NULL;
	return get_context_by_name(&obs->data.first_output, name,
			&obs->data.outputs_mutex);
}

1048
obs_encoder_t *obs_get_encoder_by_name(const char *name)
1049 1050 1051 1052 1053 1054
{
	if (!obs) return NULL;
	return get_context_by_name(&obs->data.first_encoder, name,
			&obs->data.encoders_mutex);
}

1055
obs_service_t *obs_get_service_by_name(const char *name)
1056 1057 1058 1059 1060 1061
{
	if (!obs) return NULL;
	return get_context_by_name(&obs->data.first_service, name,
			&obs->data.services_mutex);
}

1062
gs_effect_t *obs_get_default_effect(void)
1063
{
1064
	if (!obs) return NULL;
1065 1066
	return obs->video.default_effect;
}
1067

1068
gs_effect_t *obs_get_solid_effect(void)
1069 1070 1071 1072 1073
{
	if (!obs) return NULL;
	return obs->video.solid_effect;
}

1074
signal_handler_t *obs_get_signal_handler(void)
1075
{
1076
	if (!obs) return NULL;
1077 1078 1079
	return obs->signals;
}

1080
proc_handler_t *obs_get_proc_handler(void)
1081
{
1082
	if (!obs) return NULL;
1083 1084
	return obs->procs;
}
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109

void obs_add_draw_callback(
		void (*draw)(void *param, uint32_t cx, uint32_t cy),
		void *param)
{
	if (!obs) return;

	obs_display_add_draw_callback(&obs->video.main_display, draw, param);
}

void obs_remove_draw_callback(
		void (*draw)(void *param, uint32_t cx, uint32_t cy),
		void *param)
{
	if (!obs) return;

	obs_display_remove_draw_callback(&obs->video.main_display, draw, param);
}

void obs_resize(uint32_t cx, uint32_t cy)
{
	if (!obs || !obs->video.video || !obs->video.graphics) return;
	obs_display_resize(&obs->video.main_display, cx, cy);
}

J
jp9000 已提交
1110
void obs_render_main_view(void)
1111 1112
{
	if (!obs) return;
J
jp9000 已提交
1113
	obs_view_render(&obs->data.main_view);
1114
}
J
jp9000 已提交
1115 1116 1117

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

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

1122
	calldata_set_float(&data, "volume", volume);
1123 1124
	signal_handler_signal(obs->signals, "master_volume", &data);
	volume = (float)calldata_float(&data, "volume");
J
jp9000 已提交
1125 1126
	calldata_free(&data);

J
jp9000 已提交
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
	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;
}
1145

1146
obs_source_t *obs_load_source(obs_data_t *source_data)
1147
{
1148
	obs_source_t *source;
J
jp9000 已提交
1149 1150
	const char   *name    = obs_data_get_string(source_data, "name");
	const char   *id      = obs_data_get_string(source_data, "id");
1151
	obs_data_t   *settings = obs_data_get_obj(source_data, "settings");
1152 1153 1154 1155 1156
	double       volume;

	source = obs_source_create(OBS_SOURCE_TYPE_INPUT, id, name, settings);

	obs_data_set_default_double(source_data, "volume", 1.0);
J
jp9000 已提交
1157
	volume = obs_data_get_double(source_data, "volume");
1158
	obs_source_set_volume(source, (float)volume);
1159 1160 1161 1162 1163 1164

	obs_data_release(settings);

	return source;
}

1165
void obs_load_sources(obs_data_array_t *array)
1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
{
	size_t count;
	size_t i;

	if (!obs) return;

	count = obs_data_array_count(array);

	pthread_mutex_lock(&obs->data.user_sources_mutex);

	for (i = 0; i < count; i++) {
1177 1178
		obs_data_t   *source_data = obs_data_array_item(array, i);
		obs_source_t *source      = obs_load_source(source_data);
1179 1180 1181

		obs_add_source(source);

1182
		obs_source_release(source);
1183 1184 1185 1186 1187 1188 1189 1190 1191 1192
		obs_data_release(source_data);
	}

	/* tell sources that we want to load */
	for (i = 0; i < obs->data.user_sources.num; i++)
		obs_source_load(obs->data.user_sources.array[i]);

	pthread_mutex_unlock(&obs->data.user_sources_mutex);
}

1193
obs_data_t *obs_save_source(obs_source_t *source)
1194
{
1195 1196
	obs_data_t *source_data = obs_data_create();
	obs_data_t *settings    = obs_source_get_settings(source);
1197 1198
	float      volume      = obs_source_get_volume(source);
	const char *name       = obs_source_get_name(source);
J
jp9000 已提交
1199
	const char *id         = obs_source_get_id(source);
1200 1201

	obs_source_save(source);
1202

J
jp9000 已提交
1203 1204 1205 1206
	obs_data_set_string(source_data, "name",     name);
	obs_data_set_string(source_data, "id",       id);
	obs_data_set_obj   (source_data, "settings", settings);
	obs_data_set_double(source_data, "volume",   volume);
1207 1208

	obs_data_release(settings);
1209 1210

	return source_data;
1211 1212
}

1213
obs_data_array_t *obs_save_sources(void)
1214
{
1215
	obs_data_array_t *array;
1216 1217 1218 1219 1220 1221 1222 1223 1224
	size_t i;

	if (!obs) return NULL;

	array = obs_data_array_create();

	pthread_mutex_lock(&obs->data.user_sources_mutex);

	for (i = 0; i < obs->data.user_sources.num; i++) {
1225 1226
		obs_source_t *source      = obs->data.user_sources.array[i];
		obs_data_t   *source_data = obs_save_source(source);
1227 1228 1229

		obs_data_array_push_back(array, source_data);
		obs_data_release(source_data);
1230 1231 1232 1233 1234 1235 1236
	}

	pthread_mutex_unlock(&obs->data.user_sources_mutex);

	return array;
}

1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
/* ensures that names are never blank */
static inline char *dup_name(const char *name)
{
	if (!name || !*name) {
		struct dstr unnamed = {0};
		dstr_printf(&unnamed, "__unnamed%004lld",
				obs->data.unnamed_index++);

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

static inline bool obs_context_data_init_wrap(
		struct obs_context_data *context,
1253
		obs_data_t              *settings,
1254 1255 1256
		const char              *name)
{
	assert(context);
1257
	memset(context, 0, sizeof(*context));
1258

J
jp9000 已提交
1259 1260 1261 1262
	pthread_mutex_init_value(&context->rename_cache_mutex);
	if (pthread_mutex_init(&context->rename_cache_mutex, NULL) < 0)
		return false;

1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
	context->signals = signal_handler_create();
	if (!context)
		return false;

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

	context->name     = dup_name(name);
	context->settings = obs_data_newref(settings);
	return true;
}

bool obs_context_data_init(
		struct obs_context_data *context,
1278
		obs_data_t              *settings,
1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294
		const char              *name)
{
	if (obs_context_data_init_wrap(context, settings, name)) {
		return true;
	} else {
		obs_context_data_free(context);
		return false;
	}
}

void obs_context_data_free(struct obs_context_data *context)
{
	signal_handler_destroy(context->signals);
	proc_handler_destroy(context->procs);
	obs_data_release(context->settings);
	obs_context_data_remove(context);
J
jp9000 已提交
1295
	pthread_mutex_destroy(&context->rename_cache_mutex);
1296 1297
	bfree(context->name);

J
jp9000 已提交
1298 1299 1300 1301
	for (size_t i = 0; i < context->rename_cache.num; i++)
		bfree(context->rename_cache.array[i]);
	da_free(context->rename_cache);

1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328
	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);
1329 1330
		if (context->prev_next)
			*context->prev_next = context->next;
1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341
		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 已提交
1342 1343 1344 1345
	pthread_mutex_lock(&context->rename_cache_mutex);

	if (context->name)
		da_push_back(context->rename_cache, &context->name);
1346
	context->name = dup_name(name);
J
jp9000 已提交
1347 1348

	pthread_mutex_unlock(&context->rename_cache_mutex);
1349
}