#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 | |
AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY,"Trivial Record Application") | |
static int | load_module (void) |
static int | record_exec (struct ast_channel *chan, void *data) |
static int | unload_module (void) |
Variables | |
static char * | app = "Record" |
static char * | descrip |
static char * | synopsis = "Record to a file" |
Definition in file app_record.c.
AST_MODULE_INFO_STANDARD | ( | ASTERISK_GPL_KEY | , | |
"Trivial Record Application" | ||||
) |
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_channel::_state, ast_answer(), AST_APP_ARG, 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_module_user_add, ast_module_user_remove, AST_NONSTANDARD_APP_ARGS, ast_opt_transmit_silence, 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(), ext, f, ast_frame::frametype, LOG_DEBUG, LOG_WARNING, pbx_builtin_setvar_helper(), ast_channel::readformat, s, silence, strsep(), ast_frame::subclass, 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, "%d", &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, "%d", &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 }
char* app = "Record" [static] |
Definition at line 49 of file app_record.c.
char* descrip [static] |
Definition at line 53 of file app_record.c.
Definition at line 51 of file app_record.c.