[alsa-devel] [PATCH] Wrong latency in pulseaudio plugin breaks Adobe Flash Player
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
- 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
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