Wed Aug 18 22:34:00 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"

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 = "a9c98e5d177805051735cb5b0b16b0a0" , .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
static char * descrip
static char * synopsis = "Record to a file"


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 69 of file app_record.c.

00069      {
00070    OPTION_APPEND = (1 << 0),
00071    OPTION_NOANSWER = (1 << 1),
00072    OPTION_QUIET = (1 << 2),
00073    OPTION_SKIP = (1 << 3),
00074    OPTION_STAR_TERMINATE = (1 << 4),
00075    OPTION_IGNORE_TERMINATE = (1 << 5),
00076    OPTION_KEEP = (1 << 6),
00077    FLAG_HAS_PERCENT = (1 << 7),
00078 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 371 of file app_record.c.

static void __unreg_module ( void   )  [static]

Definition at line 371 of file app_record.c.

static int load_module ( void   )  [static]

Definition at line 366 of file app_record.c.

References ast_register_application, and record_exec().

00367 {
00368    return ast_register_application(app, record_exec, synopsis, descrip);
00369 }

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

Definition at line 90 of file app_record.c.

References app_opts, 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, chan, dir, ext, f, FLAG_HAS_PERCENT, ast_flags::flags, ast_channel::language, LOG_WARNING, OPTION_IGNORE_TERMINATE, OPTION_STAR_TERMINATE, parse(), pbx_builtin_setvar_helper(), s, and ast_dsp::totalsilence.

Referenced by load_module().

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

static int unload_module ( void   )  [static]

Definition at line 361 of file app_record.c.

References ast_unregister_application().

00362 {
00363    return ast_unregister_application(app);
00364 }


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

Definition at line 371 of file app_record.c.

char* app = "Record" [static]

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

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 371 of file app_record.c.

char* descrip [static]

Definition at line 44 of file app_record.c.

char* synopsis = "Record to a file" [static]

Definition at line 42 of file app_record.c.


Generated on Wed Aug 18 22:34:00 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7