From cde8f9261c72da1fe6eae7b7ed3b78b722e14f47 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 17 Jan 2024 00:08:07 +0200 Subject: [PATCH] bluez5: lc3: pick qos values from BAP spec tables Don't believe QoS values recommended by the device, which may be suboptimal. Instead, pick the values from the BAP v1.0.1 Table 5.2. Link: https://github.com/bluez/bluez/issues/713 --- spa/plugins/bluez5/bap-codec-lc3.c | 241 +++++++++++++++++------------ 1 file changed, 139 insertions(+), 102 deletions(-) diff --git a/spa/plugins/bluez5/bap-codec-lc3.c b/spa/plugins/bluez5/bap-codec-lc3.c index ac6526ba6..fe36e1b75 100644 --- a/spa/plugins/bluez5/bap-codec-lc3.c +++ b/spa/plugins/bluez5/bap-codec-lc3.c @@ -48,6 +48,17 @@ struct pac_data { bool duplex; }; +struct bap_qos { + uint8_t rate; + uint8_t frame_duration; + bool framing; + uint16_t framelen; + uint8_t retransmission; + uint16_t latency; + uint32_t delay; + unsigned int priority; +}; + typedef struct { uint8_t rate; uint8_t frame_duration; @@ -92,6 +103,73 @@ static const struct { { BAP_CHANNEL_RS, SPA_AUDIO_CHANNEL_SR }, /* is it the right mapping? */ }; + +#define BAP_QOS(rate_, duration_, framing_, framelen_, rtn_, latency_, delay_, priority_) \ + ((struct bap_qos){ .rate = (rate_), .frame_duration = (duration_), .framing = (framing_), \ + .framelen = (framelen_), .retransmission = (rtn_), .latency = (latency_), \ + .delay = (delay_), .priority = (priority_) }) + +static const struct bap_qos bap_qos_configs[] = { + /* Priority: low-latency > high-reliability, 7.5ms > 10ms, + * bigger frequency and sdu better */ + + /* BAP v1.0.1 Table 5.2; low-latency */ + BAP_QOS(LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 2, 8, 40000, 30), /* 8_1_1 */ + BAP_QOS(LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 2, 10, 40000, 20), /* 8_2_1 */ + BAP_QOS(LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 2, 8, 40000, 31), /* 16_1_1 */ + BAP_QOS(LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 2, 10, 40000, 21), /* 16_2_1 (mandatory) */ + BAP_QOS(LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 2, 8, 40000, 32), /* 24_1_1 */ + BAP_QOS(LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 2, 10, 40000, 22), /* 24_2_1 */ + BAP_QOS(LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 2, 8, 40000, 33), /* 32_1_1 */ + BAP_QOS(LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 2, 10, 40000, 23), /* 32_2_1 */ + BAP_QOS(LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 5, 24, 40000, 34), /* 441_1_1 */ + BAP_QOS(LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 5, 31, 40000, 24), /* 441_2_1 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 5, 15, 40000, 35), /* 48_1_1 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 5, 20, 40000, 25), /* 48_2_1 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 5, 15, 40000, 36), /* 48_3_1 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 5, 20, 40000, 26), /* 48_4_1 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 5, 15, 40000, 37), /* 48_5_1 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 5, 20, 40000, 27), /* 48_6_1 */ + + /* BAP v1.0.1 Table 5.2; high-reliability */ + BAP_QOS(LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 13, 75, 40000, 10), /* 8_1_2 */ + BAP_QOS(LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 13, 95, 40000, 0), /* 8_2_2 */ + BAP_QOS(LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 13, 75, 40000, 11), /* 16_1_2 */ + BAP_QOS(LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 13, 95, 40000, 1), /* 16_2_2 */ + BAP_QOS(LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 13, 75, 40000, 12), /* 24_1_2 */ + BAP_QOS(LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 13, 95, 40000, 2), /* 24_2_2 */ + BAP_QOS(LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 13, 75, 40000, 13), /* 32_1_2 */ + BAP_QOS(LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 13, 95, 40000, 3), /* 32_2_2 */ + BAP_QOS(LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 14), /* 441_1_2 */ + BAP_QOS(LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 4), /* 441_2_2 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 15), /* 48_1_2 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 5), /* 48_2_2 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 16), /* 48_3_2 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 6), /* 48_4_2 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 17), /* 48_5_2 */ + BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 7), /* 48_6_2 */ +}; + +static unsigned int get_rate_mask(uint8_t rate) { + switch (rate) { + case LC3_CONFIG_FREQ_8KHZ: return LC3_FREQ_8KHZ; + case LC3_CONFIG_FREQ_16KHZ: return LC3_FREQ_16KHZ; + case LC3_CONFIG_FREQ_24KHZ: return LC3_FREQ_24KHZ; + case LC3_CONFIG_FREQ_32KHZ: return LC3_FREQ_32KHZ; + case LC3_CONFIG_FREQ_44KHZ: return LC3_FREQ_44KHZ; + case LC3_CONFIG_FREQ_48KHZ: return LC3_FREQ_48KHZ; + } + return 0; +} + +static unsigned int get_duration_mask(uint8_t rate) { + switch (rate) { + case LC3_CONFIG_DURATION_7_5: return LC3_DUR_7_5; + case LC3_CONFIG_DURATION_10: return LC3_DUR_10; + } + return 0; +} + static int write_ltv(uint8_t *dest, uint8_t type, void* value, size_t len) { struct ltv *ltv = (struct ltv *)dest; @@ -216,6 +294,30 @@ static bool supports_channel_count(uint8_t mask, uint8_t count) return mask & (1u << (count - 1)); } +static const struct bap_qos *select_bap_qos(unsigned int rate_mask, unsigned int duration_mask, uint16_t framelen_min, uint16_t framelen_max) +{ + const struct bap_qos *best = NULL; + unsigned int best_priority = 0; + + SPA_FOR_EACH_ELEMENT_VAR(bap_qos_configs, c) { + if (c->priority < best_priority) + continue; + if (!(get_rate_mask(c->rate) & rate_mask)) + continue; + if (!(get_duration_mask(c->frame_duration) & duration_mask)) + continue; + if (c->framing) + continue; /* XXX: framing not supported */ + if (c->framelen < framelen_min || c->framelen > framelen_max) + continue; + + best = c; + best_priority = c->priority; + } + + return best; +} + static int select_channels(uint8_t channel_counts, uint32_t locations, uint32_t channel_allocation, uint32_t *allocation) { @@ -280,6 +382,9 @@ static bool select_config(bap_lc3_t *conf, const struct pac_data *pac, struct sp int max_frames = -1; uint8_t channel_counts = LC3_CHAN_1; /* Default: 1 channel (BAP v1.0.1 Sec 4.3.1) */ uint8_t max_channels = 0; + uint8_t duration_mask = 0; + uint16_t rate_mask = 0; + const struct bap_qos *bap_qos = NULL; unsigned int i; if (!data_size) @@ -289,8 +394,6 @@ static bool select_config(bap_lc3_t *conf, const struct pac_data *pac, struct sp conf->sink = pac->sink; conf->duplex = pac->duplex; - conf->frame_duration = 0xFF; - /* XXX: we always use one frame block */ conf->n_blks = 1; @@ -305,43 +408,15 @@ static bool select_config(bap_lc3_t *conf, const struct pac_data *pac, struct sp switch (ltv->type) { case LC3_TYPE_FREQ: spa_return_val_if_fail(ltv->len == 3, false); - { - uint16_t rate = ltv->value[0] + (ltv->value[1] << 8); - if (rate & LC3_FREQ_48KHZ) - conf->rate = LC3_CONFIG_FREQ_48KHZ; - else if (rate & LC3_FREQ_32KHZ) - conf->rate = LC3_CONFIG_FREQ_32KHZ; - else if (rate & LC3_FREQ_24KHZ) - conf->rate = LC3_CONFIG_FREQ_24KHZ; - else if (rate & LC3_FREQ_16KHZ) - conf->rate = LC3_CONFIG_FREQ_16KHZ; - else if (rate & LC3_FREQ_8KHZ) - conf->rate = LC3_CONFIG_FREQ_8KHZ; - else { - spa_debugc(debug_ctx, "unsupported rate: 0x%04x", rate); - return false; - } - } + rate_mask = ltv->value[0] + (ltv->value[1] << 8); break; case LC3_TYPE_DUR: spa_return_val_if_fail(ltv->len == 2, false); - { - uint8_t duration = ltv->value[0]; - if (duration & LC3_DUR_7_5) - conf->frame_duration = LC3_CONFIG_DURATION_7_5; - else if (duration & LC3_DUR_10) - conf->frame_duration = LC3_CONFIG_DURATION_10; - else { - spa_debugc(debug_ctx, "unsupported duration: 0x%02x", duration); - return false; - } - } + duration_mask = ltv->value[0]; break; case LC3_TYPE_CHAN: spa_return_val_if_fail(ltv->len == 2, false); - { - channel_counts = ltv->value[0]; - } + channel_counts = ltv->value[0]; break; case LC3_TYPE_FRAMELEN: spa_return_val_if_fail(ltv->len == 5, false); @@ -390,52 +465,20 @@ static bool select_config(bap_lc3_t *conf, const struct pac_data *pac, struct sp return false; } - if (conf->frame_duration == 0xFF || !conf->rate) { - spa_debugc(debug_ctx, "no frame duration or rate"); + /* + * Select supported rate + frame length combination + */ + bap_qos = select_bap_qos(rate_mask, duration_mask, framelen_min, framelen_max); + + if (!bap_qos) { + spa_debugc(debug_ctx, "no compatible configuration found, rate:0x%08x, duration:0x%08x frame:%u-%u", + rate_mask, duration_mask, framelen_min, framelen_max); return false; } - /* BAP v1.0.1 Table 5.2; high-reliability */ - switch (conf->rate) { - case LC3_CONFIG_FREQ_48KHZ: - if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) - conf->framelen = 117; /* 48_5_2 */ - else - conf->framelen = 120; /* 48_4_2 */ - break; - case LC3_CONFIG_FREQ_32KHZ: - if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) - conf->framelen = 60; /* 32_1_2 */ - else - conf->framelen = 80; /* 32_2_2 */ - break; - case LC3_CONFIG_FREQ_24KHZ: - if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) - conf->framelen = 45; /* 24_1_2 */ - else - conf->framelen = 60; /* 24_2_2 */ - break; - case LC3_CONFIG_FREQ_16KHZ: - if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) - conf->framelen = 30; /* 16_1_2 */ - else - conf->framelen = 40; /* 16_2_2 */ - break; - case LC3_CONFIG_FREQ_8KHZ: - if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) - conf->framelen = 26; /* 8_1_2 */ - else - conf->framelen = 30; /* 8_2_2 */ - break; - default: - spa_debugc(debug_ctx, "invalid rate"); - return false; - } - - if (conf->framelen < framelen_min || conf->framelen > framelen_max) { - spa_debugc(debug_ctx, "invalid framelen: %u %u", framelen_min, framelen_max); - return false; - } + conf->rate = bap_qos->rate; + conf->frame_duration = bap_qos->frame_duration; + conf->framelen = bap_qos->framelen; return true; } @@ -790,6 +833,7 @@ static int codec_get_qos(const struct media_codec *codec, const struct bap_endpoint_qos *endpoint_qos, struct bap_codec_qos *qos) { + const struct bap_qos *bap_qos; bap_lc3_t conf; spa_zero(*qos); @@ -797,6 +841,14 @@ static int codec_get_qos(const struct media_codec *codec, if (!parse_conf(&conf, config, config_size)) return -EINVAL; + bap_qos = select_bap_qos(get_rate_mask(conf.rate), get_duration_mask(conf.frame_duration), + conf.framelen, conf.framelen); + if (!bap_qos) { + /* shouldn't happen: select_config should pick existing one */ + spa_log_error(log, "no QoS settings found"); + return -EINVAL; + } + qos->framing = false; if (endpoint_qos->phy & 0x2) qos->phy = 0x2; @@ -804,42 +856,27 @@ static int codec_get_qos(const struct media_codec *codec, qos->phy = 0x1; else qos->phy = 0x2; + qos->sdu = conf.framelen * conf.n_blks * get_channel_count(conf.channels); qos->interval = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000); - qos->target_latency = BT_ISO_QOS_TARGET_LATENCY_RELIABILITY; + qos->target_latency = BT_ISO_QOS_TARGET_LATENCY_BALANCED; - /* Default values from BAP v1.0.1 Table 5.2; high-reliability */ - qos->delay = 40000U; - qos->retransmission = 13; - - switch (conf.rate) { - case LC3_CONFIG_FREQ_8KHZ: - case LC3_CONFIG_FREQ_16KHZ: - case LC3_CONFIG_FREQ_24KHZ: - case LC3_CONFIG_FREQ_32KHZ: - /* F_1_2, F_2_2 */ - qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 75 : 95); - break; - case LC3_CONFIG_FREQ_48KHZ: - /* 48_5_2, 48_4_2 */ - qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 75 : 100); - break; - default: - qos->latency = 100; - break; - } + qos->delay = bap_qos->delay; + qos->latency = bap_qos->latency; + qos->retransmission = bap_qos->retransmission; /* Clamp to ASE values (if known) */ - if (endpoint_qos->latency >= 0x0005 && endpoint_qos->latency <= 0x0FA0) - /* Values outside the range are RFU */ - qos->latency = endpoint_qos->latency; - if (endpoint_qos->retransmission) - qos->retransmission = endpoint_qos->retransmission; if (endpoint_qos->delay_min) qos->delay = SPA_MAX(qos->delay, endpoint_qos->delay_min); if (endpoint_qos->delay_max) qos->delay = SPA_MIN(qos->delay, endpoint_qos->delay_max); + /* + * We ignore endpoint suggested latency and RTN. On current devices + * these do not appear to be very useful numbers, so it's better + * to just pick one from the table in the spec. + */ + return 0; }