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