linphone-developers
[Top][All Lists]
Advanced

[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;
 }

reply via email to

[Prev in Thread] Current Thread [Next in Thread]