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

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

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

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

J
jp9000 已提交
18
#include "util/threading.h"
J
jp9000 已提交
19 20 21
#include "graphics/math-defs.h"
#include "obs-scene.h"

22 23 24 25 26 27
static const char *obs_scene_signals[] = {
	"void item_add(ptr scene, ptr item)",
	"void item_remove(ptr scene, ptr item)",
	NULL
};

J
jp9000 已提交
28
static inline void signal_item_remove(struct obs_scene_item *item)
J
jp9000 已提交
29
{
J
jp9000 已提交
30 31 32 33
	struct calldata params = {0};
	calldata_setptr(&params, "scene", item->parent);
	calldata_setptr(&params, "item", item);

34 35
	signal_handler_signal(item->parent->source->context.signals,
			"item_remove", &params);
J
jp9000 已提交
36
	calldata_free(&params);
J
jp9000 已提交
37 38
}

39 40
static const char *scene_getname(const char *locale)
{
41
	/* TODO: locale */
J
jp9000 已提交
42
	UNUSED_PARAMETER(locale);
43
	return "Scene";
44 45
}

46
static void *scene_create(obs_data_t settings, struct obs_source *source)
J
jp9000 已提交
47
{
P
Palana 已提交
48
	pthread_mutexattr_t attr;
J
jp9000 已提交
49
	struct obs_scene *scene = bmalloc(sizeof(struct obs_scene));
50 51 52
	scene->source     = source;
	scene->first_item = NULL;

53 54 55
	signal_handler_add_array(obs_source_signalhandler(source),
			obs_scene_signals);

P
Palana 已提交
56 57 58 59 60
	if (pthread_mutexattr_init(&attr) != 0)
		goto fail;
	if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
		goto fail;
	if (pthread_mutex_init(&scene->mutex, &attr) != 0) {
61
		blog(LOG_ERROR, "scene_create: Couldn't initialize mutex");
P
Palana 已提交
62
		goto fail;
63
	}
J
jp9000 已提交
64

J
jp9000 已提交
65
	UNUSED_PARAMETER(settings);
J
jp9000 已提交
66
	return scene;
P
Palana 已提交
67 68 69 70 71

fail:
	pthread_mutexattr_destroy(&attr);
	bfree(scene);
	return NULL;
J
jp9000 已提交
72 73
}

74
static void remove_all_items(struct obs_scene *scene)
J
jp9000 已提交
75
{
J
jp9000 已提交
76
	struct obs_scene_item *item;
J
jp9000 已提交
77 78

	pthread_mutex_lock(&scene->mutex);
J
jp9000 已提交
79

J
jp9000 已提交
80 81
	item = scene->first_item;

82 83 84 85
	while (item) {
		struct obs_scene_item *del_item = item;
		item = item->next;

J
jp9000 已提交
86
		obs_sceneitem_remove(del_item);
87
	}
J
jp9000 已提交
88

J
jp9000 已提交
89
	pthread_mutex_unlock(&scene->mutex);
90 91 92 93 94
}

static void scene_destroy(void *data)
{
	struct obs_scene *scene = data;
J
jp9000 已提交
95

96
	remove_all_items(scene);
97
	pthread_mutex_destroy(&scene->mutex);
J
jp9000 已提交
98 99 100
	bfree(scene);
}

101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
static void scene_enum_sources(void *data,
		obs_source_enum_proc_t enum_callback,
		void *param)
{
	struct obs_scene *scene = data;
	struct obs_scene_item *item;

	pthread_mutex_lock(&scene->mutex);

	item = scene->first_item;
	while (item) {
		struct obs_scene_item *next = item->next;

		obs_sceneitem_addref(item);
		enum_callback(scene->source, item->source, param);
		obs_sceneitem_release(item);

		item = next;
	}

	pthread_mutex_unlock(&scene->mutex);
}

124 125 126 127 128 129 130 131 132
static inline void detach_sceneitem(struct obs_scene_item *item)
{
	if (item->prev)
		item->prev->next = item->next;
	else
		item->parent->first_item = item->next;

	if (item->next)
		item->next->prev = item->prev;
J
jp9000 已提交
133 134

	item->parent = NULL;
135 136 137 138 139 140 141 142 143 144 145 146 147
}

static inline void attach_sceneitem(struct obs_scene_item *item,
		struct obs_scene_item *prev)
{
	item->prev = prev;

	if (prev) {
		item->next = prev->next;
		if (prev->next)
			prev->next->prev = item;
		prev->next = item;
	} else {
148 149
		assert(item->parent != NULL);

150 151 152 153 154
		item->next = item->parent->first_item;
		item->parent->first_item = item;
	}
}

J
jp9000 已提交
155
static void scene_video_render(void *data, effect_t effect)
J
jp9000 已提交
156
{
157 158 159
	struct obs_scene *scene = data;
	struct obs_scene_item *item;

160 161
	pthread_mutex_lock(&scene->mutex);

162
	item = scene->first_item;
J
jp9000 已提交
163

164
	while (item) {
165
		if (obs_source_removed(item->source)) {
166 167 168
			struct obs_scene_item *del_item = item;
			item = item->next;

J
jp9000 已提交
169
			obs_sceneitem_remove(del_item);
170 171 172
			continue;
		}

J
jp9000 已提交
173 174 175 176 177 178
		gs_matrix_push();
		gs_matrix_translate3f(item->origin.x, item->origin.y, 0.0f);
		gs_matrix_scale3f(item->scale.x, item->scale.y, 1.0f);
		gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD(-item->rot));
		gs_matrix_translate3f(-item->pos.x, -item->pos.y, 0.0f);

179
		obs_source_video_render(item->source);
J
jp9000 已提交
180 181

		gs_matrix_pop();
182 183

		item = item->next;
J
jp9000 已提交
184
	}
185 186

	pthread_mutex_unlock(&scene->mutex);
J
jp9000 已提交
187 188 189 190

	UNUSED_PARAMETER(effect);
}

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
static void scene_load_item(struct obs_scene *scene, obs_data_t item_data)
{
	const char            *name = obs_data_getstring(item_data, "name");
	obs_source_t          source = obs_get_source_by_name(name);
	struct obs_scene_item *item;

	if (!source) {
		blog(LOG_WARNING, "[scene_load_item] Source %s not found!",
				name);
		return;
	}

	item = obs_scene_add(scene, source);

	item->rot     = (float)obs_data_getdouble(item_data, "rot");
	item->visible = obs_data_getbool(item_data, "visible");
	obs_data_get_vec2(item_data, "origin", &item->origin);
	obs_data_get_vec2(item_data, "pos",    &item->pos);
	obs_data_get_vec2(item_data, "scale",  &item->scale);
	obs_source_release(source);
}

static void scene_load(void *scene, obs_data_t settings)
{
	obs_data_array_t items = obs_data_getarray(settings, "items");
	size_t           count, i;

	remove_all_items(scene);

	if (!items) return;

	count = obs_data_array_count(items);

	for (i = 0; i < count; i++) {
		obs_data_t item_data = obs_data_array_item(items, i);
		scene_load_item(scene, item_data);
		obs_data_release(item_data);
	}

	obs_data_array_release(items);
}

static void scene_save_item(struct obs_scene *scene, obs_data_array_t array,
		struct obs_scene_item *item)
{
	obs_data_t item_data = obs_data_create();
	const char *name     = obs_source_getname(item->source);

	obs_data_setstring(item_data, "name",    name);
	obs_data_setbool  (item_data, "visible", item->visible);
	obs_data_setdouble(item_data, "rot",     item->rot);
	obs_data_set_vec2 (item_data, "origin",  &item->origin);
	obs_data_set_vec2 (item_data, "pos",     &item->pos);
	obs_data_set_vec2 (item_data, "scale",   &item->scale);

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

static void scene_save(void *data, obs_data_t settings)
{
	struct obs_scene      *scene = data;
	obs_data_array_t      array  = obs_data_array_create();
	struct obs_scene_item *item;

	pthread_mutex_lock(&scene->mutex);

	item = scene->first_item;
	while (item) {
		scene_save_item(scene, array, item);
		item = item->next;
	}

	pthread_mutex_unlock(&scene->mutex);

	obs_data_setarray(settings, "items", array);
	obs_data_array_release(array);
}

J
jp9000 已提交
270 271 272 273
static uint32_t scene_getwidth(void *data)
{
	UNUSED_PARAMETER(data);
	return obs->video.base_width;
J
jp9000 已提交
274 275
}

J
jp9000 已提交
276
static uint32_t scene_getheight(void *data)
J
jp9000 已提交
277
{
J
jp9000 已提交
278 279
	UNUSED_PARAMETER(data);
	return obs->video.base_height;
J
jp9000 已提交
280 281
}

282
const struct obs_source_info scene_info =
J
jp9000 已提交
283
{
J
jp9000 已提交
284
	.id           = "scene",
285
	.type         = OBS_SOURCE_TYPE_INPUT,
J
jp9000 已提交
286 287 288 289 290
	.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW,
	.getname      = scene_getname,
	.create       = scene_create,
	.destroy      = scene_destroy,
	.video_render = scene_video_render,
J
jp9000 已提交
291 292
	.getwidth     = scene_getwidth,
	.getheight    = scene_getheight,
293 294
	.load         = scene_load,
	.save         = scene_save,
295
	.enum_sources = scene_enum_sources
J
jp9000 已提交
296 297
};

298
obs_scene_t obs_scene_create(const char *name)
J
jp9000 已提交
299
{
300 301 302
	struct obs_source *source =
		obs_source_create(OBS_SOURCE_TYPE_INPUT, "scene", name, NULL);
	return source->context.data;
J
jp9000 已提交
303 304
}

P
Palana 已提交
305
void obs_scene_addref(obs_scene_t scene)
306
{
P
Palana 已提交
307 308
	if (scene)
		obs_source_addref(scene->source);
309 310
}

P
Palana 已提交
311
void obs_scene_release(obs_scene_t scene)
J
jp9000 已提交
312
{
P
Palana 已提交
313 314
	if (scene)
		obs_source_release(scene->source);
J
jp9000 已提交
315 316
}

317
obs_source_t obs_scene_getsource(obs_scene_t scene)
J
jp9000 已提交
318
{
J
jp9000 已提交
319
	return scene ? scene->source : NULL;
J
jp9000 已提交
320 321
}

322 323
obs_scene_t obs_scene_fromsource(obs_source_t source)
{
324
	if (!source || source->info.id != scene_info.id)
325 326
		return NULL;

327
	return source->context.data;
328 329
}

330 331 332 333
obs_sceneitem_t obs_scene_findsource(obs_scene_t scene, const char *name)
{
	struct obs_scene_item *item;

J
jp9000 已提交
334 335 336
	if (!scene)
		return NULL;

337 338 339 340
	pthread_mutex_lock(&scene->mutex);

	item = scene->first_item;
	while (item) {
341
		if (strcmp(item->source->context.name, name) == 0)
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
			break;

		item = item->next;
	}

	pthread_mutex_unlock(&scene->mutex);

	return item;
}

void obs_scene_enum_items(obs_scene_t scene,
		bool (*callback)(obs_scene_t, obs_sceneitem_t, void*),
		void *param)
{
	struct obs_scene_item *item;

J
jp9000 已提交
358 359 360
	if (!scene || !callback)
		return;

361 362 363 364
	pthread_mutex_lock(&scene->mutex);

	item = scene->first_item;
	while (item) {
J
jp9000 已提交
365 366 367 368
		struct obs_scene_item *next = item->next;

		obs_sceneitem_addref(item);

369 370
		if (!callback(scene, item, param)) {
			obs_sceneitem_release(item);
371
			break;
372
		}
373

J
jp9000 已提交
374 375 376
		obs_sceneitem_release(item);

		item = next;
377 378 379 380 381
	}

	pthread_mutex_unlock(&scene->mutex);
}

382
obs_sceneitem_t obs_scene_add(obs_scene_t scene, obs_source_t source)
J
jp9000 已提交
383
{
384
	struct obs_scene_item *last;
385
	struct obs_scene_item *item;
J
jp9000 已提交
386
	struct calldata params = {0};
387

388 389 390 391
	if (!scene)
		return NULL;

	if (!source) {
J
jp9000 已提交
392
		blog(LOG_ERROR, "Tried to add a NULL source to a scene");
393 394 395 396
		return NULL;
	}

	item = bzalloc(sizeof(struct obs_scene_item));
J
jp9000 已提交
397 398 399
	item->source  = source;
	item->visible = true;
	item->parent  = scene;
J
jp9000 已提交
400
	item->ref     = 1;
J
jp9000 已提交
401 402
	vec2_set(&item->scale, 1.0f, 1.0f);

403 404
	obs_source_addref(source);
	obs_source_add_child(scene->source, source);
405

406 407 408 409 410 411 412 413 414 415 416 417
	pthread_mutex_lock(&scene->mutex);

	last = scene->first_item;
	if (!last) {
		scene->first_item = item;
	} else {
		while (last->next)
			last = last->next;

		last->next = item;
		item->prev = last;
	}
J
jp9000 已提交
418

419 420
	pthread_mutex_unlock(&scene->mutex);

J
jp9000 已提交
421 422
	calldata_setptr(&params, "scene", scene);
	calldata_setptr(&params, "item", item);
423 424
	signal_handler_signal(scene->source->context.signals, "item_add",
			&params);
J
jp9000 已提交
425 426
	calldata_free(&params);

J
jp9000 已提交
427 428 429
	return item;
}

J
jp9000 已提交
430
static void obs_sceneitem_destroy(obs_sceneitem_t item)
J
jp9000 已提交
431
{
432
	if (item) {
433
		if (item->source)
J
jp9000 已提交
434
			obs_source_release(item->source);
J
jp9000 已提交
435
		bfree(item);
J
jp9000 已提交
436 437 438
	}
}

P
Palana 已提交
439
void obs_sceneitem_addref(obs_sceneitem_t item)
J
jp9000 已提交
440
{
P
Palana 已提交
441
	if (item)
J
jp9000 已提交
442
		os_atomic_inc_long(&item->ref);
J
jp9000 已提交
443
}
444

P
Palana 已提交
445
void obs_sceneitem_release(obs_sceneitem_t item)
J
jp9000 已提交
446
{
P
Palana 已提交
447 448
	if (!item)
		return;
P
Palana 已提交
449

J
jp9000 已提交
450
	if (os_atomic_dec_long(&item->ref) == 0)
P
Palana 已提交
451
		obs_sceneitem_destroy(item);
452 453
}

J
jp9000 已提交
454 455 456 457
void obs_sceneitem_remove(obs_sceneitem_t item)
{
	obs_scene_t scene;

458
	if (!item)
J
jp9000 已提交
459 460
		return;

461
	scene = item->parent;
J
jp9000 已提交
462

463 464
	if (scene)
		pthread_mutex_lock(&scene->mutex);
J
jp9000 已提交
465

J
jp9000 已提交
466
	if (item->removed) {
467 468 469 470 471
		if (scene)
			pthread_mutex_unlock(&scene->mutex);
		return;
	}

J
jp9000 已提交
472 473
	item->removed = true;

474 475
	assert(scene != NULL);
	assert(scene->source != NULL);
476 477
	obs_source_remove_child(scene->source, item->source);

478
	signal_item_remove(item);
J
jp9000 已提交
479
	detach_sceneitem(item);
480

481
	pthread_mutex_unlock(&scene->mutex);
J
jp9000 已提交
482 483 484 485

	obs_sceneitem_release(item);
}

J
jp9000 已提交
486 487
obs_scene_t obs_sceneitem_getscene(obs_sceneitem_t item)
{
J
jp9000 已提交
488
	return item ? item->parent : NULL;
J
jp9000 已提交
489 490
}

491 492
obs_source_t obs_sceneitem_getsource(obs_sceneitem_t item)
{
J
jp9000 已提交
493
	return item ? item->source : NULL;
J
jp9000 已提交
494 495
}

496
void obs_sceneitem_setpos(obs_sceneitem_t item, const struct vec2 *pos)
J
jp9000 已提交
497
{
J
jp9000 已提交
498 499
	if (item)
		vec2_copy(&item->pos, pos);
J
jp9000 已提交
500 501
}

502
void obs_sceneitem_setrot(obs_sceneitem_t item, float rot)
J
jp9000 已提交
503
{
J
jp9000 已提交
504 505
	if (item)
		item->rot = rot;
J
jp9000 已提交
506 507
}

508
void obs_sceneitem_setorigin(obs_sceneitem_t item, const struct vec2 *origin)
J
jp9000 已提交
509
{
J
jp9000 已提交
510 511
	if (item)
		vec2_copy(&item->origin, origin);
J
jp9000 已提交
512 513
}

514
void obs_sceneitem_setscale(obs_sceneitem_t item, const struct vec2 *scale)
J
jp9000 已提交
515
{
J
jp9000 已提交
516 517
	if (item)
		vec2_copy(&item->scale, scale);
J
jp9000 已提交
518 519
}

520
void obs_sceneitem_setorder(obs_sceneitem_t item, enum order_movement movement)
J
jp9000 已提交
521
{
J
jp9000 已提交
522 523
	if (!item) return;

J
jp9000 已提交
524 525
	struct obs_scene *scene = item->parent;

J
jp9000 已提交
526
	obs_scene_addref(scene);
527
	pthread_mutex_lock(&scene->mutex);
528 529 530

	detach_sceneitem(item);

J
jp9000 已提交
531
	if (movement == ORDER_MOVE_UP) {
532
		attach_sceneitem(item, item->prev);
J
jp9000 已提交
533 534

	} else if (movement == ORDER_MOVE_DOWN) {
535
		attach_sceneitem(item, item->next);
J
jp9000 已提交
536 537

	} else if (movement == ORDER_MOVE_TOP) {
538 539 540 541 542 543 544
		struct obs_scene_item *last = item->next;
		if (!last) {
			last = item->prev;
		} else {
			while (last->next)
				last = last->next;
		}
J
jp9000 已提交
545

546 547 548 549
		attach_sceneitem(item, last);

	} else if (movement == ORDER_MOVE_BOTTOM) {
		attach_sceneitem(item, NULL);
J
jp9000 已提交
550
	}
551 552

	pthread_mutex_unlock(&scene->mutex);
553
	obs_scene_release(scene);
J
jp9000 已提交
554 555
}

556
void obs_sceneitem_getpos(obs_sceneitem_t item, struct vec2 *pos)
J
jp9000 已提交
557
{
J
jp9000 已提交
558 559
	if (item)
		vec2_copy(pos, &item->pos);
J
jp9000 已提交
560 561
}

562
float obs_sceneitem_getrot(obs_sceneitem_t item)
J
jp9000 已提交
563
{
J
jp9000 已提交
564
	return item ? item->rot : 0.0f;
J
jp9000 已提交
565 566
}

567
void obs_sceneitem_getorigin(obs_sceneitem_t item, struct vec2 *origin)
J
jp9000 已提交
568
{
J
jp9000 已提交
569 570
	if (item)
		vec2_copy(origin, &item->origin);
J
jp9000 已提交
571 572
}

573
void obs_sceneitem_getscale(obs_sceneitem_t item, struct vec2 *scale)
J
jp9000 已提交
574
{
J
jp9000 已提交
575 576
	if (item)
		vec2_copy(scale, &item->scale);
J
jp9000 已提交
577
}