mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	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:
		
							parent
							
								
									7f08cadca4
								
							
						
					
					
						commit
						4f57f3cdae
					
				
					 4 changed files with 55 additions and 18 deletions
				
			
		| 
						 | 
					@ -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
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue