[pulseaudio-discuss] [PATCH v9 1/8] bluetooth: Fix A2DP codec API to provide information about data buffer

Pali Rohár pali.rohar at gmail.com
Mon Apr 22 13:39:55 UTC 2019


Each codec has different compression ratio and own method how to calculate
buffer size of encoded or decoded samples. So change A2DP codec API to
provide this information for module-bluez5-device module and fix
a2dp_prepare_encoder_buffer() and a2dp_prepare_decoder_buffer() functions.

API functions get_read_buffer_size() and get_write_buffer_size() now set
both decoded and encoded buffer sizes. Function reduce_encoder_bitrate()
was changed to not return new buffer size (it was not obvious if buffer
size was for encoded or decoded samples), but caller rather should call
get_write_buffer_size() to get new sizes.
---
 src/modules/bluetooth/a2dp-codec-api.h       | 17 ++++++------
 src/modules/bluetooth/a2dp-codec-sbc.c       | 25 ++++++++++-------
 src/modules/bluetooth/module-bluez5-device.c | 41 ++++++++++++++++++----------
 3 files changed, 50 insertions(+), 33 deletions(-)

diff --git a/src/modules/bluetooth/a2dp-codec-api.h b/src/modules/bluetooth/a2dp-codec-api.h
index 55bb9ff70..517dc76f1 100644
--- a/src/modules/bluetooth/a2dp-codec-api.h
+++ b/src/modules/bluetooth/a2dp-codec-api.h
@@ -72,15 +72,14 @@ typedef struct pa_a2dp_codec {
     /* Reset internal state of codec info data in codec_info */
     void (*reset)(void *codec_info);
 
-    /* Get read block size for codec */
-    size_t (*get_read_block_size)(void *codec_info, size_t read_link_mtu);
-    /* Get write block size for codec */
-    size_t (*get_write_block_size)(void *codec_info, size_t write_link_mtu);
-
-    /* Reduce encoder bitrate for codec, returns new write block size or zero
-     * if not changed, called when socket is not accepting encoded data fast
-     * enough */
-    size_t (*reduce_encoder_bitrate)(void *codec_info, size_t write_link_mtu);
+    /* Get buffer sizes for read operations */
+    void (*get_read_buffer_size)(void *codec_info, size_t read_link_mtu, size_t *output_buffer_size, size_t *encoded_buffer_size);
+    /* Get buffer sizes for write operations */
+    void (*get_write_buffer_size)(void *codec_info, size_t write_link_mtu, size_t *input_buffer_size, size_t *encoded_buffer_size);
+
+    /* Reduce encoder bitrate for codec, returns non-zero on failure,
+     * called when socket is not accepting encoded data fast enough */
+    int (*reduce_encoder_bitrate)(void *codec_info);
 
     /* Encode input_buffer of input_size to output_buffer of output_size,
      * returns size of filled ouput_buffer and set processed to size of
diff --git a/src/modules/bluetooth/a2dp-codec-sbc.c b/src/modules/bluetooth/a2dp-codec-sbc.c
index cdc20d7f0..ae71f72e9 100644
--- a/src/modules/bluetooth/a2dp-codec-sbc.c
+++ b/src/modules/bluetooth/a2dp-codec-sbc.c
@@ -423,7 +423,10 @@ static void *init(bool for_encoding, bool for_backchannel, const uint8_t *config
     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 */
+    /* Set minimum bitpool for source to get the maximum possible buffer size
+     * in get_buffer_size() function. Buffer size 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);
@@ -475,20 +478,22 @@ static void reset(void *codec_info) {
     sbc_info->seq_num = 0;
 }
 
-static size_t get_block_size(void *codec_info, size_t link_mtu) {
+static void get_buffer_size(void *codec_info, size_t link_mtu, size_t *decoded_buffer_size, size_t *encoded_buffer_size) {
     struct sbc_info *sbc_info = (struct sbc_info *) codec_info;
+    size_t rtp_data_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+    size_t num_of_frames = (link_mtu - rtp_data_size) / sbc_info->frame_length;
 
-    return (link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
-           / sbc_info->frame_length * sbc_info->codesize;
+    *decoded_buffer_size = num_of_frames * sbc_info->codesize;
+    *encoded_buffer_size = num_of_frames * sbc_info->frame_length + rtp_data_size;
 }
 
-static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) {
+static int reduce_encoder_bitrate(void *codec_info) {
     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;
+        return -1;
 
     bitpool = sbc_info->sbc.bitpool - SBC_BITPOOL_DEC_STEP;
 
@@ -496,10 +501,10 @@ static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) {
         bitpool = SBC_BITPOOL_DEC_LIMIT;
 
     if (sbc_info->sbc.bitpool == bitpool)
-        return 0;
+        return -1;
 
     set_bitpool(sbc_info, bitpool);
-    return get_block_size(codec_info, write_link_mtu);
+    return 0;
 }
 
 static size_t encode_buffer(void *codec_info, uint32_t timestamp, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed) {
@@ -639,8 +644,8 @@ const pa_a2dp_codec pa_a2dp_codec_sbc = {
     .init = init,
     .deinit = deinit,
     .reset = reset,
-    .get_read_block_size = get_block_size,
-    .get_write_block_size = get_block_size,
+    .get_read_buffer_size = get_buffer_size,
+    .get_write_buffer_size = get_buffer_size,
     .reduce_encoder_bitrate = reduce_encoder_bitrate,
     .encode_buffer = encode_buffer,
     .decode_buffer = decode_buffer,
diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
index 56c96054d..c0b293d94 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -125,7 +125,9 @@ struct userdata {
     size_t read_link_mtu;
     size_t write_link_mtu;
     size_t read_block_size;
+    size_t read_encoded_block_size;
     size_t write_block_size;
+    size_t write_encoded_block_size;
     uint64_t read_index;
     uint64_t write_index;
     pa_usec_t started_at;
@@ -412,10 +414,10 @@ static int sco_process_push(struct userdata *u) {
 static void a2dp_prepare_encoder_buffer(struct userdata *u) {
     pa_assert(u);
 
-    if (u->encoder_buffer_size >= u->write_link_mtu)
+    if (u->encoder_buffer_size >= u->write_encoded_block_size)
         return;
 
-    u->encoder_buffer_size = 2 * u->write_link_mtu;
+    u->encoder_buffer_size = u->write_encoded_block_size;
     pa_xfree(u->encoder_buffer);
     u->encoder_buffer = pa_xmalloc(u->encoder_buffer_size);
 }
@@ -424,10 +426,10 @@ static void a2dp_prepare_encoder_buffer(struct userdata *u) {
 static void a2dp_prepare_decoder_buffer(struct userdata *u) {
     pa_assert(u);
 
-    if (u->decoder_buffer_size >= u->read_link_mtu)
+    if (u->decoder_buffer_size >= u->read_encoded_block_size)
         return;
 
-    u->decoder_buffer_size = 2 * u->read_link_mtu;
+    u->decoder_buffer_size = u->read_encoded_block_size;
     pa_xfree(u->decoder_buffer);
     u->decoder_buffer = pa_xmalloc(u->decoder_buffer_size);
 }
@@ -436,6 +438,9 @@ static void a2dp_prepare_decoder_buffer(struct userdata *u) {
 static int a2dp_write_buffer(struct userdata *u, size_t nbytes) {
     int ret = 0;
 
+    if (!nbytes)
+        return 0;
+
     for (;;) {
         ssize_t l;
 
@@ -504,14 +509,14 @@ static int a2dp_process_render(struct userdata *u) {
     /* Try to create a packet of the full MTU */
     ptr = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk);
 
-    length = u->a2dp_codec->encode_buffer(u->encoder_info, u->write_index / pa_frame_size(&u->encoder_sample_spec), ptr, u->write_memchunk.length, u->encoder_buffer, u->encoder_buffer_size, &processed);
+    length = u->a2dp_codec->encode_buffer(u->encoder_info, u->write_index / pa_frame_size(&u->encoder_sample_spec), ptr, u->write_memchunk.length, u->encoder_buffer, u->write_encoded_block_size, &processed);
 
     pa_memblock_release(u->write_memchunk.memblock);
 
-    if (length == 0)
+    if (processed != u->write_memchunk.length) {
+        pa_log_error("Encoding error");
         return -1;
-
-    pa_assert(processed == u->write_memchunk.length);
+    }
 
     return a2dp_write_buffer(u, length);
 }
@@ -539,7 +544,7 @@ static int a2dp_process_push(struct userdata *u) {
 
         a2dp_prepare_decoder_buffer(u);
 
-        l = pa_read(u->stream_fd, u->decoder_buffer, u->decoder_buffer_size, &u->stream_write_type);
+        l = pa_read(u->stream_fd, u->decoder_buffer, u->read_encoded_block_size, &u->stream_write_type);
 
         if (l <= 0) {
 
@@ -568,6 +573,14 @@ static int a2dp_process_push(struct userdata *u) {
         memchunk.length = pa_memblock_get_length(memchunk.memblock);
 
         memchunk.length = u->a2dp_codec->decode_buffer(u->decoder_info, u->decoder_buffer, l, ptr, memchunk.length, &processed);
+
+        if (processed != (size_t) l) {
+            pa_log_error("Decoding error");
+            pa_memblock_release(memchunk.memblock);
+            ret = -1;
+            break;
+        }
+
         if (memchunk.length == 0) {
             pa_memblock_release(memchunk.memblock);
             ret = 0;
@@ -732,9 +745,9 @@ static void transport_config_mtu(struct userdata *u) {
     } else {
         pa_assert(u->a2dp_codec);
         if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
-            u->write_block_size = u->a2dp_codec->get_write_block_size(u->encoder_info, u->write_link_mtu);
+            u->a2dp_codec->get_write_buffer_size(u->encoder_info, u->write_link_mtu, &u->write_block_size, &u->write_encoded_block_size);
         } else {
-            u->read_block_size = u->a2dp_codec->get_read_block_size(u->decoder_info, u->read_link_mtu);
+            u->a2dp_codec->get_read_buffer_size(u->decoder_info, u->read_link_mtu, &u->read_block_size, &u->read_encoded_block_size);
         }
     }
 
@@ -1438,9 +1451,9 @@ static void thread_func(void *userdata) {
                             }
 
                             if (u->write_index > 0 && u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
-                                size_t new_write_block_size = u->a2dp_codec->reduce_encoder_bitrate(u->encoder_info, u->write_link_mtu);
-                                if (new_write_block_size) {
-                                    u->write_block_size = new_write_block_size;
+                                int failed = u->a2dp_codec->reduce_encoder_bitrate(u->encoder_info);
+                                if (!failed) {
+                                    u->a2dp_codec->get_write_buffer_size(u->encoder_info, u->write_link_mtu, &u->write_block_size, &u->write_encoded_block_size);
                                     handle_sink_block_size_change(u);
                                 }
                             }
-- 
2.11.0



More information about the pulseaudio-discuss mailing list