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