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, static bool endpoint_should_be_registered(struct spa_bt_monitor *monitor,
const struct media_codec *codec, const struct media_codec *codec,
enum spa_bt_media_direction direction) 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; 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_monitor *monitor = device->monitor;
struct spa_bt_remote_endpoint *ep; struct spa_bt_remote_endpoint *ep;
enum spa_bt_profile codec_profile; enum spa_bt_profile codec_target_profile;
struct spa_bt_transport *t; struct spa_bt_transport *t;
const struct { enum spa_bluetooth_audio_codec codec; uint32_t mask; } quirks[] = { const struct { enum spa_bluetooth_audio_codec codec; uint32_t mask; } quirks[] = {
{ SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, SPA_BT_FEATURE_SBC_XQ }, { 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; return false;
} }
spa_list_for_each(ep, &device->remote_endpoint_list, device_link) { for (i = 0, codec_target_profile = 0; i < (size_t)SPA_BT_MEDIA_DIRECTION_LAST; ++i)
enum spa_bt_profile profile = spa_bt_profile_from_uuid(ep->uuid); if (codec_has_direction(codec, i))
if (codec->bap) { codec_target_profile |= swap_profile(get_codec_profile(codec, i));
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;
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; continue;
if (media_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len, 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. * can only know that the currently configured codec is supported.
*/ */
spa_list_for_each(t, &device->transport_list, device_link) { spa_list_for_each(t, &device->transport_list, device_link) {
if (codec->bap) { if (!(t->profile & codec_target_profile & profile))
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)
continue; continue;
if (codec == t->media_codec) 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; 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; struct spa_bt_monitor *monitor = device->monitor;
const struct media_codec * const * const media_codecs = monitor->media_codecs; 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; j = 0;
for (i = 0; media_codecs[i] != NULL; ++i) { 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]; supported_codecs[j] = media_codecs[i];
++j; ++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); spa_log_debug(monitor->log, "transport %p: %s=%s", transport, key, value);
if (spa_streq(key, "UUID")) { if (spa_streq(key, "UUID")) {
switch (spa_bt_profile_from_uuid(value)) { transport->profile = swap_profile(spa_bt_profile_from_uuid(value));
case SPA_BT_PROFILE_A2DP_SOURCE: if (transport->profile == SPA_BT_PROFILE_NULL)
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:
spa_log_warn(monitor->log, "unknown profile %s", value); spa_log_warn(monitor->log, "unknown profile %s", value);
break;
}
} }
else if (spa_streq(key, "State")) { else if (spa_streq(key, "State")) {
enum spa_bt_transport_state state = spa_bt_transport_state_from_string(value); 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) { 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]; preferred_codec = codecs[i];
break; break;
} }

View file

@ -190,10 +190,12 @@ static void get_media_codecs(struct impl *this, enum spa_bluetooth_audio_codec i
*codecs = NULL; *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; const struct media_codec *media_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) {
media_codec = this->supported_codecs[i]; 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; *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; 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]; char transport[32], str_id[32], object_path[512];
bool is_dyn_node = SPA_FLAG_IS_SET(id, DYNAMIC_NODE_ID_FLAG); 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); snprintf(transport, sizeof(transport), "pointer:%p", t);
items[0] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_TRANSPORT, transport); 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)); 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; break;
case DEVICE_PROFILE_BAP: case DEVICE_PROFILE_BAP:
if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_SOURCE)) { 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); 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; break;
case DEVICE_PROFILE_HSP_HFP: case DEVICE_PROFILE_HSP_HFP:
if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT) { 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) 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_source);
remove_dynamic_node (&this->dyn_media_sink); remove_dynamic_node (&this->dyn_media_sink);
remove_dynamic_node (&this->dyn_sco_source); 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) if (this->switching_codec)
return; return;
if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_MEDIA_SINK) { free(this->supported_codecs);
free(this->supported_codecs); this->supported_codecs = spa_bt_device_get_supported_media_codecs(
this->supported_codecs = spa_bt_device_get_supported_media_codecs( this->bt_dev, &this->supported_codec_count);
this->bt_dev, &this->supported_codec_count, true);
}
switch (this->profile) { switch (this->profile) {
case DEVICE_PROFILE_OFF: case DEVICE_PROFILE_OFF:
@ -1297,8 +1302,6 @@ static void profiles_changed(void *userdata, uint32_t prev_profiles, uint32_t pr
break; break;
case DEVICE_PROFILE_A2DP: case DEVICE_PROFILE_A2DP:
case DEVICE_PROFILE_BAP: 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 | nodes_changed = (connected_change & (SPA_BT_PROFILE_MEDIA_SINK |
SPA_BT_PROFILE_MEDIA_SOURCE)); SPA_BT_PROFILE_MEDIA_SOURCE));
spa_log_debug(this->log, "profiles changed: media nodes changed: %d", 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) if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK)
have_output = true; 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) if (media_codec && media_codec->duplex_codec)
have_input = true; have_input = true;
break; break;
@ -1533,7 +1536,7 @@ static void set_initial_profile(struct impl *this)
if (this->supported_codecs) if (this->supported_codecs)
free(this->supported_codecs); free(this->supported_codecs);
this->supported_codecs = spa_bt_device_get_supported_media_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 /* 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 */ 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++; n_sink++;
if (codec) { if (codec) {
size_t idx; 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) { if (media_codec == NULL) {
errno = EINVAL; errno = EINVAL;
return NULL; 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); name = spa_bt_profile_name(profile);
if (codec) { 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) { if (media_codec == NULL) {
errno = EINVAL; errno = EINVAL;
return NULL; return NULL;

View file

@ -169,6 +169,7 @@ enum spa_bt_media_direction {
SPA_BT_MEDIA_SINK, SPA_BT_MEDIA_SINK,
SPA_BT_MEDIA_SOURCE_BROADCAST, SPA_BT_MEDIA_SOURCE_BROADCAST,
SPA_BT_MEDIA_SINK_BROADCAST, SPA_BT_MEDIA_SINK_BROADCAST,
SPA_BT_MEDIA_DIRECTION_LAST,
}; };
enum spa_bt_profile { 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_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_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); 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); 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, bool sink); 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_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_supports_hfp_codec(struct spa_bt_device *device, unsigned int codec);
int spa_bt_device_release_transports(struct spa_bt_device *device); int spa_bt_device_release_transports(struct spa_bt_device *device);