00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 251683 $")
00031
00032 #include "asterisk/file.h"
00033 #include "asterisk/pbx.h"
00034 #include "asterisk/module.h"
00035 #include "asterisk/app.h"
00036 #include "asterisk/channel.h"
00037 #include "asterisk/dsp.h"
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109 static char *app = "Record";
00110
00111 enum {
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 };
00121
00122 AST_APP_OPTIONS(app_opts,{
00123 AST_APP_OPTION('a', OPTION_APPEND),
00124 AST_APP_OPTION('k', OPTION_KEEP),
00125 AST_APP_OPTION('n', OPTION_NOANSWER),
00126 AST_APP_OPTION('q', OPTION_QUIET),
00127 AST_APP_OPTION('s', OPTION_SKIP),
00128 AST_APP_OPTION('t', OPTION_STAR_TERMINATE),
00129 AST_APP_OPTION('x', OPTION_IGNORE_TERMINATE),
00130 });
00131
00132 static int record_exec(struct ast_channel *chan, void *data)
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;
00145 int totalsilence = 0;
00146 int dspsilence = 0;
00147 int silence = 0;
00148 int gotsilence = 0;
00149 int maxduration = 0;
00150 int gottimeout = 0;
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
00165 if (ast_strlen_zero(data)) {
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, '.');
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
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
00214
00215
00216
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
00226 AST_NONSTANDARD_APP_ARGS(fname, tmp2, '%');
00227 do {
00228 int tmplen;
00229
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
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
00240 tmp[tmplen++] = '%';
00241 tmp[tmplen++] = fname.piece[idx][0];
00242 }
00243
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
00252
00253 if (chan->_state != AST_STATE_UP) {
00254 if (ast_test_flag(&flags, OPTION_SKIP)) {
00255
00256 pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "SKIP");
00257 return 0;
00258 } else if (!ast_test_flag(&flags, OPTION_NOANSWER)) {
00259
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
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
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
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
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
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
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 }
00415
00416 static int unload_module(void)
00417 {
00418 return ast_unregister_application(app);
00419 }
00420
00421 static int load_module(void)
00422 {
00423 return ast_register_application_xml(app, record_exec);
00424 }
00425
00426 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial Record Application");