308f82b4d6eabe6ce377c32000cd5dbec6a4fef6
[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}.\n"
79 "All variables will be evaluated at the time MixMonitor is called.\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         ast_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_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 = ast_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) + strlen(filename) + 2;
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                 ast_free(mixmonitor);
295                 return;
296         }
297
298         ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
299
300 }
301
302 static int mixmonitor_exec(struct ast_channel *chan, void *data)
303 {
304         int x, readvol = 0, writevol = 0;
305         struct ast_module_user *u;
306         struct ast_flags flags = {0};
307         char *parse, *tmp, *slash;
308         AST_DECLARE_APP_ARGS(args,
309                 AST_APP_ARG(filename);
310                 AST_APP_ARG(options);
311                 AST_APP_ARG(post_process);
312         );
313         
314         if (ast_strlen_zero(data)) {
315                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
316                 return -1;
317         }
318
319         u = ast_module_user_add(chan);
320
321         parse = ast_strdupa(data);
322
323         AST_STANDARD_APP_ARGS(args, parse);
324         
325         if (ast_strlen_zero(args.filename)) {
326                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
327                 ast_module_user_remove(u);
328                 return -1;
329         }
330
331         if (args.options) {
332                 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
333
334                 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
335
336                 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
337                         if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
338                                 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
339                         } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
340                                 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
341                         } else {
342                                 readvol = get_volfactor(x);
343                         }
344                 }
345                 
346                 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
347                         if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
348                                 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
349                         } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
350                                 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
351                         } else {
352                                 writevol = get_volfactor(x);
353                         }
354                 }
355                 
356                 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
357                         if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
358                                 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
359                         } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
360                                 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
361                         } else {
362                                 readvol = writevol = get_volfactor(x);
363                         }
364                 }
365         }
366
367         /* if not provided an absolute path, use the system-configured monitoring directory */
368         if (args.filename[0] != '/') {
369                 char *build;
370
371                 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
372                 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
373                 args.filename = build;
374         }
375
376         tmp = ast_strdupa(args.filename);
377         if ((slash = strrchr(tmp, '/')))
378                 *slash = '\0';
379         ast_mkdir(tmp, 0777);
380
381         pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
382         launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
383
384         ast_module_user_remove(u);
385
386         return 0;
387 }
388
389 static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
390 {
391         struct ast_module_user *u;
392
393         u = ast_module_user_add(chan);
394
395         ast_channel_lock(chan);
396         ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
397         ast_channel_unlock(chan);
398
399         ast_module_user_remove(u);
400
401         return 0;
402 }
403
404 static int mixmonitor_cli(int fd, int argc, char **argv) 
405 {
406         struct ast_channel *chan;
407
408         if (argc < 3)
409                 return RESULT_SHOWUSAGE;
410
411         if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
412                 ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
413                 return RESULT_SUCCESS;
414         }
415
416         if (!strcasecmp(argv[1], "start"))
417                 mixmonitor_exec(chan, argv[3]);
418         else if (!strcasecmp(argv[1], "stop"))
419                 ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
420
421         ast_channel_unlock(chan);
422
423         return RESULT_SUCCESS;
424 }
425
426 static char *complete_mixmonitor_cli(const char *line, const char *word, int pos, int state)
427 {
428         char *options[] = {"start", "stop", NULL};
429
430         if (pos == 1)
431                 return ast_cli_complete (word, options, state);
432
433         return ast_complete_channels(line, word, pos, state, 2);
434 }
435
436 static struct ast_cli_entry cli_mixmonitor[] = {
437         { { "mixmonitor", NULL, NULL },
438         mixmonitor_cli, "Execute a MixMonitor command.",
439         "mixmonitor <start|stop> <chan_name> [args]\n\n"
440         "The optional arguments are passed to the\n"
441         "MixMonitor application when the 'start' command is used.\n",
442         complete_mixmonitor_cli },
443 };
444
445 static int unload_module(void)
446 {
447         int res;
448
449         ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
450         res = ast_unregister_application(stop_app);
451         res |= ast_unregister_application(app);
452         
453         return res;
454 }
455
456 static int load_module(void)
457 {
458         int res;
459
460         ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
461         res = ast_register_application(app, mixmonitor_exec, synopsis, desc);
462         res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc);
463
464         return res;
465 }
466
467 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");