2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Matthew Fredrickson <creslin@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Trivial application to record a sound file
23 * \author Matthew Fredrickson <creslin@digium.com>
25 * \ingroup applications
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36 #include "asterisk/lock.h"
37 #include "asterisk/file.h"
38 #include "asterisk/logger.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/module.h"
42 #include "asterisk/translate.h"
43 #include "asterisk/dsp.h"
44 #include "asterisk/utils.h"
45 #include "asterisk/options.h"
48 static char *app = "Record";
50 static char *synopsis = "Record to a file";
52 static char *descrip =
53 " Record(filename.format|silence[|maxduration][|options])\n\n"
54 "Records from the channel into a given filename. If the file exists it will\n"
56 "- 'format' is the format of the file type to be recorded (wav, gsm, etc).\n"
57 "- 'silence' is the number of seconds of silence to allow before returning.\n"
58 "- 'maxduration' is the maximum recording duration in seconds. If missing\n"
59 "or 0 there is no maximum.\n"
60 "- 'options' may contain any of the following letters:\n"
61 " 'a' : append to existing recording rather than replacing\n"
62 " 'n' : do not answer, but record anyway if line not yet answered\n"
63 " 'q' : quiet (do not play a beep tone)\n"
64 " 's' : skip recording if the line is not yet answered\n"
65 " 't' : use alternate '*' terminator key (DTMF) instead of default '#'\n"
66 " 'x' : ignore all terminator keys (DTMF) and keep recording until hangup\n"
68 "If filename contains '%d', these characters will be replaced with a number\n"
69 "incremented by one each time the file is recorded. \n\n"
70 "Use 'show file formats' to see the available formats on your system\n\n"
71 "User can press '#' to terminate the recording and continue to the next priority.\n\n"
72 "If the user should hangup during a recording, all data will be lost and the\n"
73 "application will teminate. \n";
77 static int record_exec(struct ast_channel *chan, void *data)
82 char *filename, *ext = NULL, *silstr, *maxstr, *options;
87 struct ast_filestream *s = '\0';
89 struct ast_frame *f = NULL;
91 struct ast_dsp *sildet = NULL; /* silence detector dsp */
94 int silence = 0; /* amount of silence to allow */
95 int gotsilence = 0; /* did we timeout for silence? */
96 int maxduration = 0; /* max duration of recording in milliseconds */
97 int gottimeout = 0; /* did we timeout for maxduration exceeded? */
99 int option_noanswer = 0;
100 int option_append = 0;
101 int terminator = '#';
102 int option_quiet = 0;
106 struct ast_silence_generator *silgen = NULL;
108 /* The next few lines of code parse out the filename and header from the input string */
109 if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
110 ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
116 /* Yay for strsep being easy */
117 vdata = ast_strdupa(data);
120 filename = strsep(&p, "|");
121 silstr = strsep(&p, "|");
122 maxstr = strsep(&p, "|");
123 options = strsep(&p, "|");
126 if (strstr(filename, "%d"))
128 ext = strrchr(filename, '.'); /* to support filename with a . in the filename, not format */
130 ext = strchr(filename, ':');
137 ast_log(LOG_WARNING, "No extension specified to filename!\n");
138 LOCAL_USER_REMOVE(u);
142 if ((sscanf(silstr, "%d", &i) == 1) && (i > -1)) {
144 } else if (!ast_strlen_zero(silstr)) {
145 ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", silstr);
150 if ((sscanf(maxstr, "%d", &i) == 1) && (i > -1))
151 /* Convert duration to milliseconds */
152 maxduration = i * 1000;
153 else if (!ast_strlen_zero(maxstr))
154 ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", maxstr);
157 /* Retain backwards compatibility with old style options */
158 if (!strcasecmp(options, "skip"))
160 else if (!strcasecmp(options, "noanswer"))
163 if (strchr(options, 's'))
165 if (strchr(options, 'n'))
167 if (strchr(options, 'a'))
169 if (strchr(options, 't'))
171 if (strchr(options, 'x'))
173 if (strchr(options, 'q'))
180 /* these are to allow the use of the %d in the config file for a wild card of sort to
181 create a new file with the inputed name scheme */
184 snprintf(tmp, sizeof(tmp), filename, count);
186 } while ( ast_fileexists(tmp, ext, chan->language) != -1 );
187 pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
189 strncpy(tmp, filename, sizeof(tmp)-1);
190 /* end of routine mentioned */
194 if (chan->_state != AST_STATE_UP) {
196 /* At the user's option, skip if the line is not up */
197 LOCAL_USER_REMOVE(u);
199 } else if (!option_noanswer) {
200 /* Otherwise answer unless we're supposed to record while on-hook */
201 res = ast_answer(chan);
206 ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
211 /* Some code to play a nice little beep to signify the start of the record operation */
212 res = ast_streamfile(chan, "beep", chan->language);
214 res = ast_waitstream(chan, "");
216 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
218 ast_stopstream(chan);
221 /* The end of beep code. Now the recording starts */
224 rfmt = chan->readformat;
225 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
227 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
228 LOCAL_USER_REMOVE(u);
231 sildet = ast_dsp_new();
233 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
234 LOCAL_USER_REMOVE(u);
237 ast_dsp_set_threshold(sildet, 256);
241 flags = option_append ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
242 s = ast_writefile( tmp, ext, NULL, flags , 0, 0644);
245 ast_log(LOG_WARNING, "Could not create file %s\n", filename);
249 if (ast_opt_transmit_silence)
250 silgen = ast_channel_start_silence_generator(chan);
252 /* Request a video update */
253 ast_indicate(chan, AST_CONTROL_VIDUPDATE);
255 if (maxduration <= 0)
258 while ((waitres = ast_waitfor(chan, maxduration)) > -1) {
259 if (maxduration > 0) {
264 maxduration = waitres;
272 if (f->frametype == AST_FRAME_VOICE) {
273 res = ast_writestream(s, f);
276 ast_log(LOG_WARNING, "Problem writing frame\n");
283 ast_dsp_silence(sildet, f, &dspsilence);
285 totalsilence = dspsilence;
289 if (totalsilence > silence) {
290 /* Ended happily with silence */
296 } else if (f->frametype == AST_FRAME_VIDEO) {
297 res = ast_writestream(s, f);
300 ast_log(LOG_WARNING, "Problem writing frame\n");
304 } else if ((f->frametype == AST_FRAME_DTMF) &&
305 (f->subclass == terminator)) {
312 ast_log(LOG_DEBUG, "Got hangup\n");
317 ast_stream_rewind(s, silence-1000);
319 } else if (!gottimeout) {
320 /* Strip off the last 1/4 second of it */
321 ast_stream_rewind(s, 250);
327 ast_channel_stop_silence_generator(chan, silgen);
330 if ((silence > 0) && rfmt) {
331 res = ast_set_read_format(chan, rfmt);
333 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
335 ast_dsp_free(sildet);
338 LOCAL_USER_REMOVE(u);
343 static int unload_module(void *mod)
347 res = ast_unregister_application(app);
349 STANDARD_HANGUP_LOCALUSERS;
354 static int load_module(void *mod)
356 return ast_register_application(app, record_exec, synopsis, descrip);
359 static const char *description(void)
361 return "Trivial Record Application";
364 static const char *key(void)
366 return ASTERISK_GPL_KEY;