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