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