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