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