Sat Mar 24 23:26:36 2007

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

00363 {
00364    return tdesc;
00365 }

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

References ASTERISK_GPL_KEY.

00375 {
00376    return ASTERISK_GPL_KEY;
00377 }

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

References ast_register_application(), and record_exec().

00358 {
00359    return ast_register_application(app, record_exec, synopsis, descrip);
00360 }

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

Definition at line 77 of file app_record.c.

References ast_channel::_state, ast_answer(), ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_closestream(), AST_CONTROL_VIDUPDATE, 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().

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

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

References ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS.

00347 {
00348    int res;
00349 
00350    res = ast_unregister_application(app);
00351    
00352    STANDARD_HANGUP_LOCALUSERS;
00353 
00354    return res; 
00355 }

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

References STANDARD_USECOUNT.

00368 {
00369    int res;
00370    STANDARD_USECOUNT(res);
00371    return res;
00372 }


Variable Documentation

char* app = "Record" [static]
 

Definition at line 47 of file app_record.c.

char* descrip [static]
 

Definition at line 51 of file app_record.c.

LOCAL_USER_DECL
 

Definition at line 75 of file app_record.c.

STANDARD_LOCAL_USER
 

Definition at line 73 of file app_record.c.

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

Definition at line 49 of file app_record.c.

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

Definition at line 45 of file app_record.c.


Generated on Sat Mar 24 23:26:36 2007 for Asterisk - the Open Source PBX by  doxygen 1.4.6