mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
bluez5: Implement support for ASHA
ASHA is a Bluetooth specification for hearing aids. For a high level overview of the ASHA Specification for BlueZ. https://asymptotic.notion.site/ASHA-Spec-for-Bluez-61a46027fd46458d8235e14d6bedb2ce The original specification in Android. https://source.android.com/docs/core/connect/bluetooth/asha Corresponding support in BlueZ. https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/profiles/audio/asha.c This only implements handling of one side of a ASHA device pair. Stereo support/handling of both "left" & "right" side will follow later requiring work on clock synchronization between the pair.
This commit is contained in:
parent
41d099a580
commit
13c026417b
4 changed files with 213 additions and 12 deletions
|
|
@ -47,6 +47,8 @@ static const struct spa_type_info spa_type_bluetooth_audio_codec[] = {
|
|||
|
||||
{ SPA_BLUETOOTH_AUDIO_CODEC_LC3, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "lc3", NULL },
|
||||
|
||||
{ SPA_BLUETOOTH_AUDIO_CODEC_G722, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "g722", NULL },
|
||||
|
||||
{ 0, 0, NULL, NULL },
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -556,6 +556,8 @@ static enum spa_bt_profile get_codec_profile(const struct media_codec *codec,
|
|||
case SPA_BT_MEDIA_SOURCE:
|
||||
return codec->bap ? SPA_BT_PROFILE_BAP_SOURCE : SPA_BT_PROFILE_A2DP_SOURCE;
|
||||
case SPA_BT_MEDIA_SINK:
|
||||
if (codec->asha)
|
||||
return SPA_BT_PROFILE_ASHA_SINK;
|
||||
return codec->bap ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_A2DP_SINK;
|
||||
case SPA_BT_MEDIA_SOURCE_BROADCAST:
|
||||
return SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
|
||||
|
|
@ -2020,10 +2022,11 @@ int spa_bt_device_check_profiles(struct spa_bt_device *device, bool force)
|
|||
uint32_t connected_profiles = device->connected_profiles;
|
||||
uint32_t connectable_profiles =
|
||||
device->adapter ? adapter_connectable_profiles(device->adapter) : 0;
|
||||
uint32_t direction_masks[3] = {
|
||||
uint32_t direction_masks[4] = {
|
||||
SPA_BT_PROFILE_MEDIA_SINK | SPA_BT_PROFILE_HEADSET_HEAD_UNIT,
|
||||
SPA_BT_PROFILE_MEDIA_SOURCE,
|
||||
SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY,
|
||||
SPA_BT_PROFILE_ASHA_SINK,
|
||||
};
|
||||
bool direction_connected = false;
|
||||
bool set_connected = true;
|
||||
|
|
@ -2034,6 +2037,7 @@ int spa_bt_device_check_profiles(struct spa_bt_device *device, bool force)
|
|||
connected_profiles |= SPA_BT_PROFILE_HEADSET_HEAD_UNIT;
|
||||
if (connected_profiles & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY)
|
||||
connected_profiles |= SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY;
|
||||
connected_profiles |= SPA_BT_PROFILE_ASHA_SINK;
|
||||
|
||||
for (i = 0; i < SPA_N_ELEMENTS(direction_masks); ++i) {
|
||||
uint32_t mask = direction_masks[i] & device->profiles & connectable_profiles;
|
||||
|
|
@ -2645,6 +2649,8 @@ static struct spa_bt_device *create_bcast_device(struct spa_bt_monitor *monitor,
|
|||
return d;
|
||||
}
|
||||
|
||||
static int setup_asha_transport(struct spa_bt_remote_endpoint *remote_endpoint, struct spa_bt_monitor *monitor, const char *transport_path);
|
||||
|
||||
static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_endpoint,
|
||||
DBusMessageIter *props_iter,
|
||||
DBusMessageIter *invalidated_iter)
|
||||
|
|
@ -2698,6 +2704,15 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
|
|||
spa_list_append(&device->remote_endpoint_list, &remote_endpoint->device_link);
|
||||
}
|
||||
}
|
||||
/* For ASHA */
|
||||
else if (spa_streq(key, "Transport")) {
|
||||
if (setup_asha_transport(remote_endpoint, monitor, value)) {
|
||||
spa_log_error(monitor->log, "Failed to create transport for remote_endpoint %p: %s=%s", remote_endpoint, key, value);
|
||||
goto next;
|
||||
}
|
||||
|
||||
spa_log_info(monitor->log, "Created ASHA transport for %s", value);
|
||||
}
|
||||
}
|
||||
else if (type == DBUS_TYPE_BOOLEAN) {
|
||||
int value;
|
||||
|
|
@ -2721,6 +2736,16 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
|
|||
remote_endpoint->codec = value;
|
||||
}
|
||||
}
|
||||
/* Codecs property is present for ASHA */
|
||||
else if (type == DBUS_TYPE_UINT16) {
|
||||
uint16_t value;
|
||||
|
||||
dbus_message_iter_get_basic(&it[1], &value);
|
||||
|
||||
if (spa_streq(key, "Codecs")) {
|
||||
spa_log_debug(monitor->log, "remote_endpoint %p: %s=%02x", remote_endpoint, key, value);
|
||||
}
|
||||
}
|
||||
else if (spa_streq(key, "Capabilities")) {
|
||||
DBusMessageIter iter;
|
||||
uint8_t *value;
|
||||
|
|
@ -2744,6 +2769,23 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
|
|||
remote_endpoint->capabilities_len = len;
|
||||
}
|
||||
}
|
||||
/* HiSyncId property is present for ASHA */
|
||||
else if (spa_streq(key, "HiSyncId")) {
|
||||
/*
|
||||
* TODO: Required for Stereo support in ASHA, for now just log.
|
||||
*/
|
||||
DBusMessageIter iter;
|
||||
uint8_t *value;
|
||||
int len;
|
||||
|
||||
if (!check_iter_signature(&it[1], "ay"))
|
||||
goto next;
|
||||
|
||||
dbus_message_iter_recurse(&it[1], &iter);
|
||||
dbus_message_iter_get_fixed_array(&iter, &value, &len);
|
||||
|
||||
spa_log_debug(monitor->log, "remote_endpoint %p: %s=%d", remote_endpoint, key, len);
|
||||
}
|
||||
else
|
||||
spa_log_debug(monitor->log, "remote_endpoint %p: unhandled key %s", remote_endpoint, key);
|
||||
|
||||
|
|
@ -2761,6 +2803,10 @@ next:
|
|||
profile = spa_bt_profile_from_uuid(remote_endpoint->uuid);
|
||||
if (profile & SPA_BT_PROFILE_BAP_AUDIO)
|
||||
spa_bt_device_add_profile(remote_endpoint->device, profile);
|
||||
if (profile & SPA_BT_PROFILE_ASHA_SINK) {
|
||||
spa_log_debug(monitor->log, "Adding profile for remote_endpoint %p: device -> %p", remote_endpoint, remote_endpoint->device);
|
||||
spa_bt_device_add_profile(remote_endpoint->device, SPA_BT_PROFILE_ASHA_SINK);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -3168,6 +3214,8 @@ static void spa_bt_transport_volume_changed(struct spa_bt_transport *transport)
|
|||
volume_id = SPA_BT_VOLUME_ID_TX;
|
||||
else if (transport->profile & SPA_BT_PROFILE_A2DP_SOURCE)
|
||||
volume_id = SPA_BT_VOLUME_ID_RX;
|
||||
else if (transport->profile & SPA_BT_PROFILE_ASHA_SINK)
|
||||
volume_id = SPA_BT_VOLUME_ID_TX;
|
||||
else
|
||||
return;
|
||||
|
||||
|
|
@ -3409,6 +3457,8 @@ static int transport_update_props(struct spa_bt_transport *transport,
|
|||
t_volume = &transport->volumes[SPA_BT_VOLUME_ID_TX];
|
||||
else if (transport->profile & SPA_BT_PROFILE_A2DP_SOURCE)
|
||||
t_volume = &transport->volumes[SPA_BT_VOLUME_ID_RX];
|
||||
else if (transport->profile & SPA_BT_PROFILE_ASHA_SINK)
|
||||
t_volume = &transport->volumes[SPA_BT_VOLUME_ID_TX];
|
||||
else
|
||||
goto next;
|
||||
|
||||
|
|
@ -4028,6 +4078,61 @@ static const struct spa_bt_transport_implementation transport_impl = {
|
|||
.set_delay = transport_set_delay,
|
||||
};
|
||||
|
||||
static int setup_asha_transport(struct spa_bt_remote_endpoint *remote_endpoint, struct spa_bt_monitor *monitor, const char *transport_path)
|
||||
{
|
||||
const struct media_codec * const * const media_codecs = monitor->media_codecs;
|
||||
const struct media_codec *codec = NULL;
|
||||
struct spa_bt_transport *transport;
|
||||
|
||||
transport = spa_bt_transport_find(monitor, transport_path);
|
||||
if (transport == NULL) {
|
||||
char *tpath = strdup(transport_path);
|
||||
|
||||
transport = spa_bt_transport_create(monitor, tpath, 0);
|
||||
if (transport == NULL) {
|
||||
spa_log_error(monitor->log, "Failed to create transport for %s", transport_path);
|
||||
free(tpath);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spa_bt_transport_set_implementation(transport, &transport_impl, transport);
|
||||
|
||||
spa_log_debug(monitor->log, "Created ASHA transport for %s", transport_path);
|
||||
}
|
||||
|
||||
for (int i = 0; media_codecs[i]; i++) {
|
||||
const struct media_codec *mcodec = media_codecs[i];
|
||||
if (!spa_streq(mcodec->name, "g722"))
|
||||
continue;
|
||||
codec = mcodec;
|
||||
spa_log_debug(monitor->log, "Setting ASHA codec: %s", mcodec->name);
|
||||
}
|
||||
|
||||
free(transport->endpoint_path);
|
||||
transport->endpoint_path = strdup(transport_path);
|
||||
transport->profile = SPA_BT_PROFILE_ASHA_SINK;
|
||||
transport->media_codec = codec;
|
||||
transport->device = remote_endpoint->device;
|
||||
|
||||
spa_list_append(&remote_endpoint->device->transport_list, &transport->device_link);
|
||||
|
||||
spa_bt_device_update_last_bluez_action_time(transport->device);
|
||||
|
||||
transport->volumes[SPA_BT_VOLUME_ID_TX].active = true;
|
||||
transport->volumes[SPA_BT_VOLUME_ID_TX].volume = DEFAULT_TX_VOLUME;
|
||||
transport->n_channels = 1;
|
||||
transport->channels[0] = SPA_AUDIO_CHANNEL_MONO;
|
||||
|
||||
spa_bt_device_add_profile(transport->device, transport->profile);
|
||||
spa_bt_device_connect_profile(transport->device, transport->profile);
|
||||
|
||||
transport_sync_volume(transport);
|
||||
|
||||
spa_log_debug(monitor->log, "ASHA transport setup complete");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void media_codec_switch_reply(DBusPendingCall *pending, void *userdata);
|
||||
|
||||
static int media_codec_switch_cmp(const void *a, const void *b);
|
||||
|
|
@ -5534,6 +5639,8 @@ static void interface_added(struct spa_bt_monitor *monitor,
|
|||
object_path);
|
||||
return;
|
||||
}
|
||||
spa_log_info(monitor->log, "Created Bluetooth device %s",
|
||||
object_path);
|
||||
}
|
||||
|
||||
device_update_props(d, props_iter, NULL);
|
||||
|
|
@ -5546,7 +5653,14 @@ static void interface_added(struct spa_bt_monitor *monitor,
|
|||
|
||||
/* Trigger bluez device creation before bluez profile negotiation started so that
|
||||
* profile connection handlers can receive per-device settings during profile negotiation. */
|
||||
spa_bt_device_add_profile(d, SPA_BT_PROFILE_NULL);
|
||||
if (d->profiles & SPA_BT_PROFILE_ASHA_SINK) {
|
||||
spa_log_info(monitor->log, "Add profile %s: %m",
|
||||
object_path);
|
||||
spa_bt_device_add_profile(d, SPA_BT_PROFILE_ASHA_SINK);
|
||||
spa_bt_device_connect_profile(d, SPA_BT_PROFILE_ASHA_SINK);
|
||||
}
|
||||
else
|
||||
spa_bt_device_add_profile(d, SPA_BT_PROFILE_NULL);
|
||||
}
|
||||
else if (spa_streq(interface_name, BLUEZ_DEVICE_SET_INTERFACE)) {
|
||||
device_set_update_props(monitor, object_path, props_iter, NULL);
|
||||
|
|
@ -6185,7 +6299,7 @@ static int parse_roles(struct spa_bt_monitor *monitor, const struct spa_dict *in
|
|||
{
|
||||
const char *str;
|
||||
int res = 0;
|
||||
int profiles = SPA_BT_PROFILE_MEDIA_SINK | SPA_BT_PROFILE_MEDIA_SOURCE;
|
||||
int profiles = SPA_BT_PROFILE_MEDIA_SINK | SPA_BT_PROFILE_MEDIA_SOURCE | SPA_BT_PROFILE_ASHA_SINK;
|
||||
|
||||
/* HSP/HFP backends parse this property separately */
|
||||
if (info && (str = spa_dict_lookup(info, "bluez5.roles"))) {
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ enum {
|
|||
DEVICE_PROFILE_A2DP = 2,
|
||||
DEVICE_PROFILE_HSP_HFP = 3,
|
||||
DEVICE_PROFILE_BAP = 4,
|
||||
DEVICE_PROFILE_ASHA = 5,
|
||||
DEVICE_PROFILE_LAST,
|
||||
};
|
||||
|
||||
|
|
@ -1108,6 +1109,17 @@ static int emit_nodes(struct impl *this)
|
|||
}
|
||||
}
|
||||
break;
|
||||
case DEVICE_PROFILE_ASHA:
|
||||
if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_ASHA_SINK) {
|
||||
t = find_transport(this, SPA_BT_PROFILE_ASHA_SINK);
|
||||
if (t) {
|
||||
this->props.codec = t->media_codec->id;
|
||||
emit_node(this, t, DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SINK, false);
|
||||
} else {
|
||||
spa_log_warn(this->log, "Unable to find transport for ASHA");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DEVICE_PROFILE_A2DP:
|
||||
if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE) {
|
||||
t = find_transport(this, SPA_BT_PROFILE_A2DP_SOURCE);
|
||||
|
|
@ -1284,6 +1296,7 @@ static int set_profile(struct impl *this, uint32_t profile, enum spa_bluetooth_a
|
|||
this->save_profile = save;
|
||||
|
||||
if (this->profile == profile &&
|
||||
(this->profile != DEVICE_PROFILE_ASHA || codec == this->props.codec) &&
|
||||
(this->profile != DEVICE_PROFILE_A2DP || codec == this->props.codec) &&
|
||||
(this->profile != DEVICE_PROFILE_BAP || codec == this->props.codec) &&
|
||||
(this->profile != DEVICE_PROFILE_HSP_HFP || codec == this->props.codec))
|
||||
|
|
@ -1418,6 +1431,11 @@ static void profiles_changed(void *userdata, uint32_t connected_change)
|
|||
spa_log_debug(this->log, "profiles changed: AG nodes changed: %d",
|
||||
nodes_changed);
|
||||
break;
|
||||
case DEVICE_PROFILE_ASHA:
|
||||
nodes_changed = (connected_change & SPA_BT_PROFILE_ASHA_SINK);
|
||||
spa_log_debug(this->log, "profiles changed: ASHA nodes changed: %d",
|
||||
nodes_changed);
|
||||
break;
|
||||
case DEVICE_PROFILE_A2DP:
|
||||
nodes_changed = (connected_change & SPA_BT_PROFILE_A2DP_DUPLEX);
|
||||
spa_log_debug(this->log, "profiles changed: A2DP nodes changed: %d",
|
||||
|
|
@ -1586,6 +1604,10 @@ static uint32_t profile_direction_mask(struct impl *this, uint32_t index, enum s
|
|||
if (device->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT)
|
||||
have_output = have_input = true;
|
||||
break;
|
||||
case DEVICE_PROFILE_ASHA:
|
||||
if (device->connected_profiles & SPA_BT_PROFILE_ASHA_SINK)
|
||||
have_input = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1609,6 +1631,10 @@ static uint32_t get_profile_from_index(struct impl *this, uint32_t index, uint32
|
|||
*codec = 0;
|
||||
*next = (profile + 1) << 16;
|
||||
return profile;
|
||||
case DEVICE_PROFILE_ASHA:
|
||||
*codec = SPA_BLUETOOTH_AUDIO_CODEC_G722;
|
||||
*next = (profile + 1) << 16;
|
||||
return profile;
|
||||
case DEVICE_PROFILE_A2DP:
|
||||
case DEVICE_PROFILE_HSP_HFP:
|
||||
case DEVICE_PROFILE_BAP:
|
||||
|
|
@ -1636,6 +1662,9 @@ static uint32_t get_index_from_profile(struct impl *this, uint32_t profile, enum
|
|||
case DEVICE_PROFILE_AG:
|
||||
return (profile << 16);
|
||||
|
||||
case DEVICE_PROFILE_ASHA:
|
||||
return (profile << 16) | (SPA_BLUETOOTH_AUDIO_CODEC_G722 & 0xffff);
|
||||
|
||||
case DEVICE_PROFILE_A2DP:
|
||||
case DEVICE_PROFILE_BAP:
|
||||
case DEVICE_PROFILE_HSP_HFP:
|
||||
|
|
@ -1647,6 +1676,25 @@ static uint32_t get_index_from_profile(struct impl *this, uint32_t profile, enum
|
|||
return SPA_ID_INVALID;
|
||||
}
|
||||
|
||||
static bool set_initial_asha_profile(struct impl *this)
|
||||
{
|
||||
struct spa_bt_transport *t;
|
||||
if (!(this->bt_dev->connected_profiles & SPA_BT_PROFILE_ASHA_SINK))
|
||||
return false;
|
||||
|
||||
t = find_transport(this, SPA_BT_PROFILE_ASHA_SINK);
|
||||
if (t) {
|
||||
this->profile = DEVICE_PROFILE_ASHA;
|
||||
this->props.codec = SPA_BLUETOOTH_AUDIO_CODEC_G722;
|
||||
|
||||
spa_log_debug(this->log, "initial ASHA profile:%d codec:%d",
|
||||
this->profile, this->props.codec);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool set_initial_hsp_hfp_profile(struct impl *this)
|
||||
{
|
||||
struct spa_bt_transport *t;
|
||||
|
|
@ -1688,13 +1736,16 @@ static void set_initial_profile(struct impl *this)
|
|||
/* If default profile is set to HSP/HFP, first try those and exit if found. */
|
||||
if (this->bt_dev->settings != NULL) {
|
||||
const char *str = spa_dict_lookup(this->bt_dev->settings, "bluez5.profile");
|
||||
|
||||
if (spa_streq(str, "asha-sink") && set_initial_asha_profile(this))
|
||||
return;
|
||||
if (spa_streq(str, "off"))
|
||||
goto off;
|
||||
if (spa_streq(str, "headset-head-unit") && set_initial_hsp_hfp_profile(this))
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = SPA_BT_PROFILE_BAP_SINK; i <= SPA_BT_PROFILE_A2DP_SOURCE; i <<= 1) {
|
||||
for (i = SPA_BT_PROFILE_BAP_SINK; i <= SPA_BT_PROFILE_ASHA_SINK; i <<= 1) {
|
||||
if (!(this->bt_dev->connected_profiles & i))
|
||||
continue;
|
||||
|
||||
|
|
@ -1704,6 +1755,8 @@ static void set_initial_profile(struct impl *this)
|
|||
this->profile = DEVICE_PROFILE_AG;
|
||||
else if (i == SPA_BT_PROFILE_BAP_SINK)
|
||||
this->profile = DEVICE_PROFILE_BAP;
|
||||
else if (i == SPA_BT_PROFILE_ASHA_SINK)
|
||||
this->profile = DEVICE_PROFILE_ASHA;
|
||||
else
|
||||
this->profile = DEVICE_PROFILE_A2DP;
|
||||
this->props.codec = t->media_codec->id;
|
||||
|
|
@ -1765,6 +1818,26 @@ static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *
|
|||
priority = 256;
|
||||
break;
|
||||
}
|
||||
case DEVICE_PROFILE_ASHA:
|
||||
{
|
||||
uint32_t profile = device->connected_profiles & SPA_BT_PROFILE_ASHA_SINK;
|
||||
|
||||
if (codec == 0)
|
||||
return NULL;
|
||||
if (profile == 0)
|
||||
return NULL;
|
||||
if (!(profile & SPA_BT_PROFILE_ASHA_SINK)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
name = spa_bt_profile_name(profile);
|
||||
desc = _("Audio Streaming for Hearing Aids (ASHA Sink)");
|
||||
|
||||
n_sink++;
|
||||
priority = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
case DEVICE_PROFILE_A2DP:
|
||||
{
|
||||
/* make this device profile visible only if there is an A2DP sink */
|
||||
|
|
@ -2022,6 +2095,12 @@ static bool profile_has_route(uint32_t profile, uint32_t route)
|
|||
return true;
|
||||
}
|
||||
break;
|
||||
case DEVICE_PROFILE_ASHA:
|
||||
switch (route) {
|
||||
case ROUTE_OUTPUT:
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2328,7 +2407,7 @@ static struct spa_pod *build_prop_info_codec(struct impl *this, struct spa_pod_b
|
|||
spa_pod_builder_pop(b, &f[1]);
|
||||
spa_pod_builder_prop(b, SPA_PROP_INFO_labels, 0);
|
||||
spa_pod_builder_push_struct(b, &f[1]);
|
||||
if (this->profile == DEVICE_PROFILE_A2DP || this->profile == DEVICE_PROFILE_BAP) {
|
||||
if (this->profile == DEVICE_PROFILE_A2DP || this->profile == DEVICE_PROFILE_BAP || this->profile == DEVICE_PROFILE_ASHA) {
|
||||
FOR_EACH_MEDIA_CODEC(j, codec) {
|
||||
spa_pod_builder_int(b, codec->id);
|
||||
spa_pod_builder_string(b, codec->description);
|
||||
|
|
@ -2747,7 +2826,7 @@ static int impl_set_param(void *object,
|
|||
if (codec_id == SPA_ID_INVALID)
|
||||
return 0;
|
||||
|
||||
if (this->profile == DEVICE_PROFILE_A2DP || this->profile == DEVICE_PROFILE_BAP) {
|
||||
if (this->profile == DEVICE_PROFILE_A2DP || this->profile == DEVICE_PROFILE_BAP || this->profile == DEVICE_PROFILE_ASHA) {
|
||||
size_t j;
|
||||
for (j = 0; j < this->supported_codec_count; ++j) {
|
||||
if (this->supported_codecs[j]->id == codec_id) {
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ extern "C" {
|
|||
#define SPA_BT_UUID_BAP_SOURCE "00002bcb-0000-1000-8000-00805f9b34fb"
|
||||
#define SPA_BT_UUID_BAP_BROADCAST_SOURCE "00001852-0000-1000-8000-00805f9b34fb"
|
||||
#define SPA_BT_UUID_BAP_BROADCAST_SINK "00001851-0000-1000-8000-00805f9b34fb"
|
||||
#define SPA_BT_UUID_ASHA_SINK "0000FDF0-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define PROFILE_HSP_AG "/Profile/HSPAG"
|
||||
#define PROFILE_HSP_HS "/Profile/HSPHS"
|
||||
|
|
@ -181,12 +182,13 @@ enum spa_bt_profile {
|
|||
SPA_BT_PROFILE_BAP_SOURCE = (1 << 1),
|
||||
SPA_BT_PROFILE_A2DP_SINK = (1 << 2),
|
||||
SPA_BT_PROFILE_A2DP_SOURCE = (1 << 3),
|
||||
SPA_BT_PROFILE_HSP_HS = (1 << 4),
|
||||
SPA_BT_PROFILE_HSP_AG = (1 << 5),
|
||||
SPA_BT_PROFILE_HFP_HF = (1 << 6),
|
||||
SPA_BT_PROFILE_HFP_AG = (1 << 7),
|
||||
SPA_BT_PROFILE_BAP_BROADCAST_SOURCE = (1 << 8),
|
||||
SPA_BT_PROFILE_BAP_BROADCAST_SINK = (1 << 9),
|
||||
SPA_BT_PROFILE_ASHA_SINK = (1 << 4),
|
||||
SPA_BT_PROFILE_HSP_HS = (1 << 5),
|
||||
SPA_BT_PROFILE_HSP_AG = (1 << 6),
|
||||
SPA_BT_PROFILE_HFP_HF = (1 << 7),
|
||||
SPA_BT_PROFILE_HFP_AG = (1 << 8),
|
||||
SPA_BT_PROFILE_BAP_BROADCAST_SOURCE = (1 << 9),
|
||||
SPA_BT_PROFILE_BAP_BROADCAST_SINK = (1 << 10),
|
||||
|
||||
SPA_BT_PROFILE_A2DP_DUPLEX = (SPA_BT_PROFILE_A2DP_SINK | SPA_BT_PROFILE_A2DP_SOURCE),
|
||||
SPA_BT_PROFILE_BAP_DUPLEX = (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE),
|
||||
|
|
@ -226,6 +228,8 @@ static inline enum spa_bt_profile spa_bt_profile_from_uuid(const char *uuid)
|
|||
return SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
|
||||
else if (strcasecmp(uuid, SPA_BT_UUID_BAP_BROADCAST_SINK) == 0)
|
||||
return SPA_BT_PROFILE_BAP_BROADCAST_SINK;
|
||||
else if (strcasecmp(uuid, SPA_BT_UUID_ASHA_SINK) == 0)
|
||||
return SPA_BT_PROFILE_ASHA_SINK;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -316,6 +320,8 @@ enum spa_bt_hfp_sdp_hf_features {
|
|||
|
||||
static inline const char *spa_bt_profile_name (enum spa_bt_profile profile) {
|
||||
switch (profile) {
|
||||
case SPA_BT_PROFILE_ASHA_SINK:
|
||||
return "asha-sink";
|
||||
case SPA_BT_PROFILE_A2DP_SOURCE:
|
||||
return "a2dp-source";
|
||||
case SPA_BT_PROFILE_A2DP_SINK:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue