Merged revisions 51407 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 - 2006, 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 "asterisk.h"
37
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
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 #include "asterisk/utils.h"
57
58 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
59
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 static const char *stop_app = "StopMixMonitor";
83 static const char *stop_synopsis = "Stop recording a call through MixMonitor";
84 static const char *stop_desc = ""
85 "  StopMixMonitor()\n\n"
86 "Stops the audio recording that was started with a call to MixMonitor()\n"
87 "on the current channel.\n"
88 "";
89
90 struct module_symbols *me;
91
92 static const char *mixmonitor_spy_type = "MixMonitor";
93
94 struct mixmonitor {
95         struct ast_channel_spy spy;
96         char *filename;
97         char *post_process;
98         char *name;
99         unsigned int flags;
100 };
101
102 enum {
103         MUXFLAG_APPEND = (1 << 1),
104         MUXFLAG_BRIDGED = (1 << 2),
105         MUXFLAG_VOLUME = (1 << 3),
106         MUXFLAG_READVOLUME = (1 << 4),
107         MUXFLAG_WRITEVOLUME = (1 << 5),
108 } mixmonitor_flags;
109
110 enum {
111         OPT_ARG_READVOLUME = 0,
112         OPT_ARG_WRITEVOLUME,
113         OPT_ARG_VOLUME,
114         OPT_ARG_ARRAY_SIZE,
115 } mixmonitor_args;
116
117 AST_APP_OPTIONS(mixmonitor_opts, {
118         AST_APP_OPTION('a', MUXFLAG_APPEND),
119         AST_APP_OPTION('b', MUXFLAG_BRIDGED),
120         AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
121         AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
122         AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
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_channel_lock(chan);
134         res = ast_channel_spy_add(chan, spy);
135         ast_channel_unlock(chan);
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_frame *f = NULL;
149         struct ast_filestream *fs = NULL;
150         unsigned int oflags;
151         char *ext;
152         int errflag = 0;
153
154         if (option_verbose > 1)
155                 ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name);
156         
157         ast_mutex_lock(&mixmonitor->spy.lock);
158
159         while (mixmonitor->spy.chan) {
160                 struct ast_frame *next;
161                 int write;
162
163                 ast_channel_spy_trigger_wait(&mixmonitor->spy);
164                 
165                 if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING)
166                         break;
167                 
168                 while (1) {
169                         if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME)))
170                                 break;
171
172                         write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
173                                  ast_bridged_channel(mixmonitor->spy.chan));
174
175                         /* it is possible for ast_channel_spy_read_frame() to return a chain
176                            of frames if a queue flush was necessary, so process them
177                         */
178                         for (; f; f = next) {
179                                 next = AST_LIST_NEXT(f, frame_list);
180                                 if (write && errflag == 0) {
181                                         if (!fs) {
182                                                 /* Determine creation flags and filename plus extension for filestream */
183                                                 oflags = O_CREAT | O_WRONLY;
184                                                 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
185
186                                                 if ((ext = strrchr(mixmonitor->filename, '.')))
187                                                         *(ext++) = '\0';
188                                                 else
189                                                         ext = "raw";
190
191                                                 /* Move onto actually creating the filestream */
192                                                 if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) {
193                                                         ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
194                                                         errflag = 1;
195                                                 }
196
197                                         }
198                                         if (fs)
199                                                 ast_writestream(fs, f);
200                                 }
201                                 ast_frame_free(f, 0);
202                         }
203                 }
204         }
205
206         ast_mutex_unlock(&mixmonitor->spy.lock);
207
208         ast_channel_spy_free(&mixmonitor->spy);
209         
210         if (option_verbose > 1)
211                 ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name);
212
213         if (mixmonitor->post_process) {
214                 if (option_verbose > 2)
215                         ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process);
216                 ast_safe_system(mixmonitor->post_process);
217         }
218                 
219         if (fs)
220                 ast_closestream(fs);
221
222         free(mixmonitor);
223
224
225         return NULL;
226 }
227
228 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
229                                   int readvol, int writevol, const char *post_process) 
230 {
231         pthread_attr_t attr;
232         pthread_t thread;
233         struct mixmonitor *mixmonitor;
234         char postprocess2[1024] = "";
235         size_t len;
236
237         len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
238
239         /* If a post process system command is given attach it to the structure */
240         if (!ast_strlen_zero(post_process)) {
241                 char *p1, *p2;
242
243                 p1 = ast_strdupa(post_process);
244                 for (p2 = p1; *p2 ; p2++) {
245                         if (*p2 == '^' && *(p2+1) == '{') {
246                                 *p2 = '$';
247                         }
248                 }
249
250                 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
251                 if (!ast_strlen_zero(postprocess2))
252                         len += strlen(postprocess2) + 1;
253         }
254
255         /* Pre-allocate mixmonitor structure and spy */
256         if (!(mixmonitor = calloc(1, len))) {
257                 return;
258         }
259
260         /* Copy over flags and channel name */
261         mixmonitor->flags = flags;
262         mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
263         strcpy(mixmonitor->name, chan->name);
264         if (!ast_strlen_zero(postprocess2)) {
265                 mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + 1;
266                 strcpy(mixmonitor->post_process, postprocess2);
267         }
268
269         mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
270         strcpy(mixmonitor->filename, filename);
271
272         /* Setup the actual spy before creating our thread */
273         ast_set_flag(&mixmonitor->spy, CHANSPY_FORMAT_AUDIO);
274         ast_set_flag(&mixmonitor->spy, CHANSPY_MIXAUDIO);
275         mixmonitor->spy.type = mixmonitor_spy_type;
276         mixmonitor->spy.status = CHANSPY_RUNNING;
277         mixmonitor->spy.read_queue.format = AST_FORMAT_SLINEAR;
278         mixmonitor->spy.write_queue.format = AST_FORMAT_SLINEAR;
279         if (readvol) {
280                 ast_set_flag(&mixmonitor->spy, CHANSPY_READ_VOLADJUST);
281                 mixmonitor->spy.read_vol_adjustment = readvol;
282         }
283         if (writevol) {
284                 ast_set_flag(&mixmonitor->spy, CHANSPY_WRITE_VOLADJUST);
285                 mixmonitor->spy.write_vol_adjustment = writevol;
286         }
287         ast_mutex_init(&mixmonitor->spy.lock);
288
289         if (startmon(chan, &mixmonitor->spy)) {
290                 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
291                         mixmonitor->spy.type, chan->name);
292                 /* Since we couldn't add ourselves - bail out! */
293                 ast_mutex_destroy(&mixmonitor->spy.lock);
294                 free(mixmonitor);
295                 return;
296         }
297
298         pthread_attr_init(&attr);
299         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
300         ast_pthread_create_background(&thread, &attr, mixmonitor_thread, mixmonitor);
301         pthread_attr_destroy(&attr);
302
303 }
304
305 static int mixmonitor_exec(struct ast_channel *chan, void *data)
306 {
307         int x, readvol = 0, writevol = 0;
308         struct ast_module_user *u;
309         struct ast_flags flags = {0};
310         char *parse;
311         AST_DECLARE_APP_ARGS(args,
312                 AST_APP_ARG(filename);
313                 AST_APP_ARG(options);
314                 AST_APP_ARG(post_process);
315         );
316         
317         if (ast_strlen_zero(data)) {
318                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
319                 return -1;
320         }
321
322         u = ast_module_user_add(chan);
323
324         parse = ast_strdupa(data);
325
326         AST_STANDARD_APP_ARGS(args, parse);
327         
328         if (ast_strlen_zero(args.filename)) {
329                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
330                 ast_module_user_remove(u);
331                 return -1;
332         }
333
334         if (args.options) {
335                 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
336
337                 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
338
339                 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
340                         if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
341                                 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
342                         } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
343                                 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
344                         } else {
345                                 readvol = get_volfactor(x);
346                         }
347                 }
348                 
349                 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
350                         if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
351                                 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
352                         } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
353                                 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
354                         } else {
355                                 writevol = get_volfactor(x);
356                         }
357                 }
358                 
359                 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
360                         if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
361                                 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
362                         } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
363                                 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
364                         } else {
365                                 readvol = writevol = get_volfactor(x);
366                         }
367                 }
368         }
369
370         /* if not provided an absolute path, use the system-configured monitoring directory */
371         if (args.filename[0] != '/') {
372                 char *build;
373
374                 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
375                 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
376                 args.filename = build;
377         }
378
379         pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
380         launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
381
382         ast_module_user_remove(u);
383
384         return 0;
385 }
386
387 static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
388 {
389         struct ast_module_user *u;
390
391         u = ast_module_user_add(chan);
392
393         ast_channel_lock(chan);
394         ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
395         ast_channel_unlock(chan);
396
397         ast_module_user_remove(u);
398
399         return 0;
400 }
401
402 static int mixmonitor_cli(int fd, int argc, char **argv) 
403 {
404         struct ast_channel *chan;
405
406         if (argc < 3)
407                 return RESULT_SHOWUSAGE;
408
409         if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
410                 ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
411                 return RESULT_SUCCESS;
412         }
413
414         if (!strcasecmp(argv[1], "start"))
415                 mixmonitor_exec(chan, argv[3]);
416         else if (!strcasecmp(argv[1], "stop"))
417                 ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
418
419         ast_channel_unlock(chan);
420
421         return RESULT_SUCCESS;
422 }
423
424 static char *complete_mixmonitor_cli(const char *line, const char *word, int pos, int state)
425 {
426         return ast_complete_channels(line, word, pos, state, 2);
427 }
428
429 static struct ast_cli_entry cli_mixmonitor[] = {
430         { { "mixmonitor", NULL, NULL },
431         mixmonitor_cli, "Execute a MixMonitor command.",
432         "mixmonitor <start|stop> <chan_name> [args]\n\n"
433         "The optional arguments are passed to the\n"
434         "MixMonitor application when the 'start' command is used.\n",
435         complete_mixmonitor_cli },
436 };
437
438 static int unload_module(void)
439 {
440         int res;
441
442         ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
443         res = ast_unregister_application(stop_app);
444         res |= ast_unregister_application(app);
445         
446         ast_module_user_hangup_all();
447
448         return res;
449 }
450
451 static int load_module(void)
452 {
453         int res;
454
455         ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
456         res = ast_register_application(app, mixmonitor_exec, synopsis, desc);
457         res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc);
458
459         return res;
460 }
461
462 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");