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

Luiz Augusto von Dentz luiz.dentz at gmail.com
Tue Apr 10 12:52:58 UTC 2018


Hi George,

On Mon, Apr 9, 2018 at 7:16 PM, Georg Chini <georg at chini.tk> wrote:
> 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.

Well SCO is, as the name suggests, synchronous or to be more precise
it isochronous so I wonder if this has been tested? It seems to me
this will start to behave like A2DP which buffer frames for a while
before sending, though A2DP is not really for voice where latency may
cause problems like lip sync issues or talking over someone else on a
call because the audio has a few second delay. I get that using 3ms
when the  default-fragment-size-msec is bigger than that may not make
any difference so we should probably use that instead.

> ---
>  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(-)

I do have patches changing the bluez5 modules to bluetooth, I wonder
what happen to that.


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



-- 
Luiz Augusto von Dentz


More information about the pulseaudio-discuss mailing list