I'm investigating an issue in Firefox's audio code when the PulseAudio ALSA
plugin is in use.
It wasn't clear if PulseAudio ALSA plugin issues should be raised here or on
alsa-devel, so please let me know if this is the wrong forum for this issue.
Firefox requests a particular latency (100ms, 4410 frames at 44.1kHz) via
snd_pcm_set_params. Inside the plugin (pcm_pulse.c:pulse_hw_params), that
value is used to set up buffer_attr. When the PA stream is connected in
pcm_pulse.c:pulse_prepare, PA may increase the buffer_attr values, but this
isn't reflected in pcm->buffer_attr or higher layers in ALSA.
Would a possible fix for this be to query pa_stream_get_buffer_attr after
connecting and update pcm->buffer_attr and the higher (ioplug/etc.) layers
that the buffer size has increased?
The problem I'm faced with is that there doesn't appear to be a way to
detect and handle this issue at the ALSA API level, and requesting a too low
latency results in broken audio playback rather than a PCM setup failure or
a larger buffer than requested being used.
I've attached a simple testcase that uses snd_pcm_wait,
snd_pcm_avail_update, and snd_pcm_writei. Run it with a latency argument
specified in milliseconds on the command line. For my local machine, 45ms
works and 44ms fails immediately like so:
snd_pcm_wait wakes
snd_pcm_avail_update returns 4410
snd_pcm_writei writes 4410
snd_pcm_wait wakes immediately
snd_pcm_avail_update returns -EPIPE
I'd expect to see one of the following behaviours instead:
1. PCM setup fails due to requesting a too small buffer.
2. Buffer is silently raised during setup and snd_pcm_avail_update requests
the correct number of frames.
3. PCM wakes from snd_pcm_wait for refill enough times to fill the buffer.
As a side note: this seems to be related to the PA server increasing the
minimum latency over time. Once the PA server is in this state, restarting
it (in non-daemon mode with debug logging or via pulseaudio -k and
autospawn) causes the problem to disappear. After restarting the server, I
can run the testcase with 44ms and lower latency successfully.
Thanks,
-mjg
#include <sys/time.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
static double
ts(void)
{
static struct timeval last;
struct timeval now, dt;
if (last.tv_sec == 0) {
gettimeofday(&last, NULL);
}
gettimeofday(&now, NULL);
timersub(&now, &last, &dt);
last = now;
return dt.tv_sec * 1000.0 + dt.tv_usec / 1000.0;
}
int
main(int argc, char * argv[])
{
int r;
snd_pcm_t * pcm;
snd_pcm_uframes_t buffer_size, period_size;
char silence[88200] = {0};
if (argc != 2) {
return 1;
}
r = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0);
assert(r == 0);
r = snd_pcm_set_params(pcm, SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED, 2, 44100, 1,
atoi(argv[1]) * 1000);
assert(r == 0);
r = snd_pcm_get_params(pcm, &buffer_size, &period_size);
assert(r == 0);
fprintf(stderr, "%.2f: buffer_size = %u, period_size = %u\n",
ts(), (unsigned) buffer_size, (unsigned) period_size);
assert(buffer_size <= sizeof(silence) / 4);
for (;;) {
snd_pcm_sframes_t avail, wrote = 0;
snd_pcm_wait(pcm, -1);
avail = snd_pcm_avail_update(pcm);
if (avail == -EPIPE) {
snd_pcm_recover(pcm, -EPIPE, 0);
avail = snd_pcm_avail_update(pcm);
}
if (avail > 0) {
wrote = snd_pcm_writei(pcm, silence, avail);
}
fprintf(stderr, "%.2f: avail = %d, wrote = %d\n", ts(), (int) avail, (int) wrote);
}
return 0;
}
_______________________________________________
pulseaudio-discuss mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss