Fri May 26 01:45:27 2006

Asterisk developer's documentation


app_record.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  * Matthew Fredrickson <creslin@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 Trivial application to record a sound file
00022  *
00023  * \ingroup applications
00024  */
00025  
00026 #include <stdlib.h>
00027 #include <stdio.h>
00028 #include <string.h>
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
00033 
00034 #include "asterisk/lock.h"
00035 #include "asterisk/file.h"
00036 #include "asterisk/logger.h"
00037 #include "asterisk/channel.h"
00038 #include "asterisk/pbx.h"
00039 #include "asterisk/module.h"
00040 #include "asterisk/translate.h"
00041 #include "asterisk/dsp.h"
00042 #include "asterisk/utils.h"
00043 #include "asterisk/options.h"
00044 
00045 static char *tdesc = "Trivial Record Application";
00046 
00047 static char *app = "Record";
00048 
00049 static char *synopsis = "Record to a file";
00050 
00051 static char *descrip = 
00052 "  Record(filename.format|silence[|maxduration][|options])\n\n"
00053 "Records from the channel into a given filename. If the file exists it will\n"
00054 "be overwritten.\n"
00055 "- 'format' is the format of the file type to be recorded (wav, gsm, etc).\n"
00056 "- 'silence' is the number of seconds of silence to allow before returning.\n"
00057 "- 'maxduration' is the maximum recording duration in seconds. If missing\n"
00058 "or 0 there is no maximum.\n"
00059 "- 'options' may contain any of the following letters:\n"
00060 "     'a' : append to existing recording rather than replacing\n"
00061 "     'n' : do not answer, but record anyway if line not yet answered\n"
00062 "     'q' : quiet (do not play a beep tone)\n"
00063 "     's' : skip recording if the line is not yet answered\n"
00064 "     't' : use alternate '*' terminator key instead of default '#'\n"
00065 "\n"
00066 "If filename contains '%d', these characters will be replaced with a number\n"
00067 "incremented by one each time the file is recorded. \n\n"
00068 "Use 'show file formats' to see the available formats on your system\n\n"
00069 "User can press '#' to terminate the recording and continue to the next priority.\n\n"
00070 "If the user should hangup during a recording, all data will be lost and the\n"
00071 "application will teminate. \n";
00072 
00073 STANDARD_LOCAL_USER;
00074 
00075 LOCAL_USER_DECL;
00076 
00077 static int record_exec(struct ast_channel *chan, void *data)
00078 {
00079    int res = 0;
00080    int count = 0;
00081    int percentflag = 0;
00082    char *filename, *ext = NULL, *silstr, *maxstr, *options;
00083    char *vdata, *p;
00084    int i = 0;
00085    char tmp[256];
00086 
00087    struct ast_filestream *s = '\0';
00088    struct localuser *u;
00089    struct ast_frame *f = NULL;
00090    
00091    struct ast_dsp *sildet = NULL;      /* silence detector dsp */
00092    int totalsilence = 0;
00093    int dspsilence = 0;
00094    int silence = 0;     /* amount of silence to allow */
00095    int gotsilence = 0;     /* did we timeout for silence? */
00096    int maxduration = 0;    /* max duration of recording in milliseconds */
00097    int gottimeout = 0;     /* did we timeout for maxduration exceeded? */
00098    int option_skip = 0;
00099    int option_noanswer = 0;
00100    int option_append = 0;
00101    int terminator = '#';
00102    int option_quiet = 0;
00103    int rfmt = 0;
00104    int flags;
00105    int waitres;
00106    struct ast_silence_generator *silgen = NULL;
00107    
00108    /* The next few lines of code parse out the filename and header from the input string */
00109    if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
00110       ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
00111       return -1;
00112    }
00113 
00114    LOCAL_USER_ADD(u);
00115 
00116    /* Yay for strsep being easy */
00117    vdata = ast_strdupa(data);
00118    if (!vdata) {
00119       ast_log(LOG_ERROR, "Out of memory\n");
00120       LOCAL_USER_REMOVE(u);
00121       return -1;
00122    }
00123 
00124    p = vdata;
00125    filename = strsep(&p, "|");
00126    silstr = strsep(&p, "|");
00127    maxstr = strsep(&p, "|");  
00128    options = strsep(&p, "|");
00129    
00130    if (filename) {
00131       if (strstr(filename, "%d"))
00132          percentflag = 1;
00133       ext = strrchr(filename, '.'); /* to support filename with a . in the filename, not format */
00134       if (!ext)
00135          ext = strchr(filename, ':');
00136       if (ext) {
00137          *ext = '\0';
00138          ext++;
00139       }
00140    }
00141    if (!ext) {
00142       ast_log(LOG_WARNING, "No extension specified to filename!\n");
00143       LOCAL_USER_REMOVE(u);
00144       return -1;
00145    }
00146    if (silstr) {
00147       if ((sscanf(silstr, "%d", &i) == 1) && (i > -1)) {
00148          silence = i * 1000;
00149       } else if (!ast_strlen_zero(silstr)) {
00150          ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", silstr);
00151       }
00152    }
00153    
00154    if (maxstr) {
00155       if ((sscanf(maxstr, "%d", &i) == 1) && (i > -1))
00156          /* Convert duration to milliseconds */
00157          maxduration = i * 1000;
00158       else if (!ast_strlen_zero(maxstr))
00159          ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", maxstr);
00160    }
00161    if (options) {
00162       /* Retain backwards compatibility with old style options */
00163       if (!strcasecmp(options, "skip"))
00164          option_skip = 1;
00165       else if (!strcasecmp(options, "noanswer"))
00166          option_noanswer = 1;
00167       else {
00168          if (strchr(options, 's'))
00169             option_skip = 1;
00170          if (strchr(options, 'n'))
00171             option_noanswer = 1;
00172          if (strchr(options, 'a'))
00173             option_append = 1;
00174          if (strchr(options, 't'))
00175             terminator = '*';
00176          if (strchr(options, 'q'))
00177             option_quiet = 1;
00178       }
00179    }
00180    
00181    /* done parsing */
00182    
00183    /* these are to allow the use of the %d in the config file for a wild card of sort to
00184      create a new file with the inputed name scheme */
00185    if (percentflag) {
00186       do {
00187          snprintf(tmp, sizeof(tmp), filename, count);
00188          count++;
00189       } while ( ast_fileexists(tmp, ext, chan->language) != -1 );
00190       pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
00191    } else
00192       strncpy(tmp, filename, sizeof(tmp)-1);
00193    /* end of routine mentioned */
00194    
00195    
00196    
00197    if (chan->_state != AST_STATE_UP) {
00198       if (option_skip) {
00199          /* At the user's option, skip if the line is not up */
00200          LOCAL_USER_REMOVE(u);
00201          return 0;
00202       } else if (!option_noanswer) {
00203          /* Otherwise answer unless we're supposed to record while on-hook */
00204          res = ast_answer(chan);
00205       }
00206    }
00207    
00208    if (res) {
00209       ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
00210       goto out;
00211    }
00212    
00213    if (!option_quiet) {
00214       /* Some code to play a nice little beep to signify the start of the record operation */
00215       res = ast_streamfile(chan, "beep", chan->language);
00216       if (!res) {
00217          res = ast_waitstream(chan, "");
00218       } else {
00219          ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
00220       }
00221       ast_stopstream(chan);
00222    }
00223       
00224    /* The end of beep code.  Now the recording starts */
00225       
00226    if (silence > 0) {
00227       rfmt = chan->readformat;
00228       res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
00229       if (res < 0) {
00230          ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
00231          LOCAL_USER_REMOVE(u);
00232          return -1;
00233       }
00234       sildet = ast_dsp_new();
00235       if (!sildet) {
00236          ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
00237          LOCAL_USER_REMOVE(u);
00238          return -1;
00239       }
00240       ast_dsp_set_threshold(sildet, 256);
00241    } 
00242       
00243       
00244    flags = option_append ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
00245    s = ast_writefile( tmp, ext, NULL, flags , 0, 0644);
00246       
00247    if (!s) {
00248       ast_log(LOG_WARNING, "Could not create file %s\n", filename);
00249       goto out;
00250    }
00251 
00252    if (option_transmit_silence_during_record)
00253       silgen = ast_channel_start_silence_generator(chan);
00254    
00255    /* Request a video update */
00256    ast_indicate(chan, AST_CONTROL_VIDUPDATE);
00257    
00258    if (maxduration <= 0)
00259       maxduration = -1;
00260    
00261    while ((waitres = ast_waitfor(chan, maxduration)) > -1) {
00262       if (maxduration > 0) {
00263          if (waitres == 0) {
00264             gottimeout = 1;
00265             break;
00266          }
00267          maxduration = waitres;
00268       }
00269       
00270       f = ast_read(chan);
00271       if (!f) {
00272          res = -1;
00273          break;
00274       }
00275       if (f->frametype == AST_FRAME_VOICE) {
00276          res = ast_writestream(s, f);
00277          
00278          if (res) {
00279             ast_log(LOG_WARNING, "Problem writing frame\n");
00280             ast_frfree(f);
00281             break;
00282          }
00283          
00284          if (silence > 0) {
00285             dspsilence = 0;
00286             ast_dsp_silence(sildet, f, &dspsilence);
00287             if (dspsilence) {
00288                totalsilence = dspsilence;
00289             } else {
00290                totalsilence = 0;
00291             }
00292             if (totalsilence > silence) {
00293                /* Ended happily with silence */
00294                ast_frfree(f);
00295                gotsilence = 1;
00296                break;
00297             }
00298          }
00299       } else if (f->frametype == AST_FRAME_VIDEO) {
00300          res = ast_writestream(s, f);
00301          
00302          if (res) {
00303             ast_log(LOG_WARNING, "Problem writing frame\n");
00304             ast_frfree(f);
00305             break;
00306          }
00307       } else if ((f->frametype == AST_FRAME_DTMF) &&
00308           (f->subclass == terminator)) {
00309          ast_frfree(f);
00310          break;
00311       }
00312       ast_frfree(f);
00313    }
00314    if (!f) {
00315       ast_log(LOG_DEBUG, "Got hangup\n");
00316       res = -1;
00317    }
00318          
00319    if (gotsilence) {
00320       ast_stream_rewind(s, silence-1000);
00321       ast_truncstream(s);
00322    } else if (!gottimeout) {
00323       /* Strip off the last 1/4 second of it */
00324       ast_stream_rewind(s, 250);
00325       ast_truncstream(s);
00326    }
00327    ast_closestream(s);
00328 
00329    if (silgen)
00330       ast_channel_stop_silence_generator(chan, silgen);
00331    
00332  out:
00333    if ((silence > 0) && rfmt) {
00334       res = ast_set_read_format(chan, rfmt);
00335       if (res)
00336          ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
00337       if (sildet)
00338          ast_dsp_free(sildet);
00339    }
00340 
00341    LOCAL_USER_REMOVE(u);
00342 
00343    return res;
00344 }
00345 
00346 int unload_module(void)
00347 {
00348    int res;
00349 
00350    res = ast_unregister_application(app);
00351    
00352    STANDARD_HANGUP_LOCALUSERS;
00353 
00354    return res; 
00355 }
00356 
00357 int load_module(void)
00358 {
00359    return ast_register_application(app, record_exec, synopsis, descrip);
00360 }
00361 
00362 char *description(void)
00363 {
00364    return tdesc;
00365 }
00366 
00367 int usecount(void)
00368 {
00369    int res;
00370    STANDARD_USECOUNT(res);
00371    return res;
00372 }
00373 
00374 char *key()
00375 {
00376    return ASTERISK_GPL_KEY;
00377 }

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