Fri Sep 29 11:12:52 2006

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

00391 {
00392    return tdesc;
00393 }

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

References ASTERISK_GPL_KEY.

00403 {
00404    return ASTERISK_GPL_KEY;
00405 }

int load_module ( void   ) 

Initialize the module.

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.

Definition at line 385 of file app_record.c.

References ast_register_application(), and record_exec().

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

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

Definition at line 78 of file app_record.c.

References AST_APP_ARG, ast_app_separate_args(), AST_DECLARE_APP_ARGS, ast_fileexists(), ast_log(), ast_strdupa, ast_strlen_zero(), ast_channel::language, LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_ERROR, LOG_WARNING, option_quiet, pbx_builtin_setvar_helper(), s, silence, strsep(), and ast_dsp::totalsilence.

Referenced by load_module().

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

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

References ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS.

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

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

References STANDARD_USECOUNT.

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


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

STANDARD_LOCAL_USER

Definition at line 74 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.


Generated on Fri Sep 29 11:12:53 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7