Thu May 24 14:21:10 2007

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

Generated on Thu May 24 14:21:10 2007 for Asterisk - the Open Source PBX by  doxygen 1.4.7