Hi,

I've been debugging a situation where snd_pcm_drain takes an unusually long
time when using the alsa-pulse plugin.  I reduced the testcase to a one
using the PA client API directly as the culprit seems to be the operation
returned by pa_stream_drain takes an unusually long time to complete.  I've
attached the testcase if anyone's interested.

I'm running Fedora 13 with all updates applied.  PulseAudio version:
pulseaudio-0.9.21-6.fc13.x86_64.  Sound hardware is Intel Corporation 5
Series/3400 Series Chipset High Definition Audio (rev 06).  I haven't tested
against git HEAD because my PA clients fail to connect to the PA server I
built.

The testcase writes N seconds (as specified on the command line) of 48kHz
mono audio, then immediately calls pa_stream_drain and waits on the
resulting operation by testing the operation's status in a loop.

My expectation is that the drain complete when the audio finishes playing,
so it should take roughly N seconds to complete having written N seconds of
audio.  What I'm seeing is an additional 2 second delay:

% time ./a.out 1
write 48000 samples
./a.out 1  0.00s user 0.01s system 0% cpu 3.054 total
% time ./a.out 2
write 96000 samples
./a.out 2  0.00s user 0.01s system 0% cpu 4.057 total
% time ./a.out 10
write 480000 samples
./a.out 10  0.01s user 0.00s system 0% cpu 12.109 total

Cheers,
-mjg
-- 
Matthew Gregan                     |/
                                  /|                    kine...@flim.org
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pulse/pulseaudio.h>

#define RATE 48000

struct state {
  pa_threaded_mainloop * mainloop;
  pa_context * context;
  pa_stream * stream;
};

void context_state_cb(pa_context * c, void * m)
{
  assert(PA_CONTEXT_IS_GOOD(pa_context_get_state(c)));
  pa_threaded_mainloop_signal(m, 0);
}

void stream_success_cb(pa_stream * stream, int success, void * m)
{
  pa_threaded_mainloop_signal(m, 0);
}

void stream_cb(pa_stream * stream, void * m)
{
  assert(PA_STREAM_IS_GOOD(pa_stream_get_state(stream)));
  pa_threaded_mainloop_signal(m, 0);
}

void stream_request_cb(pa_stream * stream, size_t length, void * m)
{
#if 0
  fprintf(stderr, "stream_request %zu\n", length);
#endif
}

void drain_stream(struct state * s)
{
  pa_operation * o;

  pa_threaded_mainloop_lock(s->mainloop);
  o = pa_stream_drain(s->stream, stream_success_cb, s->mainloop);
  for (;;) {
    if (pa_operation_get_state(o) != PA_OPERATION_RUNNING)
      break;
    pa_threaded_mainloop_wait(s->mainloop);
  }
  pa_threaded_mainloop_unlock(s->mainloop);
}

void state_wait(struct state * s, pa_context_state_t wanted)
{
  pa_threaded_mainloop_lock(s->mainloop);
  for (;;) {
    pa_context_state_t state = pa_context_get_state(s->context);
    assert(PA_CONTEXT_IS_GOOD(state));
    if (state == wanted)
      break;

    pa_threaded_mainloop_wait(s->mainloop);
  }
  pa_threaded_mainloop_unlock(s->mainloop);
}

void s_state_wait(struct state * s, pa_stream_state_t wanted)
{
  pa_threaded_mainloop_lock(s->mainloop);
  for (;;) {
    pa_stream_state_t state = pa_stream_get_state(s->stream);
    assert(PA_STREAM_IS_GOOD(state));
    if (state == wanted)
      break;

    pa_threaded_mainloop_wait(s->mainloop);
  }
  pa_threaded_mainloop_unlock(s->mainloop);
}

void setup(struct state * s)
{
  pa_sample_spec ss;
  pa_buffer_attr battr;
  pa_stream_flags_t flags;

  ss.format = PA_SAMPLE_S16LE;
  ss.channels = 1;
  ss.rate = RATE;

  battr.maxlength = 4 * 1024 * 1024;
  battr.tlength = 12000 * 2;
  battr.prebuf = battr.tlength - (battr.tlength / 4);
  battr.minreq = battr.tlength / 4;
  battr.fragsize = battr.tlength / 4;

  s->mainloop = pa_threaded_mainloop_new();
  s->context = pa_context_new(pa_threaded_mainloop_get_api(s->mainloop), "foo test");
  pa_context_set_state_callback(s->context, context_state_cb, s->mainloop);
  pa_threaded_mainloop_start(s->mainloop);

  pa_threaded_mainloop_lock(s->mainloop);
  pa_context_connect(s->context, NULL, 0, NULL);
  pa_threaded_mainloop_unlock(s->mainloop);

  state_wait(s, PA_CONTEXT_READY);

  pa_threaded_mainloop_lock(s->mainloop);
  s->stream = pa_stream_new(s->context, "foo playback", &ss, NULL);
  pa_stream_set_state_callback(s->stream, stream_cb, s->mainloop);
  pa_stream_set_latency_update_callback(s->stream, stream_cb, s->mainloop);
  pa_stream_set_write_callback(s->stream, stream_request_cb, s->mainloop);
  flags = PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING;
  pa_stream_connect_playback(s->stream, NULL, &battr, flags, NULL, NULL);
  pa_threaded_mainloop_unlock(s->mainloop);

  state_wait(s, PA_CONTEXT_READY);
  s_state_wait(s, PA_STREAM_READY);
}

void bwrite(struct state * s, size_t towrite)
{
  int r;
  char * garbage;

  garbage = calloc(1, towrite);
  pa_threaded_mainloop_lock(s->mainloop);
  r = pa_stream_write(s->stream, garbage, towrite, NULL, 0, 0);
  pa_threaded_mainloop_unlock(s->mainloop);
  free(garbage);
  assert(r == 0);
}

int main(int argc, char * argv[])
{
  struct state s;
  size_t towrite = RATE;
  int secs = 1;

  if (argc > 1)
    secs = atoi(argv[1]);

  towrite *= secs;

  setup(&s);

  fprintf(stderr, "write %zu samples\n", towrite);
  bwrite(&s, towrite * sizeof(short));
  drain_stream(&s);

  return 0;
}
_______________________________________________
pulseaudio-discuss mailing list
pulseaudio-discuss@mail.0pointer.de
https://tango.0pointer.de/mailman/listinfo/pulseaudio-discuss

Reply via email to