diff --git a/pipewire-pulseaudio/src/context.c b/pipewire-pulseaudio/src/context.c index 4bf2189b3..0cb77d7d9 100644 --- a/pipewire-pulseaudio/src/context.c +++ b/pipewire-pulseaudio/src/context.c @@ -44,8 +44,8 @@ static void global_free(pa_context *c, struct global *g) { spa_list_remove(&g->link); - if (g->destroy) - g->destroy(g); + if (g->ginfo && g->ginfo->destroy) + g->ginfo->destroy(g); if (g->proxy) { pw_proxy_destroy(g->proxy); } @@ -255,6 +255,17 @@ static void emit_event(pa_context *c, struct global *g, pa_subscription_event_ty } } +static void remove_params(struct spa_list *params, uint32_t id) +{ + struct param *p, *t; + spa_list_for_each_safe(p, t, params, link) { + if (id == SPA_ID_INVALID || p->id == id) { + spa_list_remove(&p->link); + free(p); + } + } +} + static void update_device_props(struct global *g) { pa_card_info *i = &g->card_info.info; @@ -298,6 +309,9 @@ static void device_event_info(void *object, const struct pw_device_info *info) switch (info->params[n].id) { case SPA_PARAM_EnumProfile: + remove_params(&g->card_info.profiles, SPA_PARAM_EnumProfile); + g->card_info.n_profiles = 0; + g->card_info.pending_profiles = true; pw_device_enum_params((struct pw_device*)g->proxy, 0, SPA_PARAM_EnumProfile, 0, -1, NULL); break; @@ -306,6 +320,9 @@ static void device_event_info(void *object, const struct pw_device_info *info) 0, SPA_PARAM_Profile, 0, -1, NULL); break; case SPA_PARAM_EnumRoute: + remove_params(&g->card_info.ports, SPA_PARAM_EnumRoute); + g->card_info.n_ports = 0; + g->card_info.pending_ports = true; pw_device_enum_params((struct pw_device*)g->proxy, 0, SPA_PARAM_EnumRoute, 0, -1, NULL); break; @@ -392,6 +409,226 @@ static void device_event_param(void *object, int seq, } } +static void device_clear_profiles(struct global *g) +{ + pa_card_info *i = &g->card_info.info; + i->n_profiles = 0; + free(i->profiles); + i->profiles = NULL; + free(g->card_info.card_profiles); + g->card_info.card_profiles = NULL; + free(i->profiles2); + i->profiles2 = NULL; +} + +static void device_sync_profiles(struct global *g) +{ + pa_card_info *i = &g->card_info.info; + uint32_t n_profiles, j; + struct param *p; + + device_clear_profiles(g); + + n_profiles = g->card_info.n_profiles; + + i->profiles = calloc(n_profiles, sizeof(pa_card_profile_info)); + g->card_info.card_profiles = calloc(n_profiles, sizeof(pa_card_profile_info2)); + i->profiles2 = calloc(n_profiles + 1, sizeof(pa_card_profile_info2 *)); + i->n_profiles = 0; + + pw_log_debug("context %p: info for %d", g->context, g->id); + + j = 0; + spa_list_for_each(p, &g->card_info.profiles, link) { + uint32_t id, priority = 0, available = 0, n_cap = 0, n_play = 0; + const char *name = NULL; + const char *description = NULL; + struct spa_pod *classes = NULL, *info = NULL; + + if (spa_pod_parse_object(p->param, + SPA_TYPE_OBJECT_ParamProfile, NULL, + SPA_PARAM_PROFILE_index, SPA_POD_Int(&id), + SPA_PARAM_PROFILE_name, SPA_POD_String(&name), + SPA_PARAM_PROFILE_description, SPA_POD_OPT_String(&description), + SPA_PARAM_PROFILE_priority, SPA_POD_OPT_Int(&priority), + SPA_PARAM_PROFILE_available, SPA_POD_OPT_Id(&available), + SPA_PARAM_PROFILE_info, SPA_POD_OPT_Pod(&info), + SPA_PARAM_PROFILE_classes, SPA_POD_OPT_Pod(&classes)) < 0) { + pw_log_warn("device %d: can't parse profile", g->id); + continue; + } + if (classes != NULL) { + struct spa_pod *iter; + + SPA_POD_STRUCT_FOREACH(classes, iter) { + struct spa_pod_parser prs; + struct spa_pod_frame f[1]; + char *class; + uint32_t count; + + spa_pod_parser_pod(&prs, iter); + if (spa_pod_parser_get_struct(&prs, + SPA_POD_String(&class), + SPA_POD_Int(&count)) < 0) + continue; + + if (strcmp(class, "Audio/Sink") == 0) + n_play += count; + else if (strcmp(class, "Audio/Source") == 0) + n_cap += count; + + spa_pod_parser_pop(&prs, &f[0]); + } + } + + i->profiles[j].name = name; + i->profiles[j].description = description ? description : name; + i->profiles[j].n_sinks = n_play; + i->profiles[j].n_sources = n_cap; + i->profiles[j].priority = priority; + + i->profiles2[j] = &g->card_info.card_profiles[j]; + i->profiles2[j]->name = i->profiles[j].name; + i->profiles2[j]->description = i->profiles[j].description; + i->profiles2[j]->n_sinks = i->profiles[j].n_sinks; + i->profiles2[j]->n_sources = i->profiles[j].n_sources; + i->profiles2[j]->priority = i->profiles[j].priority; + i->profiles2[j]->available = available != SPA_PARAM_AVAILABILITY_no; + + if (g->card_info.active_profile == id) { + i->active_profile = &i->profiles[j]; + i->active_profile2 = i->profiles2[j]; + } + j++; + } + i->profiles2[j] = NULL; + i->n_profiles = j; +} + +static void device_clear_ports(struct global *g) +{ + pa_card_info *i = &g->card_info.info; + uint32_t n; + + for (n = 0; n < i->n_ports; i++) + free(i->ports[n]->profiles2); + + i->n_ports = 0; + free(i->ports); + i->ports = NULL; + free(g->card_info.card_ports); + g->card_info.card_ports = NULL; +} + +static void device_sync_ports(struct global *g) +{ + pa_card_info *i = &g->card_info.info; + uint32_t n_ports, j; + struct param *p; + + device_clear_ports(g); + + n_ports = g->card_info.n_ports; + i->ports = calloc(n_ports+1, sizeof(pa_card_port_info *)); + g->card_info.card_ports = calloc(n_ports, sizeof(pa_card_port_info)); + i->n_ports = 0; + g->card_info.active_port_input = SPA_ID_INVALID; + g->card_info.active_port_output = SPA_ID_INVALID; + + pw_log_debug("context %p: info for %d", g->context, g->id); + + j = 0; + + spa_list_for_each(p, &g->card_info.ports, link) { + uint32_t id, priority; + enum spa_direction direction; + const char *name = NULL, *description = NULL; + enum spa_param_availability available = SPA_PARAM_AVAILABILITY_unknown; + struct spa_pod *profiles = NULL, *info = NULL; + pa_card_port_info *pi; + + if (spa_pod_parse_object(p->param, + SPA_TYPE_OBJECT_ParamRoute, NULL, + SPA_PARAM_ROUTE_index, SPA_POD_Int(&id), + SPA_PARAM_ROUTE_direction, SPA_POD_Id(&direction), + SPA_PARAM_ROUTE_name, SPA_POD_String(&name), + SPA_PARAM_ROUTE_description, SPA_POD_OPT_String(&description), + SPA_PARAM_ROUTE_priority, SPA_POD_OPT_Int(&priority), + SPA_PARAM_ROUTE_available, SPA_POD_OPT_Id(&available), + SPA_PARAM_ROUTE_info, SPA_POD_OPT_Pod(&info), + SPA_PARAM_ROUTE_profiles, SPA_POD_OPT_Pod(&profiles)) < 0) { + pw_log_warn("device %d: can't parse route", g->id); + continue; + } + + pi = i->ports[j] = &g->card_info.card_ports[j]; + spa_zero(*pi); + pi->name = name; + pi->description = description; + pi->priority = priority; + pi->available = available; + pi->direction = direction; + pi->proplist = pa_proplist_new(); + while (info) { + struct spa_pod_parser prs; + struct spa_pod_frame f[1]; + uint32_t n, n_items; + const char *key, *value; + + spa_pod_parser_pod(&prs, info); + if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 || + spa_pod_parser_get_int(&prs, &n_items) < 0) + break; + + for (n = 0; n < n_items; n++) { + if (spa_pod_parser_get(&prs, + SPA_POD_String(&key), + SPA_POD_String(&value), + NULL) < 0) + break; + pa_proplist_sets(pi->proplist, key, value); + } + spa_pod_parser_pop(&prs, &f[0]); + break; + } + pi->n_profiles = 0; + pi->profiles = NULL; + pi->profiles2 = NULL; + while (profiles) { + uint32_t *pr, n, n_pr; + + pr = spa_pod_get_array(profiles, &n_pr); + if (pr == NULL) + break; + + pi->n_profiles = n_pr; + pi->profiles2 = calloc(n_pr + 1, sizeof(pa_card_profile_info2 *)); + for (n = 0; n < n_pr; n++) + pi->profiles2[n] = i->profiles2[pr[n]]; + pi->profiles2[n_pr] = NULL; + pi->profiles = (pa_card_profile_info **)pi->profiles2; + break; + } + j++; + } + i->ports[j] = NULL; + i->n_ports = j; + if (i->n_ports == 0) + i->ports = NULL; +} + +static void device_sync(struct global *g) +{ + if (g->card_info.pending_profiles) { + device_sync_profiles(g); + g->card_info.pending_profiles = false; + } + if (g->card_info.pending_ports) { + device_sync_ports(g); + g->card_info.pending_ports = false; + } +} + static const struct pw_device_events device_events = { PW_VERSION_DEVICE_EVENTS, .info = device_event_info, @@ -401,22 +638,27 @@ static const struct pw_device_events device_events = { static void device_destroy(void *data) { struct global *global = data; - struct param *p; if (global->card_info.info.proplist) pa_proplist_free(global->card_info.info.proplist); - spa_list_consume(p, &global->card_info.profiles, link) { - spa_list_remove(&p->link); - free(p); - } - spa_list_consume(p, &global->card_info.ports, link) { - spa_list_remove(&p->link); - free(p); - } + + device_clear_ports(global); + device_clear_profiles(global); + + remove_params(&global->card_info.ports, SPA_ID_INVALID); + remove_params(&global->card_info.profiles, SPA_ID_INVALID); + if (global->info) pw_device_info_free(global->info); } +struct global_info device_info = { + .version = PW_VERSION_DEVICE, + .events = &device_events, + .destroy = device_destroy, + .sync = device_sync, +}; + static void node_event_info(void *object, const struct pw_node_info *info) { struct global *g = object; @@ -513,6 +755,13 @@ static void node_destroy(void *data) pw_node_info_free(global->info); } +struct global_info node_info = { + .version = PW_VERSION_NODE, + .events = &node_events, + .destroy = node_destroy, +}; + + static void module_event_info(void *object, const struct pw_module_info *info) { struct global *g = object; @@ -551,6 +800,12 @@ static void module_destroy(void *data) pw_module_info_free(global->info); } +struct global_info module_info = { + .version = PW_VERSION_MODULE, + .events = &module_events, + .destroy = module_destroy, +}; + static void client_event_info(void *object, const struct pw_client_info *info) { struct global *g = object; @@ -591,6 +846,12 @@ static void client_destroy(void *data) pw_client_info_free(global->info); } +struct global_info client_info = { + .version = PW_VERSION_CLIENT, + .events = &client_events, + .destroy = client_destroy, +}; + static void proxy_removed(void *data) { struct global *g = data; @@ -611,6 +872,8 @@ static void proxy_done(void *data, int seq) pa_subscription_event_type_t event; if (g->pending_seq == seq) { + if (g->ginfo && g->ginfo->sync) + g->ginfo->sync(g); if (g->init) { g->init = false; event = PA_SUBSCRIPTION_EVENT_NEW; @@ -633,9 +896,7 @@ static int set_mask(pa_context *c, struct global *g) { const char *str; struct global *f; - const void *events = NULL; - pw_destroy_t destroy; - uint32_t client_version; + struct global_info *ginfo = NULL; if (strcmp(g->type, PW_TYPE_INTERFACE_Device) == 0) { if (g->props == NULL) @@ -648,12 +909,9 @@ static int set_mask(pa_context *c, struct global *g) pw_log_debug("found card %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_CARD; g->event = PA_SUBSCRIPTION_EVENT_CARD; - - events = &device_events; - client_version = PW_VERSION_DEVICE; - destroy = device_destroy; - spa_list_init(&g->card_info.profiles); - spa_list_init(&g->card_info.ports); + ginfo = &device_info; + spa_list_init(&g->card_info.profiles); + spa_list_init(&g->card_info.ports); } else if (strcmp(g->type, PW_TYPE_INTERFACE_Node) == 0) { if (g->props == NULL) return 0; @@ -693,9 +951,7 @@ static int set_mask(pa_context *c, struct global *g) if ((str = pw_properties_get(g->props, PW_KEY_DEVICE_ID)) != NULL) g->node_info.device_id = atoi(str); - events = &node_events; - client_version = PW_VERSION_NODE; - destroy = node_destroy; + ginfo = &node_info; g->node_info.volume = 1.0; g->node_info.mute = false; } else if (strcmp(g->type, PW_TYPE_INTERFACE_Port) == 0) { @@ -712,16 +968,12 @@ static int set_mask(pa_context *c, struct global *g) pw_log_debug("found module %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_MODULE; g->event = PA_SUBSCRIPTION_EVENT_MODULE; - events = &module_events; - client_version = PW_VERSION_MODULE; - destroy = module_destroy; + ginfo = &module_info; } else if (strcmp(g->type, PW_TYPE_INTERFACE_Client) == 0) { pw_log_debug("found client %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_CLIENT; g->event = PA_SUBSCRIPTION_EVENT_CLIENT; - events = &client_events; - client_version = PW_VERSION_CLIENT; - destroy = client_destroy; + ginfo = &client_info; } else if (strcmp(g->type, PW_TYPE_INTERFACE_Link) == 0) { if ((str = pw_properties_get(g->props, PW_KEY_LINK_OUTPUT_PORT)) == NULL) return 0; @@ -752,17 +1004,17 @@ static int set_mask(pa_context *c, struct global *g) pw_log_debug("global %p: id:%u mask %d/%d", g, g->id, g->mask, g->event); - if (events) { + if (ginfo) { pw_log_debug("bind %d", g->id); g->proxy = pw_registry_bind(c->registry, g->id, g->type, - client_version, 0); + ginfo->version, 0); if (g->proxy == NULL) return -ENOMEM; - pw_proxy_add_object_listener(g->proxy, &g->object_listener, events, g); + pw_proxy_add_object_listener(g->proxy, &g->object_listener, ginfo->events, g); pw_proxy_add_listener(g->proxy, &g->proxy_listener, &proxy_events, g); - g->destroy = destroy; + g->ginfo = ginfo; global_sync(g); } else { emit_event(c, g, PA_SUBSCRIPTION_EVENT_NEW); diff --git a/pipewire-pulseaudio/src/internal.h b/pipewire-pulseaudio/src/internal.h index c6b673e03..d66966e87 100644 --- a/pipewire-pulseaudio/src/internal.h +++ b/pipewire-pulseaudio/src/internal.h @@ -223,6 +223,15 @@ struct param { #define PA_IDX_FLAG_DSP 0x800000U #define PA_IDX_MASK_DSP 0x7fffffU +struct global; + +struct global_info { + uint32_t version; + const void *events; + pw_destroy_t destroy; + void (*sync) (struct global *g); +}; + struct global { struct spa_list link; uint32_t id; @@ -239,7 +248,7 @@ struct global { int subscribed:1; void *info; - pw_destroy_t destroy; + struct global_info *ginfo; struct pw_proxy *proxy; struct spa_hook proxy_listener; @@ -272,6 +281,12 @@ struct global { struct spa_list ports; uint32_t n_ports; pa_card_info info; + pa_card_profile_info2 *card_profiles; + unsigned int pending_profiles:1; + pa_card_port_info *card_ports; + unsigned int pending_ports:1; + uint32_t active_port_output; + uint32_t active_port_input; } card_info; struct { pa_module_info info; diff --git a/pipewire-pulseaudio/src/introspect.c b/pipewire-pulseaudio/src/introspect.c index 6ae4774bb..6e8a721d2 100644 --- a/pipewire-pulseaudio/src/introspect.c +++ b/pipewire-pulseaudio/src/introspect.c @@ -91,12 +91,21 @@ static int wait_globals(pa_context *c, pa_subscription_mask_t mask, pa_operation return 0; } +static int has_profile(pa_card_profile_info2 **list, pa_card_profile_info2 *active) +{ + for(;*list; list++) { + if (*list == active) + return 1; + } + return 0; +} + static void sink_callback(struct sink_data *d) { - struct global *g = d->global; + struct global *g = d->global, *cg; struct pw_node_info *info = g->info; const char *str; - uint32_t n; + uint32_t n, j; pa_sink_info i; pa_format_info ii[1]; pa_format_info *ip[1]; @@ -138,10 +147,40 @@ static void sink_callback(struct sink_data *d) i.base_volume = PA_VOLUME_NORM; i.state = node_state_to_sink(info->state); i.n_volume_steps = PA_VOLUME_NORM+1; - i.card = PA_INVALID_INDEX; + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID))) + i.card = atoi(str); + else + i.card = PA_INVALID_INDEX; i.n_ports = 0; i.ports = NULL; i.active_port = NULL; + if ((cg = pa_context_find_global(d->context, i.card)) != NULL) { + pa_sink_port_info *spi; + pa_card_info *ci = &cg->card_info.info; + + spi = alloca(ci->n_ports * sizeof(pa_sink_port_info)); + i.ports = alloca((ci->n_ports + 1) * sizeof(pa_sink_port_info *)); + + for (n = 0,j = 0; n < ci->n_ports; n++) { + if (ci->ports[n]->direction != PA_DIRECTION_OUTPUT) + continue; + if (!has_profile(ci->ports[n]->profiles2, ci->active_profile2)) + continue; + i.ports[j] = &spi[j]; + spi[j].name = ci->ports[n]->name; + spi[j].description = ci->ports[n]->description; + spi[j].priority = ci->ports[n]->priority; + spi[j].available = ci->ports[n]->available; + if (n == cg->card_info.active_port_output) + i.active_port = i.ports[j]; + j++; + } + i.n_ports = j; + if (i.n_ports == 0) + i.ports = NULL; + else + i.ports[j] = NULL; + } i.n_formats = 1; ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); @@ -555,10 +594,10 @@ static pa_source_state_t node_state_to_source(enum pw_node_state s) } static void source_callback(struct source_data *d) { - struct global *g = d->global; + struct global *g = d->global, *cg; struct pw_node_info *info = g->info; const char *str; - uint32_t n; + uint32_t n, j; pa_source_info i; pa_format_info ii[1]; pa_format_info *ip[1]; @@ -606,10 +645,40 @@ static void source_callback(struct source_data *d) i.base_volume = PA_VOLUME_NORM; i.state = node_state_to_source(info->state); i.n_volume_steps = PA_VOLUME_NORM+1; - i.card = PA_INVALID_INDEX; + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID))) + i.card = atoi(str); + else + i.card = PA_INVALID_INDEX; i.n_ports = 0; i.ports = NULL; i.active_port = NULL; + if ((cg = pa_context_find_global(d->context, i.card)) != NULL) { + pa_source_port_info *spi; + pa_card_info *ci = &cg->card_info.info; + + spi = alloca(ci->n_ports * sizeof(pa_source_port_info)); + i.ports = alloca((ci->n_ports + 1) * sizeof(pa_source_port_info *)); + + for (n = 0,j = 0; n < ci->n_ports; n++) { + if (ci->ports[n]->direction != PA_DIRECTION_INPUT) + continue; + if (!has_profile(ci->ports[n]->profiles2, ci->active_profile2)) + continue; + i.ports[j] = &spi[j]; + spi[j].name = ci->ports[n]->name; + spi[j].description = ci->ports[n]->description; + spi[j].priority = ci->ports[n]->priority; + spi[j].available = ci->ports[n]->available; + if (n == cg->card_info.active_port_input) + i.active_port = i.ports[j]; + j++; + } + i.n_ports = j; + if (i.n_ports == 0) + i.ports = NULL; + else + i.ports[j] = NULL; + } i.n_formats = 1; ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); @@ -1247,160 +1316,6 @@ static void card_callback(struct card_data *d) { struct global *g = d->global; pa_card_info *i = &g->card_info.info; - int n_profiles, n_ports, j = 0; - struct param *p; - - n_profiles = g->card_info.n_profiles; - - i->profiles = alloca(sizeof(pa_card_profile_info) * n_profiles); - i->profiles2 = alloca(sizeof(pa_card_profile_info2 *) * (n_profiles + 1)); - i->n_profiles = 0; - - pw_log_debug("context %p: info for %d", g->context, g->id); - - spa_list_for_each(p, &g->card_info.profiles, link) { - uint32_t id, priority = 0, available = 0, n_cap = 0, n_play = 0; - const char *name = NULL; - const char *description = NULL; - struct spa_pod *classes = NULL; - - if (spa_pod_parse_object(p->param, - SPA_TYPE_OBJECT_ParamProfile, NULL, - SPA_PARAM_PROFILE_index, SPA_POD_Int(&id), - SPA_PARAM_PROFILE_name, SPA_POD_String(&name), - SPA_PARAM_PROFILE_description, SPA_POD_OPT_String(&description), - SPA_PARAM_PROFILE_priority, SPA_POD_OPT_Int(&priority), - SPA_PARAM_PROFILE_available, SPA_POD_OPT_Id(&available), - SPA_PARAM_PROFILE_classes, SPA_POD_OPT_Pod(&classes)) < 0) { - pw_log_warn("device %d: can't parse profile", g->id); - continue; - } - if (classes != NULL) { - struct spa_pod *iter; - - SPA_POD_STRUCT_FOREACH(classes, iter) { - struct spa_pod_parser prs; - struct spa_pod_frame f[1]; - char *class; - uint32_t count; - - spa_pod_parser_pod(&prs, iter); - if (spa_pod_parser_get_struct(&prs, - SPA_POD_String(&class), - SPA_POD_Int(&count)) < 0) - continue; - - if (strcmp(class, "Audio/Sink") == 0) - n_play += count; - else if (strcmp(class, "Audio/Source") == 0) - n_cap += count; - - spa_pod_parser_pop(&prs, &f[0]); - } - } - i->profiles[j].name = name; - i->profiles[j].description = description ? description : name; - i->profiles[j].n_sinks = n_play; - i->profiles[j].n_sources = n_cap; - i->profiles[j].priority = priority; - - i->profiles2[j] = alloca(sizeof(pa_card_profile_info2)); - i->profiles2[j]->name = i->profiles[j].name; - i->profiles2[j]->description = i->profiles[j].description; - i->profiles2[j]->n_sinks = i->profiles[j].n_sinks; - i->profiles2[j]->n_sources = i->profiles[j].n_sources; - i->profiles2[j]->priority = i->profiles[j].priority; - i->profiles2[j]->available = available != SPA_PARAM_AVAILABILITY_no; - - if (g->card_info.active_profile == id) { - i->active_profile = &i->profiles[j]; - i->active_profile2 = i->profiles2[j]; - } - j = ++i->n_profiles; - } - i->profiles2[j] = NULL; - - n_ports = g->card_info.n_ports; - i->ports = alloca(sizeof(pa_card_port_info *) * (n_ports + 1)); - i->n_ports = 0; - - j = 0; - spa_list_for_each(p, &g->card_info.ports, link) { - uint32_t id, priority; - enum spa_direction direction; - const char *name = NULL, *description = NULL; - enum spa_param_availability available = SPA_PARAM_AVAILABILITY_unknown; - struct spa_pod *profiles = NULL, *info = NULL; - pa_card_port_info *pi; - - if (spa_pod_parse_object(p->param, - SPA_TYPE_OBJECT_ParamRoute, NULL, - SPA_PARAM_ROUTE_index, SPA_POD_Int(&id), - SPA_PARAM_ROUTE_direction, SPA_POD_Id(&direction), - SPA_PARAM_ROUTE_name, SPA_POD_String(&name), - SPA_PARAM_ROUTE_description, SPA_POD_OPT_String(&description), - SPA_PARAM_ROUTE_priority, SPA_POD_OPT_Int(&priority), - SPA_PARAM_ROUTE_available, SPA_POD_OPT_Id(&available), - SPA_PARAM_ROUTE_info, SPA_POD_OPT_Pod(&info), - SPA_PARAM_ROUTE_profiles, SPA_POD_OPT_Pod(&profiles)) < 0) { - pw_log_warn("device %d: can't parse route", g->id); - continue; - } - - pi = i->ports[j] = alloca(sizeof(pa_card_port_info)); - spa_zero(*pi); - pi->name = name; - pi->description = description; - pi->priority = priority; - pi->available = available; - pi->direction = direction; - pi->proplist = pa_proplist_new(); - while (info) { - struct spa_pod_parser prs; - struct spa_pod_frame f[1]; - uint32_t n, n_items; - const char *key, *value; - - spa_pod_parser_pod(&prs, info); - if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 || - spa_pod_parser_get_int(&prs, &n_items) < 0) - break; - - for (n = 0; n < n_items; n++) { - if (spa_pod_parser_get(&prs, - SPA_POD_String(&key), - SPA_POD_String(&value), - NULL) < 0) - break; - pa_proplist_sets(pi->proplist, key, value); - } - spa_pod_parser_pop(&prs, &f[0]); - break; - } - while (profiles) { - uint32_t *pr, n, n_pr; - - pr = spa_pod_get_array(profiles, &n_pr); - if (pr == NULL) - break; - - pi->n_profiles = n_pr; - pi->profiles = alloca(sizeof(pa_card_profile_info *) * (n_pr + 1)); - pi->profiles2 = alloca(sizeof(pa_card_profile_info2 *) * (n_pr + 1)); - - for (n = 0; n < n_pr; n++) { - pi->profiles[n] = (pa_card_profile_info*)i->profiles2[pr[n]]; - pi->profiles2[n] = i->profiles2[pr[n]]; - } - pi->profiles[n_pr] = NULL; - pi->profiles2[n_pr] = NULL; - break; - } - j = ++i->n_ports; - } - i->ports[j] = NULL; - if (i->n_ports == 0) - i->ports = NULL; d->cb(d->context, i, 0, d->userdata); }