Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:48:12 2009

## Asterisk developer's documentation

# app_record.c File Reference

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

`#include <stdlib.h>`
`#include <stdio.h>`
`#include <string.h>`
`#include "asterisk.h"`
`#include "asterisk/lock.h"`
`#include "asterisk/file.h"`
`#include "asterisk/logger.h"`
`#include "asterisk/channel.h"`
`#include "asterisk/pbx.h"`
`#include "asterisk/module.h"`
`#include "asterisk/translate.h"`
`#include "asterisk/dsp.h"`
`#include "asterisk/utils.h"`
`#include "asterisk/options.h"`
`#include "asterisk/app.h"`

Include dependency graph for app_record.c:

Go to the source code of this file.

## Functions

char * | description (void)
| Provides a description of the module.
char * | key ()
| Returns the ASTERISK_GPL_KEY.
int | load_module (void)
| Initialize the module.
static int | record_exec (struct ast_channel *chan, void *data)
int | unload_module (void)
| Cleanup all module structures, sockets, etc.
int | usecount (void)
| Provides a usecount.

## Variables

static char * | app = "Record"
static char * | descrip
| LOCAL_USER_DECL
| STANDARD_LOCAL_USER
static char * | synopsis = "Record to a file"
static char * | tdesc = "Trivial Record Application"

## Detailed Description

Trivial application to record a sound file.

Definition in file app_record.c.

## Function Documentation

char* description | ( | void |  | ) |

Provides a description of the module.

Returns:
  a short description of your module

Definition at line 391 of file app_record.c.

    00392 {
    00393    return tdesc;
    00394 }

char* key | ( | void |  | ) |

Returns the ASTERISK_GPL_KEY.

This returns the ASTERISK_GPL_KEY, signifiying that you agree to the terms of the GPL stated in the ASTERISK_GPL_KEY. Your module will not load if it does not return the EXACT message:

     char *key(void) {
             return ASTERISK_GPL_KEY;
     }

Returns:
  ASTERISK_GPL_KEY

Definition at line 403 of file app_record.c.

References ASTERISK_GPL_KEY.

    00404 {
    00405    return ASTERISK_GPL_KEY;
    00406 }

int load_module | ( | void |  | ) |

Initialize the module.

This function is called at module load time. Put all code in here that needs to set up your module's hardware, software, registrations, etc.

Returns:
  This function should return 0 on success and non-zero on failure. If the module is not loaded successfully, Asterisk will call its unload_module() function.

Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.

Returns:
  int Always 0.

TE STUFF END

Definition at line 386 of file app_record.c.

References ast_register_application(), and record_exec().

    00387 {
    00388    return ast_register_application(app, record_exec, synopsis, descrip);
    00389 }

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

Definition at line 79 of file app_record.c.

References ast_channel::_state, ast_answer(), AST_APP_ARG, ast_app_separate_args(), ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_closestream(), AST_CONTROL_VIDUPDATE, AST_DECLARE_APP_ARGS, ast_dsp_free(), ast_dsp_new(), ast_dsp_set_threshold(), ast_dsp_silence(), ast_fileexists(), AST_FORMAT_SLINEAR, AST_FRAME_DTMF, AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_frfree(), ast_indicate(), ast_log(), ast_read(), ast_set_read_format(), AST_STATE_UP, ast_stopstream(), ast_strdupa, ast_stream_rewind(), ast_streamfile(), ast_strlen_zero(), ast_truncstream(), ast_waitfor(), ast_waitstream(), ast_writefile(), ast_writestream(), ast_frame::frametype, ast_channel::language, LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_DEBUG, LOG_ERROR, LOG_WARNING, ast_channel::name, option_quiet, option_transmit_silence_during_record, pbx_builtin_setvar_helper(), ast_channel::readformat, s, silence, strsep(), ast_frame::subclass, and ast_dsp::totalsilence.

Referenced by load_module().

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

int unload_module | ( | void |  | ) |

Cleanup all module structures, sockets, etc.

This is called at exit. Any registrations and memory allocations need to be unregistered and free'd here. Nothing else will do these for you (until exit).

Returns:
  Zero on success, or non-zero on error.

Definition at line 375 of file app_record.c.

References ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS.

    00376 {
    00377    int res;
    00378
    00379    res = ast_unregister_application(app);
    00380
    00381    STANDARD_HANGUP_LOCALUSERS;
    00382
    00383    return res;
    00384 }

int usecount | ( | void |  | ) |

Provides a usecount.

This function will be called by various parts of asterisk. Basically, all it has to do is to return a usecount when called. You will need to maintain your usecount within the module somewhere. The usecount should be how many channels provided by this module are in use.

Returns:
  The module's usecount.

Definition at line 396 of file app_record.c.

References STANDARD_USECOUNT.

    00397 {
    00398    int res;
    00399    STANDARD_USECOUNT(res);
    00400    return res;
    00401 }

## Variable Documentation

char* app = "Record"`[static]`

Definition at line 48 of file app_record.c.

char* descrip`[static]`

Definition at line 52 of file app_record.c.

LOCAL_USER_DECL

Definition at line 77 of file app_record.c.

STANDARD_LOCAL_USER

Definition at line 75 of file app_record.c.

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

Definition at line 50 of file app_record.c.

char* tdesc = "Trivial Record Application"`[static]`

Definition at line 46 of file app_record.c.