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