[pulseaudio-discuss] [PATCH] bluez5-device, bluez5-discover: add hsp_source_buffer_msec argument

Georg Chini georg at chini.tk
Mon Apr 9 16:16:49 UTC 2018


Currently the PA bluetooth code calls source_push() for each HSP source packet.
The default HSP MTU is 48 bytes, this means that source_push() is called every
3ms, which leads to increased CPU load especially on embedded systems.

This patch adds a hsp_source_buffer_msec argument to module-bluez5-discover and
module-bluez5-device. The argument gives the number of milliseconds of audio which
are buffered, before source_push() is called. The value can range from 0 to 100ms,
and is rounded down to the next multiple of the MTU size. Therefore a value of less
than 2*MTU time corresponds to the original behavior.
---
 src/modules/bluetooth/module-bluetooth-discover.c |  1 +
 src/modules/bluetooth/module-bluez5-device.c      | 68 +++++++++++++++++------
 src/modules/bluetooth/module-bluez5-discover.c    | 14 ++++-
 3 files changed, 64 insertions(+), 19 deletions(-)

diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 63195d3e..14c0a38f 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -32,6 +32,7 @@ PA_MODULE_LOAD_ONCE(true);
 PA_MODULE_USAGE(
     "headset=ofono|native|auto (bluez 5 only)"
     "autodetect_mtu=<boolean> (bluez 5 only)"
+    "hsp_source_buffer_msec=<0 - 100> (bluez5 only)"
 );
 
 struct userdata {
diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
index b81c233c..d40bbb0c 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -54,7 +54,8 @@ PA_MODULE_DESCRIPTION("BlueZ 5 Bluetooth audio sink and source");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(false);
 PA_MODULE_USAGE("path=<device object path>"
-                "autodetect_mtu=<boolean>");
+                "autodetect_mtu=<boolean>"
+                "hsp_source_buffer_msec=<0 - 100>");
 
 #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
 #define FIXED_LATENCY_PLAYBACK_SCO  (25 * PA_USEC_PER_MSEC)
@@ -68,6 +69,7 @@ PA_MODULE_USAGE("path=<device object path>"
 static const char* const valid_modargs[] = {
     "path",
     "autodetect_mtu",
+    "hsp_source_buffer_msec",
     NULL
 };
 
@@ -123,6 +125,9 @@ struct userdata {
     pa_card *card;
     pa_sink *sink;
     pa_source *source;
+    uint32_t source_buffer_usec;
+    uint32_t source_buffered_blocks;
+    pa_memchunk source_buffer;
     pa_bluetooth_profile_t profile;
     char *output_port_name;
     char *input_port_name;
@@ -314,10 +319,10 @@ static int sco_process_render(struct userdata *u) {
 /* Run from IO thread */
 static int sco_process_push(struct userdata *u) {
     ssize_t l;
-    pa_memchunk memchunk;
     struct cmsghdr *cm;
     struct msghdr m;
     bool found_tstamp = false;
+    uint32_t max_blocks;
     pa_usec_t tstamp = 0;
 
     pa_assert(u);
@@ -326,11 +331,17 @@ static int sco_process_push(struct userdata *u) {
     pa_assert(u->source);
     pa_assert(u->read_smoother);
 
-    memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
-    memchunk.index = memchunk.length = 0;
+    max_blocks = u->source_buffer_usec / pa_bytes_to_usec(u->read_block_size, &u->source->sample_spec);
+    if (max_blocks == 0)
+        max_blocks = 1;
+
+    if (!u->source_buffer.memblock) {
+        u->source_buffer.memblock = pa_memblock_new(u->core->mempool, max_blocks * u->read_block_size);
+        u->source_buffer.index = u->source_buffer.length = 0;
+    }
 
     for (;;) {
-        void *p;
+        uint8_t *p;
         uint8_t aux[1024];
         struct iovec iov;
 
@@ -343,11 +354,11 @@ static int sco_process_push(struct userdata *u) {
         m.msg_control = aux;
         m.msg_controllen = sizeof(aux);
 
-        p = pa_memblock_acquire(memchunk.memblock);
-        iov.iov_base = p;
-        iov.iov_len = pa_memblock_get_length(memchunk.memblock);
+        p = pa_memblock_acquire(u->source_buffer.memblock);
+        iov.iov_base = p + u->source_buffer.index;
+        iov.iov_len = u->read_block_size;
         l = recvmsg(u->stream_fd, &m, 0);
-        pa_memblock_release(memchunk.memblock);
+        pa_memblock_release(u->source_buffer.memblock);
 
         if (l > 0)
             break;
@@ -356,8 +367,6 @@ static int sco_process_push(struct userdata *u) {
             /* Retry right away if we got interrupted */
             continue;
 
-        pa_memblock_unref(memchunk.memblock);
-
         if (l < 0 && errno == EAGAIN)
             /* Hmm, apparently the socket was not readable, give up for now. */
             return 0;
@@ -366,7 +375,7 @@ static int sco_process_push(struct userdata *u) {
         return -1;
     }
 
-    pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock));
+    pa_assert((size_t) l <= u->read_block_size);
 
     /* In some rare occasions, we might receive packets of a very strange
      * size. This could potentially be possible if the SCO packet was
@@ -376,11 +385,10 @@ static int sco_process_push(struct userdata *u) {
      * packet */
     if (!pa_frame_aligned(l, &u->sample_spec)) {
         pa_log_warn("SCO packet received of unaligned size: %zu", l);
-        pa_memblock_unref(memchunk.memblock);
         return -1;
     }
 
-    memchunk.length = (size_t) l;
+    u->source_buffer.index += (size_t) l;
     u->read_index += (uint64_t) l;
 
     for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm))
@@ -397,11 +405,19 @@ static int sco_process_push(struct userdata *u) {
         tstamp = pa_rtclock_now();
     }
 
-    pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
-    pa_smoother_resume(u->read_smoother, tstamp, true);
+    u->source_buffered_blocks++;
 
-    pa_source_post(u->source, &memchunk);
-    pa_memblock_unref(memchunk.memblock);
+    if (u->source_buffered_blocks >= max_blocks) {
+        pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
+        pa_smoother_resume(u->read_smoother, tstamp, true);
+
+        u->source_buffer.length = u->source_buffer.index;
+        u->source_buffer.index = 0;
+        pa_source_post(u->source, &u->source_buffer);
+        pa_memblock_unref(u->source_buffer.memblock);
+        pa_memchunk_reset(&u->source_buffer);
+        u->source_buffered_blocks = 0;
+    }
 
     return l;
 }
@@ -784,6 +800,12 @@ static void teardown_stream(struct userdata *u) {
         pa_memchunk_reset(&u->write_memchunk);
     }
 
+    if (u->source_buffer.memblock) {
+        pa_memblock_unref(u->source_buffer.memblock);
+        pa_memchunk_reset(&u->source_buffer);
+    }
+    u->source_buffered_blocks = 0;
+
     pa_log_debug("Audio stream torn down");
     u->stream_setup_done = false;
 }
@@ -947,6 +969,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
                 ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
 
                 *((int64_t*) data) = u->source->thread_info.fixed_latency + wi - ri;
+                *((int64_t*) data) += u->source_buffered_blocks * pa_bytes_to_usec(u->read_block_size, &u->source->sample_spec);
             } else
                 *((int64_t*) data) = 0;
 
@@ -2362,6 +2385,7 @@ int pa__init(pa_module* m) {
     const char *path;
     pa_modargs *ma;
     bool autodetect_mtu;
+    uint32_t source_buffer_msec;
 
     pa_assert(m);
 
@@ -2397,7 +2421,15 @@ int pa__init(pa_module* m) {
         goto fail_free_modargs;
     }
 
+    source_buffer_msec = 0;
+    if (pa_modargs_get_value_u32(ma, "hsp_source_buffer_msec", &source_buffer_msec) < 0 || source_buffer_msec > 100) {
+        pa_log("Invalid value for hsp_source_buffer_msec parameter, value must be <= 100");
+        goto fail;
+    }
+
     u->device->autodetect_mtu = autodetect_mtu;
+    u->source_buffer_usec = source_buffer_msec * PA_USEC_PER_MSEC;
+    u->source_buffered_blocks = 0;
 
     pa_modargs_free(ma);
 
diff --git a/src/modules/bluetooth/module-bluez5-discover.c b/src/modules/bluetooth/module-bluez5-discover.c
index 44578214..8108627d 100644
--- a/src/modules/bluetooth/module-bluez5-discover.c
+++ b/src/modules/bluetooth/module-bluez5-discover.c
@@ -36,11 +36,14 @@ PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(true);
 PA_MODULE_USAGE(
     "headset=ofono|native|auto"
+    "autodetect_mtu=<boolean>"
+    "hsp_source_buffer_msec=<0 - 100>"
 );
 
 static const char* const valid_modargs[] = {
     "headset",
     "autodetect_mtu",
+    "hsp_source_buffer_msec",
     NULL
 };
 
@@ -51,6 +54,7 @@ struct userdata {
     pa_hook_slot *device_connection_changed_slot;
     pa_bluetooth_discovery *discovery;
     bool autodetect_mtu;
+    uint32_t source_buffer_msec;
 };
 
 static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) {
@@ -71,7 +75,7 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y,
     if (!module_loaded && pa_bluetooth_device_any_transport_connected(d)) {
         /* a new device has been connected */
         pa_module *m;
-        char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i", d->path, (int)u->autodetect_mtu);
+        char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i hsp_source_buffer_msec=%u", d->path, (int)u->autodetect_mtu, u->source_buffer_msec);
 
         pa_log_debug("Loading module-bluez5-device %s", args);
         pa_module_load(&m, u->module->core, "module-bluez5-device", args);
@@ -102,6 +106,7 @@ int pa__init(pa_module *m) {
     const char *headset_str;
     int headset_backend;
     bool autodetect_mtu;
+    uint32_t source_buffer_msec;
 
     pa_assert(m);
 
@@ -128,10 +133,17 @@ int pa__init(pa_module *m) {
         goto fail;
     }
 
+    source_buffer_msec = 0;
+    if (pa_modargs_get_value_u32(ma, "hsp_source_buffer_msec", &source_buffer_msec) < 0 || source_buffer_msec > 100) {
+        pa_log("Invalid value for hsp_source_buffer_msec parameter, value must be <= 100");
+        goto fail;
+    }
+
     m->userdata = u = pa_xnew0(struct userdata, 1);
     u->module = m;
     u->core = m->core;
     u->autodetect_mtu = autodetect_mtu;
+    u->source_buffer_msec = source_buffer_msec;
     u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 
     if (!(u->discovery = pa_bluetooth_discovery_get(u->core, headset_backend)))
-- 
2.14.1



More information about the pulseaudio-discuss mailing list