pulse-server: allow per-application latency quirks

Make it possible to set latency and buffer parameters in the matching
rules.

Add a rule for speech-dispatcher and force it to some larger latency.
This commit is contained in:
Wim Taymans 2022-01-22 16:17:09 +01:00
parent 7f08cadca4
commit 4f57f3cdae
4 changed files with 55 additions and 18 deletions

View file

@ -89,7 +89,7 @@ pulse.rules = [
# all keys must match the value. ~ starts regex. # all keys must match the value. ~ starts regex.
#client.name = "Firefox" #client.name = "Firefox"
#application.process.binary = "teams" #application.process.binary = "teams"
#application.name = "~teams.*" #application.name = "~speech-dispatcher.*"
} }
] ]
actions = { actions = {
@ -103,6 +103,8 @@ pulse.rules = [
} }
} }
{ {
# skype does not want to use devices that don't have an
# S16 sample format.
matches = [ matches = [
{ application.process.binary = "teams" } { application.process.binary = "teams" }
{ application.process.binary = "skypeforlinux" } { application.process.binary = "skypeforlinux" }
@ -110,9 +112,19 @@ pulse.rules = [
actions = { quirks = [ force-s16-info ] } actions = { quirks = [ force-s16-info ] }
} }
{ {
matches = [ # firefox marks the capture streams as don't move and then they
{ application.process.binary = "firefox" } # can't be moved with pavucontrol or other tools.
] matches = [ { application.process.binary = "firefox" } ]
actions = { quirks = [ remove-capture-dont-move ] } actions = { quirks = [ remove-capture-dont-move ] }
} }
{
# speech dispatcher asks for too small latency and then underruns.
matches = [ { application.name = "~speech-dispatcher*" } ]
actions = {
update-props = {
pulse.min.req = 1024/48000 # 21ms
pulse.min.quantum = 1024/48000 # 21ms
}
}
}
] ]

View file

@ -371,7 +371,7 @@ static uint32_t fix_playback_buffer_attr(struct stream *s, struct buffer_attr *a
struct defs *defs = &s->impl->defs; struct defs *defs = &s->impl->defs;
frame_size = s->frame_size; frame_size = s->frame_size;
minreq = frac_to_bytes_round_up(defs->min_req, &s->ss); minreq = frac_to_bytes_round_up(s->min_req, &s->ss);
max_latency = defs->quantum_limit * frame_size; max_latency = defs->quantum_limit * frame_size;
if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH) if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH)
@ -380,7 +380,7 @@ static uint32_t fix_playback_buffer_attr(struct stream *s, struct buffer_attr *a
attr->maxlength = SPA_MAX(attr->maxlength, frame_size); attr->maxlength = SPA_MAX(attr->maxlength, frame_size);
if (attr->tlength == (uint32_t) -1) if (attr->tlength == (uint32_t) -1)
attr->tlength = frac_to_bytes_round_up(defs->default_tlength, &s->ss); attr->tlength = frac_to_bytes_round_up(s->default_tlength, &s->ss);
if (attr->tlength > attr->maxlength) if (attr->tlength > attr->maxlength)
attr->tlength = attr->maxlength; attr->tlength = attr->maxlength;
attr->tlength -= attr->tlength % frame_size; attr->tlength -= attr->tlength % frame_size;
@ -388,7 +388,7 @@ static uint32_t fix_playback_buffer_attr(struct stream *s, struct buffer_attr *a
attr->tlength = SPA_MAX(attr->tlength, minreq); attr->tlength = SPA_MAX(attr->tlength, minreq);
if (attr->minreq == (uint32_t) -1) { if (attr->minreq == (uint32_t) -1) {
uint32_t process = frac_to_bytes_round_up(defs->default_req, &s->ss); uint32_t process = frac_to_bytes_round_up(s->default_req, &s->ss);
/* With low-latency, tlength/4 gives a decent default in all of traditional, /* With low-latency, tlength/4 gives a decent default in all of traditional,
* adjust latency and early request modes. */ * adjust latency and early request modes. */
uint32_t m = attr->tlength / 4; uint32_t m = attr->tlength / 4;
@ -459,7 +459,6 @@ static int reply_create_playback_stream(struct stream *stream, struct pw_manager
const char *peer_name; const char *peer_name;
struct spa_fraction lat; struct spa_fraction lat;
uint64_t lat_usec; uint64_t lat_usec;
struct defs *defs = &stream->impl->defs;
lat.denom = stream->ss.rate; lat.denom = stream->ss.rate;
lat.num = fix_playback_buffer_attr(stream, &stream->attr); lat.num = fix_playback_buffer_attr(stream, &stream->attr);
@ -468,9 +467,9 @@ static int reply_create_playback_stream(struct stream *stream, struct pw_manager
if (stream->buffer == NULL) if (stream->buffer == NULL)
return -errno; return -errno;
if (lat.num * defs->min_quantum.denom / lat.denom < defs->min_quantum.num) if (lat.num * stream->min_quantum.denom / lat.denom < stream->min_quantum.num)
lat.num = (defs->min_quantum.num * lat.denom + lat.num = (stream->min_quantum.num * lat.denom +
(defs->min_quantum.denom -1)) / defs->min_quantum.denom; (stream->min_quantum.denom -1)) / stream->min_quantum.denom;
lat_usec = lat.num * SPA_USEC_PER_SEC / lat.denom; lat_usec = lat.num * SPA_USEC_PER_SEC / lat.denom;
snprintf(latency, sizeof(latency), "%u/%u", lat.num, lat.denom); snprintf(latency, sizeof(latency), "%u/%u", lat.num, lat.denom);
@ -549,7 +548,6 @@ static int reply_create_playback_stream(struct stream *stream, struct pw_manager
static uint32_t 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, latency; uint32_t frame_size, minfrag, latency;
struct defs *defs = &s->impl->defs;
frame_size = s->frame_size; frame_size = s->frame_size;
@ -558,10 +556,10 @@ static uint32_t fix_record_buffer_attr(struct stream *s, struct buffer_attr *att
attr->maxlength -= attr->maxlength % frame_size; attr->maxlength -= attr->maxlength % frame_size;
attr->maxlength = SPA_MAX(attr->maxlength, frame_size); attr->maxlength = SPA_MAX(attr->maxlength, frame_size);
minfrag = frac_to_bytes_round_up(defs->min_frag, &s->ss); minfrag = frac_to_bytes_round_up(s->min_frag, &s->ss);
if (attr->fragsize == (uint32_t) -1 || attr->fragsize == 0) if (attr->fragsize == (uint32_t) -1 || attr->fragsize == 0)
attr->fragsize = frac_to_bytes_round_up(defs->default_frag, &s->ss); attr->fragsize = frac_to_bytes_round_up(s->default_frag, &s->ss);
attr->fragsize -= attr->fragsize % frame_size; attr->fragsize -= attr->fragsize % frame_size;
attr->fragsize = SPA_MAX(attr->fragsize, minfrag); attr->fragsize = SPA_MAX(attr->fragsize, minfrag);
attr->fragsize = SPA_MAX(attr->fragsize, frame_size); attr->fragsize = SPA_MAX(attr->fragsize, frame_size);
@ -599,7 +597,6 @@ static int reply_create_record_stream(struct stream *stream, struct pw_manager_o
uint32_t peer_index; uint32_t peer_index;
struct spa_fraction lat; struct spa_fraction lat;
uint64_t lat_usec; uint64_t lat_usec;
struct defs *defs = &stream->impl->defs;
lat.denom = stream->ss.rate; lat.denom = stream->ss.rate;
lat.num = fix_record_buffer_attr(stream, &stream->attr); lat.num = fix_record_buffer_attr(stream, &stream->attr);
@ -608,9 +605,9 @@ static int reply_create_record_stream(struct stream *stream, struct pw_manager_o
if (stream->buffer == NULL) if (stream->buffer == NULL)
return -errno; return -errno;
if (lat.num * defs->min_quantum.denom / lat.denom < defs->min_quantum.num) if (lat.num * stream->min_quantum.denom / lat.denom < stream->min_quantum.num)
lat.num = (defs->min_quantum.num * lat.denom + lat.num = (stream->min_quantum.num * lat.denom +
(defs->min_quantum.denom -1)) / defs->min_quantum.denom; (stream->min_quantum.denom -1)) / stream->min_quantum.denom;
lat_usec = lat.num * SPA_USEC_PER_SEC / lat.denom; lat_usec = lat.num * SPA_USEC_PER_SEC / lat.denom;
snprintf(latency, sizeof(latency), "%u/%u", lat.num, lat.denom); snprintf(latency, sizeof(latency), "%u/%u", lat.num, lat.denom);

View file

@ -44,11 +44,25 @@
#include "reply.h" #include "reply.h"
#include "stream.h" #include "stream.h"
static int parse_frac(struct pw_properties *props, const char *key,
const struct spa_fraction *def, struct spa_fraction *res)
{
const char *str;
if (props == NULL ||
(str = pw_properties_get(props, key)) == NULL ||
sscanf(str, "%u/%u", &res->num, &res->denom) != 2 ||
res->denom == 0) {
*res = *def;
}
return 0;
}
struct stream *stream_new(struct client *client, enum stream_type type, uint32_t create_tag, struct stream *stream_new(struct client *client, enum stream_type type, uint32_t create_tag,
const struct sample_spec *ss, const struct channel_map *map, const struct sample_spec *ss, const struct channel_map *map,
const struct buffer_attr *attr) const struct buffer_attr *attr)
{ {
int res; int res;
struct defs *defs = &client->impl->defs;
struct stream *stream = calloc(1, sizeof(*stream)); struct stream *stream = calloc(1, sizeof(*stream));
if (stream == NULL) if (stream == NULL)
@ -67,6 +81,13 @@ struct stream *stream_new(struct client *client, enum stream_type type, uint32_t
stream->attr = *attr; stream->attr = *attr;
spa_ringbuffer_init(&stream->ring); spa_ringbuffer_init(&stream->ring);
parse_frac(client->props, "pulse.min.req", &defs->min_req, &stream->min_req);
parse_frac(client->props, "pulse.min.frag", &defs->min_frag, &stream->min_frag);
parse_frac(client->props, "pulse.min.quantum", &defs->min_quantum, &stream->min_quantum);
parse_frac(client->props, "pulse.default.req", &defs->default_req, &stream->default_req);
parse_frac(client->props, "pulse.default.frag", &defs->default_frag, &stream->default_frag);
parse_frac(client->props, "pulse.default.tlength", &defs->default_tlength, &stream->default_tlength);
switch (type) { switch (type) {
case STREAM_TYPE_RECORD: case STREAM_TYPE_RECORD:
stream->direction = PW_DIRECTION_INPUT; stream->direction = PW_DIRECTION_INPUT;

View file

@ -86,6 +86,13 @@ struct stream {
uint32_t last_quantum; uint32_t last_quantum;
int64_t requested; int64_t requested;
struct spa_fraction min_req;
struct spa_fraction default_req;
struct spa_fraction min_frag;
struct spa_fraction default_frag;
struct spa_fraction default_tlength;
struct spa_fraction min_quantum;
struct sample_spec ss; struct sample_spec ss;
struct channel_map map; struct channel_map map;
struct buffer_attr attr; struct buffer_attr attr;