<div>Hi,</div><div> </div><div>v15 works, as expected : 90% of the time.</div><div> </div><div>As already discussed (for too long) : forcing bitpools for XQ modes is not a good idea, and doesn't work with all speakers.</div><div> </div><div>bitpool must always be negotiated : for standard "high quality" mode AND for xq MODES.</div><div> </div><div>My patch, available since months : already implement XQ this way (many too simple ?), and it never failed with any BT speaker or headphone : it provides XQ when negocieted bitpools are possible for the receiver : mostly always.</div><div> </div><div>JP</div><div> </div><div>15.04.2020, 21:07, "Pali Rohár" <pali.rohar@gmail.com>:</div><blockquote><p>Specify configuration for Low, Middle, High and eXtreme Quality of SBC<br />codec. SBC codec in eXtreme Quality has higher quality than aptX.<br /><br />Automatic Quality mode matches configuration of SBC codec which was used<br />before this change. Which means that it accept configuration between Low<br />and High quality.<br /><br />Current SBC code was extended to allow definitions of arbitrary<br />configuration variants of SBC codec parameters.<br /><br />SBC XQ testing is in following article:<br /><a href="http://soundexpert.org/articles/-/blogs/audio-quality-of-sbc-xq-bluetooth-audio-codec">http://soundexpert.org/articles/-/blogs/audio-quality-of-sbc-xq-bluetooth-audio-codec</a><br />---<br /> src/modules/bluetooth/a2dp-codec-sbc.c  | 655 +++++++++++++++++++-----<br /> src/modules/bluetooth/a2dp-codec-util.c |  18 +-<br /> 2 files changed, 534 insertions(+), 139 deletions(-)<br /><br />diff --git a/src/modules/bluetooth/a2dp-codec-sbc.c b/src/modules/bluetooth/a2dp-codec-sbc.c<br />index ba47fa066..f275586ef 100644<br />--- a/src/modules/bluetooth/a2dp-codec-sbc.c<br />+++ b/src/modules/bluetooth/a2dp-codec-sbc.c<br />@@ -36,8 +36,71 @@<br /> #include "a2dp-codec-api.h"<br /> #include "rtp.h"<br /><br />-#define SBC_BITPOOL_DEC_LIMIT 32<br />-#define SBC_BITPOOL_DEC_STEP 5<br />+/* Below are capabilities tables for different qualities. Order of capabilities in tables are from the most preferred to the least preferred. */<br />+<br />+#define FIXED_SBC_CAPS(mode, freq, bitpool) { .channel_mode = (mode), .frequency = (freq), .min_bitpool = (bitpool), .max_bitpool = (bitpool), .allocation_method = SBC_ALLOCATION_LOUDNESS, .subbands = SBC_SUBBANDS_8, .block_length = SBC_BLOCK_LENGTH_16 }<br />+<br />+/* SBC Low Quality, Joint Stereo is same as FastStream's SBC codec configuration, Mono was calculated to match Joint Stereo */<br />+static const a2dp_sbc_t sbc_lq_caps_table[] = {<!-- --><br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_44100, 29), /* 195.7 kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_48000, 29), /* 213   kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO,         SBC_SAMPLING_FREQ_44100, 15), /* 104.7 kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO,         SBC_SAMPLING_FREQ_48000, 15), /* 114   kbps */<br />+};<br />+<br />+/* SBC Middle Quality, based on A2DP spec: Recommended sets of SBC parameters */<br />+static const a2dp_sbc_t sbc_mq_caps_table[] = {<!-- --><br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_44100, SBC_BITPOOL_MQ_JOINT_STEREO_44100), /* bitpool = 35, 228.8 kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_48000, SBC_BITPOOL_MQ_JOINT_STEREO_48000), /* bitpool = 33, 237   kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO,         SBC_SAMPLING_FREQ_44100, SBC_BITPOOL_MQ_MONO_44100),         /* bitpool = 19, 126.8 kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO,         SBC_SAMPLING_FREQ_48000, SBC_BITPOOL_MQ_MONO_48000),         /* bitpool = 18, 132   kbps */<br />+};<br />+<br />+/* SBC High Quality, based on A2DP spec: Recommended sets of SBC parameters */<br />+static const a2dp_sbc_t sbc_hq_caps_table[] = {<!-- --><br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_44100, SBC_BITPOOL_HQ_JOINT_STEREO_44100), /* bitpool = 53, 328   kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_48000, SBC_BITPOOL_HQ_JOINT_STEREO_48000), /* bitpool = 51, 345   kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO,         SBC_SAMPLING_FREQ_44100, SBC_BITPOOL_HQ_MONO_44100),         /* bitpool = 31, 192.9 kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO,         SBC_SAMPLING_FREQ_48000, SBC_BITPOOL_HQ_MONO_48000),         /* bitpool = 29, 210   kbps */<br />+};<br />+<br />+/* SBC eXtreme Quality, calculated to minimize wasted bytes for EDR-2 and to<br />+ * be below max possible 512 kbps. In most cases bluetooth headsets would<br />+ * support only sbc dual channel mode for 2 channels as they have limited<br />+ * maximal bitpool value to 53. We need to define it in two tables to disallow<br />+ * invalid combination of joint stereo with bitpool 38 which is not XQ. */<br />+static const a2dp_sbc_t sbc_xq1_caps_table[] = {<!-- --><br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_44100, 76), /* 454.8 kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_48000, 76), /* 495   kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_STEREO,       SBC_SAMPLING_FREQ_44100, 76), /* 452   kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_STEREO,       SBC_SAMPLING_FREQ_48000, 76), /* 492   kbps */<br />+};<br />+static const a2dp_sbc_t sbc_xq2_caps_table[] = {<!-- --><br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_DUAL_CHANNEL, SBC_SAMPLING_FREQ_44100, 38), /* 452   kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_DUAL_CHANNEL, SBC_SAMPLING_FREQ_48000, 38), /* 492   kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO,         SBC_SAMPLING_FREQ_44100, 37), /* 226   kbps */<br />+    FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO,         SBC_SAMPLING_FREQ_48000, 37), /* 246   kbps */<br />+};<br />+<br />+#undef FIXED_SBC_CAPS<br />+<br />+/* SBC Auto Quality, only one row which allow any possible configuration up to common High Quality */<br />+/* We need to ensure that bitrate is below max possible 512 kbps, therefore limit configuration to High Quality */<br />+static const a2dp_sbc_t sbc_auto_caps_table[] = { {<!-- --><br />+    .channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO,<br />+    .frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,<br />+    .allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS,<br />+    .subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,<br />+    .block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,<br />+    .min_bitpool = SBC_MIN_BITPOOL,<br />+    .max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100,<br />+} };<br />+<br />+/* Bitpool limits and steps for reducing bitrate in Auto Quality mode */<br />+#define SBC_SEPARATE_BITPOOL_DEC_LIMIT 10<br />+#define SBC_COMBINED_BITPOOL_DEC_LIMIT 25<br />+#define SBC_SEPARATE_BITPOOL_DEC_STEP   2<br />+#define SBC_COMBINED_BITPOOL_DEC_STEP   4<br /><br /> struct sbc_info {<!-- --><br />     sbc_t sbc;                           /* Codec data */<br />@@ -53,30 +116,71 @@ struct sbc_info {<!-- --><br />     uint8_t max_bitpool;<br /> };<br /><br />-static bool can_accept_capabilities(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) {<!-- --><br />-    const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer;<br />+static bool are_capabilities_compatible(const a2dp_sbc_t *capabilities1, const a2dp_sbc_t *capabilities2) {<!-- --><br />+    if (!(capabilities1->channel_mode & capabilities2->channel_mode))<br />+        return false;<br /><br />-    if (capabilities_size != sizeof(*capabilities))<br />+    if (!(capabilities1->frequency & capabilities2->frequency))<br />         return false;<br /><br />-    if (!(capabilities->frequency & (SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000)))<br />+    if (!(capabilities1->allocation_method & capabilities2->allocation_method))<br />         return false;<br /><br />-    if (!(capabilities->channel_mode & (SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO)))<br />+    if (!(capabilities1->subbands & capabilities2->subbands))<br />         return false;<br /><br />-    if (!(capabilities->allocation_method & (SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS)))<br />+    if (!(capabilities1->block_length & capabilities2->block_length))<br />         return false;<br /><br />-    if (!(capabilities->subbands & (SBC_SUBBANDS_4 | SBC_SUBBANDS_8)))<br />+    if (capabilities1->min_bitpool > capabilities2->max_bitpool || capabilities2->min_bitpool > capabilities1->max_bitpool)<br />         return false;<br /><br />-    if (!(capabilities->block_length & (SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16)))<br />+    if (capabilities1->min_bitpool > capabilities1->max_bitpool || capabilities2->min_bitpool > capabilities2->max_bitpool)<br />         return false;<br /><br />     return true;<br /> }<br /><br />+static bool can_accept_capabilities_table(const uint8_t *capabilities_buffer, uint8_t capabilities_size, const a2dp_sbc_t capabilities_table[], unsigned capabilities_table_elements) {<!-- --><br />+    const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer;<br />+    unsigned i;<br />+<br />+    if (capabilities_size != sizeof(*capabilities))<br />+        return false;<br />+<br />+    for (i = 0; i < capabilities_table_elements; i++) {<!-- --><br />+        if (!are_capabilities_compatible(capabilities, &capabilities_table[i]))<br />+            continue;<br />+        return true;<br />+    }<br />+<br />+    return false;<br />+}<br />+<br />+static bool can_accept_capabilities_lq(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) {<!-- --><br />+    return can_accept_capabilities_table(capabilities_buffer, capabilities_size, sbc_lq_caps_table, PA_ELEMENTSOF(sbc_lq_caps_table));<br />+}<br />+<br />+static bool can_accept_capabilities_mq(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) {<!-- --><br />+    return can_accept_capabilities_table(capabilities_buffer, capabilities_size, sbc_mq_caps_table, PA_ELEMENTSOF(sbc_mq_caps_table));<br />+}<br />+<br />+static bool can_accept_capabilities_hq(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) {<!-- --><br />+    return can_accept_capabilities_table(capabilities_buffer, capabilities_size, sbc_hq_caps_table, PA_ELEMENTSOF(sbc_hq_caps_table));<br />+}<br />+<br />+static bool can_accept_capabilities_xq1(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) {<!-- --><br />+    return can_accept_capabilities_table(capabilities_buffer, capabilities_size, sbc_xq1_caps_table, PA_ELEMENTSOF(sbc_xq1_caps_table));<br />+}<br />+<br />+static bool can_accept_capabilities_xq2(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) {<!-- --><br />+    return can_accept_capabilities_table(capabilities_buffer, capabilities_size, sbc_xq2_caps_table, PA_ELEMENTSOF(sbc_xq2_caps_table));<br />+}<br />+<br />+static bool can_accept_capabilities(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) {<!-- --><br />+    return can_accept_capabilities_table(capabilities_buffer, capabilities_size, sbc_auto_caps_table, PA_ELEMENTSOF(sbc_auto_caps_table));<br />+}<br />+<br /> static int cmp_endpoints_by_channels(const a2dp_sbc_t *capabilities1, const a2dp_sbc_t *capabilities2, const pa_sample_spec *default_sample_spec, bool for_encoding) {<!-- --><br />     /* Prefer enpoint which number of channels is near to default sample channel number */<br />     if (default_sample_spec->channels < 2) {<!-- --><br />@@ -177,10 +281,10 @@ static int cmp_endpoints(const uint8_t *capabilities1_buffer, uint8_t capabiliti<br />     }<br /><br />     /* Calculate usable bitpool range compatible with both remote capabilities and capabilities from auto sbc quality */<br />-    max1_bitpool = PA_MIN(capabilities1->max_bitpool, SBC_BITPOOL_HQ_JOINT_STEREO_44100);<br />-    max2_bitpool = PA_MIN(capabilities2->max_bitpool, SBC_BITPOOL_HQ_JOINT_STEREO_44100);<br />-    min1_bitpool = PA_MAX(capabilities1->min_bitpool, SBC_MIN_BITPOOL);<br />-    min2_bitpool = PA_MAX(capabilities2->min_bitpool, SBC_MIN_BITPOOL);<br />+    max1_bitpool = PA_MIN(capabilities1->max_bitpool, sbc_auto_caps_table[0].max_bitpool);<br />+    max2_bitpool = PA_MIN(capabilities2->max_bitpool, sbc_auto_caps_table[0].max_bitpool);<br />+    min1_bitpool = PA_MAX(capabilities1->min_bitpool, sbc_auto_caps_table[0].min_bitpool);<br />+    min2_bitpool = PA_MAX(capabilities2->min_bitpool, sbc_auto_caps_table[0].min_bitpool);<br />     capabilities1_range = max1_bitpool - min1_bitpool;<br />     capabilities2_range = max2_bitpool - min2_bitpool;<br /><br />@@ -191,8 +295,8 @@ static int cmp_endpoints(const uint8_t *capabilities1_buffer, uint8_t capabiliti<br />         return 1;<br /><br />     /* Calculate unusable bitpool range which is not not compatible with both sides */<br />-    unusable1_range = PA_MAX(capabilities1->max_bitpool, SBC_BITPOOL_HQ_JOINT_STEREO_44100) - PA_MIN(capabilities1->min_bitpool, SBC_MIN_BITPOOL) - capabilities1_range;<br />-    unusable2_range = PA_MAX(capabilities2->max_bitpool, SBC_BITPOOL_HQ_JOINT_STEREO_44100) - PA_MIN(capabilities2->min_bitpool, SBC_MIN_BITPOOL) - capabilities2_range;<br />+    unusable1_range = PA_MAX(capabilities1->max_bitpool, sbc_auto_caps_table[0].max_bitpool) - PA_MIN(capabilities1->min_bitpool, sbc_auto_caps_table[0].min_bitpool) - capabilities1_range;<br />+    unusable2_range = PA_MAX(capabilities2->max_bitpool, sbc_auto_caps_table[0].max_bitpool) - PA_MIN(capabilities2->min_bitpool, sbc_auto_caps_table[0].min_bitpool) - capabilities2_range;<br /><br />     /* Prefer endpoint with smaller unusable bitpool range */<br />     if (unusable1_range > unusable2_range)<br />@@ -226,24 +330,76 @@ static int cmp_endpoints(const uint8_t *capabilities1_buffer, uint8_t capabiliti<br />     return 0;<br /> }<br /><br />-static uint8_t fill_capabilities(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+static int cmp_endpoints_fixed_bitpool(const uint8_t *capabilities1_buffer, uint8_t capabilities1_size, const uint8_t *capabilities2_buffer, uint8_t capabilities2_size, const pa_sample_spec *default_sample_spec, bool for_encoding) {<!-- --><br />+    const a2dp_sbc_t *capabilities1 = (const a2dp_sbc_t *) capabilities1_buffer;<br />+    const a2dp_sbc_t *capabilities2 = (const a2dp_sbc_t *) capabilities2_buffer;<br />+    int cmp;<br />+<br />+    pa_assert(capabilities1_size == sizeof(a2dp_sbc_t));<br />+    pa_assert(capabilities2_size == sizeof(a2dp_sbc_t));<br />+<br />+    cmp = cmp_endpoints_by_channels(capabilities1, capabilities2, default_sample_spec, for_encoding);<br />+    if (cmp != 0)<br />+        return cmp;<br />+<br />+    cmp = cmp_endpoints_by_freq(capabilities1, capabilities2, default_sample_spec, for_encoding);<br />+    if (cmp != 0)<br />+        return cmp;<br />+<br />+    return 0;<br />+}<br />+<br />+static uint8_t fill_capabilities_table(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE], const a2dp_sbc_t capabilities_table[], unsigned capabilities_table_elements) {<!-- --><br />     a2dp_sbc_t *capabilities = (a2dp_sbc_t *) capabilities_buffer;<br />+    unsigned i;<br /><br />     pa_zero(*capabilities);<br /><br />-    capabilities->channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO |<br />-                                 SBC_CHANNEL_MODE_JOINT_STEREO;<br />-    capabilities->frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 |<br />-                              SBC_SAMPLING_FREQ_48000;<br />-    capabilities->allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS;<br />-    capabilities->subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8;<br />-    capabilities->block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16;<br />-    capabilities->min_bitpool = SBC_MIN_BITPOOL;<br />-    capabilities->max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100;<br />+    capabilities->min_bitpool = 0xFF;<br />+    capabilities->max_bitpool = 0x00;<br />+<br />+    for (i = 0; i < capabilities_table_elements; i++) {<!-- --><br />+        capabilities->channel_mode |= capabilities_table[i].channel_mode;<br />+        capabilities->frequency |= capabilities_table[i].frequency;<br />+        capabilities->allocation_method |= capabilities_table[i].allocation_method;<br />+        capabilities->subbands |= capabilities_table[i].subbands;<br />+        capabilities->block_length |= capabilities_table[i].block_length;<br />+        if (capabilities->min_bitpool > capabilities_table[i].min_bitpool)<br />+            capabilities->min_bitpool = capabilities_table[i].min_bitpool;<br />+        if (capabilities->max_bitpool < capabilities_table[i].max_bitpool)<br />+            capabilities->max_bitpool = capabilities_table[i].max_bitpool;<br />+    }<br />+<br />+    pa_assert(capabilities->min_bitpool != 0xFF);<br />+    pa_assert(capabilities->max_bitpool != 0x00);<br /><br />     return sizeof(*capabilities);<br /> }<br /><br />+static uint8_t fill_capabilities_lq(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    return fill_capabilities_table(capabilities_buffer, sbc_lq_caps_table, PA_ELEMENTSOF(sbc_lq_caps_table));<br />+}<br />+<br />+static uint8_t fill_capabilities_mq(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    return fill_capabilities_table(capabilities_buffer, sbc_mq_caps_table, PA_ELEMENTSOF(sbc_mq_caps_table));<br />+}<br />+<br />+static uint8_t fill_capabilities_hq(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    return fill_capabilities_table(capabilities_buffer, sbc_hq_caps_table, PA_ELEMENTSOF(sbc_hq_caps_table));<br />+}<br />+<br />+static uint8_t fill_capabilities_xq1(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    return fill_capabilities_table(capabilities_buffer, sbc_xq1_caps_table, PA_ELEMENTSOF(sbc_xq1_caps_table));<br />+}<br />+<br />+static uint8_t fill_capabilities_xq2(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    return fill_capabilities_table(capabilities_buffer, sbc_xq2_caps_table, PA_ELEMENTSOF(sbc_xq2_caps_table));<br />+}<br />+<br />+static uint8_t fill_capabilities(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    return fill_capabilities_table(capabilities_buffer, sbc_auto_caps_table, PA_ELEMENTSOF(sbc_auto_caps_table));<br />+}<br />+<br /> static bool is_configuration_valid(const uint8_t *config_buffer, uint8_t config_size) {<!-- --><br />     const a2dp_sbc_t *config = (const a2dp_sbc_t *) config_buffer;<br /><br />@@ -288,52 +444,76 @@ static bool is_configuration_valid(const uint8_t *config_buffer, uint8_t config_<br />     return true;<br /> }<br /><br />-static uint8_t default_bitpool(uint8_t freq, uint8_t mode) {<!-- --><br />-    /* These bitpool values were chosen based on the A2DP spec recommendation */<br />-    switch (freq) {<!-- --><br />-        case SBC_SAMPLING_FREQ_16000:<br />-        case SBC_SAMPLING_FREQ_32000:<br />-            switch (mode) {<!-- --><br />-                case SBC_CHANNEL_MODE_MONO:<br />-                case SBC_CHANNEL_MODE_DUAL_CHANNEL:<br />-                case SBC_CHANNEL_MODE_STEREO:<br />-                case SBC_CHANNEL_MODE_JOINT_STEREO:<br />-                    return SBC_BITPOOL_HQ_JOINT_STEREO_44100;<br />-            }<br />-            break;<br />+static bool are_configs_compatible(const a2dp_sbc_t *config1, const a2dp_sbc_t *config2) {<!-- --><br />+    if (config1->frequency != config2->frequency)<br />+        return false;<br /><br />-        case SBC_SAMPLING_FREQ_44100:<br />-            switch (mode) {<!-- --><br />-                case SBC_CHANNEL_MODE_MONO:<br />-                case SBC_CHANNEL_MODE_DUAL_CHANNEL:<br />-                    return SBC_BITPOOL_HQ_MONO_44100;<br />+    if (config1->channel_mode != config2->channel_mode)<br />+        return false;<br /><br />-                case SBC_CHANNEL_MODE_STEREO:<br />-                case SBC_CHANNEL_MODE_JOINT_STEREO:<br />-                    return SBC_BITPOOL_HQ_JOINT_STEREO_44100;<br />-            }<br />-            break;<br />+    if (config1->allocation_method != config2->allocation_method)<br />+        return false;<br /><br />-        case SBC_SAMPLING_FREQ_48000:<br />-            switch (mode) {<!-- --><br />-                case SBC_CHANNEL_MODE_MONO:<br />-                case SBC_CHANNEL_MODE_DUAL_CHANNEL:<br />-                    return SBC_BITPOOL_HQ_MONO_48000;<br />+    if (config1->subbands != config2->subbands)<br />+        return false;<br /><br />-                case SBC_CHANNEL_MODE_STEREO:<br />-                case SBC_CHANNEL_MODE_JOINT_STEREO:<br />-                    return SBC_BITPOOL_HQ_JOINT_STEREO_48000;<br />-            }<br />-            break;<br />+    if (config1->block_length != config2->block_length)<br />+        return false;<br />+<br />+    /* second config must have constant bitpool */<br />+    pa_assert(config2->min_bitpool == config2->max_bitpool);<br />+<br />+    if (config1->min_bitpool > config2->min_bitpool || config1->max_bitpool < config2->min_bitpool)<br />+        return false;<br />+<br />+    return true;<br />+}<br />+<br />+static bool is_configuration_valid_table(const uint8_t *config_buffer, uint8_t config_size, const a2dp_sbc_t capabilities_table[], unsigned capabilities_table_elements) {<!-- --><br />+    const a2dp_sbc_t *config;<br />+    unsigned i;<br />+<br />+    if (!is_configuration_valid(config_buffer, config_size))<br />+        return false;<br />+<br />+    config = (const a2dp_sbc_t *) config_buffer;<br />+<br />+    for (i = 0; i < capabilities_table_elements; i++) {<!-- --><br />+        if (!are_configs_compatible(config, &capabilities_table[i]))<br />+            continue;<br />+        return true;<br />     }<br /><br />-    pa_assert_not_reached();<br />+    pa_log_error("Some configuration settings are invalid for current quality");<br />+    return false;<br /> }<br /><br />-static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+static bool is_configuration_valid_lq(const uint8_t *config_buffer, uint8_t config_size) {<!-- --><br />+    return is_configuration_valid_table(config_buffer, config_size, sbc_lq_caps_table, PA_ELEMENTSOF(sbc_lq_caps_table));<br />+}<br />+<br />+static bool is_configuration_valid_mq(const uint8_t *config_buffer, uint8_t config_size) {<!-- --><br />+    return is_configuration_valid_table(config_buffer, config_size, sbc_mq_caps_table, PA_ELEMENTSOF(sbc_mq_caps_table));<br />+}<br />+<br />+static bool is_configuration_valid_hq(const uint8_t *config_buffer, uint8_t config_size) {<!-- --><br />+    return is_configuration_valid_table(config_buffer, config_size, sbc_hq_caps_table, PA_ELEMENTSOF(sbc_hq_caps_table));<br />+}<br />+<br />+static bool is_configuration_valid_xq1(const uint8_t *config_buffer, uint8_t config_size) {<!-- --><br />+    return is_configuration_valid_table(config_buffer, config_size, sbc_xq1_caps_table, PA_ELEMENTSOF(sbc_xq1_caps_table));<br />+}<br />+<br />+static bool is_configuration_valid_xq2(const uint8_t *config_buffer, uint8_t config_size) {<!-- --><br />+    return is_configuration_valid_table(config_buffer, config_size, sbc_xq2_caps_table, PA_ELEMENTSOF(sbc_xq2_caps_table));<br />+}<br />+<br />+static uint8_t fill_preferred_configuration_table(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE], const a2dp_sbc_t capabilities_table[], unsigned capabilities_table_elements) {<!-- --><br />     a2dp_sbc_t *config = (a2dp_sbc_t *) config_buffer;<br />     const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer;<br />-    int i;<br />+    bool is_mono = (default_sample_spec->channels <= 1);<br />+    unsigned i;<br />+    int j;<br /><br />     static const struct {<!-- --><br />         uint32_t rate;<br />@@ -353,96 +533,191 @@ static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample<br />     pa_zero(*config);<br /><br />     /* Find the lowest freq that is at least as high as the requested sampling rate */<br />-    for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)<br />-        if (freq_table[i].rate >= default_sample_spec->rate && (capabilities->frequency & freq_table[i].cap)) {<!-- --><br />-            config->frequency = freq_table[i].cap;<br />-            break;<br />+    for (j = 0; (unsigned) j < PA_ELEMENTSOF(freq_table); j++) {<!-- --><br />+        if (freq_table[j].rate >= default_sample_spec->rate && (capabilities->frequency & freq_table[j].cap)) {<!-- --><br />+            for (i = 0; i < capabilities_table_elements; i++) {<!-- --><br />+                if (capabilities_table[i].frequency & freq_table[j].cap) {<!-- --><br />+                    config->frequency = freq_table[j].cap;<br />+                    break;<br />+                }<br />+            }<br />+            if (i != capabilities_table_elements)<br />+                break;<br />         }<br />+    }<br /><br />-    if ((unsigned) i == PA_ELEMENTSOF(freq_table)) {<!-- --><br />-        for (--i; i >= 0; i--) {<!-- --><br />-            if (capabilities->frequency & freq_table[i].cap) {<!-- --><br />-                config->frequency = freq_table[i].cap;<br />-                break;<br />+    if ((unsigned) j == PA_ELEMENTSOF(freq_table)) {<!-- --><br />+        for (--j; j >= 0; j--) {<!-- --><br />+            if (capabilities->frequency & freq_table[j].cap) {<!-- --><br />+                for (i = 0; i < capabilities_table_elements; i++) {<!-- --><br />+                    if (capabilities_table[i].frequency & freq_table[j].cap) {<!-- --><br />+                        config->frequency = freq_table[j].cap;<br />+                        break;<br />+                    }<br />+                }<br />+                if (i != capabilities_table_elements)<br />+                    break;<br />             }<br />         }<br /><br />-        if (i < 0) {<!-- --><br />-            pa_log_error("Not suitable sample rate");<br />+        if (j < 0) {<!-- --><br />+            pa_log_error("No suitable sample rate");<br />             return 0;<br />         }<br />     }<br /><br />-    pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table));<br />+    pa_assert((unsigned) j < PA_ELEMENTSOF(freq_table));<br />+<br />+    for (i = 0; i < capabilities_table_elements; i++) {<!-- --><br />+        if ((capabilities->block_length & SBC_BLOCK_LENGTH_16) && (capabilities_table[i].block_length & SBC_BLOCK_LENGTH_16))<br />+            config->block_length = SBC_BLOCK_LENGTH_16;<br />+        else if ((capabilities->block_length & SBC_BLOCK_LENGTH_12) && (capabilities_table[i].block_length & SBC_BLOCK_LENGTH_12))<br />+            config->block_length = SBC_BLOCK_LENGTH_12;<br />+        else if ((capabilities->block_length & SBC_BLOCK_LENGTH_8) && (capabilities_table[i].block_length & SBC_BLOCK_LENGTH_8))<br />+            config->block_length = SBC_BLOCK_LENGTH_8;<br />+        else if ((capabilities->block_length & SBC_BLOCK_LENGTH_4) && (capabilities_table[i].block_length & SBC_BLOCK_LENGTH_4))<br />+            config->block_length = SBC_BLOCK_LENGTH_4;<br />+        else {<!-- --><br />+            pa_log_debug("No supported block lengths in table entry %u", i);<br />+            continue;<br />+        }<br /><br />-    if (default_sample_spec->channels <= 1) {<!-- --><br />-        if (capabilities->channel_mode & SBC_CHANNEL_MODE_MONO)<br />-            config->channel_mode = SBC_CHANNEL_MODE_MONO;<br />-        else if (capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)<br />-            config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;<br />-        else if (capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO)<br />-            config->channel_mode = SBC_CHANNEL_MODE_STEREO;<br />-        else if (capabilities->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)<br />-            config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;<br />+        if ((capabilities->subbands & SBC_SUBBANDS_8) && (capabilities_table[i].subbands & SBC_SUBBANDS_8))<br />+            config->subbands = SBC_SUBBANDS_8;<br />+        else if ((capabilities->subbands & SBC_SUBBANDS_4) && (capabilities_table[i].subbands & SBC_SUBBANDS_4))<br />+            config->subbands = SBC_SUBBANDS_4;<br />         else {<!-- --><br />-            pa_log_error("No supported channel modes");<br />-            return 0;<br />+            pa_log_debug("No supported subbands in table entry %u", i);<br />+            continue;<br />         }<br />-    } else {<!-- --><br />-        if (capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)<br />-            config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;<br />-        else if (capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO)<br />-            config->channel_mode = SBC_CHANNEL_MODE_STEREO;<br />-        else if (capabilities->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)<br />-            config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;<br />-        else if (capabilities->channel_mode & SBC_CHANNEL_MODE_MONO)<br />-            config->channel_mode = SBC_CHANNEL_MODE_MONO;<br />+<br />+        if ((capabilities->allocation_method & SBC_ALLOCATION_LOUDNESS) && (capabilities_table[i].allocation_method & SBC_ALLOCATION_LOUDNESS))<br />+            config->allocation_method = SBC_ALLOCATION_LOUDNESS;<br />+        else if ((capabilities->allocation_method & SBC_ALLOCATION_SNR) && (capabilities_table[i].allocation_method & SBC_ALLOCATION_SNR))<br />+            config->allocation_method = SBC_ALLOCATION_SNR;<br />         else {<!-- --><br />-            pa_log_error("No supported channel modes");<br />-            return 0;<br />+            pa_log_debug("No supported allocation method in table entry %u", i);<br />+            continue;<br />         }<br />-    }<br /><br />-    if (capabilities->block_length & SBC_BLOCK_LENGTH_16)<br />-        config->block_length = SBC_BLOCK_LENGTH_16;<br />-    else if (capabilities->block_length & SBC_BLOCK_LENGTH_12)<br />-        config->block_length = SBC_BLOCK_LENGTH_12;<br />-    else if (capabilities->block_length & SBC_BLOCK_LENGTH_8)<br />-        config->block_length = SBC_BLOCK_LENGTH_8;<br />-    else if (capabilities->block_length & SBC_BLOCK_LENGTH_4)<br />-        config->block_length = SBC_BLOCK_LENGTH_4;<br />-    else {<!-- --><br />-        pa_log_error("No supported block lengths");<br />-        return 0;<br />-    }<br />+        if (is_mono) {<!-- --><br />+            if ((capabilities->channel_mode & SBC_CHANNEL_MODE_MONO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_MONO))<br />+                config->channel_mode = SBC_CHANNEL_MODE_MONO;<br />+            else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO))<br />+                config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;<br />+            else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_STEREO))<br />+                config->channel_mode = SBC_CHANNEL_MODE_STEREO;<br />+            else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL))<br />+                config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;<br />+            else {<!-- --><br />+                pa_log_debug("No supported channel mode in table entry %u", i);<br />+                continue;<br />+            }<br />+        } else {<!-- --><br />+            if ((capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO))<br />+                config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;<br />+            else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_STEREO))<br />+                config->channel_mode = SBC_CHANNEL_MODE_STEREO;<br />+            else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL))<br />+                config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;<br />+            else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_MONO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_MONO))<br />+                config->channel_mode = SBC_CHANNEL_MODE_MONO;<br />+            else {<!-- --><br />+                pa_log_debug("No supported channel mode in table entry %u", i);<br />+                continue;<br />+            }<br />+        }<br /><br />-    if (capabilities->subbands & SBC_SUBBANDS_8)<br />-        config->subbands = SBC_SUBBANDS_8;<br />-    else if (capabilities->subbands & SBC_SUBBANDS_4)<br />-        config->subbands = SBC_SUBBANDS_4;<br />-    else {<!-- --><br />-        pa_log_error("No supported subbands");<br />-        return 0;<br />+        config->min_bitpool = PA_MAX(capabilities->min_bitpool, capabilities_table[i].min_bitpool);<br />+        config->max_bitpool = PA_MIN(capabilities->max_bitpool, capabilities_table[i].max_bitpool);<br />+<br />+        if (config->min_bitpool > config->max_bitpool) {<!-- --><br />+            pa_log_debug("No supported bitpool in table entry %u [%u, %u], need [%u, %u]", i, capabilities_table[i].min_bitpool, capabilities_table[i].max_bitpool, capabilities->min_bitpool, capabilities->max_bitpool);<br />+            continue;<br />+        }<br />+<br />+        break;<br />     }<br /><br />-    if (capabilities->allocation_method & SBC_ALLOCATION_LOUDNESS)<br />-        config->allocation_method = SBC_ALLOCATION_LOUDNESS;<br />-    else if (capabilities->allocation_method & SBC_ALLOCATION_SNR)<br />-        config->allocation_method = SBC_ALLOCATION_SNR;<br />-    else {<!-- --><br />-        pa_log_error("No supported allocation method");<br />+    if (i == capabilities_table_elements) {<!-- --><br />+        pa_log_error("No supported configuration");<br />         return 0;<br />     }<br /><br />-    config->min_bitpool = (uint8_t) PA_MAX(SBC_MIN_BITPOOL, capabilities->min_bitpool);<br />-    config->max_bitpool = (uint8_t) PA_MIN(default_bitpool(config->frequency, config->channel_mode), capabilities->max_bitpool);<br />+    return sizeof(*config);<br />+}<br /><br />-    if (config->min_bitpool > config->max_bitpool) {<!-- --><br />-        pa_log_error("No supported bitpool");<br />-        return 0;<br />+static uint8_t fill_preferred_configuration_lq(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    return fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_lq_caps_table, PA_ELEMENTSOF(sbc_lq_caps_table));<br />+}<br />+<br />+static uint8_t fill_preferred_configuration_mq(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    return fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_mq_caps_table, PA_ELEMENTSOF(sbc_mq_caps_table));<br />+}<br />+<br />+static uint8_t fill_preferred_configuration_hq(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    return fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_hq_caps_table, PA_ELEMENTSOF(sbc_hq_caps_table));<br />+}<br />+<br />+static uint8_t fill_preferred_configuration_xq1(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    return fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_xq1_caps_table, PA_ELEMENTSOF(sbc_xq1_caps_table));<br />+}<br />+<br />+static uint8_t fill_preferred_configuration_xq2(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    return fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_xq2_caps_table, PA_ELEMENTSOF(sbc_xq2_caps_table));<br />+}<br />+<br />+static uint8_t default_bitpool(uint8_t freq, uint8_t mode) {<!-- --><br />+    /* These bitpool values were chosen based on the A2DP spec recommendation */<br />+    switch (freq) {<!-- --><br />+        case SBC_SAMPLING_FREQ_16000:<br />+        case SBC_SAMPLING_FREQ_32000:<br />+            switch (mode) {<!-- --><br />+                case SBC_CHANNEL_MODE_MONO:<br />+                case SBC_CHANNEL_MODE_DUAL_CHANNEL:<br />+                case SBC_CHANNEL_MODE_STEREO:<br />+                case SBC_CHANNEL_MODE_JOINT_STEREO:<br />+                    return SBC_BITPOOL_HQ_JOINT_STEREO_44100;<br />+            }<br />+            break;<br />+<br />+        case SBC_SAMPLING_FREQ_44100:<br />+            switch (mode) {<!-- --><br />+                case SBC_CHANNEL_MODE_MONO:<br />+                case SBC_CHANNEL_MODE_DUAL_CHANNEL:<br />+                    return SBC_BITPOOL_HQ_MONO_44100;<br />+<br />+                case SBC_CHANNEL_MODE_STEREO:<br />+                case SBC_CHANNEL_MODE_JOINT_STEREO:<br />+                    return SBC_BITPOOL_HQ_JOINT_STEREO_44100;<br />+            }<br />+            break;<br />+<br />+        case SBC_SAMPLING_FREQ_48000:<br />+            switch (mode) {<!-- --><br />+                case SBC_CHANNEL_MODE_MONO:<br />+                case SBC_CHANNEL_MODE_DUAL_CHANNEL:<br />+                    return SBC_BITPOOL_HQ_MONO_48000;<br />+<br />+                case SBC_CHANNEL_MODE_STEREO:<br />+                case SBC_CHANNEL_MODE_JOINT_STEREO:<br />+                    return SBC_BITPOOL_HQ_JOINT_STEREO_48000;<br />+            }<br />+            break;<br />     }<br /><br />-    return sizeof(*config);<br />+    pa_assert_not_reached();<br />+}<br />+<br />+static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {<!-- --><br />+    a2dp_sbc_t *config = (a2dp_sbc_t *) config_buffer;<br />+    uint8_t ret;<br />+<br />+    ret = fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_auto_caps_table, PA_ELEMENTSOF(sbc_auto_caps_table));<br />+    config->max_bitpool = PA_MIN(default_bitpool(config->frequency, config->channel_mode), config->max_bitpool);<br />+    config->max_bitpool = PA_MAX(config->max_bitpool, config->min_bitpool);<br />+<br />+    return ret;<br /> }<br /><br /> static void set_params(struct sbc_info *sbc_info) {<!-- --><br />@@ -635,13 +910,17 @@ static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) {<!-- --><br />     uint8_t bitpool;<br /><br />     /* Check if bitpool is already at its limit */<br />-    if (sbc_info->sbc.bitpool <= SBC_BITPOOL_DEC_LIMIT)<br />-        return 0;<br />-<br />-    bitpool = sbc_info->sbc.bitpool - SBC_BITPOOL_DEC_STEP;<br />-<br />-    if (bitpool < SBC_BITPOOL_DEC_LIMIT)<br />-        bitpool = SBC_BITPOOL_DEC_LIMIT;<br />+    if (sbc_info->mode == SBC_CHANNEL_MODE_MONO || sbc_info->mode == SBC_CHANNEL_MODE_DUAL_CHANNEL) {<!-- --><br />+        /* For Mono and Dual Channel modes bitpool value is separete for each channel */<br />+        bitpool = sbc_info->sbc.bitpool - SBC_SEPARATE_BITPOOL_DEC_STEP;<br />+        if (bitpool <= SBC_SEPARATE_BITPOOL_DEC_LIMIT)<br />+            return 0;<br />+    } else {<!-- --><br />+        /* For Stereo modes bitpool value is combined for both channels */<br />+        bitpool = sbc_info->sbc.bitpool - SBC_COMBINED_BITPOOL_DEC_STEP;<br />+        if (bitpool <= SBC_COMBINED_BITPOOL_DEC_LIMIT)<br />+            return 0;<br />+    }<br /><br />     if (sbc_info->sbc.bitpool == bitpool)<br />         return 0;<br />@@ -650,6 +929,10 @@ static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) {<!-- --><br />     return get_block_size(codec_info, write_link_mtu);<br /> }<br /><br />+static size_t reduce_encoder_bitrate_none(void *codec_info, size_t write_link_mtu) {<!-- --><br />+    return 0;<br />+}<br />+<br /> static size_t encode_buffer(void *codec_info, uint32_t timestamp, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed) {<!-- --><br />     struct sbc_info *sbc_info = (struct sbc_info *) codec_info;<br />     struct rtp_header *header;<br />@@ -796,9 +1079,69 @@ static size_t decode_buffer(void *codec_info, const uint8_t *input_buffer, size_<br />     return d - output_buffer;<br /> }<br /><br />+const pa_a2dp_codec pa_a2dp_codec_sbc_lq = {<!-- --><br />+    .name = "sbc_lq",<br />+    .description = "SBC (Low Quality)",<br />+    .id = { A2DP_CODEC_SBC, 0, 0 },<br />+    .support_backchannel = false,<br />+    .can_accept_capabilities = can_accept_capabilities_lq,<br />+    .cmp_endpoints = cmp_endpoints_fixed_bitpool,<br />+    .fill_capabilities = fill_capabilities_lq,<br />+    .is_configuration_valid = is_configuration_valid_lq,<br />+    .fill_preferred_configuration = fill_preferred_configuration_lq,<br />+    .init = init,<br />+    .deinit = deinit,<br />+    .reset = reset,<br />+    .get_read_block_size = get_block_size,<br />+    .get_write_block_size = get_block_size,<br />+    .reduce_encoder_bitrate = reduce_encoder_bitrate_none,<br />+    .encode_buffer = encode_buffer,<br />+    .decode_buffer = decode_buffer,<br />+};<br />+<br />+const pa_a2dp_codec pa_a2dp_codec_sbc_mq = {<!-- --><br />+    .name = "sbc_mq",<br />+    .description = "SBC (Middle Quality)",<br />+    .id = { A2DP_CODEC_SBC, 0, 0 },<br />+    .support_backchannel = false,<br />+    .can_accept_capabilities = can_accept_capabilities_mq,<br />+    .cmp_endpoints = cmp_endpoints_fixed_bitpool,<br />+    .fill_capabilities = fill_capabilities_mq,<br />+    .is_configuration_valid = is_configuration_valid_mq,<br />+    .fill_preferred_configuration = fill_preferred_configuration_mq,<br />+    .init = init,<br />+    .deinit = deinit,<br />+    .reset = reset,<br />+    .get_read_block_size = get_block_size,<br />+    .get_write_block_size = get_block_size,<br />+    .reduce_encoder_bitrate = reduce_encoder_bitrate_none,<br />+    .encode_buffer = encode_buffer,<br />+    .decode_buffer = decode_buffer,<br />+};<br />+<br />+const pa_a2dp_codec pa_a2dp_codec_sbc_hq = {<!-- --><br />+    .name = "sbc_hq",<br />+    .description = "SBC (High Quality)",<br />+    .id = { A2DP_CODEC_SBC, 0, 0 },<br />+    .support_backchannel = false,<br />+    .can_accept_capabilities = can_accept_capabilities_hq,<br />+    .cmp_endpoints = cmp_endpoints_fixed_bitpool,<br />+    .fill_capabilities = fill_capabilities_hq,<br />+    .is_configuration_valid = is_configuration_valid_hq,<br />+    .fill_preferred_configuration = fill_preferred_configuration_hq,<br />+    .init = init,<br />+    .deinit = deinit,<br />+    .reset = reset,<br />+    .get_read_block_size = get_block_size,<br />+    .get_write_block_size = get_block_size,<br />+    .reduce_encoder_bitrate = reduce_encoder_bitrate_none,<br />+    .encode_buffer = encode_buffer,<br />+    .decode_buffer = decode_buffer,<br />+};<br />+<br /> const pa_a2dp_codec pa_a2dp_codec_sbc = {<!-- --><br />     .name = "sbc",<br />-    .description = "SBC",<br />+    .description = "SBC (Automatic Quality)",<br />     .id = { A2DP_CODEC_SBC, 0, 0 },<br />     .support_backchannel = false,<br />     .can_accept_capabilities = can_accept_capabilities,<br />@@ -815,3 +1158,43 @@ const pa_a2dp_codec pa_a2dp_codec_sbc = {<!-- --><br />     .encode_buffer = encode_buffer,<br />     .decode_buffer = decode_buffer,<br /> };<br />+<br />+const pa_a2dp_codec pa_a2dp_codec_sbc_xq1 = {<!-- --><br />+    .name = "sbc_xq1",<br />+    .description = "SBC (eXtreme Quality profile 1)",<br />+    .id = { A2DP_CODEC_SBC, 0, 0 },<br />+    .support_backchannel = false,<br />+    .can_accept_capabilities = can_accept_capabilities_xq1,<br />+    .cmp_endpoints = cmp_endpoints_fixed_bitpool,<br />+    .fill_capabilities = fill_capabilities_xq1,<br />+    .is_configuration_valid = is_configuration_valid_xq1,<br />+    .fill_preferred_configuration = fill_preferred_configuration_xq1,<br />+    .init = init,<br />+    .deinit = deinit,<br />+    .reset = reset,<br />+    .get_read_block_size = get_block_size,<br />+    .get_write_block_size = get_block_size,<br />+    .reduce_encoder_bitrate = reduce_encoder_bitrate_none,<br />+    .encode_buffer = encode_buffer,<br />+    .decode_buffer = decode_buffer,<br />+};<br />+<br />+const pa_a2dp_codec pa_a2dp_codec_sbc_xq2 = {<!-- --><br />+    .name = "sbc_xq2",<br />+    .description = "SBC (eXtreme Quality profile 2)",<br />+    .id = { A2DP_CODEC_SBC, 0, 0 },<br />+    .support_backchannel = false,<br />+    .can_accept_capabilities = can_accept_capabilities_xq2,<br />+    .cmp_endpoints = cmp_endpoints_fixed_bitpool,<br />+    .fill_capabilities = fill_capabilities_xq2,<br />+    .is_configuration_valid = is_configuration_valid_xq2,<br />+    .fill_preferred_configuration = fill_preferred_configuration_xq2,<br />+    .init = init,<br />+    .deinit = deinit,<br />+    .reset = reset,<br />+    .get_read_block_size = get_block_size,<br />+    .get_write_block_size = get_block_size,<br />+    .reduce_encoder_bitrate = reduce_encoder_bitrate_none,<br />+    .encode_buffer = encode_buffer,<br />+    .decode_buffer = decode_buffer,<br />+};<br />diff --git a/src/modules/bluetooth/a2dp-codec-util.c b/src/modules/bluetooth/a2dp-codec-util.c<br />index aa5056e0e..a<span class="177d5a4333ac019606de889e143743a1wmi-callto">80879252 100644</span><br />--- a/src/modules/bluetooth/a2dp-codec-util.c<br />+++ b/src/modules/bluetooth/a2dp-codec-util.c<br />@@ -29,6 +29,11 @@<br /> extern const pa_a2dp_codec pa_a2dp_codec_faststream;<br /> extern const pa_a2dp_codec pa_a2dp_codec_faststream_mic;<br /> extern const pa_a2dp_codec pa_a2dp_codec_sbc;<br />+extern const pa_a2dp_codec pa_a2dp_codec_sbc_lq;<br />+extern const pa_a2dp_codec pa_a2dp_codec_sbc_mq;<br />+extern const pa_a2dp_codec pa_a2dp_codec_sbc_hq;<br />+extern const pa_a2dp_codec pa_a2dp_codec_sbc_xq1;<br />+extern const pa_a2dp_codec pa_a2dp_codec_sbc_xq2;<br /> #ifdef HAVE_OPENAPTX<br /> extern const pa_a2dp_codec pa_a2dp_codec_aptx;<br /> extern const pa_a2dp_codec pa_a2dp_codec_aptx_hd;<br />@@ -37,13 +42,20 @@ extern const pa_a2dp_codec pa_a2dp_codec_aptx_hd;<br /> /* This is list of supported codecs. Their order is important.<br />  * Codec with higher index has higher priority. */<br /> static const pa_a2dp_codec *pa_a2dp_codecs[] = {<!-- --><br />-    &pa_a2dp_codec_faststream,<br />-    &pa_a2dp_codec_faststream_mic,<br />-    &pa_a2dp_codec_sbc,<br />+    &pa_a2dp_codec_sbc_lq,<br />+    &pa_a2dp_codec_faststream,        /* Exactly same as SBC-LQ, but could provide lower latency */<br />+    &pa_a2dp_codec_faststream_mic,    /* Exactly same as FastStream, but with voice backchannel */<br />+    &pa_a2dp_codec_sbc_mq,<br />+    &pa_a2dp_codec_sbc,               /* SBC in automatic mode, from SBC-LQ to SBC-HQ; not SBC-XQ */<br /> #ifdef HAVE_OPENAPTX<br />     &pa_a2dp_codec_aptx,<br />+#endif<br />+    &pa_a2dp_codec_sbc_hq,            /* SBC-HQ has similar quality as aptX */<br />+#ifdef HAVE_OPENAPTX<br />     &pa_a2dp_codec_aptx_hd,<br /> #endif<br />+    &pa_a2dp_codec_sbc_xq1,           /* SBC-XQ has similar quality as aptX-HD */<br />+    &pa_a2dp_codec_sbc_xq2,           /* SBC-XQ has similar quality as aptX-HD */<br /> };<br /><br /> unsigned int pa_bluetooth_a2dp_codec_count(void) {<!-- --><br /></p><span class="c18e9d485856a85513717a5a5b59d3fewmi-sign">-- <br />2.20.1<br /></span><p><br />_______________________________________________<br />pulseaudio-discuss mailing list<br /><a href="mailto:pulseaudio-discuss@lists.freedesktop.org">pulseaudio-discuss@lists.freedesktop.org</a><br /><a href="https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss">https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss</a><br /></p></blockquote>