From b96f15d2feb14b6d86232f99deea4dc86504f4ff Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 4 Nov 2021 18:07:30 +0100 Subject: [PATCH] pulse-server: improve latency setup a little Reorganize the latency setup in one place, return a desired device latency for use as quantum. PulseAudio assigns half of the (tlength - minreq) latency to the sink but we can't do that because our sinks have a max-quantum of latency. Fix this by clamping our calculated sink latency to the quantum PulseAudio subtracts the sink latency from the tlength in adjust latency mode, so we need to do the same. This makes PULSE_LATENCY_MSEC values bahave more like pulseaudio. See #1769 --- src/modules/module-protocol-pulse/internal.h | 1 + .../module-protocol-pulse/pulse-server.c | 91 +++++++++++-------- 2 files changed, 54 insertions(+), 38 deletions(-) diff --git a/src/modules/module-protocol-pulse/internal.h b/src/modules/module-protocol-pulse/internal.h index 9ece0baf2..df222cf01 100644 --- a/src/modules/module-protocol-pulse/internal.h +++ b/src/modules/module-protocol-pulse/internal.h @@ -51,6 +51,7 @@ struct defs { struct spa_fraction min_quantum; struct sample_spec sample_spec; struct channel_map channel_map; + uint32_t max_quantum; }; struct stats { diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index 9e6f83253..696702070 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -367,13 +367,14 @@ static uint32_t frac_to_bytes_round_up(struct spa_fraction val, const struct sam return (uint32_t) u; } -static void fix_playback_buffer_attr(struct stream *s, struct buffer_attr *attr) +static uint32_t fix_playback_buffer_attr(struct stream *s, struct buffer_attr *attr) { - uint32_t frame_size, max_prebuf, minreq; + uint32_t frame_size, max_prebuf, minreq, latency, max_latency; struct defs *defs = &s->impl->defs; frame_size = s->frame_size; minreq = frac_to_bytes_round_up(defs->min_req, &s->ss); + max_latency = defs->max_quantum * frame_size; if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH) attr->maxlength = MAXLENGTH; @@ -401,6 +402,26 @@ static void fix_playback_buffer_attr(struct stream *s, struct buffer_attr *attr) if (attr->tlength < attr->minreq+frame_size) attr->tlength = attr->minreq + frame_size; + if (s->early_requests) { + latency = attr->minreq; + } else if (s->adjust_latency) { + if (attr->tlength > attr->minreq * 2) + latency = SPA_MIN(max_latency, (attr->tlength - attr->minreq * 2) / 2); + else + latency = attr->minreq; + + if (attr->tlength >= latency) + attr->tlength -= latency; + } else { + if (attr->tlength > attr->minreq * 2) + latency = SPA_MIN(max_latency, attr->tlength - attr->minreq * 2); + else + latency = attr->minreq; + } + + if (attr->tlength < latency + 2 * attr->minreq) + attr->tlength = latency + 2 * attr->minreq; + attr->minreq -= attr->minreq % frame_size; if (attr->minreq <= 0) { attr->minreq = frame_size; @@ -417,9 +438,11 @@ static void fix_playback_buffer_attr(struct stream *s, struct buffer_attr *attr) s->missing = attr->tlength; attr->fragsize = 0; - pw_log_info("%p: [%s] maxlength:%u tlength:%u minreq:%u prebuf:%u", s, + pw_log_info("%p: [%s] maxlength:%u tlength:%u minreq:%u/%u prebuf:%u latency:%u", s, s->client->name, attr->maxlength, attr->tlength, - attr->minreq, attr->prebuf); + attr->minreq, minreq, attr->prebuf, latency); + + return latency / frame_size; } static int reply_create_playback_stream(struct stream *stream, struct pw_manager_object *peer) @@ -438,7 +461,8 @@ static int reply_create_playback_stream(struct stream *stream, struct pw_manager uint64_t lat_usec; struct defs *defs = &stream->impl->defs; - fix_playback_buffer_attr(stream, &stream->attr); + lat.denom = stream->ss.rate; + lat.num = fix_playback_buffer_attr(stream, &stream->attr); stream->buffer = calloc(1, stream->attr.maxlength); if (stream->buffer == NULL) @@ -446,21 +470,6 @@ static int reply_create_playback_stream(struct stream *stream, struct pw_manager spa_ringbuffer_init(&stream->ring); - if (stream->early_requests) { - lat.num = stream->attr.minreq; - } else if (stream->adjust_latency) { - if (stream->attr.tlength > stream->attr.minreq * 2) - lat.num = (stream->attr.tlength - stream->attr.minreq * 2) / 2; - else - lat.num = stream->attr.minreq; - } else { - if (stream->attr.tlength > stream->attr.minreq * 2) - lat.num = stream->attr.tlength - stream->attr.minreq * 2; - else - lat.num = stream->attr.minreq; - } - lat.denom = stream->ss.rate; - lat.num /= stream->frame_size; if (lat.num * defs->min_quantum.denom / lat.denom < defs->min_quantum.num) lat.num = (defs->min_quantum.num * lat.denom + (defs->min_quantum.denom -1)) / defs->min_quantum.denom; @@ -535,9 +544,9 @@ static int reply_create_playback_stream(struct stream *stream, struct pw_manager return client_queue_message(client, reply); } -static void fix_record_buffer_attr(struct stream *s, struct buffer_attr *attr) +static uint32_t fix_record_buffer_attr(struct stream *s, struct buffer_attr *attr) { - uint32_t frame_size, minfrag; + uint32_t frame_size, minfrag, latency; struct defs *defs = &s->impl->defs; frame_size = s->frame_size; @@ -560,8 +569,19 @@ static void fix_record_buffer_attr(struct stream *s, struct buffer_attr *attr) attr->tlength = attr->minreq = attr->prebuf = 0; - pw_log_info("%p: [%s] maxlength:%u fragsize:%u minfrag:%u", s, - s->client->name, attr->maxlength, attr->fragsize, minfrag); + if (s->early_requests) { + latency = attr->fragsize; + } else if (s->adjust_latency) { + latency = attr->fragsize; + } else { + latency = attr->fragsize; + } + + pw_log_info("%p: [%s] maxlength:%u fragsize:%u minfrag:%u latency:%u", s, + s->client->name, attr->maxlength, attr->fragsize, minfrag, + latency); + + return latency / frame_size; } static int reply_create_record_stream(struct stream *stream, struct pw_manager_object *peer) @@ -579,7 +599,8 @@ static int reply_create_record_stream(struct stream *stream, struct pw_manager_o uint64_t lat_usec; struct defs *defs = &stream->impl->defs; - fix_record_buffer_attr(stream, &stream->attr); + lat.denom = stream->ss.rate; + lat.num = fix_record_buffer_attr(stream, &stream->attr); stream->buffer = calloc(1, stream->attr.maxlength); if (stream->buffer == NULL) @@ -587,16 +608,6 @@ static int reply_create_record_stream(struct stream *stream, struct pw_manager_o spa_ringbuffer_init(&stream->ring); - if (stream->early_requests) { - lat.num = stream->attr.fragsize; - } else if (stream->adjust_latency) { - lat.num = stream->attr.fragsize; - } else { - lat.num = stream->attr.fragsize; - } - - lat.num /= stream->frame_size; - lat.denom = stream->ss.rate; if (lat.num * defs->min_quantum.denom / lat.denom < defs->min_quantum.num) lat.num = (defs->min_quantum.num * lat.denom + (defs->min_quantum.denom -1)) / defs->min_quantum.denom; @@ -691,9 +702,12 @@ static void manager_added(void *data, struct pw_manager_object *o) if (strcmp(o->type, PW_TYPE_INTERFACE_Core) == 0 && manager->info != NULL) { struct pw_core_info *info = manager->info; - if (info->props && - (str = spa_dict_lookup(info->props, "default.clock.rate")) != NULL) - client->impl->defs.sample_spec.rate = atoi(str); + if (info->props) { + if ((str = spa_dict_lookup(info->props, "default.clock.rate")) != NULL) + client->impl->defs.sample_spec.rate = atoi(str); + if ((str = spa_dict_lookup(info->props, "default.clock.max-quantum")) != NULL) + client->impl->defs.max_quantum = atoi(str); + } } if (spa_streq(o->type, PW_TYPE_INTERFACE_Metadata)) { @@ -5043,6 +5057,7 @@ static void load_defaults(struct defs *def, struct pw_properties *props) parse_format(props, "pulse.default.format", DEFAULT_FORMAT, &def->sample_spec); parse_position(props, "pulse.default.position", DEFAULT_POSITION, &def->channel_map); def->sample_spec.channels = def->channel_map.channels; + def->max_quantum = 8192; } struct pw_protocol_pulse *pw_protocol_pulse_new(struct pw_context *context,