Make meetme operate in linear so as to keep alaw folk happy, minor iax2
[asterisk/asterisk.git] / file.c
diff --git a/file.c b/file.c
index 1ee68e5..1e896e8 100755 (executable)
--- a/file.c
+++ b/file.c
@@ -19,6 +19,7 @@
 #include <asterisk/sched.h>
 #include <asterisk/options.h>
 #include <asterisk/translate.h>
+#include <asterisk/utils.h>
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -65,6 +66,11 @@ struct ast_format {
 struct ast_filestream {
        /* Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
        struct ast_format *fmt;
+       int flags;
+       mode_t mode;
+       char *filename;
+       /* Video file stream */
+       struct ast_filestream *vfs;
        /* Transparently translate from another format -- just once */
        struct ast_trans_pvt *trans;
        struct ast_tranlator_pvt *tr;
@@ -73,7 +79,7 @@ struct ast_filestream {
        struct ast_channel *owner;
 };
 
-static pthread_mutex_t formatlock = AST_MUTEX_INITIALIZER;
+AST_MUTEX_DEFINE_STATIC(formatlock);
 
 static struct ast_format *formats = NULL;
 
@@ -89,14 +95,14 @@ int ast_format_register(char *name, char *exts, int format,
                                                char * (*getcomment)(struct ast_filestream *))
 {
        struct ast_format *tmp;
-       if (ast_pthread_mutex_lock(&formatlock)) {
+       if (ast_mutex_lock(&formatlock)) {
                ast_log(LOG_WARNING, "Unable to lock format list\n");
                return -1;
        }
        tmp = formats;
        while(tmp) {
                if (!strcasecmp(name, tmp->name)) {
-                       ast_pthread_mutex_unlock(&formatlock);
+                       ast_mutex_unlock(&formatlock);
                        ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", name);
                        return -1;
                }
@@ -105,7 +111,7 @@ int ast_format_register(char *name, char *exts, int format,
        tmp = malloc(sizeof(struct ast_format));
        if (!tmp) {
                ast_log(LOG_WARNING, "Out of memory\n");
-               ast_pthread_mutex_unlock(&formatlock);
+               ast_mutex_unlock(&formatlock);
                return -1;
        }
        strncpy(tmp->name, name, sizeof(tmp->name)-1);
@@ -122,7 +128,7 @@ int ast_format_register(char *name, char *exts, int format,
        tmp->getcomment = getcomment;
        tmp->next = formats;
        formats = tmp;
-       ast_pthread_mutex_unlock(&formatlock);
+       ast_mutex_unlock(&formatlock);
        if (option_verbose > 1)
                ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", name, exts);
        return 0;
@@ -131,7 +137,7 @@ int ast_format_register(char *name, char *exts, int format,
 int ast_format_unregister(char *name)
 {
        struct ast_format *tmp, *tmpl = NULL;
-       if (ast_pthread_mutex_lock(&formatlock)) {
+       if (ast_mutex_lock(&formatlock)) {
                ast_log(LOG_WARNING, "Unable to lock format list\n");
                return -1;
        }
@@ -143,11 +149,12 @@ int ast_format_unregister(char *name)
                        else
                                formats = tmp->next;
                        free(tmp);
-                       ast_pthread_mutex_unlock(&formatlock);
+                       ast_mutex_unlock(&formatlock);
                        if (option_verbose > 1)
                                ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name);
                        return 0;
                }
+               tmpl = tmp;
                tmp = tmp->next;
        }
        ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
@@ -157,11 +164,13 @@ int ast_format_unregister(char *name)
 int ast_stopstream(struct ast_channel *tmp)
 {
        /* Stop a running stream if there is one */
-       if (!tmp->stream) 
-               return 0;
-       ast_closestream(tmp->stream);
-       if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
-               ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
+       if (tmp->vstream)
+               ast_closestream(tmp->vstream);
+       if (tmp->stream) {
+               ast_closestream(tmp->stream);
+               if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
+                       ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
+       }
        return 0;
 }
 
@@ -169,11 +178,29 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
 {
        struct ast_frame *trf;
        int res = -1;
-       if (f->frametype != AST_FRAME_VOICE) {
+       int alt=0;
+       if (f->frametype == AST_FRAME_VIDEO) {
+               if (fs->fmt->format < AST_FORMAT_MAX_AUDIO) {
+                       /* This is the audio portion.  Call the video one... */
+                       if (!fs->vfs && fs->filename) {
+                               /* XXX Support other video formats XXX */
+                               char *type = "h263";
+                               fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
+                               ast_log(LOG_DEBUG, "Opened video output file\n");
+                       }
+                       if (fs->vfs)
+                               return ast_writestream(fs->vfs, f);
+                       /* Ignore */
+                       return 0;                               
+               } else {
+                       /* Might / might not have mark set */
+                       alt = 1;
+               }
+       } else if (f->frametype != AST_FRAME_VOICE) {
                ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
                return -1;
        }
-       if ((fs->fmt->format & f->subclass) == f->subclass) {
+       if (((fs->fmt->format | alt) & f->subclass) == f->subclass) {
                res =  fs->fmt->write(fs, f);
                if (res < 0) 
                        ast_log(LOG_WARNING, "Natural write failed\n");
@@ -190,7 +217,7 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
                if (!fs->trans) 
                        fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass);
                if (!fs->trans)
-                       ast_log(LOG_WARNING, "Unable to translate to format %s, source format %d\n", fs->fmt->name, f->subclass);
+                       ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n", fs->fmt->name, ast_getformatname(f->subclass));
                else {
                        fs->lastwriteformat = f->subclass;
                        res = 0;
@@ -249,7 +276,7 @@ static int copy(char *infile, char *outfile)
 static char *build_filename(char *filename, char *ext)
 {
        char *fn;
-       char tmp[AST_CONFIG_MAX_PATH];
+       char tmp[AST_CONFIG_MAX_PATH]="";
        snprintf(tmp,sizeof(tmp)-1,"%s/%s",(char *)ast_config_AST_VAR_DIR,"sounds");
        fn = malloc(strlen(tmp) + strlen(filename) + strlen(ext) + 10);
        if (fn) {
@@ -262,6 +289,22 @@ static char *build_filename(char *filename, char *ext)
        
 }
 
+static int exts_compare(char *exts, char *type)
+{
+       char *stringp = NULL, *ext;
+       char tmp[256];
+
+       strncpy(tmp, exts, sizeof(tmp) - 1);
+       stringp = tmp;
+       while ((ext = strsep(&stringp, "|"))) {
+               if (!strcmp(ext, type)) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
 #define ACTION_EXISTS 1
 #define ACTION_DELETE 2
 #define ACTION_RENAME 3
@@ -285,7 +328,7 @@ static int ast_filehelper(char *filename, char *filename2, char *fmt, int action
        if (action == ACTION_OPEN)
                ret = -1;
        /* Check for a specific format */
-       if (ast_pthread_mutex_lock(&formatlock)) {
+       if (ast_mutex_lock(&formatlock)) {
                ast_log(LOG_WARNING, "Unable to lock format list\n");
                if (action == ACTION_EXISTS)
                        return 0;
@@ -294,7 +337,7 @@ static int ast_filehelper(char *filename, char *filename2, char *fmt, int action
        }
        f = formats;
        while(f) {
-               if (!fmt || !strcasecmp(f->name, fmt)) {
+               if (!fmt || exts_compare(f->exts, fmt)) {
                        char *stringp=NULL;
                        exts = strdup(f->exts);
                        /* Try each kind of extension */
@@ -335,7 +378,8 @@ static int ast_filehelper(char *filename, char *filename2, char *fmt, int action
                                                                ast_log(LOG_WARNING, "Out of memory\n");
                                                        break;
                                                case ACTION_OPEN:
-                                                       if ((ret < 0) && ((chan->writeformat & f->format))) {
+                                                       if ((ret < 0) && ((chan->writeformat & f->format) ||
+                                                                               ((f->format >= AST_FORMAT_MAX_AUDIO) && fmt))) {
                                                                ret = open(fn, O_RDONLY);
                                                                if (ret >= 0) {
                                                                        s = f->open(ret);
@@ -343,10 +387,14 @@ static int ast_filehelper(char *filename, char *filename2, char *fmt, int action
                                                                                s->lasttimeout = -1;
                                                                                s->fmt = f;
                                                                                s->trans = NULL;
-                                                                               chan->stream = s;
+                                                                               s->filename = NULL;
+                                                                               if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
+                                                                                       chan->stream = s;
+                                                                               else
+                                                                                       chan->vstream = s;
                                                                        } else {
                                                                                close(ret);
-                                                                               ast_log(LOG_WARNING, "Unable to open fd on %s\n", filename);
+                                                                               ast_log(LOG_WARNING, "Unable to open fd on %s\n", fn);
                                                                        }
                                                                } else
                                                                        ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
@@ -367,7 +415,7 @@ static int ast_filehelper(char *filename, char *filename2, char *fmt, int action
                }
                f = f->next;
        }
-       ast_pthread_mutex_unlock(&formatlock);
+       ast_mutex_unlock(&formatlock);
        if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
                res = ret ? ret : -1;
        return res;
@@ -389,21 +437,24 @@ struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename,
        */
        int fd = -1;
        int fmts = -1;
-       char filename2[256];
-       char lang2[MAX_LANGUAGE];
+       char filename2[256]="";
+       char filename3[256]="";
+       char *endpart;
        int res;
        ast_stopstream(chan);
        /* do this first, otherwise we detect the wrong writeformat */
        if (chan->generator)
                ast_deactivate_generator(chan);
-       if (preflang && strlen(preflang)) {
-               snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
+       if (preflang && !ast_strlen_zero(preflang)) {
+               strncpy(filename3, filename, sizeof(filename3) - 1);
+               endpart = strrchr(filename3, '/');
+               if (endpart) {
+                       *endpart = '\0';
+                       endpart++;
+                       snprintf(filename2, sizeof(filename2), "%s/%s/%s", filename3, preflang, endpart);
+               } else
+                       snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
                fmts = ast_fileexists(filename2, NULL, NULL);
-               if (fmts < 1) {
-                       strncpy(lang2, preflang, sizeof(lang2)-1);
-                       snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
-                       fmts = ast_fileexists(filename2, NULL, NULL);
-               }
        }
        if (fmts < 1) {
                strncpy(filename2, filename, sizeof(filename2)-1);
@@ -423,6 +474,58 @@ struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename,
        return NULL;
 }
 
+struct ast_filestream *ast_openvstream(struct ast_channel *chan, char *filename, char *preflang)
+{
+       /* This is a fairly complex routine.  Essentially we should do 
+          the following:
+          
+          1) Find which file handlers produce our type of format.
+          2) Look for a filename which it can handle.
+          3) If we find one, then great.  
+          4) If not, see what files are there
+          5) See what we can actually support
+          6) Choose the one with the least costly translator path and
+              set it up.
+                  
+       */
+       int fd = -1;
+       int fmts = -1;
+       char filename2[256];
+       char lang2[MAX_LANGUAGE];
+       /* XXX H.263 only XXX */
+       char *fmt = "h263";
+       if (preflang && !ast_strlen_zero(preflang)) {
+               snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
+               fmts = ast_fileexists(filename2, fmt, NULL);
+               if (fmts < 1) {
+                       strncpy(lang2, preflang, sizeof(lang2)-1);
+                       snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
+                       fmts = ast_fileexists(filename2, fmt, NULL);
+               }
+       }
+       if (fmts < 1) {
+               strncpy(filename2, filename, sizeof(filename2)-1);
+               fmts = ast_fileexists(filename2, fmt, NULL);
+       }
+       if (fmts < 1) {
+               return NULL;
+       }
+       fd = ast_filehelper(filename2, (char *)chan, fmt, ACTION_OPEN);
+       if(fd >= 0)
+               return chan->vstream;
+       ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
+       return NULL;
+}
+
+struct ast_frame *ast_readframe(struct ast_filestream *s)
+{
+       struct ast_frame *f = NULL;
+       int whennext = 0;       
+       if (s && s->fmt)
+               f = s->fmt->read(s, &whennext);
+       return f;
+}
+
 static int ast_readaudio_callback(void *data)
 {
        struct ast_filestream *s = data;
@@ -435,16 +538,55 @@ static int ast_readaudio_callback(void *data)
                        if (ast_write(s->owner, fr)) {
                                ast_log(LOG_WARNING, "Failed to write frame\n");
                                s->owner->streamid = -1;
+#ifdef ZAPTEL_OPTIMIZATIONS
+                               ast_settimeout(s->owner, 0, NULL, NULL);
+#endif                 
                                return 0;
                        }
                } else {
                        /* Stream has finished */
                        s->owner->streamid = -1;
+#ifdef ZAPTEL_OPTIMIZATIONS
+                       ast_settimeout(s->owner, 0, NULL, NULL);
+#endif                 
                        return 0;
                }
        }
        if (whennext != s->lasttimeout) {
-               s->owner->streamid = ast_sched_add(s->owner->sched, whennext/8, ast_readaudio_callback, s);
+#ifdef ZAPTEL_OPTIMIZATIONS
+               if (s->owner->timingfd > -1)
+                       ast_settimeout(s->owner, whennext, ast_readaudio_callback, s);
+               else
+#endif         
+                       s->owner->streamid = ast_sched_add(s->owner->sched, whennext/8, ast_readaudio_callback, s);
+               s->lasttimeout = whennext;
+               return 0;
+       }
+       return 1;
+}
+
+static int ast_readvideo_callback(void *data)
+{
+       struct ast_filestream *s = data;
+       struct ast_frame *fr;
+       int whennext = 0;
+
+       while(!whennext) {
+               fr = s->fmt->read(s, &whennext);
+               if (fr) {
+                       if (ast_write(s->owner, fr)) {
+                               ast_log(LOG_WARNING, "Failed to write frame\n");
+                               s->owner->vstreamid = -1;
+                               return 0;
+                       }
+               } else {
+                       /* Stream has finished */
+                       s->owner->vstreamid = -1;
+                       return 0;
+               }
+       }
+       if (whennext != s->lasttimeout) {
+               s->owner->vstreamid = ast_sched_add(s->owner->sched, whennext/8, ast_readvideo_callback, s);
                s->lasttimeout = whennext;
                return 0;
        }
@@ -459,7 +601,10 @@ int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
 
 int ast_playstream(struct ast_filestream *s)
 {
-       ast_readaudio_callback(s);
+       if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
+               ast_readaudio_callback(s);
+       else
+               ast_readvideo_callback(s);
        return 0;
 }
 
@@ -497,11 +642,29 @@ int ast_closestream(struct ast_filestream *f)
 {
        /* Stop a running stream if there is one */
        if (f->owner) {
-               f->owner->stream = NULL;
-               if (f->owner->streamid > -1)
-                       ast_sched_del(f->owner->sched, f->owner->streamid);
-               f->owner->streamid = -1;
+               if (f->fmt->format < AST_FORMAT_MAX_AUDIO) {
+                       f->owner->stream = NULL;
+                       if (f->owner->streamid > -1)
+                               ast_sched_del(f->owner->sched, f->owner->streamid);
+                       f->owner->streamid = -1;
+#ifdef ZAPTEL_OPTIMIZATIONS
+                       ast_settimeout(f->owner, 0, NULL, NULL);
+#endif                 
+               } else {
+                       f->owner->vstream = NULL;
+                       if (f->owner->vstreamid > -1)
+                               ast_sched_del(f->owner->sched, f->owner->vstreamid);
+                       f->owner->vstreamid = -1;
+               }
+       }
+       /* destroy the translator on exit */
+       if (f->trans) {
+               ast_translator_free_path(f->trans);
+               f->trans = NULL;
        }
+       if (f->filename)
+               free(f->filename);
+       f->filename = NULL;
        f->fmt->close(f);
        return 0;
 }
@@ -516,7 +679,7 @@ int ast_fileexists(char *filename, char *fmt, char *preflang)
        char *c;
        char lang2[MAX_LANGUAGE];
        int res = -1;
-       if (preflang && strlen(preflang)) {
+       if (preflang && !ast_strlen_zero(preflang)) {
                /* Insert the language between the last two parts of the path */
                strncpy(tmp, filename, sizeof(tmp) - 1);
                c = strrchr(tmp, '/');
@@ -565,53 +728,116 @@ int ast_filecopy(char *filename, char *filename2, char *fmt)
 int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
 {
        struct ast_filestream *fs;
+       struct ast_filestream *vfs;
 
        fs = ast_openstream(chan, filename, preflang);
+       vfs = ast_openvstream(chan, filename, preflang);
+       if (vfs)
+               ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
        if(fs){
                if(ast_applystream(chan, fs))
                        return -1;
+               if(vfs && ast_applystream(chan, vfs))
+                       return -1;
                if(ast_playstream(fs))
                        return -1;
+               if(vfs && ast_playstream(vfs))
+                       return -1;
 #if 1
                if (option_verbose > 2)
-                       ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename);
+                       ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (language '%s')\n", filename, preflang ? preflang : "default");
 #endif
                return 0;
        }
-       ast_log(LOG_WARNING, "Unable to open %s (format %d): %s\n", filename, chan->nativeformats, strerror(errno));
+       ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname(chan->nativeformats), strerror(errno));
        return -1;
 }
 
+struct ast_filestream *ast_readfile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
+{
+       int fd,myflags = 0;
+       struct ast_format *f;
+       struct ast_filestream *fs=NULL;
+       char *fn;
+       char *ext;
+       if (ast_mutex_lock(&formatlock)) {
+               ast_log(LOG_WARNING, "Unable to lock format list\n");
+               return NULL;
+       }
+       f = formats;
+       while(f) {
+               if (exts_compare(f->exts, type)) {
+                       char *stringp=NULL;
+                       /* XXX Implement check XXX */
+                       ext = strdup(f->exts);
+                       stringp=ext;
+                       ext = strsep(&stringp, "|");
+                       fn = build_filename(filename, ext);
+                       fd = open(fn, flags | myflags);
+                       if (fd >= 0) {
+                               errno = 0;
+                               if ((fs = f->open(fd))) {
+                                       fs->trans = NULL;
+                                       fs->fmt = f;
+                                       fs->flags = flags;
+                                       fs->mode = mode;
+                                       fs->filename = strdup(filename);
+                                       fs->vfs = NULL;
+                               } else {
+                                       ast_log(LOG_WARNING, "Unable to open %s\n", fn);
+                                       close(fd);
+                                       unlink(fn);
+                               }
+                       } else if (errno != EEXIST)
+                               ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
+                       free(fn);
+                       free(ext);
+                       break;
+               }
+               f = f->next;
+       }
+       ast_mutex_unlock(&formatlock);
+       if (!f) 
+               ast_log(LOG_WARNING, "No such format '%s'\n", type);
+       return fs;
+}
 
 struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
 {
-       int fd,myflags;
+       int fd,myflags = 0;
        struct ast_format *f;
        struct ast_filestream *fs=NULL;
        char *fn;
        char *ext;
-       if (ast_pthread_mutex_lock(&formatlock)) {
+       if (ast_mutex_lock(&formatlock)) {
                ast_log(LOG_WARNING, "Unable to lock format list\n");
                return NULL;
        }
-       myflags = 0;
        /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
-       //if (!(flags & O_APPEND)) myflags = O_TRUNC;
+       if (!(flags & O_APPEND)) 
+               myflags = O_TRUNC;
+       
+       myflags |= O_WRONLY | O_CREAT;
+
        f = formats;
        while(f) {
-               if (!strcasecmp(f->name, type)) {
+               if (exts_compare(f->exts, type)) {
                        char *stringp=NULL;
                        /* XXX Implement check XXX */
                        ext = strdup(f->exts);
                        stringp=ext;
                        ext = strsep(&stringp, "|");
                        fn = build_filename(filename, ext);
-                       fd = open(fn, flags | myflags | O_WRONLY | O_CREAT, mode);
+                       fd = open(fn, flags | myflags, mode);
                        if (fd >= 0) {
                                errno = 0;
                                if ((fs = f->rewrite(fd, comment))) {
                                        fs->trans = NULL;
                                        fs->fmt = f;
+                                       fs->flags = flags;
+                                       fs->mode = mode;
+                                       fs->filename = strdup(filename);
+                                       fs->vfs = NULL;
                                } else {
                                        ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
                                        close(fd);
@@ -625,7 +851,7 @@ struct ast_filestream *ast_writefile(char *filename, char *type, char *comment,
                }
                f = f->next;
        }
-       ast_pthread_mutex_unlock(&formatlock);
+       ast_mutex_unlock(&formatlock);
        if (!f) 
                ast_log(LOG_WARNING, "No such format '%s'\n", type);
        return fs;
@@ -638,12 +864,12 @@ char ast_waitstream(struct ast_channel *c, char *breakon)
        struct ast_frame *fr;
        while(c->stream) {
                res = ast_sched_wait(c->sched);
-               if (res < 0) {
-                       ast_closestream(c->stream);
+               if ((res < 0) && !c->timingfunc) {
+                       ast_stopstream(c);
                        break;
                }
-               /* Setup timeout if supported */
-               ast_settimeout(c, res);
+               if (res < 0)
+                       res = 1000;
                res = ast_waitfor(c, res);
                if (res < 0) {
                        ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
@@ -682,8 +908,6 @@ char ast_waitstream(struct ast_channel *c, char *breakon)
                        ast_frfree(fr);
                }
                ast_sched_runq(c->sched);
-       
-               
        }
        return (c->_softhangup ? -1 : 0);
 }
@@ -694,11 +918,12 @@ char ast_waitstream_fr(struct ast_channel *c, char *breakon, char *forward, char
        struct ast_frame *fr;
        while(c->stream) {
                res = ast_sched_wait(c->sched);
-               if (res < 0) {
-                       ast_closestream(c->stream);
+               if ((res < 0) && !c->timingfunc) {
+                       ast_stopstream(c);
                        break;
                }
-               ast_settimeout(c, res);
+               if (res < 0)
+                       res = 1000;
                res = ast_waitfor(c, res);
                if (res < 0) {
                        ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
@@ -758,11 +983,12 @@ char ast_waitstream_full(struct ast_channel *c, char *breakon, int audiofd, int
        
        while(c->stream) {
                ms = ast_sched_wait(c->sched);
-               if (ms < 0) {
-                       ast_closestream(c->stream);
+               if ((ms < 0) && !c->timingfunc) {
+                       ast_stopstream(c);
                        break;
                }
-               ast_settimeout(c, ms);
+               if (ms < 0)
+                       ms = 1000;
                rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
                if (!rchan && (outfd < 0) && (ms)) {
                        ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));