Trivial application to record a sound file. More...
#include "asterisk.h"
#include "asterisk/file.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
#include "asterisk/dsp.h"
Go to the source code of this file.
Enumerations | |
enum | { OPTION_APPEND = (1 << 0), OPTION_NOANSWER = (1 << 1), OPTION_QUIET = (1 << 2), OPTION_SKIP = (1 << 3), OPTION_STAR_TERMINATE = (1 << 4), OPTION_IGNORE_TERMINATE = (1 << 5), OPTION_KEEP = (1 << 6), FLAG_HAS_PERCENT = (1 << 7), OPTION_ANY_TERMINATE = (1 << 8) } |
Functions | |
AST_APP_OPTIONS (app_opts,{AST_APP_OPTION('a', OPTION_APPEND), AST_APP_OPTION('k', OPTION_KEEP), AST_APP_OPTION('n', OPTION_NOANSWER), AST_APP_OPTION('q', OPTION_QUIET), AST_APP_OPTION('s', OPTION_SKIP), AST_APP_OPTION('t', OPTION_STAR_TERMINATE), AST_APP_OPTION('y', OPTION_ANY_TERMINATE), AST_APP_OPTION('x', OPTION_IGNORE_TERMINATE),}) | |
AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY,"Trivial Record Application") | |
static int | load_module (void) |
static int | record_exec (struct ast_channel *chan, const char *data) |
static int | unload_module (void) |
Variables | |
static char * | app = "Record" |
Trivial application to record a sound file.
Definition in file app_record.c.
anonymous enum |
OPTION_APPEND | |
OPTION_NOANSWER | |
OPTION_QUIET | |
OPTION_SKIP | |
OPTION_STAR_TERMINATE | |
OPTION_IGNORE_TERMINATE | |
OPTION_KEEP | |
FLAG_HAS_PERCENT | |
OPTION_ANY_TERMINATE |
Definition at line 118 of file app_record.c.
00118 { 00119 OPTION_APPEND = (1 << 0), 00120 OPTION_NOANSWER = (1 << 1), 00121 OPTION_QUIET = (1 << 2), 00122 OPTION_SKIP = (1 << 3), 00123 OPTION_STAR_TERMINATE = (1 << 4), 00124 OPTION_IGNORE_TERMINATE = (1 << 5), 00125 OPTION_KEEP = (1 << 6), 00126 FLAG_HAS_PERCENT = (1 << 7), 00127 OPTION_ANY_TERMINATE = (1 << 8), 00128 };
AST_APP_OPTIONS | ( | app_opts | ) |
AST_MODULE_INFO_STANDARD | ( | ASTERISK_GPL_KEY | , | |
"Trivial Record Application" | ||||
) |
static int load_module | ( | void | ) | [static] |
Definition at line 441 of file app_record.c.
References ast_register_application_xml, and record_exec().
00442 { 00443 return ast_register_application_xml(app, record_exec); 00444 }
static int record_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 141 of file app_record.c.
References ast_channel::_state, args, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_closestream(), AST_CONTROL_VIDUPDATE, ast_copy_string(), ast_debug, AST_DECLARE_APP_ARGS, ast_dsp_free(), ast_dsp_get_threshold_from_settings(), ast_dsp_new(), ast_dsp_set_threshold(), ast_dsp_silence(), AST_FILE_MODE, ast_filedelete(), ast_fileexists(), AST_FORMAT_SLINEAR, AST_FRAME_DTMF, AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_frfree, ast_indicate(), ast_log(), ast_mkdir(), AST_NONSTANDARD_APP_ARGS, ast_opt_transmit_silence, ast_read(), ast_remaining_ms(), ast_set_flag, ast_set_read_format(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stopstream(), ast_strdupa, ast_stream_rewind(), ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_truncstream(), ast_tvnow(), ast_waitfor(), ast_waitstream(), ast_writefile(), ast_writestream(), ext, f, FLAG_HAS_PERCENT, ast_frame::frametype, ast_frame_subclass::integer, LOG_WARNING, OPTION_ANY_TERMINATE, OPTION_APPEND, OPTION_IGNORE_TERMINATE, OPTION_KEEP, OPTION_NOANSWER, OPTION_QUIET, OPTION_SKIP, OPTION_STAR_TERMINATE, parse(), pbx_builtin_setvar_helper(), ast_channel::readformat, ast_frame::subclass, THRESHOLD_SILENCE, and ast_dsp::totalsilence.
Referenced by load_module().
00142 { 00143 int res = 0; 00144 int count = 0; 00145 char *ext = NULL, *opts[0]; 00146 char *parse, *dir, *file; 00147 int i = 0; 00148 char tmp[256]; 00149 00150 struct ast_filestream *s = NULL; 00151 struct ast_frame *f = NULL; 00152 00153 struct ast_dsp *sildet = NULL; /* silence detector dsp */ 00154 int totalsilence = 0; 00155 int dspsilence = 0; 00156 int silence = 0; /* amount of silence to allow */ 00157 int gotsilence = 0; /* did we timeout for silence? */ 00158 int maxduration = 0; /* max duration of recording in milliseconds */ 00159 int gottimeout = 0; /* did we timeout for maxduration exceeded? */ 00160 int terminator = '#'; 00161 int rfmt = 0; 00162 int ioflags; 00163 struct ast_silence_generator *silgen = NULL; 00164 struct ast_flags flags = { 0, }; 00165 AST_DECLARE_APP_ARGS(args, 00166 AST_APP_ARG(filename); 00167 AST_APP_ARG(silence); 00168 AST_APP_ARG(maxduration); 00169 AST_APP_ARG(options); 00170 ); 00171 int ms; 00172 struct timeval start; 00173 00174 /* The next few lines of code parse out the filename and header from the input string */ 00175 if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */ 00176 ast_log(LOG_WARNING, "Record requires an argument (filename)\n"); 00177 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); 00178 return -1; 00179 } 00180 00181 parse = ast_strdupa(data); 00182 AST_STANDARD_APP_ARGS(args, parse); 00183 if (args.argc == 4) 00184 ast_app_parse_options(app_opts, &flags, opts, args.options); 00185 00186 if (!ast_strlen_zero(args.filename)) { 00187 if (strstr(args.filename, "%d")) 00188 ast_set_flag(&flags, FLAG_HAS_PERCENT); 00189 ext = strrchr(args.filename, '.'); /* to support filename with a . in the filename, not format */ 00190 if (!ext) 00191 ext = strchr(args.filename, ':'); 00192 if (ext) { 00193 *ext = '\0'; 00194 ext++; 00195 } 00196 } 00197 if (!ext) { 00198 ast_log(LOG_WARNING, "No extension specified to filename!\n"); 00199 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); 00200 return -1; 00201 } 00202 if (args.silence) { 00203 if ((sscanf(args.silence, "%30d", &i) == 1) && (i > -1)) { 00204 silence = i * 1000; 00205 } else if (!ast_strlen_zero(args.silence)) { 00206 ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", args.silence); 00207 } 00208 } 00209 00210 if (args.maxduration) { 00211 if ((sscanf(args.maxduration, "%30d", &i) == 1) && (i > -1)) 00212 /* Convert duration to milliseconds */ 00213 maxduration = i * 1000; 00214 else if (!ast_strlen_zero(args.maxduration)) 00215 ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", args.maxduration); 00216 } 00217 00218 if (ast_test_flag(&flags, OPTION_STAR_TERMINATE)) 00219 terminator = '*'; 00220 if (ast_test_flag(&flags, OPTION_IGNORE_TERMINATE)) 00221 terminator = '\0'; 00222 00223 /* done parsing */ 00224 00225 /* these are to allow the use of the %d in the config file for a wild card of sort to 00226 create a new file with the inputed name scheme */ 00227 if (ast_test_flag(&flags, FLAG_HAS_PERCENT)) { 00228 AST_DECLARE_APP_ARGS(fname, 00229 AST_APP_ARG(piece)[100]; 00230 ); 00231 char *tmp2 = ast_strdupa(args.filename); 00232 char countstring[15]; 00233 int idx; 00234 00235 /* Separate each piece out by the format specifier */ 00236 AST_NONSTANDARD_APP_ARGS(fname, tmp2, '%'); 00237 do { 00238 int tmplen; 00239 /* First piece has no leading percent, so it's copied verbatim */ 00240 ast_copy_string(tmp, fname.piece[0], sizeof(tmp)); 00241 tmplen = strlen(tmp); 00242 for (idx = 1; idx < fname.argc; idx++) { 00243 if (fname.piece[idx][0] == 'd') { 00244 /* Substitute the count */ 00245 snprintf(countstring, sizeof(countstring), "%d", count); 00246 ast_copy_string(tmp + tmplen, countstring, sizeof(tmp) - tmplen); 00247 tmplen += strlen(countstring); 00248 } else if (tmplen + 2 < sizeof(tmp)) { 00249 /* Unknown format specifier - just copy it verbatim */ 00250 tmp[tmplen++] = '%'; 00251 tmp[tmplen++] = fname.piece[idx][0]; 00252 } 00253 /* Copy the remaining portion of the piece */ 00254 ast_copy_string(tmp + tmplen, &(fname.piece[idx][1]), sizeof(tmp) - tmplen); 00255 } 00256 count++; 00257 } while (ast_fileexists(tmp, ext, chan->language) > 0); 00258 pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp); 00259 } else 00260 ast_copy_string(tmp, args.filename, sizeof(tmp)); 00261 /* end of routine mentioned */ 00262 00263 if (chan->_state != AST_STATE_UP) { 00264 if (ast_test_flag(&flags, OPTION_SKIP)) { 00265 /* At the user's option, skip if the line is not up */ 00266 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "SKIP"); 00267 return 0; 00268 } else if (!ast_test_flag(&flags, OPTION_NOANSWER)) { 00269 /* Otherwise answer unless we're supposed to record while on-hook */ 00270 res = ast_answer(chan); 00271 } 00272 } 00273 00274 if (res) { 00275 ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); 00276 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); 00277 goto out; 00278 } 00279 00280 if (!ast_test_flag(&flags, OPTION_QUIET)) { 00281 /* Some code to play a nice little beep to signify the start of the record operation */ 00282 res = ast_streamfile(chan, "beep", chan->language); 00283 if (!res) { 00284 res = ast_waitstream(chan, ""); 00285 } else { 00286 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name); 00287 } 00288 ast_stopstream(chan); 00289 } 00290 00291 /* The end of beep code. Now the recording starts */ 00292 00293 if (silence > 0) { 00294 rfmt = chan->readformat; 00295 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); 00296 if (res < 0) { 00297 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); 00298 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); 00299 return -1; 00300 } 00301 sildet = ast_dsp_new(); 00302 if (!sildet) { 00303 ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); 00304 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); 00305 return -1; 00306 } 00307 ast_dsp_set_threshold(sildet, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE)); 00308 } 00309 00310 /* Create the directory if it does not exist. */ 00311 dir = ast_strdupa(tmp); 00312 if ((file = strrchr(dir, '/'))) 00313 *file++ = '\0'; 00314 ast_mkdir (dir, 0777); 00315 00316 ioflags = ast_test_flag(&flags, OPTION_APPEND) ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY; 00317 s = ast_writefile(tmp, ext, NULL, ioflags, 0, AST_FILE_MODE); 00318 00319 if (!s) { 00320 ast_log(LOG_WARNING, "Could not create file %s\n", args.filename); 00321 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); 00322 goto out; 00323 } 00324 00325 if (ast_opt_transmit_silence) 00326 silgen = ast_channel_start_silence_generator(chan); 00327 00328 /* Request a video update */ 00329 ast_indicate(chan, AST_CONTROL_VIDUPDATE); 00330 00331 if (maxduration <= 0) 00332 maxduration = -1; 00333 00334 start = ast_tvnow(); 00335 while ((ms = ast_remaining_ms(start, maxduration))) { 00336 ms = ast_waitfor(chan, ms); 00337 if (ms < 0) { 00338 break; 00339 } 00340 00341 if (maxduration > 0 && ms == 0) { 00342 break; 00343 } 00344 00345 f = ast_read(chan); 00346 if (!f) { 00347 res = -1; 00348 break; 00349 } 00350 if (f->frametype == AST_FRAME_VOICE) { 00351 res = ast_writestream(s, f); 00352 00353 if (res) { 00354 ast_log(LOG_WARNING, "Problem writing frame\n"); 00355 ast_frfree(f); 00356 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); 00357 break; 00358 } 00359 00360 if (silence > 0) { 00361 dspsilence = 0; 00362 ast_dsp_silence(sildet, f, &dspsilence); 00363 if (dspsilence) { 00364 totalsilence = dspsilence; 00365 } else { 00366 totalsilence = 0; 00367 } 00368 if (totalsilence > silence) { 00369 /* Ended happily with silence */ 00370 ast_frfree(f); 00371 gotsilence = 1; 00372 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "SILENCE"); 00373 break; 00374 } 00375 } 00376 } else if (f->frametype == AST_FRAME_VIDEO) { 00377 res = ast_writestream(s, f); 00378 00379 if (res) { 00380 ast_log(LOG_WARNING, "Problem writing frame\n"); 00381 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); 00382 ast_frfree(f); 00383 break; 00384 } 00385 } else if ((f->frametype == AST_FRAME_DTMF) && 00386 ((f->subclass.integer == terminator) || 00387 (ast_test_flag(&flags, OPTION_ANY_TERMINATE)))) { 00388 ast_frfree(f); 00389 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "DTMF"); 00390 break; 00391 } 00392 ast_frfree(f); 00393 } 00394 00395 if (maxduration > 0 && !ms) { 00396 gottimeout = 1; 00397 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "TIMEOUT"); 00398 } 00399 00400 if (!f) { 00401 ast_debug(1, "Got hangup\n"); 00402 res = -1; 00403 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "HANGUP"); 00404 if (!ast_test_flag(&flags, OPTION_KEEP)) { 00405 ast_filedelete(args.filename, NULL); 00406 } 00407 } 00408 00409 if (gotsilence) { 00410 ast_stream_rewind(s, silence - 1000); 00411 ast_truncstream(s); 00412 } else if (!gottimeout) { 00413 /* Strip off the last 1/4 second of it */ 00414 ast_stream_rewind(s, 250); 00415 ast_truncstream(s); 00416 } 00417 ast_closestream(s); 00418 00419 if (silgen) 00420 ast_channel_stop_silence_generator(chan, silgen); 00421 00422 out: 00423 if ((silence > 0) && rfmt) { 00424 res = ast_set_read_format(chan, rfmt); 00425 if (res) { 00426 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); 00427 } 00428 } 00429 00430 if (sildet) { 00431 ast_dsp_free(sildet); 00432 } 00433 return res; 00434 }
static int unload_module | ( | void | ) | [static] |
Definition at line 436 of file app_record.c.
References ast_unregister_application().
00437 { 00438 return ast_unregister_application(app); 00439 }
char* app = "Record" [static] |
Definition at line 116 of file app_record.c.