pulse-server: use transportCodec prop for bluez codec messages

This commit is contained in:
Pauli Virtanen 2021-03-21 18:48:19 +02:00 committed by Wim Taymans
parent a552655edc
commit 6b5b56bcc7
2 changed files with 134 additions and 121 deletions

View file

@ -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;