Sat Mar 10 01:54:36 2012

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 = "88eaa8f5c1bd988bedd71113385e0886" , .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 118 of file app_record.c.

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


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 436 of file app_record.c.

static void __unreg_module ( void   )  [static]

Definition at line 436 of file app_record.c.

static int load_module ( void   )  [static]

Definition at line 431 of file app_record.c.

References ast_register_application_xml, and record_exec().

00432 {
00433    return ast_register_application_xml(app, record_exec);
00434 }

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

Definition at line 141 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().

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

static int unload_module ( void   )  [static]

Definition at line 426 of file app_record.c.

References ast_unregister_application().

00427 {
00428    return ast_unregister_application(app);
00429 }


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 = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static]

Definition at line 436 of file app_record.c.

char* app = "Record" [static]

Definition at line 116 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 139 of file app_record.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 436 of file app_record.c.


Generated on Sat Mar 10 01:54:36 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7