-
Notifications
You must be signed in to change notification settings - Fork 5.2k
alsa: snd_pcm_drain() sometimes takes longer than it should #999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Anything appear in dmesg? |
From what I remember no, but I can check for sure tomorrow. |
Realistically it's not going to be possible to fix without a repeatable test case. |
Nothing in any of these logs. I managed to make a test case: http://sprunge.us/gZcH // Compile with: gcc -std=c99 test.c `pkg-config alsa --libs --cflags`
#define _XOPEN_SOURCE 500
#include <stdlib.h>
#include <unistd.h>
#include <alloca.h>
#include <alsa/asoundlib.h>
int main(int argc, char **argv)
{
snd_pcm_t *alsa;
if (snd_pcm_open(&alsa, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0)
abort();
if (snd_pcm_nonblock(alsa, 0) < 0)
abort();
snd_pcm_hw_params_t *alsa_hwparams;
snd_pcm_sw_params_t *alsa_swparams;
snd_pcm_hw_params_alloca(&alsa_hwparams);
snd_pcm_sw_params_alloca(&alsa_swparams);
if (snd_pcm_hw_params_any(alsa, alsa_hwparams) < 0)
abort();
if (snd_pcm_hw_params_set_format(alsa, alsa_hwparams, SND_PCM_FORMAT_S16) < 0)
abort();
if (snd_pcm_hw_params_set_access(alsa, alsa_hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
abort();
int channels = 2;
if (snd_pcm_hw_params_set_channels_near(alsa, alsa_hwparams, &channels) < 0 || channels != 2)
abort();
if (snd_pcm_hw_params_set_rate_resample(alsa, alsa_hwparams, 0) < 0)
abort();
int rate = 8000;
if (snd_pcm_hw_params_set_rate_near(alsa, alsa_hwparams, &rate, NULL) < 0 || rate != 8000)
abort();
if (snd_pcm_hw_params_set_buffer_time_near(alsa, alsa_hwparams, &(unsigned int){250000}, NULL) < 0)
abort();
if (snd_pcm_hw_params_set_periods_near(alsa, alsa_hwparams, &(unsigned int){16}, NULL) < 0)
abort();
if (snd_pcm_hw_params(alsa, alsa_hwparams) < 0)
abort();
if (snd_pcm_sw_params_current(alsa, alsa_swparams) < 0)
abort();
snd_pcm_uframes_t boundary;
if (snd_pcm_sw_params_get_boundary(alsa_swparams, &boundary) < 0)
abort();
snd_pcm_uframes_t chunk_size;
if (snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL) < 0)
abort();
if (snd_pcm_sw_params_set_start_threshold(alsa, alsa_swparams, chunk_size) < 0)
abort();
if (snd_pcm_sw_params_set_stop_threshold(alsa, alsa_swparams, boundary) < 0)
abort();
if (snd_pcm_sw_params_set_silence_size(alsa, alsa_swparams, boundary) < 0)
abort();
if (snd_pcm_sw_params(alsa, alsa_swparams) < 0)
abort();
int samples = 32768;
char *stuff = calloc(samples, 4);
if (!stuff)
abort();
fprintf(stderr, "start playing\n");
snd_pcm_prepare(alsa);
int r = snd_pcm_writei(alsa, stuff, samples);
fprintf(stderr, "r=%d\n", r);
usleep(300000);
fprintf(stderr, "start drain\n");
snd_pcm_drain(alsa);
fprintf(stderr, "end drain\n");
return 0;
} This takes about 14 seconds to complete, even though it's only about 4 seconds of audio. It basically hangs while draining. This is pretty reproducible here. So far every run took much longer than the at most 5 seconds it should take. The usleep() call is required - I think it provokes a buffer underflow. |
I see the effect you describe. I've had a look on the firmware side and it seems happy:
So we get the last WRITE comand from kernel driver. It takes another 25ms to play it out. Then a ten second pause before we are told to stop/close. I'm guessing kernel side is not understanding that the firmware side has played out and there is a 10 second timeout in alsa before it closes anyway. In general the firmware receives samples, and sends messages back to kernel as they are played out. Eventually played out samples == submitted samples and kernel knows stream has completely drained. I'm guessing the underrun that occurs before the drain is affecting the submitted vs played out numbers and kernel is still waiting for the "underrun" samples to be played out. I'll need to try to understand what goes on In the kernel when the underrun occurs... |
Ah i see this behaviour in my squeezplay Pi build also when using the default internal devices only, I see buffer underflows (large alsa buffers to some effect mitigated this). |
@wm4 has your issue been resolved? If so, please close this issue. Thanks. |
I'm not aware that this has been fixed, so probably not. |
@popcornmix @wm4 I know that there hasn't been linked directly from any commit to this PR, but could it be that it has been fixed by some other commit and there it should be tested with latest firmware/code? |
@popcornmix @wm4 Reiterating @Ruffio previous comment. |
It looks like the Whether this is the right behavior in case of an underrun I don't know. Here is the output of
|
@popcornmix ANy idea how to progress with this one? |
This happens with the bcm2835 driver.
snd_pcm_drain() should normally wait until all buffered audio data buffered has been played. But sometimes, it seems to take much longer. It takes seconds, even though the amount of buffered audio is only milliseconds.
Here's a backtrace (with mpv):
This seems to happen pretty randomly (and somehow timing related), so I'm really not sure how to provide a reproducible test case.
The text was updated successfully, but these errors were encountered: