30a1b97c07dfd30421df367d1f5994ae7113b91a
[asterisk/asterisk.git] / apps / app_record.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Trivial application to record a sound file
5  * 
6  * Copyright (C) 2001, Linux Support Services, Inc.
7  *
8  * Matthew Fredrickson <creslin@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13  
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/module.h>
20 #include <asterisk/translate.h>
21 #include <asterisk/dsp.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <pthread.h>
25
26 static char *tdesc = "Trivial Record Application";
27
28 static char *app = "Record";
29
30 static char *synopsis = "Record to a file";
31
32 static char *descrip = 
33 "  Record(filename:extension|silence): Records from the  channel into a given\n"
34 "filename. If the file exists it will be overwritten. The 'extension'\n"
35 "is the extension of the file type  to  be  recorded (wav, gsm, etc).\n"
36 "'silence' is the number of seconds of silence to allow before returning.\n"
37 "Returns -1 when the user hangs up.\n";
38
39 STANDARD_LOCAL_USER;
40
41 LOCAL_USER_DECL;
42
43 static int record_exec(struct ast_channel *chan, void *data)
44 {
45         int res = 0;
46         int count = 0;
47         int percentflag = 0;
48         char fil[256];
49         char tmp[256];
50         char ext[10];
51         char * vdata;  /* Used so I don't have to typecast every use of *data */
52         int i = 0;
53         int j = 0;
54
55         struct ast_filestream *s = '\0';
56         struct localuser *u;
57         struct ast_frame *f = NULL;
58         
59         struct ast_dsp *sildet;         /* silence detector dsp */
60         int totalsilence = 0;
61         int dspsilence = 0;
62         int silence = 0;                /* amount of silence to allow */
63         int gotsilence = 0;             /* did we timeout for silence? */
64         char silencestr[5];
65         int k = 0;
66         int rfmt;
67
68         vdata = data; /* explained above */
69
70         /* The next few lines of code parse out the filename and header from the input string */
71         if (!vdata) { /* no data implies no filename or anything is present */
72                 ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
73                 return -1;
74         }
75         
76         for (; vdata[i] && (vdata[i] != ':') && (vdata[i] != '|'); i++ ) {
77                 if ((vdata[i] == '%') && (vdata[i+1] == 'd')) {
78                         percentflag = 1;                      /* the wildcard is used */
79                 }
80                 
81                 if (i == strlen(vdata) ) {
82                         ast_log(LOG_WARNING, "No extension found\n");
83                         return -1;
84                 }
85                 fil[i] = vdata[i];
86         }
87         fil[i++] = '\0';
88
89         for (; j < 10 && i < strlen(data) && (vdata[i] != '|'); i++, j++)
90                 ext[j] = vdata[i];
91         ext[j] = '\0';
92
93         if (vdata[i] && (vdata[i] == '|')) i++;
94         for (; vdata[i] && (vdata[i] != '|') && (k < 3) && i < strlen(data); i++, k++)
95                 silencestr[k] = vdata[i];
96         silencestr[k] = '\0';
97
98         if (silencestr) {
99                 silence = atoi(silencestr);
100                 if (silence > 0)
101                         silence *= 1000;
102         }
103
104         /* done parsing */
105         
106         
107         /* these are to allow the use of the %d in the config file for a wild card of sort to
108           create a new file with the inputed name scheme */
109         if (percentflag) {
110                 do {
111                         snprintf(tmp, 256, fil, count);
112                         count++;
113                 } while ( ast_fileexists(tmp, ext, chan->language) != -1 );
114         } else
115                 strncpy(tmp, fil, 256-1);
116         /* end of routine mentioned */
117
118         LOCAL_USER_ADD(u);
119
120         if (chan->_state != AST_STATE_UP) {
121                 res = ast_answer(chan); /* Shouldn't need this, but checking to see if channel is already answered
122                                          * Theoretically asterisk should already have answered before running the app */
123         }
124         
125         if (!res) {
126                 /* Some code to play a nice little beep to signify the start of the record operation */
127                 res = ast_streamfile(chan, "beep", chan->language);
128                 if (!res) {
129                         res = ast_waitstream(chan, "");
130                 } else {
131                         ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
132                 }
133                 ast_stopstream(chan);
134
135                 /* The end of beep code.  Now the recording starts */
136
137
138                 if (silence > 0) {
139                         rfmt = chan->readformat;
140                         res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
141                         if (res < 0) {
142                                 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
143                                 return -1;
144                         }
145                         sildet = ast_dsp_new();
146                         if (!sildet) {
147                                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
148                                 return -1;
149                         }
150                         ast_dsp_set_threshold(sildet, 256);
151                 }
152
153                 s = ast_writefile( tmp, ext, NULL, O_CREAT|O_TRUNC|O_WRONLY , 0, 0644);
154         
155                 if (s) {
156                         while (ast_waitfor(chan, -1) > -1) {
157                                 f = ast_read(chan);
158                                 if (!f) {
159                                         res = -1;
160                                         break;
161                                 }
162                                 if (f->frametype == AST_FRAME_VOICE) {
163                                         res = ast_writestream(s, f);
164                                         
165                                         if (res) {
166                                                 ast_log(LOG_WARNING, "Problem writing frame\n");
167                                                 break;
168                                         }
169
170                                         if (silence > 0) {
171                                                 dspsilence = 0;
172                                                 ast_dsp_silence(sildet, f, &dspsilence);
173                                                 if (dspsilence) {
174                                                         totalsilence = dspsilence;
175                                                 } else {
176                                                         totalsilence = 0;
177                                                 }
178                                                 if (totalsilence > silence) {
179                                                         /* Ended happily with silence */
180                                                         ast_frfree(f);
181                                                         gotsilence = 1;
182                                                         break;
183                                                 }
184                                         }
185                                 }
186                                 if ((f->frametype == AST_FRAME_DTMF) &&
187                                         (f->subclass == '#')) {
188                                         ast_frfree(f);
189                                         break;
190                                 }
191                                 ast_frfree(f);
192                         }
193                         if (!f) {
194                                         ast_log(LOG_DEBUG, "Got hangup\n");
195                                         res = -1;
196                         }
197
198                         if (gotsilence) {
199                                 ast_stream_rewind(s, silence-1000);
200                                 ast_truncstream(s);
201                         } else {
202                                 /* Strip off the last 1/4 second of it */
203                                 ast_stream_rewind(s, 250);
204                                 ast_truncstream(s);
205                         }
206                         ast_closestream(s);
207                 } else                  
208                         ast_log(LOG_WARNING, "Could not create file %s\n", fil);
209         } else
210                 ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
211
212         LOCAL_USER_REMOVE(u);
213         if (silence > 0) {
214                 res = ast_set_read_format(chan, rfmt);
215                 if (res)
216                         ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
217                 ast_dsp_free(sildet);
218         }
219         return res;
220 }
221
222 int unload_module(void)
223 {
224         STANDARD_HANGUP_LOCALUSERS;
225         return ast_unregister_application(app);
226 }
227
228 int load_module(void)
229 {
230         return ast_register_application(app, record_exec, synopsis, descrip);
231 }
232
233 char *description(void)
234 {
235         return tdesc;
236 }
237
238 int usecount(void)
239 {
240         int res;
241         STANDARD_USECOUNT(res);
242         return res;
243 }
244
245 char *key()
246 {
247         return ASTERISK_GPL_KEY;
248 }