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