So you are writing an application and want to control the latency PulseAudio provides for it, for example because you are writing a voip tool that needs latencies that are near to some specific value. Here's how you do it with PA:
First, make sure you run PA in timer scheduling mode (tsched=1) for ALSA devices which is the default for most modern distributions, with the infamous exception of Ubuntu (9.04 only). Only when that is enabled PA is able to adjust the latency to what your application asks for. Non-ALSA devices (such as Bluetooth audio) generally have a fixed latency which cannot be influenced by software.
In your code you then have to do the following when calling pa_stream_connect_playback() resp. pa_stream_connect_record():
- Pass PA_STREAM_ADJUST_LATENCY in the flags parameter. Only if this flag is set PA will reconfigure the low-level device's buffer size and adjust it to the latency you specify.
- Pass a pa_buffer_attr struct in the buffer_attr parameter. In the fields of this struct make sure to initialize every single field to (uint32_t) -1, with the exception of tlength (for playback) resp. fragsize (for recording). Initialize those to the latency you want to achieve. Use pa_usec_to_bytes(usec, &ss) to convert the latency from a time unit to bytes. Please note that clients may ask for a specific latency, but PA takes this only as a hint, it does not guarantee that it will provide it. That means after initialization you MUST base your timing code on the actually measured latency as returned by pa_stream_get_latency() (and friends), you MAY NOT make assumptions that the measured latency will actually be near or even below what you asked for.
If the underlying device has latency restrictions the actual measured latency might be either higher or lower than what you asked for. If the OS scheduler was not able to provide PA with the necessary scheduling latencies needed to fulfill the audio latencies these will be increased to make sure drop-outs stay the exception, not the rule. If a stream gets moved between devices the latency may change, due to different hardware constraints. Finally, PA will configure the device buffer to the lowest latency of all applications streams connected to it. That means that your measured latency might dynamically change during runtime and might deviate very much from what you initially asked for. But again, PA will try its best to fulfill what you requested.
So in summary: tell PA what latency you want, but program defensively so that you can deal with getting both lower or higher measured latencies.
If you want to verify how the latencies your application asked for are actually handled by PA check the output of "pactl list sinks". This will show you the list of sinks with their configured and measured latencies. The 'current latency' field is the currently measured latency (probably not too useful unless you combine it with a timestamp). The 'configured latency' shows the minimal latency configured by all applications connected to the device, plus the range that is acceptable to the device. It is only shown for variable latency devices. For those with fixed latency, check out the 'fixed latency' field. Also check out the output of "pactl list sink-inputs" which shows both the measured latency for the application stream as well as the requested latency for it.
Side note: If the buffering parameters change during runtime you will get a notification callback which may register via pa_stream_set_buffer_attr_callack(). Note that this will tell you the changes to the buffer_attr structure, which is related to but different from the measured latency!
You may change the latency during runtime with pa_stream_set_buffer_attr().
Developing High-Latency Applications
By default (i.e. when the buffer_attr argument to pa_stream_connect_playback() is NULL) PA will pick a latency that is somewhat similar to what programs used to get when using OSS, or ALSA directly and didn't specify any particular latency requirements. For a lot of use cases where latency does not matter (such as media players) this latency is much smaller then would be good for optimal power consumption. That means it is highly recommended passing a buffer_attr argument in those applications, with a tlength field initialized to (uint32_t) -1, which effectively means "pick the highest latency the device supports". Usually that means that you get 2s latency, which has the effect of reducing wakeups to something near 1/s. Make sure to drop the server side playback buffers when seeking/pausing/..., so that these actions are instantanious although you buffered 2s.
Note that passing a NULL bufferattr is different from passing one with every field initialized to (uint32_t) -1! The former means 'get me the default latency'. The latter means 'get me the highest latency possible'. The latter is a much better idea most of the time than the former! _
How to pick the latency
For the sake of reducing power consumption and drop-out safety always make sure to pick the highest latency possible that fulfills your needs. Never pick a lower latency than you really need. If you do pick needlessly low latencies you just burn CPU and make it more likely that we will get a drop-out. And worst of all that makes PA and me look bad, since people will blame PA and me for it. And I don't like to look bad! ;-) Extensive CPU load will show up as PA CPU load in 'top'.
Why such a complex API?
Yes, requiring to set a flag plus filling in a struct is a bit too complex. This has mostly historical reasons. Sorry for that.
This all applies to PA 0.9.16 and newer.