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.
This commit is contained in:
Wim Taymans 2017-10-31 15:59:08 +01:00
parent f04b292d08
commit af99c196c9
4 changed files with 34 additions and 20 deletions

View file

@ -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"

View file

@ -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,

View file

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

View file

@ -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;