| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | /* PipeWire
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright © 2019 Wim Taymans | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Permission is hereby granted, free of charge, to any person obtaining a | 
					
						
							|  |  |  |  * copy of this software and associated documentation files (the "Software"), | 
					
						
							|  |  |  |  * to deal in the Software without restriction, including without limitation | 
					
						
							|  |  |  |  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | 
					
						
							|  |  |  |  * and/or sell copies of the Software, and to permit persons to whom the | 
					
						
							|  |  |  |  * Software is furnished to do so, subject to the following conditions: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The above copyright notice and this permission notice (including the next | 
					
						
							|  |  |  |  * paragraph) shall be included in all copies or substantial portions of the | 
					
						
							|  |  |  |  * Software. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
					
						
							|  |  |  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | 
					
						
							|  |  |  |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
					
						
							|  |  |  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | 
					
						
							|  |  |  |  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
					
						
							|  |  |  |  * DEALINGS IN THE SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <math.h>
 | 
					
						
							|  |  |  | #include <time.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "config.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <alsa/asoundlib.h>
 | 
					
						
							| 
									
										
										
										
											2019-11-18 10:40:50 +01:00
										 |  |  | #include <alsa/use-case.h>
 | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <spa/node/node.h>
 | 
					
						
							|  |  |  | #include <spa/utils/hook.h>
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | #include <spa/utils/result.h>
 | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | #include <spa/utils/names.h>
 | 
					
						
							|  |  |  | #include <spa/utils/keys.h>
 | 
					
						
							|  |  |  | #include <spa/param/audio/format-utils.h>
 | 
					
						
							|  |  |  | #include <spa/param/props.h>
 | 
					
						
							|  |  |  | #include <spa/debug/dict.h>
 | 
					
						
							| 
									
										
										
										
											2019-11-20 16:18:46 +01:00
										 |  |  | #include <spa/debug/pod.h>
 | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "pipewire/pipewire.h"
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | #include <extensions/session-manager.h>
 | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | #include "media-session.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define NAME		"alsa-endpoint"
 | 
					
						
							|  |  |  | #define SESSION_KEY	"alsa-endpoint"
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 16:03:00 +01:00
										 |  |  | struct endpoint { | 
					
						
							|  |  |  | 	struct spa_list link; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	struct impl *impl; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 16:03:00 +01:00
										 |  |  | 	struct pw_properties *props; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	struct node *node; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	struct spa_hook listener; | 
					
						
							| 
									
										
										
										
											2019-11-07 16:03:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-11 16:31:58 +01:00
										 |  |  | 	struct pw_client_endpoint *client_endpoint; | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	struct spa_hook proxy_listener; | 
					
						
							| 
									
										
										
										
											2019-11-07 16:03:00 +01:00
										 |  |  | 	struct spa_hook client_endpoint_listener; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	struct pw_endpoint_info info; | 
					
						
							| 
									
										
										
										
											2019-11-07 16:03:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	struct spa_param_info params[5]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 	struct endpoint *monitor; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 16:03:00 +01:00
										 |  |  | 	unsigned int use_ucm:1; | 
					
						
							|  |  |  | 	snd_use_case_mgr_t *ucm; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	struct spa_audio_info format; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	struct spa_list stream_list; | 
					
						
							| 
									
										
										
										
											2019-11-07 16:03:00 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct stream { | 
					
						
							|  |  |  | 	struct spa_list link; | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	struct endpoint *endpoint; | 
					
						
							| 
									
										
										
										
											2019-11-07 16:03:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	struct pw_properties *props; | 
					
						
							|  |  |  | 	struct pw_endpoint_stream_info info; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	struct spa_audio_info format; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	unsigned int active:1; | 
					
						
							| 
									
										
										
										
											2019-11-07 16:03:00 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | struct node { | 
					
						
							|  |  |  | 	struct impl *impl; | 
					
						
							|  |  |  | 	struct sm_node *node; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	struct device *device; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	struct endpoint *endpoint; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct device { | 
					
						
							|  |  |  | 	struct impl *impl; | 
					
						
							|  |  |  | 	uint32_t id; | 
					
						
							|  |  |  | 	struct sm_device *device; | 
					
						
							|  |  |  | 	struct spa_hook listener; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	struct spa_list endpoint_list; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct impl { | 
					
						
							|  |  |  | 	struct sm_media_session *session; | 
					
						
							|  |  |  | 	struct spa_hook listener; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | static int client_endpoint_set_session_id(void *object, uint32_t id) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-11-07 16:03:00 +01:00
										 |  |  | 	struct endpoint *endpoint = object; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	endpoint->info.session_id = id; | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int client_endpoint_set_param(void *object, | 
					
						
							|  |  |  | 		uint32_t id, uint32_t flags, const struct spa_pod *param) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-11-15 13:33:14 +01:00
										 |  |  | 	struct endpoint *endpoint = object; | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	struct impl *impl = endpoint->impl; | 
					
						
							| 
									
										
										
										
											2019-11-15 13:33:14 +01:00
										 |  |  | 	pw_log_debug(NAME " %p: endpoint %p set param %d", impl, endpoint, id); | 
					
						
							| 
									
										
										
										
											2019-12-11 15:26:11 +01:00
										 |  |  | 	return pw_node_set_param((struct pw_node*)endpoint->node->node->obj.proxy, | 
					
						
							| 
									
										
										
										
											2019-11-15 13:33:14 +01:00
										 |  |  | 				id, flags, param); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int client_endpoint_stream_set_param(void *object, uint32_t stream_id, | 
					
						
							|  |  |  | 		uint32_t id, uint32_t flags, const struct spa_pod *param) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return -ENOTSUP; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | static int stream_set_active(struct endpoint *endpoint, struct stream *stream, bool active) | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	char buf[1024]; | 
					
						
							|  |  |  | 	struct spa_pod_builder b = { 0, }; | 
					
						
							|  |  |  | 	struct spa_pod *param; | 
					
						
							| 
									
										
										
										
											2019-11-14 18:35:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	if (stream->active == active) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2019-11-15 13:34:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	if (active) { | 
					
						
							|  |  |  | 		stream->format.info.raw.rate = 48000; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		spa_pod_builder_init(&b, buf, sizeof(buf)); | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 		param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &stream->format.info.raw); | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 		param = spa_pod_builder_add_object(&b, | 
					
						
							|  |  |  | 			SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig, | 
					
						
							|  |  |  | 			SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(endpoint->info.direction), | 
					
						
							|  |  |  | 			SPA_PARAM_PORT_CONFIG_mode,	 SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp), | 
					
						
							|  |  |  | 			SPA_PARAM_PORT_CONFIG_monitor,   SPA_POD_Bool(true), | 
					
						
							|  |  |  | 			SPA_PARAM_PORT_CONFIG_format,    SPA_POD_Pod(param)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) | 
					
						
							|  |  |  | 			spa_debug_pod(2, NULL, param); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-11 15:26:11 +01:00
										 |  |  | 		pw_node_set_param((struct pw_node*)endpoint->node->node->obj.proxy, | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 				SPA_PARAM_PortConfig, 0, param); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	stream->active = active; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int client_endpoint_create_link(void *object, const struct spa_dict *props) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct endpoint *endpoint = object; | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	struct impl *impl = endpoint->impl; | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	struct pw_properties *p; | 
					
						
							|  |  |  | 	int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pw_log_debug(NAME" %p: endpoint %p", impl, endpoint); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (props == NULL) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 12:08:46 +01:00
										 |  |  | 	p = pw_properties_new_dict(props); | 
					
						
							|  |  |  | 	if (p == NULL) | 
					
						
							|  |  |  | 		return -errno; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 12:08:46 +01:00
										 |  |  | 	if (endpoint->info.direction == PW_DIRECTION_OUTPUT) { | 
					
						
							|  |  |  | 		const char *str; | 
					
						
							|  |  |  | 		struct sm_object *obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		str = spa_dict_lookup(props, PW_KEY_ENDPOINT_LINK_INPUT_ENDPOINT); | 
					
						
							|  |  |  | 		if (str == NULL) { | 
					
						
							|  |  |  | 			pw_log_warn(NAME" %p: no target endpoint given", impl); | 
					
						
							|  |  |  | 			res = -EINVAL; | 
					
						
							|  |  |  | 			goto exit; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		obj = sm_media_session_find_object(impl->session, atoi(str)); | 
					
						
							|  |  |  | 		if (obj == NULL || obj->type != PW_TYPE_INTERFACE_Endpoint) { | 
					
						
							|  |  |  | 			pw_log_warn(NAME" %p: could not find endpoint %s (%p)", impl, str, obj); | 
					
						
							|  |  |  | 			res = -EINVAL; | 
					
						
							|  |  |  | 			goto exit; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 		pw_properties_setf(p, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->node->node->info->id); | 
					
						
							| 
									
										
										
										
											2019-11-15 12:08:46 +01:00
										 |  |  | 		pw_properties_setf(p, PW_KEY_LINK_OUTPUT_PORT, "-1"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-11 16:43:53 +01:00
										 |  |  | 		pw_endpoint_create_link((struct pw_endpoint*)obj->proxy, &p->dict); | 
					
						
							| 
									
										
										
										
											2019-11-15 12:08:46 +01:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 		pw_properties_setf(p, PW_KEY_LINK_INPUT_NODE, "%d", endpoint->node->node->info->id); | 
					
						
							| 
									
										
										
										
											2019-11-15 12:08:46 +01:00
										 |  |  | 		pw_properties_setf(p, PW_KEY_LINK_INPUT_PORT, "-1"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		sm_media_session_create_links(impl->session, &p->dict); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res = 0; | 
					
						
							|  |  |  | exit: | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	pw_properties_free(p); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 12:08:46 +01:00
										 |  |  | 	return res; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-11 16:31:58 +01:00
										 |  |  | static const struct pw_client_endpoint_events client_endpoint_events = { | 
					
						
							|  |  |  | 	PW_VERSION_CLIENT_ENDPOINT_EVENTS, | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 	.set_session_id = client_endpoint_set_session_id, | 
					
						
							|  |  |  | 	.set_param = client_endpoint_set_param, | 
					
						
							|  |  |  | 	.stream_set_param = client_endpoint_stream_set_param, | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	.create_link = client_endpoint_create_link, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | static struct stream *endpoint_add_stream(struct endpoint *endpoint) | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct stream *s; | 
					
						
							|  |  |  | 	const char *str; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s = calloc(1, sizeof(*s)); | 
					
						
							|  |  |  | 	if (s == NULL) | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 		return NULL; | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	s->props = pw_properties_new(NULL, NULL); | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	s->endpoint = endpoint; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 	if ((str = pw_properties_get(endpoint->props, PW_KEY_MEDIA_CLASS)) != NULL) | 
					
						
							|  |  |  | 		pw_properties_set(s->props, PW_KEY_MEDIA_CLASS, str); | 
					
						
							|  |  |  | 	if ((str = pw_properties_get(endpoint->props, PW_KEY_PRIORITY_SESSION)) != NULL) | 
					
						
							|  |  |  | 		pw_properties_set(s->props, PW_KEY_PRIORITY_SESSION, str); | 
					
						
							|  |  |  | 	if (endpoint->info.direction == PW_DIRECTION_OUTPUT) { | 
					
						
							|  |  |  | 		if (endpoint->monitor != NULL) | 
					
						
							|  |  |  | 			pw_properties_set(s->props, PW_KEY_ENDPOINT_STREAM_NAME, "Monitor"); | 
					
						
							|  |  |  | 		else | 
					
						
							| 
									
										
										
										
											2019-12-16 13:35:41 +01:00
										 |  |  | 			pw_properties_set(s->props, PW_KEY_ENDPOINT_STREAM_NAME, "Capture"); | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2019-12-16 13:35:41 +01:00
										 |  |  | 		pw_properties_set(s->props, PW_KEY_ENDPOINT_STREAM_NAME, "Playback"); | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s->info.version = PW_VERSION_ENDPOINT_STREAM_INFO; | 
					
						
							|  |  |  | 	s->info.id = endpoint->info.n_streams; | 
					
						
							|  |  |  | 	s->info.endpoint_id = endpoint->info.id; | 
					
						
							|  |  |  | 	s->info.name = (char*)pw_properties_get(s->props, PW_KEY_ENDPOINT_STREAM_NAME); | 
					
						
							|  |  |  | 	s->info.change_mask = PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS; | 
					
						
							|  |  |  | 	s->info.props = &s->props->dict; | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	s->format = endpoint->format; | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pw_log_debug("stream %d", s->info.id); | 
					
						
							| 
									
										
										
										
											2019-12-11 16:31:58 +01:00
										 |  |  | 	pw_client_endpoint_stream_update(endpoint->client_endpoint, | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 			s->info.id, | 
					
						
							|  |  |  | 			PW_CLIENT_ENDPOINT_STREAM_UPDATE_INFO, | 
					
						
							|  |  |  | 			0, NULL, | 
					
						
							|  |  |  | 			&s->info); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spa_list_append(&endpoint->stream_list, &s->link); | 
					
						
							|  |  |  | 	endpoint->info.n_streams++; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	return s; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | static void destroy_stream(struct stream *stream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct endpoint *endpoint = stream->endpoint; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-11 16:31:58 +01:00
										 |  |  | 	pw_client_endpoint_stream_update(endpoint->client_endpoint, | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 			stream->info.id, | 
					
						
							|  |  |  | 			PW_CLIENT_ENDPOINT_STREAM_UPDATE_DESTROYED, | 
					
						
							|  |  |  | 			0, NULL, | 
					
						
							|  |  |  | 			&stream->info); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spa_list_remove(&stream->link); | 
					
						
							|  |  |  | 	endpoint->info.n_streams--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pw_properties_free(stream->props); | 
					
						
							|  |  |  | 	free(stream); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-27 12:19:28 +01:00
										 |  |  | static void update_params(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint32_t n_params; | 
					
						
							|  |  |  | 	const struct spa_pod **params; | 
					
						
							|  |  |  | 	struct endpoint *endpoint = data; | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	struct sm_node *node = endpoint->node->node; | 
					
						
							| 
									
										
										
										
											2019-11-27 12:19:28 +01:00
										 |  |  | 	struct sm_param *p; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pw_log_debug(NAME" %p: endpoint", endpoint); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	params = alloca(sizeof(struct spa_pod *) * node->n_params); | 
					
						
							|  |  |  | 	n_params = 0; | 
					
						
							|  |  |  | 	spa_list_for_each(p, &node->param_list, link) { | 
					
						
							|  |  |  | 		switch (p->id) { | 
					
						
							|  |  |  | 		case SPA_PARAM_Props: | 
					
						
							|  |  |  | 		case SPA_PARAM_PropInfo: | 
					
						
							|  |  |  | 			params[n_params++] = p->param; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-11 16:31:58 +01:00
										 |  |  | 	pw_client_endpoint_update(endpoint->client_endpoint, | 
					
						
							| 
									
										
										
										
											2019-11-27 12:19:28 +01:00
										 |  |  | 			PW_CLIENT_ENDPOINT_UPDATE_PARAMS | | 
					
						
							|  |  |  | 			PW_CLIENT_ENDPOINT_UPDATE_INFO, | 
					
						
							|  |  |  | 			n_params, params, | 
					
						
							|  |  |  | 			&endpoint->info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | static struct endpoint *create_endpoint(struct node *node, struct endpoint *monitor); | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-27 12:19:28 +01:00
										 |  |  | static void object_update(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct endpoint *endpoint = data; | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	struct impl *impl = endpoint->impl; | 
					
						
							|  |  |  | 	struct sm_node *node = endpoint->node->node; | 
					
						
							| 
									
										
										
										
											2019-11-27 12:19:28 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pw_log_debug(NAME" %p: endpoint %p", impl, endpoint); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (node->obj.changed & SM_NODE_CHANGE_MASK_PARAMS) | 
					
						
							|  |  |  | 		update_params(endpoint); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct sm_object_events object_events = { | 
					
						
							|  |  |  | 	SM_VERSION_OBJECT_EVENTS, | 
					
						
							|  |  |  | 	.update = object_update | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | static void complete_endpoint(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct endpoint *endpoint = data; | 
					
						
							|  |  |  | 	struct stream *stream; | 
					
						
							|  |  |  | 	struct sm_param *p; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	pw_log_debug("endpoint %p: complete", endpoint); | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	spa_list_for_each(p, &endpoint->node->node->param_list, link) { | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 		struct spa_audio_info info = { 0, }; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 		if (p->id != SPA_PARAM_EnumFormat) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 		if (spa_format_parse(p->param, &info.media_type, &info.media_subtype) < 0) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 		if (info.media_type != SPA_MEDIA_TYPE_audio || | 
					
						
							|  |  |  | 		    info.media_subtype != SPA_MEDIA_SUBTYPE_raw) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 		spa_pod_object_fixate((struct spa_pod_object*)p->param); | 
					
						
							|  |  |  | 		if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) | 
					
						
							|  |  |  | 			spa_debug_pod(2, NULL, p->param); | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 		if (spa_format_audio_raw_parse(p->param, &info.info.raw) < 0) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 		if (endpoint->format.info.raw.channels < info.info.raw.channels) | 
					
						
							|  |  |  | 			endpoint->format = info; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-11 16:31:58 +01:00
										 |  |  | 	pw_client_endpoint_update(endpoint->client_endpoint, | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 			PW_CLIENT_ENDPOINT_UPDATE_INFO, | 
					
						
							|  |  |  | 			0, NULL, | 
					
						
							|  |  |  | 			&endpoint->info); | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	stream = endpoint_add_stream(endpoint); | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (endpoint->info.direction == PW_DIRECTION_INPUT) { | 
					
						
							|  |  |  | 		struct endpoint *monitor; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* make monitor for sinks */ | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 		monitor = create_endpoint(endpoint->node, endpoint); | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 		if (monitor == NULL) | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		endpoint_add_stream(monitor); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	stream_set_active(endpoint, stream, true); | 
					
						
							| 
									
										
										
										
											2019-11-27 12:19:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	sm_object_add_listener(&endpoint->node->node->obj, &endpoint->listener, &object_events, endpoint); | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | static void proxy_destroy(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct endpoint *endpoint = data; | 
					
						
							|  |  |  | 	struct stream *s; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spa_list_consume(s, &endpoint->stream_list, link) | 
					
						
							|  |  |  | 		destroy_stream(s); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pw_properties_free(endpoint->props); | 
					
						
							|  |  |  | 	spa_list_remove(&endpoint->link); | 
					
						
							| 
									
										
										
										
											2019-12-17 10:47:31 +01:00
										 |  |  | 	spa_hook_remove(&endpoint->proxy_listener); | 
					
						
							|  |  |  | 	spa_hook_remove(&endpoint->listener); | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void proxy_bound(void *data, uint32_t id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct endpoint *endpoint = data; | 
					
						
							|  |  |  | 	endpoint->info.id = id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct pw_proxy_events proxy_events = { | 
					
						
							|  |  |  | 	PW_VERSION_PROXY_EVENTS, | 
					
						
							|  |  |  | 	.destroy = proxy_destroy, | 
					
						
							|  |  |  | 	.bound = proxy_bound, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct endpoint *create_endpoint(struct node *node, struct endpoint *monitor) | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	struct impl *impl = node->impl; | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	struct device *device = node->device; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	struct pw_properties *props; | 
					
						
							|  |  |  | 	struct endpoint *endpoint; | 
					
						
							|  |  |  | 	struct pw_proxy *proxy; | 
					
						
							|  |  |  | 	const char *str, *media_class = NULL, *name = NULL; | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	uint32_t subscribe[4], n_subscribe = 0; | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	struct pw_properties *pr = node->node->obj.props; | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	enum pw_direction direction; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pr == NULL) { | 
					
						
							|  |  |  | 		errno = EINVAL; | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((media_class = pw_properties_get(pr, PW_KEY_MEDIA_CLASS)) == NULL) { | 
					
						
							|  |  |  | 		errno = EINVAL; | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	props = pw_properties_new(NULL, NULL); | 
					
						
							|  |  |  | 	if (props == NULL) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	if (strstr(media_class, "Source") != NULL) { | 
					
						
							|  |  |  | 		direction = PW_DIRECTION_OUTPUT; | 
					
						
							|  |  |  | 	} else if (strstr(media_class, "Sink") != NULL) { | 
					
						
							|  |  |  | 		direction = PW_DIRECTION_INPUT; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		errno = EINVAL; | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (monitor != NULL) { | 
					
						
							|  |  |  | 		pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Source"); | 
					
						
							|  |  |  | 		direction = PW_DIRECTION_OUTPUT; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		pw_properties_set(props, PW_KEY_MEDIA_CLASS, media_class); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((str = pw_properties_get(pr, PW_KEY_PRIORITY_SESSION)) != NULL) | 
					
						
							|  |  |  | 		pw_properties_set(props, PW_KEY_PRIORITY_SESSION, str); | 
					
						
							|  |  |  | 	if ((name = pw_properties_get(pr, PW_KEY_NODE_DESCRIPTION)) != NULL) { | 
					
						
							|  |  |  | 		if (monitor != NULL) { | 
					
						
							|  |  |  | 			pw_properties_setf(props, PW_KEY_ENDPOINT_NAME, "Monitor of %s", monitor->info.name); | 
					
						
							|  |  |  | 			pw_properties_setf(props, PW_KEY_ENDPOINT_MONITOR, "%d", monitor->info.id); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			pw_properties_set(props, PW_KEY_ENDPOINT_NAME, name); | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	if ((str = pw_properties_get(pr, PW_KEY_DEVICE_ICON_NAME)) != NULL) | 
					
						
							|  |  |  | 		pw_properties_set(props, PW_KEY_ENDPOINT_ICON_NAME, str); | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 18:35:29 +01:00
										 |  |  | 	proxy = sm_media_session_create_object(impl->session, | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 						"client-endpoint", | 
					
						
							|  |  |  | 						PW_TYPE_INTERFACE_ClientEndpoint, | 
					
						
							| 
									
										
										
										
											2019-12-11 16:31:58 +01:00
										 |  |  | 						PW_VERSION_CLIENT_ENDPOINT, | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 						&props->dict, sizeof(*endpoint)); | 
					
						
							|  |  |  | 	if (proxy == NULL) { | 
					
						
							|  |  |  | 		pw_properties_free(props); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	endpoint = pw_proxy_get_user_data(proxy); | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	endpoint->impl = impl; | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	endpoint->node = node; | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 	endpoint->monitor = monitor; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	endpoint->props = props; | 
					
						
							| 
									
										
										
										
											2019-12-11 16:31:58 +01:00
										 |  |  | 	endpoint->client_endpoint = (struct pw_client_endpoint *) proxy; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	endpoint->info.version = PW_VERSION_ENDPOINT_INFO; | 
					
						
							|  |  |  | 	endpoint->info.name = (char*)pw_properties_get(endpoint->props, PW_KEY_ENDPOINT_NAME); | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 	endpoint->info.media_class = (char*)pw_properties_get(endpoint->props, PW_KEY_MEDIA_CLASS); | 
					
						
							| 
									
										
										
										
											2019-11-20 16:18:46 +01:00
										 |  |  | 	endpoint->info.session_id = impl->session->session->obj.id; | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	endpoint->info.direction = direction; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	endpoint->info.flags = 0; | 
					
						
							|  |  |  | 	endpoint->info.change_mask = | 
					
						
							|  |  |  | 		PW_ENDPOINT_CHANGE_MASK_STREAMS | | 
					
						
							|  |  |  | 		PW_ENDPOINT_CHANGE_MASK_SESSION | | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 		PW_ENDPOINT_CHANGE_MASK_PROPS | | 
					
						
							|  |  |  | 		PW_ENDPOINT_CHANGE_MASK_PARAMS; | 
					
						
							| 
									
										
										
										
											2019-11-14 18:35:29 +01:00
										 |  |  | 	endpoint->info.n_streams = 0; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	endpoint->info.props = &endpoint->props->dict; | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	endpoint->params[0] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); | 
					
						
							|  |  |  | 	endpoint->params[1] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); | 
					
						
							|  |  |  | 	endpoint->info.params = endpoint->params; | 
					
						
							|  |  |  | 	endpoint->info.n_params = 2; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	spa_list_init(&endpoint->stream_list); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	pw_log_debug(NAME" %p: new endpoint %p for alsa node %p", impl, endpoint, node); | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	pw_proxy_add_listener(proxy, | 
					
						
							|  |  |  | 			&endpoint->proxy_listener, | 
					
						
							|  |  |  | 			&proxy_events, endpoint); | 
					
						
							| 
									
										
										
										
											2019-11-15 13:34:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-11 16:31:58 +01:00
										 |  |  | 	pw_client_endpoint_add_listener(endpoint->client_endpoint, | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 			&endpoint->client_endpoint_listener, | 
					
						
							|  |  |  | 			&client_endpoint_events, | 
					
						
							|  |  |  | 			endpoint); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 	subscribe[n_subscribe++] = SPA_PARAM_EnumFormat; | 
					
						
							|  |  |  | 	subscribe[n_subscribe++] = SPA_PARAM_Props; | 
					
						
							|  |  |  | 	subscribe[n_subscribe++] = SPA_PARAM_PropInfo; | 
					
						
							|  |  |  | 	pw_log_debug(NAME" %p: endpoint %p proxy %p subscribe %d params", impl, | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 				endpoint, node->node->obj.proxy, n_subscribe); | 
					
						
							| 
									
										
										
										
											2019-12-11 15:26:11 +01:00
										 |  |  | 	pw_node_subscribe_params((struct pw_node*)node->node->obj.proxy, | 
					
						
							| 
									
										
										
										
											2019-11-26 12:53:28 +01:00
										 |  |  | 				subscribe, n_subscribe); | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	spa_list_append(&device->endpoint_list, &endpoint->link); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 13:12:04 +01:00
										 |  |  | 	if (monitor == NULL) | 
					
						
							|  |  |  | 		sm_media_session_sync(impl->session, complete_endpoint, endpoint); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	return endpoint; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | static void destroy_endpoint(struct endpoint *endpoint) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pw_proxy_destroy((struct pw_proxy*)endpoint->client_endpoint); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 16:34:12 +01:00
										 |  |  | /** fallback, one stream for each node */ | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | static int setup_alsa_fallback_endpoint(struct device *device) | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	struct impl *impl = device->impl; | 
					
						
							|  |  |  | 	struct sm_node *n; | 
					
						
							|  |  |  | 	struct sm_device *d = device->device; | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	pw_log_debug(NAME" %p: device %p fallback", impl, d); | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	spa_list_for_each(n, &d->node_list, link) { | 
					
						
							|  |  |  | 		struct node *node; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		pw_log_debug(NAME" %p: device %p has node %p", impl, d, n); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		node = sm_object_add_data(&n->obj, SESSION_KEY, sizeof(struct node)); | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 		node->device = device; | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 		node->node = n; | 
					
						
							|  |  |  | 		node->impl = impl; | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 		node->endpoint = create_endpoint(node, NULL); | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 		if (node->endpoint == NULL) | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 			return -errno; | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 16:34:12 +01:00
										 |  |  | /** UCM.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * We create 1 stream for each verb + modifier combination | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | static int setup_alsa_ucm_endpoint(struct device *device) | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	const char *str, *card_name = NULL; | 
					
						
							|  |  |  | 	char *name_free = NULL; | 
					
						
							|  |  |  | 	int i, res, num_verbs; | 
					
						
							|  |  |  | 	const char **verb_list = NULL; | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	struct spa_dict *props = device->device->info->props; | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	snd_use_case_mgr_t *ucm; | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	card_name = spa_dict_lookup(props, SPA_KEY_API_ALSA_CARD_NAME); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 	if (card_name == NULL && | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	    (str = spa_dict_lookup(props, SPA_KEY_API_ALSA_CARD)) != NULL) { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 		snd_card_get_name(atoi(str), &name_free); | 
					
						
							|  |  |  | 		card_name = name_free; | 
					
						
							|  |  |  | 		pw_log_debug("got card name %s for index %s", card_name, str); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (card_name == NULL) { | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 		pw_log_error("can't get card name for index %s", str); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 		res = -ENOTSUP; | 
					
						
							|  |  |  | 		goto exit; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	if ((res = snd_use_case_mgr_open(&ucm, card_name)) < 0) { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 		pw_log_error("can not open UCM for %s: %s", card_name, snd_strerror(res)); | 
					
						
							|  |  |  | 		goto exit; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	num_verbs = snd_use_case_verb_list(ucm, &verb_list); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 	if (num_verbs < 0) { | 
					
						
							|  |  |  | 		res = num_verbs; | 
					
						
							|  |  |  | 		pw_log_error("UCM verb list not found for %s: %s", card_name, snd_strerror(num_verbs)); | 
					
						
							|  |  |  | 		goto close_exit; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < num_verbs; i++) { | 
					
						
							|  |  |  | 		pw_log_debug("verb: %s", verb_list[i]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snd_use_case_free_list(verb_list, num_verbs); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	res = -ENOTSUP; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | close_exit: | 
					
						
							| 
									
										
										
										
											2019-11-13 09:38:40 +01:00
										 |  |  | 	snd_use_case_mgr_close(ucm); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | exit: | 
					
						
							|  |  |  | 	free(name_free); | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | static int activate_device(struct device *device) | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	int res; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	if ((res = setup_alsa_ucm_endpoint(device)) < 0) | 
					
						
							|  |  |  | 		res = setup_alsa_fallback_endpoint(device); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:27:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-11-20 16:18:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | static int deactivate_device(struct device *device) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct endpoint *e; | 
					
						
							|  |  |  | 	spa_list_consume(e, &device->endpoint_list, link) | 
					
						
							|  |  |  | 		destroy_endpoint(e); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | static void device_update(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device *device = data; | 
					
						
							|  |  |  | 	struct impl *impl = device->impl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pw_log_debug(NAME" %p: device %p %08x %08x", impl, device, | 
					
						
							|  |  |  | 			device->device->obj.avail, device->device->obj.changed); | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!SPA_FLAG_IS_SET(device->device->obj.avail, | 
					
						
							| 
									
										
										
										
											2019-12-16 13:39:42 +01:00
										 |  |  | 			SM_DEVICE_CHANGE_MASK_INFO | | 
					
						
							|  |  |  | 			SM_DEVICE_CHANGE_MASK_NODES | | 
					
						
							|  |  |  | 			SM_DEVICE_CHANGE_MASK_PARAMS)) | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-16 13:39:42 +01:00
										 |  |  | 	if (SPA_FLAG_IS_SET(device->device->obj.changed, | 
					
						
							|  |  |  | 			SM_DEVICE_CHANGE_MASK_NODES | | 
					
						
							|  |  |  | 			SM_DEVICE_CHANGE_MASK_PARAMS)) { | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 		activate_device(device); | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct sm_object_events device_events = { | 
					
						
							|  |  |  | 	SM_VERSION_OBJECT_EVENTS, | 
					
						
							|  |  |  | 	.update = device_update | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 16:18:46 +01:00
										 |  |  | static int | 
					
						
							|  |  |  | handle_device(struct impl *impl, struct sm_object *obj) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	const char *media_class, *str; | 
					
						
							|  |  |  | 	struct device *device; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (obj->props == NULL) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	media_class = pw_properties_get(obj->props, PW_KEY_MEDIA_CLASS); | 
					
						
							|  |  |  | 	str = pw_properties_get(obj->props, PW_KEY_DEVICE_API); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pw_log_debug(NAME" %p: device "PW_KEY_MEDIA_CLASS":%s api:%s", impl, media_class, str); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (strstr(media_class, "Audio/") != media_class) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	if (strcmp(str, "alsa") != 0) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	device = sm_object_add_data(obj, SESSION_KEY, sizeof(struct device)); | 
					
						
							|  |  |  | 	device->impl = impl; | 
					
						
							|  |  |  | 	device->id = obj->id; | 
					
						
							|  |  |  | 	device->device = (struct sm_device*)obj; | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	spa_list_init(&device->endpoint_list); | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	pw_log_debug(NAME" %p: found alsa device %d media_class %s", impl, obj->id, media_class); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sm_object_add_listener(obj, &device->listener, &device_events, device); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 16:18:46 +01:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | static void destroy_device(struct device *device) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	deactivate_device(device); | 
					
						
							|  |  |  | 	spa_hook_remove(&device->listener); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | static void session_create(void *data, struct sm_object *object) | 
					
						
							| 
									
										
										
										
											2019-11-20 16:18:46 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct impl *impl = data; | 
					
						
							|  |  |  | 	int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (object->type) { | 
					
						
							|  |  |  | 	case PW_TYPE_INTERFACE_Device: | 
					
						
							|  |  |  | 		res = handle_device(impl, object); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		res = 0; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (res < 0) { | 
					
						
							|  |  |  | 		pw_log_warn(NAME" %p: can't handle global %d: %s", impl, | 
					
						
							|  |  |  | 				object->id, spa_strerror(res)); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void session_remove(void *data, struct sm_object *object) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (object->type) { | 
					
						
							|  |  |  | 	case PW_TYPE_INTERFACE_Device: | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		struct device *device; | 
					
						
							|  |  |  | 		if ((device = sm_object_get_data(object, SESSION_KEY)) != NULL) | 
					
						
							|  |  |  | 			destroy_device(device); | 
					
						
							| 
									
										
										
										
											2019-11-20 16:18:46 +01:00
										 |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2019-11-29 13:21:55 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-20 16:18:46 +01:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct sm_media_session_events session_events = { | 
					
						
							|  |  |  | 	SM_VERSION_MEDIA_SESSION_EVENTS, | 
					
						
							| 
									
										
										
										
											2019-11-28 11:13:53 +01:00
										 |  |  | 	.create = session_create, | 
					
						
							| 
									
										
										
										
											2019-11-20 16:18:46 +01:00
										 |  |  | 	.remove = session_remove, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void *sm_alsa_endpoint_start(struct sm_media_session *session) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct impl *impl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	impl = calloc(1, sizeof(struct impl)); | 
					
						
							|  |  |  | 	if (impl == NULL) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	impl->session = session; | 
					
						
							|  |  |  | 	sm_media_session_add_listener(session, &impl->listener, &session_events, impl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return impl; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int sm_alsa_endpoint_stop(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } |