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 #include <stdlib.h>
00027 #include <stdio.h>
00028 #include <string.h>
00029
00030 #include "asterisk.h"
00031
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 42355 $")
00033
00034 #include "asterisk/lock.h"
00035 #include "asterisk/file.h"
00036 #include "asterisk/logger.h"
00037 #include "asterisk/channel.h"
00038 #include "asterisk/pbx.h"
00039 #include "asterisk/module.h"
00040 #include "asterisk/translate.h"
00041 #include "asterisk/dsp.h"
00042 #include "asterisk/utils.h"
00043 #include "asterisk/options.h"
00044 #include "asterisk/app.h"
00045
00046 static char *tdesc = "Trivial Record Application";
00047
00048 static char *app = "Record";
00049
00050 static char *synopsis = "Record to a file";
00051
00052 static char *descrip =
00053 " Record(filename.format|silence[|maxduration][|options])\n\n"
00054 "Records from the channel into a given filename. If the file exists it will\n"
00055 "be overwritten.\n"
00056 "- 'format' is the format of the file type to be recorded (wav, gsm, etc).\n"
00057 "- 'silence' is the number of seconds of silence to allow before returning.\n"
00058 "- 'maxduration' is the maximum recording duration in seconds. If missing\n"
00059 "or 0 there is no maximum.\n"
00060 "- 'options' may contain any of the following letters:\n"
00061 " 'a' : append to existing recording rather than replacing\n"
00062 " 'n' : do not answer, but record anyway if line not yet answered\n"
00063 " 'q' : quiet (do not play a beep tone)\n"
00064 " 's' : skip recording if the line is not yet answered\n"
00065 " 't' : use alternate '*' terminator key instead of default '#'\n"
00066 "\n"
00067 "If filename contains '%d', these characters will be replaced with a number\n"
00068 "incremented by one each time the file is recorded. \n\n"
00069 "Use 'show file formats' to see the available formats on your system\n\n"
00070 "User can press '#' to terminate the recording and continue to the next priority.\n\n"
00071 "If the user should hangup during a recording, all data will be lost and the\n"
00072 "application will teminate. \n";
00073
00074 STANDARD_LOCAL_USER;
00075
00076 LOCAL_USER_DECL;
00077
00078 static int record_exec(struct ast_channel *chan, void *data)
00079 {
00080 int res = 0;
00081 int count = 0;
00082 int percentflag = 0;
00083 char *filename, *ext = NULL, *silstr, *maxstr, *options;
00084 char *vdata, *p;
00085 int i = 0;
00086 char tmp[256];
00087
00088 struct ast_filestream *s = '\0';
00089 struct localuser *u;
00090 struct ast_frame *f = NULL;
00091
00092 struct ast_dsp *sildet = NULL;
00093 int totalsilence = 0;
00094 int dspsilence = 0;
00095 int silence = 0;
00096 int gotsilence = 0;
00097 int maxduration = 0;
00098 int gottimeout = 0;
00099 int option_skip = 0;
00100 int option_noanswer = 0;
00101 int option_append = 0;
00102 int terminator = '#';
00103 int option_quiet = 0;
00104 int rfmt = 0;
00105 int flags;
00106 int waitres;
00107 struct ast_silence_generator *silgen = NULL;
00108
00109
00110 if (ast_strlen_zero(data)) {
00111 ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
00112 return -1;
00113 }
00114
00115 LOCAL_USER_ADD(u);
00116
00117
00118 vdata = ast_strdupa(data);
00119 if (!vdata) {
00120 ast_log(LOG_ERROR, "Out of memory\n");
00121 LOCAL_USER_REMOVE(u);
00122 return -1;
00123 }
00124
00125 p = vdata;
00126 filename = strsep(&p, "|");
00127 silstr = strsep(&p, "|");
00128 maxstr = strsep(&p, "|");
00129 options = strsep(&p, "|");
00130
00131 if (filename) {
00132 if (strstr(filename, "%d"))
00133 percentflag = 1;
00134 ext = strrchr(filename, '.');
00135 if (!ext)
00136 ext = strchr(filename, ':');
00137 if (ext) {
00138 *ext = '\0';
00139 ext++;
00140 }
00141 }
00142 if (!ext) {
00143 ast_log(LOG_WARNING, "No extension specified to filename!\n");
00144 LOCAL_USER_REMOVE(u);
00145 return -1;
00146 }
00147 if (silstr) {
00148 if ((sscanf(silstr, "%d", &i) == 1) && (i > -1)) {
00149 silence = i * 1000;
00150 } else if (!ast_strlen_zero(silstr)) {
00151 ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", silstr);
00152 }
00153 }
00154
00155 if (maxstr) {
00156 if ((sscanf(maxstr, "%d", &i) == 1) && (i > -1))
00157
00158 maxduration = i * 1000;
00159 else if (!ast_strlen_zero(maxstr))
00160 ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", maxstr);
00161 }
00162 if (options) {
00163
00164 if (!strcasecmp(options, "skip"))
00165 option_skip = 1;
00166 else if (!strcasecmp(options, "noanswer"))
00167 option_noanswer = 1;
00168 else {
00169 if (strchr(options, 's'))
00170 option_skip = 1;
00171 if (strchr(options, 'n'))
00172 option_noanswer = 1;
00173 if (strchr(options, 'a'))
00174 option_append = 1;
00175 if (strchr(options, 't'))
00176 terminator = '*';
00177 if (strchr(options, 'q'))
00178 option_quiet = 1;
00179 }
00180 }
00181
00182
00183
00184
00185
00186 if (percentflag) {
00187 AST_DECLARE_APP_ARGS(fname,
00188 AST_APP_ARG(piece)[100];
00189 );
00190 char *tmp2 = ast_strdupa(filename);
00191 char countstring[15];
00192 int i;
00193
00194
00195
00196 fname.argc = ast_app_separate_args(tmp2, '%', fname.argv, (sizeof(fname) - sizeof(fname.argc)) / sizeof(fname.argv[0]));
00197 do {
00198 int tmplen;
00199
00200 ast_copy_string(tmp, fname.piece[0], sizeof(tmp));
00201 tmplen = strlen(tmp);
00202 for (i = 1; i < fname.argc; i++) {
00203 if (fname.piece[i][0] == 'd') {
00204
00205 snprintf(countstring, sizeof(countstring), "%d", count);
00206 ast_copy_string(tmp + tmplen, countstring, sizeof(tmp) - tmplen);
00207 tmplen += strlen(countstring);
00208 } else if (tmplen + 2 < sizeof(tmp)) {
00209
00210 tmp[tmplen++] = '%';
00211 tmp[tmplen++] = fname.piece[i][0];
00212 }
00213
00214 ast_copy_string(tmp + tmplen, &(fname.piece[i][1]), sizeof(tmp) - tmplen);
00215 }
00216 count++;
00217 } while ( ast_fileexists(tmp, ext, chan->language) != -1 );
00218 pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
00219 } else
00220 strncpy(tmp, filename, sizeof(tmp)-1);
00221
00222
00223
00224
00225 if (chan->_state != AST_STATE_UP) {
00226 if (option_skip) {
00227
00228 LOCAL_USER_REMOVE(u);
00229 return 0;
00230 } else if (!option_noanswer) {
00231
00232 res = ast_answer(chan);
00233 }
00234 }
00235
00236 if (res) {
00237 ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
00238 goto out;
00239 }
00240
00241 if (!option_quiet) {
00242
00243 res = ast_streamfile(chan, "beep", chan->language);
00244 if (!res) {
00245 res = ast_waitstream(chan, "");
00246 } else {
00247 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
00248 }
00249 ast_stopstream(chan);
00250 }
00251
00252
00253
00254 if (silence > 0) {
00255 rfmt = chan->readformat;
00256 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
00257 if (res < 0) {
00258 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
00259 LOCAL_USER_REMOVE(u);
00260 return -1;
00261 }
00262 sildet = ast_dsp_new();
00263 if (!sildet) {
00264 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
00265 LOCAL_USER_REMOVE(u);
00266 return -1;
00267 }
00268 ast_dsp_set_threshold(sildet, 256);
00269 }
00270
00271
00272 flags = option_append ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
00273 s = ast_writefile( tmp, ext, NULL, flags , 0, 0644);
00274
00275 if (!s) {
00276 ast_log(LOG_WARNING, "Could not create file %s\n", filename);
00277 goto out;
00278 }
00279
00280 if (option_transmit_silence_during_record)
00281 silgen = ast_channel_start_silence_generator(chan);
00282
00283
00284 ast_indicate(chan, AST_CONTROL_VIDUPDATE);
00285
00286 if (maxduration <= 0)
00287 maxduration = -1;
00288
00289 while ((waitres = ast_waitfor(chan, maxduration)) > -1) {
00290 if (maxduration > 0) {
00291 if (waitres == 0) {
00292 gottimeout = 1;
00293 break;
00294 }
00295 maxduration = waitres;
00296 }
00297
00298 f = ast_read(chan);
00299 if (!f) {
00300 res = -1;
00301 break;
00302 }
00303 if (f->frametype == AST_FRAME_VOICE) {
00304 res = ast_writestream(s, f);
00305
00306 if (res) {
00307 ast_log(LOG_WARNING, "Problem writing frame\n");
00308 ast_frfree(f);
00309 break;
00310 }
00311
00312 if (silence > 0) {
00313 dspsilence = 0;
00314 ast_dsp_silence(sildet, f, &dspsilence);
00315 if (dspsilence) {
00316 totalsilence = dspsilence;
00317 } else {
00318 totalsilence = 0;
00319 }
00320 if (totalsilence > silence) {
00321
00322 ast_frfree(f);
00323 gotsilence = 1;
00324 break;
00325 }
00326 }
00327 } else if (f->frametype == AST_FRAME_VIDEO) {
00328 res = ast_writestream(s, f);
00329
00330 if (res) {
00331 ast_log(LOG_WARNING, "Problem writing frame\n");
00332 ast_frfree(f);
00333 break;
00334 }
00335 } else if ((f->frametype == AST_FRAME_DTMF) &&
00336 (f->subclass == terminator)) {
00337 ast_frfree(f);
00338 break;
00339 }
00340 ast_frfree(f);
00341 }
00342 if (!f) {
00343 ast_log(LOG_DEBUG, "Got hangup\n");
00344 res = -1;
00345 }
00346
00347 if (gotsilence) {
00348 ast_stream_rewind(s, silence-1000);
00349 ast_truncstream(s);
00350 } else if (!gottimeout) {
00351
00352 ast_stream_rewind(s, 250);
00353 ast_truncstream(s);
00354 }
00355 ast_closestream(s);
00356
00357 if (silgen)
00358 ast_channel_stop_silence_generator(chan, silgen);
00359
00360 out:
00361 if ((silence > 0) && rfmt) {
00362 res = ast_set_read_format(chan, rfmt);
00363 if (res)
00364 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
00365 if (sildet)
00366 ast_dsp_free(sildet);
00367 }
00368
00369 LOCAL_USER_REMOVE(u);
00370
00371 return res;
00372 }
00373
00374 int unload_module(void)
00375 {
00376 int res;
00377
00378 res = ast_unregister_application(app);
00379
00380 STANDARD_HANGUP_LOCALUSERS;
00381
00382 return res;
00383 }
00384
00385 int load_module(void)
00386 {
00387 return ast_register_application(app, record_exec, synopsis, descrip);
00388 }
00389
00390 char *description(void)
00391 {
00392 return tdesc;
00393 }
00394
00395 int usecount(void)
00396 {
00397 int res;
00398 STANDARD_USECOUNT(res);
00399 return res;
00400 }
00401
00402 char *key()
00403 {
00404 return ASTERISK_GPL_KEY;
00405 }