Assume that we *really really* want no more than 10 ms of latency, and there are occasional underruns. Now the latency will increase, first to 1 ms, then 2 ms, 4 ms, 8 ms, 16 ms. At that point, when the sink's latency is 16 ms and maxlength is 10 ms, we get a constant stream of underruns, because we fill 10 ms of data up and then go to sleep for 16 ms (minus watermark). By not allowing the latency to grow beyond 7.5 ms in this case, we only get occasional underruns instead of constant underruns. This is not a finished patch: 1) I'm not sure (maxlength - minreq) is the correct formula, but it seems to work reasonably here at least. 2) The max_sink_length calculation should probably be somewhere in sink.c. 3) Setting max_sink_length should probably be done in a more thread safe fashion. 4) I'm not sure whether we should add something to the other modes (traditional, early requests). After all, it's only in adjust_latency mode that we describe as "adjusting the overall latency of the pipeline". 5) Remove debug defines Signed-off-by: David Henningsson <david.henningsson at canonical.com> --- src/modules/alsa/alsa-sink.c | 10 +++++++++- src/pulsecore/protocol-native.c | 4 +++- src/pulsecore/sink-input.c | 1 + src/pulsecore/sink-input.h | 1 + 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 68d2c45..323212a 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -60,7 +60,7 @@ #include "alsa-util.h" #include "alsa-sink.h" -/* #define DEBUG_TIMING */ +#define DEBUG_TIMING #define DEFAULT_DEVICE "default" @@ -326,6 +326,8 @@ static void fix_tsched_watermark(struct userdata *u) { static void increase_watermark(struct userdata *u) { size_t old_watermark; pa_usec_t old_min_latency, new_min_latency; + pa_sink_input *i; + void *state; pa_assert(u); pa_assert(u->use_tsched); @@ -351,6 +353,12 @@ static void increase_watermark(struct userdata *u) { new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC); new_min_latency = PA_MIN(new_min_latency, u->sink->thread_info.max_latency); + PA_HASHMAP_FOREACH(i, u->sink->thread_info.inputs, state) { + pa_log_debug("Sink input latency: %0.2f ms", (double) i->thread_info.max_sink_latency / PA_USEC_PER_MSEC); + if (i->thread_info.max_sink_latency > 0) + new_min_latency = PA_MIN(new_min_latency, i->thread_info.max_sink_latency); + } + if (old_min_latency != new_min_latency) { pa_log_info("Increasing minimal latency to %0.2f ms", (double) new_min_latency / PA_USEC_PER_MSEC); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 708878e..294252c 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -60,7 +60,7 @@ #include "protocol-native.h" -/* #define PROTOCOL_NATIVE_DEBUG */ +#define PROTOCOL_NATIVE_DEBUG /* Kick a client if it doesn't authenticate within this time */ #define AUTH_TIMEOUT (60 * PA_USEC_PER_SEC) @@ -1026,6 +1026,8 @@ static void fix_playback_buffer_attr(playback_stream *s) { if (tlength_usec >= s->configured_sink_latency) tlength_usec -= s->configured_sink_latency; + + s->sink_input->thread_info.max_sink_latency = pa_bytes_to_usec(s->buffer_attr.maxlength, &s->sink_input->sample_spec) - minreq_usec; } pa_log_debug("Requested latency=%0.2f ms, Received latency=%0.2f ms", diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index db29696..7baab7f 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -548,6 +548,7 @@ int pa_sink_input_new( i->thread_info.underrun_for_sink = 0; i->thread_info.playing_for = 0; i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + i->thread_info.max_sink_latency = -1; pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0); pa_assert_se(pa_idxset_put(i->sink->inputs, pa_sink_input_ref(i), NULL) == 0); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 47bdaed..4011325 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -250,6 +250,7 @@ struct pa_sink_input { /* The requested latency for the sink */ pa_usec_t requested_sink_latency; + pa_usec_t max_sink_latency; pa_hashmap *direct_outputs; } thread_info; -- 1.7.9.5