Add missing newline (bug #5616)
[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 enum {
96         OPT_ARG_READVOLUME = 0,
97         OPT_ARG_WRITEVOLUME,
98         OPT_ARG_VOLUME,
99         OPT_ARG_ARRAY_SIZE,
100 } mixmonitor_args;
101
102 AST_APP_OPTIONS(mixmonitor_opts, {
103         AST_APP_OPTION('a', MUXFLAG_APPEND),
104         AST_APP_OPTION('b', MUXFLAG_BRIDGED),
105         AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
106         AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
107         AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
108 });
109
110 static void stopmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
111 {
112         /* If our status has changed, then the channel we're spying on is gone....
113            DON'T TOUCH IT!!!  RUN AWAY!!! */
114         if (spy->status != CHANSPY_RUNNING)
115                 return;
116
117         if (!chan)
118                 return;
119
120         ast_mutex_lock(&chan->lock);
121         ast_channel_spy_remove(chan, spy);
122         ast_mutex_unlock(&chan->lock);
123 }
124
125 static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
126 {
127         struct ast_channel *peer;
128         int res;
129
130         if (!chan)
131                 return -1;
132
133         ast_mutex_lock(&chan->lock);
134         res = ast_channel_spy_add(chan, spy);
135         ast_mutex_unlock(&chan->lock);
136                 
137         if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
138                 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
139
140         return res;
141 }
142
143 #define SAMPLES_PER_FRAME 160
144
145 static void *mixmonitor_thread(void *obj) 
146 {
147         struct mixmonitor *mixmonitor = obj;
148         struct ast_channel_spy spy;
149         struct ast_filestream *fs = NULL;
150         char *ext, *name;
151         unsigned int oflags;
152         struct ast_frame *f;
153         char post_process[1024] = "";
154         
155         STANDARD_INCREMENT_USECOUNT;
156
157         name = ast_strdupa(mixmonitor->chan->name);
158
159         oflags = O_CREAT|O_WRONLY;
160         oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
161                 
162         if ((ext = strchr(mixmonitor->filename, '.'))) {
163                 *(ext++) = '\0';
164         } else {
165                 ext = "raw";
166         }
167
168         fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644);
169         if (!fs) {
170                 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
171                 goto out;
172         }
173
174         if (ast_test_flag(mixmonitor, MUXFLAG_APPEND))
175                 ast_seekstream(fs, 0, SEEK_END);
176         
177         memset(&spy, 0, sizeof(spy));
178         ast_set_flag(&spy, CHANSPY_FORMAT_AUDIO);
179         ast_set_flag(&spy, CHANSPY_MIXAUDIO);
180         spy.type = mixmonitor_spy_type;
181         spy.status = CHANSPY_RUNNING;
182         spy.read_queue.format = AST_FORMAT_SLINEAR;
183         spy.write_queue.format = AST_FORMAT_SLINEAR;
184         if (mixmonitor->readvol) {
185                 ast_set_flag(&spy, CHANSPY_READ_VOLADJUST);
186                 spy.read_vol_adjustment = mixmonitor->readvol;
187         }
188         if (mixmonitor->writevol) {
189                 ast_set_flag(&spy, CHANSPY_WRITE_VOLADJUST);
190                 spy.write_vol_adjustment = mixmonitor->writevol;
191         }
192         ast_mutex_init(&spy.lock);
193
194         if (startmon(mixmonitor->chan, &spy)) {
195                 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
196                         spy.type, mixmonitor->chan->name);
197                 goto out2;
198         }
199
200         if (option_verbose > 1)
201                 ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", name);
202         
203         while (1) {
204                 struct ast_frame *next;
205                 int write;
206
207                 ast_mutex_lock(&spy.lock);
208
209                 ast_channel_spy_trigger_wait(&spy);
210                 
211                 if (ast_check_hangup(mixmonitor->chan) || spy.status != CHANSPY_RUNNING) {
212                         ast_mutex_unlock(&spy.lock);
213                         break;
214                 }
215                 
216                 while (1) {
217                         if (!(f = ast_channel_spy_read_frame(&spy, SAMPLES_PER_FRAME)))
218                                 break;
219
220                         write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
221                                  ast_bridged_channel(mixmonitor->chan));
222
223                         /* it is possible for ast_channel_spy_read_frame() to return a chain
224                            of frames if a queue flush was necessary, so process them
225                         */
226                         for (; f; f = next) {
227                                 next = f->next;
228                                 if (write)
229                                         ast_writestream(fs, f);
230                                 ast_frfree(f);
231                         }
232                 }
233
234                 ast_mutex_unlock(&spy.lock);
235         }
236         
237         if (mixmonitor->post_process) {
238                 char *p;
239
240                 for (p = mixmonitor->post_process; *p ; p++) {
241                         if (*p == '^' && *(p+1) == '{') {
242                                 *p = '$';
243                         }
244                 }
245                 pbx_substitute_variables_helper(mixmonitor->chan, mixmonitor->post_process, post_process, sizeof(post_process) - 1);
246         }
247
248         stopmon(mixmonitor->chan, &spy);
249
250         if (option_verbose > 1)
251                 ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", name);
252
253         if (!ast_strlen_zero(post_process)) {
254                 if (option_verbose > 2)
255                         ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", post_process);
256                 ast_safe_system(post_process);
257         }
258
259 out2:
260         ast_mutex_destroy(&spy.lock);
261
262         if (fs)
263                 ast_closestream(fs);
264
265 out:
266         free(mixmonitor);
267
268         STANDARD_DECREMENT_USECOUNT;
269
270         return NULL;
271 }
272
273 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
274                                   int readvol, int writevol, const char *post_process) 
275 {
276         pthread_attr_t attr;
277         pthread_t thread;
278         struct mixmonitor *mixmonitor;
279         int len;
280
281         len = sizeof(*mixmonitor) + strlen(filename) + 1;
282         if (!ast_strlen_zero(post_process))
283                 len += strlen(post_process) + 1;
284
285         if (!(mixmonitor = calloc(1, len))) {
286                 ast_log(LOG_ERROR, "Memory Error!\n");
287                 return;
288         }
289
290         mixmonitor->chan = chan;
291         mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor);
292         strcpy(mixmonitor->filename, filename);
293         if (!ast_strlen_zero(post_process)) {
294                 mixmonitor->post_process = mixmonitor->filename + strlen(filename) + 1;
295                 strcpy(mixmonitor->post_process, post_process);
296         }
297         mixmonitor->readvol = readvol;
298         mixmonitor->writevol = writevol;
299         mixmonitor->flags = flags;
300
301         pthread_attr_init(&attr);
302         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
303         ast_pthread_create(&thread, &attr, mixmonitor_thread, mixmonitor);
304         pthread_attr_destroy(&attr);
305 }
306
307 static int mixmonitor_exec(struct ast_channel *chan, void *data)
308 {
309         int x, readvol = 0, writevol = 0;
310         struct localuser *u;
311         struct ast_flags flags = {0};
312         char *parse;
313         AST_DECLARE_APP_ARGS(args,
314                 AST_APP_ARG(filename);
315                 AST_APP_ARG(options);
316                 AST_APP_ARG(post_process);
317         );
318         
319         if (ast_strlen_zero(data)) {
320                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
321                 return -1;
322         }
323
324         LOCAL_USER_ADD(u);
325
326         if (!(parse = ast_strdupa(data))) {
327                 ast_log(LOG_WARNING, "Memory Error!\n");
328                 LOCAL_USER_REMOVE(u);
329                 return -1;
330         }
331
332         AST_STANDARD_APP_ARGS(args, parse);
333         
334         if (ast_strlen_zero(args.filename)) {
335                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
336                 LOCAL_USER_REMOVE(u);
337                 return -1;
338         }
339
340         if (args.options) {
341                 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
342
343                 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
344
345                 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
346                         if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
347                                 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
348                         } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
349                                 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
350                         } else {
351                                 readvol = get_volfactor(x);
352                         }
353                 }
354                 
355                 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
356                         if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
357                                 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
358                         } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
359                                 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
360                         } else {
361                                 writevol = get_volfactor(x);
362                         }
363                 }
364                 
365                 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
366                         if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
367                                 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
368                         } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
369                                 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
370                         } else {
371                                 readvol = writevol = get_volfactor(x);
372                         }
373                 }
374         }
375
376         /* if not provided an absolute path, use the system-configured monitoring directory */
377         if (args.filename[0] != '/') {
378                 char *build;
379
380                 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
381                 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
382                 args.filename = build;
383         }
384
385         pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
386         launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
387
388         LOCAL_USER_REMOVE(u);
389
390         return 0;
391 }
392
393 static int mixmonitor_cli(int fd, int argc, char **argv) 
394 {
395         struct ast_channel *chan;
396
397         if (argc < 3)
398                 return RESULT_SHOWUSAGE;
399
400         if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
401                 ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
402                 return RESULT_SUCCESS;
403         }
404
405         if (!strcasecmp(argv[1], "start"))
406                 mixmonitor_exec(chan, argv[3]);
407         else if (!strcasecmp(argv[1], "stop"))
408                 ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
409
410         ast_mutex_unlock(&chan->lock);
411
412         return RESULT_SUCCESS;
413 }
414
415
416 static struct ast_cli_entry cli_mixmonitor = {
417         { "mixmonitor", NULL, NULL },
418         mixmonitor_cli, 
419         "Execute a MixMonitor command",
420         "mixmonitor <start|stop> <chan_name> [<args>]\n"
421 };
422
423
424 int unload_module(void)
425 {
426         int res;
427
428         res = ast_cli_unregister(&cli_mixmonitor);
429         res |= ast_unregister_application(app);
430         
431         STANDARD_HANGUP_LOCALUSERS;
432
433         return res;
434 }
435
436 int load_module(void)
437 {
438         int res;
439
440         res = ast_cli_register(&cli_mixmonitor);
441         res |= ast_register_application(app, mixmonitor_exec, synopsis, desc);
442
443         return res;
444 }
445
446 char *description(void)
447 {
448         return (char *) tdesc;
449 }
450
451 int usecount(void)
452 {
453         int res;
454
455         STANDARD_USECOUNT(res);
456
457         return res;
458 }
459
460 char *key()
461 {
462         return ASTERISK_GPL_KEY;
463 }