Fri Nov 12 11:51:22 2010

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"
Include dependency graph for app_record.c:

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)
}

Functions

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

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, }
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 }, [ '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 

Definition at line 111 of file app_record.c.

00111      {
00112    OPTION_APPEND = (1 << 0),
00113    OPTION_NOANSWER = (1 << 1),
00114    OPTION_QUIET = (1 << 2),
00115    OPTION_SKIP = (1 << 3),
00116    OPTION_STAR_TERMINATE = (1 << 4),
00117    OPTION_IGNORE_TERMINATE = (1 << 5),
00118    OPTION_KEEP = (1 << 6),
00119    FLAG_HAS_PERCENT = (1 << 7),
00120 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 426 of file app_record.c.

static void __unreg_module ( void   )  [static]

Definition at line 426 of file app_record.c.

static int load_module ( void   )  [static]

Definition at line 421 of file app_record.c.

References ast_register_application_xml, and record_exec().

00422 {
00423    return ast_register_application_xml(app, record_exec);
00424 }

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

Definition at line 132 of file app_record.c.

References ast_channel::_state, app_opts, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_closestream(), AST_CONTROL_VIDUPDATE, ast_copy_string(), ast_debug, AST_DECLARE_APP_ARGS, ast_dsp_free(), ast_dsp_get_threshold_from_settings(), ast_dsp_new(), ast_dsp_set_threshold(), ast_dsp_silence(), ast_filedelete(), ast_fileexists(), AST_FORMAT_SLINEAR, AST_FRAME_DTMF, AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_frfree, ast_indicate(), ast_log(), ast_mkdir(), AST_NONSTANDARD_APP_ARGS, ast_opt_transmit_silence, ast_read(), ast_set_flag, ast_set_read_format(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stopstream(), ast_stream_rewind(), ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_truncstream(), ast_waitfor(), ast_waitstream(), ast_writefile(), ast_writestream(), dir, ext, f, FLAG_HAS_PERCENT, ast_frame::frametype, ast_channel::language, LOG_WARNING, ast_channel::name, OPTION_APPEND, OPTION_IGNORE_TERMINATE, OPTION_KEEP, OPTION_NOANSWER, OPTION_QUIET, OPTION_SKIP, OPTION_STAR_TERMINATE, parse(), pbx_builtin_setvar_helper(), ast_channel::readformat, s, ast_frame::subclass, THRESHOLD_SILENCE, and ast_dsp::totalsilence.

Referenced by load_module().

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

static int unload_module ( void   )  [static]

Definition at line 416 of file app_record.c.

References ast_unregister_application().

00417 {
00418    return ast_unregister_application(app);
00419 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, } [static]

Definition at line 426 of file app_record.c.

char* app = "Record" [static]

Definition at line 109 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 }, [ 'x' ] = { .flag = OPTION_IGNORE_TERMINATE },} [static]

Definition at line 130 of file app_record.c.

Referenced by record_exec().

Definition at line 426 of file app_record.c.


Generated by  doxygen 1.6.2