bluez5: adjust codec profile priority ordering

Higher priority for A2DP over HFP/HSP.  Prefer mSBC over CVSD for HFP,
and put A2DP codecs in the order we tell BlueZ to use.

Ensures that picking highest-priority profile gives sensible results
(e.g. does not pick HFP unless input route is required, and prefers A2DP
duplex codecs over HFP).
This commit is contained in:
Pauli Virtanen 2022-01-03 16:15:52 +02:00 committed by Wim Taymans
parent 878e630527
commit effa0ca124

View file

@ -183,13 +183,17 @@ static void get_a2dp_codecs(struct impl *this, enum spa_bluetooth_audio_codec id
*codecs = NULL; *codecs = NULL;
} }
static const struct a2dp_codec *get_supported_a2dp_codec(struct impl *this, enum spa_bluetooth_audio_codec id) static const struct a2dp_codec *get_supported_a2dp_codec(struct impl *this, enum spa_bluetooth_audio_codec id, size_t *idx)
{ {
const struct a2dp_codec *a2dp_codec = NULL; const struct a2dp_codec *a2dp_codec = NULL;
size_t i; size_t i;
for (i = 0; i < this->supported_codec_count; ++i) for (i = 0; i < this->supported_codec_count; ++i) {
if (this->supported_codecs[i]->id == id) if (this->supported_codecs[i]->id == id) {
a2dp_codec = this->supported_codecs[i]; a2dp_codec = this->supported_codecs[i];
if (idx)
*idx = i;
}
}
return a2dp_codec; return a2dp_codec;
} }
@ -684,7 +688,7 @@ static int emit_nodes(struct impl *this)
} }
} }
if (get_supported_a2dp_codec(this, this->props.codec) == NULL) if (get_supported_a2dp_codec(this, this->props.codec, NULL) == NULL)
this->props.codec = 0; this->props.codec = 0;
break; break;
case DEVICE_PROFILE_HSP_HFP: case DEVICE_PROFILE_HSP_HFP:
@ -894,7 +898,7 @@ static void profiles_changed(void *userdata, uint32_t prev_profiles, uint32_t pr
nodes_changed); nodes_changed);
break; break;
case DEVICE_PROFILE_A2DP: case DEVICE_PROFILE_A2DP:
if (get_supported_a2dp_codec(this, this->props.codec) == NULL) if (get_supported_a2dp_codec(this, this->props.codec, NULL) == NULL)
this->props.codec = 0; this->props.codec = 0;
nodes_changed = (connected_change & (SPA_BT_PROFILE_A2DP_SINK | nodes_changed = (connected_change & (SPA_BT_PROFILE_A2DP_SINK |
SPA_BT_PROFILE_A2DP_SOURCE)); SPA_BT_PROFILE_A2DP_SOURCE));
@ -990,7 +994,7 @@ static uint32_t profile_direction_mask(struct impl *this, uint32_t index, enum s
if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK)
have_output = true; have_output = true;
a2dp_codec = get_supported_a2dp_codec(this, codec); a2dp_codec = get_supported_a2dp_codec(this, codec, NULL);
if (a2dp_codec && a2dp_codec->duplex_codec) if (a2dp_codec && a2dp_codec->duplex_codec)
have_input = true; have_input = true;
break; break;
@ -1138,11 +1142,13 @@ static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *
char *desc_and_codec = NULL; char *desc_and_codec = NULL;
uint32_t n_source = 0, n_sink = 0; uint32_t n_source = 0, n_sink = 0;
uint32_t capture[1] = { DEVICE_ID_SOURCE }, playback[1] = { DEVICE_ID_SINK }; uint32_t capture[1] = { DEVICE_ID_SOURCE }, playback[1] = { DEVICE_ID_SINK };
int priority;
switch (profile_index) { switch (profile_index) {
case DEVICE_PROFILE_OFF: case DEVICE_PROFILE_OFF:
name = "off"; name = "off";
desc = _("Off"); desc = _("Off");
priority = 0;
break; break;
case DEVICE_PROFILE_AG: case DEVICE_PROFILE_AG:
{ {
@ -1154,6 +1160,7 @@ static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *
name = "audio-gateway"; name = "audio-gateway";
desc = _("Audio Gateway (A2DP Source & HSP/HFP AG)"); desc = _("Audio Gateway (A2DP Source & HSP/HFP AG)");
} }
priority = 256;
break; break;
} }
case DEVICE_PROFILE_A2DP: case DEVICE_PROFILE_A2DP:
@ -1167,7 +1174,8 @@ static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *
name = spa_bt_profile_name(profile); name = spa_bt_profile_name(profile);
n_sink++; n_sink++;
if (codec) { if (codec) {
const struct a2dp_codec *a2dp_codec = get_supported_a2dp_codec(this, codec); size_t idx;
const struct a2dp_codec *a2dp_codec = get_supported_a2dp_codec(this, codec, &idx);
if (a2dp_codec == NULL) { if (a2dp_codec == NULL) {
errno = EINVAL; errno = EINVAL;
return NULL; return NULL;
@ -1183,12 +1191,14 @@ static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *
} }
desc = desc_and_codec; desc = desc_and_codec;
priority = 16 + this->supported_codec_count - idx; /* order as in codec list */
} else { } else {
if (profile == SPA_BT_PROFILE_A2DP_SINK) { if (profile == SPA_BT_PROFILE_A2DP_SINK) {
desc = _("High Fidelity Playback (A2DP Sink)"); desc = _("High Fidelity Playback (A2DP Sink)");
} else { } else {
desc = _("High Fidelity Duplex (A2DP Source/Sink)"); desc = _("High Fidelity Duplex (A2DP Source/Sink)");
} }
priority = 16;
} }
break; break;
} }
@ -1217,8 +1227,10 @@ static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *
desc_and_codec = spa_aprintf(_("Headset Head Unit (HSP/HFP, codec %s)"), desc_and_codec = spa_aprintf(_("Headset Head Unit (HSP/HFP, codec %s)"),
get_hfp_codec_description(hfp_codec)); get_hfp_codec_description(hfp_codec));
desc = desc_and_codec; desc = desc_and_codec;
priority = 1 + hfp_codec; /* prefer msbc over cvsd */
} else { } else {
desc = _("Headset Head Unit (HSP/HFP)"); desc = _("Headset Head Unit (HSP/HFP)");
priority = 1;
} }
break; break;
} }
@ -1233,7 +1245,7 @@ static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *
SPA_PARAM_PROFILE_name, SPA_POD_String(name), SPA_PARAM_PROFILE_name, SPA_POD_String(name),
SPA_PARAM_PROFILE_description, SPA_POD_String(desc), SPA_PARAM_PROFILE_description, SPA_POD_String(desc),
SPA_PARAM_PROFILE_available, SPA_POD_Id(SPA_PARAM_AVAILABILITY_yes), SPA_PARAM_PROFILE_available, SPA_POD_Id(SPA_PARAM_AVAILABILITY_yes),
SPA_PARAM_PROFILE_priority, SPA_POD_Int(codec), SPA_PARAM_PROFILE_priority, SPA_POD_Int(priority),
0); 0);
if (n_source > 0 || n_sink > 0) { if (n_source > 0 || n_sink > 0) {
spa_pod_builder_prop(b, SPA_PARAM_PROFILE_classes, 0); spa_pod_builder_prop(b, SPA_PARAM_PROFILE_classes, 0);