bluez5: fix device supported codec checks

Make supported codec checks to use profiles, not "is-a-sink" flag, to
determine which codecs can be used.

Fixes bluez5-device checking only source profiles, even when the local
device is only a sink.
This commit is contained in:
Pauli Virtanen 2023-09-02 15:06:27 +03:00 committed by P V
parent 6abc6e6693
commit 186b730c9c
3 changed files with 59 additions and 66 deletions

View file

@ -526,6 +526,26 @@ static enum spa_bt_profile get_codec_profile(const struct media_codec *codec,
}
}
static enum spa_bt_profile swap_profile(enum spa_bt_profile profile)
{
switch (profile) {
case SPA_BT_PROFILE_A2DP_SOURCE:
return SPA_BT_PROFILE_A2DP_SINK;
case SPA_BT_PROFILE_A2DP_SINK:
return SPA_BT_PROFILE_A2DP_SOURCE;
case SPA_BT_PROFILE_BAP_SOURCE:
return SPA_BT_PROFILE_BAP_SINK;
case SPA_BT_PROFILE_BAP_SINK:
return SPA_BT_PROFILE_BAP_SOURCE;
case SPA_BT_PROFILE_BAP_BROADCAST_SOURCE:
return SPA_BT_PROFILE_BAP_BROADCAST_SINK;
case SPA_BT_PROFILE_BAP_BROADCAST_SINK:
return SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
default:
return SPA_BT_PROFILE_NULL;
}
}
static bool endpoint_should_be_registered(struct spa_bt_monitor *monitor,
const struct media_codec *codec,
enum spa_bt_media_direction direction)
@ -2230,11 +2250,11 @@ static bool device_props_ready(struct spa_bt_device *device)
return device->adapter && device->address;
}
bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, bool sink)
bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, enum spa_bt_profile profile)
{
struct spa_bt_monitor *monitor = device->monitor;
struct spa_bt_remote_endpoint *ep;
enum spa_bt_profile codec_profile;
enum spa_bt_profile codec_target_profile;
struct spa_bt_transport *t;
const struct { enum spa_bluetooth_audio_codec codec; uint32_t mask; } quirks[] = {
{ SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, SPA_BT_FEATURE_SBC_XQ },
@ -2270,17 +2290,14 @@ bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const stru
return false;
}
spa_list_for_each(ep, &device->remote_endpoint_list, device_link) {
enum spa_bt_profile profile = spa_bt_profile_from_uuid(ep->uuid);
if (codec->bap) {
if ((profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK) || (profile == SPA_BT_PROFILE_BAP_BROADCAST_SOURCE))
codec_profile = sink ? SPA_BT_PROFILE_BAP_BROADCAST_SINK : SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
else
codec_profile = sink ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_BAP_SOURCE;
} else
codec_profile = sink ? SPA_BT_PROFILE_A2DP_SINK : SPA_BT_PROFILE_A2DP_SOURCE;
for (i = 0, codec_target_profile = 0; i < (size_t)SPA_BT_MEDIA_DIRECTION_LAST; ++i)
if (codec_has_direction(codec, i))
codec_target_profile |= swap_profile(get_codec_profile(codec, i));
if (profile != codec_profile)
spa_list_for_each(ep, &device->remote_endpoint_list, device_link) {
enum spa_bt_profile ep_profile = spa_bt_profile_from_uuid(ep->uuid);
if (!(ep_profile & codec_target_profile & profile))
continue;
if (media_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len,
@ -2296,15 +2313,7 @@ bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const stru
* can only know that the currently configured codec is supported.
*/
spa_list_for_each(t, &device->transport_list, device_link) {
if (codec->bap) {
if((t->profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK) || (t->profile == SPA_BT_PROFILE_BAP_BROADCAST_SOURCE))
codec_profile = sink ? SPA_BT_PROFILE_BAP_BROADCAST_SINK : SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
else
codec_profile = sink ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_BAP_SOURCE;
} else
codec_profile = sink ? SPA_BT_PROFILE_A2DP_SINK : SPA_BT_PROFILE_A2DP_SOURCE;
if (t->profile != codec_profile)
if (!(t->profile & codec_target_profile & profile))
continue;
if (codec == t->media_codec)
@ -2314,7 +2323,7 @@ bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const stru
return false;
}
const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count, bool sink)
const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count)
{
struct spa_bt_monitor *monitor = device->monitor;
const struct media_codec * const * const media_codecs = monitor->media_codecs;
@ -2329,7 +2338,7 @@ const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_b
j = 0;
for (i = 0; media_codecs[i] != NULL; ++i) {
if (spa_bt_device_supports_media_codec(device, media_codecs[i], sink)) {
if (spa_bt_device_supports_media_codec(device, media_codecs[i], device->connected_profiles)) {
supported_codecs[j] = media_codecs[i];
++j;
}
@ -3048,29 +3057,9 @@ static int transport_update_props(struct spa_bt_transport *transport,
spa_log_debug(monitor->log, "transport %p: %s=%s", transport, key, value);
if (spa_streq(key, "UUID")) {
switch (spa_bt_profile_from_uuid(value)) {
case SPA_BT_PROFILE_A2DP_SOURCE:
transport->profile = SPA_BT_PROFILE_A2DP_SINK;
break;
case SPA_BT_PROFILE_A2DP_SINK:
transport->profile = SPA_BT_PROFILE_A2DP_SOURCE;
break;
case SPA_BT_PROFILE_BAP_SOURCE:
transport->profile = SPA_BT_PROFILE_BAP_SINK;
break;
case SPA_BT_PROFILE_BAP_SINK:
transport->profile = SPA_BT_PROFILE_BAP_SOURCE;
break;
case SPA_BT_PROFILE_BAP_BROADCAST_SOURCE:
transport->profile = SPA_BT_PROFILE_BAP_BROADCAST_SINK;
break;
case SPA_BT_PROFILE_BAP_BROADCAST_SINK:
transport->profile = SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
break;
default:
transport->profile = swap_profile(spa_bt_profile_from_uuid(value));
if (transport->profile == SPA_BT_PROFILE_NULL)
spa_log_warn(monitor->log, "unknown profile %s", value);
break;
}
}
else if (spa_streq(key, "State")) {
enum spa_bt_transport_state state = spa_bt_transport_state_from_string(value);
@ -4154,7 +4143,7 @@ int spa_bt_device_ensure_media_codec(struct spa_bt_device *device, const struct
}
for (i = 0; codecs[i] != NULL; ++i) {
if (spa_bt_device_supports_media_codec(device, codecs[i], true)) {
if (spa_bt_device_supports_media_codec(device, codecs[i], device->connected_profiles)) {
preferred_codec = codecs[i];
break;
}

View file

@ -190,10 +190,12 @@ static void get_media_codecs(struct impl *this, enum spa_bluetooth_audio_codec i
*codecs = NULL;
}
static const struct media_codec *get_supported_media_codec(struct impl *this, enum spa_bluetooth_audio_codec id, size_t *idx)
static const struct media_codec *get_supported_media_codec(struct impl *this, enum spa_bluetooth_audio_codec id,
size_t *idx, enum spa_bt_profile profile)
{
const struct media_codec *media_codec = NULL;
size_t i;
for (i = 0; i < this->supported_codec_count; ++i) {
if (this->supported_codecs[i]->id == id) {
media_codec = this->supported_codecs[i];
@ -201,6 +203,13 @@ static const struct media_codec *get_supported_media_codec(struct impl *this, en
*idx = i;
}
}
if (!media_codec)
return NULL;
if (!spa_bt_device_supports_media_codec(this->bt_dev, media_codec, profile))
return NULL;
return media_codec;
}
@ -625,6 +634,8 @@ static void emit_node(struct impl *this, struct spa_bt_transport *t,
char transport[32], str_id[32], object_path[512];
bool is_dyn_node = SPA_FLAG_IS_SET(id, DYNAMIC_NODE_ID_FLAG);
spa_log_debug(this->log, "node, transport:%p id:%08x factory:%s", t, id, factory_name);
snprintf(transport, sizeof(transport), "pointer:%p", t);
items[0] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_TRANSPORT, transport);
items[1] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_PROFILE, spa_bt_profile_name(t->profile));
@ -1016,9 +1027,6 @@ static int emit_nodes(struct impl *this)
}
}
}
if (get_supported_media_codec(this, this->props.codec, NULL) == NULL)
this->props.codec = 0;
break;
case DEVICE_PROFILE_BAP:
if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_SOURCE)) {
@ -1070,9 +1078,6 @@ static int emit_nodes(struct impl *this)
DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false);
}
}
if (get_supported_media_codec(this, this->props.codec, NULL) == NULL)
this->props.codec = 0;
break;
case DEVICE_PROFILE_HSP_HFP:
if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT) {
@ -1119,6 +1124,8 @@ static void emit_info(struct impl *this, bool full)
static void emit_remove_nodes(struct impl *this)
{
spa_log_debug(this->log, "remove nodes");
remove_dynamic_node (&this->dyn_media_source);
remove_dynamic_node (&this->dyn_media_sink);
remove_dynamic_node (&this->dyn_sco_source);
@ -1278,11 +1285,9 @@ static void profiles_changed(void *userdata, uint32_t prev_profiles, uint32_t pr
if (this->switching_codec)
return;
if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_MEDIA_SINK) {
free(this->supported_codecs);
this->supported_codecs = spa_bt_device_get_supported_media_codecs(
this->bt_dev, &this->supported_codec_count, true);
}
free(this->supported_codecs);
this->supported_codecs = spa_bt_device_get_supported_media_codecs(
this->bt_dev, &this->supported_codec_count);
switch (this->profile) {
case DEVICE_PROFILE_OFF:
@ -1297,8 +1302,6 @@ static void profiles_changed(void *userdata, uint32_t prev_profiles, uint32_t pr
break;
case DEVICE_PROFILE_A2DP:
case DEVICE_PROFILE_BAP:
if (get_supported_media_codec(this, this->props.codec, NULL) == NULL)
this->props.codec = 0;
nodes_changed = (connected_change & (SPA_BT_PROFILE_MEDIA_SINK |
SPA_BT_PROFILE_MEDIA_SOURCE));
spa_log_debug(this->log, "profiles changed: media nodes changed: %d",
@ -1419,7 +1422,7 @@ static uint32_t profile_direction_mask(struct impl *this, uint32_t index, enum s
if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK)
have_output = true;
media_codec = get_supported_media_codec(this, codec, NULL);
media_codec = get_supported_media_codec(this, codec, NULL, device->connected_profiles);
if (media_codec && media_codec->duplex_codec)
have_input = true;
break;
@ -1533,7 +1536,7 @@ static void set_initial_profile(struct impl *this)
if (this->supported_codecs)
free(this->supported_codecs);
this->supported_codecs = spa_bt_device_get_supported_media_codecs(
this->bt_dev, &this->supported_codec_count, true);
this->bt_dev, &this->supported_codec_count);
/* Prefer BAP, then A2DP, then HFP, then null, but select AG if the device
appears not to have BAP_SINK, A2DP_SINK or any HEAD_UNIT profile */
@ -1625,7 +1628,7 @@ static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *
n_sink++;
if (codec) {
size_t idx;
const struct media_codec *media_codec = get_supported_media_codec(this, codec, &idx);
const struct media_codec *media_codec = get_supported_media_codec(this, codec, &idx, profile);
if (media_codec == NULL) {
errno = EINVAL;
return NULL;
@ -1686,7 +1689,7 @@ static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *
name = spa_bt_profile_name(profile);
if (codec) {
media_codec = get_supported_media_codec(this, codec, &idx);
media_codec = get_supported_media_codec(this, codec, &idx, profile);
if (media_codec == NULL) {
errno = EINVAL;
return NULL;

View file

@ -169,6 +169,7 @@ enum spa_bt_media_direction {
SPA_BT_MEDIA_SINK,
SPA_BT_MEDIA_SOURCE_BROADCAST,
SPA_BT_MEDIA_SINK_BROADCAST,
SPA_BT_MEDIA_DIRECTION_LAST,
};
enum spa_bt_profile {
@ -538,8 +539,8 @@ int spa_bt_device_add_profile(struct spa_bt_device *device, enum spa_bt_profile
int spa_bt_device_connect_profile(struct spa_bt_device *device, enum spa_bt_profile profile);
int spa_bt_device_check_profiles(struct spa_bt_device *device, bool force);
int spa_bt_device_ensure_media_codec(struct spa_bt_device *device, const struct media_codec * const *codecs);
bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, bool sink);
const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count, bool sink);
bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, enum spa_bt_profile profile);
const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count);
int spa_bt_device_ensure_hfp_codec(struct spa_bt_device *device, unsigned int codec);
int spa_bt_device_supports_hfp_codec(struct spa_bt_device *device, unsigned int codec);
int spa_bt_device_release_transports(struct spa_bt_device *device);