1 /* PipeWire
2  *
3  * Copyright © 2018 Wim Taymans
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include "config.h"
26 
27 #include <string.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <math.h>
31 #include <getopt.h>
32 #include <time.h>
33 #include <unistd.h>
34 #include <limits.h>
35 #include <fcntl.h>
36 #include <signal.h>
37 #include <sys/stat.h>
38 #include <sys/mman.h>
39 #include <sys/types.h>
40 
41 #include <spa/node/node.h>
42 #include <spa/utils/hook.h>
43 #include <spa/utils/result.h>
44 #include <spa/utils/json.h>
45 #include <spa/utils/string.h>
46 #include <spa/param/audio/format-utils.h>
47 #include <spa/param/props.h>
48 #include <spa/debug/pod.h>
49 #include <spa/support/dbus.h>
50 #include <spa/monitor/device.h>
51 
52 #include "pipewire/pipewire.h"
53 #include "pipewire/conf.h"
54 #include "pipewire/extensions/session-manager.h"
55 #include "pipewire/extensions/client-node.h"
56 
57 #include <dbus/dbus.h>
58 
59 #include "media-session.h"
60 
61 #define NAME		"media-session"
62 #define SESSION_PREFIX	"media-session.d"
63 #define SESSION_CONF	"media-session.conf"
64 
65 PW_LOG_TOPIC(ms_topic, "ms.core");
66 #define PW_LOG_TOPIC_DEFAULT ms_topic
67 
68 #define sm_object_emit(o,m,v,...) spa_hook_list_call(&(o)->hooks, struct sm_object_events, m, v, ##__VA_ARGS__)
69 
70 #define sm_object_emit_update(s)		sm_object_emit(s, update, 0)
71 #define sm_object_emit_destroy(s)		sm_object_emit(s, destroy, 0)
72 #define sm_object_emit_free(s)			sm_object_emit(s, free, 0)
73 
74 #define sm_media_session_emit(s,m,v,...) spa_hook_list_call(&(s)->hooks, struct sm_media_session_events, m, v, ##__VA_ARGS__)
75 
76 #define sm_media_session_emit_info(s,i)			sm_media_session_emit(s, info, 0, i)
77 #define sm_media_session_emit_create(s,obj)		sm_media_session_emit(s, create, 0, obj)
78 #define sm_media_session_emit_remove(s,obj)		sm_media_session_emit(s, remove, 0, obj)
79 #define sm_media_session_emit_rescan(s,seq)		sm_media_session_emit(s, rescan, 0, seq)
80 #define sm_media_session_emit_shutdown(s)		sm_media_session_emit(s, shutdown, 0)
81 #define sm_media_session_emit_destroy(s)		sm_media_session_emit(s, destroy, 0)
82 #define sm_media_session_emit_seat_active(s,...)	sm_media_session_emit(s, seat_active, 0, __VA_ARGS__)
83 #define sm_media_session_emit_dbus_disconnected(s)	sm_media_session_emit(s, dbus_disconnected, 0)
84 
85 int sm_access_flatpak_start(struct sm_media_session *sess);
86 int sm_access_portal_start(struct sm_media_session *sess);
87 int sm_default_nodes_start(struct sm_media_session *sess);
88 int sm_default_profile_start(struct sm_media_session *sess);
89 int sm_default_routes_start(struct sm_media_session *sess);
90 int sm_restore_stream_start(struct sm_media_session *sess);
91 int sm_streams_follow_default_start(struct sm_media_session *sess);
92 int sm_alsa_no_dsp_start(struct sm_media_session *sess);
93 int sm_alsa_midi_start(struct sm_media_session *sess);
94 int sm_v4l2_monitor_start(struct sm_media_session *sess);
95 int sm_libcamera_monitor_start(struct sm_media_session *sess);
96 int sm_bluez5_monitor_start(struct sm_media_session *sess);
97 int sm_bluez5_autoswitch_start(struct sm_media_session *sess);
98 int sm_alsa_monitor_start(struct sm_media_session *sess);
99 int sm_suspend_node_start(struct sm_media_session *sess);
100 #ifdef HAVE_SYSTEMD
101 int sm_logind_start(struct sm_media_session *sess);
102 #endif
103 
104 int sm_policy_node_start(struct sm_media_session *sess);
105 
106 int sm_session_manager_start(struct sm_media_session *sess);
107 
108 /** user data to add to an object */
109 struct data {
110 	struct spa_list link;
111 	const char *id;
112 	size_t size;
113 };
114 
115 struct param {
116 	struct sm_param this;
117 };
118 
119 struct sync {
120 	struct spa_list link;
121 	int seq;
122 	void (*callback) (void *data);
123 	void *data;
124 };
125 
126 struct impl {
127 	struct sm_media_session this;
128 
129 	const char *config_dir;
130 
131 	struct pw_properties *conf;
132 	struct pw_properties *modules;
133 
134 	struct pw_main_loop *loop;
135 	struct spa_dbus *dbus;
136 	struct spa_hook dbus_connection_listener;
137 
138 	struct pw_core *monitor_core;
139 	struct spa_hook monitor_listener;
140 	int monitor_seq;
141 
142 	struct pw_core *policy_core;
143 	struct spa_hook policy_listener;
144 	struct spa_hook proxy_policy_listener;
145 
146 	struct pw_registry *registry;
147 	struct spa_hook registry_listener;
148 
149 	struct pw_registry *monitor_registry;
150 	struct spa_hook monitor_registry_listener;
151 
152 	struct pw_map globals;
153 	struct spa_list object_list;		/**< all sm_objects */
154 
155 	struct spa_list registry_event_list;	/**< pending registry events */
156 
157 	struct spa_hook_list hooks;
158 
159 	struct spa_list endpoint_link_list;	/** list of struct endpoint_link */
160 	struct pw_map endpoint_links;		/** map of endpoint_link */
161 
162 	struct spa_list link_list;		/** list of struct link */
163 
164 	struct spa_list sync_list;		/** list of struct sync */
165 	int rescan_seq;
166 	int last_seq;
167 
168 	unsigned int scanning:1;
169 	unsigned int rescan_pending:1;
170 	unsigned int seat_active:1;
171 };
172 
173 struct endpoint_link {
174 	uint32_t id;
175 
176 	struct pw_endpoint_link_info info;
177 
178 	struct impl *impl;
179 
180 	struct spa_list link;			/**< link in struct impl endpoint_link_list */
181 	struct spa_list link_list;		/**< list of struct link */
182 };
183 
184 struct link {
185 	struct pw_proxy *proxy;		/**< proxy for link */
186 	struct spa_hook listener;	/**< proxy listener */
187 
188 	uint32_t output_node;
189 	uint32_t output_port;
190 	uint32_t input_node;
191 	uint32_t input_port;
192 
193 	struct endpoint_link *endpoint_link;
194 	struct spa_list link;		/**< link in struct endpoint_link link_list or
195 					  *  struct impl link_list */
196 };
197 
198 struct object_info {
199 	const char *type;
200 	uint32_t version;
201 	const void *events;
202 	size_t size;
203 	int (*init) (void *object);
204 	void (*destroy) (void *object);
205 };
206 
207 struct registry_event {
208 	uint32_t id;
209 	uint32_t permissions;
210 	const char *type;
211 	uint32_t version;
212 	const struct spa_dict *props;
213 
214 	struct pw_proxy *proxy;
215 
216 	int seq;
217 	struct pw_properties *props_store;
218 
219 	struct spa_list link;
220 	unsigned int monitor:1;
221 	unsigned int allocated:1;
222 };
223 
add_object(struct impl * impl,struct sm_object * obj,uint32_t id)224 static void add_object(struct impl *impl, struct sm_object *obj, uint32_t id)
225 {
226 	size_t size = pw_map_get_size(&impl->globals);
227 	obj->id = id;
228 	pw_log_debug("add global '%u' %p monitor:%d", obj->id, obj, obj->monitor_global);
229 	while (obj->id > size)
230 		pw_map_insert_at(&impl->globals, size++, NULL);
231 	pw_map_insert_at(&impl->globals, obj->id, obj);
232 	sm_media_session_emit_create(impl, obj);
233 }
234 
remove_object(struct impl * impl,struct sm_object * obj)235 static void remove_object(struct impl *impl, struct sm_object *obj)
236 {
237 	pw_log_debug("remove global '%u' %p monitor:%d", obj->id, obj, obj->monitor_global);
238 	pw_map_insert_at(&impl->globals, obj->id, NULL);
239 	sm_media_session_emit_remove(impl, obj);
240 	obj->id = SPA_ID_INVALID;
241 }
242 
find_object(struct impl * impl,uint32_t id,const char * type)243 static void *find_object(struct impl *impl, uint32_t id, const char *type)
244 {
245 	struct sm_object *obj;
246 	if ((obj = pw_map_lookup(&impl->globals, id)) == NULL)
247 		return NULL;
248 	if (type != NULL && !spa_streq(obj->type, type))
249 		return NULL;
250 	return obj;
251 }
252 
object_find_data(struct sm_object * obj,const char * id)253 static struct data *object_find_data(struct sm_object *obj, const char *id)
254 {
255 	struct data *d;
256 	spa_list_for_each(d, &obj->data, link) {
257 		if (spa_streq(d->id, id))
258 			return d;
259 	}
260 	return NULL;
261 }
262 
sm_object_add_data(struct sm_object * obj,const char * id,size_t size)263 void *sm_object_add_data(struct sm_object *obj, const char *id, size_t size)
264 {
265 	struct data *d;
266 
267 	d = object_find_data(obj, id);
268 	if (d != NULL) {
269 		if (d->size == size)
270 			goto done;
271 		spa_list_remove(&d->link);
272 		free(d);
273 	}
274 	d = calloc(1, sizeof(struct data) + size);
275 	d->id = id;
276 	d->size = size;
277 
278 	spa_list_append(&obj->data, &d->link);
279 done:
280 	return SPA_PTROFF(d, sizeof(struct data), void);
281 }
282 
sm_object_get_data(struct sm_object * obj,const char * id)283 void *sm_object_get_data(struct sm_object *obj, const char *id)
284 {
285 	struct data *d;
286 	d = object_find_data(obj, id);
287 	if (d == NULL)
288 		return NULL;
289 	return SPA_PTROFF(d, sizeof(struct data), void);
290 }
291 
sm_object_remove_data(struct sm_object * obj,const char * id)292 int sm_object_remove_data(struct sm_object *obj, const char *id)
293 {
294 	struct data *d;
295 	d = object_find_data(obj, id);
296 	if (d == NULL)
297 		return -ENOENT;
298 	spa_list_remove(&d->link);
299 	free(d);
300 	return 0;
301 }
302 
sm_object_destroy_maybe_free(struct sm_object * obj)303 static int sm_object_destroy_maybe_free(struct sm_object *obj)
304 {
305 	struct impl *impl = SPA_CONTAINER_OF(obj->session, struct impl, this);
306 	struct data *d;
307 
308 	pw_log_debug("%p: destroy object %p id:%d proxy:%p handle:%p monitor:%d destroyed:%d discarded:%d", obj->session,
309 			obj, obj->id, obj->proxy, obj->handle, obj->monitor_global, obj->destroyed, obj->discarded);
310 
311 	if (obj->destroyed)
312 		goto unref;
313 
314 	obj->destroyed = true;
315 
316 	sm_object_emit_destroy(obj);
317 
318 	if (SPA_FLAG_IS_SET(obj->mask, SM_OBJECT_CHANGE_MASK_LISTENER)) {
319 		SPA_FLAG_CLEAR(obj->mask, SM_OBJECT_CHANGE_MASK_LISTENER);
320 		spa_hook_remove(&obj->object_listener);
321 	}
322 
323 	if (obj->id != SPA_ID_INVALID)
324 		remove_object(impl, obj);
325 
326 	if (obj->destroy)
327 		obj->destroy(obj);
328 
329 	spa_hook_remove(&obj->handle_listener);
330 
331 	if (obj->proxy) {
332 		spa_hook_remove(&obj->proxy_listener);
333 		if (obj->proxy != obj->handle)
334 			pw_proxy_destroy(obj->proxy);
335 		obj->proxy = NULL;
336 	}
337 
338 	pw_proxy_ref(obj->handle);
339 	pw_proxy_destroy(obj->handle);
340 
341 	sm_object_emit_free(obj);
342 
343 unref:
344 	if (!obj->discarded)
345 		return 0;
346 
347 	pw_properties_free(obj->props);
348 	obj->props = NULL;
349 
350 	spa_list_consume(d, &obj->data, link) {
351 		spa_list_remove(&d->link);
352 		free(d);
353 	}
354 
355 	spa_list_remove(&obj->link);
356 	pw_proxy_unref(obj->handle);  /* frees obj */
357 
358 	return 0;
359 }
360 
sm_object_destroy(struct sm_object * obj)361 int sm_object_destroy(struct sm_object *obj)
362 {
363 	sm_object_discard(obj);
364 	return sm_object_destroy_maybe_free(obj);
365 }
366 
add_param(struct spa_list * param_list,int seq,int * param_seq,uint32_t id,const struct spa_pod * param)367 static struct param *add_param(struct spa_list *param_list,
368 		int seq, int *param_seq, uint32_t id, const struct spa_pod *param)
369 {
370 	struct param *p;
371 
372 	if (param == NULL || !spa_pod_is_object(param)) {
373 		errno = EINVAL;
374 		return NULL;
375 	}
376 	if (id == SPA_ID_INVALID)
377 		id = SPA_POD_OBJECT_ID(param);
378 
379 	if (id >= SM_MAX_PARAMS) {
380 		pw_log_error("too big param id %d", id);
381 		errno = EINVAL;
382 		return NULL;
383 	}
384 
385 	if (seq != param_seq[id]) {
386 		pw_log_debug("ignoring param %d, seq:%d != current_seq:%d",
387 				id, seq, param_seq[id]);
388 		errno = EBUSY;
389 		return NULL;
390 	}
391 
392 	p = malloc(sizeof(struct param) + SPA_POD_SIZE(param));
393 	if (p == NULL)
394 		return NULL;
395 
396 	p->this.id = id;
397 	p->this.param = SPA_PTROFF(p, sizeof(struct param), struct spa_pod);
398 	memcpy(p->this.param, param, SPA_POD_SIZE(param));
399 
400 	spa_list_append(param_list, &p->this.link);
401 
402 	return p;
403 }
404 
405 
clear_params(struct spa_list * param_list,uint32_t id)406 static uint32_t clear_params(struct spa_list *param_list, uint32_t id)
407 {
408 	struct param *p, *t;
409 	uint32_t count = 0;
410 
411 	spa_list_for_each_safe(p, t, param_list, this.link) {
412 		if (id == SPA_ID_INVALID || p->this.id == id) {
413 			spa_list_remove(&p->this.link);
414 			free(p);
415 			count++;
416 		}
417 	}
418 	return count;
419 }
420 
421 /**
422  * Core
423  */
424 static const struct object_info core_object_info = {
425 	.type = PW_TYPE_INTERFACE_Core,
426 	.version = PW_VERSION_CORE,
427 	.size = sizeof(struct sm_object),
428 	.init = NULL,
429 };
430 
431 /**
432  * Module
433  */
434 static const struct object_info module_info = {
435 	.type = PW_TYPE_INTERFACE_Module,
436 	.version = PW_VERSION_MODULE,
437 	.size = sizeof(struct sm_object),
438 	.init = NULL,
439 };
440 
441 /**
442  * Factory
443  */
444 static const struct object_info factory_info = {
445 	.type = PW_TYPE_INTERFACE_Factory,
446 	.version = PW_VERSION_FACTORY,
447 	.size = sizeof(struct sm_object),
448 	.init = NULL,
449 };
450 
451 /**
452  * Clients
453  */
client_event_info(void * object,const struct pw_client_info * info)454 static void client_event_info(void *object, const struct pw_client_info *info)
455 {
456 	struct sm_client *client = object;
457 	struct impl *impl = SPA_CONTAINER_OF(client->obj.session, struct impl, this);
458 
459 	pw_log_debug("%p: client %d info", impl, client->obj.id);
460 	client->info = pw_client_info_merge(client->info, info, client->obj.changed == 0);
461 
462 	client->obj.avail |= SM_CLIENT_CHANGE_MASK_INFO;
463 	client->obj.changed |= SM_CLIENT_CHANGE_MASK_INFO;
464 	sm_object_sync_update(&client->obj);
465 }
466 
467 static const struct pw_client_events client_events = {
468 	PW_VERSION_CLIENT_EVENTS,
469 	.info = client_event_info,
470 };
471 
client_destroy(void * object)472 static void client_destroy(void *object)
473 {
474 	struct sm_client *client = object;
475 	if (client->info)
476 		pw_client_info_free(client->info);
477 }
478 
479 static const struct object_info client_info = {
480 	.type = PW_TYPE_INTERFACE_Client,
481 	.version = PW_VERSION_CLIENT,
482 	.events = &client_events,
483 	.size = sizeof(struct sm_client),
484 	.init = NULL,
485 	.destroy = client_destroy,
486 };
487 
488 /**
489  * Device
490  */
device_event_info(void * object,const struct pw_device_info * info)491 static void device_event_info(void *object, const struct pw_device_info *info)
492 {
493 	struct sm_device *device = object;
494 	struct impl *impl = SPA_CONTAINER_OF(device->obj.session, struct impl, this);
495 	uint32_t i;
496 
497 	pw_log_debug("%p: device %d info", impl, device->obj.id);
498 	info = device->info = pw_device_info_merge(device->info, info, device->obj.changed == 0);
499 
500 	device->obj.avail |= SM_DEVICE_CHANGE_MASK_INFO;
501 	device->obj.changed |= SM_DEVICE_CHANGE_MASK_INFO;
502 
503 	if (info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) {
504 		for (i = 0; i < info->n_params; i++) {
505 			uint32_t id = info->params[i].id;
506 
507 			if (info->params[i].user == 0)
508 				continue;
509 
510 			if (id >= SM_MAX_PARAMS) {
511 				pw_log_error("%p: too big param id %d", impl, id);
512 				continue;
513 			}
514 
515 			device->n_params -= clear_params(&device->param_list, id);
516 
517 			if (info->params[i].flags & SPA_PARAM_INFO_READ) {
518 				int res;
519 				res = pw_device_enum_params((struct pw_device*)device->obj.proxy,
520 						++device->param_seq[id], id, 0, UINT32_MAX, NULL);
521 				if (SPA_RESULT_IS_ASYNC(res))
522 					device->param_seq[id] = res;
523 				pw_log_debug("%p: device %d enum params %d seq:%d", impl,
524 						device->obj.id, id, device->param_seq[id]);
525 			}
526 			info->params[i].user = 0;
527 		}
528 	}
529 	sm_object_sync_update(&device->obj);
530 	sm_media_session_schedule_rescan(&impl->this);
531 }
532 
device_event_param(void * object,int seq,uint32_t id,uint32_t index,uint32_t next,const struct spa_pod * param)533 static void device_event_param(void *object, int seq,
534 		uint32_t id, uint32_t index, uint32_t next,
535 		const struct spa_pod *param)
536 {
537 	struct sm_device *device = object;
538 	struct impl *impl = SPA_CONTAINER_OF(device->obj.session, struct impl, this);
539 
540 	pw_log_debug("%p: device %p param %d index:%d seq:%d", impl, device, id, index, seq);
541 	if (add_param(&device->param_list, seq, device->param_seq, id, param) != NULL)
542 		device->n_params++;
543 
544 	device->obj.avail |= SM_DEVICE_CHANGE_MASK_PARAMS;
545 	device->obj.changed |= SM_DEVICE_CHANGE_MASK_PARAMS;
546 }
547 
548 static const struct pw_device_events device_events = {
549 	PW_VERSION_DEVICE_EVENTS,
550 	.info = device_event_info,
551 	.param = device_event_param,
552 };
553 
device_init(void * object)554 static int device_init(void *object)
555 {
556 	struct sm_device *device = object;
557 	spa_list_init(&device->node_list);
558 	spa_list_init(&device->param_list);
559 	return 0;
560 }
561 
device_destroy(void * object)562 static void device_destroy(void *object)
563 {
564 	struct sm_device *device = object;
565 	struct sm_node *node;
566 
567 	spa_list_consume(node, &device->node_list, link) {
568 		node->device = NULL;
569 		spa_list_remove(&node->link);
570 	}
571 	clear_params(&device->param_list, SPA_ID_INVALID);
572 	device->n_params = 0;
573 
574 	if (device->info)
575 		pw_device_info_free(device->info);
576 	device->info = NULL;
577 }
578 
579 static const struct object_info device_info = {
580 	.type = PW_TYPE_INTERFACE_Device,
581 	.version = PW_VERSION_DEVICE,
582 	.events = &device_events,
583 	.size = sizeof(struct sm_device),
584 	.init = device_init,
585 	.destroy = device_destroy,
586 };
587 
588 static const struct object_info spa_device_info = {
589 	.type = SPA_TYPE_INTERFACE_Device,
590 	.version = SPA_VERSION_DEVICE,
591 	.size = sizeof(struct sm_device),
592 	.init = device_init,
593 	.destroy = device_destroy,
594 };
595 
596 /**
597  * Node
598  */
node_event_info(void * object,const struct pw_node_info * info)599 static void node_event_info(void *object, const struct pw_node_info *info)
600 {
601 	struct sm_node *node = object;
602 	struct impl *impl = SPA_CONTAINER_OF(node->obj.session, struct impl, this);
603 	uint32_t i;
604 
605 	pw_log_debug("%p: node %d info", impl, node->obj.id);
606 	info = node->info = pw_node_info_merge(node->info, info, node->obj.changed == 0);
607 
608 	node->obj.avail |= SM_NODE_CHANGE_MASK_INFO;
609 	node->obj.changed |= SM_NODE_CHANGE_MASK_INFO;
610 
611 	if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS &&
612 	    (node->obj.mask & SM_NODE_CHANGE_MASK_PARAMS)) {
613 		for (i = 0; i < info->n_params; i++) {
614 			uint32_t id = info->params[i].id;
615 
616 			if (info->params[i].user == 0)
617 				continue;
618 
619 			if (id >= SM_MAX_PARAMS) {
620 				pw_log_error("%p: too big param id %d", impl, id);
621 				continue;
622 			}
623 
624 			node->n_params -= clear_params(&node->param_list, id);
625 
626 			if (info->params[i].flags & SPA_PARAM_INFO_READ) {
627 				int res;
628 				res = pw_node_enum_params((struct pw_node*)node->obj.proxy,
629 						++node->param_seq[id], id, 0, UINT32_MAX, NULL);
630 				if (SPA_RESULT_IS_ASYNC(res))
631 					node->param_seq[id] = res;
632 				pw_log_debug("%p: node %d enum params %d seq:%d", impl,
633 						node->obj.id, id, node->param_seq[id]);
634 			}
635 			info->params[i].user = 0;
636 		}
637 	}
638 	sm_object_sync_update(&node->obj);
639 	sm_media_session_schedule_rescan(&impl->this);
640 }
641 
node_event_param(void * object,int seq,uint32_t id,uint32_t index,uint32_t next,const struct spa_pod * param)642 static void node_event_param(void *object, int seq,
643 		uint32_t id, uint32_t index, uint32_t next,
644 		const struct spa_pod *param)
645 {
646 	struct sm_node *node = object;
647 	struct impl *impl = SPA_CONTAINER_OF(node->obj.session, struct impl, this);
648 
649 	pw_log_debug("%p: node %p param %d index:%d seq:%d", impl, node, id, index, seq);
650 	if (add_param(&node->param_list, seq, node->param_seq, id, param) != NULL)
651 		node->n_params++;
652 
653 	node->obj.avail |= SM_NODE_CHANGE_MASK_PARAMS;
654 	node->obj.changed |= SM_NODE_CHANGE_MASK_PARAMS;
655 }
656 
657 static const struct pw_node_events node_events = {
658 	PW_VERSION_NODE_EVENTS,
659 	.info = node_event_info,
660 	.param = node_event_param,
661 };
662 
node_init(void * object)663 static int node_init(void *object)
664 {
665 	struct sm_node *node = object;
666 	struct impl *impl = SPA_CONTAINER_OF(node->obj.session, struct impl, this);
667 	struct pw_properties *props = node->obj.props;
668 
669 	spa_list_init(&node->port_list);
670 	spa_list_init(&node->param_list);
671 
672 	if (props) {
673 		uint32_t id = SPA_ID_INVALID;
674 
675 		if (pw_properties_fetch_uint32(props, PW_KEY_DEVICE_ID, &id) == 0)
676 			node->device = find_object(impl, id, NULL);
677 		pw_log_debug("%p: node %d parent device %d (%p)", impl,
678 				node->obj.id, id, node->device);
679 		if (node->device) {
680 			spa_list_append(&node->device->node_list, &node->link);
681 			node->device->obj.avail |= SM_DEVICE_CHANGE_MASK_NODES;
682 			node->device->obj.changed |= SM_DEVICE_CHANGE_MASK_NODES;
683 		}
684 	}
685 	return 0;
686 }
687 
node_destroy(void * object)688 static void node_destroy(void *object)
689 {
690 	struct sm_node *node = object;
691 	struct sm_port *port;
692 
693 	spa_list_consume(port, &node->port_list, link) {
694 		port->node = NULL;
695 		spa_list_remove(&port->link);
696 	}
697 	clear_params(&node->param_list, SPA_ID_INVALID);
698 	node->n_params = 0;
699 
700 	if (node->device) {
701 		spa_list_remove(&node->link);
702 		node->device->obj.changed |= SM_DEVICE_CHANGE_MASK_NODES;
703 	}
704 	if (node->info) {
705 		pw_node_info_free(node->info);
706 		node->info = NULL;
707 	}
708 	free(node->target_node);
709 	node->target_node = NULL;
710 }
711 
712 static const struct object_info node_info = {
713 	.type = PW_TYPE_INTERFACE_Node,
714 	.version = PW_VERSION_NODE,
715 	.events = &node_events,
716 	.size = sizeof(struct sm_node),
717 	.init = node_init,
718 	.destroy = node_destroy,
719 };
720 
721 /**
722  * Port
723  */
port_event_info(void * object,const struct pw_port_info * info)724 static void port_event_info(void *object, const struct pw_port_info *info)
725 {
726 	struct sm_port *port = object;
727 	struct impl *impl = SPA_CONTAINER_OF(port->obj.session, struct impl, this);
728 
729 	pw_log_debug("%p: port %d info", impl, port->obj.id);
730 	port->info = pw_port_info_merge(port->info, info, port->obj.changed == 0);
731 
732 	port->obj.avail |= SM_PORT_CHANGE_MASK_INFO;
733 	port->obj.changed |= SM_PORT_CHANGE_MASK_INFO;
734 	sm_object_sync_update(&port->obj);
735 }
736 
737 static const struct pw_port_events port_events = {
738 	PW_VERSION_PORT_EVENTS,
739 	.info = port_event_info,
740 };
741 
find_channel(const char * name)742 static enum spa_audio_channel find_channel(const char *name)
743 {
744         int i;
745         for (i = 0; spa_type_audio_channel[i].name; i++) {
746                 if (spa_streq(name, spa_debug_type_short_name(spa_type_audio_channel[i].name)))
747                         return spa_type_audio_channel[i].type;
748         }
749         return SPA_AUDIO_CHANNEL_UNKNOWN;
750 }
751 
port_init(void * object)752 static int port_init(void *object)
753 {
754 	struct sm_port *port = object;
755 	struct impl *impl = SPA_CONTAINER_OF(port->obj.session, struct impl, this);
756 	struct pw_properties *props = port->obj.props;
757 	const char *str;
758 
759 	if (props) {
760 		uint32_t id = SPA_ID_INVALID;
761 
762 		if ((str = pw_properties_get(props, PW_KEY_PORT_DIRECTION)) != NULL)
763 			port->direction = spa_streq(str, "out") ?
764 				PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT;
765 		if ((str = pw_properties_get(props, PW_KEY_FORMAT_DSP)) != NULL) {
766 			if (spa_streq(str, "32 bit float mono audio"))
767 				port->type = SM_PORT_TYPE_DSP_AUDIO;
768 			else if (spa_streq(str, "8 bit raw midi"))
769 				port->type = SM_PORT_TYPE_DSP_MIDI;
770 		}
771 		if ((str = pw_properties_get(props, PW_KEY_AUDIO_CHANNEL)) != NULL)
772 			port->channel = find_channel(str);
773 		if (pw_properties_fetch_uint32(props, PW_KEY_NODE_ID, &id) == 0)
774 			port->node = find_object(impl, id, PW_TYPE_INTERFACE_Node);
775 
776 		pw_log_debug("%p: port %d parent node %s (%p) direction:%d type:%d", impl,
777 				port->obj.id, str, port->node, port->direction, port->type);
778 		if (port->node) {
779 			spa_list_append(&port->node->port_list, &port->link);
780 			port->node->obj.avail |= SM_NODE_CHANGE_MASK_PORTS;
781 			port->node->obj.changed |= SM_NODE_CHANGE_MASK_PORTS;
782 		}
783 	}
784 	return 0;
785 }
786 
port_destroy(void * object)787 static void port_destroy(void *object)
788 {
789 	struct sm_port *port = object;
790 	if (port->info)
791 		pw_port_info_free(port->info);
792 	if (port->node) {
793 		spa_list_remove(&port->link);
794 		port->node->obj.changed |= SM_NODE_CHANGE_MASK_PORTS;
795 	}
796 }
797 
798 static const struct object_info port_info = {
799 	.type = PW_TYPE_INTERFACE_Port,
800 	.version = PW_VERSION_PORT,
801 	.events = &port_events,
802 	.size = sizeof(struct sm_port),
803 	.init = port_init,
804 	.destroy = port_destroy,
805 };
806 
807 /**
808  * Session
809  */
session_event_info(void * object,const struct pw_session_info * info)810 static void session_event_info(void *object, const struct pw_session_info *info)
811 {
812 	struct sm_session *sess = object;
813 	struct impl *impl = SPA_CONTAINER_OF(sess->obj.session, struct impl, this);
814 	struct pw_session_info *i = sess->info;
815 
816 	pw_log_debug("%p: session %d info", impl, sess->obj.id);
817 	if (i == NULL && info) {
818 		i = sess->info = calloc(1, sizeof(struct pw_session_info));
819 		i->version = PW_VERSION_SESSION_INFO;
820 		i->id = info->id;
821         }
822 	if (info) {
823 		i->change_mask = info->change_mask;
824 		if (info->change_mask & PW_SESSION_CHANGE_MASK_PROPS) {
825 			pw_properties_free ((struct pw_properties *)i->props);
826 			i->props = (struct spa_dict *) pw_properties_new_dict (info->props);
827 		}
828 	}
829 
830 	sess->obj.avail |= SM_SESSION_CHANGE_MASK_INFO;
831 	sess->obj.changed |= SM_SESSION_CHANGE_MASK_INFO;
832 	sm_object_sync_update(&sess->obj);
833 }
834 
835 static const struct pw_session_events session_events = {
836 	PW_VERSION_SESSION_EVENTS,
837 	.info = session_event_info,
838 };
839 
session_init(void * object)840 static int session_init(void *object)
841 {
842 	struct sm_session *sess = object;
843 	struct impl *impl = SPA_CONTAINER_OF(sess->obj.session, struct impl, this);
844 
845 	if (sess->obj.id == impl->this.session_id)
846 		impl->this.session = sess;
847 
848 	spa_list_init(&sess->endpoint_list);
849 	return 0;
850 }
851 
session_destroy(void * object)852 static void session_destroy(void *object)
853 {
854 	struct sm_session *sess = object;
855 	struct sm_endpoint *endpoint;
856 	struct pw_session_info *i = sess->info;
857 
858 	spa_list_consume(endpoint, &sess->endpoint_list, link) {
859 		endpoint->session = NULL;
860 		spa_list_remove(&endpoint->link);
861 	}
862 	if (i) {
863 		pw_properties_free ((struct pw_properties *)i->props);
864 		free(i);
865 	}
866 
867 }
868 
869 static const struct object_info session_info = {
870 	.type = PW_TYPE_INTERFACE_Session,
871 	.version = PW_VERSION_SESSION,
872 	.events = &session_events,
873 	.size = sizeof(struct sm_session),
874 	.init = session_init,
875 	.destroy = session_destroy,
876 };
877 
878 /**
879  * Endpoint
880  */
endpoint_event_info(void * object,const struct pw_endpoint_info * info)881 static void endpoint_event_info(void *object, const struct pw_endpoint_info *info)
882 {
883 	struct sm_endpoint *endpoint = object;
884 	struct impl *impl = SPA_CONTAINER_OF(endpoint->obj.session, struct impl, this);
885 	struct pw_endpoint_info *i = endpoint->info;
886 	const char *str;
887 
888 	pw_log_debug("%p: endpoint %d info", impl, endpoint->obj.id);
889 	if (i == NULL && info) {
890 		i = endpoint->info = calloc(1, sizeof(struct pw_endpoint_info));
891 		i->id = info->id;
892 		i->name = info->name ? strdup(info->name) : NULL;
893 		i->media_class = info->media_class ? strdup(info->media_class) : NULL;
894 		i->direction = info->direction;
895 		i->flags = info->flags;
896         }
897 	if (info) {
898 		i->change_mask = info->change_mask;
899 		if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) {
900 			i->session_id = info->session_id;
901 		}
902 		if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
903 			pw_properties_free ((struct pw_properties *)i->props);
904 			i->props = (struct spa_dict *) pw_properties_new_dict (info->props);
905 			if ((str = spa_dict_lookup(i->props, PW_KEY_PRIORITY_SESSION)) != NULL)
906 				endpoint->priority = pw_properties_parse_int(str);
907 		}
908 	}
909 
910 	endpoint->obj.avail |= SM_ENDPOINT_CHANGE_MASK_INFO;
911 	endpoint->obj.changed |= SM_ENDPOINT_CHANGE_MASK_INFO;
912 	sm_object_sync_update(&endpoint->obj);
913 }
914 
915 static const struct pw_endpoint_events endpoint_events = {
916 	PW_VERSION_ENDPOINT_EVENTS,
917 	.info = endpoint_event_info,
918 };
919 
endpoint_init(void * object)920 static int endpoint_init(void *object)
921 {
922 	struct sm_endpoint *endpoint = object;
923 	struct impl *impl = SPA_CONTAINER_OF(endpoint->obj.session, struct impl, this);
924 	struct pw_properties *props = endpoint->obj.props;
925 
926 	if (props) {
927 		uint32_t id = SPA_ID_INVALID;
928 
929 		if (pw_properties_fetch_uint32(props, PW_KEY_SESSION_ID, &id) == 0)
930 			endpoint->session = find_object(impl, id, PW_TYPE_INTERFACE_Session);
931 		pw_log_debug("%p: endpoint %d parent session %d", impl,
932 				endpoint->obj.id, id);
933 		if (endpoint->session) {
934 			spa_list_append(&endpoint->session->endpoint_list, &endpoint->link);
935 			endpoint->session->obj.avail |= SM_SESSION_CHANGE_MASK_ENDPOINTS;
936 			endpoint->session->obj.changed |= SM_SESSION_CHANGE_MASK_ENDPOINTS;
937 		}
938 	}
939 	spa_list_init(&endpoint->stream_list);
940 
941 	return 0;
942 }
943 
endpoint_destroy(void * object)944 static void endpoint_destroy(void *object)
945 {
946 	struct sm_endpoint *endpoint = object;
947 	struct sm_endpoint_stream *stream;
948 	struct pw_endpoint_info *i = endpoint->info;
949 
950 	spa_list_consume(stream, &endpoint->stream_list, link) {
951 		stream->endpoint = NULL;
952 		spa_list_remove(&stream->link);
953 	}
954 	if (endpoint->session) {
955 		endpoint->session = NULL;
956 		spa_list_remove(&endpoint->link);
957 	}
958 	if (i) {
959 		pw_properties_free ((struct pw_properties *)i->props);
960 		free(i->name);
961 		free(i->media_class);
962 		free(i);
963 	}
964 }
965 
966 static const struct object_info endpoint_info = {
967 	.type = PW_TYPE_INTERFACE_Endpoint,
968 	.version = PW_VERSION_ENDPOINT,
969 	.events = &endpoint_events,
970 	.size = sizeof(struct sm_endpoint),
971 	.init = endpoint_init,
972 	.destroy = endpoint_destroy,
973 };
974 
975 
976 /**
977  * Endpoint Stream
978  */
endpoint_stream_event_info(void * object,const struct pw_endpoint_stream_info * info)979 static void endpoint_stream_event_info(void *object, const struct pw_endpoint_stream_info *info)
980 {
981 	struct sm_endpoint_stream *stream = object;
982 	struct impl *impl = SPA_CONTAINER_OF(stream->obj.session, struct impl, this);
983 
984 	pw_log_debug("%p: endpoint stream %d info", impl, stream->obj.id);
985 	if (stream->info == NULL && info) {
986 		stream->info = calloc(1, sizeof(struct pw_endpoint_stream_info));
987 		stream->info->version = PW_VERSION_ENDPOINT_STREAM_INFO;
988 		stream->info->id = info->id;
989 		stream->info->endpoint_id = info->endpoint_id;
990 		stream->info->name = info->name ? strdup(info->name) : NULL;
991         }
992 	if (info) {
993 		stream->info->change_mask = info->change_mask;
994 	}
995 
996 	stream->obj.avail |= SM_ENDPOINT_CHANGE_MASK_INFO;
997 	stream->obj.changed |= SM_ENDPOINT_CHANGE_MASK_INFO;
998 	sm_object_sync_update(&stream->obj);
999 }
1000 
1001 static const struct pw_endpoint_stream_events endpoint_stream_events = {
1002 	PW_VERSION_ENDPOINT_STREAM_EVENTS,
1003 	.info = endpoint_stream_event_info,
1004 };
1005 
endpoint_stream_init(void * object)1006 static int endpoint_stream_init(void *object)
1007 {
1008 	struct sm_endpoint_stream *stream = object;
1009 	struct impl *impl = SPA_CONTAINER_OF(stream->obj.session, struct impl, this);
1010 	struct pw_properties *props = stream->obj.props;
1011 
1012 	if (props) {
1013 		uint32_t id = SPA_ID_INVALID;
1014 
1015 		if (pw_properties_fetch_uint32(props, PW_KEY_ENDPOINT_ID, &id) == 0)
1016 			stream->endpoint = find_object(impl, id, PW_TYPE_INTERFACE_Endpoint);
1017 		pw_log_debug("%p: stream %d parent endpoint %d", impl,
1018 				stream->obj.id, id);
1019 		if (stream->endpoint) {
1020 			spa_list_append(&stream->endpoint->stream_list, &stream->link);
1021 			stream->endpoint->obj.avail |= SM_ENDPOINT_CHANGE_MASK_STREAMS;
1022 			stream->endpoint->obj.changed |= SM_ENDPOINT_CHANGE_MASK_STREAMS;
1023 		}
1024 	}
1025 	spa_list_init(&stream->link_list);
1026 
1027 	return 0;
1028 }
1029 
endpoint_stream_destroy(void * object)1030 static void endpoint_stream_destroy(void *object)
1031 {
1032 	struct sm_endpoint_stream *stream = object;
1033 
1034 	if (stream->info) {
1035 		free(stream->info->name);
1036 		free(stream->info);
1037 	}
1038 	if (stream->endpoint) {
1039 		stream->endpoint = NULL;
1040 		spa_list_remove(&stream->link);
1041 	}
1042 }
1043 
1044 static const struct object_info endpoint_stream_info = {
1045 	.type = PW_TYPE_INTERFACE_EndpointStream,
1046 	.version = PW_VERSION_ENDPOINT_STREAM,
1047 	.events = &endpoint_stream_events,
1048 	.size = sizeof(struct sm_endpoint_stream),
1049 	.init = endpoint_stream_init,
1050 	.destroy = endpoint_stream_destroy,
1051 };
1052 
1053 /**
1054  * Endpoint Link
1055  */
endpoint_link_event_info(void * object,const struct pw_endpoint_link_info * info)1056 static void endpoint_link_event_info(void *object, const struct pw_endpoint_link_info *info)
1057 {
1058 	struct sm_endpoint_link *link = object;
1059 	struct impl *impl = SPA_CONTAINER_OF(link->obj.session, struct impl, this);
1060 
1061 	pw_log_debug("%p: endpoint link %d info", impl, link->obj.id);
1062 	if (link->info == NULL && info) {
1063 		link->info = calloc(1, sizeof(struct pw_endpoint_link_info));
1064 		link->info->version = PW_VERSION_ENDPOINT_LINK_INFO;
1065 		link->info->id = info->id;
1066 		link->info->session_id = info->session_id;
1067 		link->info->output_endpoint_id = info->output_endpoint_id;
1068 		link->info->output_stream_id = info->output_stream_id;
1069 		link->info->input_endpoint_id = info->input_endpoint_id;
1070 		link->info->input_stream_id = info->input_stream_id;
1071 	}
1072 	if (info) {
1073 		link->info->change_mask = info->change_mask;
1074 	}
1075 
1076 	link->obj.avail |= SM_ENDPOINT_LINK_CHANGE_MASK_INFO;
1077 	link->obj.changed |= SM_ENDPOINT_LINK_CHANGE_MASK_INFO;
1078 	sm_object_sync_update(&link->obj);
1079 }
1080 
1081 static const struct pw_endpoint_link_events endpoint_link_events = {
1082 	PW_VERSION_ENDPOINT_LINK_EVENTS,
1083 	.info = endpoint_link_event_info,
1084 };
1085 
endpoint_link_destroy(void * object)1086 static void endpoint_link_destroy(void *object)
1087 {
1088 	struct sm_endpoint_link *link = object;
1089 
1090 	if (link->info) {
1091 		free(link->info->error);
1092 		free(link->info);
1093 	}
1094 	if (link->output) {
1095 		link->output = NULL;
1096 		spa_list_remove(&link->output_link);
1097 	}
1098 	if (link->input) {
1099 		link->input = NULL;
1100 		spa_list_remove(&link->input_link);
1101 	}
1102 }
1103 
1104 static const struct object_info endpoint_link_info = {
1105 	.type = PW_TYPE_INTERFACE_EndpointLink,
1106 	.version = PW_VERSION_ENDPOINT_LINK,
1107 	.events = &endpoint_link_events,
1108 	.size = sizeof(struct sm_endpoint_link),
1109 	.init = NULL,
1110 	.destroy = endpoint_link_destroy,
1111 };
1112 
1113 /**
1114  * Proxy
1115  */
done_proxy(void * data,int seq)1116 static void done_proxy(void *data, int seq)
1117 {
1118 	struct sm_object *obj = data;
1119 
1120 	pw_log_debug("done %p proxy %p avail:%08x update:%08x %d/%d", obj,
1121 			obj->proxy, obj->avail, obj->changed, obj->pending, seq);
1122 
1123 	if (obj->pending == seq) {
1124 		obj->pending = SPA_ID_INVALID;
1125 		if (obj->changed)
1126 			sm_object_emit_update(obj);
1127 		obj->changed = 0;
1128 	}
1129 }
1130 
1131 static const struct pw_proxy_events proxy_events = {
1132 	PW_VERSION_PROXY_EVENTS,
1133 	.done = done_proxy,
1134 };
1135 
bound_handle(void * data,uint32_t id)1136 static void bound_handle(void *data, uint32_t id)
1137 {
1138 	struct sm_object *obj = data;
1139 	struct impl *impl = SPA_CONTAINER_OF(obj->session, struct impl, this);
1140 
1141 	pw_log_debug("bound %p proxy %p handle %p id:%d->%d",
1142 			obj, obj->proxy, obj->handle, obj->id, id);
1143 
1144 	if (obj->id == SPA_ID_INVALID) {
1145 		struct sm_object *old_obj = find_object(impl, id, NULL);
1146 
1147 		if (old_obj != NULL) {
1148 			/*
1149 			 * Monitor core is always more up-to-date in object creation
1150 			 * events (see registry_global), so in case of duplicate objects
1151 			 * we should prefer monitor globals.
1152 			 */
1153 			if (obj->monitor_global)
1154 				sm_object_destroy_maybe_free(old_obj);
1155 			else {
1156 				sm_object_destroy_maybe_free(obj);
1157 				return;
1158 			}
1159 		}
1160 
1161 		add_object(impl, obj, id);
1162 	}
1163 }
1164 
1165 static const struct pw_proxy_events handle_events = {
1166 	PW_VERSION_PROXY_EVENTS,
1167 	.bound = bound_handle,
1168 };
1169 
sm_object_sync_update(struct sm_object * obj)1170 int sm_object_sync_update(struct sm_object *obj)
1171 {
1172 	obj->pending = pw_proxy_sync(obj->proxy, 1);
1173 	pw_log_debug("sync %p proxy %p %d", obj, obj->proxy, obj->pending);
1174 	return obj->pending;
1175 }
1176 
get_object_info(struct impl * impl,const char * type)1177 static const struct object_info *get_object_info(struct impl *impl, const char *type)
1178 {
1179 	const struct object_info *info;
1180 
1181 	if (spa_streq(type, PW_TYPE_INTERFACE_Core))
1182 		info = &core_object_info;
1183 	else if (spa_streq(type, PW_TYPE_INTERFACE_Module))
1184 		info = &module_info;
1185 	else if (spa_streq(type, PW_TYPE_INTERFACE_Factory))
1186 		info = &factory_info;
1187 	else if (spa_streq(type, PW_TYPE_INTERFACE_Client))
1188 		info = &client_info;
1189 	else if (spa_streq(type, SPA_TYPE_INTERFACE_Device))
1190 		info = &spa_device_info;
1191 	else if (spa_streq(type, PW_TYPE_INTERFACE_Device))
1192 		info = &device_info;
1193 	else if (spa_streq(type, PW_TYPE_INTERFACE_Node))
1194 		info = &node_info;
1195 	else if (spa_streq(type, PW_TYPE_INTERFACE_Port))
1196 		info = &port_info;
1197 	else if (spa_streq(type, PW_TYPE_INTERFACE_Session))
1198 		info = &session_info;
1199 	else if (spa_streq(type, PW_TYPE_INTERFACE_Endpoint))
1200 		info = &endpoint_info;
1201 	else if (spa_streq(type, PW_TYPE_INTERFACE_EndpointStream))
1202 		info = &endpoint_stream_info;
1203 	else if (spa_streq(type, PW_TYPE_INTERFACE_EndpointLink))
1204 		info = &endpoint_link_info;
1205 	else
1206 		info = NULL;
1207 
1208 	return info;
1209 }
1210 
init_object(struct impl * impl,const struct object_info * info,struct pw_proxy * proxy,struct pw_proxy * handle,uint32_t id,const struct spa_dict * props,bool monitor_global)1211 static struct sm_object *init_object(struct impl *impl, const struct object_info *info,
1212 		struct pw_proxy *proxy, struct pw_proxy *handle, uint32_t id,
1213 		const struct spa_dict *props, bool monitor_global)
1214 {
1215 	struct sm_object *obj;
1216 
1217 	obj = pw_proxy_get_user_data(handle);
1218 	obj->session = &impl->this;
1219 	obj->id = id;
1220 	obj->type = info->type;
1221 	obj->props = props ? pw_properties_new_dict(props) : pw_properties_new(NULL, NULL);
1222 	obj->proxy = proxy;
1223 	obj->handle = handle;
1224 	obj->destroy = info->destroy;
1225 	obj->mask |= SM_OBJECT_CHANGE_MASK_PROPERTIES | SM_OBJECT_CHANGE_MASK_BIND;
1226 	obj->avail |= obj->mask;
1227 	obj->monitor_global = monitor_global;
1228 	spa_hook_list_init(&obj->hooks);
1229 	spa_list_init(&obj->data);
1230 
1231 	spa_list_append(&impl->object_list, &obj->link);
1232 
1233 	if (proxy) {
1234 		pw_proxy_add_listener(obj->proxy, &obj->proxy_listener, &proxy_events, obj);
1235 		if (info->events != NULL)
1236 			pw_proxy_add_object_listener(obj->proxy, &obj->object_listener, info->events, obj);
1237 		SPA_FLAG_UPDATE(obj->mask, SM_OBJECT_CHANGE_MASK_LISTENER, info->events != NULL);
1238 	}
1239 	pw_proxy_add_listener(obj->handle, &obj->handle_listener, &handle_events, obj);
1240 
1241 	if (info->init)
1242 		info->init(obj);
1243 
1244 	return obj;
1245 }
1246 
1247 static struct sm_object *
create_object(struct impl * impl,struct pw_proxy * proxy,struct pw_proxy * handle,const struct spa_dict * props,bool monitor_global)1248 create_object(struct impl *impl, struct pw_proxy *proxy, struct pw_proxy *handle,
1249 		const struct spa_dict *props, bool monitor_global)
1250 {
1251 	const char *type;
1252 	const struct object_info *info;
1253 	struct sm_object *obj;
1254 
1255 	type = pw_proxy_get_type(handle, NULL);
1256 
1257 	if (spa_streq(type, PW_TYPE_INTERFACE_ClientNode))
1258 		type = PW_TYPE_INTERFACE_Node;
1259 
1260 	info = get_object_info(impl, type);
1261 	if (info == NULL) {
1262 		pw_log_error("%p: unknown object type %s", impl, type);
1263 		errno = ENOTSUP;
1264 		return NULL;
1265 	}
1266 	obj = init_object(impl, info, proxy, handle, SPA_ID_INVALID, props, monitor_global);
1267 
1268 	pw_log_debug("%p: created new object %p proxy:%p handle:%p", impl,
1269 			obj, obj->proxy, obj->handle);
1270 
1271 	return obj;
1272 }
1273 
1274 static struct sm_object *
bind_object(struct impl * impl,const struct object_info * info,struct registry_event * re)1275 bind_object(struct impl *impl, const struct object_info *info, struct registry_event *re)
1276 {
1277 	struct pw_proxy *proxy;
1278 	struct sm_object *obj;
1279 
1280 	proxy = re->proxy;
1281 	re->proxy = NULL;
1282 
1283 	obj = init_object(impl, info, proxy, proxy, re->id, re->props, false);
1284 	sm_object_discard(obj);
1285 	add_object(impl, obj, re->id);
1286 
1287 	pw_log_debug("%p: bound new object %p proxy %p id:%d", impl, obj, obj->proxy, obj->id);
1288 
1289 	return obj;
1290 }
1291 
1292 static int
update_object(struct impl * impl,const struct object_info * info,struct sm_object * obj,struct registry_event * re)1293 update_object(struct impl *impl, const struct object_info *info, struct sm_object *obj,
1294               struct registry_event *re)
1295 {
1296 	struct pw_proxy *proxy;
1297 
1298 	pw_properties_update(obj->props, re->props);
1299 
1300 	if (obj->proxy != NULL)
1301 		return 0;
1302 
1303 	pw_log_debug("%p: update type:%s", impl, obj->type);
1304 
1305 	proxy = re->proxy;
1306 	re->proxy = NULL;
1307 
1308 	obj->proxy = proxy;
1309 	obj->type = info->type;
1310 
1311 	pw_proxy_add_listener(obj->proxy, &obj->proxy_listener, &proxy_events, obj);
1312 	if (info->events)
1313 		pw_proxy_add_object_listener(obj->proxy, &obj->object_listener, info->events, obj);
1314 
1315 	SPA_FLAG_UPDATE(obj->mask, SM_OBJECT_CHANGE_MASK_LISTENER, info->events != NULL);
1316 
1317 	sm_media_session_emit_create(impl, obj);
1318 
1319 	return 0;
1320 }
1321 
registry_event_free(struct registry_event * re)1322 static void registry_event_free(struct registry_event *re)
1323 {
1324 	if (re->proxy)
1325 		pw_proxy_destroy(re->proxy);
1326 	pw_properties_free(re->props_store);
1327 	if (re->allocated) {
1328 		spa_list_remove(&re->link);
1329 		free(re);
1330 	} else {
1331 		spa_zero(*re);
1332 	}
1333 }
1334 
handle_registry_event(struct impl * impl,struct registry_event * re)1335 static int handle_registry_event(struct impl *impl, struct registry_event *re)
1336 {
1337 	struct sm_object *obj;
1338 	const struct object_info *info = NULL;
1339 
1340 	obj = find_object(impl, re->id, NULL);
1341 
1342 	pw_log_debug("%p: new global '%d' %s/%d obj:%p monitor:%d seq:%d",
1343 			impl, re->id, re->type, re->version, obj, re->monitor, re->seq);
1344 
1345 	info = get_object_info(impl, re->type);
1346 	if (info == NULL)
1347 		return 0;
1348 
1349 	if (obj == NULL && !re->monitor) {
1350 		/*
1351 		 * Only policy core binds new objects.
1352 		 *
1353 		 * The monitor core event corresponding to this one has already been
1354 		 * processed. If monitor doesn't have the id now, the object either has
1355 		 * not been created there, or there is a race condition and it was already
1356 		 * removed. In that case, we create a zombie object here, but its remove
1357 		 * event is already queued and arrives soon.
1358 		 */
1359 		bind_object(impl, info, re);
1360 	} else if (obj != NULL && obj->monitor_global == re->monitor) {
1361 		/* Each core handles their own object updates */
1362 		update_object(impl, info, obj, re);
1363 	}
1364 
1365 	sm_media_session_schedule_rescan(&impl->this);
1366 	return 0;
1367 }
1368 
handle_postponed_registry_events(struct impl * impl,int seq)1369 static int handle_postponed_registry_events(struct impl *impl, int seq)
1370 {
1371 	struct registry_event *re, *t;
1372 
1373 	spa_list_for_each_safe(re, t, &impl->registry_event_list, link) {
1374 		if (re->seq == seq) {
1375 			handle_registry_event(impl, re);
1376 			registry_event_free(re);
1377 		}
1378 	}
1379 	return 0;
1380 }
1381 
monitor_sync(struct impl * impl)1382 static int monitor_sync(struct impl *impl)
1383 {
1384 	pw_core_set_paused(impl->policy_core, true);
1385 	impl->monitor_seq = pw_core_sync(impl->monitor_core, 0, impl->monitor_seq);
1386 	pw_log_debug("%p: monitor sync start %d", impl, impl->monitor_seq);
1387 	sm_media_session_schedule_rescan(&impl->this);
1388 	return impl->monitor_seq;
1389 }
1390 
1391 static void
registry_global(void * data,uint32_t id,uint32_t permissions,const char * type,uint32_t version,const struct spa_dict * props)1392 registry_global(void *data, uint32_t id,
1393 		uint32_t permissions, const char *type, uint32_t version,
1394 		const struct spa_dict *props)
1395 {
1396 	struct impl *impl = data;
1397 	const struct object_info *info;
1398 	struct registry_event *re = NULL;
1399 	static bool warned_about_wireplumber = false;
1400 
1401 	info = get_object_info(impl, type);
1402 	if (info == NULL)
1403 		return;
1404 
1405 	pw_log_debug("%p: registry event (policy) for new global '%d'", impl, id);
1406 
1407 	if (!warned_about_wireplumber && props &&
1408 	    spa_streq(info->type, PW_TYPE_INTERFACE_Client)) {
1409 		const char *name = spa_dict_lookup(props, PW_KEY_APP_NAME);
1410 		if (spa_streq(name, "WirePlumber")) {
1411 			pw_log_error("WirePlumber appears to be running; "
1412 				     "please stop it before starting pipewire-media-session");
1413 			warned_about_wireplumber = true;
1414 		}
1415 	}
1416 
1417 	/*
1418 	 * Handle policy core events after monitor core ones.
1419 	 *
1420 	 * Monitor sync pauses policy core, so the event will be handled before
1421 	 * further registry or proxy events are received via policy core.
1422 	 */
1423 	re = calloc(1, sizeof(struct registry_event));
1424 	if (re == NULL)
1425 		goto error;
1426 
1427 	re->allocated = true;
1428 	spa_list_append(&impl->registry_event_list, &re->link);
1429 
1430 	re->id = id;
1431 	re->monitor = false;
1432 	re->permissions = permissions;
1433 	re->type = info->type;
1434 	re->version = version;
1435 
1436 	/* Bind proxy now */
1437 	re->proxy = pw_registry_bind(impl->registry, id, type, info->version, info->size);
1438 	if (re->proxy == NULL)
1439 		goto error;
1440 
1441 	if (props) {
1442 		re->props_store = pw_properties_new_dict(props);
1443 		if (re->props_store == NULL)
1444 			goto error;
1445 		re->props = &re->props_store->dict;
1446 	}
1447 
1448 	re->seq = monitor_sync(impl);
1449 
1450 	return;
1451 
1452 error:
1453 	if (re)
1454 		registry_event_free(re);
1455 	pw_log_warn("%p: can't handle global %d: %s", impl, id, spa_strerror(-errno));
1456 }
1457 
1458 static void
registry_global_remove(void * data,uint32_t id)1459 registry_global_remove(void *data, uint32_t id)
1460 {
1461 	struct impl *impl = data;
1462 	struct sm_object *obj;
1463 
1464 	obj = find_object(impl, id, NULL);
1465 	obj = (obj && !obj->monitor_global) ? obj : NULL;
1466 
1467 	pw_log_debug("%p: registry event (policy) for remove global '%d' obj:%p",
1468 			impl, id, obj);
1469 
1470 	if (obj)
1471 		sm_object_destroy_maybe_free(obj);
1472 }
1473 
1474 static const struct pw_registry_events registry_events = {
1475 	PW_VERSION_REGISTRY_EVENTS,
1476 	.global = registry_global,
1477 	.global_remove = registry_global_remove,
1478 };
1479 
1480 static void
monitor_registry_global(void * data,uint32_t id,uint32_t permissions,const char * type,uint32_t version,const struct spa_dict * props)1481 monitor_registry_global(void *data, uint32_t id,
1482 		uint32_t permissions, const char *type, uint32_t version,
1483 		const struct spa_dict *props)
1484 {
1485 	struct impl *impl = data;
1486 	const struct object_info *info;
1487 	struct registry_event re = {
1488 		.id = id, .permissions = permissions, .type = type, .version = version,
1489 		.props = props,	.monitor = true
1490 	};
1491 
1492 	pw_log_debug("%p: registry event (monitor) for new global '%d'", impl, id);
1493 
1494 	info = get_object_info(impl, type);
1495 	if (info == NULL)
1496 		return;
1497 
1498 	/* Bind proxy now from policy core */
1499 	re.proxy = pw_registry_bind(impl->registry, id, type, info->version, 0);
1500 	if (re.proxy)
1501 		handle_registry_event(impl, &re);
1502 	else
1503 		pw_log_warn("%p: can't handle global %d: %s", impl, id, spa_strerror(-errno));
1504 
1505 	registry_event_free(&re);
1506 	return;
1507 }
1508 
1509 static void
monitor_registry_global_remove(void * data,uint32_t id)1510 monitor_registry_global_remove(void *data, uint32_t id)
1511 {
1512 	struct impl *impl = data;
1513 	struct sm_object *obj;
1514 
1515 	obj = find_object(impl, id, NULL);
1516 	obj = (obj && obj->monitor_global) ? obj : NULL;
1517 
1518 	pw_log_debug("%p: registry event (monitor) for remove global '%d' obj:%p", impl, id, obj);
1519 
1520 	if (obj)
1521 		sm_object_destroy_maybe_free(obj);
1522 }
1523 
1524 static const struct pw_registry_events monitor_registry_events = {
1525       PW_VERSION_REGISTRY_EVENTS,
1526       .global = monitor_registry_global,
1527       .global_remove = monitor_registry_global_remove,
1528 };
1529 
sm_object_add_listener(struct sm_object * obj,struct spa_hook * listener,const struct sm_object_events * events,void * data)1530 int sm_object_add_listener(struct sm_object *obj, struct spa_hook *listener,
1531 		const struct sm_object_events *events, void *data)
1532 {
1533 	spa_hook_list_append(&obj->hooks, listener, events, data);
1534 	return 0;
1535 }
1536 
sm_media_session_add_listener(struct sm_media_session * sess,struct spa_hook * listener,const struct sm_media_session_events * events,void * data)1537 int sm_media_session_add_listener(struct sm_media_session *sess, struct spa_hook *listener,
1538                 const struct sm_media_session_events *events, void *data)
1539 {
1540 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1541 	struct spa_hook_list save;
1542 	struct sm_object *obj;
1543 
1544 	spa_hook_list_isolate(&impl->hooks, &save, listener, events, data);
1545 
1546 	spa_list_for_each(obj, &impl->object_list, link) {
1547 		if (obj->id == SPA_ID_INVALID)
1548 			continue;
1549 		sm_media_session_emit_create(impl, obj);
1550 	}
1551 
1552         spa_hook_list_join(&impl->hooks, &save);
1553 
1554 	return 0;
1555 }
1556 
sm_media_session_find_object(struct sm_media_session * sess,uint32_t id)1557 struct sm_object *sm_media_session_find_object(struct sm_media_session *sess, uint32_t id)
1558 {
1559 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1560 	return find_object(impl, id, NULL);
1561 }
1562 
sm_media_session_destroy_object(struct sm_media_session * sess,uint32_t id)1563 int sm_media_session_destroy_object(struct sm_media_session *sess, uint32_t id)
1564 {
1565 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1566 	pw_registry_destroy(impl->registry, id);
1567 	return 0;
1568 }
1569 
sm_media_session_for_each_object(struct sm_media_session * sess,int (* callback)(void * data,struct sm_object * object),void * data)1570 int sm_media_session_for_each_object(struct sm_media_session *sess,
1571                             int (*callback) (void *data, struct sm_object *object),
1572                             void *data)
1573 {
1574 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1575 	struct sm_object *obj;
1576 	int res;
1577 
1578 	spa_list_for_each(obj, &impl->object_list, link) {
1579 		if (obj->id == SPA_ID_INVALID)
1580 			continue;
1581 		if ((res = callback(data, obj)) != 0)
1582 			return res;
1583 	}
1584 	return 0;
1585 }
1586 
sm_media_session_schedule_rescan(struct sm_media_session * sess)1587 int sm_media_session_schedule_rescan(struct sm_media_session *sess)
1588 {
1589 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1590 
1591 	if (impl->scanning) {
1592 		impl->rescan_pending = true;
1593 		return impl->rescan_seq;
1594 	}
1595 	if (impl->policy_core)
1596 		impl->rescan_seq = pw_core_sync(impl->policy_core, 0, impl->last_seq);
1597 	return impl->rescan_seq;
1598 }
1599 
sm_media_session_sync(struct sm_media_session * sess,void (* callback)(void * data),void * data)1600 int sm_media_session_sync(struct sm_media_session *sess,
1601 		void (*callback) (void *data), void *data)
1602 {
1603 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1604 	struct sync *sync;
1605 
1606 	sync = calloc(1, sizeof(struct sync));
1607 	if (sync == NULL)
1608 		return -errno;
1609 
1610 	spa_list_append(&impl->sync_list, &sync->link);
1611 	sync->callback = callback;
1612 	sync->data = data;
1613 	sync->seq = pw_core_sync(impl->policy_core, 0, impl->last_seq);
1614 	return sync->seq;
1615 }
1616 
roundtrip_callback(void * data)1617 static void roundtrip_callback(void *data)
1618 {
1619 	int *done = data;
1620 	*done = 1;
1621 }
1622 
sm_media_session_roundtrip(struct sm_media_session * sess)1623 int sm_media_session_roundtrip(struct sm_media_session *sess)
1624 {
1625 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1626 	struct pw_loop *loop = impl->this.loop;
1627 	int done, res, seq;
1628 
1629 	if (impl->policy_core == NULL)
1630 		return -EIO;
1631 
1632 	done = 0;
1633 	if ((seq = sm_media_session_sync(sess, roundtrip_callback, &done)) < 0)
1634 		return seq;
1635 
1636 	pw_log_debug("%p: roundtrip %d", impl, seq);
1637 
1638 	pw_loop_enter(loop);
1639 	while (!done) {
1640 		if ((res = pw_loop_iterate(loop, -1)) < 0) {
1641 			if (res == -EINTR)
1642 				continue;
1643 			pw_log_warn("%p: iterate error %d (%s)",
1644 				loop, res, spa_strerror(res));
1645 			break;
1646 		}
1647 	}
1648         pw_loop_leave(loop);
1649 
1650 	pw_log_debug("%p: roundtrip %d done", impl, seq);
1651 
1652 	return 0;
1653 }
1654 
sm_media_session_export(struct sm_media_session * sess,const char * type,const struct spa_dict * props,void * object,size_t user_data_size)1655 struct pw_proxy *sm_media_session_export(struct sm_media_session *sess,
1656 		const char *type, const struct spa_dict *props,
1657 		void *object, size_t user_data_size)
1658 {
1659 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1660 	struct pw_proxy *handle;
1661 
1662 	pw_log_debug("%p: object %s %p", impl, type, object);
1663 
1664 	handle = pw_core_export(impl->monitor_core, type,
1665 			props, object, user_data_size);
1666 
1667 	monitor_sync(impl);
1668 
1669 	return handle;
1670 }
1671 
sm_media_session_export_node(struct sm_media_session * sess,const struct spa_dict * props,struct pw_impl_node * object)1672 struct sm_node *sm_media_session_export_node(struct sm_media_session *sess,
1673 		const struct spa_dict *props, struct pw_impl_node *object)
1674 {
1675 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1676 	struct sm_node *node;
1677 	struct pw_proxy *handle;
1678 
1679 	pw_log_debug("%p: node %p", impl, object);
1680 
1681 	handle = pw_core_export(impl->monitor_core, PW_TYPE_INTERFACE_Node,
1682 			props, object, sizeof(struct sm_node));
1683 
1684 	node = (struct sm_node *) create_object(impl, NULL, handle, props, true);
1685 
1686 	monitor_sync(impl);
1687 
1688 	return node;
1689 }
1690 
sm_media_session_export_device(struct sm_media_session * sess,const struct spa_dict * props,struct spa_device * object)1691 struct sm_device *sm_media_session_export_device(struct sm_media_session *sess,
1692 		const struct spa_dict *props, struct spa_device *object)
1693 {
1694 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1695 	struct sm_device *device;
1696 	struct pw_proxy *handle;
1697 
1698 	pw_log_debug("%p: device %p", impl, object);
1699 
1700 	handle = pw_core_export(impl->monitor_core, SPA_TYPE_INTERFACE_Device,
1701 			props, object, sizeof(struct sm_device));
1702 
1703 	device = (struct sm_device *) create_object(impl, NULL, handle, props, true);
1704 
1705 	monitor_sync(impl);
1706 
1707 	return device;
1708 }
1709 
sm_media_session_create_object(struct sm_media_session * sess,const char * factory_name,const char * type,uint32_t version,const struct spa_dict * props,size_t user_data_size)1710 struct pw_proxy *sm_media_session_create_object(struct sm_media_session *sess,
1711 		const char *factory_name, const char *type, uint32_t version,
1712 		const struct spa_dict *props, size_t user_data_size)
1713 {
1714 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1715 	return pw_core_create_object(impl->policy_core,
1716 			factory_name, type, version, props, user_data_size);
1717 }
1718 
sm_media_session_create_node(struct sm_media_session * sess,const char * factory_name,const struct spa_dict * props)1719 struct sm_node *sm_media_session_create_node(struct sm_media_session *sess,
1720 		const char *factory_name, const struct spa_dict *props)
1721 {
1722 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1723 	struct sm_node *node;
1724 	struct pw_proxy *proxy;
1725 
1726 	pw_log_debug("%p: node '%s'", impl, factory_name);
1727 
1728 	proxy = pw_core_create_object(impl->policy_core,
1729 				factory_name,
1730 				PW_TYPE_INTERFACE_Node,
1731 				PW_VERSION_NODE,
1732 				props,
1733 				sizeof(struct sm_node));
1734 
1735 	node = (struct sm_node *)create_object(impl, proxy, proxy, props, false);
1736 
1737 	return node;
1738 }
1739 
check_endpoint_link(struct endpoint_link * link)1740 static void check_endpoint_link(struct endpoint_link *link)
1741 {
1742 	if (!spa_list_is_empty(&link->link_list))
1743 		return;
1744 
1745 	if (link->impl) {
1746 		spa_list_remove(&link->link);
1747 		pw_map_remove(&link->impl->endpoint_links, link->id);
1748 
1749 		pw_client_session_link_update(link->impl->this.client_session,
1750 				link->id,
1751 				PW_CLIENT_SESSION_LINK_UPDATE_DESTROYED,
1752 				0, NULL, NULL);
1753 
1754 		link->impl = NULL;
1755 		free(link);
1756 	}
1757 }
1758 
proxy_link_error(void * data,int seq,int res,const char * message)1759 static void proxy_link_error(void *data, int seq, int res, const char *message)
1760 {
1761 	struct link *l = data;
1762 	pw_log_info("can't link %d:%d -> %d:%d: %s",
1763 			l->output_node, l->output_port,
1764 			l->input_node, l->input_port, message);
1765 	pw_proxy_destroy(l->proxy);
1766 }
1767 
proxy_link_removed(void * data)1768 static void proxy_link_removed(void *data)
1769 {
1770 	struct link *l = data;
1771 	pw_proxy_destroy(l->proxy);
1772 }
1773 
proxy_link_destroy(void * data)1774 static void proxy_link_destroy(void *data)
1775 {
1776 	struct link *l = data;
1777 
1778 	spa_list_remove(&l->link);
1779 	spa_hook_remove(&l->listener);
1780 
1781 	if (l->endpoint_link) {
1782 		check_endpoint_link(l->endpoint_link);
1783 		l->endpoint_link = NULL;
1784 	}
1785 }
1786 
1787 static const struct pw_proxy_events proxy_link_events = {
1788 	PW_VERSION_PROXY_EVENTS,
1789 	.error = proxy_link_error,
1790 	.removed = proxy_link_removed,
1791 	.destroy = proxy_link_destroy
1792 };
1793 
channel_is_aux(uint32_t channel)1794 static bool channel_is_aux(uint32_t channel)
1795 {
1796 	return channel >= SPA_AUDIO_CHANNEL_START_Aux &&
1797 		channel <= SPA_AUDIO_CHANNEL_LAST_Aux;
1798 }
1799 
score_ports(struct sm_port * out,struct sm_port * in)1800 static int score_ports(struct sm_port *out, struct sm_port *in)
1801 {
1802 	int score = 0;
1803 
1804 	if (in->direction != PW_DIRECTION_INPUT || out->direction != PW_DIRECTION_OUTPUT)
1805 		return 0;
1806 
1807 	if (out->type != SM_PORT_TYPE_UNKNOWN && in->type != SM_PORT_TYPE_UNKNOWN &&
1808 	    in->type != out->type)
1809 		return 0;
1810 
1811 	if (out->channel == in->channel)
1812 		score += 100;
1813 	else if ((out->channel == SPA_AUDIO_CHANNEL_SL && in->channel == SPA_AUDIO_CHANNEL_RL) ||
1814 	         (out->channel == SPA_AUDIO_CHANNEL_RL && in->channel == SPA_AUDIO_CHANNEL_SL) ||
1815 	         (out->channel == SPA_AUDIO_CHANNEL_SR && in->channel == SPA_AUDIO_CHANNEL_RR) ||
1816 	         (out->channel == SPA_AUDIO_CHANNEL_RR && in->channel == SPA_AUDIO_CHANNEL_SR))
1817 		score += 60;
1818 	else if ((out->channel == SPA_AUDIO_CHANNEL_FC && in->channel == SPA_AUDIO_CHANNEL_MONO) ||
1819 	         (out->channel == SPA_AUDIO_CHANNEL_MONO && in->channel == SPA_AUDIO_CHANNEL_FC))
1820 		score += 50;
1821 	else if (in->channel == SPA_AUDIO_CHANNEL_UNKNOWN ||
1822 	    in->channel == SPA_AUDIO_CHANNEL_MONO ||
1823 	    out->channel == SPA_AUDIO_CHANNEL_UNKNOWN ||
1824 	    out->channel == SPA_AUDIO_CHANNEL_MONO)
1825 		score += 10;
1826 	else if (channel_is_aux(in->channel) != channel_is_aux(out->channel))
1827 		score += 7;
1828 	if (score > 0 && !in->visited)
1829 		score += 5;
1830 	if (score <= 10)
1831 		score = 0;
1832 	return score;
1833 }
1834 
find_input_port(struct impl * impl,struct sm_node * outnode,struct sm_port * outport,struct sm_node * innode)1835 static struct sm_port *find_input_port(struct impl *impl, struct sm_node *outnode,
1836 		struct sm_port *outport, struct sm_node *innode)
1837 {
1838 	struct sm_port *inport, *best_port = NULL;
1839 	int score, best_score = 0;
1840 
1841 	spa_list_for_each(inport, &innode->port_list, link) {
1842 		score = score_ports(outport, inport);
1843 		if (score > best_score) {
1844 			best_score = score;
1845 			best_port = inport;
1846 		}
1847 	}
1848 	return best_port;
1849 }
1850 
link_nodes(struct impl * impl,struct endpoint_link * link,struct sm_node * outnode,struct sm_node * innode)1851 static int link_nodes(struct impl *impl, struct endpoint_link *link,
1852 		struct sm_node *outnode, struct sm_node *innode)
1853 {
1854 	struct pw_properties *props;
1855 	struct sm_port *outport, *inport;
1856 	int count = 0;
1857 	bool passive = false;
1858 	const char *str;
1859 
1860 	pw_log_debug("%p: linking %d -> %d", impl, outnode->obj.id, innode->obj.id);
1861 
1862 	if ((str = spa_dict_lookup(outnode->info->props, PW_KEY_NODE_PASSIVE)) != NULL)
1863 		passive |= (pw_properties_parse_bool(str) || spa_streq(str, "out"));
1864 	if ((str = spa_dict_lookup(innode->info->props, PW_KEY_NODE_PASSIVE)) != NULL)
1865 		passive |= (pw_properties_parse_bool(str) || spa_streq(str, "in"));
1866 
1867 	props = pw_properties_new(NULL, NULL);
1868 	pw_properties_setf(props, PW_KEY_LINK_OUTPUT_NODE, "%d", outnode->obj.id);
1869 	pw_properties_setf(props, PW_KEY_LINK_INPUT_NODE, "%d", innode->obj.id);
1870 	pw_properties_setf(props, PW_KEY_LINK_PASSIVE, "%s", passive ? "true" : "false");
1871 
1872 	spa_list_for_each(inport, &innode->port_list, link)
1873 		inport->visited = false;
1874 
1875 	spa_list_for_each(outport, &outnode->port_list, link) {
1876 		struct link *l;
1877 		struct pw_proxy *p;
1878 
1879 		if (outport->direction != PW_DIRECTION_OUTPUT)
1880 			continue;
1881 
1882 		inport = find_input_port(impl, outnode, outport, innode);
1883 		if (inport == NULL) {
1884 			pw_log_debug("%p: port %d:%d can't be linked", impl,
1885 				outport->direction, outport->obj.id);
1886 			continue;
1887 		}
1888 		inport->visited = true;
1889 
1890 		pw_log_debug("%p: port %d:%d -> %d:%d", impl,
1891 				outport->direction, outport->obj.id,
1892 				inport->direction, inport->obj.id);
1893 
1894 		pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", outport->obj.id);
1895 		pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", inport->obj.id);
1896 
1897 		p = pw_core_create_object(impl->policy_core,
1898 					"link-factory",
1899 					PW_TYPE_INTERFACE_Link,
1900 					PW_VERSION_LINK,
1901 					&props->dict, sizeof(struct link));
1902 		if (p == NULL)
1903 			return -errno;
1904 
1905 		l = pw_proxy_get_user_data(p);
1906 		l->proxy = p;
1907 		l->output_node = outnode->obj.id;
1908 		l->output_port = outport->obj.id;
1909 		l->input_node = innode->obj.id;
1910 		l->input_port = inport->obj.id;
1911 		pw_proxy_add_listener(p, &l->listener, &proxy_link_events, l);
1912 		count++;
1913 
1914 		if (link) {
1915 			l->endpoint_link = link;
1916 			spa_list_append(&link->link_list, &l->link);
1917 		} else {
1918 			spa_list_append(&impl->link_list, &l->link);
1919 		}
1920 	}
1921 	pw_properties_free(props);
1922 
1923 	return count;
1924 }
1925 
1926 
sm_media_session_create_links(struct sm_media_session * sess,const struct spa_dict * dict)1927 int sm_media_session_create_links(struct sm_media_session *sess,
1928 		const struct spa_dict *dict)
1929 {
1930 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
1931 	struct sm_object *obj;
1932 	struct sm_node *outnode = NULL, *innode = NULL;
1933 	struct sm_endpoint *outendpoint = NULL, *inendpoint = NULL;
1934 	struct sm_endpoint_stream *outstream = NULL, *instream = NULL;
1935 	struct endpoint_link *link = NULL;
1936 	const char *str;
1937 	int res;
1938 
1939 	sm_media_session_roundtrip(sess);
1940 
1941 	/* find output node */
1942 	if ((str = spa_dict_lookup(dict, PW_KEY_LINK_OUTPUT_NODE)) != NULL &&
1943 	    (obj = find_object(impl, atoi(str), PW_TYPE_INTERFACE_Node)) != NULL)
1944 		outnode = (struct sm_node*)obj;
1945 
1946 	/* find input node */
1947 	if ((str = spa_dict_lookup(dict, PW_KEY_LINK_INPUT_NODE)) != NULL &&
1948 	    (obj = find_object(impl, atoi(str), PW_TYPE_INTERFACE_Node)) != NULL)
1949 		innode = (struct sm_node*)obj;
1950 
1951 	/* find endpoints and streams */
1952 	if ((str = spa_dict_lookup(dict, PW_KEY_ENDPOINT_LINK_OUTPUT_ENDPOINT)) != NULL &&
1953 	    (obj = find_object(impl, atoi(str), PW_TYPE_INTERFACE_Endpoint)) != NULL)
1954 		outendpoint = (struct sm_endpoint*)obj;
1955 
1956 	if ((str = spa_dict_lookup(dict, PW_KEY_ENDPOINT_LINK_OUTPUT_STREAM)) != NULL &&
1957 	    (obj = find_object(impl, atoi(str), PW_TYPE_INTERFACE_EndpointStream)) != NULL)
1958 		outstream = (struct sm_endpoint_stream*)obj;
1959 
1960 	if ((str = spa_dict_lookup(dict, PW_KEY_ENDPOINT_LINK_INPUT_ENDPOINT)) != NULL &&
1961 	    (obj = find_object(impl, atoi(str), PW_TYPE_INTERFACE_Endpoint)) != NULL)
1962 		inendpoint = (struct sm_endpoint*)obj;
1963 
1964 	if ((str = spa_dict_lookup(dict, PW_KEY_ENDPOINT_LINK_INPUT_STREAM)) != NULL &&
1965 	    (obj = find_object(impl, atoi(str), PW_TYPE_INTERFACE_EndpointStream)) != NULL)
1966 		instream = (struct sm_endpoint_stream*)obj;
1967 
1968 	if (outendpoint != NULL && inendpoint != NULL) {
1969 		link = calloc(1, sizeof(struct endpoint_link));
1970 		if (link == NULL)
1971 			return -errno;
1972 
1973 		link->id = pw_map_insert_new(&impl->endpoint_links, link);
1974 		link->impl = impl;
1975 		spa_list_init(&link->link_list);
1976 		spa_list_append(&impl->endpoint_link_list, &link->link);
1977 
1978 		link->info.version = PW_VERSION_ENDPOINT_LINK_INFO;
1979 		link->info.id = link->id;
1980 		link->info.session_id = impl->this.session->obj.id;
1981 		link->info.output_endpoint_id = outendpoint->info->id;
1982 		link->info.output_stream_id = outstream ? outstream->info->id : SPA_ID_INVALID;
1983 		link->info.input_endpoint_id = inendpoint->info->id;
1984 		link->info.input_stream_id = instream ?  instream->info->id : SPA_ID_INVALID;
1985 		link->info.change_mask =
1986 			PW_ENDPOINT_LINK_CHANGE_MASK_STATE |
1987 			PW_ENDPOINT_LINK_CHANGE_MASK_PROPS;
1988 		link->info.state = PW_ENDPOINT_LINK_STATE_ACTIVE;
1989 		link->info.props = (struct spa_dict*) dict;
1990 	}
1991 
1992 	/* link the nodes, record the link proxies in the endpoint_link */
1993 	if (outnode != NULL && innode != NULL)
1994 		res = link_nodes(impl, link, outnode, innode);
1995 	else
1996 		res = 0;
1997 
1998 	if (link != NULL) {
1999 		/* now create the endpoint link */
2000 		pw_client_session_link_update(impl->this.client_session,
2001 				link->id,
2002 				PW_CLIENT_SESSION_UPDATE_INFO,
2003 				0, NULL,
2004 				&link->info);
2005 	}
2006 	return res;
2007 }
2008 
sm_media_session_remove_links(struct sm_media_session * sess,const struct spa_dict * dict)2009 int sm_media_session_remove_links(struct sm_media_session *sess,
2010 		const struct spa_dict *dict)
2011 {
2012 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
2013 	struct sm_object *obj;
2014 	struct sm_node *outnode = NULL, *innode = NULL;
2015 	const char *str;
2016 	struct link *l, *t;
2017 
2018 	/* find output node */
2019 	if ((str = spa_dict_lookup(dict, PW_KEY_LINK_OUTPUT_NODE)) != NULL &&
2020 	    (obj = find_object(impl, atoi(str), PW_TYPE_INTERFACE_Node)) != NULL)
2021 		outnode = (struct sm_node*)obj;
2022 
2023 	/* find input node */
2024 	if ((str = spa_dict_lookup(dict, PW_KEY_LINK_INPUT_NODE)) != NULL &&
2025 	    (obj = find_object(impl, atoi(str), PW_TYPE_INTERFACE_Node)) != NULL)
2026 		innode = (struct sm_node*)obj;
2027 
2028 	if (innode == NULL || outnode == NULL)
2029 		return -EINVAL;
2030 
2031 	spa_list_for_each_safe(l, t, &impl->link_list, link) {
2032 		if (l->output_node == outnode->obj.id && l->input_node == innode->obj.id) {
2033 			pw_proxy_destroy(l->proxy);
2034 		}
2035 	}
2036 	return 0;
2037 }
2038 
sm_media_session_load_conf(struct sm_media_session * sess,const char * name,struct pw_properties * conf)2039 int sm_media_session_load_conf(struct sm_media_session *sess, const char *name,
2040 		struct pw_properties *conf)
2041 {
2042 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
2043 	return pw_conf_load_conf(impl->config_dir, name, conf);
2044 }
2045 
sm_media_session_load_state(struct sm_media_session * sess,const char * name,struct pw_properties * props)2046 int sm_media_session_load_state(struct sm_media_session *sess,
2047 		const char *name, struct pw_properties *props)
2048 {
2049 	return pw_conf_load_state(SESSION_PREFIX, name, props);
2050 }
2051 
sm_media_session_save_state(struct sm_media_session * sess,const char * name,const struct pw_properties * props)2052 int sm_media_session_save_state(struct sm_media_session *sess,
2053 		const char *name, const struct pw_properties *props)
2054 {
2055 	return pw_conf_save_state(SESSION_PREFIX, name, props);
2056 }
2057 
sm_media_session_sanitize_name(char * name,int size,char sub,const char * fmt,...)2058 char *sm_media_session_sanitize_name(char *name, int size, char sub, const char *fmt, ...)
2059 {
2060 	char *p;
2061 	va_list varargs;
2062 	int res;
2063 
2064 	va_start(varargs, fmt);
2065 	res = vsnprintf(name, size, fmt, varargs);
2066 	va_end(varargs);
2067 
2068 	if (res < 0)
2069 		return NULL;
2070 
2071 	for (p = name; *p; p++) {
2072 		switch(*p) {
2073 		case '0' ... '9':
2074 		case 'a' ... 'z':
2075 		case 'A' ... 'Z':
2076 		case '.': case '-': case '_':
2077 			break;
2078 		default:
2079 			*p = sub;
2080 			break;
2081 		}
2082 	}
2083 	return name;
2084 }
2085 
sm_media_session_sanitize_description(char * name,int size,char sub,const char * fmt,...)2086 char *sm_media_session_sanitize_description(char *name, int size, char sub, const char *fmt, ...)
2087 {
2088 	char *p;
2089 	va_list varargs;
2090 	int res;
2091 
2092 	va_start(varargs, fmt);
2093 	res = vsnprintf(name, size, fmt, varargs);
2094 	va_end(varargs);
2095 
2096 	if (res < 0)
2097 		return NULL;
2098 
2099 	for (p = name; *p; p++) {
2100 		switch(*p) {
2101 		case ':':
2102 			*p = sub;
2103 			break;
2104 		}
2105 	}
2106 	return name;
2107 }
2108 
sm_media_session_seat_active_changed(struct sm_media_session * sess,bool active)2109 int sm_media_session_seat_active_changed(struct sm_media_session *sess, bool active)
2110 {
2111 	struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
2112 	if (active != impl->seat_active) {
2113 		impl->seat_active = active;
2114 		sm_media_session_emit_seat_active(impl, active);
2115 	}
2116 	return 0;
2117 }
2118 
monitor_core_done(void * data,uint32_t id,int seq)2119 static void monitor_core_done(void *data, uint32_t id, int seq)
2120 {
2121 	struct impl *impl = data;
2122 
2123 	if (id == 0)
2124 		handle_postponed_registry_events(impl, seq);
2125 
2126 	if (seq == impl->monitor_seq) {
2127 		pw_log_debug("%p: monitor sync stop %d", impl, seq);
2128 		pw_core_set_paused(impl->policy_core, false);
2129 	}
2130 }
2131 
2132 static const struct pw_core_events monitor_core_events = {
2133 	PW_VERSION_CORE_EVENTS,
2134 	.done = monitor_core_done,
2135 };
2136 
start_session(struct impl * impl)2137 static int start_session(struct impl *impl)
2138 {
2139 	impl->monitor_core = pw_context_connect(impl->this.context, NULL, 0);
2140 	if (impl->monitor_core == NULL) {
2141 		pw_log_error("can't start monitor: %m");
2142 		return -errno;
2143 	}
2144 
2145 	pw_core_add_listener(impl->monitor_core,
2146 			&impl->monitor_listener,
2147 			&monitor_core_events, impl);
2148 
2149 	impl->monitor_registry = pw_core_get_registry(impl->monitor_core,
2150 			PW_VERSION_REGISTRY, 0);
2151 	pw_registry_add_listener(impl->monitor_registry,
2152 			&impl->monitor_registry_listener,
2153 			&monitor_registry_events, impl);
2154 
2155 	return 0;
2156 }
2157 
core_info(void * data,const struct pw_core_info * info)2158 static void core_info(void *data, const struct pw_core_info *info)
2159 {
2160 	struct impl *impl = data;
2161 	pw_log_debug("%p: info", impl);
2162 	impl->this.info = pw_core_info_merge(impl->this.info, info, true);
2163 
2164 	if (impl->this.info->change_mask != 0)
2165 		sm_media_session_emit_info(impl, impl->this.info);
2166 	impl->this.info->change_mask = 0;
2167 }
2168 
core_done(void * data,uint32_t id,int seq)2169 static void core_done(void *data, uint32_t id, int seq)
2170 {
2171 	struct impl *impl = data;
2172 	struct sync *s, *t;
2173 	impl->last_seq = seq;
2174 
2175 	spa_list_for_each_safe(s, t, &impl->sync_list, link) {
2176 		if (s->seq == seq) {
2177 			spa_list_remove(&s->link);
2178 			s->callback(s->data);
2179 			free(s);
2180 		}
2181 	}
2182 	if (impl->rescan_seq == seq) {
2183 		struct sm_object *obj, *to;
2184 
2185 		if (!impl->scanning) {
2186 			pw_log_trace("%p: rescan %u %d", impl, id, seq);
2187 			impl->scanning = true;
2188 			sm_media_session_emit_rescan(impl, seq);
2189 			impl->scanning = false;
2190 			if (impl->rescan_pending) {
2191 				impl->rescan_pending = false;
2192 				sm_media_session_schedule_rescan(&impl->this);
2193 			}
2194 		}
2195 
2196 		spa_list_for_each_safe(obj, to, &impl->object_list, link) {
2197 			if (obj->id == SPA_ID_INVALID)
2198 				continue;
2199 			pw_log_trace("%p: obj %p %08x", impl, obj, obj->changed);
2200 			if (obj->changed)
2201 				sm_object_emit_update(obj);
2202 			obj->changed = 0;
2203 		}
2204 	}
2205 }
2206 
core_error(void * data,uint32_t id,int seq,int res,const char * message)2207 static void core_error(void *data, uint32_t id, int seq, int res, const char *message)
2208 {
2209 	struct impl *impl = data;
2210 
2211 	pw_log(res == -ENOENT || res == -EINVAL ? SPA_LOG_LEVEL_INFO : SPA_LOG_LEVEL_WARN,
2212 			"error id:%u seq:%d res:%d (%s): %s",
2213 			id, seq, res, spa_strerror(res), message);
2214 
2215 	if (id == PW_ID_CORE) {
2216 		if (res == -EPIPE)
2217 			pw_main_loop_quit(impl->loop);
2218 	}
2219 }
2220 
2221 
2222 static const struct pw_core_events policy_core_events = {
2223 	PW_VERSION_CORE_EVENTS,
2224 	.info = core_info,
2225 	.done = core_done,
2226 	.error = core_error
2227 };
2228 
policy_core_destroy(void * data)2229 static void policy_core_destroy(void *data)
2230 {
2231 	struct impl *impl = data;
2232 	pw_log_debug("%p: policy core destroy", impl);
2233 	impl->policy_core = NULL;
2234 }
2235 
2236 static const struct pw_proxy_events proxy_core_events = {
2237 	PW_VERSION_PROXY_EVENTS,
2238 	.destroy = policy_core_destroy,
2239 };
2240 
start_policy(struct impl * impl)2241 static int start_policy(struct impl *impl)
2242 {
2243 	impl->policy_core = pw_context_connect(impl->this.context, NULL, 0);
2244 	if (impl->policy_core == NULL) {
2245 		pw_log_error("can't start policy: %m");
2246 		return -errno;
2247 	}
2248 
2249 	pw_core_add_listener(impl->policy_core,
2250 			&impl->policy_listener,
2251 			&policy_core_events, impl);
2252 	pw_proxy_add_listener((struct pw_proxy*)impl->policy_core,
2253 			&impl->proxy_policy_listener,
2254 			&proxy_core_events, impl);
2255 
2256 	impl->registry = pw_core_get_registry(impl->policy_core,
2257 			PW_VERSION_REGISTRY, 0);
2258 	pw_registry_add_listener(impl->registry,
2259 			&impl->registry_listener,
2260 			&registry_events, impl);
2261 
2262 	return 0;
2263 }
2264 
session_shutdown(struct impl * impl)2265 static void session_shutdown(struct impl *impl)
2266 {
2267 	struct sm_object *obj;
2268 	struct registry_event *re;
2269 	struct spa_list free_list;
2270 
2271 	pw_log_info("%p", impl);
2272 	sm_media_session_emit_shutdown(impl);
2273 
2274 	/*
2275 	 * Monitors may still hold references to objects, which they
2276 	 * drop in session destroy event, so don't free undiscarded
2277 	 * objects yet. Destroy event handlers may remove any objects
2278 	 * in the list, so iterate carefully.
2279 	 */
2280 	spa_list_init(&free_list);
2281 	spa_list_consume(obj, &impl->object_list, link) {
2282 		if (obj->destroyed) {
2283 			spa_list_remove(&obj->link);
2284 			spa_list_append(&free_list, &obj->link);
2285 		} else {
2286 			sm_object_destroy_maybe_free(obj);
2287 		}
2288 	}
2289 
2290 	spa_list_consume(re, &impl->registry_event_list, link)
2291 		registry_event_free(re);
2292 
2293 	impl->this.metadata = NULL;
2294 
2295 	sm_media_session_emit_destroy(impl);
2296 
2297 	spa_list_consume(obj, &free_list, link)
2298 		sm_object_destroy(obj);
2299 	spa_list_consume(obj, &impl->object_list, link)
2300 		sm_object_destroy(obj);  /* in case emit_destroy created new objects */
2301 
2302 	if (impl->registry) {
2303 		spa_hook_remove(&impl->registry_listener);
2304 		pw_proxy_destroy((struct pw_proxy*)impl->registry);
2305 	}
2306 	if (impl->monitor_registry) {
2307 		spa_hook_remove(&impl->monitor_registry_listener);
2308 		pw_proxy_destroy((struct pw_proxy*)impl->monitor_registry);
2309 	}
2310 	if (impl->policy_core) {
2311 		spa_hook_remove(&impl->policy_listener);
2312 		spa_hook_remove(&impl->proxy_policy_listener);
2313 		pw_core_disconnect(impl->policy_core);
2314 	}
2315 	if (impl->monitor_core) {
2316 		spa_hook_remove(&impl->monitor_listener);
2317 		pw_core_disconnect(impl->monitor_core);
2318 	}
2319 	if (impl->this.info)
2320 		pw_core_info_free(impl->this.info);
2321 }
2322 
sm_metadata_start(struct sm_media_session * sess)2323 static int sm_metadata_start(struct sm_media_session *sess)
2324 {
2325 	sess->metadata = sm_media_session_export_metadata(sess, "default");
2326 	if (sess->metadata == NULL)
2327 		return -errno;
2328 	return 0;
2329 }
2330 
sm_pulse_bridge_start(struct sm_media_session * sess)2331 static int sm_pulse_bridge_start(struct sm_media_session *sess)
2332 {
2333 	if (pw_context_load_module(sess->context,
2334 			"libpipewire-module-protocol-pulse",
2335 			NULL, NULL) == NULL)
2336 		return -errno;
2337 	return 0;
2338 }
2339 
dbus_connection_disconnected(void * data)2340 static void dbus_connection_disconnected(void *data)
2341 {
2342 	struct impl *impl = data;
2343 	pw_log_info("DBus disconnected");
2344 	sm_media_session_emit_dbus_disconnected(impl);
2345 }
2346 
2347 static const struct spa_dbus_connection_events dbus_connection_events = {
2348 	SPA_VERSION_DBUS_CONNECTION_EVENTS,
2349 	.disconnected = dbus_connection_disconnected
2350 };
2351 
do_quit(void * data,int signal_number)2352 static void do_quit(void *data, int signal_number)
2353 {
2354 	struct impl *impl = data;
2355 	pw_main_loop_quit(impl->loop);
2356 }
2357 
collect_modules(struct impl * impl,const char * str)2358 static int collect_modules(struct impl *impl, const char *str)
2359 {
2360 	struct spa_json it[3];
2361 	char key[512], value[512];
2362 	const char *dir, *prefix = NULL, *val;
2363 	char check_path[PATH_MAX];
2364 	struct stat statbuf;
2365 	int count = 0;
2366 
2367 	dir = getenv("MEDIA_SESSION_CONFIG_DIR");
2368 	if (dir == NULL) {
2369 		prefix = SESSION_PREFIX;
2370 		if ((dir = getenv("PIPEWIRE_CONFIG_DIR")) == NULL)
2371 			dir = MEDIA_SESSION_CONFDATADIR;
2372 	}
2373 	if (dir == NULL)
2374 		return -ENOENT;
2375 
2376 again:
2377 	spa_json_init(&it[0], str, strlen(str));
2378 	if (spa_json_enter_object(&it[0], &it[1]) < 0)
2379 		return -EINVAL;
2380 
2381 	while (spa_json_get_string(&it[1], key, sizeof(key)-1) > 0) {
2382 		bool add = false;
2383 
2384 		if (pw_properties_get(impl->modules, key) != NULL) {
2385 			add = true;
2386 		} else {
2387 			snprintf(check_path, sizeof(check_path),
2388 				 "%s%s%s/%s", dir, prefix ? "/" : "", prefix ? prefix : "", key);
2389 			add = (stat(check_path, &statbuf) == 0);
2390 		}
2391 		if (add) {
2392 			if (spa_json_enter_array(&it[1], &it[2]) < 0)
2393 				continue;
2394 
2395 			while (spa_json_get_string(&it[2], value, sizeof(value)-1) > 0) {
2396 				pw_properties_set(impl->modules, value, "true");
2397 			}
2398 		}
2399 		else if (spa_json_next(&it[1], &val) <= 0)
2400 			break;
2401 	}
2402 	/* twice to resolve groups in module list */
2403 	if (count++ == 0)
2404 		goto again;
2405 
2406 	return 0;
2407 }
2408 
2409 static const struct {
2410 	const char *name;
2411 	const char *desc;
2412 	int (*start)(struct sm_media_session *sess);
2413 	const char *props;
2414 
2415 } modules[] = {
2416 	{ "flatpak", "manage flatpak access", sm_access_flatpak_start, NULL },
2417 	{ "portal", "manage portal permissions", sm_access_portal_start, NULL },
2418 	{ "metadata", "export metadata API", sm_metadata_start, NULL },
2419 	{ "default-nodes", "restore default nodes", sm_default_nodes_start, NULL },
2420 	{ "default-profile", "restore default profiles", sm_default_profile_start, NULL },
2421 	{ "default-routes", "restore default route", sm_default_routes_start, NULL },
2422 	{ "restore-stream", "restore stream settings", sm_restore_stream_start, NULL },
2423 	{ "streams-follow-default", "move streams when default changes", sm_streams_follow_default_start, NULL },
2424 	{ "alsa-no-dsp", "do not configure audio nodes in DSP mode", sm_alsa_no_dsp_start, NULL },
2425 	{ "alsa-seq", "alsa seq midi support", sm_alsa_midi_start, NULL },
2426 	{ "alsa-monitor", "alsa card udev detection", sm_alsa_monitor_start, NULL },
2427 	{ "v4l2", "video for linux udev detection", sm_v4l2_monitor_start, NULL },
2428 	{ "libcamera", "libcamera udev detection", sm_libcamera_monitor_start, NULL },
2429 	{ "bluez5", "bluetooth support", sm_bluez5_monitor_start, NULL },
2430 	{ "bluez5-autoswitch", "switch bluetooth profiles automatically", sm_bluez5_autoswitch_start, NULL },
2431 	{ "suspend-node", "suspend inactive nodes", sm_suspend_node_start, NULL },
2432 	{ "policy-node", "configure and link nodes", sm_policy_node_start, NULL },
2433 	{ "pulse-bridge", "accept pulseaudio clients", sm_pulse_bridge_start, NULL },
2434 #ifdef HAVE_SYSTEMD
2435 	{ "logind", "systemd-logind seat support", sm_logind_start, NULL },
2436 #endif
2437 };
2438 
is_module_enabled(struct impl * impl,const char * val)2439 static bool is_module_enabled(struct impl *impl, const char *val)
2440 {
2441 	return pw_properties_get_bool(impl->modules, val, false);
2442 }
2443 
show_help(const char * name,struct impl * impl,const char * config_name)2444 static void show_help(const char *name, struct impl *impl, const char *config_name)
2445 {
2446 	size_t i;
2447 
2448         fprintf(stdout, "%s [options]\n"
2449 		"  -h, --help                            Show this help\n"
2450 		"      --version                         Show version\n"
2451 		"  -c, --config                          Load config (Default %s)\n",
2452 		name, config_name);
2453 
2454 	fprintf(stdout, "\noptions: (*=enabled)\n");
2455 	for (i = 0; i < SPA_N_ELEMENTS(modules); i++) {
2456 		fprintf(stdout, "\t  %c %-15.15s: %s\n",
2457 				is_module_enabled(impl, modules[i].name) ? '*' : ' ',
2458 				modules[i].name, modules[i].desc);
2459 	}
2460 }
2461 
main(int argc,char * argv[])2462 int main(int argc, char *argv[])
2463 {
2464 	struct impl impl = { .seat_active = true };
2465 	const struct spa_support *support;
2466 	const char *str, *config_name = SESSION_CONF;
2467 	bool do_show_help = false;
2468 	uint32_t n_support;
2469 	int res = 0, c;
2470 	static const struct option long_options[] = {
2471 		{ "help",	no_argument,		NULL, 'h' },
2472 		{ "version",	no_argument,		NULL, 'V' },
2473 		{ "config",	required_argument,	NULL, 'c' },
2474 		{ "verbose",	no_argument,		NULL, 'v' },
2475 		{ NULL, 0, NULL, 0}
2476 	};
2477         size_t i;
2478 	const struct spa_dict_item *item;
2479 	enum spa_log_level level = pw_log_level;
2480 	const char *config_dir;
2481 
2482 	pw_init(&argc, &argv);
2483 
2484 	PW_LOG_TOPIC_INIT(ms_topic);
2485 
2486 	while ((c = getopt_long(argc, argv, "hVc:v", long_options, NULL)) != -1) {
2487 		switch (c) {
2488 		case 'v':
2489 			if (level < SPA_LOG_LEVEL_TRACE)
2490 				pw_log_set_level(++level);
2491 			break;
2492 		case 'h':
2493 			do_show_help = true;
2494 			break;
2495 		case 'V':
2496 			fprintf(stdout, "%s\n"
2497 				"Compiled with libpipewire %s\n"
2498 				"Linked with libpipewire %s\n",
2499 				argv[0],
2500 				pw_get_headers_version(),
2501 				pw_get_library_version());
2502 			return 0;
2503 		case 'c':
2504 			config_name = optarg;
2505 			break;
2506 		default:
2507 			return 1;
2508 		}
2509 	}
2510 
2511 	config_dir = getenv("MEDIA_SESSION_CONFIG_DIR");
2512 	impl.config_dir = config_dir ? config_dir : SESSION_PREFIX;
2513 	impl.this.props = pw_properties_new(
2514 			PW_KEY_CONFIG_PREFIX, impl.config_dir,
2515 			PW_KEY_CONFIG_NAME, config_name,
2516 			NULL);
2517 	if (impl.this.props == NULL)
2518 		return 1;
2519 
2520 	if ((impl.conf = pw_properties_new(NULL, NULL)) == NULL)
2521 		return 1;
2522 
2523 	pw_conf_load_conf(impl.config_dir, config_name, impl.conf);
2524 
2525 	if ((str = pw_properties_get(impl.conf, "context.properties")) != NULL)
2526 		pw_properties_update_string(impl.this.props, str, strlen(str));
2527 
2528 	if ((impl.modules = pw_properties_new("default", "true", NULL)) == NULL)
2529 		return 1;
2530 	if ((str = pw_properties_get(impl.conf, "session.modules")) != NULL)
2531 		collect_modules(&impl, str);
2532 
2533 	if (do_show_help) {
2534 		show_help(argv[0], &impl, config_name);
2535 		return 0;
2536 	}
2537 
2538 	pw_log_info("media-session context properties:");
2539 	spa_dict_for_each(item, &impl.this.props->dict)
2540 		pw_log_info("  '%s' = '%s'", item->key, item->value);
2541 
2542 	impl.loop = pw_main_loop_new(NULL);
2543 	if (impl.loop == NULL)
2544 		return 1;
2545 	impl.this.loop = pw_main_loop_get_loop(impl.loop);
2546 
2547 	pw_loop_add_signal(impl.this.loop, SIGINT, do_quit, &impl);
2548 	pw_loop_add_signal(impl.this.loop, SIGTERM, do_quit, &impl);
2549 
2550 	impl.this.context = pw_context_new(impl.this.loop,
2551 				pw_properties_copy(impl.this.props),
2552 				0);
2553 
2554 	if (impl.this.context == NULL)
2555 		return 1;
2556 
2557 	pw_context_set_object(impl.this.context, SM_TYPE_MEDIA_SESSION, &impl);
2558 
2559 	pw_map_init(&impl.globals, 64, 64);
2560 	spa_list_init(&impl.object_list);
2561 	spa_list_init(&impl.registry_event_list);
2562 	spa_list_init(&impl.link_list);
2563 	pw_map_init(&impl.endpoint_links, 64, 64);
2564 	spa_list_init(&impl.endpoint_link_list);
2565 	spa_list_init(&impl.sync_list);
2566 	spa_hook_list_init(&impl.hooks);
2567 
2568 	support = pw_context_get_support(impl.this.context, &n_support);
2569 
2570 	impl.dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus);
2571 	if (impl.dbus) {
2572 		impl.this.dbus_connection = spa_dbus_get_connection(impl.dbus, SPA_DBUS_TYPE_SESSION);
2573 		if (impl.this.dbus_connection == NULL)
2574 			pw_log_warn("no dbus connection");
2575 		else {
2576 			pw_log_debug("got dbus connection %p", impl.this.dbus_connection);
2577 			spa_dbus_connection_add_listener(impl.this.dbus_connection,
2578 					&impl.dbus_connection_listener,
2579 					&dbus_connection_events, &impl);
2580 		}
2581 	} else {
2582 		pw_log_info("dbus disabled");
2583 	}
2584 
2585 	if ((res = start_session(&impl)) < 0)
2586 		goto exit;
2587 	if ((res = start_policy(&impl)) < 0)
2588 		goto exit;
2589 
2590 	for (i = 0; i < SPA_N_ELEMENTS(modules); i++) {
2591 		const char *name = modules[i].name;
2592 		if (is_module_enabled(&impl, name)) {
2593 			pw_log_info("enabling media session module: %s", name);
2594 			modules[i].start(&impl.this);
2595 		}
2596 	}
2597 
2598 //	sm_session_manager_start(&impl.this);
2599 
2600 	pw_main_loop_run(impl.loop);
2601 
2602 exit:
2603 	session_shutdown(&impl);
2604 
2605 	pw_context_destroy(impl.this.context);
2606 	pw_main_loop_destroy(impl.loop);
2607 
2608 	pw_map_clear(&impl.endpoint_links);
2609 	pw_map_clear(&impl.globals);
2610 	pw_properties_free(impl.this.props);
2611 	pw_properties_free(impl.conf);
2612 	pw_properties_free(impl.modules);
2613 
2614 	pw_deinit();
2615 
2616 	return res;
2617 }
2618