Fri May 26 01:45:34 2006

Asterisk developer's documentation


file.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Generic File Format Support.
00022  * 
00023  */
00024 
00025 #include <sys/types.h>
00026 #include <errno.h>
00027 #include <unistd.h>
00028 #include <stdlib.h>
00029 #include <string.h>
00030 #include <stdio.h>
00031 #include <fcntl.h>
00032 #include <dirent.h>
00033 #include <sys/types.h>
00034 #include <sys/stat.h>
00035 
00036 #include "asterisk.h"
00037 
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7915 $")
00039 
00040 #include "asterisk/frame.h"
00041 #include "asterisk/file.h"
00042 #include "asterisk/cli.h"
00043 #include "asterisk/logger.h"
00044 #include "asterisk/channel.h"
00045 #include "asterisk/sched.h"
00046 #include "asterisk/options.h"
00047 #include "asterisk/translate.h"
00048 #include "asterisk/utils.h"
00049 #include "asterisk/lock.h"
00050 #include "asterisk/app.h"
00051 #include "asterisk/pbx.h"
00052 
00053 struct ast_format {
00054    /* Name of format */
00055    char name[80];
00056    /* Extensions (separated by | if more than one) 
00057       this format can read.  First is assumed for writing (e.g. .mp3) */
00058    char exts[80];
00059    /* Format of frames it uses/provides (one only) */
00060    int format;
00061    /* Open an input stream, and start playback */
00062    struct ast_filestream * (*open)(FILE * f);
00063    /* Open an output stream, of a given file descriptor and comment it appropriately if applicable */
00064    struct ast_filestream * (*rewrite)(FILE *f, const char *comment);
00065    /* Write a frame to a channel */
00066    int (*write)(struct ast_filestream *, struct ast_frame *);
00067    /* seek num samples into file, whence(think normal seek) */
00068    int (*seek)(struct ast_filestream *, long offset, int whence);
00069    /* trunc file to current position */
00070    int (*trunc)(struct ast_filestream *fs);
00071    /* tell current position */
00072    long (*tell)(struct ast_filestream *fs);
00073    /* Read the next frame from the filestream (if available) and report when to get next one
00074       (in samples) */
00075    struct ast_frame * (*read)(struct ast_filestream *, int *whennext);
00076    /* Close file, and destroy filestream structure */
00077    void (*close)(struct ast_filestream *);
00078    /* Retrieve file comment */
00079    char * (*getcomment)(struct ast_filestream *);
00080    /* Link */
00081    struct ast_format *next;
00082 };
00083 
00084 struct ast_filestream {
00085    /* Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
00086    struct ast_format *fmt;
00087    int flags;
00088    mode_t mode;
00089    char *filename;
00090    char *realfilename;
00091    /* Video file stream */
00092    struct ast_filestream *vfs;
00093    /* Transparently translate from another format -- just once */
00094    struct ast_trans_pvt *trans;
00095    struct ast_tranlator_pvt *tr;
00096    int lastwriteformat;
00097    int lasttimeout;
00098    struct ast_channel *owner;
00099 };
00100 
00101 AST_MUTEX_DEFINE_STATIC(formatlock);
00102 
00103 static struct ast_format *formats = NULL;
00104 
00105 int ast_format_register(const char *name, const char *exts, int format,
00106                   struct ast_filestream * (*open)(FILE *f),
00107                   struct ast_filestream * (*rewrite)(FILE *f, const char *comment),
00108                   int (*write)(struct ast_filestream *, struct ast_frame *),
00109                   int (*seek)(struct ast_filestream *, long sample_offset, int whence),
00110                   int (*trunc)(struct ast_filestream *),
00111                   long (*tell)(struct ast_filestream *),
00112                   struct ast_frame * (*read)(struct ast_filestream *, int *whennext),
00113                   void (*close)(struct ast_filestream *),
00114                   char * (*getcomment)(struct ast_filestream *))
00115 {
00116    struct ast_format *tmp;
00117    if (ast_mutex_lock(&formatlock)) {
00118       ast_log(LOG_WARNING, "Unable to lock format list\n");
00119       return -1;
00120    }
00121    tmp = formats;
00122    while(tmp) {
00123       if (!strcasecmp(name, tmp->name)) {
00124          ast_mutex_unlock(&formatlock);
00125          ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", name);
00126          return -1;
00127       }
00128       tmp = tmp->next;
00129    }
00130    tmp = malloc(sizeof(struct ast_format));
00131    if (!tmp) {
00132       ast_log(LOG_WARNING, "Out of memory\n");
00133       ast_mutex_unlock(&formatlock);
00134       return -1;
00135    }
00136    ast_copy_string(tmp->name, name, sizeof(tmp->name));
00137    ast_copy_string(tmp->exts, exts, sizeof(tmp->exts));
00138    tmp->open = open;
00139    tmp->rewrite = rewrite;
00140    tmp->read = read;
00141    tmp->write = write;
00142    tmp->seek = seek;
00143    tmp->trunc = trunc;
00144    tmp->tell = tell;
00145    tmp->close = close;
00146    tmp->format = format;
00147    tmp->getcomment = getcomment;
00148    tmp->next = formats;
00149    formats = tmp;
00150    ast_mutex_unlock(&formatlock);
00151    if (option_verbose > 1)
00152       ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", name, exts);
00153    return 0;
00154 }
00155 
00156 int ast_format_unregister(const char *name)
00157 {
00158    struct ast_format *tmp, *tmpl = NULL;
00159    if (ast_mutex_lock(&formatlock)) {
00160       ast_log(LOG_WARNING, "Unable to lock format list\n");
00161       return -1;
00162    }
00163    tmp = formats;
00164    while(tmp) {
00165       if (!strcasecmp(name, tmp->name)) {
00166          if (tmpl) 
00167             tmpl->next = tmp->next;
00168          else
00169             formats = tmp->next;
00170          free(tmp);
00171          ast_mutex_unlock(&formatlock);
00172          if (option_verbose > 1)
00173             ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name);
00174          return 0;
00175       }
00176       tmpl = tmp;
00177       tmp = tmp->next;
00178    }
00179    ast_mutex_unlock(&formatlock);
00180    ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
00181    return -1;
00182 }
00183 
00184 int ast_stopstream(struct ast_channel *tmp)
00185 {
00186    /* Stop a running stream if there is one */
00187    if (tmp->vstream)
00188       ast_closestream(tmp->vstream);
00189    if (tmp->stream) {
00190       ast_closestream(tmp->stream);
00191       if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
00192          ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
00193    }
00194    return 0;
00195 }
00196 
00197 int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
00198 {
00199    struct ast_frame *trf;
00200    int res = -1;
00201    int alt=0;
00202    if (f->frametype == AST_FRAME_VIDEO) {
00203       if (fs->fmt->format < AST_FORMAT_MAX_AUDIO) {
00204          /* This is the audio portion.  Call the video one... */
00205          if (!fs->vfs && fs->filename) {
00206             /* XXX Support other video formats XXX */
00207             const char *type = "h263";
00208             fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
00209             ast_log(LOG_DEBUG, "Opened video output file\n");
00210          }
00211          if (fs->vfs)
00212             return ast_writestream(fs->vfs, f);
00213          /* Ignore */
00214          return 0;            
00215       } else {
00216          /* Might / might not have mark set */
00217          alt = 1;
00218       }
00219    } else if (f->frametype != AST_FRAME_VOICE) {
00220       ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
00221       return -1;
00222    }
00223    if (((fs->fmt->format | alt) & f->subclass) == f->subclass) {
00224       res =  fs->fmt->write(fs, f);
00225       if (res < 0) 
00226          ast_log(LOG_WARNING, "Natural write failed\n");
00227       if (res > 0)
00228          ast_log(LOG_WARNING, "Huh??\n");
00229       return res;
00230    } else {
00231       /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
00232              the one we've setup a translator for, we do the "wrong thing" XXX */
00233       if (fs->trans && (f->subclass != fs->lastwriteformat)) {
00234          ast_translator_free_path(fs->trans);
00235          fs->trans = NULL;
00236       }
00237       if (!fs->trans) 
00238          fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass);
00239       if (!fs->trans)
00240          ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n", fs->fmt->name, ast_getformatname(f->subclass));
00241       else {
00242          fs->lastwriteformat = f->subclass;
00243          res = 0;
00244          /* Get the translated frame but don't consume the original in case they're using it on another stream */
00245          trf = ast_translate(fs->trans, f, 0);
00246          if (trf) {
00247             res = fs->fmt->write(fs, trf);
00248             if (res) 
00249                ast_log(LOG_WARNING, "Translated frame write failed\n");
00250          } else
00251             res = 0;
00252       }
00253       return res;
00254    }
00255 }
00256 
00257 static int copy(const char *infile, const char *outfile)
00258 {
00259    int ifd;
00260    int ofd;
00261    int res;
00262    int len;
00263    char buf[4096];
00264 
00265    if ((ifd = open(infile, O_RDONLY)) < 0) {
00266       ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
00267       return -1;
00268    }
00269    if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
00270       ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
00271       close(ifd);
00272       return -1;
00273    }
00274    do {
00275       len = read(ifd, buf, sizeof(buf));
00276       if (len < 0) {
00277          ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
00278          close(ifd);
00279          close(ofd);
00280          unlink(outfile);
00281       }
00282       if (len) {
00283          res = write(ofd, buf, len);
00284          if (res != len) {
00285             ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
00286             close(ifd);
00287             close(ofd);
00288             unlink(outfile);
00289          }
00290       }
00291    } while(len);
00292    close(ifd);
00293    close(ofd);
00294    return 0;
00295 }
00296 
00297 static char *build_filename(const char *filename, const char *ext)
00298 {
00299    char *fn, type[16];
00300    int fnsize = 0;
00301 
00302    if (!strcmp(ext, "wav49")) {
00303       ast_copy_string(type, "WAV", sizeof(type));
00304    } else {
00305       ast_copy_string(type, ext, sizeof(type));
00306    }
00307 
00308    if (filename[0] == '/') {
00309       fnsize = strlen(filename) + strlen(type) + 2;
00310       fn = malloc(fnsize);
00311       if (fn)
00312          snprintf(fn, fnsize, "%s.%s", filename, type);
00313    } else {
00314       char tmp[AST_CONFIG_MAX_PATH] = "";
00315 
00316       snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_DATA_DIR, "sounds");
00317       fnsize = strlen(tmp) + strlen(filename) + strlen(type) + 3;
00318       fn = malloc(fnsize);
00319       if (fn)
00320          snprintf(fn, fnsize, "%s/%s.%s", tmp, filename, type);
00321    }
00322 
00323    return fn;
00324 }
00325 
00326 static int exts_compare(const char *exts, const char *type)
00327 {
00328    char *stringp = NULL, *ext;
00329    char tmp[256];
00330 
00331    ast_copy_string(tmp, exts, sizeof(tmp));
00332    stringp = tmp;
00333    while ((ext = strsep(&stringp, "|"))) {
00334       if (!strcmp(ext, type)) {
00335          return 1;
00336       }
00337    }
00338 
00339    return 0;
00340 }
00341 
00342 #define ACTION_EXISTS 1
00343 #define ACTION_DELETE 2
00344 #define ACTION_RENAME 3
00345 #define ACTION_OPEN   4
00346 #define ACTION_COPY   5
00347 
00348 static int ast_filehelper(const char *filename, const char *filename2, const char *fmt, int action)
00349 {
00350    struct stat st;
00351    struct ast_format *f;
00352    struct ast_filestream *s;
00353    int res=0, ret = 0;
00354    char *ext=NULL, *exts, *fn, *nfn;
00355    FILE *bfile;
00356    struct ast_channel *chan = (struct ast_channel *)filename2;
00357    
00358    /* Start with negative response */
00359    if (action == ACTION_EXISTS)
00360       res = 0;
00361    else
00362       res = -1;
00363    if (action == ACTION_OPEN)
00364       ret = -1;
00365    /* Check for a specific format */
00366    if (ast_mutex_lock(&formatlock)) {
00367       ast_log(LOG_WARNING, "Unable to lock format list\n");
00368       if (action == ACTION_EXISTS)
00369          return 0;
00370       else
00371          return -1;
00372    }
00373    f = formats;
00374    while(f) {
00375       if (!fmt || exts_compare(f->exts, fmt)) {
00376          char *stringp=NULL;
00377          exts = ast_strdupa(f->exts);
00378          /* Try each kind of extension */
00379          stringp=exts;
00380          ext = strsep(&stringp, "|");
00381          do {
00382             fn = build_filename(filename, ext);
00383             if (fn) {
00384                res = stat(fn, &st);
00385                if (!res) {
00386                   switch(action) {
00387                   case ACTION_EXISTS:
00388                      ret |= f->format;
00389                      break;
00390                   case ACTION_DELETE:
00391                      res = unlink(fn);
00392                      if (res)
00393                         ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
00394                      break;
00395                   case ACTION_RENAME:
00396                      nfn = build_filename(filename2, ext);
00397                      if (nfn) {
00398                         res = rename(fn, nfn);
00399                         if (res)
00400                            ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
00401                         free(nfn);
00402                      } else
00403                         ast_log(LOG_WARNING, "Out of memory\n");
00404                      break;
00405                   case ACTION_COPY:
00406                      nfn = build_filename(filename2, ext);
00407                      if (nfn) {
00408                         res = copy(fn, nfn);
00409                         if (res)
00410                            ast_log(LOG_WARNING, "copy(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
00411                         free(nfn);
00412                      } else
00413                         ast_log(LOG_WARNING, "Out of memory\n");
00414                      break;
00415                   case ACTION_OPEN:
00416                      if ((ret < 0) && ((chan->writeformat & f->format) ||
00417                               ((f->format >= AST_FORMAT_MAX_AUDIO) && fmt))) {
00418                         bfile = fopen(fn, "r");
00419                         if (bfile) {
00420                            ret = 1;
00421                            s = f->open(bfile);
00422                            if (s) {
00423                               s->lasttimeout = -1;
00424                               s->fmt = f;
00425                               s->trans = NULL;
00426                               s->filename = NULL;
00427                               if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
00428                                  chan->stream = s;
00429                               else
00430                                  chan->vstream = s;
00431                            } else {
00432                               fclose(bfile);
00433                               ast_log(LOG_WARNING, "Unable to open file on %s\n", fn);
00434                               ret = -1;
00435                            }
00436                         } else{
00437                            ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
00438                            ret = -1;
00439                         }
00440                      }
00441                      break;
00442                   default:
00443                      ast_log(LOG_WARNING, "Unknown helper %d\n", action);
00444                   }
00445                   /* Conveniently this logic is the same for all */
00446                   if (res)
00447                      break;
00448                }
00449                free(fn);
00450             }
00451             ext = strsep(&stringp, "|");
00452          } while(ext);
00453          
00454       }
00455       f = f->next;
00456    }
00457    ast_mutex_unlock(&formatlock);
00458    if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
00459       res = ret ? ret : -1;
00460    return res;
00461 }
00462 struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *filename, const char *preflang)
00463 {
00464    return ast_openstream_full(chan, filename, preflang, 0);
00465 }
00466 
00467 struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis)
00468 {
00469    /* This is a fairly complex routine.  Essentially we should do 
00470       the following:
00471       
00472       1) Find which file handlers produce our type of format.
00473       2) Look for a filename which it can handle.
00474       3) If we find one, then great.  
00475       4) If not, see what files are there
00476       5) See what we can actually support
00477       6) Choose the one with the least costly translator path and
00478           set it up.
00479          
00480    */
00481    int fmts = -1;
00482    char filename2[256]="";
00483    char filename3[256];
00484    char *endpart;
00485    int res;
00486 
00487    if (!asis) {
00488       /* do this first, otherwise we detect the wrong writeformat */
00489       ast_stopstream(chan);
00490       if (chan->generator)
00491          ast_deactivate_generator(chan);
00492    }
00493    if (!ast_strlen_zero(preflang)) {
00494       ast_copy_string(filename3, filename, sizeof(filename3));
00495       endpart = strrchr(filename3, '/');
00496       if (endpart) {
00497          *endpart = '\0';
00498          endpart++;
00499          snprintf(filename2, sizeof(filename2), "%s/%s/%s", filename3, preflang, endpart);
00500       } else
00501          snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
00502       fmts = ast_fileexists(filename2, NULL, NULL);
00503    }
00504    if (fmts < 1) {
00505       ast_copy_string(filename2, filename, sizeof(filename2));
00506       fmts = ast_fileexists(filename2, NULL, NULL);
00507    }
00508    if (fmts < 1) {
00509       ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
00510       return NULL;
00511    }
00512    chan->oldwriteformat = chan->writeformat;
00513    /* Set the channel to a format we can work with */
00514    res = ast_set_write_format(chan, fmts);
00515    
00516    res = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
00517    if (res >= 0)
00518       return chan->stream;
00519    return NULL;
00520 }
00521 
00522 struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *filename, const char *preflang)
00523 {
00524    /* This is a fairly complex routine.  Essentially we should do 
00525       the following:
00526       
00527       1) Find which file handlers produce our type of format.
00528       2) Look for a filename which it can handle.
00529       3) If we find one, then great.  
00530       4) If not, see what files are there
00531       5) See what we can actually support
00532       6) Choose the one with the least costly translator path and
00533           set it up.
00534          
00535    */
00536    int fd = -1;
00537    int fmts = -1;
00538    char filename2[256];
00539    char lang2[MAX_LANGUAGE];
00540    /* XXX H.263 only XXX */
00541    char *fmt = "h263";
00542    if (!ast_strlen_zero(preflang)) {
00543       snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
00544       fmts = ast_fileexists(filename2, fmt, NULL);
00545       if (fmts < 1) {
00546          ast_copy_string(lang2, preflang, sizeof(lang2));
00547          snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
00548          fmts = ast_fileexists(filename2, fmt, NULL);
00549       }
00550    }
00551    if (fmts < 1) {
00552       ast_copy_string(filename2, filename, sizeof(filename2));
00553       fmts = ast_fileexists(filename2, fmt, NULL);
00554    }
00555    if (fmts < 1) {
00556       return NULL;
00557    }
00558    fd = ast_filehelper(filename2, (char *)chan, fmt, ACTION_OPEN);
00559    if (fd >= 0)
00560       return chan->vstream;
00561    ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
00562    return NULL;
00563 }
00564 
00565 struct ast_frame *ast_readframe(struct ast_filestream *s)
00566 {
00567    struct ast_frame *f = NULL;
00568    int whennext = 0; 
00569    if (s && s->fmt)
00570       f = s->fmt->read(s, &whennext);
00571    return f;
00572 }
00573 
00574 static int ast_readaudio_callback(void *data)
00575 {
00576    struct ast_filestream *s = data;
00577    struct ast_frame *fr;
00578    int whennext = 0;
00579 
00580    while(!whennext) {
00581       fr = s->fmt->read(s, &whennext);
00582       if (fr) {
00583          if (ast_write(s->owner, fr)) {
00584             ast_log(LOG_WARNING, "Failed to write frame\n");
00585             s->owner->streamid = -1;
00586 #ifdef ZAPTEL_OPTIMIZATIONS
00587             ast_settimeout(s->owner, 0, NULL, NULL);
00588 #endif         
00589             return 0;
00590          }
00591       } else {
00592          /* Stream has finished */
00593          s->owner->streamid = -1;
00594 #ifdef ZAPTEL_OPTIMIZATIONS
00595          ast_settimeout(s->owner, 0, NULL, NULL);
00596 #endif         
00597          return 0;
00598       }
00599    }
00600    if (whennext != s->lasttimeout) {
00601 #ifdef ZAPTEL_OPTIMIZATIONS
00602       if (s->owner->timingfd > -1)
00603          ast_settimeout(s->owner, whennext, ast_readaudio_callback, s);
00604       else
00605 #endif      
00606          s->owner->streamid = ast_sched_add(s->owner->sched, whennext/8, ast_readaudio_callback, s);
00607       s->lasttimeout = whennext;
00608       return 0;
00609    }
00610    return 1;
00611 }
00612 
00613 static int ast_readvideo_callback(void *data)
00614 {
00615    struct ast_filestream *s = data;
00616    struct ast_frame *fr;
00617    int whennext = 0;
00618 
00619    while(!whennext) {
00620       fr = s->fmt->read(s, &whennext);
00621       if (fr) {
00622          if (ast_write(s->owner, fr)) {
00623             ast_log(LOG_WARNING, "Failed to write frame\n");
00624             s->owner->vstreamid = -1;
00625             return 0;
00626          }
00627       } else {
00628          /* Stream has finished */
00629          s->owner->vstreamid = -1;
00630          return 0;
00631       }
00632    }
00633    if (whennext != s->lasttimeout) {
00634       s->owner->vstreamid = ast_sched_add(s->owner->sched, whennext/8, ast_readvideo_callback, s);
00635       s->lasttimeout = whennext;
00636       return 0;
00637    }
00638    return 1;
00639 }
00640 
00641 int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
00642 {
00643    s->owner = chan;
00644    return 0;
00645 }
00646 
00647 int ast_playstream(struct ast_filestream *s)
00648 {
00649    if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
00650       ast_readaudio_callback(s);
00651    else
00652       ast_readvideo_callback(s);
00653    return 0;
00654 }
00655 
00656 int ast_seekstream(struct ast_filestream *fs, long sample_offset, int whence)
00657 {
00658    return fs->fmt->seek(fs, sample_offset, whence);
00659 }
00660 
00661 int ast_truncstream(struct ast_filestream *fs)
00662 {
00663    return fs->fmt->trunc(fs);
00664 }
00665 
00666 long ast_tellstream(struct ast_filestream *fs)
00667 {
00668    return fs->fmt->tell(fs);
00669 }
00670 
00671 int ast_stream_fastforward(struct ast_filestream *fs, long ms)
00672 {
00673    /* I think this is right, 8000 samples per second, 1000 ms a second so 8
00674     * samples per ms  */
00675    long samples = ms * 8;
00676    return ast_seekstream(fs, samples, SEEK_CUR);
00677 }
00678 
00679 int ast_stream_rewind(struct ast_filestream *fs, long ms)
00680 {
00681    long samples = ms * 8;
00682    samples = samples * -1;
00683    return ast_seekstream(fs, samples, SEEK_CUR);
00684 }
00685 
00686 int ast_closestream(struct ast_filestream *f)
00687 {
00688    char *cmd = NULL;
00689    size_t size = 0;
00690    /* Stop a running stream if there is one */
00691    if (f->owner) {
00692       if (f->fmt->format < AST_FORMAT_MAX_AUDIO) {
00693          f->owner->stream = NULL;
00694          if (f->owner->streamid > -1)
00695             ast_sched_del(f->owner->sched, f->owner->streamid);
00696          f->owner->streamid = -1;
00697 #ifdef ZAPTEL_OPTIMIZATIONS
00698          ast_settimeout(f->owner, 0, NULL, NULL);
00699 #endif         
00700       } else {
00701          f->owner->vstream = NULL;
00702          if (f->owner->vstreamid > -1)
00703             ast_sched_del(f->owner->sched, f->owner->vstreamid);
00704          f->owner->vstreamid = -1;
00705       }
00706    }
00707    /* destroy the translator on exit */
00708    if (f->trans) {
00709       ast_translator_free_path(f->trans);
00710       f->trans = NULL;
00711    }
00712 
00713    if (f->realfilename && f->filename) {
00714          size = strlen(f->filename) + strlen(f->realfilename) + 15;
00715          cmd = alloca(size);
00716          memset(cmd,0,size);
00717          snprintf(cmd,size,"/bin/mv -f %s %s",f->filename,f->realfilename);
00718          ast_safe_system(cmd);
00719    }
00720 
00721    if (f->filename) {
00722       free(f->filename);
00723       f->filename = NULL;
00724    }
00725    if (f->realfilename) {
00726       free(f->realfilename);
00727       f->realfilename = NULL;
00728    }
00729    f->fmt->close(f);
00730    return 0;
00731 }
00732 
00733 
00734 int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
00735 {
00736    char filename2[256];
00737    char tmp[256];
00738    char *postfix;
00739    char *prefix;
00740    char *c;
00741    char lang2[MAX_LANGUAGE];
00742    int res = -1;
00743    if (!ast_strlen_zero(preflang)) {
00744       /* Insert the language between the last two parts of the path */
00745       ast_copy_string(tmp, filename, sizeof(tmp));
00746       c = strrchr(tmp, '/');
00747       if (c) {
00748          *c = '\0';
00749          postfix = c+1;
00750          prefix = tmp;
00751          snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, preflang, postfix);
00752       } else {
00753          postfix = tmp;
00754          prefix="";
00755          snprintf(filename2, sizeof(filename2), "%s/%s", preflang, postfix);
00756       }
00757       res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
00758       if (res < 1) {
00759          char *stringp=NULL;
00760          ast_copy_string(lang2, preflang, sizeof(lang2));
00761          stringp=lang2;
00762          strsep(&stringp, "_");
00763          /* If language is a specific locality of a language (like es_MX), strip the locality and try again */
00764          if (strcmp(lang2, preflang)) {
00765             if (ast_strlen_zero(prefix)) {
00766                snprintf(filename2, sizeof(filename2), "%s/%s", lang2, postfix);
00767             } else {
00768                snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, lang2, postfix);
00769             }
00770             res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
00771          }
00772       }
00773    }
00774 
00775    /* Fallback to no language (usually winds up being American English) */
00776    if (res < 1) {
00777       res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
00778    }
00779    return res;
00780 }
00781 
00782 int ast_filedelete(const char *filename, const char *fmt)
00783 {
00784    return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
00785 }
00786 
00787 int ast_filerename(const char *filename, const char *filename2, const char *fmt)
00788 {
00789    return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
00790 }
00791 
00792 int ast_filecopy(const char *filename, const char *filename2, const char *fmt)
00793 {
00794    return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
00795 }
00796 
00797 int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
00798 {
00799    struct ast_filestream *fs;
00800    struct ast_filestream *vfs;
00801 
00802    fs = ast_openstream(chan, filename, preflang);
00803    vfs = ast_openvstream(chan, filename, preflang);
00804    if (vfs)
00805       ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
00806    if (fs){
00807       if (ast_applystream(chan, fs))
00808          return -1;
00809       if (vfs && ast_applystream(chan, vfs))
00810          return -1;
00811       if (ast_playstream(fs))
00812          return -1;
00813       if (vfs && ast_playstream(vfs))
00814          return -1;
00815 #if 1
00816       if (option_verbose > 2)
00817          ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (language '%s')\n", filename, preflang ? preflang : "default");
00818 #endif
00819       return 0;
00820    }
00821    ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname(chan->nativeformats), strerror(errno));
00822    return -1;
00823 }
00824 
00825 struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
00826 {
00827    FILE *bfile;
00828    struct ast_format *f;
00829    struct ast_filestream *fs = NULL;
00830    char *fn;
00831 
00832    if (ast_mutex_lock(&formatlock)) {
00833       ast_log(LOG_WARNING, "Unable to lock format list\n");
00834       return NULL;
00835    }
00836 
00837    for (f = formats; f && !fs; f = f->next) {
00838       if (!exts_compare(f->exts, type))
00839          continue;
00840 
00841       fn = build_filename(filename, type);
00842       bfile = fopen(fn, "r");
00843       if (bfile) {
00844          errno = 0;
00845 
00846          if (!(fs = f->open(bfile))) {
00847             ast_log(LOG_WARNING, "Unable to open %s\n", fn);
00848             fclose(bfile);
00849             free(fn);
00850             continue;
00851          }
00852 
00853          fs->trans = NULL;
00854          fs->fmt = f;
00855          fs->flags = flags;
00856          fs->mode = mode;
00857          fs->filename = strdup(filename);
00858          fs->vfs = NULL;
00859       } else if (errno != EEXIST)
00860          ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
00861       free(fn);
00862    }
00863 
00864    ast_mutex_unlock(&formatlock);
00865    if (!fs) 
00866       ast_log(LOG_WARNING, "No such format '%s'\n", type);
00867 
00868    return fs;
00869 }
00870 
00871 struct ast_filestream *ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
00872 {
00873    int fd, myflags = 0;
00874    /* compiler claims this variable can be used before initialization... */
00875    FILE *bfile = NULL;
00876    struct ast_format *f;
00877    struct ast_filestream *fs = NULL;
00878    char *fn, *orig_fn = NULL;
00879    char *buf = NULL;
00880    size_t size = 0;
00881 
00882    if (ast_mutex_lock(&formatlock)) {
00883       ast_log(LOG_WARNING, "Unable to lock format list\n");
00884       return NULL;
00885    }
00886 
00887    /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
00888    if (flags & O_APPEND) { 
00889       /* We really can't use O_APPEND as it will break WAV header updates */
00890       flags &= ~O_APPEND;
00891    } else {
00892       myflags = O_TRUNC;
00893    }
00894    
00895    myflags |= O_WRONLY | O_CREAT;
00896 
00897    for (f = formats; f && !fs; f = f->next) {
00898       if (!exts_compare(f->exts, type))
00899          continue;
00900 
00901       fn = build_filename(filename, type);
00902       fd = open(fn, flags | myflags, mode);
00903       if (fd > -1) {
00904          /* fdopen() the resulting file stream */
00905          bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
00906          if (!bfile) {
00907             ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
00908             close(fd);
00909             fd = -1;
00910          }
00911       }
00912       
00913       if (option_cache_record_files && (fd > -1)) {
00914          char *c;
00915 
00916          fclose(bfile);
00917          /*
00918            We touch orig_fn just as a place-holder so other things (like vmail) see the file is there.
00919            What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place.
00920          */
00921          orig_fn = ast_strdupa(fn);
00922          for (c = fn; *c; c++)
00923             if (*c == '/')
00924                *c = '_';
00925 
00926          size = strlen(fn) + strlen(record_cache_dir) + 2;
00927          buf = alloca(size);
00928          strcpy(buf, record_cache_dir);
00929          strcat(buf, "/");
00930          strcat(buf, fn);
00931          free(fn);
00932          fn = buf;
00933          fd = open(fn, flags | myflags, mode);
00934          if (fd > -1) {
00935             /* fdopen() the resulting file stream */
00936             bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
00937             if (!bfile) {
00938                ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
00939                close(fd);
00940                fd = -1;
00941             }
00942          }
00943       }
00944       if (fd > -1) {
00945          errno = 0;
00946          if ((fs = f->rewrite(bfile, comment))) {
00947             fs->trans = NULL;
00948             fs->fmt = f;
00949             fs->flags = flags;
00950             fs->mode = mode;
00951             if (orig_fn) {
00952                fs->realfilename = strdup(orig_fn);
00953                fs->filename = strdup(fn);
00954             } else {
00955                fs->realfilename = NULL;
00956                fs->filename = strdup(filename);
00957             }
00958             fs->vfs = NULL;
00959             /* If truncated, we'll be at the beginning; if not truncated, then append */
00960             f->seek(fs, 0, SEEK_END);
00961          } else {
00962             ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
00963             close(fd);
00964             if (orig_fn) {
00965                unlink(fn);
00966                unlink(orig_fn);
00967             }
00968          }
00969       } else if (errno != EEXIST) {
00970          ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
00971          if (orig_fn)
00972             unlink(orig_fn);
00973       }
00974       /* if buf != NULL then fn is already free and pointing to it */
00975       if (!buf)
00976          free(fn);
00977    }
00978 
00979    ast_mutex_unlock(&formatlock);
00980    if (!fs)
00981       ast_log(LOG_WARNING, "No such format '%s'\n", type);
00982 
00983    return fs;
00984 }
00985 
00986 int ast_waitstream(struct ast_channel *c, const char *breakon)
00987 {
00988    /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */
00989    int res;
00990    struct ast_frame *fr;
00991    if (!breakon) breakon = "";
00992    while(c->stream) {
00993       res = ast_sched_wait(c->sched);
00994       if ((res < 0) && !c->timingfunc) {
00995          ast_stopstream(c);
00996          break;
00997       }
00998       if (res < 0)
00999          res = 1000;
01000       res = ast_waitfor(c, res);
01001       if (res < 0) {
01002          ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
01003          return res;
01004       } else if (res > 0) {
01005          fr = ast_read(c);
01006          if (!fr) {
01007 #if 0
01008             ast_log(LOG_DEBUG, "Got hung up\n");
01009 #endif
01010             return -1;
01011          }
01012          
01013          switch(fr->frametype) {
01014          case AST_FRAME_DTMF:
01015             res = fr->subclass;
01016             if (strchr(breakon, res)) {
01017                ast_frfree(fr);
01018                return res;
01019             }
01020             break;
01021          case AST_FRAME_CONTROL:
01022             switch(fr->subclass) {
01023             case AST_CONTROL_HANGUP:
01024                ast_frfree(fr);
01025                return -1;
01026             case AST_CONTROL_RINGING:
01027             case AST_CONTROL_ANSWER:
01028             case AST_CONTROL_VIDUPDATE:
01029                /* Unimportant */
01030                break;
01031             default:
01032                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
01033             }
01034          }
01035          /* Ignore */
01036          ast_frfree(fr);
01037       }
01038       ast_sched_runq(c->sched);
01039    }
01040    return (c->_softhangup ? -1 : 0);
01041 }
01042 
01043 int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *rewind, int ms)
01044 {
01045    int res;
01046    struct ast_frame *fr;
01047 
01048    if (!breakon)
01049          breakon = "";
01050    if (!forward)
01051          forward = "";
01052    if (!rewind)
01053          rewind = "";
01054    
01055    while(c->stream) {
01056       res = ast_sched_wait(c->sched);
01057       if ((res < 0) && !c->timingfunc) {
01058          ast_stopstream(c);
01059          break;
01060       }
01061       if (res < 0)
01062          res = 1000;
01063       res = ast_waitfor(c, res);
01064       if (res < 0) {
01065          ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
01066          return res;
01067       } else
01068       if (res > 0) {
01069          fr = ast_read(c);
01070          if (!fr) {
01071 #if 0
01072             ast_log(LOG_DEBUG, "Got hung up\n");
01073 #endif
01074             return -1;
01075          }
01076          
01077          switch(fr->frametype) {
01078          case AST_FRAME_DTMF:
01079             res = fr->subclass;
01080             if (strchr(forward,res)) {
01081                ast_stream_fastforward(c->stream, ms);
01082             } else if (strchr(rewind,res)) {
01083                ast_stream_rewind(c->stream, ms);
01084             } else if (strchr(breakon, res)) {
01085                ast_frfree(fr);
01086                return res;
01087             }              
01088             break;
01089          case AST_FRAME_CONTROL:
01090             switch(fr->subclass) {
01091             case AST_CONTROL_HANGUP:
01092                ast_frfree(fr);
01093                return -1;
01094             case AST_CONTROL_RINGING:
01095             case AST_CONTROL_ANSWER:
01096                /* Unimportant */
01097                break;
01098             default:
01099                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
01100             }
01101          }
01102          /* Ignore */
01103          ast_frfree(fr);
01104       } else
01105          ast_sched_runq(c->sched);
01106    
01107       
01108    }
01109    return (c->_softhangup ? -1 : 0);
01110 }
01111 
01112 int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd)
01113 {
01114    int res;
01115    int ms;
01116    int outfd;
01117    struct ast_frame *fr;
01118    struct ast_channel *rchan;
01119 
01120    if (!breakon)
01121       breakon = "";
01122    
01123    while(c->stream) {
01124       ms = ast_sched_wait(c->sched);
01125       if ((ms < 0) && !c->timingfunc) {
01126          ast_stopstream(c);
01127          break;
01128       }
01129       if (ms < 0)
01130          ms = 1000;
01131       rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
01132       if (!rchan && (outfd < 0) && (ms)) {
01133          /* Continue */
01134          if (errno == EINTR)
01135             continue;
01136          ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
01137          return -1;
01138       } else if (outfd > -1) {
01139          /* The FD we were watching has something waiting */
01140          return 1;
01141       } else if (rchan) {
01142          fr = ast_read(c);
01143          if (!fr) {
01144 #if 0
01145             ast_log(LOG_DEBUG, "Got hung up\n");
01146 #endif
01147             return -1;
01148          }
01149          
01150          switch(fr->frametype) {
01151          case AST_FRAME_DTMF:
01152             res = fr->subclass;
01153             if (strchr(breakon, res)) {
01154                ast_frfree(fr);
01155                return res;
01156             }
01157             break;
01158          case AST_FRAME_CONTROL:
01159             switch(fr->subclass) {
01160             case AST_CONTROL_HANGUP:
01161                ast_frfree(fr);
01162                return -1;
01163             case AST_CONTROL_RINGING:
01164             case AST_CONTROL_ANSWER:
01165                /* Unimportant */
01166                break;
01167             default:
01168                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
01169             }
01170          case AST_FRAME_VOICE:
01171             /* Write audio if appropriate */
01172             if (audiofd > -1)
01173                write(audiofd, fr->data, fr->datalen);
01174          }
01175          /* Ignore */
01176          ast_frfree(fr);
01177       }
01178       ast_sched_runq(c->sched);
01179    }
01180    return (c->_softhangup ? -1 : 0);
01181 }
01182 
01183 int ast_waitstream_exten(struct ast_channel *c, const char *context)
01184 {
01185    /* Waitstream, with return in the case of a valid 1 digit extension */
01186    /* in the current or specified context being pressed */
01187    /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */
01188    int res;
01189    struct ast_frame *fr;
01190    char exten[AST_MAX_EXTENSION];
01191 
01192    if (!context) context = c->context;
01193    while(c->stream) {
01194       res = ast_sched_wait(c->sched);
01195       if ((res < 0) && !c->timingfunc) {
01196          ast_stopstream(c);
01197          break;
01198       }
01199       if (res < 0)
01200          res = 1000;
01201       res = ast_waitfor(c, res);
01202       if (res < 0) {
01203          ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
01204          return res;
01205       } else if (res > 0) {
01206          fr = ast_read(c);
01207          if (!fr) {
01208 #if 0
01209             ast_log(LOG_DEBUG, "Got hung up\n");
01210 #endif
01211             return -1;
01212          }
01213          
01214          switch(fr->frametype) {
01215          case AST_FRAME_DTMF:
01216             res = fr->subclass;
01217             snprintf(exten, sizeof(exten), "%c", res);
01218             if (ast_exists_extension(c, context, exten, 1, c->cid.cid_num)) {
01219                ast_frfree(fr);
01220                return res;
01221             }
01222             break;
01223          case AST_FRAME_CONTROL:
01224             switch(fr->subclass) {
01225             case AST_CONTROL_HANGUP:
01226                ast_frfree(fr);
01227                return -1;
01228             case AST_CONTROL_RINGING:
01229             case AST_CONTROL_ANSWER:
01230                /* Unimportant */
01231                break;
01232             default:
01233                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
01234             }
01235          }
01236          /* Ignore */
01237          ast_frfree(fr);
01238       }
01239       ast_sched_runq(c->sched);
01240    }
01241    return (c->_softhangup ? -1 : 0);
01242 }
01243 
01244 static int show_file_formats(int fd, int argc, char *argv[])
01245 {
01246 #define FORMAT "%-10s %-10s %-20s\n"
01247 #define FORMAT2 "%-10s %-10s %-20s\n"
01248    struct ast_format *f;
01249    int count_fmt = 0;
01250 
01251    if (argc != 3)
01252       return RESULT_SHOWUSAGE;
01253    ast_cli(fd, FORMAT, "Format", "Name", "Extensions");
01254            
01255    if (ast_mutex_lock(&formatlock)) {
01256       ast_log(LOG_WARNING, "Unable to lock format list\n");
01257       return -1;
01258    }
01259 
01260    f = formats;
01261    while(f) {
01262       ast_cli(fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts);
01263       f = f->next;
01264       count_fmt++;
01265    };
01266    ast_mutex_unlock(&formatlock);
01267    ast_cli(fd, "%d file formats registered.\n", count_fmt);
01268    return RESULT_SUCCESS;
01269 #undef FORMAT
01270 #undef FORMAT2
01271    
01272 }
01273 
01274 struct ast_cli_entry show_file =
01275 {
01276    { "show", "file", "formats" },
01277    show_file_formats,
01278    "Displays file formats",
01279    "Usage: show file formats\n"
01280    "       displays currently registered file formats (if any)\n"
01281 };
01282 
01283 int ast_file_init(void)
01284 {
01285    ast_cli_register(&show_file);
01286    return 0;
01287 }

Generated on Fri May 26 01:45:34 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6