Mon Jun 27 16:50:59 2011

Asterisk developer's documentation


app_record.c File Reference

Trivial application to record a sound file. More...

#include "asterisk.h"
#include "asterisk/file.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
#include "asterisk/dsp.h"

Go to the source code of this file.

Enumerations

enum  {
  OPTION_APPEND = (1 << 0), OPTION_NOANSWER = (1 << 1), OPTION_QUIET = (1 << 2), OPTION_SKIP = (1 << 3),
  OPTION_STAR_TERMINATE = (1 << 4), OPTION_IGNORE_TERMINATE = (1 << 5), OPTION_KEEP = (1 << 6), FLAG_HAS_PERCENT = (1 << 7),
  OPTION_ANY_TERMINATE = (1 << 8)
}

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int load_module (void)
static int record_exec (struct ast_channel *chan, const char *data)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Trivial Record Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, }
static char * app = "Record"
static struct ast_app_option app_opts [128] = { [ 'a' ] = { .flag = OPTION_APPEND }, [ 'k' ] = { .flag = OPTION_KEEP }, [ 'n' ] = { .flag = OPTION_NOANSWER }, [ 'q' ] = { .flag = OPTION_QUIET }, [ 's' ] = { .flag = OPTION_SKIP }, [ 't' ] = { .flag = OPTION_STAR_TERMINATE }, [ 'y' ] = { .flag = OPTION_ANY_TERMINATE }, [ 'x' ] = { .flag = OPTION_IGNORE_TERMINATE },}
static struct ast_module_infoast_module_info = &__mod_info


Detailed Description

Trivial application to record a sound file.

Author:
Matthew Fredrickson <creslin@digium.com>

Definition in file app_record.c.


Enumeration Type Documentation

anonymous enum

Enumerator:
OPTION_APPEND 
OPTION_NOANSWER 
OPTION_QUIET 
OPTION_SKIP 
OPTION_STAR_TERMINATE 
OPTION_IGNORE_TERMINATE 
OPTION_KEEP 
FLAG_HAS_PERCENT 
OPTION_ANY_TERMINATE 

Definition at line 114 of file app_record.c.

00114      {
00115    OPTION_APPEND = (1 << 0),
00116    OPTION_NOANSWER = (1 << 1),
00117    OPTION_QUIET = (1 << 2),
00118    OPTION_SKIP = (1 << 3),
00119    OPTION_STAR_TERMINATE = (1 << 4),
00120    OPTION_IGNORE_TERMINATE = (1 << 5),
00121    OPTION_KEEP = (1 << 6),
00122    FLAG_HAS_PERCENT = (1 << 7),
00123    OPTION_ANY_TERMINATE = (1 << 8),
00124 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 432 of file app_record.c.

static void __unreg_module ( void   )  [static]

Definition at line 432 of file app_record.c.

static int load_module ( void   )  [static]

Definition at line 427 of file app_record.c.

References ast_register_application_xml, and record_exec().

00428 {
00429    return ast_register_application_xml(app, record_exec);
00430 }

static int record_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 137 of file app_record.c.

References app_opts, args, AST_APP_ARG, ast_app_parse_options(), ast_copy_string(), AST_DECLARE_APP_ARGS, ast_fileexists(), ast_log(), AST_NONSTANDARD_APP_ARGS, ast_set_flag, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, ext, f, FLAG_HAS_PERCENT, ast_flags::flags, ast_channel::language, LOG_WARNING, OPTION_IGNORE_TERMINATE, OPTION_STAR_TERMINATE, parse(), pbx_builtin_setvar_helper(), and ast_dsp::totalsilence.

Referenced by load_module().

00138 {
00139    int res = 0;
00140    int count = 0;
00141    char *ext = NULL, *opts[0];
00142    char *parse, *dir, *file;
00143    int i = 0;
00144    char tmp[256];
00145 
00146    struct ast_filestream *s = NULL;
00147    struct ast_frame *f = NULL;
00148    
00149    struct ast_dsp *sildet = NULL;      /* silence detector dsp */
00150    int totalsilence = 0;
00151    int dspsilence = 0;
00152    int silence = 0;     /* amount of silence to allow */
00153    int gotsilence = 0;     /* did we timeout for silence? */
00154    int maxduration = 0;    /* max duration of recording in milliseconds */
00155    int gottimeout = 0;     /* did we timeout for maxduration exceeded? */
00156    int terminator = '#';
00157    int rfmt = 0;
00158    int ioflags;
00159    int waitres;
00160    struct ast_silence_generator *silgen = NULL;
00161    struct ast_flags flags = { 0, };
00162    AST_DECLARE_APP_ARGS(args,
00163       AST_APP_ARG(filename);
00164       AST_APP_ARG(silence);
00165       AST_APP_ARG(maxduration);
00166       AST_APP_ARG(options);
00167    );
00168    
00169    /* The next few lines of code parse out the filename and header from the input string */
00170    if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
00171       ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
00172       pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
00173       return -1;
00174    }
00175 
00176    parse = ast_strdupa(data);
00177    AST_STANDARD_APP_ARGS(args, parse);
00178    if (args.argc == 4)
00179       ast_app_parse_options(app_opts, &flags, opts, args.options);
00180 
00181    if (!ast_strlen_zero(args.filename)) {
00182       if (strstr(args.filename, "%d"))
00183          ast_set_flag(&flags, FLAG_HAS_PERCENT);
00184       ext = strrchr(args.filename, '.'); /* to support filename with a . in the filename, not format */
00185       if (!ext)
00186          ext = strchr(args.filename, ':');
00187       if (ext) {
00188          *ext = '\0';
00189          ext++;
00190       }
00191    }
00192    if (!ext) {
00193       ast_log(LOG_WARNING, "No extension specified to filename!\n");
00194       pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
00195       return -1;
00196    }
00197    if (args.silence) {
00198       if ((sscanf(args.silence, "%30d", &i) == 1) && (i > -1)) {
00199          silence = i * 1000;
00200       } else if (!ast_strlen_zero(args.silence)) {
00201          ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", args.silence);
00202       }
00203    }
00204    
00205    if (args.maxduration) {
00206       if ((sscanf(args.maxduration, "%30d", &i) == 1) && (i > -1))
00207          /* Convert duration to milliseconds */
00208          maxduration = i * 1000;
00209       else if (!ast_strlen_zero(args.maxduration))
00210          ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", args.maxduration);
00211    }
00212 
00213    if (ast_test_flag(&flags, OPTION_STAR_TERMINATE))
00214       terminator = '*';
00215    if (ast_test_flag(&flags, OPTION_IGNORE_TERMINATE))
00216       terminator = '\0';
00217 
00218    /* done parsing */
00219 
00220    /* these are to allow the use of the %d in the config file for a wild card of sort to
00221      create a new file with the inputed name scheme */
00222    if (ast_test_flag(&flags, FLAG_HAS_PERCENT)) {
00223       AST_DECLARE_APP_ARGS(fname,
00224          AST_APP_ARG(piece)[100];
00225       );
00226       char *tmp2 = ast_strdupa(args.filename);
00227       char countstring[15];
00228       int idx;
00229 
00230       /* Separate each piece out by the format specifier */
00231       AST_NONSTANDARD_APP_ARGS(fname, tmp2, '%');
00232       do {
00233          int tmplen;
00234          /* First piece has no leading percent, so it's copied verbatim */
00235          ast_copy_string(tmp, fname.piece[0], sizeof(tmp));
00236          tmplen = strlen(tmp);
00237          for (idx = 1; idx < fname.argc; idx++) {
00238             if (fname.piece[idx][0] == 'd') {
00239                /* Substitute the count */
00240                snprintf(countstring, sizeof(countstring), "%d", count);
00241                ast_copy_string(tmp + tmplen, countstring, sizeof(tmp) - tmplen);
00242                tmplen += strlen(countstring);
00243             } else if (tmplen + 2 < sizeof(tmp)) {
00244                /* Unknown format specifier - just copy it verbatim */
00245                tmp[tmplen++] = '%';
00246                tmp[tmplen++] = fname.piece[idx][0];
00247             }
00248             /* Copy the remaining portion of the piece */
00249             ast_copy_string(tmp + tmplen, &(fname.piece[idx][1]), sizeof(tmp) - tmplen);
00250          }
00251          count++;
00252       } while (ast_fileexists(tmp, ext, chan->language) > 0);
00253       pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
00254    } else
00255       ast_copy_string(tmp, args.filename, sizeof(tmp));
00256    /* end of routine mentioned */
00257 
00258    if (chan->_state != AST_STATE_UP) {
00259       if (ast_test_flag(&flags, OPTION_SKIP)) {
00260          /* At the user's option, skip if the line is not up */
00261          pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "SKIP");
00262          return 0;
00263       } else if (!ast_test_flag(&flags, OPTION_NOANSWER)) {
00264          /* Otherwise answer unless we're supposed to record while on-hook */
00265          res = ast_answer(chan);
00266       }
00267    }
00268 
00269    if (res) {
00270       ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
00271       pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
00272       goto out;
00273    }
00274 
00275    if (!ast_test_flag(&flags, OPTION_QUIET)) {
00276       /* Some code to play a nice little beep to signify the start of the record operation */
00277       res = ast_streamfile(chan, "beep", chan->language);
00278       if (!res) {
00279          res = ast_waitstream(chan, "");
00280       } else {
00281          ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
00282       }
00283       ast_stopstream(chan);
00284    }
00285 
00286    /* The end of beep code.  Now the recording starts */
00287 
00288    if (silence > 0) {
00289       rfmt = chan->readformat;
00290       res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
00291       if (res < 0) {
00292          ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
00293          pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
00294          return -1;
00295       }
00296       sildet = ast_dsp_new();
00297       if (!sildet) {
00298          ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
00299          pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
00300          return -1;
00301       }
00302       ast_dsp_set_threshold(sildet, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE));
00303    } 
00304 
00305    /* Create the directory if it does not exist. */
00306    dir = ast_strdupa(tmp);
00307    if ((file = strrchr(dir, '/')))
00308       *file++ = '\0';
00309    ast_mkdir (dir, 0777);
00310 
00311    ioflags = ast_test_flag(&flags, OPTION_APPEND) ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
00312    s = ast_writefile(tmp, ext, NULL, ioflags, 0, AST_FILE_MODE);
00313 
00314    if (!s) {
00315       ast_log(LOG_WARNING, "Could not create file %s\n", args.filename);
00316       pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
00317       goto out;
00318    }
00319 
00320    if (ast_opt_transmit_silence)
00321       silgen = ast_channel_start_silence_generator(chan);
00322 
00323    /* Request a video update */
00324    ast_indicate(chan, AST_CONTROL_VIDUPDATE);
00325 
00326    if (maxduration <= 0)
00327       maxduration = -1;
00328 
00329    while ((waitres = ast_waitfor(chan, maxduration)) > -1) {
00330       if (maxduration > 0) {
00331          if (waitres == 0) {
00332             gottimeout = 1;
00333             pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "TIMEOUT");
00334             break;
00335          }
00336          maxduration = waitres;
00337       }
00338 
00339       f = ast_read(chan);
00340       if (!f) {
00341          res = -1;
00342          break;
00343       }
00344       if (f->frametype == AST_FRAME_VOICE) {
00345          res = ast_writestream(s, f);
00346 
00347          if (res) {
00348             ast_log(LOG_WARNING, "Problem writing frame\n");
00349             ast_frfree(f);
00350             pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
00351             break;
00352          }
00353 
00354          if (silence > 0) {
00355             dspsilence = 0;
00356             ast_dsp_silence(sildet, f, &dspsilence);
00357             if (dspsilence) {
00358                totalsilence = dspsilence;
00359             } else {
00360                totalsilence = 0;
00361             }
00362             if (totalsilence > silence) {
00363                /* Ended happily with silence */
00364                ast_frfree(f);
00365                gotsilence = 1;
00366                pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "SILENCE");
00367                break;
00368             }
00369          }
00370       } else if (f->frametype == AST_FRAME_VIDEO) {
00371          res = ast_writestream(s, f);
00372 
00373          if (res) {
00374             ast_log(LOG_WARNING, "Problem writing frame\n");
00375             pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
00376             ast_frfree(f);
00377             break;
00378          }
00379       } else if ((f->frametype == AST_FRAME_DTMF) &&
00380             ((f->subclass.integer == terminator) ||
00381              (ast_test_flag(&flags, OPTION_ANY_TERMINATE)))) {
00382          ast_frfree(f);
00383          pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "DTMF");
00384          break;
00385       }
00386       ast_frfree(f);
00387    }
00388    if (!f) {
00389       ast_debug(1, "Got hangup\n");
00390       res = -1;
00391       pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "HANGUP");
00392       if (!ast_test_flag(&flags, OPTION_KEEP)) {
00393          ast_filedelete(args.filename, NULL);
00394       }
00395    }
00396 
00397    if (gotsilence) {
00398       ast_stream_rewind(s, silence - 1000);
00399       ast_truncstream(s);
00400    } else if (!gottimeout) {
00401       /* Strip off the last 1/4 second of it */
00402       ast_stream_rewind(s, 250);
00403       ast_truncstream(s);
00404    }
00405    ast_closestream(s);
00406 
00407    if (silgen)
00408       ast_channel_stop_silence_generator(chan, silgen);
00409 
00410 out:
00411    if ((silence > 0) && rfmt) {
00412       res = ast_set_read_format(chan, rfmt);
00413       if (res)
00414          ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
00415       if (sildet)
00416          ast_dsp_free(sildet);
00417    }
00418 
00419    return res;
00420 }

static int unload_module ( void   )  [static]

Definition at line 422 of file app_record.c.

References ast_unregister_application().

00423 {
00424    return ast_unregister_application(app);
00425 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Trivial Record Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static]

Definition at line 432 of file app_record.c.

char* app = "Record" [static]

Definition at line 112 of file app_record.c.

struct ast_app_option app_opts[128] = { [ 'a' ] = { .flag = OPTION_APPEND }, [ 'k' ] = { .flag = OPTION_KEEP }, [ 'n' ] = { .flag = OPTION_NOANSWER }, [ 'q' ] = { .flag = OPTION_QUIET }, [ 's' ] = { .flag = OPTION_SKIP }, [ 't' ] = { .flag = OPTION_STAR_TERMINATE }, [ 'y' ] = { .flag = OPTION_ANY_TERMINATE }, [ 'x' ] = { .flag = OPTION_IGNORE_TERMINATE },} [static]

Definition at line 135 of file app_record.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 432 of file app_record.c.


Generated on Mon Jun 27 16:50:59 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7