Hi Pali, I believe that the average user SHOULD be provided a high quality sound by default. And legacy low quality bitrates SHOULD be a workaround. IMHO, an "AUTOMATIC mode with max XQ bitrates and DUAL CHANNEL mode prefered" (aka : max negociated bitpool = 38 per channel) : SHOULD be the default mode of operations, because it will provide a high quality sound for 99% of the devices. Rare remaining devices that have a broken A2DP implementation, OR rare very old devices designed before BT version 2.0 : SHOULD be handled by an "automatic mode with the legacy MQ bitrates" (aka max negociated bitpool = 53). Other SBC modes with FORCED bitpool are usefull for sound researchers / advanced sound specialists, but not relevant for the average user. Please tell me what you think about it. All the best JP 06.10.2019, 19:59, "Pali Rohár" <pali.rohar@xxxxxxxxx>: > Specify configuration for Low, Middle, High and eXtreme Quality of SBC > codec. SBC codec in eXtreme Quality has higher quality than aptX. > > Automatic Quality mode matches configuration of SBC codec which was used > before this change. Which means that it accept configuration between Low > and High quality. > > Current SBC code was extended to allow definitions of arbitrary > configuration variants of SBC codec parameters. > > SBC XQ testing is in following article: > http://soundexpert.org/articles/-/blogs/audio-quality-of-sbc-xq-bluetooth-audio-codec > --- > src/modules/bluetooth/a2dp-codec-sbc.c | 737 ++++++++++++++++++++++++++------ > src/modules/bluetooth/a2dp-codec-util.c | 20 +- > 2 files changed, 618 insertions(+), 139 deletions(-) > > diff --git a/src/modules/bluetooth/a2dp-codec-sbc.c b/src/modules/bluetooth/a2dp-codec-sbc.c > index 733c1a9ab..8db3416b9 100644 > --- a/src/modules/bluetooth/a2dp-codec-sbc.c > +++ b/src/modules/bluetooth/a2dp-codec-sbc.c > @@ -36,8 +36,71 @@ > #include "a2dp-codec-api.h" > #include "rtp.h" > > -#define SBC_BITPOOL_DEC_LIMIT 32 > -#define SBC_BITPOOL_DEC_STEP 5 > +/* Below are capabilities tables for different qualities. Order of capabilities in tables are from the most preferred to the least preferred. */ > + > +#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 } > + > +/* SBC Low Quality, Joint Stereo is same as FastStream's SBC codec configuration, Mono was calculated to match Joint Stereo */ > +static const a2dp_sbc_t sbc_lq_caps_table[] = { > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_44100, 29), /* 195.7 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_48000, 29), /* 213 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO, SBC_SAMPLING_FREQ_44100, 15), /* 104.7 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO, SBC_SAMPLING_FREQ_48000, 15), /* 114 kbps */ > +}; > + > +/* SBC Middle Quality, based on A2DP spec: Recommended sets of SBC parameters */ > +static const a2dp_sbc_t sbc_mq_caps_table[] = { > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_44100, SBC_BITPOOL_MQ_JOINT_STEREO_44100), /* bitpool = 35, 228.8 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_48000, SBC_BITPOOL_MQ_JOINT_STEREO_48000), /* bitpool = 33, 237 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO, SBC_SAMPLING_FREQ_44100, SBC_BITPOOL_MQ_MONO_44100), /* bitpool = 19, 126.8 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO, SBC_SAMPLING_FREQ_48000, SBC_BITPOOL_MQ_MONO_48000), /* bitpool = 18, 132 kbps */ > +}; > + > +/* SBC High Quality, based on A2DP spec: Recommended sets of SBC parameters */ > +static const a2dp_sbc_t sbc_hq_caps_table[] = { > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_44100, SBC_BITPOOL_HQ_JOINT_STEREO_44100), /* bitpool = 53, 328 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_48000, SBC_BITPOOL_HQ_JOINT_STEREO_48000), /* bitpool = 51, 345 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO, SBC_SAMPLING_FREQ_44100, SBC_BITPOOL_HQ_MONO_44100), /* bitpool = 31, 192.9 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO, SBC_SAMPLING_FREQ_48000, SBC_BITPOOL_HQ_MONO_48000), /* bitpool = 29, 210 kbps */ > +}; > + > +/* SBC eXtreme Quality, calculated to minimize wasted bytes and to be below max possible 512 kbps */ > +static const a2dp_sbc_t sbc_xq1_caps_table[] = { > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_44100, 76), /* 454.8 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_JOINT_STEREO, SBC_SAMPLING_FREQ_48000, 76), /* 495 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_STEREO, SBC_SAMPLING_FREQ_44100, 76), /* 452 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_STEREO, SBC_SAMPLING_FREQ_48000, 76), /* 492 kbps */ > +}; > +static const a2dp_sbc_t sbc_xq2_caps_table[] = { > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_DUAL_CHANNEL, SBC_SAMPLING_FREQ_44100, 38), /* 452 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_DUAL_CHANNEL, SBC_SAMPLING_FREQ_48000, 38), /* 492 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO, SBC_SAMPLING_FREQ_44100, 37), /* 226 kbps */ > + FIXED_SBC_CAPS(SBC_CHANNEL_MODE_MONO, SBC_SAMPLING_FREQ_48000, 37), /* 246 kbps */ > +}; > +/* In most cases bluetooth headsets would support only sbc dual channel mode > + * for 2 channels as they have limited maximal bitpool value to 53. > + * We need to define it in two tables to disallow invalid combination of > + * joint stereo with bitpool 38 which is not XQ. */ > + > +#undef FIXED_SBC_CAPS > + > +/* SBC Auto Quality, only one row which allow any possible configuration up to common High Quality */ > +/* We need to ensure that bitrate is below max possible 512 kbps, therefore limit configuration to High Quality */ > +static const a2dp_sbc_t sbc_auto_caps_table[] = { { > + .channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO, > + .frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000, > + .allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS, > + .subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8, > + .block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16, > + .min_bitpool = SBC_MIN_BITPOOL, > + .max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100, > +} }; > + > +/* Bitpool limits and steps for reducing bitrate in Auto Quality mode */ > +#define SBC_SEPARATE_BITPOOL_DEC_LIMIT 10 > +#define SBC_COMBINED_BITPOOL_DEC_LIMIT 25 > +#define SBC_SEPARATE_BITPOOL_DEC_STEP 2 > +#define SBC_COMBINED_BITPOOL_DEC_STEP 4 > > struct sbc_info { > sbc_t sbc; /* Codec data */ > @@ -53,62 +116,237 @@ struct sbc_info { > uint8_t max_bitpool; > }; > > -static bool can_accept_capabilities(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) { > - const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer; > +static bool are_capabilities_compatible(const a2dp_sbc_t *capabilities1, const a2dp_sbc_t *capabilities2) { > + if (!(capabilities1->channel_mode & capabilities2->channel_mode)) > + return false; > > - if (capabilities_size != sizeof(*capabilities)) > + if (!(capabilities1->frequency & capabilities2->frequency)) > return false; > > - if (!(capabilities->frequency & (SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000))) > + if (!(capabilities1->allocation_method & capabilities2->allocation_method)) > return false; > > - if (!(capabilities->channel_mode & (SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO))) > + if (!(capabilities1->subbands & capabilities2->subbands)) > return false; > > - if (!(capabilities->allocation_method & (SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS))) > + if (!(capabilities1->block_length & capabilities2->block_length)) > return false; > > - if (!(capabilities->subbands & (SBC_SUBBANDS_4 | SBC_SUBBANDS_8))) > + if (capabilities1->min_bitpool > capabilities2->max_bitpool || capabilities2->min_bitpool > capabilities1->max_bitpool) > return false; > > - if (!(capabilities->block_length & (SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16))) > + if (capabilities1->min_bitpool > capabilities1->max_bitpool || capabilities2->min_bitpool > capabilities2->max_bitpool) > return false; > > return true; > } > > -static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) { > +static bool can_accept_capabilities(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) { > + const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer; > + > + if (capabilities_size != sizeof(*capabilities)) > + return false; > + > + return are_capabilities_compatible(capabilities, &sbc_auto_caps_table[0]); > +} > + > +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) { > + const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer; > + unsigned i; > + > + if (!can_accept_capabilities(capabilities_buffer, capabilities_size, false)) > + return false; > + > + for (i = 0; i < capabilities_table_elements; i++) { > + if (!are_capabilities_compatible(capabilities, &capabilities_table[i])) > + continue; > + return true; > + } > + > + return false; > +} > + > +static bool can_accept_capabilities_lq(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) { > + return can_accept_capabilities_table(capabilities_buffer, capabilities_size, sbc_lq_caps_table, PA_ELEMENTSOF(sbc_lq_caps_table)); > +} > + > +static bool can_accept_capabilities_mq(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) { > + return can_accept_capabilities_table(capabilities_buffer, capabilities_size, sbc_mq_caps_table, PA_ELEMENTSOF(sbc_mq_caps_table)); > +} > + > +static bool can_accept_capabilities_hq(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) { > + return can_accept_capabilities_table(capabilities_buffer, capabilities_size, sbc_hq_caps_table, PA_ELEMENTSOF(sbc_hq_caps_table)); > +} > + > +static bool can_accept_capabilities_xq1(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) { > + return can_accept_capabilities_table(capabilities_buffer, capabilities_size, sbc_xq1_caps_table, PA_ELEMENTSOF(sbc_xq1_caps_table)); > +} > + > +static bool can_accept_capabilities_xq2(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) { > + return can_accept_capabilities_table(capabilities_buffer, capabilities_size, sbc_xq2_caps_table, PA_ELEMENTSOF(sbc_xq2_caps_table)); > +} > + > +static const char *choose_remote_endpoint_table(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, const a2dp_sbc_t capabilities_table[], unsigned capabilities_table_elements) { > const pa_a2dp_codec_capabilities *a2dp_capabilities; > + const a2dp_sbc_t *capabilities; > const char *key; > void *state; > + unsigned i; > + uint8_t table_range; > + uint8_t current_range; > + const char *best_key = NULL; > + uint8_t best_range = 0; > + uint8_t frequency = 0; > + bool is_mono = (default_sample_spec->channels <= 1); > + > + static const struct { > + uint32_t rate; > + uint8_t cap; > + } freq_table[] = { > + { 16000U, SBC_SAMPLING_FREQ_16000 }, > + { 32000U, SBC_SAMPLING_FREQ_32000 }, > + { 44100U, SBC_SAMPLING_FREQ_44100 }, > + { 48000U, SBC_SAMPLING_FREQ_48000 } > + }; > + > + for (i = 0; i < PA_ELEMENTSOF(freq_table); i++) { > + if (freq_table[i].rate == default_sample_spec->rate) { > + frequency = freq_table[i].cap; > + break; > + } > + } > + > + if (!frequency) > + return NULL; > > - /* There is no preference, just choose random valid entry */ > + /* choose remote capabilities which are compatible and its bitpool range is nearest to one from capabilities table */ > PA_HASHMAP_FOREACH_KV(key, a2dp_capabilities, capabilities_hashmap, state) { > - if (can_accept_capabilities(a2dp_capabilities->buffer, a2dp_capabilities->size, for_encoding)) > - return key; > + /* skip remote capabilities which are not compatible */ > + if (!can_accept_capabilities(a2dp_capabilities->buffer, a2dp_capabilities->size, false)) > + continue; > + > + capabilities = (const a2dp_sbc_t *) a2dp_capabilities->buffer; > + > + /* choose capabilities from our table which is compatible with sample spec and remote capabilities */ > + for (i = 0; i < capabilities_table_elements; i++) { > + if (!are_capabilities_compatible(capabilities, &capabilities_table[i])) > + continue; > + /* For mono mode both capabilities must support mono */ > + if (is_mono && !((capabilities->channel_mode & SBC_CHANNEL_MODE_MONO) & (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_MONO))) > + continue; > + /* For non-mono mode both capabilities must support at least one common non-mode mode */ > + if (!is_mono && !((capabilities->channel_mode & (SBC_CHANNEL_MODE_JOINT_STEREO | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_DUAL_CHANNEL)) & (capabilities_table[i].channel_mode & (SBC_CHANNEL_MODE_JOINT_STEREO | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_DUAL_CHANNEL)))) > + continue; > + /* And both capabilites must be compatible with chosen frequency */ > + if (!(capabilities->frequency & frequency) || !(capabilities_table[i].frequency & frequency)) > + continue; > + break; > + } > + > + /* skip if nothing is compatible */ > + if (i == capabilities_table_elements) > + continue; > + > + /* calculate current bitpool range compatible with both remote capabilities and capabilities from our table */ > + if (capabilities->min_bitpool > capabilities_table[i].min_bitpool) { > + if (capabilities->max_bitpool > capabilities_table[i].max_bitpool) > + current_range = capabilities_table[i].max_bitpool - capabilities->min_bitpool; > + else > + current_range = capabilities->max_bitpool - capabilities->min_bitpool; > + } else { > + if (capabilities->max_bitpool > capabilities_table[i].max_bitpool) > + current_range = capabilities_table[i].max_bitpool - capabilities_table[i].min_bitpool; > + else > + current_range = capabilities->max_bitpool - capabilities_table[i].min_bitpool; > + } > + > + table_range = capabilities_table[i].max_bitpool - capabilities_table[i].min_bitpool; > + > + /* use current remote capabilities if its bitpool range is closer to bitpool range in table */ > + if (!best_key || abs((int)current_range - (int)(table_range)) < abs((int)best_range - (int)(table_range))) { > + best_range = current_range; > + best_key = key; > + } > } > > - return NULL; > + return best_key; > } > > -static uint8_t fill_capabilities(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) { > +static const char *choose_remote_endpoint_lq(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) { > + return choose_remote_endpoint_table(capabilities_hashmap, default_sample_spec, sbc_lq_caps_table, PA_ELEMENTSOF(sbc_lq_caps_table)); > +} > + > +static const char *choose_remote_endpoint_mq(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) { > + return choose_remote_endpoint_table(capabilities_hashmap, default_sample_spec, sbc_mq_caps_table, PA_ELEMENTSOF(sbc_mq_caps_table)); > +} > + > +static const char *choose_remote_endpoint_hq(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) { > + return choose_remote_endpoint_table(capabilities_hashmap, default_sample_spec, sbc_hq_caps_table, PA_ELEMENTSOF(sbc_hq_caps_table)); > +} > + > +static const char *choose_remote_endpoint_xq1(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) { > + return choose_remote_endpoint_table(capabilities_hashmap, default_sample_spec, sbc_xq1_caps_table, PA_ELEMENTSOF(sbc_xq1_caps_table)); > +} > + > +static const char *choose_remote_endpoint_xq2(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) { > + return choose_remote_endpoint_table(capabilities_hashmap, default_sample_spec, sbc_xq2_caps_table, PA_ELEMENTSOF(sbc_xq2_caps_table)); > +} > + > +static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) { > + return choose_remote_endpoint_table(capabilities_hashmap, default_sample_spec, sbc_auto_caps_table, PA_ELEMENTSOF(sbc_auto_caps_table)); > +} > + > +static uint8_t fill_capabilities_table(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE], const a2dp_sbc_t capabilities_table[], unsigned capabilities_table_elements) { > a2dp_sbc_t *capabilities = (a2dp_sbc_t *) capabilities_buffer; > + unsigned i; > > pa_zero(*capabilities); > > - capabilities->channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | > - SBC_CHANNEL_MODE_JOINT_STEREO; > - capabilities->frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | > - SBC_SAMPLING_FREQ_48000; > - capabilities->allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS; > - capabilities->subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8; > - capabilities->block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16; > - capabilities->min_bitpool = SBC_MIN_BITPOOL; > - capabilities->max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100; > + capabilities->min_bitpool = 0xFF; > + capabilities->max_bitpool = 0x00; > + > + for (i = 0; i < capabilities_table_elements; i++) { > + capabilities->channel_mode |= capabilities_table[i].channel_mode; > + capabilities->frequency |= capabilities_table[i].frequency; > + capabilities->allocation_method |= capabilities_table[i].allocation_method; > + capabilities->subbands |= capabilities_table[i].subbands; > + capabilities->block_length |= capabilities_table[i].block_length; > + if (capabilities->min_bitpool > capabilities_table[i].min_bitpool) > + capabilities->min_bitpool = capabilities_table[i].min_bitpool; > + if (capabilities->max_bitpool < capabilities_table[i].max_bitpool) > + capabilities->max_bitpool = capabilities_table[i].max_bitpool; > + } > + > + pa_assert(capabilities->min_bitpool != 0xFF); > + pa_assert(capabilities->max_bitpool != 0x00); > > return sizeof(*capabilities); > } > > +static uint8_t fill_capabilities_lq(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) { > + return fill_capabilities_table(capabilities_buffer, sbc_lq_caps_table, PA_ELEMENTSOF(sbc_lq_caps_table)); > +} > + > +static uint8_t fill_capabilities_mq(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) { > + return fill_capabilities_table(capabilities_buffer, sbc_mq_caps_table, PA_ELEMENTSOF(sbc_mq_caps_table)); > +} > + > +static uint8_t fill_capabilities_hq(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) { > + return fill_capabilities_table(capabilities_buffer, sbc_hq_caps_table, PA_ELEMENTSOF(sbc_hq_caps_table)); > +} > + > +static uint8_t fill_capabilities_xq1(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) { > + return fill_capabilities_table(capabilities_buffer, sbc_xq1_caps_table, PA_ELEMENTSOF(sbc_xq1_caps_table)); > +} > + > +static uint8_t fill_capabilities_xq2(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) { > + return fill_capabilities_table(capabilities_buffer, sbc_xq2_caps_table, PA_ELEMENTSOF(sbc_xq2_caps_table)); > +} > + > +static uint8_t fill_capabilities(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) { > + return fill_capabilities_table(capabilities_buffer, sbc_auto_caps_table, PA_ELEMENTSOF(sbc_auto_caps_table)); > +} > + > static bool is_configuration_valid(const uint8_t *config_buffer, uint8_t config_size) { > const a2dp_sbc_t *config = (const a2dp_sbc_t *) config_buffer; > > @@ -153,52 +391,76 @@ static bool is_configuration_valid(const uint8_t *config_buffer, uint8_t config_ > return true; > } > > -static uint8_t default_bitpool(uint8_t freq, uint8_t mode) { > - /* These bitpool values were chosen based on the A2DP spec recommendation */ > - switch (freq) { > - case SBC_SAMPLING_FREQ_16000: > - case SBC_SAMPLING_FREQ_32000: > - switch (mode) { > - case SBC_CHANNEL_MODE_MONO: > - case SBC_CHANNEL_MODE_DUAL_CHANNEL: > - case SBC_CHANNEL_MODE_STEREO: > - case SBC_CHANNEL_MODE_JOINT_STEREO: > - return SBC_BITPOOL_HQ_JOINT_STEREO_44100; > - } > - break; > +static bool are_configs_compatible(const a2dp_sbc_t *config1, const a2dp_sbc_t *config2) { > + if (config1->frequency != config2->frequency) > + return false; > > - case SBC_SAMPLING_FREQ_44100: > - switch (mode) { > - case SBC_CHANNEL_MODE_MONO: > - case SBC_CHANNEL_MODE_DUAL_CHANNEL: > - return SBC_BITPOOL_HQ_MONO_44100; > + if (config1->channel_mode != config2->channel_mode) > + return false; > > - case SBC_CHANNEL_MODE_STEREO: > - case SBC_CHANNEL_MODE_JOINT_STEREO: > - return SBC_BITPOOL_HQ_JOINT_STEREO_44100; > - } > - break; > + if (config1->allocation_method != config2->allocation_method) > + return false; > > - case SBC_SAMPLING_FREQ_48000: > - switch (mode) { > - case SBC_CHANNEL_MODE_MONO: > - case SBC_CHANNEL_MODE_DUAL_CHANNEL: > - return SBC_BITPOOL_HQ_MONO_48000; > + if (config1->subbands != config2->subbands) > + return false; > > - case SBC_CHANNEL_MODE_STEREO: > - case SBC_CHANNEL_MODE_JOINT_STEREO: > - return SBC_BITPOOL_HQ_JOINT_STEREO_48000; > - } > - break; > + if (config1->block_length != config2->block_length) > + return false; > + > + /* second config must have constant bitpool */ > + pa_assert(config2->min_bitpool == config2->max_bitpool); > + > + if (config1->min_bitpool > config2->min_bitpool || config1->max_bitpool < config2->min_bitpool) > + return false; > + > + return true; > +} > + > +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) { > + const a2dp_sbc_t *config; > + unsigned i; > + > + if (!is_configuration_valid(config_buffer, config_size)) > + return false; > + > + config = (const a2dp_sbc_t *) config_buffer; > + > + for (i = 0; i < capabilities_table_elements; i++) { > + if (!are_configs_compatible(config, &capabilities_table[i])) > + continue; > + return true; > } > > - pa_assert_not_reached(); > + pa_log_error("Some configuration settings are invalid for current quality"); > + return false; > } > > -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]) { > +static bool is_configuration_valid_lq(const uint8_t *config_buffer, uint8_t config_size) { > + return is_configuration_valid_table(config_buffer, config_size, sbc_lq_caps_table, PA_ELEMENTSOF(sbc_lq_caps_table)); > +} > + > +static bool is_configuration_valid_mq(const uint8_t *config_buffer, uint8_t config_size) { > + return is_configuration_valid_table(config_buffer, config_size, sbc_mq_caps_table, PA_ELEMENTSOF(sbc_mq_caps_table)); > +} > + > +static bool is_configuration_valid_hq(const uint8_t *config_buffer, uint8_t config_size) { > + return is_configuration_valid_table(config_buffer, config_size, sbc_hq_caps_table, PA_ELEMENTSOF(sbc_hq_caps_table)); > +} > + > +static bool is_configuration_valid_xq1(const uint8_t *config_buffer, uint8_t config_size) { > + return is_configuration_valid_table(config_buffer, config_size, sbc_xq1_caps_table, PA_ELEMENTSOF(sbc_xq1_caps_table)); > +} > + > +static bool is_configuration_valid_xq2(const uint8_t *config_buffer, uint8_t config_size) { > + return is_configuration_valid_table(config_buffer, config_size, sbc_xq2_caps_table, PA_ELEMENTSOF(sbc_xq2_caps_table)); > +} > + > +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) { > a2dp_sbc_t *config = (a2dp_sbc_t *) config_buffer; > const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer; > - int i; > + bool is_mono = (default_sample_spec->channels <= 1); > + unsigned i; > + int j; > > static const struct { > uint32_t rate; > @@ -218,96 +480,191 @@ static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample > pa_zero(*config); > > /* Find the lowest freq that is at least as high as the requested sampling rate */ > - for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++) > - if (freq_table[i].rate >= default_sample_spec->rate && (capabilities->frequency & freq_table[i].cap)) { > - config->frequency = freq_table[i].cap; > - break; > + for (j = 0; (unsigned) j < PA_ELEMENTSOF(freq_table); j++) { > + if (freq_table[j].rate >= default_sample_spec->rate && (capabilities->frequency & freq_table[j].cap)) { > + for (i = 0; i < capabilities_table_elements; i++) { > + if (capabilities_table[i].frequency & freq_table[j].cap) { > + config->frequency = freq_table[j].cap; > + break; > + } > + } > + if (i != capabilities_table_elements) > + break; > } > + } > > - if ((unsigned) i == PA_ELEMENTSOF(freq_table)) { > - for (--i; i >= 0; i--) { > - if (capabilities->frequency & freq_table[i].cap) { > - config->frequency = freq_table[i].cap; > - break; > + if ((unsigned) j == PA_ELEMENTSOF(freq_table)) { > + for (--j; j >= 0; j--) { > + if (capabilities->frequency & freq_table[j].cap) { > + for (i = 0; i < capabilities_table_elements; i++) { > + if (capabilities_table[i].frequency & freq_table[j].cap) { > + config->frequency = freq_table[j].cap; > + break; > + } > + } > + if (i != capabilities_table_elements) > + break; > } > } > > - if (i < 0) { > - pa_log_error("Not suitable sample rate"); > + if (j < 0) { > + pa_log_error("No suitable sample rate"); > return 0; > } > } > > - pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table)); > - > - if (default_sample_spec->channels <= 1) { > - if (capabilities->channel_mode & SBC_CHANNEL_MODE_MONO) > - config->channel_mode = SBC_CHANNEL_MODE_MONO; > - else if (capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) > - config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; > - else if (capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO) > - config->channel_mode = SBC_CHANNEL_MODE_STEREO; > - else if (capabilities->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) > - config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; > + pa_assert((unsigned) j < PA_ELEMENTSOF(freq_table)); > + > + for (i = 0; i < capabilities_table_elements; i++) { > + if ((capabilities->block_length & SBC_BLOCK_LENGTH_16) && (capabilities_table[i].block_length & SBC_BLOCK_LENGTH_16)) > + config->block_length = SBC_BLOCK_LENGTH_16; > + else if ((capabilities->block_length & SBC_BLOCK_LENGTH_12) && (capabilities_table[i].block_length & SBC_BLOCK_LENGTH_12)) > + config->block_length = SBC_BLOCK_LENGTH_12; > + else if ((capabilities->block_length & SBC_BLOCK_LENGTH_8) && (capabilities_table[i].block_length & SBC_BLOCK_LENGTH_8)) > + config->block_length = SBC_BLOCK_LENGTH_8; > + else if ((capabilities->block_length & SBC_BLOCK_LENGTH_4) && (capabilities_table[i].block_length & SBC_BLOCK_LENGTH_4)) > + config->block_length = SBC_BLOCK_LENGTH_4; > else { > - pa_log_error("No supported channel modes"); > - return 0; > + pa_log_debug("No supported block lengths in table %u", i); > + continue; > } > - } else { > - if (capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) > - config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; > - else if (capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO) > - config->channel_mode = SBC_CHANNEL_MODE_STEREO; > - else if (capabilities->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) > - config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; > - else if (capabilities->channel_mode & SBC_CHANNEL_MODE_MONO) > - config->channel_mode = SBC_CHANNEL_MODE_MONO; > + > + if ((capabilities->subbands & SBC_SUBBANDS_8) && (capabilities_table[i].subbands & SBC_SUBBANDS_8)) > + config->subbands = SBC_SUBBANDS_8; > + else if ((capabilities->subbands & SBC_SUBBANDS_4) && (capabilities_table[i].subbands & SBC_SUBBANDS_4)) > + config->subbands = SBC_SUBBANDS_4; > else { > - pa_log_error("No supported channel modes"); > - return 0; > + pa_log_debug("No supported subbands in table %u", i); > + continue; > } > - } > > - if (capabilities->block_length & SBC_BLOCK_LENGTH_16) > - config->block_length = SBC_BLOCK_LENGTH_16; > - else if (capabilities->block_length & SBC_BLOCK_LENGTH_12) > - config->block_length = SBC_BLOCK_LENGTH_12; > - else if (capabilities->block_length & SBC_BLOCK_LENGTH_8) > - config->block_length = SBC_BLOCK_LENGTH_8; > - else if (capabilities->block_length & SBC_BLOCK_LENGTH_4) > - config->block_length = SBC_BLOCK_LENGTH_4; > - else { > - pa_log_error("No supported block lengths"); > - return 0; > - } > + if ((capabilities->allocation_method & SBC_ALLOCATION_LOUDNESS) && (capabilities_table[i].allocation_method & SBC_ALLOCATION_LOUDNESS)) > + config->allocation_method = SBC_ALLOCATION_LOUDNESS; > + else if ((capabilities->allocation_method & SBC_ALLOCATION_SNR) && (capabilities_table[i].allocation_method & SBC_ALLOCATION_SNR)) > + config->allocation_method = SBC_ALLOCATION_SNR; > + else { > + pa_log_debug("No supported allocation method in table %u", i); > + continue; > + } > > - if (capabilities->subbands & SBC_SUBBANDS_8) > - config->subbands = SBC_SUBBANDS_8; > - else if (capabilities->subbands & SBC_SUBBANDS_4) > - config->subbands = SBC_SUBBANDS_4; > - else { > - pa_log_error("No supported subbands"); > - return 0; > + if (is_mono) { > + if ((capabilities->channel_mode & SBC_CHANNEL_MODE_MONO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_MONO)) > + config->channel_mode = SBC_CHANNEL_MODE_MONO; > + else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)) > + config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; > + else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_STEREO)) > + config->channel_mode = SBC_CHANNEL_MODE_STEREO; > + else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)) > + config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; > + else { > + pa_log_debug("No supported channel mode in table %u", i); > + continue; > + } > + } else { > + if ((capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)) > + config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; > + else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_STEREO)) > + config->channel_mode = SBC_CHANNEL_MODE_STEREO; > + else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)) > + config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; > + else if ((capabilities->channel_mode & SBC_CHANNEL_MODE_MONO) && (capabilities_table[i].channel_mode & SBC_CHANNEL_MODE_MONO)) > + config->channel_mode = SBC_CHANNEL_MODE_MONO; > + else { > + pa_log_debug("No supported channel mode in table %u", i); > + continue; > + } > + } > + > + config->min_bitpool = PA_MAX(capabilities->min_bitpool, capabilities_table[i].min_bitpool); > + config->max_bitpool = PA_MIN(capabilities->max_bitpool, capabilities_table[i].max_bitpool); > + > + if (config->min_bitpool > config->max_bitpool) { > + pa_log_debug("No supported bitpool in table %u [%u, %u], need [%u, %u]", i, capabilities_table[i].min_bitpool, capabilities_table[i].max_bitpool, capabilities->min_bitpool, capabilities->max_bitpool); > + continue; > + } > + > + break; > } > > - if (capabilities->allocation_method & SBC_ALLOCATION_LOUDNESS) > - config->allocation_method = SBC_ALLOCATION_LOUDNESS; > - else if (capabilities->allocation_method & SBC_ALLOCATION_SNR) > - config->allocation_method = SBC_ALLOCATION_SNR; > - else { > - pa_log_error("No supported allocation method"); > + if (i == capabilities_table_elements) { > + pa_log_error("No supported configuration"); > return 0; > } > > - config->min_bitpool = (uint8_t) PA_MAX(SBC_MIN_BITPOOL, capabilities->min_bitpool); > - config->max_bitpool = (uint8_t) PA_MIN(default_bitpool(config->frequency, config->channel_mode), capabilities->max_bitpool); > + return sizeof(*config); > +} > > - if (config->min_bitpool > config->max_bitpool) { > - pa_log_error("No supported bitpool"); > - return 0; > +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]) { > + return fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_lq_caps_table, PA_ELEMENTSOF(sbc_lq_caps_table)); > +} > + > +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]) { > + return fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_mq_caps_table, PA_ELEMENTSOF(sbc_mq_caps_table)); > +} > + > +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]) { > + return fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_hq_caps_table, PA_ELEMENTSOF(sbc_hq_caps_table)); > +} > + > +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]) { > + return fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_xq1_caps_table, PA_ELEMENTSOF(sbc_xq1_caps_table)); > +} > + > +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]) { > + return fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_xq2_caps_table, PA_ELEMENTSOF(sbc_xq2_caps_table)); > +} > + > +static uint8_t default_bitpool(uint8_t freq, uint8_t mode) { > + /* These bitpool values were chosen based on the A2DP spec recommendation */ > + switch (freq) { > + case SBC_SAMPLING_FREQ_16000: > + case SBC_SAMPLING_FREQ_32000: > + switch (mode) { > + case SBC_CHANNEL_MODE_MONO: > + case SBC_CHANNEL_MODE_DUAL_CHANNEL: > + case SBC_CHANNEL_MODE_STEREO: > + case SBC_CHANNEL_MODE_JOINT_STEREO: > + return SBC_BITPOOL_HQ_JOINT_STEREO_44100; > + } > + break; > + > + case SBC_SAMPLING_FREQ_44100: > + switch (mode) { > + case SBC_CHANNEL_MODE_MONO: > + case SBC_CHANNEL_MODE_DUAL_CHANNEL: > + return SBC_BITPOOL_HQ_MONO_44100; > + > + case SBC_CHANNEL_MODE_STEREO: > + case SBC_CHANNEL_MODE_JOINT_STEREO: > + return SBC_BITPOOL_HQ_JOINT_STEREO_44100; > + } > + break; > + > + case SBC_SAMPLING_FREQ_48000: > + switch (mode) { > + case SBC_CHANNEL_MODE_MONO: > + case SBC_CHANNEL_MODE_DUAL_CHANNEL: > + return SBC_BITPOOL_HQ_MONO_48000; > + > + case SBC_CHANNEL_MODE_STEREO: > + case SBC_CHANNEL_MODE_JOINT_STEREO: > + return SBC_BITPOOL_HQ_JOINT_STEREO_48000; > + } > + break; > } > > - return sizeof(*config); > + pa_assert_not_reached(); > +} > + > +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]) { > + a2dp_sbc_t *config = (a2dp_sbc_t *) config_buffer; > + uint8_t ret; > + > + ret = fill_preferred_configuration_table(default_sample_spec, capabilities_buffer, capabilities_size, config_buffer, sbc_auto_caps_table, PA_ELEMENTSOF(sbc_auto_caps_table)); > + config->max_bitpool = PA_MIN(default_bitpool(config->frequency, config->channel_mode), config->max_bitpool); > + config->max_bitpool = PA_MAX(config->max_bitpool, config->min_bitpool); > + > + return ret; > } > > static void set_params(struct sbc_info *sbc_info) { > @@ -500,13 +857,17 @@ static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) { > uint8_t bitpool; > > /* Check if bitpool is already at its limit */ > - if (sbc_info->sbc.bitpool <= SBC_BITPOOL_DEC_LIMIT) > - return 0; > - > - bitpool = sbc_info->sbc.bitpool - SBC_BITPOOL_DEC_STEP; > - > - if (bitpool < SBC_BITPOOL_DEC_LIMIT) > - bitpool = SBC_BITPOOL_DEC_LIMIT; > + if (sbc_info->mode == SBC_CHANNEL_MODE_MONO || sbc_info->mode == SBC_CHANNEL_MODE_DUAL_CHANNEL) { > + /* For Mono and Dual Channel modes bitpool value is separete for each channel */ > + bitpool = sbc_info->sbc.bitpool - SBC_SEPARATE_BITPOOL_DEC_STEP; > + if (bitpool <= SBC_SEPARATE_BITPOOL_DEC_LIMIT) > + return 0; > + } else { > + /* For Stereo modes bitpool value is combined for both channels */ > + bitpool = sbc_info->sbc.bitpool - SBC_COMBINED_BITPOOL_DEC_STEP; > + if (bitpool <= SBC_COMBINED_BITPOOL_DEC_LIMIT) > + return 0; > + } > > if (sbc_info->sbc.bitpool == bitpool) > return 0; > @@ -515,6 +876,10 @@ static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) { > return get_block_size(codec_info, write_link_mtu); > } > > +static size_t reduce_encoder_bitrate_none(void *codec_info, size_t write_link_mtu) { > + return 0; > +} > + > 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) { > struct sbc_info *sbc_info = (struct sbc_info *) codec_info; > struct rtp_header *header; > @@ -662,9 +1027,69 @@ static size_t decode_buffer(void *codec_info, uint32_t *timestamp, const uint8_t > return d - output_buffer; > } > > +const pa_a2dp_codec pa_a2dp_codec_sbc_lq = { > + .name = "sbc_lq", > + .description = "SBC (Low Quality)", > + .id = { A2DP_CODEC_SBC, 0, 0 }, > + .support_backchannel = false, > + .can_accept_capabilities = can_accept_capabilities_lq, > + .choose_remote_endpoint = choose_remote_endpoint_lq, > + .fill_capabilities = fill_capabilities_lq, > + .is_configuration_valid = is_configuration_valid_lq, > + .fill_preferred_configuration = fill_preferred_configuration_lq, > + .init = init, > + .deinit = deinit, > + .reset = reset, > + .get_read_block_size = get_block_size, > + .get_write_block_size = get_block_size, > + .reduce_encoder_bitrate = reduce_encoder_bitrate_none, > + .encode_buffer = encode_buffer, > + .decode_buffer = decode_buffer, > +}; > + > +const pa_a2dp_codec pa_a2dp_codec_sbc_mq = { > + .name = "sbc_mq", > + .description = "SBC (Middle Quality)", > + .id = { A2DP_CODEC_SBC, 0, 0 }, > + .support_backchannel = false, > + .can_accept_capabilities = can_accept_capabilities_mq, > + .choose_remote_endpoint = choose_remote_endpoint_mq, > + .fill_capabilities = fill_capabilities_mq, > + .is_configuration_valid = is_configuration_valid_mq, > + .fill_preferred_configuration = fill_preferred_configuration_mq, > + .init = init, > + .deinit = deinit, > + .reset = reset, > + .get_read_block_size = get_block_size, > + .get_write_block_size = get_block_size, > + .reduce_encoder_bitrate = reduce_encoder_bitrate_none, > + .encode_buffer = encode_buffer, > + .decode_buffer = decode_buffer, > +}; > + > +const pa_a2dp_codec pa_a2dp_codec_sbc_hq = { > + .name = "sbc_hq", > + .description = "SBC (High Quality)", > + .id = { A2DP_CODEC_SBC, 0, 0 }, > + .support_backchannel = false, > + .can_accept_capabilities = can_accept_capabilities_hq, > + .choose_remote_endpoint = choose_remote_endpoint_hq, > + .fill_capabilities = fill_capabilities_hq, > + .is_configuration_valid = is_configuration_valid_hq, > + .fill_preferred_configuration = fill_preferred_configuration_hq, > + .init = init, > + .deinit = deinit, > + .reset = reset, > + .get_read_block_size = get_block_size, > + .get_write_block_size = get_block_size, > + .reduce_encoder_bitrate = reduce_encoder_bitrate_none, > + .encode_buffer = encode_buffer, > + .decode_buffer = decode_buffer, > +}; > + > const pa_a2dp_codec pa_a2dp_codec_sbc = { > .name = "sbc", > - .description = "SBC", > + .description = "SBC (Automatic Quality)", > .id = { A2DP_CODEC_SBC, 0, 0 }, > .support_backchannel = false, > .can_accept_capabilities = can_accept_capabilities, > @@ -681,3 +1106,43 @@ const pa_a2dp_codec pa_a2dp_codec_sbc = { > .encode_buffer = encode_buffer, > .decode_buffer = decode_buffer, > }; > + > +const pa_a2dp_codec pa_a2dp_codec_sbc_xq1 = { > + .name = "sbc_xq1", > + .description = "SBC (eXtreme Quality profile 1)", > + .id = { A2DP_CODEC_SBC, 0, 0 }, > + .support_backchannel = false, > + .can_accept_capabilities = can_accept_capabilities_xq1, > + .choose_remote_endpoint = choose_remote_endpoint_xq1, > + .fill_capabilities = fill_capabilities_xq1, > + .is_configuration_valid = is_configuration_valid_xq1, > + .fill_preferred_configuration = fill_preferred_configuration_xq1, > + .init = init, > + .deinit = deinit, > + .reset = reset, > + .get_read_block_size = get_block_size, > + .get_write_block_size = get_block_size, > + .reduce_encoder_bitrate = reduce_encoder_bitrate_none, > + .encode_buffer = encode_buffer, > + .decode_buffer = decode_buffer, > +}; > + > +const pa_a2dp_codec pa_a2dp_codec_sbc_xq2 = { > + .name = "sbc_xq2", > + .description = "SBC (eXtreme Quality profile 2)", > + .id = { A2DP_CODEC_SBC, 0, 0 }, > + .support_backchannel = false, > + .can_accept_capabilities = can_accept_capabilities_xq2, > + .choose_remote_endpoint = choose_remote_endpoint_xq2, > + .fill_capabilities = fill_capabilities_xq2, > + .is_configuration_valid = is_configuration_valid_xq2, > + .fill_preferred_configuration = fill_preferred_configuration_xq2, > + .init = init, > + .deinit = deinit, > + .reset = reset, > + .get_read_block_size = get_block_size, > + .get_write_block_size = get_block_size, > + .reduce_encoder_bitrate = reduce_encoder_bitrate_none, > + .encode_buffer = encode_buffer, > + .decode_buffer = decode_buffer, > +}; > diff --git a/src/modules/bluetooth/a2dp-codec-util.c b/src/modules/bluetooth/a2dp-codec-util.c > index 521251aea..7b123f7e1 100644 > --- a/src/modules/bluetooth/a2dp-codec-util.c > +++ b/src/modules/bluetooth/a2dp-codec-util.c > @@ -26,24 +26,38 @@ > > #include "a2dp-codec-util.h" > > +extern const pa_a2dp_codec pa_a2dp_codec_sbc_lq; > extern const pa_a2dp_codec pa_a2dp_codec_faststream; > extern const pa_a2dp_codec pa_a2dp_codec_faststream_mic; > +extern const pa_a2dp_codec pa_a2dp_codec_sbc_mq; > extern const pa_a2dp_codec pa_a2dp_codec_sbc; > #ifdef HAVE_OPENAPTX > extern const pa_a2dp_codec pa_a2dp_codec_aptx; > +#endif > +extern const pa_a2dp_codec pa_a2dp_codec_sbc_hq; > +#ifdef HAVE_OPENAPTX > extern const pa_a2dp_codec pa_a2dp_codec_aptx_hd; > #endif > +extern const pa_a2dp_codec pa_a2dp_codec_sbc_xq1; > +extern const pa_a2dp_codec pa_a2dp_codec_sbc_xq2; > > /* This is list of supported codecs. Their order is important. > * Codec with higher index has higher priority. */ > const pa_a2dp_codec *pa_a2dp_codecs[] = { > - &pa_a2dp_codec_faststream, > - &pa_a2dp_codec_faststream_mic, > - &pa_a2dp_codec_sbc, > + &pa_a2dp_codec_sbc_lq, > + &pa_a2dp_codec_faststream, /* Exactly same as SBC-LQ, but could provide lower latency */ > + &pa_a2dp_codec_faststream_mic, /* Exactly same as FastStream, but with voice backchannel */ > + &pa_a2dp_codec_sbc_mq, > + &pa_a2dp_codec_sbc, /* SBC in automatic mode, from SBC-LQ to SBC-HQ; not SBC-XQ */ > #ifdef HAVE_OPENAPTX > &pa_a2dp_codec_aptx, > +#endif > + &pa_a2dp_codec_sbc_hq, /* SBC-HQ has similar quality as aptX */ > +#ifdef HAVE_OPENAPTX > &pa_a2dp_codec_aptx_hd, > #endif > + &pa_a2dp_codec_sbc_xq1, /* SBC-XQ has similar quality as aptX-HD */ > + &pa_a2dp_codec_sbc_xq2, /* SBC-XQ has similar quality as aptX-HD */ > }; > > unsigned int pa_bluetooth_a2dp_codec_count(void) { > -- > 2.11.0 > > _______________________________________________ > pulseaudio-discuss mailing list > pulseaudio-discuss@xxxxxxxxxxxxxxxxxxxxx > https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss _______________________________________________ pulseaudio-discuss mailing list pulseaudio-discuss@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss