mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
bluez5: select BAP audio locations in SelectProperties
Do BAP audio location selection properly in SelectProperties, now that BlueZ provides the supported locations there. Remove a previous workaround. The audio location in SelectProperties determines the audio channel allocation, which determines the channel positions.
This commit is contained in:
parent
815501cc1f
commit
420f7cb48e
3 changed files with 97 additions and 117 deletions
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <spa/param/audio/format.h>
|
||||
#include <spa/param/audio/format-utils.h>
|
||||
#include <spa/utils/string.h>
|
||||
|
||||
#include <lc3.h>
|
||||
|
||||
|
|
@ -37,6 +38,7 @@ struct impl {
|
|||
struct pac_data {
|
||||
const uint8_t *data;
|
||||
size_t size;
|
||||
uint32_t locations;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -47,6 +49,40 @@ typedef struct {
|
|||
uint8_t n_blks;
|
||||
} bap_lc3_t;
|
||||
|
||||
static const struct {
|
||||
uint32_t bit;
|
||||
enum spa_audio_channel channel;
|
||||
} channel_bits[] = {
|
||||
{ LC3_CONFIG_CHNL_FL, SPA_AUDIO_CHANNEL_FL },
|
||||
{ LC3_CONFIG_CHNL_FR, SPA_AUDIO_CHANNEL_FR },
|
||||
{ LC3_CONFIG_CHNL_FC, SPA_AUDIO_CHANNEL_FC },
|
||||
{ LC3_CONFIG_CHNL_LFE, SPA_AUDIO_CHANNEL_LFE },
|
||||
{ LC3_CONFIG_CHNL_BL, SPA_AUDIO_CHANNEL_RL },
|
||||
{ LC3_CONFIG_CHNL_BR, SPA_AUDIO_CHANNEL_RR },
|
||||
{ LC3_CONFIG_CHNL_FLC, SPA_AUDIO_CHANNEL_FLC },
|
||||
{ LC3_CONFIG_CHNL_FRC, SPA_AUDIO_CHANNEL_FRC },
|
||||
{ LC3_CONFIG_CHNL_BC, SPA_AUDIO_CHANNEL_BC },
|
||||
{ LC3_CONFIG_CHNL_LFE2, SPA_AUDIO_CHANNEL_LFE2 },
|
||||
{ LC3_CONFIG_CHNL_SL, SPA_AUDIO_CHANNEL_SL },
|
||||
{ LC3_CONFIG_CHNL_SR, SPA_AUDIO_CHANNEL_SR },
|
||||
{ LC3_CONFIG_CHNL_TFL, SPA_AUDIO_CHANNEL_TFL },
|
||||
{ LC3_CONFIG_CHNL_TFR, SPA_AUDIO_CHANNEL_TFR },
|
||||
{ LC3_CONFIG_CHNL_TFC, SPA_AUDIO_CHANNEL_TFC },
|
||||
{ LC3_CONFIG_CHNL_TC, SPA_AUDIO_CHANNEL_TC },
|
||||
{ LC3_CONFIG_CHNL_TBL, SPA_AUDIO_CHANNEL_TRL },
|
||||
{ LC3_CONFIG_CHNL_TBR, SPA_AUDIO_CHANNEL_TRR },
|
||||
{ LC3_CONFIG_CHNL_TSL, SPA_AUDIO_CHANNEL_TSL },
|
||||
{ LC3_CONFIG_CHNL_TSR, SPA_AUDIO_CHANNEL_TSR },
|
||||
{ LC3_CONFIG_CHNL_TBC, SPA_AUDIO_CHANNEL_TRC },
|
||||
{ LC3_CONFIG_CHNL_BFC, SPA_AUDIO_CHANNEL_BC },
|
||||
{ LC3_CONFIG_CHNL_BFL, SPA_AUDIO_CHANNEL_BLC },
|
||||
{ LC3_CONFIG_CHNL_BFR, SPA_AUDIO_CHANNEL_BRC },
|
||||
{ LC3_CONFIG_CHNL_FLW, SPA_AUDIO_CHANNEL_FLW },
|
||||
{ LC3_CONFIG_CHNL_FRW, SPA_AUDIO_CHANNEL_FRW },
|
||||
{ LC3_CONFIG_CHNL_LS, SPA_AUDIO_CHANNEL_SL }, /* is it the right mapping? */
|
||||
{ LC3_CONFIG_CHNL_RS, SPA_AUDIO_CHANNEL_SR }, /* is it the right mapping? */
|
||||
};
|
||||
|
||||
static int write_ltv(uint8_t *dest, uint8_t type, void* value, size_t len)
|
||||
{
|
||||
struct ltv *ltv = (struct ltv *)dest;
|
||||
|
|
@ -136,6 +172,36 @@ static uint8_t get_num_channels(uint32_t channels)
|
|||
return num;
|
||||
}
|
||||
|
||||
static int select_channels(uint8_t channels, uint32_t locations, uint32_t *mapping)
|
||||
{
|
||||
unsigned int i, num;
|
||||
|
||||
if (channels & LC3_CHAN_2)
|
||||
num = 2;
|
||||
else if (channels & LC3_CHAN_1)
|
||||
num = 1;
|
||||
else
|
||||
return -1;
|
||||
|
||||
if (!locations) {
|
||||
*mapping = 0; /* mono (omit Audio_Channel_Allocation) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: select some channels, but upper level should tell us what */
|
||||
*mapping = 0;
|
||||
for (i = 0; i < SPA_N_ELEMENTS(channel_bits); ++i) {
|
||||
if (locations & channel_bits[i].bit) {
|
||||
*mapping |= channel_bits[i].bit;
|
||||
--num;
|
||||
if (num == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool select_config(bap_lc3_t *conf, const struct pac_data *pac)
|
||||
{
|
||||
const uint8_t *data = pac->data;
|
||||
|
|
@ -191,12 +257,8 @@ static bool select_config(bap_lc3_t *conf, const struct pac_data *pac)
|
|||
spa_return_val_if_fail(ltv->len == 2, false);
|
||||
{
|
||||
uint8_t channels = ltv->value[0];
|
||||
/* XXX: we hardcode mono or stereo stream */
|
||||
if (channels & LC3_CHAN_2)
|
||||
conf->channels = LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL;
|
||||
else if (channels & LC3_CHAN_1)
|
||||
conf->channels = 0; /* mono (omit Audio_Channel_Allocation) */
|
||||
else
|
||||
|
||||
if (select_channels(channels, pac->locations, &conf->channels) < 0)
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
|
@ -369,10 +431,18 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
|||
int npacs;
|
||||
bap_lc3_t conf;
|
||||
uint8_t *data = config;
|
||||
uint32_t locations = 0;
|
||||
int i;
|
||||
|
||||
if (caps == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (settings) {
|
||||
for (i = 0; i < (int)settings->n_items; ++i)
|
||||
if (spa_streq(settings->items[i].key, "bluez5.bap.locations"))
|
||||
sscanf(settings->items[i].value, "%"PRIu32, &locations);
|
||||
}
|
||||
|
||||
/* Select best conf from those possible */
|
||||
npacs = parse_bluez_pacs(caps, caps_size, pacs);
|
||||
if (npacs < 0)
|
||||
|
|
@ -380,6 +450,9 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
|||
else if (npacs == 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < npacs; ++i)
|
||||
pacs[i].locations = locations;
|
||||
|
||||
qsort(pacs, npacs, sizeof(struct pac_data), pac_cmp);
|
||||
|
||||
if (!select_config(&conf, &pacs[0]))
|
||||
|
|
@ -405,8 +478,8 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f
|
|||
int res1, res2;
|
||||
|
||||
/* Order selected configurations by preference */
|
||||
res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1);
|
||||
res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2);
|
||||
res1 = codec->select_config(codec, 0, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1);
|
||||
res2 = codec->select_config(codec, 0, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2);
|
||||
|
||||
return conf_cmp(&conf1, res1, &conf2, res2);
|
||||
}
|
||||
|
|
@ -422,38 +495,11 @@ static uint8_t channels_to_positions(uint32_t channels, uint32_t *position)
|
|||
position[0] = SPA_AUDIO_CHANNEL_MONO;
|
||||
n_positions = 1;
|
||||
} else {
|
||||
#define CHANNEL_2_SPACHANNEL(channel,spa_channel) if (channels & channel) position[n_positions++] = spa_channel;
|
||||
unsigned int i;
|
||||
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FL, SPA_AUDIO_CHANNEL_FL);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FR, SPA_AUDIO_CHANNEL_FR);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FC, SPA_AUDIO_CHANNEL_FC);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LFE, SPA_AUDIO_CHANNEL_LFE);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BL, SPA_AUDIO_CHANNEL_RL);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BR, SPA_AUDIO_CHANNEL_RR);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FLC, SPA_AUDIO_CHANNEL_FLC);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FRC, SPA_AUDIO_CHANNEL_FRC);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BC, SPA_AUDIO_CHANNEL_BC);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LFE2, SPA_AUDIO_CHANNEL_LFE2);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_SL, SPA_AUDIO_CHANNEL_SL);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_SR, SPA_AUDIO_CHANNEL_SR);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFL, SPA_AUDIO_CHANNEL_TFL);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFR, SPA_AUDIO_CHANNEL_TFR);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFC, SPA_AUDIO_CHANNEL_TFC);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TC, SPA_AUDIO_CHANNEL_TC);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBL, SPA_AUDIO_CHANNEL_TRL);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBR, SPA_AUDIO_CHANNEL_TRR);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TSL, SPA_AUDIO_CHANNEL_TSL);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TSR, SPA_AUDIO_CHANNEL_TSR);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBC, SPA_AUDIO_CHANNEL_TRC);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFC, SPA_AUDIO_CHANNEL_BC);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFL, SPA_AUDIO_CHANNEL_BLC);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFR, SPA_AUDIO_CHANNEL_BRC);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FLW, SPA_AUDIO_CHANNEL_FLW);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FRW, SPA_AUDIO_CHANNEL_FRW);
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LS, SPA_AUDIO_CHANNEL_LLFE); /* is it the right mapping? */
|
||||
CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_RS, SPA_AUDIO_CHANNEL_RLFE); /* is it the right mapping? */
|
||||
|
||||
#undef CHANNEL_2_SPACHANNEL
|
||||
for (i = 0; i < SPA_N_ELEMENTS(channel_bits); ++i)
|
||||
if (channels & channel_bits[i].bit)
|
||||
position[n_positions++] = channel_bits[i].channel;
|
||||
}
|
||||
|
||||
if (n_positions != n_channels)
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@
|
|||
#include "iso-io.h"
|
||||
#include "defs.h"
|
||||
|
||||
#include "bap-codec-caps.h"
|
||||
|
||||
static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5");
|
||||
#undef SPA_LOG_TOPIC_DEFAULT
|
||||
#define SPA_LOG_TOPIC_DEFAULT &log_topic
|
||||
|
|
@ -632,10 +630,14 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
const struct media_codec *codec;
|
||||
bool sink;
|
||||
const char *err_msg = "Unknown error";
|
||||
struct spa_dict settings;
|
||||
struct spa_dict_item setting_items[SPA_N_ELEMENTS(monitor->global_setting_items) + 1];
|
||||
int i;
|
||||
|
||||
const char *endpoint_path = NULL;
|
||||
uint8_t caps[A2DP_MAX_CAPS_SIZE];
|
||||
uint8_t config[A2DP_MAX_CAPS_SIZE];
|
||||
char locations[64] = {0};
|
||||
int caps_size = 0;
|
||||
int conf_size;
|
||||
DBusMessageIter dict;
|
||||
|
|
@ -751,6 +753,8 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
endpoint_qos.preferred_delay_min = v;
|
||||
else if (spa_streq(key, "PreferredMaximumDelay"))
|
||||
endpoint_qos.preferred_delay_max = v;
|
||||
else if (spa_streq(key, "Location"))
|
||||
spa_scnprintf(locations, sizeof(locations), "%"PRIu32, v);
|
||||
else
|
||||
spa_log_info(monitor->log, "Unknown property %s", key);
|
||||
} else {
|
||||
|
|
@ -775,10 +779,12 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
ep->acceptor = true;
|
||||
}
|
||||
|
||||
/* TODO: determine which device the SelectConfiguration() call is associated
|
||||
* with; it's known here based on the remote endpoint.
|
||||
*/
|
||||
conf_size = codec->select_config(codec, 0, caps, caps_size, &monitor->default_audio_info, NULL, config);
|
||||
for (i = 0; i < (int)monitor->global_settings.n_items; ++i)
|
||||
setting_items[i] = monitor->global_settings.items[i];
|
||||
setting_items[i] = SPA_DICT_ITEM_INIT("bluez5.bap.locations", locations);
|
||||
settings = SPA_DICT_INIT(setting_items, monitor->global_settings.n_items + 1);
|
||||
|
||||
conf_size = codec->select_config(codec, 0, caps, caps_size, &monitor->default_audio_info, &settings, config);
|
||||
if (conf_size < 0) {
|
||||
spa_log_error(monitor->log, "can't select config: %d (%s)",
|
||||
conf_size, spa_strerror(conf_size));
|
||||
|
|
@ -3189,16 +3195,6 @@ static int transport_update_props(struct spa_bt_transport *transport,
|
|||
else
|
||||
transport->bap_cis = value;
|
||||
}
|
||||
else if (spa_streq(key, "Location")) {
|
||||
uint32_t value;
|
||||
|
||||
if (type != DBUS_TYPE_UINT32)
|
||||
goto next;
|
||||
dbus_message_iter_get_basic(&it[1], &value);
|
||||
|
||||
spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
|
||||
transport->bap_location = value;
|
||||
}
|
||||
next:
|
||||
dbus_message_iter_next(props_iter);
|
||||
}
|
||||
|
|
@ -4187,64 +4183,6 @@ int spa_bt_device_supports_hfp_codec(struct spa_bt_device *device, unsigned int
|
|||
return spa_bt_backend_supports_codec(monitor->backend, device, codec);
|
||||
}
|
||||
|
||||
static void bap_update_codec_location(struct spa_bt_transport *t)
|
||||
{
|
||||
uint8_t *data = t->configuration;
|
||||
size_t size = t->configuration_len;
|
||||
struct ltv *ltv;
|
||||
uint32_t location;
|
||||
int i;
|
||||
|
||||
if (!t->bap_location)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Append channel location from BAP transport location, if no channel
|
||||
* configuration is present in the configuration.
|
||||
*
|
||||
* XXX: The codec select_configuration should set the location
|
||||
* XXX: for mono channels from the device location. We have to do
|
||||
* XXX: this here because transport location is not know
|
||||
* XXX: in SelectProperties (TODO: should be fixed in bluez).
|
||||
*/
|
||||
|
||||
while (size > 0) {
|
||||
ltv = (struct ltv *)data;
|
||||
|
||||
if (ltv->len < sizeof(struct ltv) || ltv->len >= size)
|
||||
return;
|
||||
|
||||
if (ltv->type == LC3_TYPE_CHAN)
|
||||
return; /* already has the channel info */
|
||||
|
||||
size -= ltv->len + 1;
|
||||
data += ltv->len + 1;
|
||||
}
|
||||
|
||||
/* Pick the first location bit set */
|
||||
location = t->bap_location;
|
||||
for (i = 0; i < 32; ++i) {
|
||||
if (location & (1 << i)) {
|
||||
location = (1 << i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Append LTV value to transport configuration */
|
||||
size = t->configuration_len + sizeof(struct ltv) + sizeof(uint32_t);
|
||||
data = realloc(t->configuration, size);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
ltv = SPA_PTROFF(data, t->configuration_len, struct ltv);
|
||||
ltv->len = 5;
|
||||
ltv->type = LC3_TYPE_CHAN;
|
||||
memcpy(ltv->value, &location, sizeof(uint32_t));
|
||||
|
||||
t->configuration = data;
|
||||
t->configuration_len = size;
|
||||
}
|
||||
|
||||
static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn,
|
||||
const char *path, DBusMessage *m, void *userdata)
|
||||
{
|
||||
|
|
@ -4329,9 +4267,6 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn,
|
|||
|= transport->device->a2dp_volume_active[SPA_BT_VOLUME_ID_TX];
|
||||
}
|
||||
|
||||
if (codec->bap)
|
||||
bap_update_codec_location(transport);
|
||||
|
||||
if (codec->validate_config) {
|
||||
struct spa_audio_info info;
|
||||
if (codec->validate_config(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0,
|
||||
|
|
|
|||
|
|
@ -630,7 +630,6 @@ struct spa_bt_transport {
|
|||
unsigned int latency_us;
|
||||
uint8_t bap_cig;
|
||||
uint8_t bap_cis;
|
||||
uint32_t bap_location;
|
||||
uint32_t bap_interval;
|
||||
|
||||
struct spa_bt_iso_io *iso_io;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue