Thu Jul 9 13:40:48 2009

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 = "068e67f60f50dd9ee86464c05884a49d" , .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 const 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 68 of file app_record.c.

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


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 370 of file app_record.c.

static void __unreg_module ( void   )  [static]

Definition at line 370 of file app_record.c.

static int load_module ( void   )  [static]

Definition at line 365 of file app_record.c.

References ast_register_application, and record_exec().

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

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

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

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

static int unload_module ( void   )  [static]

Definition at line 360 of file app_record.c.

References ast_unregister_application().

00361 {
00362    return ast_unregister_application(app);
00363 }


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

Definition at line 370 of file app_record.c.

char* app = "Record" [static]

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

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 370 of file app_record.c.

char* descrip [static]

Definition at line 43 of file app_record.c.

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

Definition at line 41 of file app_record.c.


Generated on Thu Jul 9 13:40:48 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7