Negociation wth Divacore Addict headset, resulting in Dual Channel 2x47kb bitpool : HCI sniffer - Bluetooth packet analyzer ver 5.50 device: hci0 snap_len: 1500 filter: 0x400 < AVDTP(s): Discover cmd: transaction 7 nsp 0x00 > AVDTP(s): Discover rsp: transaction 7 nsp 0x00 ACP SEID 1 - Audio Sink ACP SEID 5 - Audio Sink ACP SEID 3 - Audio Sink ACP SEID 2 - Audio Sink < AVDTP(s): Capabilities cmd: transaction 8 nsp 0x00 ACP SEID 1 > AVDTP(s): Capabilities rsp: transaction 8 nsp 0x00 Media Transport Media Codec - SBC 16kHz 32kHz 44.1kHz 48kHz Mono DualChannel Stereo JointStereo 4 8 12 16 Blocks 4 8 Subbands SNR Loudness Bitpool Range 2-53 Content Protection 02 00 < AVDTP(s): Capabilities cmd: transaction 9 nsp 0x00 ACP SEID 5 > AVDTP(s): Capabilities rsp: transaction 9 nsp 0x00 Media Transport Media Codec - non-A2DP (aptX) 16kHz 32kHz 44.1kHz 48kHz Stereo < AVDTP(s): Capabilities cmd: transaction 10 nsp 0x00 ACP SEID 3 > AVDTP(s): Capabilities rsp: transaction 10 nsp 0x00 Media Transport Media Codec - MPEG-2,4 AAC MPEG-2 AAC LC MPEG-4 AAC LC 8kHz 11.025kHz 12kHz 16kHz 22.05kHz 24kHz 32kHz 44.1kHz 48kHz 1 2 Channels 320000bps VBR Content Protection 02 00 < AVDTP(s): Capabilities cmd: transaction 11 nsp 0x00 ACP SEID 2 > AVDTP(s): Capabilities rsp: transaction 11 nsp 0x00 Media Transport Media Codec - MPEG-1,2 Audio Layers: 3 CRC Protection: Yes Mono DualChannel Stereo JointStereo Media Payload Format: RFC-2250 16kHz 22.05kHz 24kHz 32kHz 44.1kHz 48kHz VBR: Yes Bit Rate Indexes: n/a Content Protection 02 00 < AVDTP(s): Set config cmd: transaction 12 nsp 0x00 ACP SEID 1 - INT SEID 2 Media Transport Media Codec - SBC 44.1kHz DualChannel 16 Blocks 8 Subbands Loudness Bitpool Range 2-47 19.09.2019, 10:13, "Hyperion" <h1p8r10n@xxxxxxxxxx>: > Negociation wth Harman Kardon Onyx, resulting in Dual Channel 2x47kb bitpool : > > device: hci0 snap_len: 1500 filter: 0x400 > < AVDTP(s): Discover cmd: transaction 15 nsp 0x00 >> AVDTP(s): Discover rsp: transaction 15 nsp 0x00 > > ACP SEID 1 - Audio Sink > ACP SEID 2 - Audio Sink > ACP SEID 3 - Audio Sink > < AVDTP(s): All Capabilities cmd: transaction 0 nsp 0x00 > ACP SEID 1 >> AVDTP(s): All Capabilities rsp: transaction 0 nsp 0x00 > > Media Transport > Reporting > Media Codec - SBC > 16kHz 32kHz 44.1kHz 48kHz > Mono DualChannel Stereo JointStereo > 4 8 12 16 Blocks > 4 8 Subbands > SNR Loudness > Bitpool Range 2-89 > Content Protection > 02 00 > Delay Reporting > < AVDTP(s): All Capabilities cmd: transaction 1 nsp 0x00 > ACP SEID 2 >> AVDTP(s): All Capabilities rsp: transaction 1 nsp 0x00 > > Media Transport > Reporting > Media Codec - SBC > 16kHz 32kHz 44.1kHz 48kHz > Mono DualChannel Stereo JointStereo > 4 8 12 16 Blocks > 4 8 Subbands > SNR Loudness > Bitpool Range 2-89 > Content Protection > 02 00 > Delay Reporting > < AVDTP(s): All Capabilities cmd: transaction 2 nsp 0x00 > ACP SEID 3 >> AVDTP(s): All Capabilities rsp: transaction 2 nsp 0x00 > > Media Transport > Reporting > Media Codec - SBC > 16kHz 32kHz 44.1kHz 48kHz > Mono DualChannel Stereo JointStereo > 4 8 12 16 Blocks > 4 8 Subbands > SNR Loudness > Bitpool Range 2-89 > Content Protection > 02 00 > Delay Reporting > < AVDTP(s): Set config cmd: transaction 3 nsp 0x00 > ACP SEID 1 - INT SEID 2 > Media Transport > Media Codec - SBC > 44.1kHz > DualChannel > 16 Blocks > 8 Subbands > Loudness > Bitpool Range 2-47 > > 18.09.2019, 13:02, "Hyperion" <h1p8r10n@xxxxxxxxxx>: >> Patch V2 with added DUAL_CHANNEL as preferred mode. >> >> Works without any issue on more than 10 stereo and mono devices that I have here. >> >> http://download.zenwalk.org/x86_64/testing/pulseaudio-13.0-SBC-XQ_V2.patch >> >> /*** >> This file is part of PulseAudio. >> >> Copyright 2018-2019 Pali Rohár <pali.rohar@xxxxxxxxx> >> >> PulseAudio is free software; you can redistribute it and/or modify >> it under the terms of the GNU Lesser General Public License as >> published by the Free Software Foundation; either version 2.1 of the >> License, or (at your option) any later version. >> >> PulseAudio is distributed in the hope that it will be useful, but >> WITHOUT ANY WARRANTY; without even the implied warranty of >> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> General Public License for more details. >> >> You should have received a copy of the GNU Lesser General Public >> License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. >> ***/ >> >> #ifdef HAVE_CONFIG_H >> #include <config.h> >> #endif >> >> #include <pulsecore/core-util.h> >> #include <pulsecore/log.h> >> #include <pulsecore/macro.h> >> #include <pulsecore/once.h> >> #include <pulse/sample.h> >> #include <pulse/xmalloc.h> >> >> #include <arpa/inet.h> >> >> #include <sbc/sbc.h> >> >> #include "a2dp-codecs.h" >> #include "a2dp-codec-api.h" >> #include "rtp.h" >> >> #define SBC_BITPOOL_DEC_LIMIT 32 >> #define SBC_BITPOOL_DEC_STEP 5 >> >> struct sbc_info { >> sbc_t sbc; /* Codec data */ >> size_t codesize, frame_length; /* SBC Codesize, frame_length. We simply cache those values here */ >> uint16_t seq_num; /* Cumulative packet sequence */ >> uint8_t frequency; >> uint8_t blocks; >> uint8_t subbands; >> uint8_t mode; >> uint8_t allocation; >> uint8_t initial_bitpool; >> uint8_t min_bitpool; >> 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; >> >> if (capabilities_size != sizeof(*capabilities)) >> return false; >> >> if (!(capabilities->frequency & (SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000))) >> return false; >> >> if (!(capabilities->channel_mode & (SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO))) >> return false; >> >> if (!(capabilities->allocation_method & (SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS))) >> return false; >> >> if (!(capabilities->subbands & (SBC_SUBBANDS_4 | SBC_SUBBANDS_8))) >> return false; >> >> if (!(capabilities->block_length & (SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16))) >> 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) { >> const pa_a2dp_codec_capabilities *a2dp_capabilities; >> const char *key; >> void *state; >> >> /* There is no preference, just choose random valid entry */ >> PA_HASHMAP_FOREACH_KV(key, a2dp_capabilities, capabilities_hashmap, state) { >> if (can_accept_capabilities(a2dp_capabilities->buffer, a2dp_capabilities->size, for_encoding)) >> return key; >> } >> >> return NULL; >> } >> >> static uint8_t fill_capabilities(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) { >> a2dp_sbc_t *capabilities = (a2dp_sbc_t *) capabilities_buffer; >> >> 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; >> >> return sizeof(*capabilities); >> } >> >> 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; >> >> if (config_size != sizeof(*config)) { >> pa_log_error("Invalid size of config buffer"); >> return false; >> } >> >> if (config->frequency != SBC_SAMPLING_FREQ_16000 && config->frequency != SBC_SAMPLING_FREQ_32000 && >> config->frequency != SBC_SAMPLING_FREQ_44100 && config->frequency != SBC_SAMPLING_FREQ_48000) { >> pa_log_error("Invalid sampling frequency in configuration"); >> return false; >> } >> >> if (config->channel_mode != SBC_CHANNEL_MODE_MONO && config->channel_mode != SBC_CHANNEL_MODE_DUAL_CHANNEL && >> config->channel_mode != SBC_CHANNEL_MODE_STEREO && config->channel_mode != SBC_CHANNEL_MODE_JOINT_STEREO) { >> pa_log_error("Invalid channel mode in configuration"); >> return false; >> } >> >> if (config->allocation_method != SBC_ALLOCATION_SNR && config->allocation_method != SBC_ALLOCATION_LOUDNESS) { >> pa_log_error("Invalid allocation method in configuration"); >> return false; >> } >> >> if (config->subbands != SBC_SUBBANDS_4 && config->subbands != SBC_SUBBANDS_8) { >> pa_log_error("Invalid SBC subbands in configuration"); >> return false; >> } >> >> if (config->block_length != SBC_BLOCK_LENGTH_4 && config->block_length != SBC_BLOCK_LENGTH_8 && >> config->block_length != SBC_BLOCK_LENGTH_12 && config->block_length != SBC_BLOCK_LENGTH_16) { >> pa_log_error("Invalid block length in configuration"); >> return false; >> } >> >> if (config->min_bitpool > config->max_bitpool) { >> pa_log_error("Invalid bitpool in configuration"); >> return false; >> } >> >> 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_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; >> 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_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; >> } >> >> 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; >> const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer; >> int i; >> >> 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 } >> }; >> >> if (capabilities_size != sizeof(*capabilities)) { >> pa_log_error("Invalid size of capabilities buffer"); >> return 0; >> } >> >> 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; >> } >> >> 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 (i < 0) { >> pa_log_error("Not 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; >> else { >> pa_log_error("No supported channel modes"); >> return 0; >> } >> } 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_STEREO) >> config->channel_mode = SBC_CHANNEL_MODE_STEREO; >> 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_MONO) >> config->channel_mode = SBC_CHANNEL_MODE_MONO; >> else { >> pa_log_error("No supported channel modes"); >> return 0; >> } >> } >> >> 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->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 (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"); >> 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); >> >> if (config->min_bitpool > config->max_bitpool) { >> pa_log_error("No supported bitpool"); >> return 0; >> } >> >> return sizeof(*config); >> } >> >> static void set_params(struct sbc_info *sbc_info) { >> sbc_info->sbc.frequency = sbc_info->frequency; >> sbc_info->sbc.blocks = sbc_info->blocks; >> sbc_info->sbc.subbands = sbc_info->subbands; >> sbc_info->sbc.mode = sbc_info->mode; >> sbc_info->sbc.allocation = sbc_info->allocation; >> sbc_info->sbc.bitpool = sbc_info->initial_bitpool; >> sbc_info->sbc.endian = SBC_LE; >> >> sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); >> sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); >> } >> >> static void *init(bool for_encoding, bool for_backchannel, const uint8_t *config_buffer, uint8_t config_size, pa_sample_spec *sample_spec) { >> struct sbc_info *sbc_info; >> const a2dp_sbc_t *config = (const a2dp_sbc_t *) config_buffer; >> int ret; >> >> pa_assert(config_size == sizeof(*config)); >> pa_assert(!for_backchannel); >> >> sbc_info = pa_xnew0(struct sbc_info, 1); >> >> ret = sbc_init(&sbc_info->sbc, 0); >> if (ret != 0) { >> pa_xfree(sbc_info); >> pa_log_error("SBC initialization failed: %d", ret); >> return NULL; >> } >> >> sample_spec->format = PA_SAMPLE_S16LE; >> >> switch (config->frequency) { >> case SBC_SAMPLING_FREQ_48000: >> sbc_info->frequency = SBC_FREQ_48000; >> sample_spec->rate = 48000U; >> break; >> case SBC_SAMPLING_FREQ_44100: >> sbc_info->frequency = SBC_FREQ_44100; >> sample_spec->rate = 44100U; >> break; >> case SBC_SAMPLING_FREQ_32000: >> sbc_info->frequency = SBC_FREQ_32000; >> sample_spec->rate = 32000U; >> break; >> case SBC_SAMPLING_FREQ_16000: >> sbc_info->frequency = SBC_FREQ_16000; >> sample_spec->rate = 16000U; >> break; >> default: >> pa_assert_not_reached(); >> } >> >> switch (config->channel_mode) { >> case SBC_CHANNEL_MODE_DUAL_CHANNEL: >> sbc_info->mode = SBC_MODE_DUAL_CHANNEL; >> sample_spec->channels = 2; >> break; >> case SBC_CHANNEL_MODE_STEREO: >> sbc_info->mode = SBC_MODE_STEREO; >> sample_spec->channels = 2; >> break; >> case SBC_CHANNEL_MODE_JOINT_STEREO: >> sbc_info->mode = SBC_MODE_JOINT_STEREO; >> sample_spec->channels = 2; >> break; >> case SBC_CHANNEL_MODE_MONO: >> sbc_info->mode = SBC_MODE_MONO; >> sample_spec->channels = 1; >> break; >> default: >> pa_assert_not_reached(); >> } >> >> switch (config->allocation_method) { >> case SBC_ALLOCATION_LOUDNESS: >> sbc_info->allocation = SBC_AM_LOUDNESS; >> break; >> case SBC_ALLOCATION_SNR: >> sbc_info->allocation = SBC_AM_SNR; >> break; >> default: >> pa_assert_not_reached(); >> } >> >> switch (config->subbands) { >> case SBC_SUBBANDS_4: >> sbc_info->subbands = SBC_SB_4; >> break; >> case SBC_SUBBANDS_8: >> sbc_info->subbands = SBC_SB_8; >> break; >> default: >> pa_assert_not_reached(); >> } >> >> switch (config->block_length) { >> case SBC_BLOCK_LENGTH_4: >> sbc_info->blocks = SBC_BLK_4; >> break; >> case SBC_BLOCK_LENGTH_8: >> sbc_info->blocks = SBC_BLK_8; >> break; >> case SBC_BLOCK_LENGTH_12: >> sbc_info->blocks = SBC_BLK_12; >> break; >> case SBC_BLOCK_LENGTH_16: >> sbc_info->blocks = SBC_BLK_16; >> break; >> default: >> pa_assert_not_reached(); >> } >> >> sbc_info->min_bitpool = config->min_bitpool; >> sbc_info->max_bitpool = config->max_bitpool; >> >> /* Set minimum bitpool for source to get the maximum possible block_size >> * in get_block_size() function. This block_size is length of buffer used >> * for decoded audio data and so is inversely proportional to frame length >> * which depends on bitpool value. Bitpool is controlled by other side from >> * range [min_bitpool, max_bitpool]. */ >> sbc_info->initial_bitpool = for_encoding ? sbc_info->max_bitpool : sbc_info->min_bitpool; >> >> set_params(sbc_info); >> >> pa_log_info("SBC parameters: allocation=%s, subbands=%u, blocks=%u, mode=%s bitpool=%u codesize=%u frame_length=%u", >> sbc_info->sbc.allocation ? "SNR" : "Loudness", sbc_info->sbc.subbands ? 8 : 4, >> (sbc_info->sbc.blocks+1)*4, sbc_info->sbc.mode == SBC_MODE_MONO ? "Mono" : >> sbc_info->sbc.mode == SBC_MODE_DUAL_CHANNEL ? "DualChannel" : >> sbc_info->sbc.mode == SBC_MODE_STEREO ? "Stereo" : "JointStereo", >> sbc_info->sbc.bitpool, (unsigned)sbc_info->codesize, (unsigned)sbc_info->frame_length); >> >> return sbc_info; >> } >> >> static void deinit(void *codec_info) { >> struct sbc_info *sbc_info = (struct sbc_info *) codec_info; >> >> sbc_finish(&sbc_info->sbc); >> pa_xfree(sbc_info); >> } >> >> static void set_bitpool(struct sbc_info *sbc_info, uint8_t bitpool) { >> if (bitpool > sbc_info->max_bitpool) >> bitpool = sbc_info->max_bitpool; >> else if (bitpool < sbc_info->min_bitpool) >> bitpool = sbc_info->min_bitpool; >> >> sbc_info->sbc.bitpool = bitpool; >> >> sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); >> sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); >> >> pa_log_debug("Bitpool has changed to %u", sbc_info->sbc.bitpool); >> } >> >> static int reset(void *codec_info) { >> struct sbc_info *sbc_info = (struct sbc_info *) codec_info; >> int ret; >> >> ret = sbc_reinit(&sbc_info->sbc, 0); >> if (ret != 0) { >> pa_log_error("SBC reinitialization failed: %d", ret); >> return -1; >> } >> >> /* sbc_reinit() sets also default parameters, so reset them back */ >> set_params(sbc_info); >> >> sbc_info->seq_num = 0; >> return 0; >> } >> >> static size_t get_block_size(void *codec_info, size_t link_mtu) { >> struct sbc_info *sbc_info = (struct sbc_info *) codec_info; >> size_t rtp_size = sizeof(struct rtp_header) + sizeof(struct rtp_sbc_payload); >> size_t frame_count = (link_mtu - rtp_size) / sbc_info->frame_length; >> >> /* frame_count is only 4 bit number */ >> if (frame_count > 15) >> frame_count = 15; >> >> return frame_count * sbc_info->codesize; >> } >> >> static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) { >> struct sbc_info *sbc_info = (struct sbc_info *) codec_info; >> 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->sbc.bitpool == bitpool) >> return 0; >> >> set_bitpool(sbc_info, bitpool); >> return get_block_size(codec_info, write_link_mtu); >> } >> >> 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; >> struct rtp_sbc_payload *payload; >> uint8_t *d; >> const uint8_t *p; >> size_t to_write, to_encode; >> uint8_t frame_count; >> >> header = (struct rtp_header*) output_buffer; >> payload = (struct rtp_sbc_payload*) (output_buffer + sizeof(*header)); >> >> frame_count = 0; >> >> p = input_buffer; >> to_encode = input_size; >> >> d = output_buffer + sizeof(*header) + sizeof(*payload); >> to_write = output_size - sizeof(*header) - sizeof(*payload); >> >> /* frame_count is only 4 bit number */ >> while (PA_LIKELY(to_encode > 0 && to_write > 0 && frame_count < 15)) { >> ssize_t written; >> ssize_t encoded; >> >> encoded = sbc_encode(&sbc_info->sbc, >> p, to_encode, >> d, to_write, >> &written); >> >> if (PA_UNLIKELY(encoded <= 0)) { >> pa_log_error("SBC encoding error (%li)", (long) encoded); >> break; >> } >> >> if (PA_UNLIKELY(written < 0)) { >> pa_log_error("SBC encoding error (%li)", (long) written); >> break; >> } >> >> pa_assert_fp((size_t) encoded <= to_encode); >> pa_assert_fp((size_t) encoded == sbc_info->codesize); >> >> pa_assert_fp((size_t) written <= to_write); >> pa_assert_fp((size_t) written == sbc_info->frame_length); >> >> p += encoded; >> to_encode -= encoded; >> >> d += written; >> to_write -= written; >> >> frame_count++; >> } >> >> PA_ONCE_BEGIN { >> pa_log_debug("Using SBC codec implementation: %s", pa_strnull(sbc_get_implementation_info(&sbc_info->sbc))); >> } PA_ONCE_END; >> >> if (PA_UNLIKELY(frame_count == 0)) { >> *processed = 0; >> return 0; >> } >> >> /* write it to the fifo */ >> pa_memzero(output_buffer, sizeof(*header) + sizeof(*payload)); >> header->v = 2; >> >> /* A2DP spec: "A payload type in the RTP dynamic range shall be chosen". >> * RFC3551 defines the dynamic range to span from 96 to 127, and 96 appears >> * to be the most common choice in A2DP implementations. */ >> header->pt = 96; >> >> header->sequence_number = htons(sbc_info->seq_num++); >> header->timestamp = htonl(timestamp); >> header->ssrc = htonl(1); >> payload->frame_count = frame_count; >> >> *processed = p - input_buffer; >> return d - output_buffer; >> } >> >> static size_t decode_buffer(void *codec_info, 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; >> struct rtp_sbc_payload *payload; >> const uint8_t *p; >> uint8_t *d; >> size_t to_write, to_decode; >> uint8_t frame_count; >> >> header = (struct rtp_header *) input_buffer; >> payload = (struct rtp_sbc_payload*) (input_buffer + sizeof(*header)); >> >> frame_count = payload->frame_count; >> >> /* TODO: Add support for decoding fragmented SBC frames */ >> if (payload->is_fragmented) { >> pa_log_error("Unsupported fragmented SBC frame"); >> *processed = 0; >> return 0; >> } >> >> p = input_buffer + sizeof(*header) + sizeof(*payload); >> to_decode = input_size - sizeof(*header) - sizeof(*payload); >> >> d = output_buffer; >> to_write = output_size; >> >> while (PA_LIKELY(to_decode > 0 && to_write > 0 && frame_count > 0)) { >> size_t written; >> ssize_t decoded; >> >> decoded = sbc_decode(&sbc_info->sbc, >> p, to_decode, >> d, to_write, >> &written); >> >> if (PA_UNLIKELY(decoded <= 0)) { >> pa_log_error("SBC decoding error (%li)", (long) decoded); >> break; >> } >> >> /* Reset frame length, it can be changed due to bitpool change */ >> sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); >> >> pa_assert_fp((size_t) decoded <= to_decode); >> pa_assert_fp((size_t) decoded == sbc_info->frame_length); >> >> pa_assert_fp((size_t) written <= to_write); >> pa_assert_fp((size_t) written == sbc_info->codesize); >> >> p += decoded; >> to_decode -= decoded; >> >> d += written; >> to_write -= written; >> >> frame_count--; >> } >> >> *processed = p - input_buffer; >> return d - output_buffer; >> } >> >> const pa_a2dp_codec pa_a2dp_codec_sbc = { >> .name = "sbc", >> .description = "SBC", >> .id = { A2DP_CODEC_SBC, 0, 0 }, >> .support_backchannel = false, >> .can_accept_capabilities = can_accept_capabilities, >> .choose_remote_endpoint = choose_remote_endpoint, >> .fill_capabilities = fill_capabilities, >> .is_configuration_valid = is_configuration_valid, >> .fill_preferred_configuration = fill_preferred_configuration, >> .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, >> .encode_buffer = encode_buffer, >> .decode_buffer = decode_buffer, >> }; >> >> 17.09.2019, 09:34, "Hyperion" <h1p8r10n@xxxxxxxxxx>: >>> btw here's the updated V12-12 patch in Pali's latest rel95 patchset that applies to PA 13.0. >>> >>> http://download.zenwalk.org/x86_64/testing/v12-12-13-zen-bluetooth-Implement-A2DP-codec-switching-and-backchannel-support-.patch >>> >>> JP >>> >>> 16.09.2019, 09:32, "Hyperion" <h1p8r10n@xxxxxxxxxx>: >>>> Hi, >>>> >>>> Following what has already be done in some Android derivatives, here's a simple patch that extend SBP bitpool negociation to XQ quality. >>>> >>>> According to : >>>> --> http://soundexpert.org/articles/-/blogs/audio-quality-of-sbc-xq-bluetooth-audio-codec >>>> --> https://lineageos.org/engineering/Bluetooth-SBC-XQ/ , >>>> --> and confirmed by my own experimentation on more than 10 different devices, >>>> >>>> here are the features of the (very simple and non intrusive) patch : >>>> - allow to use bitpool 76 on devices that support it, aka SBC XQ >>>> - harmless for devices limited to bitpool 53 >>>> - deprecates the need for APTX & APTX HD support, which are not better than SBC XQ, are not Open Source, and less supported by devices >>>> >>>> This patch will be superseded by multi-profiles Pali Rohar A2DP stack implementation, when it's ready for production. Thanks to Pali for the help testing many codec parameters. >>>> >>>> Let me know if I have to clone the git to push my patch, or if a regular PA dev could do it. >>>> >>>> All the best >>>> JP >>>> >>>> diff -rNaud pulseaudio-13.0/src/modules/bluetooth/a2dp-codec-sbc.c pulseaudio-13.0-new/src/modules/bluetooth/a2dp-codec-sbc.c >>>> --- pulseaudio-13.0/src/modules/bluetooth/a2dp-codec-sbc.c 2019-09-13 15:20:03.000000000 +0200 >>>> +++ pulseaudio-13.0-new/src/modules/bluetooth/a2dp-codec-sbc.c 2019-09-16 08:57:50.363122019 +0200 >>>> @@ -290,10 +290,10 @@ >>>> return 0; >>>> } >>>> >>>> - if (capabilities->allocation_method & SBC_ALLOCATION_LOUDNESS) >>>> - config->allocation_method = SBC_ALLOCATION_LOUDNESS; >>>> - else if (capabilities->allocation_method & SBC_ALLOCATION_SNR) >>>> + if (capabilities->allocation_method & SBC_ALLOCATION_SNR) >>>> config->allocation_method = SBC_ALLOCATION_SNR; >>>> + else if (capabilities->allocation_method & SBC_ALLOCATION_LOUDNESS) >>>> + config->allocation_method = SBC_ALLOCATION_LOUDNESS; >>>> else { >>>> pa_log_error("No supported allocation method"); >>>> return 0; >>>> diff -rNaud pulseaudio-13.0/src/modules/bluetooth/a2dp-codecs.h pulseaudio-13.0-new/src/modules/bluetooth/a2dp-codecs.h >>>> --- pulseaudio-13.0/src/modules/bluetooth/a2dp-codecs.h 2019-09-13 15:20:03.000000000 +0200 >>>> +++ pulseaudio-13.0-new/src/modules/bluetooth/a2dp-codecs.h 2019-09-16 08:44:20.382086305 +0200 >>>> @@ -61,14 +61,11 @@ >>>> * Allocation method = Loudness >>>> * Subbands = 8 >>>> */ >>>> -#define SBC_BITPOOL_MQ_MONO_44100 19 >>>> -#define SBC_BITPOOL_MQ_MONO_48000 18 >>>> -#define SBC_BITPOOL_MQ_JOINT_STEREO_44100 35 >>>> -#define SBC_BITPOOL_MQ_JOINT_STEREO_48000 33 >>>> -#define SBC_BITPOOL_HQ_MONO_44100 31 >>>> -#define SBC_BITPOOL_HQ_MONO_48000 29 >>>> -#define SBC_BITPOOL_HQ_JOINT_STEREO_44100 53 >>>> -#define SBC_BITPOOL_HQ_JOINT_STEREO_48000 51 >>>> + >>>> +#define SBC_BITPOOL_HQ_MONO_44100 38 >>>> +#define SBC_BITPOOL_HQ_MONO_48000 38 >>>> +#define SBC_BITPOOL_HQ_JOINT_STEREO_44100 76 >>>> +#define SBC_BITPOOL_HQ_JOINT_STEREO_48000 76 >>>> >>>> #define MPEG_CHANNEL_MODE_MONO (1 << 3) >>>> #define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) >>>> >>>> _______________________________________________ >>>> 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 _______________________________________________ pulseaudio-discuss mailing list pulseaudio-discuss@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss