mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
bluez5: implement BAP ucast reconfiguration
Use SelectProperties() DBus API to reconfigure BAP unicast setup. Add support to spa_bt_ensure_codec() to select whether to configure as sink/source/duplex.
This commit is contained in:
parent
592a97a7b0
commit
8795298f69
3 changed files with 223 additions and 56 deletions
|
|
@ -208,6 +208,11 @@ struct spa_bt_big {
|
||||||
#define SPA_TYPE_BT_WORK_CODEC_SWITCH SPA_TYPE_INFO_BT_WORK_BASE "CodecSwitch"
|
#define SPA_TYPE_BT_WORK_CODEC_SWITCH SPA_TYPE_INFO_BT_WORK_BASE "CodecSwitch"
|
||||||
#define SPA_TYPE_BT_WORK_RATE_LIMIT SPA_TYPE_INFO_BT_WORK_BASE "RateLimit"
|
#define SPA_TYPE_BT_WORK_RATE_LIMIT SPA_TYPE_INFO_BT_WORK_BASE "RateLimit"
|
||||||
|
|
||||||
|
struct spa_bt_codec_switch_path {
|
||||||
|
char *path;
|
||||||
|
bool clear;
|
||||||
|
};
|
||||||
|
|
||||||
struct spa_bt_codec_switch {
|
struct spa_bt_codec_switch {
|
||||||
struct spa_list link;
|
struct spa_list link;
|
||||||
|
|
||||||
|
|
@ -215,6 +220,8 @@ struct spa_bt_codec_switch {
|
||||||
bool failed;
|
bool failed;
|
||||||
bool waiting;
|
bool waiting;
|
||||||
|
|
||||||
|
uint32_t profiles;
|
||||||
|
|
||||||
struct spa_bt_device *device;
|
struct spa_bt_device *device;
|
||||||
|
|
||||||
struct spa_source *timer;
|
struct spa_source *timer;
|
||||||
|
|
@ -225,8 +232,8 @@ struct spa_bt_codec_switch {
|
||||||
* invalidated in the meantime).
|
* invalidated in the meantime).
|
||||||
*/
|
*/
|
||||||
const struct media_codec *codec;
|
const struct media_codec *codec;
|
||||||
char **paths;
|
struct spa_bt_codec_switch_path *paths;
|
||||||
char **path_iter;
|
unsigned int path_idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spa_bt_codec_switch *codec_switch_cmp_sw; /* global for qsort */
|
static struct spa_bt_codec_switch *codec_switch_cmp_sw; /* global for qsort */
|
||||||
|
|
@ -1060,7 +1067,10 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
||||||
if (ep->qos.channel_allocation)
|
if (ep->qos.channel_allocation)
|
||||||
spa_scnprintf(channel_allocation, sizeof(channel_allocation), "%"PRIu32, ep->qos.channel_allocation);
|
spa_scnprintf(channel_allocation, sizeof(channel_allocation), "%"PRIu32, ep->qos.channel_allocation);
|
||||||
|
|
||||||
duplex = SPA_FLAG_IS_SET(ep->device->profiles, SPA_BT_PROFILE_BAP_DUPLEX);
|
if (!ep->device->preferred_profiles)
|
||||||
|
ep->device->preferred_profiles = ep->device->profiles;
|
||||||
|
|
||||||
|
duplex = SPA_FLAG_IS_SET(ep->device->preferred_profiles, SPA_BT_PROFILE_BAP_DUPLEX);
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
setting_items[i++] = SPA_DICT_ITEM_INIT("bluez5.bap.locations", locations);
|
setting_items[i++] = SPA_DICT_ITEM_INIT("bluez5.bap.locations", locations);
|
||||||
|
|
@ -1647,6 +1657,7 @@ static void device_clear_sub(struct spa_bt_device *device)
|
||||||
battery_remove(device);
|
battery_remove(device);
|
||||||
spa_bt_device_release_transports(device);
|
spa_bt_device_release_transports(device);
|
||||||
device->preferred_codec = NULL;
|
device->preferred_codec = NULL;
|
||||||
|
device->preferred_profiles = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void device_free(struct spa_bt_device *device)
|
static void device_free(struct spa_bt_device *device)
|
||||||
|
|
@ -2163,7 +2174,9 @@ int spa_bt_device_check_profiles(struct spa_bt_device *device, bool force)
|
||||||
device, device->profiles, connected_profiles, connectable_profiles,
|
device, device->profiles, connected_profiles, connectable_profiles,
|
||||||
device->added, all_connected, direction_connected, set_connected);
|
device->added, all_connected, direction_connected, set_connected);
|
||||||
|
|
||||||
if (connected_profiles == 0 && !has_codec_switch(device)) {
|
if (has_codec_switch(device)) {
|
||||||
|
/* noop */
|
||||||
|
} else if (connected_profiles == 0) {
|
||||||
device_stop_timer(device);
|
device_stop_timer(device);
|
||||||
device_connected(monitor, device, BT_DEVICE_DISCONNECTED);
|
device_connected(monitor, device, BT_DEVICE_DISCONNECTED);
|
||||||
} else if (force || ((direction_connected || all_connected) && set_connected && connected_profiles)) {
|
} else if (force || ((direction_connected || all_connected) && set_connected && connected_profiles)) {
|
||||||
|
|
@ -3146,6 +3159,7 @@ void spa_bt_transport_free(struct spa_bt_transport *transport)
|
||||||
|
|
||||||
free(transport->configuration);
|
free(transport->configuration);
|
||||||
free(transport->endpoint_path);
|
free(transport->endpoint_path);
|
||||||
|
free(transport->remote_endpoint_path);
|
||||||
free(transport->path);
|
free(transport->path);
|
||||||
free(transport);
|
free(transport);
|
||||||
}
|
}
|
||||||
|
|
@ -3543,6 +3557,10 @@ static int transport_update_props(struct spa_bt_transport *transport,
|
||||||
}
|
}
|
||||||
else if (spa_streq(key, "Endpoint")) {
|
else if (spa_streq(key, "Endpoint")) {
|
||||||
struct spa_bt_remote_endpoint *ep = remote_endpoint_find(monitor, value);
|
struct spa_bt_remote_endpoint *ep = remote_endpoint_find(monitor, value);
|
||||||
|
|
||||||
|
free(transport->remote_endpoint_path);
|
||||||
|
transport->remote_endpoint_path = strdup(value);
|
||||||
|
|
||||||
if (!ep) {
|
if (!ep) {
|
||||||
spa_log_warn(monitor->log, "Unable to find remote endpoint for %s", value);
|
spa_log_warn(monitor->log, "Unable to find remote endpoint for %s", value);
|
||||||
goto next;
|
goto next;
|
||||||
|
|
@ -4261,7 +4279,9 @@ static int setup_asha_transport(struct spa_bt_remote_endpoint *remote_endpoint,
|
||||||
spa_log_debug(monitor->log, "Setting ASHA codec: %s", mcodec->name);
|
spa_log_debug(monitor->log, "Setting ASHA codec: %s", mcodec->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(transport->remote_endpoint_path);
|
||||||
free(transport->endpoint_path);
|
free(transport->endpoint_path);
|
||||||
|
transport->remote_endpoint_path = strdup(remote_endpoint->path);
|
||||||
transport->endpoint_path = strdup(remote_endpoint->path);
|
transport->endpoint_path = strdup(remote_endpoint->path);
|
||||||
transport->profile = SPA_BT_PROFILE_ASHA_SINK;
|
transport->profile = SPA_BT_PROFILE_ASHA_SINK;
|
||||||
transport->media_codec = codec;
|
transport->media_codec = codec;
|
||||||
|
|
@ -4404,7 +4424,7 @@ static void codec_switch_reply(DBusPendingCall *pending, void *user_data)
|
||||||
codec_switch_resume(sw);
|
codec_switch_resume(sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool codec_switch_configure(struct spa_bt_codec_switch *sw)
|
static bool codec_switch_configure_a2dp(struct spa_bt_codec_switch *sw, const char *path)
|
||||||
{
|
{
|
||||||
struct spa_bt_device *device = sw->device;
|
struct spa_bt_device *device = sw->device;
|
||||||
struct spa_bt_monitor *monitor = device->monitor;
|
struct spa_bt_monitor *monitor = device->monitor;
|
||||||
|
|
@ -4419,26 +4439,24 @@ static bool codec_switch_configure(struct spa_bt_codec_switch *sw)
|
||||||
bool sink;
|
bool sink;
|
||||||
|
|
||||||
codec = sw->codec;
|
codec = sw->codec;
|
||||||
ep = device_remote_endpoint_find(device, *sw->path_iter);
|
ep = device_remote_endpoint_find(device, path);
|
||||||
|
|
||||||
if (!codec_switch_check_endpoint(ep, codec, &sink, &local_endpoint)) {
|
if (!codec_switch_check_endpoint(ep, codec, &sink, &local_endpoint)) {
|
||||||
spa_log_error(monitor->log, "media codec switch %p: endpoint %s not valid",
|
spa_log_error(monitor->log, "media codec switch %p: endpoint %s not valid",
|
||||||
sw, *sw->path_iter);
|
sw, path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!codec->bap) {
|
/* Each A2DP endpoint can be used by only one device at a time (on each adapter) */
|
||||||
/* Each A2DP endpoint can be used by only one device at a time (on each adapter) */
|
spa_list_for_each(t, &monitor->transport_list, link) {
|
||||||
spa_list_for_each(t, &monitor->transport_list, link) {
|
if (t->device == device)
|
||||||
if (t->device == device)
|
continue;
|
||||||
continue;
|
if (t->device->adapter != device->adapter)
|
||||||
if (t->device->adapter != device->adapter)
|
continue;
|
||||||
continue;
|
if (spa_streq(t->endpoint_path, local_endpoint)) {
|
||||||
if (spa_streq(t->endpoint_path, local_endpoint)) {
|
spa_log_error(monitor->log, "media codec switch %p: endpoint %s in use",
|
||||||
spa_log_error(monitor->log, "media codec switch %p: endpoint %s in use",
|
sw, local_endpoint);
|
||||||
sw, local_endpoint);
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4485,6 +4503,85 @@ static bool codec_switch_configure(struct spa_bt_codec_switch *sw)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool codec_switch_configure_bap(struct spa_bt_codec_switch *sw, const char *path, bool last)
|
||||||
|
{
|
||||||
|
struct spa_bt_device *device = sw->device;
|
||||||
|
struct spa_bt_monitor *monitor = device->monitor;
|
||||||
|
struct spa_bt_remote_endpoint *ep;
|
||||||
|
spa_autoptr(DBusMessage) m = NULL;
|
||||||
|
DBusMessageIter iter, d;
|
||||||
|
dbus_bool_t defer = !last;
|
||||||
|
|
||||||
|
ep = device_remote_endpoint_find(device, path);
|
||||||
|
if (!ep) {
|
||||||
|
spa_log_error(monitor->log, "media codec switch %p: no endpoint %s", sw, path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->preferred_codec = sw->codec;
|
||||||
|
device->preferred_profiles = sw->profiles;
|
||||||
|
|
||||||
|
m = dbus_message_new_method_call(BLUEZ_SERVICE, ep->path, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "Reconfigure");
|
||||||
|
if (m == NULL) {
|
||||||
|
spa_log_error(monitor->log, "media codec switch %p: dbus allocation failure", sw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
spa_bt_device_update_last_bluez_action_time(device);
|
||||||
|
|
||||||
|
spa_log_info(monitor->log, "media codec switch %p: reconfigure endpoint %s, defer:%d",
|
||||||
|
sw, ep->path, (int)defer);
|
||||||
|
|
||||||
|
dbus_message_iter_init_append(m, &iter);
|
||||||
|
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &d);
|
||||||
|
append_basic_variant_dict_entry(&d, "Defer", DBUS_TYPE_BOOLEAN, "b", &defer);
|
||||||
|
dbus_message_iter_close_container(&iter, &d);
|
||||||
|
|
||||||
|
spa_assert(sw->pending == NULL);
|
||||||
|
sw->pending = send_with_reply(monitor->conn, m, codec_switch_reply, sw);
|
||||||
|
if (!sw->pending) {
|
||||||
|
spa_log_error(monitor->log, "media codec switch %p: dbus call failure", sw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool codec_switch_clear_bap(struct spa_bt_codec_switch *sw, const char *path)
|
||||||
|
{
|
||||||
|
struct spa_bt_device *device = sw->device;
|
||||||
|
struct spa_bt_monitor *monitor = device->monitor;
|
||||||
|
struct spa_bt_remote_endpoint *ep;
|
||||||
|
spa_autoptr(DBusMessage) m = NULL;
|
||||||
|
DBusMessageIter iter;
|
||||||
|
|
||||||
|
ep = device_remote_endpoint_find(device, path);
|
||||||
|
if (!ep)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
m = dbus_message_new_method_call(BLUEZ_SERVICE, ep->path, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "ClearConfiguration");
|
||||||
|
if (m == NULL) {
|
||||||
|
spa_log_error(monitor->log, "media codec switch %p: dbus allocation failure", sw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
spa_bt_device_update_last_bluez_action_time(device);
|
||||||
|
|
||||||
|
spa_log_info(monitor->log, "media codec switch %p: clear endpoint %s", sw, ep->path);
|
||||||
|
|
||||||
|
dbus_message_iter_init_append(m, &iter);
|
||||||
|
dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
|
||||||
|
|
||||||
|
spa_assert(sw->pending == NULL);
|
||||||
|
sw->pending = send_with_reply(monitor->conn, m, codec_switch_reply, sw);
|
||||||
|
if (!sw->pending) {
|
||||||
|
spa_log_error(monitor->log, "media codec switch %p: dbus call failure", sw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool codec_switch_process(struct spa_bt_codec_switch *sw)
|
static bool codec_switch_process(struct spa_bt_codec_switch *sw)
|
||||||
{
|
{
|
||||||
if (sw->waiting)
|
if (sw->waiting)
|
||||||
|
|
@ -4494,7 +4591,7 @@ static bool codec_switch_process(struct spa_bt_codec_switch *sw)
|
||||||
if (sw->failed)
|
if (sw->failed)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (*sw->path_iter == NULL) {
|
if (sw->paths[sw->path_idx].path == NULL) {
|
||||||
/* Success */
|
/* Success */
|
||||||
spa_log_info(sw->device->monitor->log, "media codec switch %p: success", sw);
|
spa_log_info(sw->device->monitor->log, "media codec switch %p: success", sw);
|
||||||
spa_bt_device_emit_codec_switched(sw->device, 0);
|
spa_bt_device_emit_codec_switched(sw->device, 0);
|
||||||
|
|
@ -4502,15 +4599,42 @@ static bool codec_switch_process(struct spa_bt_codec_switch *sw)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Rate limit BlueZ calls */
|
if (sw->profiles & SPA_BT_PROFILE_A2DP_DUPLEX) {
|
||||||
if (codec_switch_rate_limit(sw))
|
/* Rate limit BlueZ calls */
|
||||||
return false;
|
if (codec_switch_rate_limit(sw))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!codec_switch_configure(sw))
|
if (!codec_switch_configure_a2dp(sw, sw->paths[sw->path_idx].path))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
} else {
|
||||||
|
if (sw->path_idx == 0 && codec_switch_rate_limit(sw))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (sw->path_idx == 0) {
|
||||||
|
struct spa_bt_transport *t, *t2;
|
||||||
|
|
||||||
|
/* Force CIG inactive */
|
||||||
|
spa_list_for_each(t, &sw->device->transport_list, link) {
|
||||||
|
spa_bt_transport_release_now(t);
|
||||||
|
spa_list_for_each(t2, &sw->device->monitor->transport_list, link)
|
||||||
|
if (t2->device != sw->device && transport_in_same_cig(t, t2))
|
||||||
|
spa_bt_transport_release_now(t2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sw->paths[sw->path_idx].clear) {
|
||||||
|
if (!codec_switch_clear_bap(sw, sw->paths[sw->path_idx].path))
|
||||||
|
goto fail;
|
||||||
|
} else {
|
||||||
|
bool last = (sw->paths[sw->path_idx + 1].path == NULL);
|
||||||
|
|
||||||
|
if (!codec_switch_configure_bap(sw, sw->paths[sw->path_idx].path, last))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Configure another endpoint next */
|
/* Configure another endpoint next */
|
||||||
++sw->path_iter;
|
sw->path_idx++;
|
||||||
|
|
||||||
/* Wait for dbus reply */
|
/* Wait for dbus reply */
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -4533,15 +4657,15 @@ static void codec_switch_cancel(struct spa_bt_codec_switch *sw)
|
||||||
|
|
||||||
static void codec_switch_destroy(struct spa_bt_codec_switch *sw)
|
static void codec_switch_destroy(struct spa_bt_codec_switch *sw)
|
||||||
{
|
{
|
||||||
char **p;
|
unsigned int i;
|
||||||
|
|
||||||
spa_list_remove(&sw->link);
|
spa_list_remove(&sw->link);
|
||||||
|
|
||||||
cancel_and_unref(&sw->pending);
|
cancel_and_unref(&sw->pending);
|
||||||
|
|
||||||
if (sw->paths != NULL)
|
if (sw->paths != NULL)
|
||||||
for (p = sw->paths; *p != NULL; ++p)
|
for (i = 0; sw->paths[i].path; ++i)
|
||||||
free(*p);
|
free(sw->paths[i].path);
|
||||||
|
|
||||||
if (sw->timer)
|
if (sw->timer)
|
||||||
spa_loop_utils_destroy_source(sw->device->monitor->loop_utils, sw->timer);
|
spa_loop_utils_destroy_source(sw->device->monitor->loop_utils, sw->timer);
|
||||||
|
|
@ -4569,12 +4693,13 @@ static int codec_switch_cmp(const void *a, const void *b)
|
||||||
struct spa_bt_codec_switch *sw = codec_switch_cmp_sw;
|
struct spa_bt_codec_switch *sw = codec_switch_cmp_sw;
|
||||||
const struct media_codec *codec = sw->codec;
|
const struct media_codec *codec = sw->codec;
|
||||||
struct spa_bt_monitor *monitor = sw->device->monitor;
|
struct spa_bt_monitor *monitor = sw->device->monitor;
|
||||||
const char *path1 = *(char **)a, *path2 = *(char **)b;
|
const struct spa_bt_codec_switch_path *path1 = a;
|
||||||
|
const struct spa_bt_codec_switch_path *path2 = b;
|
||||||
struct spa_bt_remote_endpoint *ep1, *ep2;
|
struct spa_bt_remote_endpoint *ep1, *ep2;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
|
||||||
ep1 = device_remote_endpoint_find(sw->device, path1);
|
ep1 = device_remote_endpoint_find(sw->device, path1->path);
|
||||||
ep2 = device_remote_endpoint_find(sw->device, path2);
|
ep2 = device_remote_endpoint_find(sw->device, path2->path);
|
||||||
|
|
||||||
if (ep1 != NULL && (ep1->uuid == NULL || ep1->codec != codec->codec_id))
|
if (ep1 != NULL && (ep1->uuid == NULL || ep1->codec != codec->codec_id))
|
||||||
ep1 = NULL;
|
ep1 = NULL;
|
||||||
|
|
@ -4603,7 +4728,8 @@ static int codec_switch_cmp(const void *a, const void *b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure there's a transport for at least one of the listed codecs */
|
/* Ensure there's a transport for at least one of the listed codecs */
|
||||||
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,
|
||||||
|
uint32_t profiles)
|
||||||
{
|
{
|
||||||
struct spa_bt_monitor *monitor = device->monitor;
|
struct spa_bt_monitor *monitor = device->monitor;
|
||||||
struct spa_bt_codec_switch *sw, *sw2;
|
struct spa_bt_codec_switch *sw, *sw2;
|
||||||
|
|
@ -4611,7 +4737,7 @@ int spa_bt_device_ensure_media_codec(struct spa_bt_device *device, const struct
|
||||||
struct spa_bt_transport *t;
|
struct spa_bt_transport *t;
|
||||||
const struct media_codec *codec = NULL;
|
const struct media_codec *codec = NULL;
|
||||||
size_t i, j, num_eps, res;
|
size_t i, j, num_eps, res;
|
||||||
uint32_t profiles = 0;
|
uint32_t remaining = 0;
|
||||||
|
|
||||||
if (!device->adapter->a2dp_application_registered &&
|
if (!device->adapter->a2dp_application_registered &&
|
||||||
!device->adapter->bap_application_registered) {
|
!device->adapter->bap_application_registered) {
|
||||||
|
|
@ -4626,21 +4752,28 @@ int spa_bt_device_ensure_media_codec(struct spa_bt_device *device, const struct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!profiles)
|
||||||
|
profiles = device->connected_profiles & (SPA_BT_PROFILE_MEDIA_SOURCE |
|
||||||
|
SPA_BT_PROFILE_MEDIA_SINK);
|
||||||
|
|
||||||
if (!codec)
|
if (!codec)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* Check if we already have an enabled transport for the codec.
|
/* Check if we already have an enabled transports for the profiles.
|
||||||
* However, if there already was a codec switch running, these transports may
|
* However, if there already was a codec switch running, these transports may
|
||||||
* disappear soon. In that case, we have to do the full thing.
|
* disappear soon. In that case, we have to do the full thing.
|
||||||
*/
|
*/
|
||||||
if (!has_codec_switch(device)) {
|
if (!has_codec_switch(device)) {
|
||||||
|
uint32_t found_profiles = 0;
|
||||||
|
|
||||||
spa_list_for_each(t, &device->transport_list, device_link) {
|
spa_list_for_each(t, &device->transport_list, device_link) {
|
||||||
if (t->media_codec != codec)
|
if (t->media_codec != codec)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((device->connected_profiles & t->profile) != t->profile)
|
found_profiles |= t->profile;
|
||||||
continue;
|
}
|
||||||
|
|
||||||
|
if (found_profiles == profiles) {
|
||||||
spa_bt_device_emit_codec_switched(device, 0);
|
spa_bt_device_emit_codec_switched(device, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -4653,6 +4786,7 @@ int spa_bt_device_ensure_media_codec(struct spa_bt_device *device, const struct
|
||||||
|
|
||||||
sw->codec = codec;
|
sw->codec = codec;
|
||||||
sw->device = device;
|
sw->device = device;
|
||||||
|
sw->profiles = profiles;
|
||||||
|
|
||||||
spa_list_append(&device->codec_switch_list, &sw->link);
|
spa_list_append(&device->codec_switch_list, &sw->link);
|
||||||
|
|
||||||
|
|
@ -4661,57 +4795,83 @@ int spa_bt_device_ensure_media_codec(struct spa_bt_device *device, const struct
|
||||||
spa_list_for_each(ep, &device->remote_endpoint_list, device_link)
|
spa_list_for_each(ep, &device->remote_endpoint_list, device_link)
|
||||||
++num_eps;
|
++num_eps;
|
||||||
|
|
||||||
sw->paths = calloc(num_eps + 1, sizeof(char *));
|
sw->paths = calloc(num_eps + 1, sizeof(*sw->paths));
|
||||||
if (!sw->paths)
|
if (!sw->paths)
|
||||||
goto error_errno;
|
goto error_errno;
|
||||||
|
|
||||||
sw->path_iter = sw->paths;
|
sw->path_idx = 0;
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
spa_list_for_each(ep, &device->remote_endpoint_list, device_link) {
|
spa_list_for_each(ep, &device->remote_endpoint_list, device_link) {
|
||||||
sw->paths[i] = strdup(ep->path);
|
sw->paths[i].path = strdup(ep->path);
|
||||||
if (sw->paths[i] == NULL)
|
if (sw->paths[i].path == NULL)
|
||||||
goto error_errno;
|
goto error_errno;
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sort in codec preference order */
|
/* Sort in codec preference order */
|
||||||
codec_switch_cmp_sw = sw;
|
codec_switch_cmp_sw = sw;
|
||||||
qsort(sw->paths, num_eps, sizeof(char *), codec_switch_cmp);
|
qsort(sw->paths, num_eps, sizeof(*sw->paths), codec_switch_cmp);
|
||||||
|
|
||||||
/* Pick at most one source and one sink endpoint, if corresponding profiles are
|
/* Pick at most one source and one sink endpoint, if corresponding profiles are
|
||||||
* set */
|
* set */
|
||||||
profiles = device->connected_profiles;
|
remaining = profiles;
|
||||||
for (i = 0, j = 0; i < num_eps; ++i) {
|
for (i = 0, j = 0; i < num_eps; ++i) {
|
||||||
struct spa_bt_remote_endpoint *ep;
|
struct spa_bt_remote_endpoint *ep;
|
||||||
bool sink;
|
bool sink;
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
|
|
||||||
ep = remote_endpoint_find(monitor, sw->paths[i]);
|
ep = remote_endpoint_find(monitor, sw->paths[i].path);
|
||||||
if (!codec_switch_check_endpoint(ep, codec, &sink, NULL))
|
if (!codec_switch_check_endpoint(ep, codec, &sink, NULL))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
mask = sink ? SPA_BT_PROFILE_MEDIA_SOURCE : SPA_BT_PROFILE_MEDIA_SINK;
|
mask = sink ? SPA_BT_PROFILE_MEDIA_SOURCE : SPA_BT_PROFILE_MEDIA_SINK;
|
||||||
if (!(profiles & mask))
|
if (!(remaining & mask))
|
||||||
continue;
|
continue;
|
||||||
SPA_FLAG_CLEAR(profiles, mask);
|
SPA_FLAG_CLEAR(remaining, mask);
|
||||||
|
|
||||||
spa_log_debug(monitor->log,
|
spa_log_debug(monitor->log,
|
||||||
"media codec switch %p: select endpoint %s for codec %s",
|
"media codec switch %p: select endpoint %s for codec %s",
|
||||||
sw, sw->paths[i], codec->name);
|
sw, sw->paths[i].path, codec->name);
|
||||||
|
|
||||||
SPA_SWAP(sw->paths[j], sw->paths[i]);
|
SPA_SWAP(sw->paths[j], sw->paths[i]);
|
||||||
++j;
|
++j;
|
||||||
}
|
}
|
||||||
|
if (profiles & SPA_BT_PROFILE_BAP_AUDIO) {
|
||||||
|
/* Active unselected endpoints must be cleared */
|
||||||
|
for (i = j; i < num_eps; ++i) {
|
||||||
|
bool active_ep = false;
|
||||||
|
|
||||||
|
spa_list_for_each(t, &device->transport_list, device_link) {
|
||||||
|
if (spa_streq(t->remote_endpoint_path, sw->paths[i].path)) {
|
||||||
|
active_ep = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!active_ep)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
spa_log_debug(monitor->log,
|
||||||
|
"media codec switch %p: select endpoint %s to be cleared",
|
||||||
|
sw, sw->paths[i].path);
|
||||||
|
SPA_SWAP(sw->paths[j], sw->paths[i]);
|
||||||
|
sw->paths[j].clear = true;
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reverse order so that clears come first */
|
||||||
|
for (i = 0; i < j/2; ++i)
|
||||||
|
SPA_SWAP(sw->paths[i], sw->paths[j - 1 - i]);
|
||||||
|
}
|
||||||
for (; j < num_eps; ++j) {
|
for (; j < num_eps; ++j) {
|
||||||
free(sw->paths[j]);
|
free(sw->paths[j].path);
|
||||||
sw->paths[j] = NULL;
|
spa_zero(sw->paths[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sw->paths[0]) {
|
if (!sw->paths[0].path || remaining) {
|
||||||
spa_log_debug(monitor->log,
|
spa_log_error(monitor->log,
|
||||||
"media codec switch %p: no valid endpoints for codec %s",
|
"media codec switch %p: no valid profile 0x%x endpoints for codec %s",
|
||||||
sw, codec->name);
|
sw, profiles, codec->name);
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
goto error_errno;
|
goto error_errno;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1410,7 +1410,7 @@ static int set_profile(struct impl *this, uint32_t profile, enum spa_bluetooth_a
|
||||||
|
|
||||||
this->switching_codec = true;
|
this->switching_codec = true;
|
||||||
|
|
||||||
ret = spa_bt_device_ensure_media_codec(this->bt_dev, codecs);
|
ret = spa_bt_device_ensure_media_codec(this->bt_dev, codecs, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret != -ENOTSUP)
|
if (ret != -ENOTSUP)
|
||||||
spa_log_error(this->log, "failed to switch codec (%d), setting basic profile", ret);
|
spa_log_error(this->log, "failed to switch codec (%d), setting basic profile", ret);
|
||||||
|
|
@ -1563,6 +1563,9 @@ static void device_set_changed(void *userdata)
|
||||||
this->profile != DEVICE_PROFILE_ASHA)
|
this->profile != DEVICE_PROFILE_ASHA)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (this->switching_codec)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!device_set_needs_update(this)) {
|
if (!device_set_needs_update(this)) {
|
||||||
spa_log_debug(this->log, "%p: device set not changed", this);
|
spa_log_debug(this->log, "%p: device set not changed", this);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,9 @@ enum spa_bt_profile {
|
||||||
|
|
||||||
static inline enum spa_bt_profile spa_bt_profile_from_uuid(const char *uuid)
|
static inline enum spa_bt_profile spa_bt_profile_from_uuid(const char *uuid)
|
||||||
{
|
{
|
||||||
if (strcasecmp(uuid, SPA_BT_UUID_A2DP_SOURCE) == 0)
|
if (!uuid)
|
||||||
|
return 0;
|
||||||
|
else if (strcasecmp(uuid, SPA_BT_UUID_A2DP_SOURCE) == 0)
|
||||||
return SPA_BT_PROFILE_A2DP_SOURCE;
|
return SPA_BT_PROFILE_A2DP_SOURCE;
|
||||||
else if (strcasecmp(uuid, SPA_BT_UUID_A2DP_SINK) == 0)
|
else if (strcasecmp(uuid, SPA_BT_UUID_A2DP_SINK) == 0)
|
||||||
return SPA_BT_PROFILE_A2DP_SINK;
|
return SPA_BT_PROFILE_A2DP_SINK;
|
||||||
|
|
@ -565,6 +567,7 @@ struct spa_bt_device {
|
||||||
DBusPendingCall *battery_pending_call;
|
DBusPendingCall *battery_pending_call;
|
||||||
|
|
||||||
const struct media_codec *preferred_codec;
|
const struct media_codec *preferred_codec;
|
||||||
|
uint32_t preferred_profiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct spa_bt_device *spa_bt_device_find(struct spa_bt_monitor *monitor, const char *path);
|
struct spa_bt_device *spa_bt_device_find(struct spa_bt_monitor *monitor, const char *path);
|
||||||
|
|
@ -572,7 +575,7 @@ struct spa_bt_device *spa_bt_device_find_by_address(struct spa_bt_monitor *monit
|
||||||
int spa_bt_device_add_profile(struct spa_bt_device *device, enum spa_bt_profile profile);
|
int spa_bt_device_add_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_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, uint32_t profiles);
|
||||||
int spa_bt_device_ensure_hfp_codec(struct spa_bt_device *device, const struct media_codec *codec);
|
int spa_bt_device_ensure_hfp_codec(struct spa_bt_device *device, const struct media_codec *codec);
|
||||||
bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, enum spa_bt_profile profile);
|
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);
|
const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count);
|
||||||
|
|
@ -666,6 +669,7 @@ struct spa_bt_transport {
|
||||||
void *configuration;
|
void *configuration;
|
||||||
int configuration_len;
|
int configuration_len;
|
||||||
char *endpoint_path;
|
char *endpoint_path;
|
||||||
|
char *remote_endpoint_path;
|
||||||
bool bap_initiator;
|
bool bap_initiator;
|
||||||
struct spa_list bap_transport_linked;
|
struct spa_list bap_transport_linked;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue