mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	pulse: control volume on device or node
Send the mute and volume values in the route param when hw volume is available. Check if the route has volume/mute and then use that to control the volume instead of them node volume.
This commit is contained in:
		
							parent
							
								
									9ad2732405
								
							
						
					
					
						commit
						dc7868a476
					
				
					 3 changed files with 142 additions and 89 deletions
				
			
		| 
						 | 
				
			
			@ -338,11 +338,70 @@ static void device_event_info(void *object, const struct pw_device_info *info)
 | 
			
		|||
	global_sync(g);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void parse_props(struct global *g, const struct spa_pod *param, bool hw)
 | 
			
		||||
{
 | 
			
		||||
	struct spa_pod_prop *prop;
 | 
			
		||||
	struct spa_pod_object *obj = (struct spa_pod_object *) param;
 | 
			
		||||
 | 
			
		||||
	SPA_POD_OBJECT_FOREACH(obj, prop) {
 | 
			
		||||
		switch (prop->key) {
 | 
			
		||||
		case SPA_PROP_volume:
 | 
			
		||||
			spa_pod_get_float(&prop->value, &g->node_info.volume);
 | 
			
		||||
			SPA_FLAG_UPDATE(g->node_info.flags, NODE_FLAG_HW_VOLUME, hw);
 | 
			
		||||
			break;
 | 
			
		||||
		case SPA_PROP_mute:
 | 
			
		||||
			spa_pod_get_bool(&prop->value, &g->node_info.mute);
 | 
			
		||||
			SPA_FLAG_UPDATE(g->node_info.flags, NODE_FLAG_HW_MUTE, hw);
 | 
			
		||||
			break;
 | 
			
		||||
		case SPA_PROP_channelVolumes:
 | 
			
		||||
		{
 | 
			
		||||
			uint32_t n_vals;
 | 
			
		||||
 | 
			
		||||
			n_vals = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
 | 
			
		||||
					g->node_info.channel_volumes, SPA_AUDIO_MAX_CHANNELS);
 | 
			
		||||
 | 
			
		||||
			if (n_vals != g->node_info.n_channel_volumes) {
 | 
			
		||||
				pw_log_debug("channel change %d->%d, trigger remove",
 | 
			
		||||
						g->node_info.n_channel_volumes, n_vals);
 | 
			
		||||
				emit_event(g->context, g, PA_SUBSCRIPTION_EVENT_REMOVE);
 | 
			
		||||
				g->node_info.n_channel_volumes = n_vals;
 | 
			
		||||
				/* mark as init, this will emit the NEW event when the
 | 
			
		||||
				 * params are updated */
 | 
			
		||||
				g->init = true;
 | 
			
		||||
			}
 | 
			
		||||
			SPA_FLAG_UPDATE(g->node_info.flags, NODE_FLAG_HW_VOLUME, hw);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct global *find_node_for_route(pa_context *c, struct global *card, uint32_t device)
 | 
			
		||||
{
 | 
			
		||||
	struct global *n;
 | 
			
		||||
	spa_list_for_each(n, &c->globals, link) {
 | 
			
		||||
		if (strcmp(n->type, PW_TYPE_INTERFACE_Node) != 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		pw_log_info("%d/%d %d/%d",
 | 
			
		||||
				n->node_info.device_id, card->id,
 | 
			
		||||
				n->node_info.profile_device_id, device);
 | 
			
		||||
		if (n->node_info.device_id != card->id)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (n->node_info.profile_device_id != device)
 | 
			
		||||
			continue;
 | 
			
		||||
		return n;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void device_event_param(void *object, int seq,
 | 
			
		||||
		uint32_t id, uint32_t index, uint32_t next,
 | 
			
		||||
		const struct spa_pod *param)
 | 
			
		||||
{
 | 
			
		||||
	struct global *g = object;
 | 
			
		||||
	pa_context *c = g->context;
 | 
			
		||||
 | 
			
		||||
	switch (id) {
 | 
			
		||||
	case SPA_PARAM_EnumProfile:
 | 
			
		||||
| 
						 | 
				
			
			@ -410,16 +469,26 @@ static void device_event_param(void *object, int seq,
 | 
			
		|||
	}
 | 
			
		||||
	case SPA_PARAM_Route:
 | 
			
		||||
	{
 | 
			
		||||
		uint32_t id;
 | 
			
		||||
		uint32_t id, device;
 | 
			
		||||
		enum spa_direction direction;
 | 
			
		||||
		struct spa_pod *props = NULL;
 | 
			
		||||
		struct global *ng;
 | 
			
		||||
 | 
			
		||||
		if (spa_pod_parse_object(param,
 | 
			
		||||
				SPA_TYPE_OBJECT_ParamRoute, NULL,
 | 
			
		||||
				SPA_PARAM_ROUTE_index, SPA_POD_Int(&id),
 | 
			
		||||
				SPA_PARAM_ROUTE_direction, SPA_POD_Id(&direction)) < 0) {
 | 
			
		||||
				SPA_PARAM_ROUTE_direction, SPA_POD_Id(&direction),
 | 
			
		||||
				SPA_PARAM_ROUTE_device, SPA_POD_Int(&device),
 | 
			
		||||
				SPA_PARAM_ROUTE_props, SPA_POD_OPT_Pod(&props)) < 0) {
 | 
			
		||||
			pw_log_warn("device %d: can't parse route", g->id);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		ng = find_node_for_route(c, g, device);
 | 
			
		||||
 | 
			
		||||
		if (props && ng) {
 | 
			
		||||
			parse_props(ng, props, true);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (direction == SPA_DIRECTION_OUTPUT)
 | 
			
		||||
			g->card_info.active_port_output = id;
 | 
			
		||||
		else
 | 
			
		||||
| 
						 | 
				
			
			@ -685,10 +754,16 @@ struct global_info device_info = {
 | 
			
		|||
static void node_event_info(void *object, const struct pw_node_info *info)
 | 
			
		||||
{
 | 
			
		||||
	struct global *g = object;
 | 
			
		||||
	const char *str;
 | 
			
		||||
	uint32_t i;
 | 
			
		||||
 | 
			
		||||
	pw_log_debug("update %d %"PRIu64, g->id, info->change_mask);
 | 
			
		||||
	g->info = pw_node_info_update(g->info, info);
 | 
			
		||||
	info = g->info = pw_node_info_update(g->info, info);
 | 
			
		||||
 | 
			
		||||
	if (info->props && (str = spa_dict_lookup(info->props, "card.profile.device")))
 | 
			
		||||
		g->node_info.profile_device_id = atoi(str);
 | 
			
		||||
	else
 | 
			
		||||
		g->node_info.profile_device_id = SPA_ID_INVALID;
 | 
			
		||||
 | 
			
		||||
	if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS && !g->subscribed) {
 | 
			
		||||
		uint32_t subscribed[32], n_subscribed = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -712,43 +787,6 @@ static void node_event_info(void *object, const struct pw_node_info *info)
 | 
			
		|||
	global_sync(g);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void parse_props(struct global *g, const struct spa_pod *param)
 | 
			
		||||
{
 | 
			
		||||
	struct spa_pod_prop *prop;
 | 
			
		||||
	struct spa_pod_object *obj = (struct spa_pod_object *) param;
 | 
			
		||||
 | 
			
		||||
	SPA_POD_OBJECT_FOREACH(obj, prop) {
 | 
			
		||||
		switch (prop->key) {
 | 
			
		||||
		case SPA_PROP_volume:
 | 
			
		||||
			spa_pod_get_float(&prop->value, &g->node_info.volume);
 | 
			
		||||
			break;
 | 
			
		||||
		case SPA_PROP_mute:
 | 
			
		||||
			spa_pod_get_bool(&prop->value, &g->node_info.mute);
 | 
			
		||||
			break;
 | 
			
		||||
		case SPA_PROP_channelVolumes:
 | 
			
		||||
		{
 | 
			
		||||
			uint32_t n_vals;
 | 
			
		||||
 | 
			
		||||
			n_vals = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
 | 
			
		||||
					g->node_info.channel_volumes, SPA_AUDIO_MAX_CHANNELS);
 | 
			
		||||
 | 
			
		||||
			if (n_vals != g->node_info.n_channel_volumes) {
 | 
			
		||||
				pw_log_debug("channel change %d->%d, trigger remove",
 | 
			
		||||
						g->node_info.n_channel_volumes, n_vals);
 | 
			
		||||
				emit_event(g->context, g, PA_SUBSCRIPTION_EVENT_REMOVE);
 | 
			
		||||
				g->node_info.n_channel_volumes = n_vals;
 | 
			
		||||
				/* mark as init, this will emit the NEW event when the
 | 
			
		||||
				 * params are updated */
 | 
			
		||||
				g->init = true;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void node_event_param(void *object, int seq,
 | 
			
		||||
		uint32_t id, uint32_t index, uint32_t next,
 | 
			
		||||
		const struct spa_pod *param)
 | 
			
		||||
| 
						 | 
				
			
			@ -758,7 +796,8 @@ static void node_event_param(void *object, int seq,
 | 
			
		|||
 | 
			
		||||
	switch (id) {
 | 
			
		||||
	case SPA_PARAM_Props:
 | 
			
		||||
		parse_props(g, param);
 | 
			
		||||
		if (!SPA_FLAG_IS_SET(g->node_info.flags, NODE_FLAG_HW_VOLUME | NODE_FLAG_HW_MUTE))
 | 
			
		||||
			parse_props(g, param, false);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
| 
						 | 
				
			
			@ -935,6 +974,8 @@ static int set_mask(pa_context *c, struct global *g)
 | 
			
		|||
		ginfo = &device_info;
 | 
			
		||||
		spa_list_init(&g->card_info.profiles);
 | 
			
		||||
		spa_list_init(&g->card_info.ports);
 | 
			
		||||
		g->card_info.active_port_output = SPA_ID_INVALID;
 | 
			
		||||
		g->card_info.active_port_input = SPA_ID_INVALID;
 | 
			
		||||
	} else if (strcmp(g->type, PW_TYPE_INTERFACE_Node) == 0) {
 | 
			
		||||
		if (g->props == NULL)
 | 
			
		||||
			return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -971,8 +1012,13 @@ static int set_mask(pa_context *c, struct global *g)
 | 
			
		|||
 | 
			
		||||
		if ((str = pw_properties_get(g->props, PW_KEY_CLIENT_ID)) != NULL)
 | 
			
		||||
			g->node_info.client_id = atoi(str);
 | 
			
		||||
		else
 | 
			
		||||
			g->node_info.client_id = SPA_ID_INVALID;
 | 
			
		||||
 | 
			
		||||
		if ((str = pw_properties_get(g->props, PW_KEY_DEVICE_ID)) != NULL)
 | 
			
		||||
			g->node_info.device_id = atoi(str);
 | 
			
		||||
		else
 | 
			
		||||
			g->node_info.device_id = SPA_ID_INVALID;
 | 
			
		||||
 | 
			
		||||
		ginfo = &node_info;
 | 
			
		||||
		g->node_info.volume = 1.0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -262,13 +262,17 @@ struct global {
 | 
			
		|||
		} link_info;
 | 
			
		||||
		/* for sink/source */
 | 
			
		||||
		struct {
 | 
			
		||||
			uint32_t client_id;
 | 
			
		||||
			uint32_t client_id;	/* if of owner client */
 | 
			
		||||
			uint32_t monitor;
 | 
			
		||||
#define NODE_FLAG_HW_VOLUME	(1 << 0)
 | 
			
		||||
#define NODE_FLAG_HW_MUTE	(1 << 4)
 | 
			
		||||
			uint32_t flags;
 | 
			
		||||
			float volume;
 | 
			
		||||
			bool mute;
 | 
			
		||||
			uint32_t n_channel_volumes;
 | 
			
		||||
			float channel_volumes[SPA_AUDIO_MAX_CHANNELS];
 | 
			
		||||
			uint32_t device_id;
 | 
			
		||||
			uint32_t device_id;		/* id of device (card) */
 | 
			
		||||
			uint32_t profile_device_id;	/* id in profile */
 | 
			
		||||
		} node_info;
 | 
			
		||||
		struct {
 | 
			
		||||
			uint32_t node_id;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -141,18 +141,18 @@ static void sink_callback(pa_context *c, struct global *g, struct sink_data *d)
 | 
			
		|||
	i.latency = 0;
 | 
			
		||||
	i.driver = "PipeWire";
 | 
			
		||||
	i.flags = PA_SINK_HARDWARE |
 | 
			
		||||
		  PA_SINK_HW_VOLUME_CTRL | PA_SINK_HW_MUTE_CTRL |
 | 
			
		||||
		  PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY |
 | 
			
		||||
		  PA_SINK_DECIBEL_VOLUME;
 | 
			
		||||
	if (SPA_FLAG_IS_SET(g->node_info.flags, NODE_FLAG_HW_VOLUME))
 | 
			
		||||
		  i.flags |= PA_SINK_HW_VOLUME_CTRL;
 | 
			
		||||
	if (SPA_FLAG_IS_SET(g->node_info.flags, NODE_FLAG_HW_MUTE))
 | 
			
		||||
		  i.flags |= PA_SINK_HW_MUTE_CTRL;
 | 
			
		||||
	i.proplist = pa_proplist_new_dict(info->props);
 | 
			
		||||
	i.configured_latency = 0;
 | 
			
		||||
	i.base_volume = PA_VOLUME_NORM;
 | 
			
		||||
	i.state = node_state_to_sink(info->state);
 | 
			
		||||
	i.n_volume_steps = PA_VOLUME_NORM+1;
 | 
			
		||||
	if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)))
 | 
			
		||||
		i.card = atoi(str);
 | 
			
		||||
	else
 | 
			
		||||
		i.card = PA_INVALID_INDEX;
 | 
			
		||||
	i.card = g->node_info.device_id;
 | 
			
		||||
	i.n_ports = 0;
 | 
			
		||||
	i.ports = NULL;
 | 
			
		||||
	i.active_port = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -384,34 +384,16 @@ static int set_node_volume(pa_context *c, struct global *g, const pa_cvolume *vo
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int set_device_volume(pa_context *c, struct global *g, const pa_cvolume *volume, bool mute)
 | 
			
		||||
static int set_device_volume(pa_context *c, struct global *g, struct global *cg, uint32_t id,
 | 
			
		||||
		uint32_t device_id, const pa_cvolume *volume, bool mute)
 | 
			
		||||
{
 | 
			
		||||
	struct global *cg;
 | 
			
		||||
	uint32_t id = SPA_ID_INVALID, card_id, device_id;
 | 
			
		||||
	char buf[1024];
 | 
			
		||||
	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
 | 
			
		||||
	struct spa_pod_frame f[2];
 | 
			
		||||
	struct spa_pod *param;
 | 
			
		||||
	struct pw_node_info *info = g->info;
 | 
			
		||||
	uint32_t i, n_channel_volumes;
 | 
			
		||||
	float channel_volumes[SPA_AUDIO_MAX_CHANNELS];
 | 
			
		||||
	float *vols;
 | 
			
		||||
	const char *str;
 | 
			
		||||
 | 
			
		||||
	if (info->props && (str = spa_dict_lookup(info->props, "card.profile.device")))
 | 
			
		||||
		device_id = atoi(str);
 | 
			
		||||
	else
 | 
			
		||||
		device_id = 0;
 | 
			
		||||
 | 
			
		||||
	if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)))
 | 
			
		||||
		card_id = atoi(str);
 | 
			
		||||
	else
 | 
			
		||||
		card_id = PA_INVALID_INDEX;
 | 
			
		||||
 | 
			
		||||
	pw_log_info("card:%u device:%u global:%u", card_id, device_id, g->id);
 | 
			
		||||
 | 
			
		||||
	if ((cg = pa_context_find_global(c, card_id)) == NULL)
 | 
			
		||||
		return PA_ERR_NOENTITY;
 | 
			
		||||
 | 
			
		||||
	if (volume) {
 | 
			
		||||
		for (i = 0; i < volume->channels; i++)
 | 
			
		||||
| 
						 | 
				
			
			@ -455,6 +437,34 @@ static int set_device_volume(pa_context *c, struct global *g, const pa_cvolume *
 | 
			
		|||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int set_volume(pa_context *c, struct global *g, const pa_cvolume *volume, bool mute,
 | 
			
		||||
		uint32_t mask)
 | 
			
		||||
{
 | 
			
		||||
	struct global *cg;
 | 
			
		||||
	uint32_t id = SPA_ID_INVALID, card_id, device_id;
 | 
			
		||||
	int res;
 | 
			
		||||
 | 
			
		||||
	card_id = g->node_info.device_id;
 | 
			
		||||
	device_id = g->node_info.profile_device_id;
 | 
			
		||||
 | 
			
		||||
	pw_log_info("card:%u global:%u flags:%08x", card_id, g->id, g->node_info.flags);
 | 
			
		||||
 | 
			
		||||
	if (SPA_FLAG_IS_SET(g->node_info.flags, NODE_FLAG_HW_VOLUME | NODE_FLAG_HW_MUTE) &&
 | 
			
		||||
	    (cg = pa_context_find_global(c, card_id)) != NULL) {
 | 
			
		||||
		if (mask & PA_SUBSCRIPTION_MASK_SINK)
 | 
			
		||||
			id = cg->card_info.active_port_output;
 | 
			
		||||
		else if (mask & PA_SUBSCRIPTION_MASK_SOURCE)
 | 
			
		||||
			id = cg->card_info.active_port_input;
 | 
			
		||||
	}
 | 
			
		||||
	if (id != SPA_ID_INVALID && device_id != SPA_ID_INVALID) {
 | 
			
		||||
		res = set_device_volume(c, g, cg, id, device_id, volume, mute);
 | 
			
		||||
	} else {
 | 
			
		||||
		res = set_node_volume(c, g, volume, mute);
 | 
			
		||||
	}
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct volume_data {
 | 
			
		||||
	pa_context_success_cb_t cb;
 | 
			
		||||
	uint32_t mask;
 | 
			
		||||
| 
						 | 
				
			
			@ -482,9 +492,10 @@ static void do_node_volume_mute(pa_operation *o, void *userdata)
 | 
			
		|||
			g = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (g) {
 | 
			
		||||
		error = set_device_volume(c, g,
 | 
			
		||||
		error = set_volume(c, g,
 | 
			
		||||
				d->have_volume ? &d->volume : NULL,
 | 
			
		||||
				d->have_volume ? g->node_info.mute : d->mute);
 | 
			
		||||
				d->have_volume ? g->node_info.mute : d->mute,
 | 
			
		||||
				d->mask);
 | 
			
		||||
	} else {
 | 
			
		||||
		error = PA_ERR_INVALID;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -651,22 +662,13 @@ static int set_device_route(pa_context *c, struct global *g, const char *port, e
 | 
			
		|||
	uint32_t id = SPA_ID_INVALID, card_id, device_id;
 | 
			
		||||
	char buf[1024];
 | 
			
		||||
	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
 | 
			
		||||
	struct pw_node_info *info = g->info;
 | 
			
		||||
	const char *str;
 | 
			
		||||
 | 
			
		||||
	if (info->props && (str = spa_dict_lookup(info->props, "card.profile.device")))
 | 
			
		||||
		device_id = atoi(str);
 | 
			
		||||
	else
 | 
			
		||||
		device_id = 0;
 | 
			
		||||
 | 
			
		||||
	if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)))
 | 
			
		||||
		card_id = atoi(str);
 | 
			
		||||
	else
 | 
			
		||||
		card_id = PA_INVALID_INDEX;
 | 
			
		||||
	card_id = g->node_info.device_id;
 | 
			
		||||
	device_id = g->node_info.profile_device_id;
 | 
			
		||||
 | 
			
		||||
	pw_log_info("port \"%s\": card:%u device:%u global:%u", port, card_id, device_id, g->id);
 | 
			
		||||
 | 
			
		||||
	if ((cg = pa_context_find_global(c, card_id)) == NULL)
 | 
			
		||||
	if ((cg = pa_context_find_global(c, card_id)) == NULL || device_id == SPA_ID_INVALID)
 | 
			
		||||
		return PA_ERR_NOENTITY;
 | 
			
		||||
 | 
			
		||||
	spa_list_for_each(p, &cg->card_info.ports, link) {
 | 
			
		||||
| 
						 | 
				
			
			@ -859,7 +861,11 @@ static void source_callback(pa_context *c, struct global *g, struct source_data
 | 
			
		|||
	} else {
 | 
			
		||||
		i.monitor_of_sink = PA_INVALID_INDEX;
 | 
			
		||||
		i.monitor_of_sink_name = NULL;
 | 
			
		||||
		flags |= PA_SOURCE_HARDWARE | PA_SOURCE_HW_VOLUME_CTRL | PA_SOURCE_HW_MUTE_CTRL;
 | 
			
		||||
		flags |= PA_SOURCE_HARDWARE;
 | 
			
		||||
		if (SPA_FLAG_IS_SET(g->node_info.flags, NODE_FLAG_HW_VOLUME))
 | 
			
		||||
			flags |= PA_SINK_HW_VOLUME_CTRL;
 | 
			
		||||
		if (SPA_FLAG_IS_SET(g->node_info.flags, NODE_FLAG_HW_MUTE))
 | 
			
		||||
			flags |= PA_SINK_HW_MUTE_CTRL;
 | 
			
		||||
	}
 | 
			
		||||
	i.latency = 0;
 | 
			
		||||
	i.driver = "PipeWire";
 | 
			
		||||
| 
						 | 
				
			
			@ -869,10 +875,7 @@ static void source_callback(pa_context *c, struct global *g, struct source_data
 | 
			
		|||
	i.base_volume = PA_VOLUME_NORM;
 | 
			
		||||
	i.state = node_state_to_source(info->state);
 | 
			
		||||
	i.n_volume_steps = PA_VOLUME_NORM+1;
 | 
			
		||||
	if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)))
 | 
			
		||||
		i.card = atoi(str);
 | 
			
		||||
	else
 | 
			
		||||
		i.card = PA_INVALID_INDEX;
 | 
			
		||||
	i.card = g->node_info.device_id;
 | 
			
		||||
	i.n_ports = 0;
 | 
			
		||||
	i.ports = NULL;
 | 
			
		||||
	i.active_port = NULL;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue