[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