I spend some of yesterday and today investigating our different buffer modes to see how they behave in practice, i e when PulseAudio asks for more data and what the resulting latency will be. Our buffer modes are * "adjust latency mode" / PA_STREAM_ADJUST_LATENCY, * "traditional mode" / PA_STREAM_NOFLAGS, * "early requests mode" / PA_STREAM_EARLY_REQUESTS. All tests were done on my onboard soundcard, which has 64K of maximum hardware buffer, which translates to 371 ms in the chosen sample format. When reading this, allow a margin of 1-2 ms for various system latencies, including the turn-around time of the "Stream started" notification, and also remember the default process_msec of 20 ms. The first test - high latency scenario, I had all buffer_attr parameters set to -1. PA_STREAM_ADJUST_LATENCY: 7.19: Writing 1628.50 ms, latency 0.00 to 1628.50 ms 10.88: Stream started 10.96: Writing 363.74 ms, latency 1628.42 to 1992.16 ms 363.12: Writing 358.25 ms, latency 1640.00 to 1998.26 ms 715.59: Writing 352.83 ms, latency 1645.79 to 1998.62 ms PA_STREAM_NOFLAGS: 1.35: Writing 2000.00 ms, latency 0.00 to 2000.00 ms 2.70: Stream started 2.72: Writing 370.07 ms, latency 1999.98 to 2370.05 ms 355.11: Writing 351.75 ms, latency 2017.66 to 2369.41 ms 706.85: Writing 351.84 ms, latency 2017.67 to 2369.50 ms PA_STREAM_EARLY_REQUESTS: 11.67: Writing 2000.00 ms, latency 0.00 to 2000.00 ms 15.24: Stream started 25.53: Writing 29.02 ms, latency 1989.71 to 2018.73 ms 46.16: Writing 20.61 ms, latency 1998.10 to 2018.72 ms 66.90: Writing 20.63 ms, latency 1997.98 to 2018.62 ms To sum up: PA_STREAM_ADJUST_LATENCY - Every time latency is below 2000 - 371 + 20 ms, we're asked to fill up to 2000 ms. PA_STREAM_NOFLAGS - Every time latency is below 2000 + 20 ms, we're asked to fill up to 2000 + 371 ms. PA_STREAM_EARLY_REQUESTS - Every time latency is below 2000 ms, we're asked to fill up to 2000 + 20 ms. Side note: for both PA_STREAM_ADJUST_LATENCY and PA_STREAM_NOFLAGS, after a while the write request is split, like this: 1771.19: Writing 221.90 ms, latency 2018.19 to 2240.09 ms 1771.55: Writing 130.27 ms, latency 2239.73 to 2370.00 ms 2122.88: Writing 90.48 ms, latency 2018.67 to 2109.15 ms 2123.12: Writing 149.43 ms, latency 2108.91 to 2258.35 ms 2123.35: Writing 111.93 ms, latency 2258.11 to 2370.04 ms The sum is the same, and it's nothing a well written client shouldn't be able to handle, but it sounds like there is some room for optimisation here. I haven't looked into why this could be. Ok, second test, a medium-latency scenario, where we set tlength = 200 ms, which IIRC is the GStreamer default. PA_STREAM_ADJUST_LATENCY: 8.29: Writing 120.00 ms, latency 0.00 to 120.00 ms 9.98: Stream started 10.06: Writing 78.71 ms, latency 119.92 to 198.63 ms 70.17: Writing 60.32 ms, latency 138.52 to 198.84 ms 130.06: Writing 60.23 ms, latency 138.95 to 199.17 ms PA_STREAM_NOFLAGS: 9.01: Writing 200.00 ms, latency 0.00 to 200.00 ms 10.68: Stream started 10.77: Writing 158.71 ms, latency 199.90 to 358.61 ms 151.09: Writing 140.29 ms, latency 218.29 to 358.59 ms 291.38: Writing 140.29 ms, latency 218.30 to 358.59 ms PA_STREAM_EARLY_REQUESTS: 7.51: Writing 200.00 ms, latency 0.00 to 200.00 ms 9.07: Stream started 19.45: Writing 28.98 ms, latency 189.62 to 218.60 ms 39.67: Writing 20.36 ms, latency 198.38 to 218.74 ms 60.50: Writing 20.52 ms, latency 197.91 to 218.43 ms To sum up: PA_STREAM_ADJUST_LATENCY - Every time latency is below 200 / 2 + 2 * 20 ms = 140 ms, we're asked to fill up to 200 ms. PA_STREAM_NOFLAGS - Every time latency is below 200 + 20 ms, we're asked to fill up to 200 * 2 - 2 * 20 ms = 360 ms. PA_STREAM_EARLY_REQUESTS - Every time latency is below 200 ms, we're asked to fill up to 200 + 20 ms. The 2 * minreq thing used in the core isn't entirely obvious to me, but the general rule remain; if you want *max* latency to be tlength, set PA_STREAM_ADJUST_LATENCY, and if you want *min* latency to be tlength, go for PA_STREAM_NOFLAGS. PA_STREAM_EARLY_REQUESTS is probably only meant where you specify minreq too, like the alsa-plugins layer does. Let's leave that for now. Ok, next is a low-latency scenario of setting maxlength = tlength = 10 ms. But it failed miserably in all buffer modes, because PulseAudio wouldn't ask for more data soon enough. With patch 2/2 in this series applied, things got a lot better: PA_STREAM_ADJUST_LATENCY: 7.52: Writing 7.50 ms, latency 0.00 to 7.50 ms 8.55: Stream started 10.11: Writing 2.72 ms, latency 5.95 to 8.67 ms 14.21: Writing 3.88 ms, latency 4.56 to 8.44 ms 17.88: Writing 3.83 ms, latency 4.77 to 8.60 ms PA_STREAM_NOFLAGS: 8.40: Writing 10.00 ms, latency 0.00 to 10.00 ms 9.80: Stream started 9.87: Writing 3.70 ms, latency 9.94 to 13.63 ms 12.33: Writing 2.77 ms, latency 11.16 to 13.93 ms 15.04: Writing 2.72 ms, latency 11.22 to 13.94 ms PA_STREAM_EARLY_REQUESTS: 7.18: Writing 10.00 ms, latency 0.00 to 10.00 ms 8.15: Stream started 9.58: Writing 2.65 ms, latency 8.57 to 11.22 ms 13.27: Writing 3.72 ms, latency 7.54 to 11.26 ms 17.06: Writing 3.74 ms, latency 7.46 to 11.21 ms With the minreq patch that sets minreq to 10 / 4 = 2.5 ms, we can see that the result for PA_STREAM_ADJUST_LATENCY is to try to keep the latency between 7.5 ms and 10 ms, for PA_STREAM_NOFLAGS to try to keep the latency between 12.5 ms and 15 ms, and for PA_STREAM_EARLY_REQUESTS to try to keep latency between 10 ms and 12.5 ms. We end up always writing a little bit more than 2.5 ms in all scenarios, probably due to scheduling variations within the system. Comments / questions? David Henningsson (2): protocol-native: Ensure tlength is not set higher than maxlength protocol-native: Lower default minreq in low-latency scenarios src/pulsecore/protocol-native.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) -- 1.7.9.5