[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Linphone-developers] Bug in ALSA write error handling?
From: |
Greg Farrell |
Subject: |
[Linphone-developers] Bug in ALSA write error handling? |
Date: |
Mon, 15 May 2006 16:47:27 +0100 |
User-agent: |
Thunderbird 1.5.0.2 (X11/20060501) |
Hi,
I've been running into problems reliably controlling linphonec
programatically
via a fifo socket. It would sometimes fail to write to ALSA's output device.
I tracked this down to
mediastreamer/alsacard.c/ __alsa_card_write()
This method writes data to the output device using snd_pcm_writei().
The problem I'm experiencing is that it doesn't seem to handle any
errors more
serious than EPIPE. If a more serious error like EBADFD (which I've seen
ocassionally)
occurs then linphone will not recover and the output device will be
useless for the
duration of the rest of the phone call.
Instead would it be reasonable for linphone to close the output device
and then re-open
it on serious error. This is what I've changed the code to do for myself
and it makes linphone
much more reliable in my situation.
There is also a similar problem in
alsa_card_can_read(AlsaCard)
where the input device can be in state
SND_PCM_STATE_XRUN
meaning that an overrun has occured. Linphone does not catch this and
hence does not
recover from this state. ALSA will not I believe recover from this state
itself, it expects the
application using it to do so via a snd_pcm_drain() call.
Both these problems are fixed in the attached patches. They're not
particularly elegant as they're
just a fix for the problem here. I'm hoping that if this really is a
problem they'll help you fix it.
linphone_alsa_write_recovery is against a recent 1.3.4 source.
alsaxrun.patch was not written by myself
but by a colleague and is against an old 0.12 branch, although the code
has scarely changed in 1.3.4.
thanks for Linphone,
Greg
diff -ur linphone-0.12.2/mediastreamer/alsacard.c
lincor-linphone-0.12.2/mediastreamer/alsacard.c
--- linphone-0.12.2/mediastreamer/alsacard.c 2003-12-03 14:10:51.000000000
+0000
+++ lincor-linphone-0.12.2/mediastreamer/alsacard.c 2006-03-15
14:04:46.000000000 +0000
@@ -250,12 +250,55 @@
gboolean alsa_card_can_read(AlsaCard *obj)
{
- int frames;
+ long frames;
+ int state, retval;
+ gboolean rescue_error = FALSE;
+
g_return_val_if_fail(obj->read_handle!=NULL,0);
- if (obj->readpos!=0) return TRUE;
- if ( frames=snd_pcm_avail_update(obj->read_handle)>=obj->frames) return
1;
- //g_message("frames=%i",frames);
- return 0;
+
+ if (obj->readpos!=0) {
+ return TRUE;
+ }
+
+ frames = snd_pcm_avail_update(obj->read_handle);
+
+ if ( frames >= obj->frames) {
+ return 1;
+ }
+
+ state = snd_pcm_state(obj->read_handle);
+// printf("state: %d\n", state);
+
+ if (state == SND_PCM_STATE_XRUN) {
+ g_warning("XRUN detected attempting drain");
+
+ if ((retval = snd_pcm_drain(obj->read_handle)) == 0) {
+ if ((retval = snd_pcm_prepare(obj->read_handle)) == 0) {
+ if ((retval = snd_pcm_start(obj->read_handle))
== 0) {
+ g_warning("Drain, prepare and start
successfully executed");
+ }
+ else {
+ rescue_error = TRUE;
+ }
+ }
+ else {
+ rescue_error = TRUE;
+ }
+ }
+ else {
+ rescue_error = TRUE;
+ }
+
+ if (rescue_error) {
+ g_warning("Recovery failed, attempting to close and
reopen obj");
+ alsa_card_close_r(obj);
+ alsa_card_open_r(obj, 16, 0, 8000);
+
+ return 1;
+ }
+ }
+
+ return 0;
}
diff -uNr linphone-0.12.2.orig/console/linphonec.c
linphone-0.12.2.work/console/linphonec.c
--- linphone-0.12.2.orig/console/linphonec.c 2006-05-15 15:03:18.000000000
+0100
+++ linphone-0.12.2.work/console/linphonec.c 2006-05-15 15:46:27.000000000
+0100
@@ -471,6 +471,8 @@
retval=EXIT_FAILURE;
}
+ setlinebuf(stdout);
+
printf("Starting main loop...\n");
linphonec_main_loop (&linphonec);
linphone_core_uninit (&linphonec);
diff -uNr linphone-0.12.2.orig/mediastreamer/alsacard.c
linphone-0.12.2.work/mediastreamer/alsacard.c
--- linphone-0.12.2.orig/mediastreamer/alsacard.c 2006-05-15
15:03:18.000000000 +0100
+++ linphone-0.12.2.work/mediastreamer/alsacard.c 2006-05-15
16:01:04.000000000 +0100
@@ -349,10 +349,60 @@
int __alsa_card_write(AlsaCard *obj,char *buf,int size)
{
int err;
+ gboolean rescue_error = FALSE;
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGALRM);
sigprocmask(SIG_BLOCK,&set,NULL);
+
+ if ((err=snd_pcm_writei(obj->write_handle,buf,size/obj->frame_size))<0){
+ g_warning("alsa_card_write: snd_pcm_writei()
failed:%s.",snd_strerror(err));
+ if (err < 0 && err != -EAGAIN){
+ if (err == -EPIPE) { /* under-run */
+ err = snd_pcm_prepare(obj->write_handle);
+ if (err < 0)
+ {
+ rescue_error = TRUE;
+ g_error("Can't recover from EPIPE,
prepare failed: %s\n", snd_strerror(err));
+ }
+ } else if (err == -ESTRPIPE) {
+ int max_recovery_attempts = 0;
+ while ((err =
snd_pcm_resume(obj->write_handle)) == -EAGAIN && (max_recovery_attempts++ <
10)){
+ usleep(250000); /* wait until
the suspend flag is released */
+ g_error("Attempting to recover from
ESTRPIPE, try %d", max_recovery_attempts);
+ }
+ if (err < 0) {
+ err =
snd_pcm_prepare(obj->write_handle);
+ if (err < 0)
+ {
+ g_error("Unable to recover
from suspend, prepare failed: %s\n", snd_strerror(err));
+ rescue_error = TRUE;
+ }
+ }
+ }
+ else { // Unknown error. Don't know what to do with
it so reopen device as fall-back plan
+ g_error("PCM write device failure %d.
Re-initialising device", err);
+ rescue_error = TRUE;
+ }
+ /* If the output devices buffer is full we don't want to save
this data as we'll just build up
+ a backlog of real time data. So dump it
+ */
+ } else if ( err == -EAGAIN ){
+ g_warning("Write device buffer full, EAGAIN message.
Discarding data. This is mostly harmless.");
+ }
+
+ if (rescue_error) {
+ g_warning("Recovery of write device failed, attempting
to close and reopen obj");
+ alsa_card_close_w(obj);
+ alsa_card_open_w(obj, 16, 0, 8000);
+
+
err=snd_pcm_writei(obj->write_handle,buf,size/obj->frame_size);
+ if (err<0)
+ g_warning("alsa_card_write: Error writing
sound buffer (size=%i):%s",size,snd_strerror(err));
+ }
+ }
+
+/*
if ((err=snd_pcm_writei(obj->write_handle,buf,size/obj->frame_size))<0){
if (err!=-EPIPE){
g_warning("alsa_card_write: snd_pcm_writei()
failed:%s.",snd_strerror(err));
@@ -362,6 +412,7 @@
if (err<0) g_warning("alsa_card_write: Error writing sound
buffer (size=%i):%s",size,snd_strerror(err));
}
+*/
sigprocmask(SIG_UNBLOCK,&set,NULL);
return err;
}
- [Linphone-developers] Bug in ALSA write error handling?,
Greg Farrell <=