mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	context: list audio devices as cards
Find the sink/source a stream is linked to
This commit is contained in:
		
							parent
							
								
									e8dfd22a6b
								
							
						
					
					
						commit
						08d6071693
					
				
					 4 changed files with 126 additions and 39 deletions
				
			
		| 
						 | 
					@ -96,11 +96,53 @@ struct global *pa_context_find_global(pa_context *c, uint32_t id)
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct global *pa_context_find_linked(pa_context *c, uint32_t idx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct global *g, *f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_list_for_each(g, &c->globals, link) {
 | 
				
			||||||
 | 
							if (g->type != PW_TYPE_INTERFACE_Link)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pw_log_debug("%d %d %d", idx,
 | 
				
			||||||
 | 
									g->link_info.src->parent_id,
 | 
				
			||||||
 | 
									g->link_info.dst->parent_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (g->link_info.src->parent_id == idx)
 | 
				
			||||||
 | 
								f = pa_context_find_global(c, g->link_info.dst->parent_id);
 | 
				
			||||||
 | 
							else if (g->link_info.dst->parent_id == idx)
 | 
				
			||||||
 | 
								f = pa_context_find_global(c, g->link_info.src->parent_id);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (f == NULL)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							if (f->mask & PA_SUBSCRIPTION_MASK_DSP) {
 | 
				
			||||||
 | 
								f = f->dsp_info.session;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return f;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int set_mask(pa_context *c, struct global *g)
 | 
					static int set_mask(pa_context *c, struct global *g)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const char *str;
 | 
						const char *str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (g->type) {
 | 
						switch (g->type) {
 | 
				
			||||||
 | 
						case PW_TYPE_INTERFACE_Device:
 | 
				
			||||||
 | 
							if (g->props == NULL)
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							if ((str = pw_properties_get(g->props, "media.class")) == NULL)
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							if (strcmp(str, "Audio/Device") != 0)
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pw_log_debug("found card %d", g->id);
 | 
				
			||||||
 | 
							g->mask = PA_SUBSCRIPTION_MASK_CARD;
 | 
				
			||||||
 | 
							g->event = PA_SUBSCRIPTION_EVENT_CARD;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case PW_TYPE_INTERFACE_Node:
 | 
						case PW_TYPE_INTERFACE_Node:
 | 
				
			||||||
		if (g->props == NULL)
 | 
							if (g->props == NULL)
 | 
				
			||||||
			return 0;
 | 
								return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -258,6 +258,7 @@ struct pa_context {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct global *pa_context_find_global(pa_context *c, uint32_t id);
 | 
					struct global *pa_context_find_global(pa_context *c, uint32_t id);
 | 
				
			||||||
struct global *pa_context_find_global_by_name(pa_context *c, uint32_t mask, const char *name);
 | 
					struct global *pa_context_find_global_by_name(pa_context *c, uint32_t mask, const char *name);
 | 
				
			||||||
 | 
					struct global *pa_context_find_linked(pa_context *c, uint32_t id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MAX_BUFFERS     64
 | 
					#define MAX_BUFFERS     64
 | 
				
			||||||
#define MASK_BUFFERS    (MAX_BUFFERS-1)
 | 
					#define MASK_BUFFERS    (MAX_BUFFERS-1)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,6 +73,18 @@ static const struct pw_client_proxy_events client_events = {
 | 
				
			||||||
	.info = client_event_info,
 | 
						.info = client_event_info,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void device_event_info(void *object, struct pw_device_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					        struct global *g = object;
 | 
				
			||||||
 | 
						pw_log_debug("update %d", g->id);
 | 
				
			||||||
 | 
					        g->info = pw_device_info_update(g->info, info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct pw_device_proxy_events device_events = {
 | 
				
			||||||
 | 
						PW_VERSION_DEVICE_PROXY_EVENTS,
 | 
				
			||||||
 | 
						.info = device_event_info,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ensure_global(pa_context *c, struct global *g)
 | 
					static int ensure_global(pa_context *c, struct global *g)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint32_t client_version;
 | 
						uint32_t client_version;
 | 
				
			||||||
| 
						 | 
					@ -98,6 +110,11 @@ static int ensure_global(pa_context *c, struct global *g)
 | 
				
			||||||
                client_version = PW_VERSION_CLIENT;
 | 
					                client_version = PW_VERSION_CLIENT;
 | 
				
			||||||
                destroy = (pw_destroy_t) pw_client_info_free;
 | 
					                destroy = (pw_destroy_t) pw_client_info_free;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case PW_TYPE_INTERFACE_Device:
 | 
				
			||||||
 | 
							events = &device_events;
 | 
				
			||||||
 | 
					                client_version = PW_VERSION_DEVICE;
 | 
				
			||||||
 | 
					                destroy = (pw_destroy_t) pw_device_info_free;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -851,12 +868,45 @@ struct card_data {
 | 
				
			||||||
	pa_context *context;
 | 
						pa_context *context;
 | 
				
			||||||
	pa_card_info_cb_t cb;
 | 
						pa_card_info_cb_t cb;
 | 
				
			||||||
	void *userdata;
 | 
						void *userdata;
 | 
				
			||||||
 | 
						struct global *global;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void card_info(pa_operation *o, void *userdata)
 | 
					static void card_callback(struct card_data *d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct global *g = d->global;
 | 
				
			||||||
 | 
						struct pw_device_info *info = g->info;
 | 
				
			||||||
 | 
						pa_card_info i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_zero(i);
 | 
				
			||||||
 | 
						i.index = g->id;
 | 
				
			||||||
 | 
						i.name = info->name;
 | 
				
			||||||
 | 
						i.owner_module = g->parent_id;
 | 
				
			||||||
 | 
						i.driver = info->props ?
 | 
				
			||||||
 | 
							spa_dict_lookup(info->props, "device.api") : NULL;
 | 
				
			||||||
 | 
						i.n_profiles = 0;
 | 
				
			||||||
 | 
						i.profiles = NULL;
 | 
				
			||||||
 | 
						i.active_profile = NULL;
 | 
				
			||||||
 | 
						i.proplist = pa_proplist_new_dict(info->props);
 | 
				
			||||||
 | 
						i.n_ports = 0;
 | 
				
			||||||
 | 
						i.ports = NULL;
 | 
				
			||||||
 | 
						i.profiles2 = NULL;
 | 
				
			||||||
 | 
						i.active_profile = NULL;
 | 
				
			||||||
 | 
						d->cb(d->context, &i, 0, d->userdata);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void card_info_list(pa_operation *o, void *userdata)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct card_data *d = userdata;
 | 
						struct card_data *d = userdata;
 | 
				
			||||||
	d->cb(d->context, NULL, 1, d->userdata);
 | 
						pa_context *c = d->context;
 | 
				
			||||||
 | 
						struct global *g;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_list_for_each(g, &c->globals, link) {
 | 
				
			||||||
 | 
							if (!(g->mask & PA_SUBSCRIPTION_MASK_CARD))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							d->global = g;
 | 
				
			||||||
 | 
							card_callback(d);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d->cb(c, NULL, 1, d->userdata);
 | 
				
			||||||
	pa_operation_done(o);
 | 
						pa_operation_done(o);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -869,7 +919,10 @@ pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb,
 | 
				
			||||||
	pa_assert(c->refcount >= 1);
 | 
						pa_assert(c->refcount >= 1);
 | 
				
			||||||
	pa_assert(cb);
 | 
						pa_assert(cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	o = pa_operation_new(c, NULL, card_info, sizeof(struct card_data));
 | 
						PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ensure_types(c, PA_SUBSCRIPTION_MASK_CARD);
 | 
				
			||||||
 | 
						o = pa_operation_new(c, NULL, card_info_list, sizeof(struct card_data));
 | 
				
			||||||
	d = o->userdata;
 | 
						d = o->userdata;
 | 
				
			||||||
	d->context = c;
 | 
						d->context = c;
 | 
				
			||||||
	d->cb = cb;
 | 
						d->cb = cb;
 | 
				
			||||||
| 
						 | 
					@ -916,7 +969,7 @@ struct sink_input_data {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sink_input_callback(struct sink_input_data *d)
 | 
					static void sink_input_callback(struct sink_input_data *d)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct global *g = d->global;
 | 
						struct global *g = d->global, *l;
 | 
				
			||||||
	struct pw_node_info *info = g->info;
 | 
						struct pw_node_info *info = g->info;
 | 
				
			||||||
	pa_sink_input_info i;
 | 
						pa_sink_input_info i;
 | 
				
			||||||
	pa_format_info ii[1];
 | 
						pa_format_info ii[1];
 | 
				
			||||||
| 
						 | 
					@ -933,11 +986,21 @@ static void sink_input_callback(struct sink_input_data *d)
 | 
				
			||||||
	i.name = info->name;
 | 
						i.name = info->name;
 | 
				
			||||||
	i.owner_module = PA_INVALID_INDEX;
 | 
						i.owner_module = PA_INVALID_INDEX;
 | 
				
			||||||
	i.client = g->parent_id;
 | 
						i.client = g->parent_id;
 | 
				
			||||||
	i.sink = PA_INVALID_INDEX;
 | 
						if (s) {
 | 
				
			||||||
 | 
							i.sink = s->device_index;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else {
 | 
				
			||||||
 | 
							l = pa_context_find_linked(d->context, g->id);
 | 
				
			||||||
 | 
							i.sink = l ? l->id : PA_INVALID_INDEX;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	pa_cvolume_init(&i.volume);
 | 
						pa_cvolume_init(&i.volume);
 | 
				
			||||||
	if (s && s->sample_spec.channels > 0) {
 | 
						if (s && s->sample_spec.channels > 0) {
 | 
				
			||||||
		i.sample_spec = s->sample_spec;
 | 
							i.sample_spec = s->sample_spec;
 | 
				
			||||||
		i.channel_map = s->channel_map;
 | 
							if (s->channel_map.channels == s->sample_spec.channels)
 | 
				
			||||||
 | 
								i.channel_map = s->channel_map;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								pa_channel_map_init_auto(&i.channel_map,
 | 
				
			||||||
 | 
										i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
 | 
				
			||||||
		pa_cvolume_set(&i.volume, i.sample_spec.channels, s->volume * PA_VOLUME_NORM);
 | 
							pa_cvolume_set(&i.volume, i.sample_spec.channels, s->volume * PA_VOLUME_NORM);
 | 
				
			||||||
		i.format = s->format;
 | 
							i.format = s->format;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1159,7 +1222,7 @@ struct source_output_data {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void source_output_callback(struct source_output_data *d)
 | 
					static void source_output_callback(struct source_output_data *d)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct global *g = d->global;
 | 
						struct global *g = d->global, *l;
 | 
				
			||||||
	struct pw_node_info *info = g->info;
 | 
						struct pw_node_info *info = g->info;
 | 
				
			||||||
	pa_source_output_info i;
 | 
						pa_source_output_info i;
 | 
				
			||||||
	pa_format_info ii[1];
 | 
						pa_format_info ii[1];
 | 
				
			||||||
| 
						 | 
					@ -1176,11 +1239,21 @@ static void source_output_callback(struct source_output_data *d)
 | 
				
			||||||
	i.name = info->name;
 | 
						i.name = info->name;
 | 
				
			||||||
	i.owner_module = PA_INVALID_INDEX;
 | 
						i.owner_module = PA_INVALID_INDEX;
 | 
				
			||||||
	i.client = g->parent_id;
 | 
						i.client = g->parent_id;
 | 
				
			||||||
	i.source = PA_INVALID_INDEX;
 | 
						if (s) {
 | 
				
			||||||
 | 
							i.source = s->device_index;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else {
 | 
				
			||||||
 | 
							l = pa_context_find_linked(d->context, g->id);
 | 
				
			||||||
 | 
							i.source = l ? l->id : PA_INVALID_INDEX;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	pa_cvolume_init(&i.volume);
 | 
						pa_cvolume_init(&i.volume);
 | 
				
			||||||
	if (s && s->sample_spec.channels > 0) {
 | 
						if (s && s->sample_spec.channels > 0) {
 | 
				
			||||||
		i.sample_spec = s->sample_spec;
 | 
							i.sample_spec = s->sample_spec;
 | 
				
			||||||
		i.channel_map = s->channel_map;
 | 
							if (s->channel_map.channels == s->sample_spec.channels)
 | 
				
			||||||
 | 
								i.channel_map = s->channel_map;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								pa_channel_map_init_auto(&i.channel_map,
 | 
				
			||||||
 | 
										i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
 | 
				
			||||||
		pa_cvolume_set(&i.volume, i.sample_spec.channels, s->volume * PA_VOLUME_NORM);
 | 
							pa_cvolume_set(&i.volume, i.sample_spec.channels, s->volume * PA_VOLUME_NORM);
 | 
				
			||||||
		i.format = s->format;
 | 
							i.format = s->format;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										31
									
								
								src/stream.c
									
										
									
									
									
								
							
							
						
						
									
										31
									
								
								src/stream.c
									
										
									
									
									
								
							| 
						 | 
					@ -185,41 +185,12 @@ static void configure_buffers(pa_stream *s)
 | 
				
			||||||
	dump_buffer_attr(s, &s->buffer_attr);
 | 
						dump_buffer_attr(s, &s->buffer_attr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct global *find_linked(pa_stream *s, uint32_t idx)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct global *g, *f;
 | 
					 | 
				
			||||||
	pa_context *c = s->context;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spa_list_for_each(g, &c->globals, link) {
 | 
					 | 
				
			||||||
		if (g->type != PW_TYPE_INTERFACE_Link)
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		pw_log_debug("%d %d %d", idx,
 | 
					 | 
				
			||||||
				g->link_info.src->parent_id,
 | 
					 | 
				
			||||||
				g->link_info.dst->parent_id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (g->link_info.src->parent_id == idx)
 | 
					 | 
				
			||||||
			f = pa_context_find_global(c, g->link_info.dst->parent_id);
 | 
					 | 
				
			||||||
		else if (g->link_info.dst->parent_id == idx)
 | 
					 | 
				
			||||||
			f = pa_context_find_global(c, g->link_info.src->parent_id);
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (f == NULL)
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		if (f->mask & PA_SUBSCRIPTION_MASK_DSP) {
 | 
					 | 
				
			||||||
			f = f->dsp_info.session;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return f;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
static void configure_device(pa_stream *s)
 | 
					static void configure_device(pa_stream *s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct global *g;
 | 
						struct global *g;
 | 
				
			||||||
	const char *str;
 | 
						const char *str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g = find_linked(s, pa_stream_get_index(s));
 | 
						g = pa_context_find_linked(s->context, pa_stream_get_index(s));
 | 
				
			||||||
	if (g == NULL) {
 | 
						if (g == NULL) {
 | 
				
			||||||
		s->device_index = PA_INVALID_INDEX;
 | 
							s->device_index = PA_INVALID_INDEX;
 | 
				
			||||||
		s->device_name = NULL;
 | 
							s->device_name = NULL;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue