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