OSS Layer: restart stream after xrun once stream is drained.
PATCH 1/1 OSS Layer: restart stream after xrun once stream is drained.
Quoted 8 lines · Jaroslav Kysela
Yes, but at another level - in the OSS emulation code.
Quoted 2 lines
What is the correct way to recover from this, to continue recording in this case, after the xrun?
Call
read()or poll(POLLIN) again. I think that your problem might be that poll(POLLIN) does not trigger the input in the current ALSA code. Please, try this patch with ViaVoice (the patch is also in the ALSA CVS tree):
Hi Jaroslav,
I wrote a patch for sound/core/oss/pcm_oss.c, that fixes the ViaVoice problem.
Problem:
When an xrun happens, the stream is stopped.
The OSS layer (read1) changes the xrun state into a draining state, but never restarts the stream.
This patch restart the stream once so much had been drained from the stream that less than one fragement remains.
It is not safe to wait till the stream is empty (although it should be when the application only reads full fragments) because it might be that the application only calls read() when there is at least one fragment ... kind of a contradiction, but then why not like this.
sound/core/oss/pcm_oss.c +14 -1
@@ -627,8 +627,21 @@ }continue ; } -if (ret != -ESTRPIPE ) +if (ret != -ESTRPIPE ) { +if (runtime->status->state ==SNDRV_PCM_STATE_DRAINING ) { +snd_pcm_status_t status; +memset (&status,0 ,sizeof (status)); +int err =snd_pcm_kernel_ioctl (substream,SNDRV_PCM_IOCTL_STATUS , &status); +if (!err && status.avail < runtime->period_size) { + err =snd_pcm_kernel_ioctl (substream,SNDRV_PCM_IOCTL_PREPARE ,0 ); +if (!err) +snd_pcm_start (substream); + } +if (err <0 ) + ret = err; + }break ; + } }return ret; }
The attached test program gives, WITHOUT this patch the following output (This test case reads SLOW, to make sure that xruns happen frequently):
gment size: 1024
Number of fragments: 32767
Number of channels: 1
Sample rate: 22050
Number of full fragments that can be read or written without blocking: 0 Total number of fragments allocated for buffering: 2
Size of a fragment in bytes: 1024
Number of bytes that can be read or written immediately without blocking: 0
space: bytes = 32, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 1056, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 30635.751683
space: bytes = 928, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2016, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 16124.799227
space: bytes = 0, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 0, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 0, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 0, periods = 0, fragstotal = 2, fragsize = 1024 [...]
WITH the above patch it does this:
Fragment size: 1024
Number of fragments: 32767
Number of channels: 1
Sample rate: 22050
Number of full fragments that can be read or written without blocking: 0 Total number of fragments allocated for buffering: 2
Size of a fragment in bytes: 1024
Number of bytes that can be read or written immediately without blocking: 0
space: bytes = 32, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 1088, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 29704.837189
space: bytes = 928, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 14847.537252
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 15708.954795
space: bytes = 800, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 12830.954678
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 13536.438665
space: bytes = 832, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 12074.536003
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 12573.210712
space: bytes = 832, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11650.397069
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 12040.231304
space: bytes = 928, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11327.103292
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11590.736089
space: bytes = 928, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11034.144344
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11272.664653
space: bytes = 992, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 10823.670013
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11022.745850
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11211.084341
space: bytes = 1056, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11387.316413
space: bytes = 1056, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11569.761392
space: bytes = 960, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11186.543811
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11377.988481
space: bytes = 864, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11060.036340
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11234.400773
space: bytes = 864, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 10955.674898
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11107.524306
space: bytes = 896, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 10858.584314
space: bytes = 1024, periods = 1, fragstotal = 2, fragsize = 1024 Read 1024 bytes. Average bytes/second read: 11011.375394
space: bytes = 864, periods = 0, fragstotal = 2, fragsize = 1024
space: bytes = 2048, periods = 2, fragstotal = 2, fragsize = 1024 [...etc...]
The real rate being 8000, we have 16 kb/s of input
from the soundcard, but only read (on average) 11 kb/s, from which we can conclude that the stream is stopped 31% of the time.
Signature · 8 lines
Carlo Wood <[email protected]> ------------------------------------------------------- This SF.Net email sponsored by: Parasoft Error proof Web apps, automate testing & more. Download & eval WebKing and get a free book. www.parasoft.com/bulletproofapps1
Quoted 2 lines · Carlo Wood
I wrote a patch for
sound/core/oss/pcm_oss.c, that fixes the ViaVoice problem.
Later I wrote a test case that still doesn't get fixed:
Quoted 1 line
a.out
Size of a fragment in bytes: 1024
Allocated fragments for buffering: 2
Successfully caused an xrun.
non-blocking fragments: 2
non-blocking bytes: 2048
Stream is not restarted after xrun.
now this is expected without my patch, but with
the patch the stream *is* set back to a RUNNING state again successfully - nevertheless, snd_pcm_update_hw_ptr_interrupt is not called anymore.
Is there anyone who can get the following test code to work (by patching the ALSA kernel module, not by changing the test code! ;).
Test code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/soundcard.h>
#include <time.h>
int main(void)
{
int fd = open("/dev/dsp", O_RDONLY);
if (fd == -1) { perror("open"); exit(127); }
int res = AFMT_S16_LE;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &res) == -1) {
perror("ioctl"); exit(127); }
res = 0;
if (ioctl(fd, SNDCTL_DSP_STEREO, &res) == -1) {
perror("ioctl"); exit(127); }
res = 22050;
if (ioctl(fd, SOUND_PCM_READ_RATE, 0xbfffdcfc) == -1) {
perror("ioctl"); exit(127); }
res = 0x7fff000a;
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &res) == -1) {
perror("ioctl"); exit(127); }
audio_buf_info info;
if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) == -1) {
perror("read"); exit(127); }
printf(" Size of a fragment in bytes: %d\n", info.fragsize);
printf(" Allocated fragments for buffering: %d\n", info.fragstotal);
char buf[1024];
if (read(fd, buf, sizeof(buf)) < 0) { perror("read"); exit(127); }
static struct timespec naptime = { 0, 100000000 };
int count = 0;
do {
if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) == -1) {
perror("read"); exit(127); }
nanosleep(&naptime, 0);
if (++count == 20) { printf("Failed to cause an xrun.\n"); exit(127); }
} while(info.bytes < info.fragsize * info.fragstotal);
printf(" Successfully caused an xrun.\n");
printf(" non-blocking fragments: %d\n", info.fragments);
printf(" non-blocking bytes: %d\n", info.bytes);
ssize_t bufsize = info.bytes;
ssize_t trlen = 0;
int nf = 0;
for (;;)
{
if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) == -1) {
perror("ioctl"); exit(127); }
if (info.fragments > 0) {
ssize_t rlen;
if ((rlen = read(fd, buf, sizeof(buf))) < 0)
{ perror("read"); exit(127); }
trlen += rlen;
if (trlen > bufsize) {
printf(" Read %d bytes: stream successfully restarted.\n", trlen);
exit(0);
}
nf = 0;
}
else if (++nf > 10) {
printf(" Stream is not restarted after xrun.\n");
exit(1);
} }
close(fd);
return 0;
}
Here is some debug output that I generated with added printk's:
Jul 13 17:22:27 ansset kernel: Entering snd_pcm_update_hw_ptr_interrupt Jul 13 17:22:27 ansset kernel: snd_pcm_update_hw_ptr_interrupt: status->hw_ptr set to 1536 Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail: boundary = 1073741824, status->hw_ptr = 1536, control->appl_ptr = 512 Jul 13 17:22:27 ansset kernel: Calling snd_pcm_stop, runtime->status->state = SNDRV_PCM_STATE_RUNNING Jul 13 17:22:27 ansset kernel: Returned from snd_pcm_stop, runtime->status->state = SNDRV_PCM_STATE_XRUN Jul 13 17:22:27 ansset kernel: Leaving snd_pcm_update_hw_ptr_interrupt with EPIPE ... Jul 13 17:22:27 ansset kernel: snd_pcm_oss_read3: calling snd_pcm_kernel_ioctl SNDRV_PCM_IOCTL_DRAIN Jul 13 17:22:27 ansset kernel: snd_pcm_capture_drain: state is SNDRV_PCM_STATE_XRUN. Calling snd_pcm_change_state. Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail: boundary = 1073741824, status->hw_ptr = 1536, control->appl_ptr = 512 Jul 13 17:22:27 ansset kernel: snd_pcm_capture_drain: Returning from snd_pcm_change_state, state is now 5 Jul 13 17:22:27 ansset kernel: Entering snd_pcm_lib_read1 Jul 13 17:22:27 ansset kernel: state is SNDRV_PCM_STATE_DRAINING Jul 13 17:22:27 ansset kernel: size = 512 Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail: boundary = 1073741824, status->hw_ptr = 1536, control->appl_ptr = 512 Jul 13 17:22:27 ansset kernel: Leaving snd_pcm_lib_read1; xfer = 512, err = 0 ... Jul 13 17:22:27 ansset kernel: Entering snd_pcm_lib_read1 Jul 13 17:22:27 ansset kernel: state is SNDRV_PCM_STATE_DRAINING Jul 13 17:22:27 ansset kernel: size = 512 Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail: boundary = 1073741824, status->hw_ptr = 1536, control->appl_ptr = 1024 Jul 13 17:22:27 ansset kernel: Leaving snd_pcm_lib_read1; xfer = 512, err = 0 Jul 13 17:22:27 ansset kernel: snd_pcm_status: status->hw_ptr copied to be 1536 Jul 13 17:22:27 ansset kernel: Calling snd_pcm_capture_avail() Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail: boundary = 1073741824, status->hw_ptr = 1536, control->appl_ptr = 1536 Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail returned status->avail = 0 Jul 13 17:22:27 ansset kernel: snd_pcm_status_user: status.avail = 0
[start of effect of my patch]
Jul 13 17:22:27 ansset kernel: snd_pcm_oss_read3: calling snd_pcm_kernel_ioctl(SNDRV_PCM_IOCTL_PREPARE) Jul 13 17:22:27 ansset kernel: snd_pcm_lib_ioctl_reset: status->hw_ptr set to 0 Jul 13 17:22:27 ansset kernel: snd_pcm_oss_read3: state now SNDRV_PCM_STATE_RUNNING Jul 13 17:22:27 ansset kernel: snd_pcm_update_hw_ptr: status->hw_ptr set to 0 Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail: boundary = 1073741824, status->hw_ptr = 0, control->appl_ptr = 0 Jul 13 17:22:27 ansset kernel: snd_pcm_status: status->hw_ptr copied to be 0 Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail: boundary = 1073741824, status->hw_ptr = 0, control->appl_ptr = 0 Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail returned status->avail = 0 Jul 13 17:22:27 ansset kernel: snd_pcm_status_user: status.avail = 0 Jul 13 17:22:27 ansset kernel: space: bytes = 0, periods = 0, fragstotal = 2, fragsize = 1024
[end of patch effects]
...
but snd_pcm_update_hw_ptr_interrupt is never called anymore, it only repeats
Jul 13 17:22:27 ansset kernel: snd_pcm_update_hw_ptr: status->hw_ptr set to 0 Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail: boundary = 1073741824, status->hw_ptr = 0, control->appl_ptr = 0 Jul 13 17:22:27 ansset kernel: snd_pcm_status: status->hw_ptr copied to be 0 Jul 13 17:22:27 ansset kernel: Calling snd_pcm_capture_avail() Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail: boundary = 1073741824, status->hw_ptr = 0, control->appl_ptr = 0 Jul 13 17:22:27 ansset kernel: snd_pcm_capture_avail returned status->avail = 0 Jul 13 17:22:27 ansset kernel: snd_pcm_status_user: status.avail = 0 Jul 13 17:22:27 ansset kernel: space: bytes = 0, periods = 0, fragstotal = 2, fragsize = 1024
after that.
Signature · 8 lines
Carlo Wood <[email protected]> ------------------------------------------------------- This SF.Net email sponsored by: Parasoft Error proof Web apps, automate testing & more. Download & eval WebKing and get a free book. www.parasoft.com/bulletproofapps1