2 * Asterisk -- A telephony toolkit for Linux.
4 * Generic File Format Support.
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
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>
37 /* Extensions (separated by | if more than one)
38 this format can read. First is assumed for writing (e.g. .mp3) */
40 /* Format of frames it uses/provides (one only) */
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
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 *);
62 struct ast_format *next;
65 struct ast_filestream {
66 /* Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
67 struct ast_format *fmt;
71 /* Video file stream */
72 struct ast_filestream *vfs;
73 /* Transparently translate from another format -- just once */
74 struct ast_trans_pvt *trans;
75 struct ast_tranlator_pvt *tr;
78 struct ast_channel *owner;
81 static ast_mutex_t formatlock = AST_MUTEX_INITIALIZER;
83 static struct ast_format *formats = NULL;
85 int ast_format_register(char *name, char *exts, int format,
86 struct ast_filestream * (*open)(int fd),
87 struct ast_filestream * (*rewrite)(int fd, char *comment),
88 int (*write)(struct ast_filestream *, struct ast_frame *),
89 int (*seek)(struct ast_filestream *, long sample_offset, int whence),
90 int (*trunc)(struct ast_filestream *),
91 long (*tell)(struct ast_filestream *),
92 struct ast_frame * (*read)(struct ast_filestream *, int *whennext),
93 void (*close)(struct ast_filestream *),
94 char * (*getcomment)(struct ast_filestream *))
96 struct ast_format *tmp;
97 if (ast_mutex_lock(&formatlock)) {
98 ast_log(LOG_WARNING, "Unable to lock format list\n");
103 if (!strcasecmp(name, tmp->name)) {
104 ast_mutex_unlock(&formatlock);
105 ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", name);
110 tmp = malloc(sizeof(struct ast_format));
112 ast_log(LOG_WARNING, "Out of memory\n");
113 ast_mutex_unlock(&formatlock);
116 strncpy(tmp->name, name, sizeof(tmp->name)-1);
117 strncpy(tmp->exts, exts, sizeof(tmp->exts)-1);
119 tmp->rewrite = rewrite;
126 tmp->format = format;
127 tmp->getcomment = getcomment;
130 ast_mutex_unlock(&formatlock);
131 if (option_verbose > 1)
132 ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", name, exts);
136 int ast_format_unregister(char *name)
138 struct ast_format *tmp, *tmpl = NULL;
139 if (ast_mutex_lock(&formatlock)) {
140 ast_log(LOG_WARNING, "Unable to lock format list\n");
145 if (!strcasecmp(name, tmp->name)) {
147 tmpl->next = tmp->next;
151 ast_mutex_unlock(&formatlock);
152 if (option_verbose > 1)
153 ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name);
158 ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
162 int ast_stopstream(struct ast_channel *tmp)
164 /* Stop a running stream if there is one */
166 ast_closestream(tmp->vstream);
168 ast_closestream(tmp->stream);
169 if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
170 ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
175 int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
177 struct ast_frame *trf;
180 if (f->frametype == AST_FRAME_VIDEO) {
181 if (fs->fmt->format < AST_FORMAT_MAX_AUDIO) {
182 /* This is the audio portion. Call the video one... */
183 if (!fs->vfs && fs->filename) {
184 /* XXX Support other video formats XXX */
186 fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
187 ast_log(LOG_DEBUG, "Opened video output file\n");
190 return ast_writestream(fs->vfs, f);
194 /* Might / might not have mark set */
197 } else if (f->frametype != AST_FRAME_VOICE) {
198 ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
201 if (((fs->fmt->format | alt) & f->subclass) == f->subclass) {
202 res = fs->fmt->write(fs, f);
204 ast_log(LOG_WARNING, "Natural write failed\n");
206 ast_log(LOG_WARNING, "Huh??\n");
209 /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
210 the one we've setup a translator for, we do the "wrong thing" XXX */
211 if (fs->trans && (f->subclass != fs->lastwriteformat)) {
212 ast_translator_free_path(fs->trans);
216 fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass);
218 ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n", fs->fmt->name, ast_getformatname(f->subclass));
220 fs->lastwriteformat = f->subclass;
222 /* Get the translated frame but don't consume the original in case they're using it on another stream */
223 trf = ast_translate(fs->trans, f, 0);
225 res = fs->fmt->write(fs, trf);
227 ast_log(LOG_WARNING, "Translated frame write failed\n");
235 static int copy(char *infile, char *outfile)
242 if ((ifd = open(infile, O_RDONLY)) < 0) {
243 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
246 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
247 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
252 len = read(ifd, buf, sizeof(buf));
254 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
260 res = write(ofd, buf, len);
262 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
274 static char *build_filename(char *filename, char *ext)
277 char tmp[AST_CONFIG_MAX_PATH]="";
278 snprintf(tmp,sizeof(tmp)-1,"%s/%s",(char *)ast_config_AST_VAR_DIR,"sounds");
279 fn = malloc(strlen(tmp) + strlen(filename) + strlen(ext) + 10);
281 if (filename[0] == '/')
282 sprintf(fn, "%s.%s", filename, ext);
284 sprintf(fn, "%s/%s.%s", (char *)tmp, filename, ext);
290 static int exts_compare(char *exts, char *type)
292 char *stringp = NULL, *ext;
295 strncpy(tmp, exts, sizeof(tmp) - 1);
297 while ((ext = strsep(&stringp, "|"))) {
298 if (!strcmp(ext, type)) {
306 #define ACTION_EXISTS 1
307 #define ACTION_DELETE 2
308 #define ACTION_RENAME 3
309 #define ACTION_OPEN 4
310 #define ACTION_COPY 5
312 static int ast_filehelper(char *filename, char *filename2, char *fmt, int action)
315 struct ast_format *f;
316 struct ast_filestream *s;
318 char *ext=NULL, *exts, *fn, *nfn;
319 struct ast_channel *chan = (struct ast_channel *)filename2;
321 /* Start with negative response */
322 if (action == ACTION_EXISTS)
326 if (action == ACTION_OPEN)
328 /* Check for a specific format */
329 if (ast_mutex_lock(&formatlock)) {
330 ast_log(LOG_WARNING, "Unable to lock format list\n");
331 if (action == ACTION_EXISTS)
338 if (!fmt || exts_compare(f->exts, fmt)) {
340 exts = strdup(f->exts);
341 /* Try each kind of extension */
343 ext = strsep(&stringp, "|");
345 fn = build_filename(filename, ext);
356 ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
359 nfn = build_filename(filename2, ext);
361 res = rename(fn, nfn);
363 ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
366 ast_log(LOG_WARNING, "Out of memory\n");
369 nfn = build_filename(filename2, ext);
373 ast_log(LOG_WARNING, "copy(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
376 ast_log(LOG_WARNING, "Out of memory\n");
379 if ((ret < 0) && ((chan->writeformat & f->format) ||
380 ((f->format >= AST_FORMAT_MAX_AUDIO) && fmt))) {
381 ret = open(fn, O_RDONLY);
389 if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
395 ast_log(LOG_WARNING, "Unable to open fd on %s\n", fn);
398 ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
402 ast_log(LOG_WARNING, "Unknown helper %d\n", action);
404 /* Conveniently this logic is the same for all */
410 ext = strsep(&stringp, "|");
416 ast_mutex_unlock(&formatlock);
417 if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
418 res = ret ? ret : -1;
422 struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename, char *preflang)
424 /* This is a fairly complex routine. Essentially we should do
427 1) Find which file handlers produce our type of format.
428 2) Look for a filename which it can handle.
429 3) If we find one, then great.
430 4) If not, see what files are there
431 5) See what we can actually support
432 6) Choose the one with the least costly translator path and
438 char filename2[256]="";
439 char filename3[256]="";
442 ast_stopstream(chan);
443 /* do this first, otherwise we detect the wrong writeformat */
445 ast_deactivate_generator(chan);
446 if (preflang && strlen(preflang)) {
447 strncpy(filename3, filename, sizeof(filename3) - 1);
448 endpart = strrchr(filename3, '/');
452 snprintf(filename2, sizeof(filename2), "%s/%s/%s", filename3, preflang, endpart);
454 snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
455 fmts = ast_fileexists(filename2, NULL, NULL);
458 strncpy(filename2, filename, sizeof(filename2)-1);
459 fmts = ast_fileexists(filename2, NULL, NULL);
462 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
465 chan->oldwriteformat = chan->writeformat;
466 /* Set the channel to a format we can work with */
467 res = ast_set_write_format(chan, fmts);
469 fd = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
475 struct ast_filestream *ast_openvstream(struct ast_channel *chan, char *filename, char *preflang)
477 /* This is a fairly complex routine. Essentially we should do
480 1) Find which file handlers produce our type of format.
481 2) Look for a filename which it can handle.
482 3) If we find one, then great.
483 4) If not, see what files are there
484 5) See what we can actually support
485 6) Choose the one with the least costly translator path and
492 char lang2[MAX_LANGUAGE];
493 /* XXX H.263 only XXX */
495 if (preflang && strlen(preflang)) {
496 snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
497 fmts = ast_fileexists(filename2, fmt, NULL);
499 strncpy(lang2, preflang, sizeof(lang2)-1);
500 snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
501 fmts = ast_fileexists(filename2, fmt, NULL);
505 strncpy(filename2, filename, sizeof(filename2)-1);
506 fmts = ast_fileexists(filename2, fmt, NULL);
511 fd = ast_filehelper(filename2, (char *)chan, fmt, ACTION_OPEN);
513 return chan->vstream;
514 ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
518 struct ast_frame *ast_readframe(struct ast_filestream *s)
520 struct ast_frame *f = NULL;
523 f = s->fmt->read(s, &whennext);
527 static int ast_readaudio_callback(void *data)
529 struct ast_filestream *s = data;
530 struct ast_frame *fr;
534 fr = s->fmt->read(s, &whennext);
536 if (ast_write(s->owner, fr)) {
537 ast_log(LOG_WARNING, "Failed to write frame\n");
538 s->owner->streamid = -1;
539 #ifdef ZAPTEL_OPTIMIZATIONS
540 ast_settimeout(s->owner, 0, NULL, NULL);
545 /* Stream has finished */
546 s->owner->streamid = -1;
547 #ifdef ZAPTEL_OPTIMIZATIONS
548 ast_settimeout(s->owner, 0, NULL, NULL);
553 if (whennext != s->lasttimeout) {
554 #ifdef ZAPTEL_OPTIMIZATIONS
555 if (s->owner->timingfd > -1)
556 ast_settimeout(s->owner, whennext, ast_readaudio_callback, s);
559 s->owner->streamid = ast_sched_add(s->owner->sched, whennext/8, ast_readaudio_callback, s);
560 s->lasttimeout = whennext;
566 static int ast_readvideo_callback(void *data)
568 struct ast_filestream *s = data;
569 struct ast_frame *fr;
573 fr = s->fmt->read(s, &whennext);
575 if (ast_write(s->owner, fr)) {
576 ast_log(LOG_WARNING, "Failed to write frame\n");
577 s->owner->vstreamid = -1;
581 /* Stream has finished */
582 s->owner->vstreamid = -1;
586 if (whennext != s->lasttimeout) {
587 s->owner->vstreamid = ast_sched_add(s->owner->sched, whennext/8, ast_readvideo_callback, s);
588 s->lasttimeout = whennext;
594 int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
600 int ast_playstream(struct ast_filestream *s)
602 if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
603 ast_readaudio_callback(s);
605 ast_readvideo_callback(s);
609 int ast_seekstream(struct ast_filestream *fs, long sample_offset, int whence)
611 return fs->fmt->seek(fs, sample_offset, whence);
614 int ast_truncstream(struct ast_filestream *fs)
616 return fs->fmt->trunc(fs);
619 long ast_tellstream(struct ast_filestream *fs)
621 return fs->fmt->tell(fs);
624 int ast_stream_fastforward(struct ast_filestream *fs, long ms)
626 /* I think this is right, 8000 samples per second, 1000 ms a second so 8
628 long samples = ms * 8;
629 return ast_seekstream(fs, samples, SEEK_CUR);
632 int ast_stream_rewind(struct ast_filestream *fs, long ms)
634 long samples = ms * 8;
635 samples = samples * -1;
636 return ast_seekstream(fs, samples, SEEK_CUR);
639 int ast_closestream(struct ast_filestream *f)
641 /* Stop a running stream if there is one */
643 if (f->fmt->format < AST_FORMAT_MAX_AUDIO) {
644 f->owner->stream = NULL;
645 if (f->owner->streamid > -1)
646 ast_sched_del(f->owner->sched, f->owner->streamid);
647 f->owner->streamid = -1;
648 #ifdef ZAPTEL_OPTIMIZATIONS
649 ast_settimeout(f->owner, 0, NULL, NULL);
652 f->owner->vstream = NULL;
653 if (f->owner->vstreamid > -1)
654 ast_sched_del(f->owner->sched, f->owner->vstreamid);
655 f->owner->vstreamid = -1;
658 /* destroy the translator on exit */
660 ast_translator_free_path(f->trans);
671 int ast_fileexists(char *filename, char *fmt, char *preflang)
678 char lang2[MAX_LANGUAGE];
680 if (preflang && strlen(preflang)) {
681 /* Insert the language between the last two parts of the path */
682 strncpy(tmp, filename, sizeof(tmp) - 1);
683 c = strrchr(tmp, '/');
692 snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, preflang, postfix);
693 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
696 strncpy(lang2, preflang, sizeof(lang2)-1);
698 strsep(&stringp, "_");
699 if (strcmp(lang2, preflang)) {
700 snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, lang2, postfix);
701 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
706 res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
711 int ast_filedelete(char *filename, char *fmt)
713 return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
716 int ast_filerename(char *filename, char *filename2, char *fmt)
718 return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
721 int ast_filecopy(char *filename, char *filename2, char *fmt)
723 return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
726 int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
728 struct ast_filestream *fs;
729 struct ast_filestream *vfs;
731 fs = ast_openstream(chan, filename, preflang);
732 vfs = ast_openvstream(chan, filename, preflang);
734 ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
736 if(ast_applystream(chan, fs))
738 if(vfs && ast_applystream(chan, vfs))
740 if(ast_playstream(fs))
742 if(vfs && ast_playstream(vfs))
745 if (option_verbose > 2)
746 ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (language '%s')\n", filename, preflang ? preflang : "default");
750 ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname(chan->nativeformats), strerror(errno));
754 struct ast_filestream *ast_readfile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
757 struct ast_format *f;
758 struct ast_filestream *fs=NULL;
761 if (ast_mutex_lock(&formatlock)) {
762 ast_log(LOG_WARNING, "Unable to lock format list\n");
767 if (exts_compare(f->exts, type)) {
769 /* XXX Implement check XXX */
770 ext = strdup(f->exts);
772 ext = strsep(&stringp, "|");
773 fn = build_filename(filename, ext);
774 fd = open(fn, flags | myflags);
777 if ((fs = f->open(fd))) {
782 fs->filename = strdup(filename);
785 ast_log(LOG_WARNING, "Unable to open %s\n", fn);
789 } else if (errno != EEXIST)
790 ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
797 ast_mutex_unlock(&formatlock);
799 ast_log(LOG_WARNING, "No such format '%s'\n", type);
803 struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
806 struct ast_format *f;
807 struct ast_filestream *fs=NULL;
810 if (ast_mutex_lock(&formatlock)) {
811 ast_log(LOG_WARNING, "Unable to lock format list\n");
814 /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
815 if (!(flags & O_APPEND))
818 myflags |= O_WRONLY | O_CREAT;
822 if (exts_compare(f->exts, type)) {
824 /* XXX Implement check XXX */
825 ext = strdup(f->exts);
827 ext = strsep(&stringp, "|");
828 fn = build_filename(filename, ext);
829 fd = open(fn, flags | myflags, mode);
832 if ((fs = f->rewrite(fd, comment))) {
837 fs->filename = strdup(filename);
840 ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
844 } else if (errno != EEXIST)
845 ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
852 ast_mutex_unlock(&formatlock);
854 ast_log(LOG_WARNING, "No such format '%s'\n", type);
858 char ast_waitstream(struct ast_channel *c, char *breakon)
860 /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */
862 struct ast_frame *fr;
864 res = ast_sched_wait(c->sched);
865 if ((res < 0) && !c->timingfunc) {
871 res = ast_waitfor(c, res);
873 ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
875 } else if (res > 0) {
879 ast_log(LOG_DEBUG, "Got hung up\n");
884 switch(fr->frametype) {
887 if (strchr(breakon, res)) {
892 case AST_FRAME_CONTROL:
893 switch(fr->subclass) {
894 case AST_CONTROL_HANGUP:
897 case AST_CONTROL_RINGING:
898 case AST_CONTROL_ANSWER:
902 ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
908 ast_sched_runq(c->sched);
910 return (c->_softhangup ? -1 : 0);
913 char ast_waitstream_fr(struct ast_channel *c, char *breakon, char *forward, char *rewind, int ms)
916 struct ast_frame *fr;
918 res = ast_sched_wait(c->sched);
919 if ((res < 0) && !c->timingfunc) {
925 res = ast_waitfor(c, res);
927 ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
934 ast_log(LOG_DEBUG, "Got hung up\n");
939 switch(fr->frametype) {
942 if (strchr(forward,res)) {
943 ast_stream_fastforward(c->stream, ms);
944 } else if (strchr(rewind,res)) {
945 ast_stream_rewind(c->stream, ms);
946 } else if (strchr(breakon, res)) {
951 case AST_FRAME_CONTROL:
952 switch(fr->subclass) {
953 case AST_CONTROL_HANGUP:
956 case AST_CONTROL_RINGING:
957 case AST_CONTROL_ANSWER:
961 ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
967 ast_sched_runq(c->sched);
971 return (c->_softhangup ? -1 : 0);
974 char ast_waitstream_full(struct ast_channel *c, char *breakon, int audiofd, int cmdfd)
979 struct ast_frame *fr;
980 struct ast_channel *rchan;
983 ms = ast_sched_wait(c->sched);
984 if ((ms < 0) && !c->timingfunc) {
990 rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
991 if (!rchan && (outfd < 0) && (ms)) {
992 ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
994 } else if (outfd > -1) {
995 /* The FD we were watching has something waiting */
1001 ast_log(LOG_DEBUG, "Got hung up\n");
1006 switch(fr->frametype) {
1007 case AST_FRAME_DTMF:
1009 if (strchr(breakon, res)) {
1014 case AST_FRAME_CONTROL:
1015 switch(fr->subclass) {
1016 case AST_CONTROL_HANGUP:
1019 case AST_CONTROL_RINGING:
1020 case AST_CONTROL_ANSWER:
1024 ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
1026 case AST_FRAME_VOICE:
1027 /* Write audio if appropriate */
1029 write(audiofd, fr->data, fr->datalen);
1034 ast_sched_runq(c->sched);
1038 return (c->_softhangup ? -1 : 0);