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