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:
Huang-Huang Bao 2021-05-03 15:40:13 +08:00 committed by Wim Taymans
parent 15239ff74d
commit 9384ba5500
8 changed files with 234 additions and 132 deletions

View file

@ -90,22 +90,26 @@ static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags,
return sizeof(a2dp_aac);
}
static struct {
int config;
int freq;
} aac_frequencies[] = {
{ AAC_SAMPLING_FREQ_48000, 48000 },
{ AAC_SAMPLING_FREQ_44100, 44100 },
{ AAC_SAMPLING_FREQ_96000, 96000 },
{ AAC_SAMPLING_FREQ_88200, 88200 },
{ AAC_SAMPLING_FREQ_64000, 64000 },
{ AAC_SAMPLING_FREQ_32000, 32000 },
{ AAC_SAMPLING_FREQ_24000, 24000 },
{ AAC_SAMPLING_FREQ_22050, 22050 },
{ AAC_SAMPLING_FREQ_16000, 16000 },
{ AAC_SAMPLING_FREQ_12000, 12000 },
{ AAC_SAMPLING_FREQ_11025, 11025 },
{ AAC_SAMPLING_FREQ_8000, 8000 },
static struct a2dp_codec_config
aac_frequencies[] = {
{ AAC_SAMPLING_FREQ_48000, 48000, 11 },
{ AAC_SAMPLING_FREQ_44100, 44100, 10 },
{ AAC_SAMPLING_FREQ_96000, 96000, 9 },
{ AAC_SAMPLING_FREQ_88200, 88200, 8 },
{ AAC_SAMPLING_FREQ_64000, 64000, 7 },
{ AAC_SAMPLING_FREQ_32000, 32000, 6 },
{ AAC_SAMPLING_FREQ_24000, 24000, 5 },
{ AAC_SAMPLING_FREQ_22050, 22050, 4 },
{ AAC_SAMPLING_FREQ_16000, 16000, 3 },
{ AAC_SAMPLING_FREQ_12000, 12000, 2 },
{ AAC_SAMPLING_FREQ_11025, 11025, 1 },
{ AAC_SAMPLING_FREQ_8000, 8000, 0 },
};
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)
@ -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,
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])
{
a2dp_aac_t conf;
int freq;
bool freq_found;
int i;
if (caps_size < sizeof(conf))
return -EINVAL;
@ -142,24 +146,21 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
else
return -ENOTSUP;
freq = AAC_GET_FREQUENCY(conf);
freq_found = false;
for (size_t i = 0; i < SPA_N_ELEMENTS(aac_frequencies); i++) {
if (freq & aac_frequencies[i].config) {
if ((i = a2dp_codec_select_config(aac_frequencies,
SPA_N_ELEMENTS(aac_frequencies),
AAC_GET_FREQUENCY(conf),
info ? info->rate : A2DP_CODEC_DEFAULT_RATE
)) < 0)
return -ENOTSUP;
AAC_SET_FREQUENCY(conf, aac_frequencies[i].config);
freq_found = true;
break;
}
}
if (!freq_found)
return -ENOTSUP;
if (conf.channels & AAC_CHANNELS_2)
conf.channels = AAC_CHANNELS_2;
else if (conf.channels & AAC_CHANNELS_1)
conf.channels = AAC_CHANNELS_1;
else
if ((i = a2dp_codec_select_config(aac_channel_modes,
SPA_N_ELEMENTS(aac_channel_modes),
conf.channels,
info ? info->channels : A2DP_CODEC_DEFAULT_CHANNELS
)) < 0)
return -ENOTSUP;
conf.channels = aac_channel_modes[i].config;
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++) {
if (AAC_GET_FREQUENCY(conf) & aac_frequencies[j].config) {
if (i++ == 0)
spa_pod_builder_int(b, aac_frequencies[j].freq);
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].value);
}
}
if (i == 0)

View file

@ -76,11 +76,21 @@ static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags,
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,
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])
{
a2dp_aptx_t conf;
int i;
size_t actual_conf_size = codec_get_caps_size(codec);
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)
return -ENOTSUP;
if (conf.frequency & APTX_SAMPLING_FREQ_48000)
conf.frequency = APTX_SAMPLING_FREQ_48000;
else if (conf.frequency & APTX_SAMPLING_FREQ_44100)
conf.frequency = APTX_SAMPLING_FREQ_44100;
else if (conf.frequency & APTX_SAMPLING_FREQ_32000)
conf.frequency = APTX_SAMPLING_FREQ_32000;
else if (conf.frequency & APTX_SAMPLING_FREQ_16000)
conf.frequency = APTX_SAMPLING_FREQ_16000;
else
if ((i = a2dp_codec_select_config(aptx_frequencies,
SPA_N_ELEMENTS(aptx_frequencies),
conf.frequency,
info ? info->rate : A2DP_CODEC_DEFAULT_RATE
)) < 0)
return -ENOTSUP;
conf.frequency = aptx_frequencies[i].config;
if (conf.channel_mode & APTX_CHANNEL_MODE_STEREO)
conf.channel_mode = APTX_CHANNEL_MODE_STEREO;

View file

@ -95,11 +95,28 @@ static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags, uint8
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,
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])
{
a2dp_ldac_t conf;
int i;
if (caps_size < sizeof(conf))
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)
return -ENOTSUP;
if (conf.frequency & LDACBT_SAMPLING_FREQ_044100)
conf.frequency = LDACBT_SAMPLING_FREQ_044100;
else if (conf.frequency & LDACBT_SAMPLING_FREQ_048000)
conf.frequency = LDACBT_SAMPLING_FREQ_048000;
else if (conf.frequency & LDACBT_SAMPLING_FREQ_088200)
conf.frequency = LDACBT_SAMPLING_FREQ_088200;
else if (conf.frequency & LDACBT_SAMPLING_FREQ_096000)
conf.frequency = LDACBT_SAMPLING_FREQ_096000;
else
if ((i = a2dp_codec_select_config(ldac_frequencies,
SPA_N_ELEMENTS(ldac_frequencies),
conf.frequency,
info ? info->rate : A2DP_CODEC_DEFAULT_RATE
)) < 0)
return -ENOTSUP;
conf.frequency = ldac_frequencies[i].config;
if (conf.channel_mode & LDACBT_CHANNEL_MODE_STEREO)
conf.channel_mode = LDACBT_CHANNEL_MODE_STEREO;
else if (conf.channel_mode & LDACBT_CHANNEL_MODE_DUAL_CHANNEL)
conf.channel_mode = LDACBT_CHANNEL_MODE_DUAL_CHANNEL;
else if (conf.channel_mode & LDACBT_CHANNEL_MODE_MONO)
conf.channel_mode = LDACBT_CHANNEL_MODE_MONO;
else
if ((i = a2dp_codec_select_config(ldac_channel_modes,
SPA_N_ELEMENTS(ldac_channel_modes),
conf.channel_mode,
info ? info->channels : A2DP_CODEC_DEFAULT_CHANNELS
)) < 0)
return -ENOTSUP;
conf.channel_mode = ldac_channel_modes[i].config;
memcpy(config, &conf, sizeof(conf));

View file

@ -119,14 +119,48 @@ static uint8_t default_bitpool(uint8_t freq, uint8_t mode, bool xq)
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,
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])
{
a2dp_sbc_t conf;
int bitpool;
int bitpool, i;
size_t n;
struct a2dp_codec_config * configs;
bool xq = false;
if (caps_size < sizeof(conf))
return -EINVAL;
@ -135,44 +169,30 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
memcpy(&conf, caps, sizeof(conf));
if (xq) {
if (conf.frequency & SBC_SAMPLING_FREQ_44100)
conf.frequency = SBC_SAMPLING_FREQ_44100;
else if (conf.frequency & SBC_SAMPLING_FREQ_48000)
conf.frequency = SBC_SAMPLING_FREQ_48000;
else
return -ENOTSUP;
configs = sbc_xq_frequencies;
n = SPA_N_ELEMENTS(sbc_xq_frequencies);
} else {
configs = sbc_frequencies;
n = SPA_N_ELEMENTS(sbc_frequencies);
}
else if (conf.frequency & SBC_SAMPLING_FREQ_48000)
conf.frequency = SBC_SAMPLING_FREQ_48000;
else if (conf.frequency & SBC_SAMPLING_FREQ_44100)
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
if ((i = a2dp_codec_select_config(configs, n, conf.frequency,
info ? info->rate : A2DP_CODEC_DEFAULT_RATE
)) < 0)
return -ENOTSUP;
conf.frequency = configs[i].config;
if (xq) {
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_JOINT_STEREO)
conf.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
else if (conf.channel_mode & SBC_CHANNEL_MODE_STEREO)
conf.channel_mode = SBC_CHANNEL_MODE_STEREO;
else
return -ENOTSUP;
configs = sbc_xq_channel_modes;
n = SPA_N_ELEMENTS(sbc_xq_channel_modes);
} else {
configs = sbc_channel_modes;
n = SPA_N_ELEMENTS(sbc_channel_modes);
}
else if (conf.channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
conf.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
else if (conf.channel_mode & SBC_CHANNEL_MODE_STEREO)
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
if ((i = a2dp_codec_select_config(configs, n, conf.channel_mode,
info ? info->channels : A2DP_CODEC_DEFAULT_CHANNELS
)) < 0)
return -ENOTSUP;
conf.channel_mode = configs[i].config;
if (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,
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 *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);
/* Order selected configurations by preference */
res1 = codec->select_config(codec, 0, caps1, caps1_size, NULL, (uint8_t *)&conf1);
res2 = codec->select_config(codec, 0, caps2, caps2_size, NULL, (uint8_t *)&conf2);
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);
#define PREFER_EXPR(expr) \
do { \

View file

@ -10,7 +10,57 @@
#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];
int res;
@ -21,7 +71,7 @@ bool a2dp_codec_check_caps(const struct a2dp_codec *codec, unsigned int codec_id
if (caps == NULL)
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)
return false;

View file

@ -293,35 +293,13 @@ typedef struct {
#error "Unknown byte order"
#endif
static inline int a2dp_sbc_get_channels(a2dp_sbc_t *config)
{
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;
}
}
#define A2DP_CODEC_DEFAULT_RATE 48000
#define A2DP_CODEC_DEFAULT_CHANNELS 2
static inline int a2dp_sbc_get_frequency(a2dp_sbc_t *config)
{
switch (config->frequency) {
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_audio_info {
uint32_t rate;
uint32_t channels;
};
struct a2dp_codec_handle;
@ -342,6 +320,7 @@ struct a2dp_codec {
uint8_t caps[A2DP_MAX_CAPS_SIZE]);
int (*select_config) (const struct a2dp_codec *codec, uint32_t flags,
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]);
int (*enum_config) (const struct a2dp_codec *codec,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
@ -356,7 +335,7 @@ struct a2dp_codec {
* otherwise not checked beforehand.
*/
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 (*clear_props) (void *);
@ -394,6 +373,16 @@ struct a2dp_codec {
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

View file

@ -91,6 +91,9 @@ struct spa_bt_monitor {
unsigned int backend_native_registered:1;
unsigned int backend_ofono_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 */
@ -468,7 +471,7 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu
* This causes inconsistency with SelectConfiguration() triggered
* 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
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) {
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;
}
@ -2200,7 +2204,9 @@ static bool a2dp_codec_switch_process_current(struct spa_bt_a2dp_codec_switch *s
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) {
spa_log_debug(sw->device->monitor->log, NAME": a2dp codec switch %p: incompatible capabilities (%d), try next",
sw, res);
@ -2364,7 +2370,7 @@ static int a2dp_codec_switch_cmp(const void *a, const void *b)
return -1;
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 */
@ -3860,13 +3866,25 @@ impl_init(const struct spa_handle_factory *factory,
if ((res = parse_codec_array(this, info)) < 0)
return res;
this->default_audio_info.rate = A2DP_CODEC_DEFAULT_RATE;
this->default_audio_info.channels = A2DP_CODEC_DEFAULT_CHANNELS;
if (info) {
const char *str;
uint32_t tmp;
if ((str = spa_dict_lookup(info, "api.bluez5.connection-info")) != NULL &&
(strcmp(str, "true") == 0 || atoi(str)))
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 &&
(strcmp(str, "true") == 0 || atoi(str)))
this->enable_sbc_xq = true;

View file

@ -22,6 +22,10 @@ properties = {
# Enabled A2DP codecs (default: all).
#bluez5.codecs = [ sbc aac ldac aptx aptx_hd ]
# Properties for the A2DP codec configuration
#bluez5.default.rate = 48000
#bluez5.default.channels = 2
}
rules = [