correct comment
[asterisk/asterisk.git] / apps / app_mixmonitor.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005, Anthony Minessale II
5  * Copyright (C) 2005, Digium, Inc.
6  *
7  * Mark Spencer <markster@digium.com>
8  * Kevin P. Fleming <kpfleming@digium.com>
9  *
10  * Based on app_muxmon.c provided by
11  * Anthony Minessale II <anthmct@yahoo.com>
12  *
13  * See http://www.asterisk.org for more information about
14  * the Asterisk project. Please do not directly contact
15  * any of the maintainers of this project for assistance;
16  * the project provides a web site, mailing lists and IRC
17  * channels for your use.
18  *
19  * This program is free software, distributed under the terms of
20  * the GNU General Public License Version 2. See the LICENSE file
21  * at the top of the source tree.
22  */
23
24 /*! \file
25  * \brief MixMonitor() - Record a call and mix the audio during the recording
26  */
27
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include "asterisk/file.h"
37 #include "asterisk/logger.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/module.h"
41 #include "asterisk/lock.h"
42 #include "asterisk/cli.h"
43 #include "asterisk/options.h"
44 #include "asterisk/app.h"
45 #include "asterisk/linkedlists.h"
46
47 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
48
49 static const char *tdesc = "Mixed Audio Monitoring Application";
50 static const char *app = "MixMonitor";
51 static const char *synopsis = "Record a call and mix the audio during the recording";
52 static const char *desc = ""
53 "  MixMonitor(<file>.<ext>[|<options>[|<command>]])\n\n"
54 "Records the audio on the current channel to the specified file.\n"
55 "If the filename is an absolute path, uses that path, otherwise\n"
56 "creates the file in the configured monitoring directory from\n"
57 "asterisk.conf.\n\n"
58 "Valid options:\n"
59 " a      - Append to the file instead of overwriting it.\n"
60 " b      - Only save audio to the file while the channel is bridged.\n"
61 "          Note: does not include conferences.\n"
62 " v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"        
63 " V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"       
64 " W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
65 "         (range -4 to 4)\n\n"  
66 "<command> will be executed when the recording is over\n"
67 "Any strings matching ^{X} will be unescaped to ${X} and \n"
68 "all variables will be evaluated at that time.\n"
69 "The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
70 "";
71
72 STANDARD_LOCAL_USER;
73
74 LOCAL_USER_DECL;
75
76 static const char *mixmonitor_spy_type = "MixMonitor";
77
78 struct mixmonitor {
79         struct ast_channel *chan;
80         char *filename;
81         char *post_process;
82         unsigned int flags;
83         int readvol;
84         int writevol;
85 };
86
87 enum {
88     MUXFLAG_APPEND = (1 << 1),
89     MUXFLAG_BRIDGED = (1 << 2),
90     MUXFLAG_VOLUME = (1 << 3),
91     MUXFLAG_READVOLUME = (1 << 4),
92     MUXFLAG_WRITEVOLUME = (1 << 5),
93 } mixmonitor_flags;
94
95 AST_DECLARE_OPTIONS(mixmonitor_opts,{
96         ['a'] = { MUXFLAG_APPEND },
97         ['b'] = { MUXFLAG_BRIDGED },
98         ['v'] = { MUXFLAG_READVOLUME, 1 },
99         ['V'] = { MUXFLAG_WRITEVOLUME, 2 },
100         ['W'] = { MUXFLAG_VOLUME, 3 },
101 });
102
103 static void stopmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
104 {
105         /* If our status has changed, then the channel we're spying on is gone....
106            DON'T TOUCH IT!!!  RUN AWAY!!! */
107         if (spy->status != CHANSPY_RUNNING)
108                 return;
109
110         if (!chan)
111                 return;
112
113         ast_mutex_lock(&chan->lock);
114         ast_channel_spy_remove(chan, spy);
115         ast_mutex_unlock(&chan->lock);
116 }
117
118 static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
119 {
120         struct ast_channel *peer;
121         int res;
122
123         if (!chan)
124                 return -1;
125
126         ast_mutex_lock(&chan->lock);
127         res = ast_channel_spy_add(chan, spy);
128         ast_mutex_unlock(&chan->lock);
129                 
130         if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
131                 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
132
133         return res;
134 }
135
136 #define SAMPLES_PER_FRAME 160
137
138 static void *mixmonitor_thread(void *obj) 
139 {
140         struct mixmonitor *mixmonitor = obj;
141         struct ast_channel_spy spy;
142         struct ast_filestream *fs = NULL;
143         char *ext, *name;
144         unsigned int oflags;
145         struct ast_frame *f;
146         char post_process[1024] = "";
147         
148         STANDARD_INCREMENT_USECOUNT;
149
150         name = ast_strdupa(mixmonitor->chan->name);
151
152         oflags = O_CREAT|O_WRONLY;
153         oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
154                 
155         if ((ext = strchr(mixmonitor->filename, '.'))) {
156                 *(ext++) = '\0';
157         } else {
158                 ext = "raw";
159         }
160
161         fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644);
162         if (!fs) {
163                 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
164                 goto out;
165         }
166
167         if (ast_test_flag(mixmonitor, MUXFLAG_APPEND))
168                 ast_seekstream(fs, 0, SEEK_END);
169         
170         memset(&spy, 0, sizeof(spy));
171         ast_set_flag(&spy, CHANSPY_FORMAT_AUDIO);
172         ast_set_flag(&spy, CHANSPY_MIXAUDIO);
173         spy.type = mixmonitor_spy_type;
174         spy.status = CHANSPY_RUNNING;
175         spy.read_queue.format = AST_FORMAT_SLINEAR;
176         spy.write_queue.format = AST_FORMAT_SLINEAR;
177         if (mixmonitor->readvol) {
178                 ast_set_flag(&spy, CHANSPY_READ_VOLADJUST);
179                 spy.read_vol_adjustment = mixmonitor->readvol;
180         }
181         if (mixmonitor->writevol) {
182                 ast_set_flag(&spy, CHANSPY_WRITE_VOLADJUST);
183                 spy.write_vol_adjustment = mixmonitor->writevol;
184         }
185         ast_mutex_init(&spy.lock);
186
187         if (startmon(mixmonitor->chan, &spy)) {
188                 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
189                         spy.type, mixmonitor->chan->name);
190                 goto out2;
191         }
192
193         if (option_verbose > 1)
194                 ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", name);
195         
196         while (1) {
197                 struct ast_frame *next;
198                 int write;
199
200                 ast_mutex_lock(&spy.lock);
201
202                 ast_channel_spy_trigger_wait(&spy);
203                 
204                 if (ast_check_hangup(mixmonitor->chan) || spy.status != CHANSPY_RUNNING) {
205                         ast_mutex_unlock(&spy.lock);
206                         break;
207                 }
208                 
209                 while (1) {
210                         if (!(f = ast_channel_spy_read_frame(&spy, SAMPLES_PER_FRAME)))
211                                 break;
212
213                         write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
214                                  ast_bridged_channel(mixmonitor->chan));
215
216                         /* it is possible for ast_channel_spy_read_frame() to return a chain
217                            of frames if a queue flush was necessary, so process them
218                         */
219                         for (; f; f = next) {
220                                 next = f->next;
221                                 if (write)
222                                         ast_writestream(fs, f);
223                                 ast_frfree(f);
224                         }
225                 }
226
227                 ast_mutex_unlock(&spy.lock);
228         }
229         
230         if (mixmonitor->post_process) {
231                 char *p;
232
233                 for (p = mixmonitor->post_process; *p ; p++) {
234                         if (*p == '^' && *(p+1) == '{') {
235                                 *p = '$';
236                         }
237                 }
238                 pbx_substitute_variables_helper(mixmonitor->chan, mixmonitor->post_process, post_process, sizeof(post_process) - 1);
239         }
240
241         stopmon(mixmonitor->chan, &spy);
242
243         if (option_verbose > 1)
244                 ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", name);
245
246         if (!ast_strlen_zero(post_process)) {
247                 if (option_verbose > 2)
248                         ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", post_process);
249                 ast_safe_system(post_process);
250         }
251
252 out2:
253         ast_mutex_destroy(&spy.lock);
254
255         if (fs)
256                 ast_closestream(fs);
257
258 out:
259         free(mixmonitor);
260
261         STANDARD_DECREMENT_USECOUNT;
262
263         return NULL;
264 }
265
266 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
267                                   int readvol, int writevol, const char *post_process) 
268 {
269         pthread_attr_t attr;
270         pthread_t thread;
271         struct mixmonitor *mixmonitor;
272         int len;
273
274         len = sizeof(*mixmonitor) + strlen(filename) + 1;
275         if (post_process && !ast_strlen_zero(post_process))
276                 len += strlen(post_process) + 1;
277
278         if (!(mixmonitor = calloc(1, len))) {
279                 ast_log(LOG_ERROR, "Memory Error!\n");
280                 return;
281         }
282
283         mixmonitor->chan = chan;
284         mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor);
285         strcpy(mixmonitor->filename, filename);
286         if (post_process && !ast_strlen_zero(post_process)) {
287                 mixmonitor->post_process = mixmonitor->filename + strlen(filename) + 1;
288                 strcpy(mixmonitor->post_process, post_process);
289         }
290         mixmonitor->readvol = readvol;
291         mixmonitor->writevol = writevol;
292         mixmonitor->flags = flags;
293
294         pthread_attr_init(&attr);
295         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
296         ast_pthread_create(&thread, &attr, mixmonitor_thread, mixmonitor);
297         pthread_attr_destroy(&attr);
298 }
299
300 static int mixmonitor_exec(struct ast_channel *chan, void *data)
301 {
302         int x, readvol = 0, writevol = 0;
303         struct localuser *u;
304         struct ast_flags flags = {0};
305         char *parse;
306         AST_DECLARE_APP_ARGS(args,
307                 AST_APP_ARG(filename);
308                 AST_APP_ARG(options);
309                 AST_APP_ARG(post_process);
310         );
311         
312         if (ast_strlen_zero(data)) {
313                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
314                 return -1;
315         }
316
317         LOCAL_USER_ADD(u);
318
319         if (!(parse = ast_strdupa(data))) {
320                 ast_log(LOG_WARNING, "Memory Error!\n");
321                 LOCAL_USER_REMOVE(u);
322                 return -1;
323         }
324
325         AST_STANDARD_APP_ARGS(args, parse);
326         
327         if (ast_strlen_zero(args.filename)) {
328                 ast_log(LOG_WARNING, "Muxmon requires an argument (filename)\n");
329                 LOCAL_USER_REMOVE(u);
330                 return -1;
331         }
332
333         if (args.options) {
334                 char *opts[3] = { NULL, };
335
336                 ast_parseoptions(mixmonitor_opts, &flags, opts, args.options);
337
338                 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
339                         if (!opts[0] || ast_strlen_zero(opts[0])) {
340                                 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
341                         } else if ((sscanf(opts[0], "%d", &x) != 1) || (x < -4) || (x > 4)) {
342                                 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[0]);
343                         } else {
344                                 readvol = get_volfactor(x);
345                         }
346                 }
347                 
348                 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
349                         if (!opts[1] || ast_strlen_zero(opts[1])) {
350                                 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
351                         } else if ((sscanf(opts[1], "%d", &x) != 1) || (x < -4) || (x > 4)) {
352                                 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[1]);
353                         } else {
354                                 writevol = get_volfactor(x);
355                         }
356                 }
357                 
358                 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
359                         if (!opts[2] || ast_strlen_zero(opts[2])) {
360                                 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
361                         } else if ((sscanf(opts[2], "%d", &x) != 1) || (x < -4) || (x > 4)) {
362                                 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[2]);
363                         } else {
364                                 readvol = writevol = get_volfactor(x);
365                         }
366                 }
367         }
368
369         /* if not provided an absolute path, use the system-configured monitoring directory */
370         if (args.filename[0] != '/') {
371                 char *build;
372
373                 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
374                 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
375                 args.filename = build;
376         }
377
378         pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
379         launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
380
381         LOCAL_USER_REMOVE(u);
382
383         return 0;
384 }
385
386 static int mixmonitor_cli(int fd, int argc, char **argv) 
387 {
388         struct ast_channel *chan;
389
390         if (argc < 3)
391                 return RESULT_SHOWUSAGE;
392
393         if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
394                 ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
395                 return RESULT_SUCCESS;
396         }
397
398         if (!strcasecmp(argv[1], "start"))
399                 mixmonitor_exec(chan, argv[3]);
400         else if (!strcasecmp(argv[1], "stop"))
401                 ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
402
403         ast_mutex_unlock(&chan->lock);
404
405         return RESULT_SUCCESS;
406 }
407
408
409 static struct ast_cli_entry cli_mixmonitor = {
410         { "mixmonitor", NULL, NULL },
411         mixmonitor_cli, 
412         "Execute a MixMonitor command",
413         "mixmonitor <start|stop> <chan_name> [<args>]"
414 };
415
416
417 int unload_module(void)
418 {
419         int res;
420
421         res = ast_cli_unregister(&cli_mixmonitor);
422         res |= ast_unregister_application(app);
423         
424         STANDARD_HANGUP_LOCALUSERS;
425
426         return res;
427 }
428
429 int load_module(void)
430 {
431         int res;
432
433         res = ast_cli_register(&cli_mixmonitor);
434         res |= ast_register_application(app, mixmonitor_exec, synopsis, desc);
435
436         return res;
437 }
438
439 char *description(void)
440 {
441         return (char *) tdesc;
442 }
443
444 int usecount(void)
445 {
446         int res;
447
448         STANDARD_USECOUNT(res);
449
450         return res;
451 }
452
453 char *key()
454 {
455         return ASTERISK_GPL_KEY;
456 }