mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
bluez5: add properties for A2DP codec sample rate & channel mode configuration
Those properties are set as reference sample rate and channels number for A2DP configuration selecting.
This commit is contained in:
parent
15239ff74d
commit
9384ba5500
8 changed files with 234 additions and 132 deletions
|
|
@ -90,22 +90,26 @@ static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags,
|
||||||
return sizeof(a2dp_aac);
|
return sizeof(a2dp_aac);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct {
|
static struct a2dp_codec_config
|
||||||
int config;
|
aac_frequencies[] = {
|
||||||
int freq;
|
{ AAC_SAMPLING_FREQ_48000, 48000, 11 },
|
||||||
} aac_frequencies[] = {
|
{ AAC_SAMPLING_FREQ_44100, 44100, 10 },
|
||||||
{ AAC_SAMPLING_FREQ_48000, 48000 },
|
{ AAC_SAMPLING_FREQ_96000, 96000, 9 },
|
||||||
{ AAC_SAMPLING_FREQ_44100, 44100 },
|
{ AAC_SAMPLING_FREQ_88200, 88200, 8 },
|
||||||
{ AAC_SAMPLING_FREQ_96000, 96000 },
|
{ AAC_SAMPLING_FREQ_64000, 64000, 7 },
|
||||||
{ AAC_SAMPLING_FREQ_88200, 88200 },
|
{ AAC_SAMPLING_FREQ_32000, 32000, 6 },
|
||||||
{ AAC_SAMPLING_FREQ_64000, 64000 },
|
{ AAC_SAMPLING_FREQ_24000, 24000, 5 },
|
||||||
{ AAC_SAMPLING_FREQ_32000, 32000 },
|
{ AAC_SAMPLING_FREQ_22050, 22050, 4 },
|
||||||
{ AAC_SAMPLING_FREQ_24000, 24000 },
|
{ AAC_SAMPLING_FREQ_16000, 16000, 3 },
|
||||||
{ AAC_SAMPLING_FREQ_22050, 22050 },
|
{ AAC_SAMPLING_FREQ_12000, 12000, 2 },
|
||||||
{ AAC_SAMPLING_FREQ_16000, 16000 },
|
{ AAC_SAMPLING_FREQ_11025, 11025, 1 },
|
||||||
{ AAC_SAMPLING_FREQ_12000, 12000 },
|
{ AAC_SAMPLING_FREQ_8000, 8000, 0 },
|
||||||
{ AAC_SAMPLING_FREQ_11025, 11025 },
|
};
|
||||||
{ AAC_SAMPLING_FREQ_8000, 8000 },
|
|
||||||
|
static struct a2dp_codec_config
|
||||||
|
aac_channel_modes[] = {
|
||||||
|
{ AAC_CHANNELS_2, 2, 1 },
|
||||||
|
{ AAC_CHANNELS_1, 1, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int get_valid_aac_bitrate(a2dp_aac_t *conf)
|
static int get_valid_aac_bitrate(a2dp_aac_t *conf)
|
||||||
|
|
@ -120,11 +124,11 @@ static int get_valid_aac_bitrate(a2dp_aac_t *conf)
|
||||||
|
|
||||||
static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
||||||
const void *caps, size_t caps_size,
|
const void *caps, size_t caps_size,
|
||||||
|
const struct a2dp_codec_audio_info *info,
|
||||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||||
{
|
{
|
||||||
a2dp_aac_t conf;
|
a2dp_aac_t conf;
|
||||||
int freq;
|
int i;
|
||||||
bool freq_found;
|
|
||||||
|
|
||||||
if (caps_size < sizeof(conf))
|
if (caps_size < sizeof(conf))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -142,24 +146,21 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
||||||
else
|
else
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
|
||||||
freq = AAC_GET_FREQUENCY(conf);
|
if ((i = a2dp_codec_select_config(aac_frequencies,
|
||||||
freq_found = false;
|
SPA_N_ELEMENTS(aac_frequencies),
|
||||||
for (size_t i = 0; i < SPA_N_ELEMENTS(aac_frequencies); i++) {
|
AAC_GET_FREQUENCY(conf),
|
||||||
if (freq & aac_frequencies[i].config) {
|
info ? info->rate : A2DP_CODEC_DEFAULT_RATE
|
||||||
AAC_SET_FREQUENCY(conf, aac_frequencies[i].config);
|
)) < 0)
|
||||||
freq_found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!freq_found)
|
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
AAC_SET_FREQUENCY(conf, aac_frequencies[i].config);
|
||||||
|
|
||||||
if (conf.channels & AAC_CHANNELS_2)
|
if ((i = a2dp_codec_select_config(aac_channel_modes,
|
||||||
conf.channels = AAC_CHANNELS_2;
|
SPA_N_ELEMENTS(aac_channel_modes),
|
||||||
else if (conf.channels & AAC_CHANNELS_1)
|
conf.channels,
|
||||||
conf.channels = AAC_CHANNELS_1;
|
info ? info->channels : A2DP_CODEC_DEFAULT_CHANNELS
|
||||||
else
|
)) < 0)
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
conf.channels = aac_channel_modes[i].config;
|
||||||
|
|
||||||
AAC_SET_BITRATE(conf, get_valid_aac_bitrate(&conf));
|
AAC_SET_BITRATE(conf, get_valid_aac_bitrate(&conf));
|
||||||
|
|
||||||
|
|
@ -200,8 +201,8 @@ static int codec_enum_config(const struct a2dp_codec *codec,
|
||||||
for (size_t j = 0; j < SPA_N_ELEMENTS(aac_frequencies); j++) {
|
for (size_t j = 0; j < SPA_N_ELEMENTS(aac_frequencies); j++) {
|
||||||
if (AAC_GET_FREQUENCY(conf) & aac_frequencies[j].config) {
|
if (AAC_GET_FREQUENCY(conf) & aac_frequencies[j].config) {
|
||||||
if (i++ == 0)
|
if (i++ == 0)
|
||||||
spa_pod_builder_int(b, aac_frequencies[j].freq);
|
spa_pod_builder_int(b, aac_frequencies[j].value);
|
||||||
spa_pod_builder_int(b, aac_frequencies[j].freq);
|
spa_pod_builder_int(b, aac_frequencies[j].value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
|
|
|
||||||
|
|
@ -76,11 +76,21 @@ static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags,
|
||||||
return actual_conf_size;
|
return actual_conf_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct a2dp_codec_config
|
||||||
|
aptx_frequencies[] = {
|
||||||
|
{ APTX_SAMPLING_FREQ_48000, 48000, 3 },
|
||||||
|
{ APTX_SAMPLING_FREQ_44100, 44100, 2 },
|
||||||
|
{ APTX_SAMPLING_FREQ_32000, 32000, 1 },
|
||||||
|
{ APTX_SAMPLING_FREQ_16000, 16000, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
||||||
const void *caps, size_t caps_size,
|
const void *caps, size_t caps_size,
|
||||||
|
const struct a2dp_codec_audio_info *info,
|
||||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||||
{
|
{
|
||||||
a2dp_aptx_t conf;
|
a2dp_aptx_t conf;
|
||||||
|
int i;
|
||||||
size_t actual_conf_size = codec_get_caps_size(codec);
|
size_t actual_conf_size = codec_get_caps_size(codec);
|
||||||
|
|
||||||
if (caps_size < sizeof(conf) || actual_conf_size < sizeof(conf))
|
if (caps_size < sizeof(conf) || actual_conf_size < sizeof(conf))
|
||||||
|
|
@ -92,16 +102,13 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
||||||
codec->vendor.codec_id != conf.info.codec_id)
|
codec->vendor.codec_id != conf.info.codec_id)
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
|
||||||
if (conf.frequency & APTX_SAMPLING_FREQ_48000)
|
if ((i = a2dp_codec_select_config(aptx_frequencies,
|
||||||
conf.frequency = APTX_SAMPLING_FREQ_48000;
|
SPA_N_ELEMENTS(aptx_frequencies),
|
||||||
else if (conf.frequency & APTX_SAMPLING_FREQ_44100)
|
conf.frequency,
|
||||||
conf.frequency = APTX_SAMPLING_FREQ_44100;
|
info ? info->rate : A2DP_CODEC_DEFAULT_RATE
|
||||||
else if (conf.frequency & APTX_SAMPLING_FREQ_32000)
|
)) < 0)
|
||||||
conf.frequency = APTX_SAMPLING_FREQ_32000;
|
|
||||||
else if (conf.frequency & APTX_SAMPLING_FREQ_16000)
|
|
||||||
conf.frequency = APTX_SAMPLING_FREQ_16000;
|
|
||||||
else
|
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
conf.frequency = aptx_frequencies[i].config;
|
||||||
|
|
||||||
if (conf.channel_mode & APTX_CHANNEL_MODE_STEREO)
|
if (conf.channel_mode & APTX_CHANNEL_MODE_STEREO)
|
||||||
conf.channel_mode = APTX_CHANNEL_MODE_STEREO;
|
conf.channel_mode = APTX_CHANNEL_MODE_STEREO;
|
||||||
|
|
|
||||||
|
|
@ -95,11 +95,28 @@ static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags, uint8
|
||||||
return sizeof(a2dp_ldac);
|
return sizeof(a2dp_ldac);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct a2dp_codec_config
|
||||||
|
ldac_frequencies[] = {
|
||||||
|
{ LDACBT_SAMPLING_FREQ_044100, 44100, 3 },
|
||||||
|
{ LDACBT_SAMPLING_FREQ_048000, 48000, 2 },
|
||||||
|
{ LDACBT_SAMPLING_FREQ_088200, 88200, 1 },
|
||||||
|
{ LDACBT_SAMPLING_FREQ_096000, 96000, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct a2dp_codec_config
|
||||||
|
ldac_channel_modes[] = {
|
||||||
|
{ LDACBT_CHANNEL_MODE_STEREO, 2, 2 },
|
||||||
|
{ LDACBT_CHANNEL_MODE_DUAL_CHANNEL, 2, 1 },
|
||||||
|
{ LDACBT_CHANNEL_MODE_MONO, 1, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
||||||
const void *caps, size_t caps_size,
|
const void *caps, size_t caps_size,
|
||||||
|
const struct a2dp_codec_audio_info *info,
|
||||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||||
{
|
{
|
||||||
a2dp_ldac_t conf;
|
a2dp_ldac_t conf;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (caps_size < sizeof(conf))
|
if (caps_size < sizeof(conf))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -110,25 +127,21 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
||||||
codec->vendor.codec_id != conf.info.codec_id)
|
codec->vendor.codec_id != conf.info.codec_id)
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
|
||||||
if (conf.frequency & LDACBT_SAMPLING_FREQ_044100)
|
if ((i = a2dp_codec_select_config(ldac_frequencies,
|
||||||
conf.frequency = LDACBT_SAMPLING_FREQ_044100;
|
SPA_N_ELEMENTS(ldac_frequencies),
|
||||||
else if (conf.frequency & LDACBT_SAMPLING_FREQ_048000)
|
conf.frequency,
|
||||||
conf.frequency = LDACBT_SAMPLING_FREQ_048000;
|
info ? info->rate : A2DP_CODEC_DEFAULT_RATE
|
||||||
else if (conf.frequency & LDACBT_SAMPLING_FREQ_088200)
|
)) < 0)
|
||||||
conf.frequency = LDACBT_SAMPLING_FREQ_088200;
|
|
||||||
else if (conf.frequency & LDACBT_SAMPLING_FREQ_096000)
|
|
||||||
conf.frequency = LDACBT_SAMPLING_FREQ_096000;
|
|
||||||
else
|
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
conf.frequency = ldac_frequencies[i].config;
|
||||||
|
|
||||||
if (conf.channel_mode & LDACBT_CHANNEL_MODE_STEREO)
|
if ((i = a2dp_codec_select_config(ldac_channel_modes,
|
||||||
conf.channel_mode = LDACBT_CHANNEL_MODE_STEREO;
|
SPA_N_ELEMENTS(ldac_channel_modes),
|
||||||
else if (conf.channel_mode & LDACBT_CHANNEL_MODE_DUAL_CHANNEL)
|
conf.channel_mode,
|
||||||
conf.channel_mode = LDACBT_CHANNEL_MODE_DUAL_CHANNEL;
|
info ? info->channels : A2DP_CODEC_DEFAULT_CHANNELS
|
||||||
else if (conf.channel_mode & LDACBT_CHANNEL_MODE_MONO)
|
)) < 0)
|
||||||
conf.channel_mode = LDACBT_CHANNEL_MODE_MONO;
|
|
||||||
else
|
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
conf.channel_mode = ldac_channel_modes[i].config;
|
||||||
|
|
||||||
memcpy(config, &conf, sizeof(conf));
|
memcpy(config, &conf, sizeof(conf));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,14 +119,48 @@ static uint8_t default_bitpool(uint8_t freq, uint8_t mode, bool xq)
|
||||||
return xq ? 86 : 64;
|
return xq ? 86 : 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct a2dp_codec_config
|
||||||
|
sbc_frequencies[] = {
|
||||||
|
{ SBC_SAMPLING_FREQ_48000, 48000, 3 },
|
||||||
|
{ SBC_SAMPLING_FREQ_44100, 44100, 2 },
|
||||||
|
{ SBC_SAMPLING_FREQ_32000, 32000, 1 },
|
||||||
|
{ SBC_SAMPLING_FREQ_16000, 16000, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct a2dp_codec_config
|
||||||
|
sbc_xq_frequencies[] = {
|
||||||
|
{ SBC_SAMPLING_FREQ_44100, 44100, 1 },
|
||||||
|
{ SBC_SAMPLING_FREQ_48000, 48000, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct a2dp_codec_config
|
||||||
|
sbc_channel_modes[] = {
|
||||||
|
{ SBC_CHANNEL_MODE_JOINT_STEREO, 2, 3 },
|
||||||
|
{ SBC_CHANNEL_MODE_STEREO, 2, 2 },
|
||||||
|
{ SBC_CHANNEL_MODE_DUAL_CHANNEL, 2, 1 },
|
||||||
|
{ SBC_CHANNEL_MODE_MONO, 1, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct a2dp_codec_config
|
||||||
|
sbc_xq_channel_modes[] = {
|
||||||
|
{ SBC_CHANNEL_MODE_DUAL_CHANNEL, 2, 2 },
|
||||||
|
{ SBC_CHANNEL_MODE_JOINT_STEREO, 2, 1 },
|
||||||
|
{ SBC_CHANNEL_MODE_STEREO, 2, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
||||||
const void *caps, size_t caps_size,
|
const void *caps, size_t caps_size,
|
||||||
|
const struct a2dp_codec_audio_info *info,
|
||||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||||
{
|
{
|
||||||
a2dp_sbc_t conf;
|
a2dp_sbc_t conf;
|
||||||
int bitpool;
|
int bitpool, i;
|
||||||
|
size_t n;
|
||||||
|
struct a2dp_codec_config * configs;
|
||||||
bool xq = false;
|
bool xq = false;
|
||||||
|
|
||||||
|
|
||||||
if (caps_size < sizeof(conf))
|
if (caps_size < sizeof(conf))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
|
@ -135,44 +169,30 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
||||||
memcpy(&conf, caps, sizeof(conf));
|
memcpy(&conf, caps, sizeof(conf));
|
||||||
|
|
||||||
if (xq) {
|
if (xq) {
|
||||||
if (conf.frequency & SBC_SAMPLING_FREQ_44100)
|
configs = sbc_xq_frequencies;
|
||||||
conf.frequency = SBC_SAMPLING_FREQ_44100;
|
n = SPA_N_ELEMENTS(sbc_xq_frequencies);
|
||||||
else if (conf.frequency & SBC_SAMPLING_FREQ_48000)
|
} else {
|
||||||
conf.frequency = SBC_SAMPLING_FREQ_48000;
|
configs = sbc_frequencies;
|
||||||
else
|
n = SPA_N_ELEMENTS(sbc_frequencies);
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
}
|
||||||
else if (conf.frequency & SBC_SAMPLING_FREQ_48000)
|
if ((i = a2dp_codec_select_config(configs, n, conf.frequency,
|
||||||
conf.frequency = SBC_SAMPLING_FREQ_48000;
|
info ? info->rate : A2DP_CODEC_DEFAULT_RATE
|
||||||
else if (conf.frequency & SBC_SAMPLING_FREQ_44100)
|
)) < 0)
|
||||||
conf.frequency = SBC_SAMPLING_FREQ_44100;
|
|
||||||
else if (conf.frequency & SBC_SAMPLING_FREQ_32000)
|
|
||||||
conf.frequency = SBC_SAMPLING_FREQ_32000;
|
|
||||||
else if (conf.frequency & SBC_SAMPLING_FREQ_16000)
|
|
||||||
conf.frequency = SBC_SAMPLING_FREQ_16000;
|
|
||||||
else
|
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
conf.frequency = configs[i].config;
|
||||||
|
|
||||||
if (xq) {
|
if (xq) {
|
||||||
if (conf.channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
|
configs = sbc_xq_channel_modes;
|
||||||
conf.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
|
n = SPA_N_ELEMENTS(sbc_xq_channel_modes);
|
||||||
else if (conf.channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
|
} else {
|
||||||
conf.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
|
configs = sbc_channel_modes;
|
||||||
else if (conf.channel_mode & SBC_CHANNEL_MODE_STEREO)
|
n = SPA_N_ELEMENTS(sbc_channel_modes);
|
||||||
conf.channel_mode = SBC_CHANNEL_MODE_STEREO;
|
|
||||||
else
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
}
|
||||||
else if (conf.channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
|
if ((i = a2dp_codec_select_config(configs, n, conf.channel_mode,
|
||||||
conf.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
|
info ? info->channels : A2DP_CODEC_DEFAULT_CHANNELS
|
||||||
else if (conf.channel_mode & SBC_CHANNEL_MODE_STEREO)
|
)) < 0)
|
||||||
conf.channel_mode = SBC_CHANNEL_MODE_STEREO;
|
|
||||||
else if (conf.channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
|
|
||||||
conf.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
|
|
||||||
else if (conf.channel_mode & SBC_CHANNEL_MODE_MONO)
|
|
||||||
conf.channel_mode = SBC_CHANNEL_MODE_MONO;
|
|
||||||
else
|
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
conf.channel_mode = configs[i].config;
|
||||||
|
|
||||||
if (conf.block_length & SBC_BLOCK_LENGTH_16)
|
if (conf.block_length & SBC_BLOCK_LENGTH_16)
|
||||||
conf.block_length = SBC_BLOCK_LENGTH_16;
|
conf.block_length = SBC_BLOCK_LENGTH_16;
|
||||||
|
|
@ -209,7 +229,7 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int codec_caps_preference_cmp(const struct a2dp_codec *codec, const void *caps1, size_t caps1_size,
|
static int codec_caps_preference_cmp(const struct a2dp_codec *codec, const void *caps1, size_t caps1_size,
|
||||||
const void *caps2, size_t caps2_size)
|
const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info)
|
||||||
{
|
{
|
||||||
a2dp_sbc_t conf1, conf2;
|
a2dp_sbc_t conf1, conf2;
|
||||||
a2dp_sbc_t *conf;
|
a2dp_sbc_t *conf;
|
||||||
|
|
@ -218,8 +238,8 @@ static int codec_caps_preference_cmp(const struct a2dp_codec *codec, const void
|
||||||
bool xq = (strcmp(codec->name, "sbc_xq") == 0);
|
bool xq = (strcmp(codec->name, "sbc_xq") == 0);
|
||||||
|
|
||||||
/* Order selected configurations by preference */
|
/* Order selected configurations by preference */
|
||||||
res1 = codec->select_config(codec, 0, caps1, caps1_size, NULL, (uint8_t *)&conf1);
|
res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1);
|
||||||
res2 = codec->select_config(codec, 0, caps2, caps2_size, NULL, (uint8_t *)&conf2);
|
res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2);
|
||||||
|
|
||||||
#define PREFER_EXPR(expr) \
|
#define PREFER_EXPR(expr) \
|
||||||
do { \
|
do { \
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,57 @@
|
||||||
|
|
||||||
#include "a2dp-codecs.h"
|
#include "a2dp-codecs.h"
|
||||||
|
|
||||||
bool a2dp_codec_check_caps(const struct a2dp_codec *codec, unsigned int codec_id, const void *caps, size_t caps_size)
|
int a2dp_codec_select_config(const struct a2dp_codec_config configs[], size_t n,
|
||||||
|
uint32_t cap, int preferred_value)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
int *scores, res;
|
||||||
|
unsigned int max_priority;
|
||||||
|
|
||||||
|
if (n == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
scores = calloc(n, sizeof(int));
|
||||||
|
if (scores == NULL)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
max_priority = configs[0].priority;
|
||||||
|
for (i = 1; i < n; ++i) {
|
||||||
|
if (configs[i].priority > max_priority)
|
||||||
|
max_priority = configs[i].priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
if (!(configs[i].config & cap)) {
|
||||||
|
scores[i] = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (configs[i].value == preferred_value)
|
||||||
|
scores[i] = 100 * (max_priority + 1);
|
||||||
|
else if (configs[i].value > preferred_value)
|
||||||
|
scores[i] = 10 * (max_priority + 1);
|
||||||
|
else
|
||||||
|
scores[i] = 1;
|
||||||
|
|
||||||
|
scores[i] *= configs[i].priority + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = 0;
|
||||||
|
for (i = 1; i < n; ++i) {
|
||||||
|
if (scores[i] > scores[res])
|
||||||
|
res = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scores[res] < 0)
|
||||||
|
res = -EINVAL;
|
||||||
|
|
||||||
|
free(scores);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool a2dp_codec_check_caps(const struct a2dp_codec *codec, unsigned int codec_id,
|
||||||
|
const void *caps, size_t caps_size,
|
||||||
|
const struct a2dp_codec_audio_info *info)
|
||||||
{
|
{
|
||||||
uint8_t config[A2DP_MAX_CAPS_SIZE];
|
uint8_t config[A2DP_MAX_CAPS_SIZE];
|
||||||
int res;
|
int res;
|
||||||
|
|
@ -21,7 +71,7 @@ bool a2dp_codec_check_caps(const struct a2dp_codec *codec, unsigned int codec_id
|
||||||
if (caps == NULL)
|
if (caps == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
res = codec->select_config(codec, 0, caps, caps_size, NULL, config);
|
res = codec->select_config(codec, 0, caps, caps_size, info, NULL, config);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -293,35 +293,13 @@ typedef struct {
|
||||||
#error "Unknown byte order"
|
#error "Unknown byte order"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline int a2dp_sbc_get_channels(a2dp_sbc_t *config)
|
#define A2DP_CODEC_DEFAULT_RATE 48000
|
||||||
{
|
#define A2DP_CODEC_DEFAULT_CHANNELS 2
|
||||||
switch (config->channel_mode) {
|
|
||||||
case SBC_CHANNEL_MODE_MONO:
|
|
||||||
return 1;
|
|
||||||
case SBC_CHANNEL_MODE_DUAL_CHANNEL:
|
|
||||||
case SBC_CHANNEL_MODE_STEREO:
|
|
||||||
case SBC_CHANNEL_MODE_JOINT_STEREO:
|
|
||||||
return 2;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int a2dp_sbc_get_frequency(a2dp_sbc_t *config)
|
struct a2dp_codec_audio_info {
|
||||||
{
|
uint32_t rate;
|
||||||
switch (config->frequency) {
|
uint32_t channels;
|
||||||
case SBC_SAMPLING_FREQ_16000:
|
};
|
||||||
return 16000;
|
|
||||||
case SBC_SAMPLING_FREQ_32000:
|
|
||||||
return 32000;
|
|
||||||
case SBC_SAMPLING_FREQ_44100:
|
|
||||||
return 44100;
|
|
||||||
case SBC_SAMPLING_FREQ_48000:
|
|
||||||
return 48000;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct a2dp_codec_handle;
|
struct a2dp_codec_handle;
|
||||||
|
|
||||||
|
|
@ -342,6 +320,7 @@ struct a2dp_codec {
|
||||||
uint8_t caps[A2DP_MAX_CAPS_SIZE]);
|
uint8_t caps[A2DP_MAX_CAPS_SIZE]);
|
||||||
int (*select_config) (const struct a2dp_codec *codec, uint32_t flags,
|
int (*select_config) (const struct a2dp_codec *codec, uint32_t flags,
|
||||||
const void *caps, size_t caps_size,
|
const void *caps, size_t caps_size,
|
||||||
|
const struct a2dp_codec_audio_info *info,
|
||||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]);
|
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]);
|
||||||
int (*enum_config) (const struct a2dp_codec *codec,
|
int (*enum_config) (const struct a2dp_codec *codec,
|
||||||
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
|
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
|
||||||
|
|
@ -356,7 +335,7 @@ struct a2dp_codec {
|
||||||
* otherwise not checked beforehand.
|
* otherwise not checked beforehand.
|
||||||
*/
|
*/
|
||||||
int (*caps_preference_cmp) (const struct a2dp_codec *codec, const void *caps1, size_t caps1_size,
|
int (*caps_preference_cmp) (const struct a2dp_codec *codec, const void *caps1, size_t caps1_size,
|
||||||
const void *caps2, size_t caps2_size);
|
const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info);
|
||||||
|
|
||||||
void *(*init_props) (const struct a2dp_codec *codec, const struct spa_dict *settings);
|
void *(*init_props) (const struct a2dp_codec *codec, const struct spa_dict *settings);
|
||||||
void (*clear_props) (void *);
|
void (*clear_props) (void *);
|
||||||
|
|
@ -394,6 +373,16 @@ struct a2dp_codec {
|
||||||
|
|
||||||
extern const struct a2dp_codec **a2dp_codecs;
|
extern const struct a2dp_codec **a2dp_codecs;
|
||||||
|
|
||||||
bool a2dp_codec_check_caps(const struct a2dp_codec *codec, unsigned int codec_id, const void *caps, size_t caps_size);
|
struct a2dp_codec_config {
|
||||||
|
uint32_t config;
|
||||||
|
int value;
|
||||||
|
unsigned int priority;
|
||||||
|
};
|
||||||
|
|
||||||
|
int a2dp_codec_select_config(const struct a2dp_codec_config configs[], size_t n,
|
||||||
|
uint32_t cap, int preferred_value);
|
||||||
|
|
||||||
|
bool a2dp_codec_check_caps(const struct a2dp_codec *codec, unsigned int codec_id,
|
||||||
|
const void *caps, size_t caps_size, const struct a2dp_codec_audio_info *info);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,9 @@ struct spa_bt_monitor {
|
||||||
unsigned int backend_native_registered:1;
|
unsigned int backend_native_registered:1;
|
||||||
unsigned int backend_ofono_registered:1;
|
unsigned int backend_ofono_registered:1;
|
||||||
unsigned int backend_hsphfpd_registered:1;
|
unsigned int backend_hsphfpd_registered:1;
|
||||||
|
|
||||||
|
/* A reference audio info for A2DP codec configuration. */
|
||||||
|
struct a2dp_codec_audio_info default_audio_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Stream endpoints owned by BlueZ for each device */
|
/* Stream endpoints owned by BlueZ for each device */
|
||||||
|
|
@ -468,7 +471,7 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu
|
||||||
* This causes inconsistency with SelectConfiguration() triggered
|
* This causes inconsistency with SelectConfiguration() triggered
|
||||||
* by codec switching.
|
* by codec switching.
|
||||||
*/
|
*/
|
||||||
res = codec->select_config(codec, 0, cap, size, NULL, config);
|
res = codec->select_config(codec, 0, cap, size, &monitor->default_audio_info, NULL, config);
|
||||||
else
|
else
|
||||||
res = -ENOTSUP;
|
res = -ENOTSUP;
|
||||||
|
|
||||||
|
|
@ -1233,7 +1236,8 @@ bool spa_bt_device_supports_a2dp_codec(struct spa_bt_device *device, const struc
|
||||||
}
|
}
|
||||||
|
|
||||||
spa_list_for_each(ep, &device->remote_endpoint_list, device_link) {
|
spa_list_for_each(ep, &device->remote_endpoint_list, device_link) {
|
||||||
if (a2dp_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len))
|
if (a2dp_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len,
|
||||||
|
&ep->monitor->default_audio_info))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2200,7 +2204,9 @@ static bool a2dp_codec_switch_process_current(struct spa_bt_a2dp_codec_switch *s
|
||||||
goto next;
|
goto next;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = codec->select_config(codec, 0, ep->capabilities, ep->capabilities_len, sw->device->settings, config);
|
res = codec->select_config(codec, 0, ep->capabilities, ep->capabilities_len,
|
||||||
|
&sw->device->monitor->default_audio_info,
|
||||||
|
sw->device->settings, config);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
spa_log_debug(sw->device->monitor->log, NAME": a2dp codec switch %p: incompatible capabilities (%d), try next",
|
spa_log_debug(sw->device->monitor->log, NAME": a2dp codec switch %p: incompatible capabilities (%d), try next",
|
||||||
sw, res);
|
sw, res);
|
||||||
|
|
@ -2364,7 +2370,7 @@ static int a2dp_codec_switch_cmp(const void *a, const void *b)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return codec->caps_preference_cmp(codec, ep1->capabilities, ep1->capabilities_len,
|
return codec->caps_preference_cmp(codec, ep1->capabilities, ep1->capabilities_len,
|
||||||
ep2->capabilities, ep2->capabilities_len);
|
ep2->capabilities, ep2->capabilities_len, &sw->device->monitor->default_audio_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 */
|
||||||
|
|
@ -3860,13 +3866,25 @@ impl_init(const struct spa_handle_factory *factory,
|
||||||
if ((res = parse_codec_array(this, info)) < 0)
|
if ((res = parse_codec_array(this, info)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
this->default_audio_info.rate = A2DP_CODEC_DEFAULT_RATE;
|
||||||
|
this->default_audio_info.channels = A2DP_CODEC_DEFAULT_CHANNELS;
|
||||||
|
|
||||||
if (info) {
|
if (info) {
|
||||||
const char *str;
|
const char *str;
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
if ((str = spa_dict_lookup(info, "api.bluez5.connection-info")) != NULL &&
|
if ((str = spa_dict_lookup(info, "api.bluez5.connection-info")) != NULL &&
|
||||||
(strcmp(str, "true") == 0 || atoi(str)))
|
(strcmp(str, "true") == 0 || atoi(str)))
|
||||||
this->connection_info_supported = true;
|
this->connection_info_supported = true;
|
||||||
|
|
||||||
|
if ((str = spa_dict_lookup(info, "bluez5.default.rate")) != NULL &&
|
||||||
|
(tmp = atoi(str)) > 0)
|
||||||
|
this->default_audio_info.rate = tmp;
|
||||||
|
|
||||||
|
if ((str = spa_dict_lookup(info, "bluez5.default.channels")) != NULL &&
|
||||||
|
((tmp = atoi(str)) > 0))
|
||||||
|
this->default_audio_info.channels = tmp;
|
||||||
|
|
||||||
if ((str = spa_dict_lookup(info, "bluez5.sbc-xq-support")) != NULL &&
|
if ((str = spa_dict_lookup(info, "bluez5.sbc-xq-support")) != NULL &&
|
||||||
(strcmp(str, "true") == 0 || atoi(str)))
|
(strcmp(str, "true") == 0 || atoi(str)))
|
||||||
this->enable_sbc_xq = true;
|
this->enable_sbc_xq = true;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,10 @@ properties = {
|
||||||
|
|
||||||
# Enabled A2DP codecs (default: all).
|
# Enabled A2DP codecs (default: all).
|
||||||
#bluez5.codecs = [ sbc aac ldac aptx aptx_hd ]
|
#bluez5.codecs = [ sbc aac ldac aptx aptx_hd ]
|
||||||
|
|
||||||
|
# Properties for the A2DP codec configuration
|
||||||
|
#bluez5.default.rate = 48000
|
||||||
|
#bluez5.default.channels = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
rules = [
|
rules = [
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue