[pulseaudio-discuss] SBC XQ for PA 13.0

Hyperion h1p8r10n at yandex.com
Thu Sep 19 08:22:39 UTC 2019


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 at yandex.com>:
> 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 at yandex.com>:
>>  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 at yandex.com>:
>>>   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 at gmail.com>
>>>
>>>     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 at yandex.com>:
>>>>    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 at yandex.com>:
>>>>>     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 at lists.freedesktop.org
>>>>>     https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
>>
>>  _______________________________________________
>>  pulseaudio-discuss mailing list
>>  pulseaudio-discuss at lists.freedesktop.org
>>  https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
>
> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


More information about the pulseaudio-discuss mailing list