From af99c196c92b6eef8b13ceb50d981ddf3182c7b8 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 31 Oct 2017 15:59:08 +0100 Subject: [PATCH] alsa: improve ringbuffer handling Add max-latency property to configure the maximum amount we write to the device. Only try to pull after we consumed our current buffers. --- spa/include/spa/props.h | 1 + spa/plugins/alsa/alsa-sink.c | 13 +++++++++---- spa/plugins/alsa/alsa-utils.c | 34 +++++++++++++++++++--------------- spa/plugins/alsa/alsa-utils.h | 6 +++++- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/spa/include/spa/props.h b/spa/include/spa/props.h index c068663b6..4566aca80 100644 --- a/spa/include/spa/props.h +++ b/spa/include/spa/props.h @@ -41,6 +41,7 @@ struct spa_props { #define SPA_TYPE_PROPS__card SPA_TYPE_PROPS_BASE "card" #define SPA_TYPE_PROPS__cardName SPA_TYPE_PROPS_BASE "cardName" #define SPA_TYPE_PROPS__minLatency SPA_TYPE_PROPS_BASE "minLatency" +#define SPA_TYPE_PROPS__maxLatency SPA_TYPE_PROPS_BASE "maxLatency" #define SPA_TYPE_PROPS__periods SPA_TYPE_PROPS_BASE "periods" #define SPA_TYPE_PROPS__periodSize SPA_TYPE_PROPS_BASE "periodSize" #define SPA_TYPE_PROPS__periodEvent SPA_TYPE_PROPS_BASE "periodEvent" diff --git a/spa/plugins/alsa/alsa-sink.c b/spa/plugins/alsa/alsa-sink.c index f9556d51b..4a072c029 100644 --- a/spa/plugins/alsa/alsa-sink.c +++ b/spa/plugins/alsa/alsa-sink.c @@ -33,11 +33,13 @@ static const char default_device[] = "hw:0"; static const uint32_t default_min_latency = 128; +static const uint32_t default_max_latency = 1024; static void reset_props(struct props *props) { strncpy(props->device, default_device, 64); props->min_latency = default_min_latency; + props->max_latency = default_max_latency; } static int impl_node_get_props(struct spa_node *node, struct spa_props **props) @@ -58,6 +60,8 @@ static int impl_node_get_props(struct spa_node *node, struct spa_props **props) ":", this->type.prop_device_name, "S", this->props.device_name, sizeof(this->props.device_name), ":", this->type.prop_card_name, "S", this->props.card_name, sizeof(this->props.card_name), ":", this->type.prop_min_latency, "ir", this->props.min_latency, + 2, 1, INT32_MAX, + ":", this->type.prop_max_latency, "ir", this->props.max_latency, 2, 1, INT32_MAX); return SPA_RESULT_OK; @@ -367,7 +371,7 @@ impl_node_port_enum_params(struct spa_node *node, INT32_MAX, ":", t->param_alloc_buffers.stride, "i", 0, ":", t->param_alloc_buffers.buffers, "ir", 2, - 2, 2, 32, + 2, 2, MAX_BUFFERS, ":", t->param_alloc_buffers.align, "i", 16); break; @@ -381,9 +385,10 @@ impl_node_port_enum_params(struct spa_node *node, case 2: *param = spa_pod_builder_param(&b, t->param_alloc_meta_enable.MetaEnable, - ":", t->param_alloc_meta_enable.type, "I", t->meta.Ringbuffer, - ":", t->param_alloc_meta_enable.size, "i", sizeof(struct spa_meta_ringbuffer), - ":", t->param_alloc_meta_enable.ringbufferSize, "iru", this->props.min_latency * this->frame_size, + ":", t->param_alloc_meta_enable.type, "I", t->meta.Ringbuffer, + ":", t->param_alloc_meta_enable.size, "i", sizeof(struct spa_meta_ringbuffer), + ":", t->param_alloc_meta_enable.ringbufferSize, "iru", + this->props.min_latency * this->frame_size, 2, this->props.min_latency * this->frame_size, this->period_frames * this->frame_size, ":", t->param_alloc_meta_enable.ringbufferStride, "i", 0, diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c index 615992e55..75242d0e7 100644 --- a/spa/plugins/alsa/alsa-utils.c +++ b/spa/plugins/alsa/alsa-utils.c @@ -341,7 +341,7 @@ pull_frames(struct state *state, snd_pcm_uframes_t frames, bool do_pull) { - snd_pcm_uframes_t total_frames = 0, to_write = frames; + snd_pcm_uframes_t total_frames = 0, to_write = SPA_MIN(frames, state->props.max_latency); bool underrun = false; try_pull(state, frames, do_pull); @@ -365,16 +365,17 @@ pull_frames(struct state *state, int32_t avail; avail = spa_ringbuffer_get_read_index(ringbuffer, &index); + avail /= state->frame_size; - n_bytes = SPA_MIN(avail, to_write * state->frame_size); - n_frames = SPA_MIN(to_write, n_bytes / state->frame_size); + n_frames = SPA_MIN(avail, to_write); + n_bytes = n_frames * state->frame_size; + + spa_ringbuffer_read_data(ringbuffer, d[0].data, index % ringbuffer->size, dst, n_bytes); - spa_ringbuffer_read_data(ringbuffer, d[0].data, index % ringbuffer->mask, dst, n_bytes); spa_ringbuffer_read_update(ringbuffer, index + n_bytes); - reuse = avail == n_bytes; - spa_log_trace(state->log, "%d %d %ld %zd", avail, index, to_write, n_bytes); - reuse = true; + + reuse = avail == n_frames || state->n_buffers == 1; } else { offs = SPA_MIN(d[0].chunk->offset + state->ready_offset, d[0].maxsize); size = SPA_MIN(d[0].chunk->size + offs, d[0].maxsize) - offs; @@ -394,12 +395,14 @@ pull_frames(struct state *state, spa_log_trace(state->log, "alsa-util %p: reuse buffer %u", state, b->outbuf->id); state->callbacks->reuse_buffer(state->callbacks_data, 0, b->outbuf->id); state->ready_offset = 0; - - try_pull(state, frames, do_pull); } total_frames += n_frames; to_write -= n_frames; + spa_log_trace(state->log, "alsa-util %p: written %lu frames, left %ld", state, total_frames, to_write); } + + try_pull(state, frames, do_pull); + if (total_frames == 0 && do_pull) { total_frames = SPA_MIN(frames, state->threshold); snd_pcm_areas_silence(my_areas, offset, state->channels, total_frames, state->format); @@ -454,12 +457,10 @@ push_frames(struct state *state, d[0].chunk->size = n_bytes; d[0].chunk->stride = 0; - { - b->outstanding = true; - io->buffer_id = b->outbuf->id; - io->status = SPA_RESULT_HAVE_BUFFER; - state->callbacks->have_output(state->callbacks_data); - } + b->outstanding = true; + io->buffer_id = b->outbuf->id; + io->status = SPA_RESULT_HAVE_BUFFER; + state->callbacks->have_output(state->callbacks_data); } return total_frames; } @@ -549,10 +550,13 @@ static void alsa_on_playback_timeout_event(struct spa_source *source) spa_log_error(state->log, "snd_pcm_mmap_begin error: %s", snd_strerror(res)); return; } + spa_log_trace(state->log, "begin %ld %ld", offset, frames); + written = pull_frames(state, my_areas, offset, frames, do_pull); if (written < frames) to_write = 0; + spa_log_trace(state->log, "commit %ld %ld", offset, written); if ((res = snd_pcm_mmap_commit(hndl, offset, written)) < 0) { spa_log_error(state->log, "snd_pcm_mmap_commit error: %s", snd_strerror(res)); if (res != -EPIPE && res != -ESTRPIPE) diff --git a/spa/plugins/alsa/alsa-utils.h b/spa/plugins/alsa/alsa-utils.h index 33e355eb8..b12709515 100644 --- a/spa/plugins/alsa/alsa-utils.h +++ b/spa/plugins/alsa/alsa-utils.h @@ -44,9 +44,10 @@ struct props { char device_name[128]; char card_name[128]; uint32_t min_latency; + uint32_t max_latency; }; -#define MAX_BUFFERS 64 +#define MAX_BUFFERS 32 struct buffer { struct spa_buffer *outbuf; @@ -65,6 +66,7 @@ struct type { uint32_t prop_device_name; uint32_t prop_card_name; uint32_t prop_min_latency; + uint32_t prop_max_latency; struct spa_type_meta meta; struct spa_type_data data; struct spa_type_media_type media_type; @@ -88,6 +90,7 @@ static inline void init_type(struct type *type, struct spa_type_map *map) type->prop_device_name = spa_type_map_get_id(map, SPA_TYPE_PROPS__deviceName); type->prop_card_name = spa_type_map_get_id(map, SPA_TYPE_PROPS__cardName); type->prop_min_latency = spa_type_map_get_id(map, SPA_TYPE_PROPS__minLatency); + type->prop_max_latency = spa_type_map_get_id(map, SPA_TYPE_PROPS__maxLatency); spa_type_meta_map(map, &type->meta); spa_type_data_map(map, &type->data); @@ -148,6 +151,7 @@ struct state { struct spa_list free; struct spa_list ready; + size_t ready_offset; bool started;