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

Reply via email to