Sat Aug 6 00:39:36 2011

Asterisk developer's documentation


app_record.c File Reference

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

#include "asterisk.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.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"

Go to the source code of this file.

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 | AST_MODFLAG_BUILDSUM, .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, }
static char * app = "Record"
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.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 386 of file app_record.c.

static void __unreg_module ( void   )  [static]

Definition at line 386 of file app_record.c.

static int load_module ( void   )  [static]

Definition at line 381 of file app_record.c.

References ast_register_application(), and record_exec().

00382 {
00383    return ast_register_application(app, record_exec, synopsis, descrip);
00384 }

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

static int unload_module ( void   )  [static]

Definition at line 370 of file app_record.c.

References ast_module_user_hangup_all, and ast_unregister_application().

00371 {
00372    int res;
00373 
00374    res = ast_unregister_application(app);
00375    
00376    ast_module_user_hangup_all();
00377 
00378    return res; 
00379 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, } [static]

Definition at line 386 of file app_record.c.

char* app = "Record" [static]

Definition at line 49 of file app_record.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 386 of file app_record.c.

char* descrip [static]

Definition at line 53 of file app_record.c.

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

Definition at line 51 of file app_record.c.


Generated on Sat Aug 6 00:39:36 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7