make app_queue 1.2 jump compliant (issue #5580)
[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, 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  * \brief MixMonitor() - Record a call and mix the audio during the recording
26  * \ingroup applications
27  */
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
38 #include "asterisk/file.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/module.h"
43 #include "asterisk/lock.h"
44 #include "asterisk/cli.h"
45 #include "asterisk/options.h"
46 #include "asterisk/app.h"
47 #include "asterisk/linkedlists.h"
48
49 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
50
51 static const char *tdesc = "Mixed Audio Monitoring Application";
52 static const char *app = "MixMonitor";
53 static const char *synopsis = "Record a call and mix the audio during the recording";
54 static const char *desc = ""
55 "  MixMonitor(<file>.<ext>[|<options>[|<command>]])\n\n"
56 "Records the audio on the current channel to the specified file.\n"
57 "If the filename is an absolute path, uses that path, otherwise\n"
58 "creates the file in the configured monitoring directory from\n"
59 "asterisk.conf.\n\n"
60 "Valid options:\n"
61 " a      - Append to the file instead of overwriting it.\n"
62 " b      - Only save audio to the file while the channel is bridged.\n"
63 "          Note: does not include conferences.\n"
64 " v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"        
65 " V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"       
66 " W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
67 "         (range -4 to 4)\n\n"  
68 "<command> will be executed when the recording is over\n"
69 "Any strings matching ^{X} will be unescaped to ${X} and \n"
70 "all variables will be evaluated at that time.\n"
71 "The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
72 "";
73
74 STANDARD_LOCAL_USER;
75
76 LOCAL_USER_DECL;
77
78 static const char *mixmonitor_spy_type = "MixMonitor";
79
80 struct mixmonitor {
81         struct ast_channel *chan;
82         char *filename;
83         char *post_process;
84         unsigned int flags;
85         int readvol;
86         int writevol;
87 };
88
89 enum {
90         MUXFLAG_APPEND = (1 << 1),
91         MUXFLAG_BRIDGED = (1 << 2),
92         MUXFLAG_VOLUME = (1 << 3),
93         MUXFLAG_READVOLUME = (1 << 4),
94         MUXFLAG_WRITEVOLUME = (1 << 5),
95 } mixmonitor_flags;
96
97 enum {
98         OPT_ARG_READVOLUME = 0,
99         OPT_ARG_WRITEVOLUME,
100         OPT_ARG_VOLUME,
101         OPT_ARG_ARRAY_SIZE,
102 } mixmonitor_args;
103
104 AST_APP_OPTIONS(mixmonitor_opts, {
105         AST_APP_OPTION('a', MUXFLAG_APPEND),
106         AST_APP_OPTION('b', MUXFLAG_BRIDGED),
107         AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
108         AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
109         AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
110 });
111
112 static void stopmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
113 {
114         /* If our status has changed, then the channel we're spying on is gone....
115            DON'T TOUCH IT!!!  RUN AWAY!!! */
116         if (spy->status != CHANSPY_RUNNING)
117                 return;
118
119         if (!chan)
120                 return;
121
122         ast_mutex_lock(&chan->lock);
123         ast_channel_spy_remove(chan, spy);
124         ast_mutex_unlock(&chan->lock);
125 }
126
127 static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
128 {
129         struct ast_channel *peer;
130         int res;
131
132         if (!chan)
133                 return -1;
134
135         ast_mutex_lock(&chan->lock);
136         res = ast_channel_spy_add(chan, spy);
137         ast_mutex_unlock(&chan->lock);
138                 
139         if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
140                 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
141
142         return res;
143 }
144
145 #define SAMPLES_PER_FRAME 160
146
147 static void *mixmonitor_thread(void *obj) 
148 {
149         struct mixmonitor *mixmonitor = obj;
150         struct ast_channel_spy spy;
151         struct ast_filestream *fs = NULL;
152         char *ext, *name;
153         unsigned int oflags;
154         struct ast_frame *f;
155         char post_process[1024] = "";
156         
157         STANDARD_INCREMENT_USECOUNT;
158
159         name = ast_strdupa(mixmonitor->chan->name);
160
161         oflags = O_CREAT|O_WRONLY;
162         oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
163                 
164         if ((ext = strrchr(mixmonitor->filename, '.'))) {
165                 *(ext++) = '\0';
166         } else {
167                 ext = "raw";
168         }
169
170         fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644);
171         if (!fs) {
172                 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
173                 goto out;
174         }
175
176         if (ast_test_flag(mixmonitor, MUXFLAG_APPEND))
177                 ast_seekstream(fs, 0, SEEK_END);
178         
179         memset(&spy, 0, sizeof(spy));
180         ast_set_flag(&spy, CHANSPY_FORMAT_AUDIO);
181         ast_set_flag(&spy, CHANSPY_MIXAUDIO);
182         spy.type = mixmonitor_spy_type;
183         spy.status = CHANSPY_RUNNING;
184         spy.read_queue.format = AST_FORMAT_SLINEAR;
185         spy.write_queue.format = AST_FORMAT_SLINEAR;
186         if (mixmonitor->readvol) {
187                 ast_set_flag(&spy, CHANSPY_READ_VOLADJUST);
188                 spy.read_vol_adjustment = mixmonitor->readvol;
189         }
190         if (mixmonitor->writevol) {
191                 ast_set_flag(&spy, CHANSPY_WRITE_VOLADJUST);
192                 spy.write_vol_adjustment = mixmonitor->writevol;
193         }
194         ast_mutex_init(&spy.lock);
195
196         if (startmon(mixmonitor->chan, &spy)) {
197                 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
198                         spy.type, mixmonitor->chan->name);
199                 goto out2;
200         }
201
202         if (option_verbose > 1)
203                 ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", name);
204         
205         while (1) {
206                 struct ast_frame *next;
207                 int write;
208
209                 ast_mutex_lock(&spy.lock);
210
211                 ast_channel_spy_trigger_wait(&spy);
212                 
213                 if (ast_check_hangup(mixmonitor->chan) || spy.status != CHANSPY_RUNNING) {
214                         ast_mutex_unlock(&spy.lock);
215                         break;
216                 }
217                 
218                 while (1) {
219                         if (!(f = ast_channel_spy_read_frame(&spy, SAMPLES_PER_FRAME)))
220                                 break;
221
222                         write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
223                                  ast_bridged_channel(mixmonitor->chan));
224
225                         /* it is possible for ast_channel_spy_read_frame() to return a chain
226                            of frames if a queue flush was necessary, so process them
227                         */
228                         for (; f; f = next) {
229                                 next = f->next;
230                                 if (write)
231                                         ast_writestream(fs, f);
232                                 ast_frfree(f);
233                         }
234                 }
235
236                 ast_mutex_unlock(&spy.lock);
237         }
238         
239         if (mixmonitor->post_process) {
240                 char *p;
241
242                 for (p = mixmonitor->post_process; *p ; p++) {
243                         if (*p == '^' && *(p+1) == '{') {
244                                 *p = '$';
245                         }
246                 }
247                 pbx_substitute_variables_helper(mixmonitor->chan, mixmonitor->post_process, post_process, sizeof(post_process) - 1);
248         }
249
250         stopmon(mixmonitor->chan, &spy);
251
252         if (option_verbose > 1)
253                 ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", name);
254
255         if (!ast_strlen_zero(post_process)) {
256                 if (option_verbose > 2)
257                         ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", post_process);
258                 ast_safe_system(post_process);
259         }
260
261 out2:
262         ast_mutex_destroy(&spy.lock);
263
264         if (fs)
265                 ast_closestream(fs);
266
267 out:
268         free(mixmonitor);
269
270         STANDARD_DECREMENT_USECOUNT;
271
272         return NULL;
273 }
274
275 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
276                                   int readvol, int writevol, const char *post_process) 
277 {
278         pthread_attr_t attr;
279         pthread_t thread;
280         struct mixmonitor *mixmonitor;
281         int len;
282
283         len = sizeof(*mixmonitor) + strlen(filename) + 1;
284         if (!ast_strlen_zero(post_process))
285                 len += strlen(post_process) + 1;
286
287         if (!(mixmonitor = calloc(1, len))) {
288                 ast_log(LOG_ERROR, "Memory Error!\n");
289                 return;
290         }
291
292         mixmonitor->chan = chan;
293         mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor);
294         strcpy(mixmonitor->filename, filename);
295         if (!ast_strlen_zero(post_process)) {
296                 mixmonitor->post_process = mixmonitor->filename + strlen(filename) + 1;
297                 strcpy(mixmonitor->post_process, post_process);
298         }
299         mixmonitor->readvol = readvol;
300         mixmonitor->writevol = writevol;
301         mixmonitor->flags = flags;
302
303         pthread_attr_init(&attr);
304         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
305         ast_pthread_create(&thread, &attr, mixmonitor_thread, mixmonitor);
306         pthread_attr_destroy(&attr);
307 }
308
309 static int mixmonitor_exec(struct ast_channel *chan, void *data)
310 {
311         int x, readvol = 0, writevol = 0;
312         struct localuser *u;
313         struct ast_flags flags = {0};
314         char *parse;
315         AST_DECLARE_APP_ARGS(args,
316                 AST_APP_ARG(filename);
317                 AST_APP_ARG(options);
318                 AST_APP_ARG(post_process);
319         );
320         
321         if (ast_strlen_zero(data)) {
322                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
323                 return -1;
324         }
325
326         LOCAL_USER_ADD(u);
327
328         if (!(parse = ast_strdupa(data))) {
329                 ast_log(LOG_WARNING, "Memory Error!\n");
330                 LOCAL_USER_REMOVE(u);
331                 return -1;
332         }
333
334         AST_STANDARD_APP_ARGS(args, parse);
335         
336         if (ast_strlen_zero(args.filename)) {
337                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
338                 LOCAL_USER_REMOVE(u);
339                 return -1;
340         }
341
342         if (args.options) {
343                 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
344
345                 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
346
347                 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
348                         if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
349                                 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
350                         } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
351                                 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
352                         } else {
353                                 readvol = get_volfactor(x);
354                         }
355                 }
356                 
357                 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
358                         if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
359                                 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
360                         } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
361                                 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
362                         } else {
363                                 writevol = get_volfactor(x);
364                         }
365                 }
366                 
367                 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
368                         if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
369                                 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
370                         } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
371                                 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
372                         } else {
373                                 readvol = writevol = get_volfactor(x);
374                         }
375                 }
376         }
377
378         /* if not provided an absolute path, use the system-configured monitoring directory */
379         if (args.filename[0] != '/') {
380                 char *build;
381
382                 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
383                 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
384                 args.filename = build;
385         }
386
387         pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
388         launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
389
390         LOCAL_USER_REMOVE(u);
391
392         return 0;
393 }
394
395 static int mixmonitor_cli(int fd, int argc, char **argv) 
396 {
397         struct ast_channel *chan;
398
399         if (argc < 3)
400                 return RESULT_SHOWUSAGE;
401
402         if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
403                 ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
404                 return RESULT_SUCCESS;
405         }
406
407         if (!strcasecmp(argv[1], "start"))
408                 mixmonitor_exec(chan, argv[3]);
409         else if (!strcasecmp(argv[1], "stop"))
410                 ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
411
412         ast_mutex_unlock(&chan->lock);
413
414         return RESULT_SUCCESS;
415 }
416
417
418 static struct ast_cli_entry cli_mixmonitor = {
419         { "mixmonitor", NULL, NULL },
420         mixmonitor_cli, 
421         "Execute a MixMonitor command",
422         "mixmonitor <start|stop> <chan_name> [<args>]\n"
423 };
424
425
426 int unload_module(void)
427 {
428         int res;
429
430         res = ast_cli_unregister(&cli_mixmonitor);
431         res |= ast_unregister_application(app);
432         
433         STANDARD_HANGUP_LOCALUSERS;
434
435         return res;
436 }
437
438 int load_module(void)
439 {
440         int res;
441
442         res = ast_cli_register(&cli_mixmonitor);
443         res |= ast_register_application(app, mixmonitor_exec, synopsis, desc);
444
445         return res;
446 }
447
448 char *description(void)
449 {
450         return (char *) tdesc;
451 }
452
453 int usecount(void)
454 {
455         int res;
456
457         STANDARD_USECOUNT(res);
458
459         return res;
460 }
461
462 char *key()
463 {
464         return ASTERISK_GPL_KEY;
465 }