[alsa-devel] [PATCH] Wrong latency in pulseaudio plugin breaks Adobe Flash Player

Philip Spencer pspencer at fields.utoronto.ca
Thu Feb 23 03:50:57 CET 2012

Thank you for the reply! (I've not quoted below because it was getting too 
cumbersome and the contents of this message are somewhat different from 
the previous discussions).

I have now dug into exactly what's happening by putting trace statements 
in all the alsa calls, and it turns out to be something very different 
from what I thought. It isn't pulseaudio at all that's introducing the 
extra latency, nor are underruns happening there.

What's happening is this:

   - Flash Player's understanding of ALSA polling and snd_pcm_wait is
     different from how ALSA actually behaves; it expects to be able to
     use these to wake up at the time of the next IO period.

   - After putting the first few packets into the buffer (each packet has
     20ms of samples, and 20ms is the IO period length), it waits about
     16.4 ms then calls snd_pcm_wait.

   - It expects the call to return at the start of the next I/O period
     (when the first 20ms worth of data have been played) and then writes
     the next packet, which by then it will have received from the network.

   - However, that's not how ALSA works. If the buffer isn't full, polling
     returns immediately, instead of waiting for the next I/O period.

   - Flash player then thinks playback is happening at an accelerated rate.
     The first time it probably has a packet or two buffered up internally
     so it sends those packets, but when these run out it inserts extra
     packets of silence and then starts trying to resample the incoming
     data to match what it thinks is a faster playback rate, resulting
     in extra packets being passed to ALSA: a 20ms packet is written
     every 16.4ms.

   - Eventually (after about 2.5 seconds) these extra packets have filled
     up the buffer and then Flash Player's snd_pcm_wait calls actually
     block until the next I/O period. At this point playback stabilizes
     but with a 500ms latency due to the now-full buffer.

This presumably happens with other ALSA backends too, not just the 
pulseaudio plugin.

If I change the pulseaudio plugin so that:

    -- Whenever a write request callback is received from pulseaudio,
       it records the writable size

    -- Whenever writes occur that reduce the writable size by at least
       an IO period's worth from the value that was recorded, polling is
       deactivated, and the "avail_min" parameter is adjusted so that
       the pcm will only be considered "ready" when the available bytes
       rise by an IO period's amount.

    -- When the next write request callback is received (pulseaudio
       seems to send them whenever an IO period's worth of samples are
       transferred to the sink), avail_min is reset to its normal
       value and polling is reactivated

thereby establishing the polling behaviour flash player seems to expect, 
then it works flawlessly.

However, that's probably not how ALSA was intended to behave -- or is it?
The documentation is somewhat ambiguous, and it does seem to imply in 
places that polling can be used to "wake up once every IO period".

What is the "right" behaviour in the following scenario?

    1. An app writes samples, at least one I/O period's worth, but not
       a full buffer's worth.

    2. The app then calls pcm_snd_wait, or calls poll or select on the
       polling file descriptors.

    3. What should happen?

           (a) The call returns immediately, since there's lots of room
               in the buffer.

           (b) The call returns after one I/O period's worth of data has
               been drained from the buffer.

If the correct answer is (a) (ALSA's current behaviour), then is there 
any mechanism for an app to achieve (b) -- get woken up after one I/O 
period's worth of data have been drained (other than just doing some
infinite loop of sleeping then checking snd_pcm_avail periodically)?

If the correct answer is (b), then things would need to be changed in 
ALSA, but would this break any existing apps that wait or poll before
filling up the buffer?

If ALSA were to be changed to behave as (b), then an app can easily 
achieve the old behaviour (a) by simply doing

     if (snd_pcm_avail(...) < ...) snd_pcm_wait(...)

instead of a plain snd_pcm_wait, but I don't see how to easily achieve (b)
if ALSA behaves as (a).

Do you, or anyone, have a definitive answer on what the "right" polling 
behaviour should be, and is ALSA currently doing the right thing or not?



Philip Spencer  pspencer at fields.utoronto.ca | Director of Computing Services
Room 336        (416)-348-9710  ext3036     | The Fields Institute for
222 College St, Toronto ON M5T 3J1 Canada   | Research in Mathematical Sciences

More information about the Alsa-devel mailing list