mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-03-06 01:40:26 -05:00
stream: improve latency requirements
This commit is contained in:
parent
a30722c442
commit
c3a1807eff
1 changed files with 79 additions and 63 deletions
142
src/stream.c
142
src/stream.c
|
|
@ -141,6 +141,9 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att
|
||||||
else
|
else
|
||||||
buffers = SPA_CLAMP(attr->maxlength / (maxsize * stride), 3, 64);
|
buffers = SPA_CLAMP(attr->maxlength / (maxsize * stride), 3, 64);
|
||||||
|
|
||||||
|
pw_log_info("stream %p: stride %d maxsize %d size %u buffers %d", s, stride, maxsize,
|
||||||
|
size, buffers);
|
||||||
|
|
||||||
param = spa_pod_builder_object(b,
|
param = spa_pod_builder_object(b,
|
||||||
t->param.idBuffers, t->param_buffers.Buffers,
|
t->param.idBuffers, t->param_buffers.Buffers,
|
||||||
":", t->param_buffers.buffers, "iru", buffers,
|
":", t->param_buffers.buffers, "iru", buffers,
|
||||||
|
|
@ -153,6 +156,64 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att
|
||||||
return param;
|
return param;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flags_t *flags) {
|
||||||
|
const char *e;
|
||||||
|
|
||||||
|
pa_assert(s);
|
||||||
|
pa_assert(attr);
|
||||||
|
|
||||||
|
if ((e = getenv("PULSE_LATENCY_MSEC"))) {
|
||||||
|
uint32_t ms;
|
||||||
|
pa_sample_spec ss;
|
||||||
|
|
||||||
|
pa_sample_spec_init(&ss);
|
||||||
|
|
||||||
|
if (pa_sample_spec_valid(&s->sample_spec))
|
||||||
|
ss = s->sample_spec;
|
||||||
|
else if (s->n_formats == 1)
|
||||||
|
pa_format_info_to_sample_spec(s->req_formats[0], &ss, NULL);
|
||||||
|
|
||||||
|
if ((ms = atoi(e)) < 0 || ms <= 0) {
|
||||||
|
pa_log_debug("Failed to parse $PULSE_LATENCY_MSEC: %s", e);
|
||||||
|
}
|
||||||
|
else if (!pa_sample_spec_valid(&s->sample_spec)) {
|
||||||
|
pa_log_debug("Ignoring $PULSE_LATENCY_MSEC: %s (invalid sample spec)", e);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
attr->maxlength = (uint32_t) -1;
|
||||||
|
attr->tlength = pa_usec_to_bytes(ms * PA_USEC_PER_MSEC, &ss);
|
||||||
|
attr->minreq = (uint32_t) -1;
|
||||||
|
attr->prebuf = (uint32_t) -1;
|
||||||
|
attr->fragsize = attr->tlength;
|
||||||
|
|
||||||
|
if (flags)
|
||||||
|
*flags |= PA_STREAM_ADJUST_LATENCY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attr->maxlength == (uint32_t) -1)
|
||||||
|
attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */
|
||||||
|
|
||||||
|
if (attr->tlength == (uint32_t) -1)
|
||||||
|
attr->tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &s->sample_spec); /* 250ms of buffering */
|
||||||
|
|
||||||
|
if (attr->minreq == (uint32_t) -1)
|
||||||
|
attr->minreq = (attr->tlength)/5; /* Ask for more data when there are only 200ms left in the playback buffer */
|
||||||
|
|
||||||
|
if (attr->prebuf == (uint32_t) -1)
|
||||||
|
attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */
|
||||||
|
|
||||||
|
if (attr->fragsize == (uint32_t) -1)
|
||||||
|
attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */
|
||||||
|
|
||||||
|
pw_log_info("stream %p: maxlength: %u", s, attr->maxlength);
|
||||||
|
pw_log_info("stream %p: tlength: %u", s, attr->tlength);
|
||||||
|
pw_log_info("stream %p: minreq: %u", s, attr->minreq);
|
||||||
|
pw_log_info("stream %p: prebuf: %u", s, attr->prebuf);
|
||||||
|
pw_log_info("stream %p: fragsize: %u", s, attr->fragsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void stream_format_changed(void *data, const struct spa_pod *format)
|
static void stream_format_changed(void *data, const struct spa_pod *format)
|
||||||
{
|
{
|
||||||
pa_stream *s = data;
|
pa_stream *s = data;
|
||||||
|
|
@ -187,6 +248,8 @@ static void stream_format_changed(void *data, const struct spa_pod *format)
|
||||||
pa_format_info_free(s->format);
|
pa_format_info_free(s->format);
|
||||||
s->format = pa_format_info_from_sample_spec(&s->sample_spec, NULL);
|
s->format = pa_format_info_from_sample_spec(&s->sample_spec, NULL);
|
||||||
|
|
||||||
|
patch_buffer_attr(s, &s->buffer_attr, NULL);
|
||||||
|
|
||||||
params[n_params++] = get_buffers_param(s, &s->buffer_attr, &b);
|
params[n_params++] = get_buffers_param(s, &s->buffer_attr, &b);
|
||||||
|
|
||||||
res = 0;
|
res = 0;
|
||||||
|
|
@ -467,64 +530,6 @@ int pa_stream_is_corked(pa_stream *s)
|
||||||
return s->corked;
|
return s->corked;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flags_t *flags) {
|
|
||||||
const char *e;
|
|
||||||
|
|
||||||
pa_assert(s);
|
|
||||||
pa_assert(attr);
|
|
||||||
|
|
||||||
if ((e = getenv("PULSE_LATENCY_MSEC"))) {
|
|
||||||
uint32_t ms;
|
|
||||||
pa_sample_spec ss;
|
|
||||||
|
|
||||||
pa_sample_spec_init(&ss);
|
|
||||||
|
|
||||||
if (pa_sample_spec_valid(&s->sample_spec))
|
|
||||||
ss = s->sample_spec;
|
|
||||||
else if (s->n_formats == 1)
|
|
||||||
pa_format_info_to_sample_spec(s->req_formats[0], &ss, NULL);
|
|
||||||
|
|
||||||
if ((ms = atoi(e)) < 0 || ms <= 0) {
|
|
||||||
pa_log_debug("Failed to parse $PULSE_LATENCY_MSEC: %s", e);
|
|
||||||
}
|
|
||||||
else if (!pa_sample_spec_valid(&s->sample_spec)) {
|
|
||||||
pa_log_debug("Ignoring $PULSE_LATENCY_MSEC: %s (invalid sample spec)", e);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
attr->maxlength = (uint32_t) -1;
|
|
||||||
attr->tlength = pa_usec_to_bytes(ms * PA_USEC_PER_MSEC, &ss);
|
|
||||||
attr->minreq = (uint32_t) -1;
|
|
||||||
attr->prebuf = (uint32_t) -1;
|
|
||||||
attr->fragsize = attr->tlength;
|
|
||||||
|
|
||||||
if (flags)
|
|
||||||
*flags |= PA_STREAM_ADJUST_LATENCY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attr->maxlength == (uint32_t) -1)
|
|
||||||
attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */
|
|
||||||
|
|
||||||
if (attr->tlength == (uint32_t) -1)
|
|
||||||
attr->tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &s->sample_spec); /* 250ms of buffering */
|
|
||||||
|
|
||||||
if (attr->minreq == (uint32_t) -1)
|
|
||||||
attr->minreq = (attr->tlength)/5; /* Ask for more data when there are only 200ms left in the playback buffer */
|
|
||||||
|
|
||||||
if (attr->prebuf == (uint32_t) -1)
|
|
||||||
attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */
|
|
||||||
|
|
||||||
if (attr->fragsize == (uint32_t) -1)
|
|
||||||
attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */
|
|
||||||
|
|
||||||
pw_log_info("stream %p: maxlength: %u", s, attr->maxlength);
|
|
||||||
pw_log_info("stream %p: tlength: %u", s, attr->tlength);
|
|
||||||
pw_log_info("stream %p: minreq: %u", s, attr->minreq);
|
|
||||||
pw_log_info("stream %p: prebuf: %u", s, attr->prebuf);
|
|
||||||
pw_log_info("stream %p: fragsize: %u", s, attr->fragsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const struct spa_pod *get_param(pa_stream *s, pa_sample_spec *ss, pa_channel_map *map,
|
static const struct spa_pod *get_param(pa_stream *s, pa_sample_spec *ss, pa_channel_map *map,
|
||||||
struct spa_pod_builder *b)
|
struct spa_pod_builder *b)
|
||||||
{
|
{
|
||||||
|
|
@ -557,6 +562,7 @@ static int create_stream(pa_stream_direction_t direction,
|
||||||
uint8_t buffer[4096];
|
uint8_t buffer[4096];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||||
struct pw_properties *props;
|
struct pw_properties *props;
|
||||||
|
uint32_t sample_rate = 0, stride = 0;
|
||||||
|
|
||||||
spa_assert(s);
|
spa_assert(s);
|
||||||
spa_assert(s->refcount >= 1);
|
spa_assert(s->refcount >= 1);
|
||||||
|
|
@ -579,23 +585,32 @@ static int create_stream(pa_stream_direction_t direction,
|
||||||
|
|
||||||
if (pa_sample_spec_valid(&s->sample_spec)) {
|
if (pa_sample_spec_valid(&s->sample_spec)) {
|
||||||
params[n_params++] = get_param(s, &s->sample_spec, &s->channel_map, &b);
|
params[n_params++] = get_param(s, &s->sample_spec, &s->channel_map, &b);
|
||||||
|
sample_rate = s->sample_spec.rate;
|
||||||
|
stride = pa_frame_size(&s->sample_spec);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pa_sample_spec ss;
|
pa_sample_spec ss;
|
||||||
pa_channel_map map;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < s->n_formats; i++) {
|
for (i = 0; i < s->n_formats; i++) {
|
||||||
if (pa_format_info_to_sample_spec(s->req_formats[i], &ss, &map) < 0) {
|
if ((res = pa_format_info_to_sample_spec(s->req_formats[i], &ss, NULL)) < 0) {
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
pw_log_warn("can't convert format %s",
|
pw_log_warn("can't convert format %d %s", res,
|
||||||
pa_format_info_snprint(buf,4096,s->req_formats[i]));
|
pa_format_info_snprint(buf,4096,s->req_formats[i]));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
params[n_params++] = get_param(s, &ss, &map, &b);
|
params[n_params++] = get_param(s, &ss, NULL, &b);
|
||||||
|
if (ss.rate > sample_rate) {
|
||||||
|
sample_rate = ss.rate;
|
||||||
|
stride = pa_frame_size(&ss);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (sample_rate == 0) {
|
||||||
|
sample_rate = 48000;
|
||||||
|
stride = sizeof(int16_t) * 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (attr)
|
if (attr)
|
||||||
s->buffer_attr = *attr;
|
s->buffer_attr = *attr;
|
||||||
|
|
@ -605,7 +620,8 @@ static int create_stream(pa_stream_direction_t direction,
|
||||||
dev = getenv("PIPEWIRE_NODE");
|
dev = getenv("PIPEWIRE_NODE");
|
||||||
|
|
||||||
props = (struct pw_properties *) pw_stream_get_properties(s->stream);
|
props = (struct pw_properties *) pw_stream_get_properties(s->stream);
|
||||||
pw_properties_setf(props, "node.latency", "%u/44100", s->buffer_attr.minreq);
|
pw_properties_setf(props, "node.latency", "%u/%u",
|
||||||
|
s->buffer_attr.minreq / stride, sample_rate);
|
||||||
|
|
||||||
res = pw_stream_connect(s->stream,
|
res = pw_stream_connect(s->stream,
|
||||||
direction == PA_STREAM_PLAYBACK ?
|
direction == PA_STREAM_PLAYBACK ?
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue