[pulseaudio-discuss] [PATCH v13 05/10] bluetooth: Add A2DP aptX and aptX HD codecs support
Andrey Semashev
andrey.semashev at gmail.com
Thu Dec 12 19:27:39 UTC 2019
On 2019-10-06 20:58, Pali Rohár wrote:
> This patch provides support for aptX and aptX HD codecs in bluetooth A2DP
> profile. It uses open source LGPLv2.1+ licensed libopenaptx library which
> can be found at https://github.com/pali/libopenaptx.
>
> aptX for s24 stereo samples provides fixed 6:1 compression ratio and
> bitrate 352.8 kbit/s, aptX HD provides fixed 4:1 compression ratio and
> bitrate 529.2 kbit/s.
>
> According to soundexpert research, aptX codec used in bluetooth A2DP is no
> better than SBC High Quality settings. And you cannot hear difference
> between aptX and SBC High Quality, aptX is just a copper-less overpriced
> audio cable.
>
> aptX HD is high-bitrate version of aptX. It has clearly noticeable increase
> in sound quality (not dramatic though taking into account the increase in
> bitrate).
>
> http://soundexpert.org/news/-/blogs/audio-quality-of-bluetooth-aptx
> ---
> configure.ac | 36 +++
> src/Makefile.am | 6 +
> src/modules/bluetooth/a2dp-codec-aptx.c | 479 ++++++++++++++++++++++++++++++++
> src/modules/bluetooth/a2dp-codec-util.c | 8 +
> 4 files changed, 529 insertions(+)
> create mode 100644 src/modules/bluetooth/a2dp-codec-aptx.c
[snip]
> +
> +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 aptx_context *aptx_c = (struct aptx_context *) codec_info;
> + size_t written;
> +
> + *processed = aptx_encode(aptx_c, input_buffer, input_size, output_buffer, output_size, &written);
> + if (PA_UNLIKELY(*processed == 0 || *processed != input_size))
> + pa_log_error("aptX encoding error");
> +
> + return written;
> +}
> +
> +static size_t encode_buffer_hd(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 aptx_hd_info *aptx_hd_info = (struct aptx_hd_info *) codec_info;
> + struct rtp_header *header;
> + size_t written;
> +
> + if (PA_UNLIKELY(output_size < sizeof(*header))) {
> + *processed = 0;
> + return 0;
> + }
> +
> + written = encode_buffer(aptx_hd_info->aptx_c, timestamp, input_buffer, input_size, output_buffer + sizeof(*header), output_size - sizeof(*header), processed);
> +
> + if (PA_LIKELY(written > 0)) {
> + header = (struct rtp_header *) output_buffer;
I'm not sure I understand it correctly, but encode_buffer seems to be
producing encoded content at the beginning of the output buffer, and
encode_buffer_hd produces an RTP header followed by encoded content. Is
this difference intentional? What is expected to be placed in the output
buffer - a complete RTP packet or RTP payload only?
The same comment applies to decode_buffer and decode_buffer_hd below.
> + pa_zero(*header);
> + header->v = 2;
> + header->pt = 96;
> + header->sequence_number = htons(aptx_hd_info->seq_num++);
> + header->timestamp = htonl(timestamp);
> + header->ssrc = htonl(1);
> + written += sizeof(*header);
> + }
> +
> + return written;
> +}
> +
> +static size_t decode_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 aptx_context *aptx_c = (struct aptx_context *) codec_info;
> + size_t written;
> +
> + *processed = aptx_decode(aptx_c, input_buffer, input_size, output_buffer, output_size, &written);
> +
> + /* Due to aptX latency, aptx_decode starts filling output buffer after 90 input samples.
> + * If input buffer contains less than 90 samples, aptx_decode returns zero (=no output)
> + * but set *processed to non zero as input samples were processed. So do not check for
> + * return value of aptx_decode, zero is valid. Decoding error is indicating by fact that
> + * not all input samples were processed. */
> + if (PA_UNLIKELY(*processed != input_size))
> + pa_log_error("aptX decoding error");
> +
> + return written;
> +}
> +
> +static size_t decode_buffer_hd(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 aptx_hd_info *aptx_hd_info = (struct aptx_hd_info *) codec_info;
> + struct rtp_header *header;
> + size_t written;
> +
> + if (PA_UNLIKELY(input_size < sizeof(*header))) {
> + *processed = 0;
> + return 0;
> + }
> +
> + header = (struct rtp_header *) input_buffer;
> + written = decode_buffer(aptx_hd_info->aptx_c, timestamp, input_buffer + sizeof(*header), input_size - sizeof(*header), output_buffer, output_size, processed);
> + *timestamp = ntohl(header->timestamp);
> + *processed += sizeof(*header);
> + return written;
> +}
More information about the pulseaudio-discuss
mailing list