Totally redo file formats
[asterisk/asterisk.git] / file.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Generic File Format Support.
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <sys/types.h>
15 #include <asterisk/frame.h>
16 #include <asterisk/file.h>
17 #include <asterisk/logger.h>
18 #include <asterisk/channel.h>
19 #include <asterisk/sched.h>
20 #include <asterisk/options.h>
21 #include <asterisk/translate.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <pthread.h>
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <dirent.h>
30 #include <sys/stat.h>
31 #include "asterisk.h"
32 #include "astconf.h"
33
34 struct ast_format {
35         /* Name of format */
36         char name[80];
37         /* Extensions (separated by | if more than one) 
38            this format can read.  First is assumed for writing (e.g. .mp3) */
39         char exts[80];
40         /* Format of frames it uses/provides (one only) */
41         int format;
42         /* Open an input stream, and start playback */
43         struct ast_filestream * (*open)(int fd);
44         /* Open an output stream, of a given file descriptor and comment it appropriately if applicable */
45         struct ast_filestream * (*rewrite)(int fd, char *comment);
46         /* Write a frame to a channel */
47         int (*write)(struct ast_filestream *, struct ast_frame *);
48         /* seek num samples into file, whence(think normal seek) */
49         int (*seek)(struct ast_filestream *, long offset, int whence);
50         /* trunc file to current position */
51         int (*trunc)(struct ast_filestream *fs);
52         /* tell current position */
53         long (*tell)(struct ast_filestream *fs);
54         /* Read the next frame from the filestream (if available) and report when to get next one
55                 (in samples) */
56         struct ast_frame * (*read)(struct ast_filestream *, int *whennext);
57         /* Close file, and destroy filestream structure */
58         void (*close)(struct ast_filestream *);
59         /* Retrieve file comment */
60         char * (*getcomment)(struct ast_filestream *);
61         /* Link */
62         struct ast_format *next;
63 };
64
65 struct ast_filestream {
66         /* Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
67         struct ast_format *fmt;
68         /* Transparently translate from another format -- just once */
69         struct ast_trans_pvt *trans;
70         struct ast_tranlator_pvt *tr;
71         int lastwriteformat;
72         int lasttimeout;
73         struct ast_channel *owner;
74 };
75
76 static pthread_mutex_t formatlock = AST_MUTEX_INITIALIZER;
77
78 static struct ast_format *formats = NULL;
79
80 int ast_format_register(char *name, char *exts, int format,
81                                                 struct ast_filestream * (*open)(int fd),
82                                                 struct ast_filestream * (*rewrite)(int fd, char *comment),
83                                                 int (*write)(struct ast_filestream *, struct ast_frame *),
84                                                 int (*seek)(struct ast_filestream *, long sample_offset, int whence),
85                                                 int (*trunc)(struct ast_filestream *),
86                                                 long (*tell)(struct ast_filestream *),
87                                                 struct ast_frame * (*read)(struct ast_filestream *, int *whennext),
88                                                 void (*close)(struct ast_filestream *),
89                                                 char * (*getcomment)(struct ast_filestream *))
90 {
91         struct ast_format *tmp;
92         if (ast_pthread_mutex_lock(&formatlock)) {
93                 ast_log(LOG_WARNING, "Unable to lock format list\n");
94                 return -1;
95         }
96         tmp = formats;
97         while(tmp) {
98                 if (!strcasecmp(name, tmp->name)) {
99                         ast_pthread_mutex_unlock(&formatlock);
100                         ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", name);
101                         return -1;
102                 }
103                 tmp = tmp->next;
104         }
105         tmp = malloc(sizeof(struct ast_format));
106         if (!tmp) {
107                 ast_log(LOG_WARNING, "Out of memory\n");
108                 ast_pthread_mutex_unlock(&formatlock);
109                 return -1;
110         }
111         strncpy(tmp->name, name, sizeof(tmp->name)-1);
112         strncpy(tmp->exts, exts, sizeof(tmp->exts)-1);
113         tmp->open = open;
114         tmp->rewrite = rewrite;
115         tmp->read = read;
116         tmp->write = write;
117         tmp->seek = seek;
118         tmp->trunc = trunc;
119         tmp->tell = tell;
120         tmp->close = close;
121         tmp->format = format;
122         tmp->getcomment = getcomment;
123         tmp->next = formats;
124         formats = tmp;
125         ast_pthread_mutex_unlock(&formatlock);
126         if (option_verbose > 1)
127                 ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", name, exts);
128         return 0;
129 }
130
131 int ast_format_unregister(char *name)
132 {
133         struct ast_format *tmp, *tmpl = NULL;
134         if (ast_pthread_mutex_lock(&formatlock)) {
135                 ast_log(LOG_WARNING, "Unable to lock format list\n");
136                 return -1;
137         }
138         tmp = formats;
139         while(tmp) {
140                 if (!strcasecmp(name, tmp->name)) {
141                         if (tmpl) 
142                                 tmpl->next = tmp->next;
143                         else
144                                 formats = tmp->next;
145                         free(tmp);
146                         ast_pthread_mutex_unlock(&formatlock);
147                         if (option_verbose > 1)
148                                 ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name);
149                         return 0;
150                 }
151                 tmp = tmp->next;
152         }
153         ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
154         return -1;
155 }
156
157 int ast_stopstream(struct ast_channel *tmp)
158 {
159         /* Stop a running stream if there is one */
160         if (!tmp->stream) 
161                 return 0;
162         ast_closestream(tmp->stream);
163         if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
164                 ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
165         return 0;
166 }
167
168 int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
169 {
170         struct ast_frame *trf;
171         int res = -1;
172         if (f->frametype != AST_FRAME_VOICE) {
173                 ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
174                 return -1;
175         }
176         if ((fs->fmt->format & f->subclass) == f->subclass) {
177                 res =  fs->fmt->write(fs, f);
178                 if (res < 0) 
179                         ast_log(LOG_WARNING, "Natural write failed\n");
180                 if (res > 0)
181                         ast_log(LOG_WARNING, "Huh??\n");
182                 return res;
183         } else {
184                 /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
185                        the one we've setup a translator for, we do the "wrong thing" XXX */
186                 if (fs->trans && (f->subclass != fs->lastwriteformat)) {
187                         ast_translator_free_path(fs->trans);
188                         fs->trans = NULL;
189                 }
190                 if (!fs->trans) 
191                         fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass);
192                 if (!fs->trans)
193                         ast_log(LOG_WARNING, "Unable to translate to format %s, source format %d\n", fs->fmt->name, f->subclass);
194                 else {
195                         fs->lastwriteformat = f->subclass;
196                         res = 0;
197                         /* Get the translated frame but don't consume the original in case they're using it on another stream */
198                         trf = ast_translate(fs->trans, f, 0);
199                         if (trf) {
200                                 res = fs->fmt->write(fs, trf);
201                                 if (res) 
202                                         ast_log(LOG_WARNING, "Translated frame write failed\n");
203                         } else
204                                 res = 0;
205                 }
206                 return res;
207         }
208 }
209
210 static int copy(char *infile, char *outfile)
211 {
212         int ifd;
213         int ofd;
214         int res;
215         int len;
216         char buf[4096];
217         if ((ifd = open(infile, O_RDONLY)) < 0) {
218                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
219                 return -1;
220         }
221         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
222                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
223                 close(ifd);
224                 return -1;
225         }
226         do {
227                 len = read(ifd, buf, sizeof(buf));
228                 if (len < 0) {
229                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
230                         close(ifd);
231                         close(ofd);
232                         unlink(outfile);
233                 }
234                 if (len) {
235                         res = write(ofd, buf, len);
236                         if (res != len) {
237                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
238                                 close(ifd);
239                                 close(ofd);
240                                 unlink(outfile);
241                         }
242                 }
243         } while(len);
244         close(ifd);
245         close(ofd);
246         return 0;
247 }
248
249 static char *build_filename(char *filename, char *ext)
250 {
251         char *fn;
252         char tmp[AST_CONFIG_MAX_PATH];
253         snprintf(tmp,sizeof(tmp)-1,"%s/%s",(char *)ast_config_AST_VAR_DIR,"sounds");
254         fn = malloc(strlen(tmp) + strlen(filename) + strlen(ext) + 10);
255         if (fn) {
256                 if (filename[0] == '/') 
257                         sprintf(fn, "%s.%s", filename, ext);
258                 else
259                         sprintf(fn, "%s/%s.%s", (char *)tmp, filename, ext);
260         }
261         return fn;
262         
263 }
264
265 #define ACTION_EXISTS 1
266 #define ACTION_DELETE 2
267 #define ACTION_RENAME 3
268 #define ACTION_OPEN   4
269 #define ACTION_COPY   5
270
271 static int ast_filehelper(char *filename, char *filename2, char *fmt, int action)
272 {
273         struct stat st;
274         struct ast_format *f;
275         struct ast_filestream *s;
276         int res=0, ret = 0;
277         char *ext=NULL, *exts, *fn, *nfn;
278         struct ast_channel *chan = (struct ast_channel *)filename2;
279         
280         /* Start with negative response */
281         if (action == ACTION_EXISTS)
282                 res = 0;
283         else
284                 res = -1;
285         if (action == ACTION_OPEN)
286                 ret = -1;
287         /* Check for a specific format */
288         if (ast_pthread_mutex_lock(&formatlock)) {
289                 ast_log(LOG_WARNING, "Unable to lock format list\n");
290                 if (action == ACTION_EXISTS)
291                         return 0;
292                 else
293                         return -1;
294         }
295         f = formats;
296         while(f) {
297                 if (!fmt || !strcasecmp(f->name, fmt)) {
298                         char *stringp=NULL;
299                         exts = strdup(f->exts);
300                         /* Try each kind of extension */
301                         stringp=exts;
302                         ext = strsep(&stringp, "|");
303                         do {
304                                 fn = build_filename(filename, ext);
305                                 if (fn) {
306                                         res = stat(fn, &st);
307                                         if (!res) {
308                                                 switch(action) {
309                                                 case ACTION_EXISTS:
310                                                         ret |= f->format;
311                                                         break;
312                                                 case ACTION_DELETE:
313                                                         res = unlink(fn);
314                                                         if (res)
315                                                                 ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
316                                                         break;
317                                                 case ACTION_RENAME:
318                                                         nfn = build_filename(filename2, ext);
319                                                         if (nfn) {
320                                                                 res = rename(fn, nfn);
321                                                                 if (res)
322                                                                         ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
323                                                                 free(nfn);
324                                                         } else
325                                                                 ast_log(LOG_WARNING, "Out of memory\n");
326                                                         break;
327                                                 case ACTION_COPY:
328                                                         nfn = build_filename(filename2, ext);
329                                                         if (nfn) {
330                                                                 res = copy(fn, nfn);
331                                                                 if (res)
332                                                                         ast_log(LOG_WARNING, "copy(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
333                                                                 free(nfn);
334                                                         } else
335                                                                 ast_log(LOG_WARNING, "Out of memory\n");
336                                                         break;
337                                                 case ACTION_OPEN:
338                                                         if ((ret < 0) && ((chan->writeformat & f->format))) {
339                                                                 ret = open(fn, O_RDONLY);
340                                                                 if (ret >= 0) {
341                                                                         s = f->open(ret);
342                                                                         if (s) {
343                                                                                 s->lasttimeout = -1;
344                                                                                 s->fmt = f;
345                                                                                 s->trans = NULL;
346                                                                                 chan->stream = s;
347                                                                         } else {
348                                                                                 close(ret);
349                                                                                 ast_log(LOG_WARNING, "Unable to open fd on %s\n", filename);
350                                                                         }
351                                                                 } else
352                                                                         ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
353                                                         }
354                                                         break;
355                                                 default:
356                                                         ast_log(LOG_WARNING, "Unknown helper %d\n", action);
357                                                 }
358                                                 /* Conveniently this logic is the same for all */
359                                                 if (res)
360                                                         break;
361                                         }
362                                         free(fn);
363                                 }
364                                 ext = strsep(&stringp, "|");
365                         } while(ext);
366                         free(exts);
367                 }
368                 f = f->next;
369         }
370         ast_pthread_mutex_unlock(&formatlock);
371         if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
372                 res = ret ? ret : -1;
373         return res;
374 }
375
376 struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename, char *preflang)
377 {
378         /* This is a fairly complex routine.  Essentially we should do 
379            the following:
380            
381            1) Find which file handlers produce our type of format.
382            2) Look for a filename which it can handle.
383            3) If we find one, then great.  
384            4) If not, see what files are there
385            5) See what we can actually support
386            6) Choose the one with the least costly translator path and
387                set it up.
388                    
389         */
390         int fd = -1;
391         int fmts = -1;
392         char filename2[256];
393         char lang2[MAX_LANGUAGE];
394         int res;
395         ast_stopstream(chan);
396         /* do this first, otherwise we detect the wrong writeformat */
397         if (chan->generator)
398                 ast_deactivate_generator(chan);
399         if (preflang && strlen(preflang)) {
400                 snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
401                 fmts = ast_fileexists(filename2, NULL, NULL);
402                 if (fmts < 1) {
403                         strncpy(lang2, preflang, sizeof(lang2)-1);
404                         snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
405                         fmts = ast_fileexists(filename2, NULL, NULL);
406                 }
407         }
408         if (fmts < 1) {
409                 strncpy(filename2, filename, sizeof(filename2)-1);
410                 fmts = ast_fileexists(filename2, NULL, NULL);
411         }
412         if (fmts < 1) {
413                 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
414                 return NULL;
415         }
416         chan->oldwriteformat = chan->writeformat;
417         /* Set the channel to a format we can work with */
418         res = ast_set_write_format(chan, fmts);
419         
420         fd = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
421         if(fd >= 0)
422                 return chan->stream;
423         return NULL;
424 }
425
426 static int ast_readaudio_callback(void *data)
427 {
428         struct ast_filestream *s = data;
429         struct ast_frame *fr;
430         int whennext = 0;
431
432         while(!whennext) {
433                 fr = s->fmt->read(s, &whennext);
434                 if (fr) {
435                         if (ast_write(s->owner, fr)) {
436                                 ast_log(LOG_WARNING, "Failed to write frame\n");
437                                 s->owner->streamid = -1;
438                                 return 0;
439                         }
440                 } else {
441                         /* Stream has finished */
442                         s->owner->streamid = -1;
443                         return 0;
444                 }
445         }
446         if (whennext != s->lasttimeout) {
447                 s->owner->streamid = ast_sched_add(s->owner->sched, whennext/8, ast_readaudio_callback, s);
448                 s->lasttimeout = whennext;
449                 return 0;
450         }
451         return 1;
452 }
453
454 int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
455 {
456         s->owner = chan;
457         return 0;
458 }
459
460 int ast_playstream(struct ast_filestream *s)
461 {
462         ast_readaudio_callback(s);
463         return 0;
464 }
465
466 int ast_seekstream(struct ast_filestream *fs, long sample_offset, int whence)
467 {
468         return fs->fmt->seek(fs, sample_offset, whence);
469 }
470
471 int ast_truncstream(struct ast_filestream *fs)
472 {
473         return fs->fmt->trunc(fs);
474 }
475
476 long ast_tellstream(struct ast_filestream *fs)
477 {
478         return fs->fmt->tell(fs);
479 }
480
481 int ast_stream_fastforward(struct ast_filestream *fs, long ms)
482 {
483         /* I think this is right, 8000 samples per second, 1000 ms a second so 8
484          * samples per ms  */
485         long samples = ms * 8;
486         return ast_seekstream(fs, samples, SEEK_CUR);
487 }
488
489 int ast_stream_rewind(struct ast_filestream *fs, long ms)
490 {
491         long samples = ms * 8;
492         samples = samples * -1;
493         return ast_seekstream(fs, samples, SEEK_CUR);
494 }
495
496 int ast_closestream(struct ast_filestream *f)
497 {
498         /* Stop a running stream if there is one */
499         if (f->owner) {
500                 f->owner->stream = NULL;
501                 if (f->owner->streamid > -1)
502                         ast_sched_del(f->owner->sched, f->owner->streamid);
503                 f->owner->streamid = -1;
504         }
505         f->fmt->close(f);
506         return 0;
507 }
508
509
510 int ast_fileexists(char *filename, char *fmt, char *preflang)
511 {
512         char filename2[256];
513         char tmp[256];
514         char *postfix;
515         char *prefix;
516         char *c;
517         char lang2[MAX_LANGUAGE];
518         int res = -1;
519         if (preflang && strlen(preflang)) {
520                 /* Insert the language between the last two parts of the path */
521                 strncpy(tmp, filename, sizeof(tmp) - 1);
522                 c = strrchr(tmp, '/');
523                 if (c) {
524                         *c = '\0';
525                         postfix = c+1;
526                         prefix = tmp;
527                 } else {
528                         postfix = tmp;
529                         prefix="";
530                 }
531                 snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, preflang, postfix);
532                 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
533                 if (res < 1) {
534                         char *stringp=NULL;
535                         strncpy(lang2, preflang, sizeof(lang2)-1);
536                         stringp=lang2;
537                         strsep(&stringp, "_");
538                         if (strcmp(lang2, preflang)) {
539                                 snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, lang2, postfix);
540                                 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
541                         }
542                 }
543         }
544         if (res < 1) {
545                 res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
546         }
547         return res;
548 }
549
550 int ast_filedelete(char *filename, char *fmt)
551 {
552         return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
553 }
554
555 int ast_filerename(char *filename, char *filename2, char *fmt)
556 {
557         return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
558 }
559
560 int ast_filecopy(char *filename, char *filename2, char *fmt)
561 {
562         return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
563 }
564
565 int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
566 {
567         struct ast_filestream *fs;
568
569         fs = ast_openstream(chan, filename, preflang);
570         if(fs){
571                 if(ast_applystream(chan, fs))
572                         return -1;
573                 if(ast_playstream(fs))
574                         return -1;
575 #if 1
576                 if (option_verbose > 2)
577                         ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename);
578 #endif
579                 return 0;
580         }
581         ast_log(LOG_WARNING, "Unable to open %s (format %d): %s\n", filename, chan->nativeformats, strerror(errno));
582         return -1;
583 }
584
585
586 struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
587 {
588         int fd,myflags;
589         struct ast_format *f;
590         struct ast_filestream *fs=NULL;
591         char *fn;
592         char *ext;
593         if (ast_pthread_mutex_lock(&formatlock)) {
594                 ast_log(LOG_WARNING, "Unable to lock format list\n");
595                 return NULL;
596         }
597         myflags = 0;
598         /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
599         //if (!(flags & O_APPEND)) myflags = O_TRUNC;
600         f = formats;
601         while(f) {
602                 if (!strcasecmp(f->name, type)) {
603                         char *stringp=NULL;
604                         /* XXX Implement check XXX */
605                         ext = strdup(f->exts);
606                         stringp=ext;
607                         ext = strsep(&stringp, "|");
608                         fn = build_filename(filename, ext);
609                         fd = open(fn, flags | myflags | O_WRONLY | O_CREAT, mode);
610                         if (fd >= 0) {
611                                 errno = 0;
612                                 if ((fs = f->rewrite(fd, comment))) {
613                                         fs->trans = NULL;
614                                         fs->fmt = f;
615                                 } else {
616                                         ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
617                                         close(fd);
618                                         unlink(fn);
619                                 }
620                         } else if (errno != EEXIST)
621                                 ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
622                         free(fn);
623                         free(ext);
624                         break;
625                 }
626                 f = f->next;
627         }
628         ast_pthread_mutex_unlock(&formatlock);
629         if (!f) 
630                 ast_log(LOG_WARNING, "No such format '%s'\n", type);
631         return fs;
632 }
633
634 char ast_waitstream(struct ast_channel *c, char *breakon)
635 {
636         /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */
637         int res;
638         struct ast_frame *fr;
639         while(c->stream) {
640                 res = ast_sched_wait(c->sched);
641                 if (res < 0) {
642                         ast_closestream(c->stream);
643                         break;
644                 }
645                 /* Setup timeout if supported */
646                 ast_settimeout(c, res);
647                 res = ast_waitfor(c, res);
648                 if (res < 0) {
649                         ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
650                         return res;
651                 } else if (res > 0) {
652                         fr = ast_read(c);
653                         if (!fr) {
654 #if 0
655                                 ast_log(LOG_DEBUG, "Got hung up\n");
656 #endif
657                                 return -1;
658                         }
659                         
660                         switch(fr->frametype) {
661                         case AST_FRAME_DTMF:
662                                 res = fr->subclass;
663                                 if (strchr(breakon, res)) {
664                                         ast_frfree(fr);
665                                         return res;
666                                 }
667                                 break;
668                         case AST_FRAME_CONTROL:
669                                 switch(fr->subclass) {
670                                 case AST_CONTROL_HANGUP:
671                                         ast_frfree(fr);
672                                         return -1;
673                                 case AST_CONTROL_RINGING:
674                                 case AST_CONTROL_ANSWER:
675                                         /* Unimportant */
676                                         break;
677                                 default:
678                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
679                                 }
680                         }
681                         /* Ignore */
682                         ast_frfree(fr);
683                 }
684                 ast_sched_runq(c->sched);
685         
686                 
687         }
688         return (c->_softhangup ? -1 : 0);
689 }
690
691 char ast_waitstream_fr(struct ast_channel *c, char *breakon, char *forward, char *rewind, int ms)
692 {
693         int res;
694         struct ast_frame *fr;
695         while(c->stream) {
696                 res = ast_sched_wait(c->sched);
697                 if (res < 0) {
698                         ast_closestream(c->stream);
699                         break;
700                 }
701                 ast_settimeout(c, res);
702                 res = ast_waitfor(c, res);
703                 if (res < 0) {
704                         ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
705                         return res;
706                 } else
707                 if (res > 0) {
708                         fr = ast_read(c);
709                         if (!fr) {
710 #if 0
711                                 ast_log(LOG_DEBUG, "Got hung up\n");
712 #endif
713                                 return -1;
714                         }
715                         
716                         switch(fr->frametype) {
717                         case AST_FRAME_DTMF:
718                                 res = fr->subclass;
719                                 if (strchr(forward,res)) {
720                                         ast_stream_fastforward(c->stream, ms);
721                                 } else if (strchr(rewind,res)) {
722                                         ast_stream_rewind(c->stream, ms);
723                                 } else if (strchr(breakon, res)) {
724                                         ast_frfree(fr);
725                                         return res;
726                                 }                                       
727                                 break;
728                         case AST_FRAME_CONTROL:
729                                 switch(fr->subclass) {
730                                 case AST_CONTROL_HANGUP:
731                                         ast_frfree(fr);
732                                         return -1;
733                                 case AST_CONTROL_RINGING:
734                                 case AST_CONTROL_ANSWER:
735                                         /* Unimportant */
736                                         break;
737                                 default:
738                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
739                                 }
740                         }
741                         /* Ignore */
742                         ast_frfree(fr);
743                 } else
744                         ast_sched_runq(c->sched);
745         
746                 
747         }
748         return (c->_softhangup ? -1 : 0);
749 }
750
751 char ast_waitstream_full(struct ast_channel *c, char *breakon, int audiofd, int cmdfd)
752 {
753         int res;
754         int ms;
755         int outfd;
756         struct ast_frame *fr;
757         struct ast_channel *rchan;
758         
759         while(c->stream) {
760                 ms = ast_sched_wait(c->sched);
761                 if (ms < 0) {
762                         ast_closestream(c->stream);
763                         break;
764                 }
765                 ast_settimeout(c, ms);
766                 rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
767                 if (!rchan && (outfd < 0) && (ms)) {
768                         ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
769                         return -1;
770                 } else if (outfd > -1) {
771                         /* The FD we were watching has something waiting */
772                         return 1;
773                 } else if (rchan) {
774                         fr = ast_read(c);
775                         if (!fr) {
776 #if 0
777                                 ast_log(LOG_DEBUG, "Got hung up\n");
778 #endif
779                                 return -1;
780                         }
781                         
782                         switch(fr->frametype) {
783                         case AST_FRAME_DTMF:
784                                 res = fr->subclass;
785                                 if (strchr(breakon, res)) {
786                                         ast_frfree(fr);
787                                         return res;
788                                 }
789                                 break;
790                         case AST_FRAME_CONTROL:
791                                 switch(fr->subclass) {
792                                 case AST_CONTROL_HANGUP:
793                                         ast_frfree(fr);
794                                         return -1;
795                                 case AST_CONTROL_RINGING:
796                                 case AST_CONTROL_ANSWER:
797                                         /* Unimportant */
798                                         break;
799                                 default:
800                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
801                                 }
802                         case AST_FRAME_VOICE:
803                                 /* Write audio if appropriate */
804                                 if (audiofd > -1)
805                                         write(audiofd, fr->data, fr->datalen);
806                         }
807                         /* Ignore */
808                         ast_frfree(fr);
809                 }
810                 ast_sched_runq(c->sched);
811         
812                 
813         }
814         return (c->_softhangup ? -1 : 0);
815 }