mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-12-21 08:56:56 -05:00
pulse-server: use transportCodec prop for bluez codec messages
This commit is contained in:
parent
a552655edc
commit
6b5b56bcc7
2 changed files with 134 additions and 121 deletions
|
|
@ -553,3 +553,104 @@ static struct spa_dict *collect_props(struct spa_pod *info, struct spa_dict *dic
|
||||||
dict->n_items = n;
|
dict->n_items = n;
|
||||||
return dict;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
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;
|
struct transport_codec_info codecs[64];
|
||||||
uint32_t n_profiles;
|
uint32_t n_codecs, active;
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
pw_log_debug(NAME "bluez-card %p object message:'%s' params:'%s'", o, message, params);
|
pw_log_debug(NAME "bluez-card %p object message:'%s' params:'%s'", o, message, params);
|
||||||
|
|
||||||
collect_card_info(o, &card_info);
|
n_codecs = collect_transport_codec_info(o, codecs, SPA_N_ELEMENTS(codecs), &active);
|
||||||
profile_info = alloca(card_info.n_profiles * sizeof(*profile_info));
|
|
||||||
n_profiles = collect_profile_info(o, &card_info, profile_info);
|
|
||||||
|
|
||||||
if (card_info.active_profile_name && strstr(card_info.active_profile_name, "headset-head-unit") != NULL)
|
if (n_codecs == 0)
|
||||||
prefix = "headset-head-unit-";
|
return -EINVAL;
|
||||||
else
|
|
||||||
prefix = "a2dp-sink-";
|
|
||||||
prefix_len = strlen(prefix);
|
|
||||||
|
|
||||||
if (strcmp(message, "switch-codec") == 0) {
|
if (strcmp(message, "switch-codec") == 0) {
|
||||||
uint32_t i;
|
|
||||||
uint32_t profile_id = SPA_ID_INVALID;
|
|
||||||
regex_t re;
|
regex_t re;
|
||||||
regmatch_t matches[2];
|
regmatch_t matches[2];
|
||||||
const char *str;
|
char *codec;
|
||||||
uint32_t str_len;
|
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 */
|
/* Parse args */
|
||||||
if (params == NULL)
|
if (params == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (regcomp(&re, "[:space:]*{\\(.*\\)}[:space:]*", 0) != 0)
|
if (regcomp(&re, "[:space:]*{\\([0-9]*\\)}[:space:]*", 0) != 0)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
if (regexec(&re, params, SPA_N_ELEMENTS(matches), matches, 0) != 0) {
|
if (regexec(&re, params, SPA_N_ELEMENTS(matches), matches, 0) != 0) {
|
||||||
regfree(&re);
|
regfree(&re);
|
||||||
|
|
@ -42,122 +31,45 @@ static int bluez_card_object_message_handler(struct pw_manager *m, struct pw_man
|
||||||
}
|
}
|
||||||
regfree(&re);
|
regfree(&re);
|
||||||
|
|
||||||
str = params + matches[1].rm_so;
|
codec = strndup(params + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
|
||||||
str_len = matches[1].rm_eo - matches[1].rm_so;
|
if (codec) {
|
||||||
|
codec_id = atoi(codec);
|
||||||
/* Find profile corresponding to selected codec */
|
free(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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
found:
|
/* Switch codec */
|
||||||
if (profile_id != SPA_ID_INVALID) {
|
spa_pod_builder_push_object(&b, &f[0],
|
||||||
char buf[1024];
|
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
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,
|
||||||
pw_device_set_param((struct pw_device*)o->proxy,
|
SPA_PARAM_Props, 0, param);
|
||||||
SPA_PARAM_Profile, 0,
|
return 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;
|
|
||||||
}
|
|
||||||
} else if (strcmp(message, "list-codecs") == 0) {
|
} else if (strcmp(message, "list-codecs") == 0) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
const char *p;
|
|
||||||
FILE *r;
|
FILE *r;
|
||||||
size_t size;
|
size_t size;
|
||||||
regex_t re;
|
|
||||||
regmatch_t matches[2];
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
r = open_memstream(response, &size);
|
r = open_memstream(response, &size);
|
||||||
if (r == NULL)
|
if (r == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (regcomp(&re, "codec \\(.*\\))", 0) != 0)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
fputc('{', r);
|
fputc('{', r);
|
||||||
|
for (i = 0; i < n_codecs; ++i) {
|
||||||
for (i = 0; i < n_profiles; ++i) {
|
const char *desc = codecs[i].description;
|
||||||
if (strncmp(profile_info[i].name, prefix, prefix_len) != 0)
|
fprintf(r, "{{%d}{%s}}", (int)codecs[i].id, desc ? desc : "Unknown");
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fputc('}', r);
|
fputc('}', r);
|
||||||
|
|
||||||
regfree(&re);
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
fclose(r);
|
|
||||||
free(*response);
|
|
||||||
*response = NULL;
|
|
||||||
return -ENOSYS;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fclose(r) ? -errno : 0;
|
return fclose(r) ? -errno : 0;
|
||||||
} else if (strcmp(message, "get-codec") == 0) {
|
} else if (strcmp(message, "get-codec") == 0) {
|
||||||
const char *name = "none";
|
if (active == SPA_ID_INVALID)
|
||||||
FILE *r;
|
*response = strdup("{none}");
|
||||||
size_t size;
|
else
|
||||||
struct pw_manager_object *obj;
|
*response = spa_aprintf("{%d}", (int)codecs[active].id);
|
||||||
|
return *response ? 0 : -ENOMEM;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue