Negociation wth JBL E55BT headset, resulting in Dual Channel 2x40kb bitpool : hcidump avdtp HCI sniffer - Bluetooth packet analyzer ver 5.50 device: hci0 snap_len: 1500 filter: 0x400 < AVDTP(s): Discover cmd: transaction 0 nsp 0x00 > AVDTP(s): Discover rsp: transaction 0 nsp 0x00 ACP SEID 1 - Audio Sink ACP SEID 2 - Audio Sink < AVDTP(s): Capabilities cmd: transaction 1 nsp 0x00 ACP SEID 1 > AVDTP(s): Capabilities rsp: transaction 1 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-40 Content Protection 02 00 < AVDTP(s): Capabilities cmd: transaction 2 nsp 0x00 ACP SEID 2 > AVDTP(s): Capabilities rsp: transaction 2 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-40 Content Protection 02 00 < 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-40 19.09.2019, 10:15, "Hyperion" <h1p8r10n@xxxxxxxxxx>: > 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 _______________________________________________ pulseaudio-discuss mailing list pulseaudio-discuss@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss