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