diff --git a/pipewire-pulseaudio/src/context.c b/pipewire-pulseaudio/src/context.c index 586cdea6e..4bf2189b3 100644 --- a/pipewire-pulseaudio/src/context.c +++ b/pipewire-pulseaudio/src/context.c @@ -305,6 +305,10 @@ static void device_event_info(void *object, const struct pw_device_info *info) pw_device_enum_params((struct pw_device*)g->proxy, 0, SPA_PARAM_Profile, 0, -1, NULL); break; + case SPA_PARAM_EnumRoute: + pw_device_enum_params((struct pw_device*)g->proxy, + 0, SPA_PARAM_EnumRoute, 0, -1, NULL); + break; default: break; } @@ -358,6 +362,31 @@ static void device_event_param(void *object, int seq, pw_log_debug("device %d: current profile %d", g->id, id); break; } + case SPA_PARAM_EnumRoute: + { + uint32_t id; + const char *name; + struct param *p; + + if (spa_pod_parse_object(param, + SPA_TYPE_OBJECT_ParamRoute, NULL, + SPA_PARAM_ROUTE_index, SPA_POD_Int(&id), + SPA_PARAM_ROUTE_name, SPA_POD_String(&name)) < 0) { + pw_log_warn("device %d: can't parse route", g->id); + return; + } + p = malloc(sizeof(struct param) + SPA_POD_SIZE(param)); + if (p) { + p->id = id; + p->seq = seq; + p->param = SPA_MEMBER(p, sizeof(struct param), struct spa_pod); + memcpy(p->param, param, SPA_POD_SIZE(param)); + spa_list_append(&g->card_info.ports, &p->link); + g->card_info.n_ports++; + } + pw_log_debug("device %d: enum route %d: \"%s\"", g->id, id, name); + break; + } default: break; } @@ -380,6 +409,10 @@ static void device_destroy(void *data) spa_list_remove(&p->link); free(p); } + spa_list_consume(p, &global->card_info.ports, link) { + spa_list_remove(&p->link); + free(p); + } if (global->info) pw_device_info_free(global->info); } @@ -617,9 +650,10 @@ static int set_mask(pa_context *c, struct global *g) g->event = PA_SUBSCRIPTION_EVENT_CARD; events = &device_events; - client_version = PW_VERSION_DEVICE; - destroy = device_destroy; - spa_list_init(&g->card_info.profiles); + client_version = PW_VERSION_DEVICE; + destroy = device_destroy; + 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; diff --git a/pipewire-pulseaudio/src/internal.h b/pipewire-pulseaudio/src/internal.h index 7ce68ff10..c6b673e03 100644 --- a/pipewire-pulseaudio/src/internal.h +++ b/pipewire-pulseaudio/src/internal.h @@ -269,6 +269,8 @@ struct global { struct spa_list profiles; uint32_t n_profiles; uint32_t active_profile; + struct spa_list ports; + uint32_t n_ports; pa_card_info info; } card_info; struct { diff --git a/pipewire-pulseaudio/src/introspect.c b/pipewire-pulseaudio/src/introspect.c index 6cdb46de2..6ae4774bb 100644 --- a/pipewire-pulseaudio/src/introspect.c +++ b/pipewire-pulseaudio/src/introspect.c @@ -1247,7 +1247,7 @@ static void card_callback(struct card_data *d) { struct global *g = d->global; pa_card_info *i = &g->card_info.info; - int n_profiles, j = 0; + int n_profiles, n_ports, j = 0; struct param *p; n_profiles = g->card_info.n_profiles; @@ -1262,7 +1262,7 @@ static void card_callback(struct card_data *d) 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, *iter; + struct spa_pod *classes = NULL; if (spa_pod_parse_object(p->param, SPA_TYPE_OBJECT_ParamProfile, NULL, @@ -1276,6 +1276,8 @@ static void card_callback(struct card_data *d) 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]; @@ -1283,16 +1285,16 @@ static void card_callback(struct card_data *d) uint32_t count; spa_pod_parser_pod(&prs, iter); - spa_pod_parser_push_struct(&prs, &f[0]); - while (spa_pod_parser_get(&prs, + if (spa_pod_parser_get_struct(&prs, SPA_POD_String(&class), - SPA_POD_Int(&count), - NULL) == 2) { - if (strcmp(class, "Audio/Sink") == 0) - n_play += count; - else if (strcmp(class, "Audio/Source") == 0) - n_cap += count; - } + 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]); } } @@ -1317,6 +1319,88 @@ static void card_callback(struct card_data *d) 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); }