From 6b5b56bcc7141e8d9a00ecc5db10e5328b2c22f7 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 21 Mar 2021 18:48:19 +0200 Subject: [PATCH] pulse-server: use transportCodec prop for bluez codec messages --- src/modules/module-protocol-pulse/collect.c | 101 ++++++++++++ .../module-protocol-pulse/message-handler.c | 154 ++++-------------- 2 files changed, 134 insertions(+), 121 deletions(-) diff --git a/src/modules/module-protocol-pulse/collect.c b/src/modules/module-protocol-pulse/collect.c index f0e6b80fc..32bbfa075 100644 --- a/src/modules/module-protocol-pulse/collect.c +++ b/src/modules/module-protocol-pulse/collect.c @@ -553,3 +553,104 @@ static struct spa_dict *collect_props(struct spa_pod *info, struct spa_dict *dic dict->n_items = n; return dict; } + +struct transport_codec_info { + enum spa_bluetooth_audio_codec id; + const char *description; +}; + +static uint32_t collect_transport_codec_info(struct pw_manager_object *card, + struct transport_codec_info *codecs, uint32_t max_codecs, uint32_t *active) +{ + struct pw_manager_param *p; + uint32_t n_codecs = 0; + + *active = SPA_ID_INVALID; + + if (card == NULL) + return 0; + + spa_list_for_each(p, &card->param_list, link) { + uint32_t iid; + const struct spa_pod_choice *type; + const struct spa_pod_struct *labels; + struct spa_pod_parser prs; + struct spa_pod_frame f; + int32_t *id; + bool first; + + if (p->id != SPA_PARAM_PropInfo) + continue; + + if (spa_pod_parse_object(p->param, + SPA_TYPE_OBJECT_PropInfo, NULL, + SPA_PROP_INFO_id, SPA_POD_Id(&iid), + SPA_PROP_INFO_type, SPA_POD_PodChoice(&type), + SPA_PROP_INFO_labels, SPA_POD_PodStruct(&labels)) < 0) + continue; + + if (iid != SPA_PROP_bluetoothAudioCodec) + continue; + + if (SPA_POD_CHOICE_TYPE(type) != SPA_CHOICE_Enum || + SPA_POD_TYPE(SPA_POD_CHOICE_CHILD(type)) != SPA_TYPE_Int) + continue; + + /* + * XXX: PropInfo currently uses Int, not Id, in type and labels. + */ + + /* Codec name list */ + first = true; + SPA_POD_CHOICE_FOREACH(type, id) { + if (first) { + /* Skip default */ + first = false; + continue; + } + if (n_codecs >= max_codecs) + break; + codecs[n_codecs++].id = *id; + } + + /* Codec description list */ + spa_pod_parser_pod(&prs, (struct spa_pod *)labels); + spa_pod_parser_push_struct(&prs, &f); + + while (1) { + int32_t id; + const char *desc; + uint32_t j; + + if (spa_pod_parser_get_int(&prs, &id) < 0 || + spa_pod_parser_get_string(&prs, &desc) < 0) + break; + + for (j = 0; j < n_codecs; ++j) { + if (codecs[j].id == (uint32_t)id) + codecs[j].description = desc; + } + } + } + + /* Active codec */ + spa_list_for_each(p, &card->param_list, link) { + uint32_t j; + uint32_t id; + + if (p->id != SPA_PARAM_Props) + continue; + + if (spa_pod_parse_object(p->param, + SPA_TYPE_OBJECT_Props, NULL, + SPA_PROP_bluetoothAudioCodec, SPA_POD_Id(&id)) < 0) + continue; + + for (j = 0; j < n_codecs; ++j) { + if (codecs[j].id == id) + *active = j; + } + } + + return n_codecs; +} diff --git a/src/modules/module-protocol-pulse/message-handler.c b/src/modules/module-protocol-pulse/message-handler.c index dd2cfa74f..7e52c1852 100644 --- a/src/modules/module-protocol-pulse/message-handler.c +++ b/src/modules/module-protocol-pulse/message-handler.c @@ -1,40 +1,29 @@ static int bluez_card_object_message_handler(struct pw_manager *m, struct pw_manager_object *o, const char *message, const char *params, char **response) { - struct card_info card_info = CARD_INFO_INIT; - uint32_t n_profiles; - struct profile_info *profile_info; - const char *prefix; - uint32_t prefix_len; - - /* - * XXX: We just list/switch profiles here, until codec is a separate - * XXX: device param. - */ + struct transport_codec_info codecs[64]; + uint32_t n_codecs, active; pw_log_debug(NAME "bluez-card %p object message:'%s' params:'%s'", o, message, params); - collect_card_info(o, &card_info); - profile_info = alloca(card_info.n_profiles * sizeof(*profile_info)); - n_profiles = collect_profile_info(o, &card_info, profile_info); + n_codecs = collect_transport_codec_info(o, codecs, SPA_N_ELEMENTS(codecs), &active); - if (card_info.active_profile_name && strstr(card_info.active_profile_name, "headset-head-unit") != NULL) - prefix = "headset-head-unit-"; - else - prefix = "a2dp-sink-"; - prefix_len = strlen(prefix); + if (n_codecs == 0) + return -EINVAL; if (strcmp(message, "switch-codec") == 0) { - uint32_t i; - uint32_t profile_id = SPA_ID_INVALID; regex_t re; regmatch_t matches[2]; - const char *str; - uint32_t str_len; + char *codec; + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + struct spa_pod_frame f[1]; + struct spa_pod *param; + uint32_t codec_id = SPA_ID_INVALID; /* Parse args */ if (params == NULL) return -EINVAL; - if (regcomp(&re, "[:space:]*{\\(.*\\)}[:space:]*", 0) != 0) + if (regcomp(&re, "[:space:]*{\\([0-9]*\\)}[:space:]*", 0) != 0) return -EIO; if (regexec(&re, params, SPA_N_ELEMENTS(matches), matches, 0) != 0) { regfree(&re); @@ -42,122 +31,45 @@ static int bluez_card_object_message_handler(struct pw_manager *m, struct pw_man } regfree(&re); - str = params + matches[1].rm_so; - str_len = matches[1].rm_eo - matches[1].rm_so; - - /* Find profile corresponding to selected codec */ - for (i = 0; i < n_profiles; ++i) { - if (strncmp(profile_info[i].name, prefix, prefix_len) != 0) - continue; - if (strncmp(profile_info[i].name + prefix_len, str, str_len) == 0 && - strlen(profile_info[i].name) == prefix_len + str_len) { - profile_id = profile_info[i].id; - goto found; - } + codec = strndup(params + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + if (codec) { + codec_id = atoi(codec); + free(codec); } - found: - if (profile_id != SPA_ID_INVALID) { - char buf[1024]; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + /* Switch codec */ + spa_pod_builder_push_object(&b, &f[0], + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); + spa_pod_builder_add(&b, + SPA_PROP_bluetoothAudioCodec, SPA_POD_Id(codec_id), 0); + param = spa_pod_builder_pop(&b, &f[0]); - /* Switch profile */ - pw_device_set_param((struct pw_device*)o->proxy, - SPA_PARAM_Profile, 0, - spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, - SPA_PARAM_PROFILE_index, SPA_POD_Int(profile_id), - SPA_PARAM_PROFILE_save, SPA_POD_Bool(true))); - return 0; - } else { - return -EINVAL; - } + pw_device_set_param((struct pw_device *)o->proxy, + SPA_PARAM_Props, 0, param); + return 0; } else if (strcmp(message, "list-codecs") == 0) { uint32_t i; - const char *p; FILE *r; size_t size; - regex_t re; - regmatch_t matches[2]; - bool found = false; r = open_memstream(response, &size); if (r == NULL) return -ENOMEM; - if (regcomp(&re, "codec \\(.*\\))", 0) != 0) - return -ENOMEM; - fputc('{', r); - - for (i = 0; i < n_profiles; ++i) { - if (strncmp(profile_info[i].name, prefix, prefix_len) != 0) - continue; - - found = true; - fputc('{', r); - fprintf(r, "{%s}", profile_info[i].name + prefix_len); - - /* Parse codec name from description */ - p = profile_info[i].description; - if (regexec(&re, p, SPA_N_ELEMENTS(matches), matches, 0) == 0) { - fputc('{', r); - fwrite(p + matches[1].rm_so, 1, matches[1].rm_eo - matches[1].rm_so, r); - fputc('}', r); - } else { - fprintf(r, "{%s}", p); - } - - fputc('}', r); + for (i = 0; i < n_codecs; ++i) { + const char *desc = codecs[i].description; + fprintf(r, "{{%d}{%s}}", (int)codecs[i].id, desc ? desc : "Unknown"); } - fputc('}', r); - regfree(&re); - - if (!found) { - fclose(r); - free(*response); - *response = NULL; - return -ENOSYS; - } - return fclose(r) ? -errno : 0; } else if (strcmp(message, "get-codec") == 0) { - const char *name = "none"; - FILE *r; - size_t size; - struct pw_manager_object *obj; - - r = open_memstream(response, &size); - if (r == NULL) - return -ENOMEM; - - /* Look up from nodes */ - spa_list_for_each(obj, &m->object_list, link) { - const char *str; - uint32_t card_id = SPA_ID_INVALID; - struct pw_node_info *info; - - if (obj->creating || obj->removing) - continue; - if (!object_is_sink(obj) && !object_is_source_or_monitor(obj)) - continue; - if ((info = obj->info) == NULL || info->props == NULL) - continue; - if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)) != NULL) - card_id = (uint32_t)atoi(str); - if (card_id != o->id) - continue; - str = spa_dict_lookup(info->props, "api.bluez5.codec"); - if (str) { - name = str; - break; - } - } - - fprintf(r, "{%s}", name); - return fclose(r) ? -errno : 0; + if (active == SPA_ID_INVALID) + *response = strdup("{none}"); + else + *response = spa_aprintf("{%d}", (int)codecs[active].id); + return *response ? 0 : -ENOMEM; } return -ENOSYS;