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