Sat Aug 6 00:39:21 2011

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

Generated on Sat Aug 6 00:39:21 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7