Doxygen documentation update from oej (issue #5505)
[asterisk/asterisk.git] / apps / app_muxmon.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  *
5  * Copyright (C) 2005, Anthony Minessale II
6  *
7  * Anthony Minessale II <anthmct@yahoo.com>
8  *
9  * This program is free software, distributed under the terms of
10  * the GNU General Public License
11  */
12
13 /*! \file
14  * \brief muxmon() - record a call natively
15  */
16
17 #include <asterisk/file.h>
18 #include <asterisk/logger.h>
19 #include <asterisk/channel.h>
20 #include <asterisk/pbx.h>
21 #include <asterisk/module.h>
22 #include <asterisk/lock.h>
23 #include <asterisk/cli.h>
24 #include <asterisk/options.h>
25 #include <asterisk/app.h>
26 #include <asterisk/translate.h>
27 #include <asterisk/slinfactory.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
32 #define minmax(x,y) x ? (x > y) ? y : ((x < (y * -1)) ? (y * -1) : x) : 0 
33
34 static char *tdesc = "Native Channel Monitoring Module";
35 static char *app = "MuxMon";
36 static char *synopsis = "Record A Call Natively";
37 static char *desc = ""
38 "  MuxMon(<file>.<ext>[|<options>[|<command>]])\n\n"
39 "Records The audio on the current channel to the specified file.\n\n"
40 "Valid Options:\n"
41 " b    - Only save audio to the file while the channel is bridged. Note: does\n"
42 "        not include conferences\n"
43 " a    - Append to the file instead of overwriting it.\n"
44 " v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"        
45 " V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"       
46 " W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
47 "         (range -4 to 4)\n\n"  
48 "<command> will be executed when the recording is over\n"
49 "Any strings matching ^{X} will be unescaped to ${X} and \n"
50 "all variables will be evaluated at that time.\n"
51 "The variable MUXMON_FILENAME will contain the filename used to record.\n"
52 "";
53
54 STANDARD_LOCAL_USER;
55
56 LOCAL_USER_DECL;
57
58 struct muxmon {
59         struct ast_channel *chan;
60         char *filename;
61         char *post_process;
62         unsigned int flags;
63         int readvol;
64         int writevol;
65 };
66
67 typedef enum {
68     MUXFLAG_RUNNING = (1 << 0),
69     MUXFLAG_APPEND = (1 << 1),
70     MUXFLAG_BRIDGED = (1 << 2),
71     MUXFLAG_VOLUME = (1 << 3),
72     MUXFLAG_READVOLUME = (1 << 4),
73     MUXFLAG_WRITEVOLUME = (1 << 5)
74 } muxflags;
75
76
77 AST_DECLARE_OPTIONS(muxmon_opts,{
78     ['a'] = { MUXFLAG_APPEND },
79         ['b'] = { MUXFLAG_BRIDGED },
80         ['v'] = { MUXFLAG_READVOLUME, 1 },
81         ['V'] = { MUXFLAG_WRITEVOLUME, 2 },
82         ['W'] = { MUXFLAG_VOLUME, 3 },
83 });
84
85
86 static void stopmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
87 {
88         struct ast_channel_spy *cptr=NULL, *prev=NULL;
89         int count = 0;
90
91         if (chan) {
92                 while(ast_mutex_trylock(&chan->lock)) {
93                         if (chan->spiers == spy) {
94                                 chan->spiers = NULL;
95                                 return;
96                         }
97                         count++;
98                         if (count > 10) {
99                                 return;
100                         }
101                         sched_yield();
102                 }
103                 
104                 for(cptr=chan->spiers; cptr; cptr=cptr->next) {
105                         if (cptr == spy) {
106                                 if (prev) {
107                                         prev->next = cptr->next;
108                                         cptr->next = NULL;
109                                 } else
110                                         chan->spiers = NULL;
111                         }
112                         prev = cptr;
113                 }
114
115                 ast_mutex_unlock(&chan->lock);
116         }
117 }
118
119 static void startmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
120 {
121
122         struct ast_channel_spy *cptr=NULL;
123         struct ast_channel *peer;
124
125         if (chan) {
126                 ast_mutex_lock(&chan->lock);
127                 if (chan->spiers) {
128                         for(cptr=chan->spiers;cptr->next;cptr=cptr->next);
129                         cptr->next = spy;
130                 } else {
131                         chan->spiers = spy;
132                 }
133                 ast_mutex_unlock(&chan->lock);
134                 
135                 if (ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
136                         ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
137                 }
138         }
139 }
140
141 static int spy_queue_translate(struct ast_channel_spy *spy,
142                                                            struct ast_slinfactory *slinfactory0,
143                                                            struct ast_slinfactory *slinfactory1)
144 {
145         int res = 0;
146         struct ast_frame *f;
147         
148         ast_mutex_lock(&spy->lock);
149         while((f = spy->queue[0])) {
150                 spy->queue[0] = f->next;
151                 ast_slinfactory_feed(slinfactory0, f);
152                 ast_frfree(f);
153         }
154         ast_mutex_unlock(&spy->lock);
155         ast_mutex_lock(&spy->lock);
156         while((f = spy->queue[1])) {
157                 spy->queue[1] = f->next;
158                 ast_slinfactory_feed(slinfactory1, f);
159                 ast_frfree(f);
160         }
161         ast_mutex_unlock(&spy->lock);
162         return res;
163 }
164
165 static void *muxmon_thread(void *obj) 
166 {
167
168         int len0 = 0, len1 = 0, samp0 = 0, samp1 = 0, framelen, maxsamp = 0, x = 0;
169         short buf0[1280], buf1[1280], buf[1280];
170         struct ast_frame frame;
171         struct muxmon *muxmon = obj;
172         struct ast_channel_spy spy;
173         struct ast_filestream *fs = NULL;
174         char *ext, *name;
175         unsigned int oflags;
176         struct ast_slinfactory slinfactory[2];
177         char post_process[1024] = "";
178         
179         name = ast_strdupa(muxmon->chan->name);
180
181         framelen = 320;
182         frame.frametype = AST_FRAME_VOICE;
183         frame.subclass = AST_FORMAT_SLINEAR;
184         frame.data = buf;
185         ast_set_flag(muxmon, MUXFLAG_RUNNING);
186         oflags = O_CREAT|O_WRONLY;
187         ast_slinfactory_init(&slinfactory[0]);
188         ast_slinfactory_init(&slinfactory[1]);
189         
190
191
192         /* for efficiency, use a flag to bypass volume logic when it's not needed */
193         if (muxmon->readvol || muxmon->writevol) {
194                 ast_set_flag(muxmon, MUXFLAG_VOLUME);
195         }
196
197         if ((ext = strchr(muxmon->filename, '.'))) {
198                 *(ext++) = '\0';
199         } else {
200                 ext = "raw";
201         }
202
203         memset(&spy, 0, sizeof(spy));
204         spy.status = CHANSPY_RUNNING;
205         ast_mutex_init(&spy.lock);
206         startmon(muxmon->chan, &spy);
207         if (ast_test_flag(muxmon, MUXFLAG_RUNNING)) {
208                 if (option_verbose > 1) {
209                         ast_verbose(VERBOSE_PREFIX_2 "Begin Muxmon Recording %s\n", name);
210                 }
211
212                 oflags |= ast_test_flag(muxmon, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
213                 
214                 if (!(fs = ast_writefile(muxmon->filename, ext, NULL, oflags, 0, 0644))) {
215                         ast_log(LOG_ERROR, "Cannot open %s\n", muxmon->filename);
216                         spy.status = CHANSPY_DONE;
217                 }  else {
218
219                         if (ast_test_flag(muxmon, MUXFLAG_APPEND)) {
220                                 ast_seekstream(fs, 0, SEEK_END);
221                         }
222
223                         while (ast_test_flag(muxmon, MUXFLAG_RUNNING)) {
224                                 samp0 = samp1 = len0 = len1 = 0;
225
226                                 if (ast_check_hangup(muxmon->chan) || spy.status != CHANSPY_RUNNING) {
227                                         ast_clear_flag(muxmon, MUXFLAG_RUNNING);
228                                         break;
229                                 }
230
231                                 if (ast_test_flag(muxmon, MUXFLAG_BRIDGED) && !ast_bridged_channel(muxmon->chan)) {
232                                         usleep(1000);
233                                         sched_yield();
234                                         continue;
235                                 }
236                                 
237                                 spy_queue_translate(&spy, &slinfactory[0], &slinfactory[1]);
238                                 
239                                 if (slinfactory[0].size < framelen || slinfactory[1].size < framelen) {
240                                         usleep(1000);
241                                         sched_yield();
242                                         continue;
243                                 }
244
245                                 if ((len0 = ast_slinfactory_read(&slinfactory[0], buf0, framelen))) {
246                                         samp0 = len0 / 2;
247                                 }
248                                 if((len1 = ast_slinfactory_read(&slinfactory[1], buf1, framelen))) {
249                                         samp1 = len1 / 2;
250                                 }
251                                 
252                                 if (ast_test_flag(muxmon, MUXFLAG_VOLUME)) {
253                                         if (samp0 && muxmon->readvol > 0) {
254                                                 for(x=0; x < samp0 / 2; x++) {
255                                                         buf0[x] *= muxmon->readvol;
256                                                 }
257                                         } else if (samp0 && muxmon->readvol < 0) {
258                                                 for(x=0; x < samp0 / 2; x++) {
259                                                         buf0[x] /= muxmon->readvol;
260                                                 }
261                                         }
262                                         if (samp1 && muxmon->writevol > 0) {
263                                                 for(x=0; x < samp1 / 2; x++) {
264                                                         buf1[x] *= muxmon->writevol;
265                                                 }
266                                         } else if (muxmon->writevol < 0) {
267                                                 for(x=0; x < samp1 / 2; x++) {
268                                                         buf1[x] /= muxmon->writevol;
269                                                 }
270                                         }
271                                 }
272                                 
273                                 maxsamp = (samp0 > samp1) ? samp0 : samp1;
274
275                                 if (samp0 && samp1) {
276                                         for(x=0; x < maxsamp; x++) {
277                                                 if (x < samp0 && x < samp1) {
278                                                         buf[x] = buf0[x] + buf1[x];
279                                                 } else if (x < samp0) {
280                                                         buf[x] = buf0[x];
281                                                 } else if (x < samp1) {
282                                                         buf[x] = buf1[x];
283                                                 }
284                                         }
285                                 } else if(samp0) {
286                                         memcpy(buf, buf0, len0);
287                                         x = samp0;
288                                 } else if(samp1) {
289                                         memcpy(buf, buf1, len1);
290                                         x = samp1;
291                                 }
292
293                                 frame.samples = x;
294                                 frame.datalen = x * 2;
295                                 ast_writestream(fs, &frame);
296                 
297                                 usleep(1000);
298                                 sched_yield();
299                         }
300                 }
301         }
302
303         if (muxmon->post_process) {
304                 char *p;
305                 for(p = muxmon->post_process; *p ; p++) {
306                         if (*p == '^' && *(p+1) == '{') {
307                                 *p = '$';
308                         }
309                 }
310                 pbx_substitute_variables_helper(muxmon->chan, muxmon->post_process, post_process, sizeof(post_process) - 1);
311                 free(muxmon->post_process);
312                 muxmon->post_process = NULL;
313         }
314
315         stopmon(muxmon->chan, &spy);
316         if (option_verbose > 1) {
317                 ast_verbose(VERBOSE_PREFIX_2 "Finished Recording %s\n", name);
318         }
319         ast_mutex_destroy(&spy.lock);
320         
321         if(fs) {
322                 ast_closestream(fs);
323         }
324         
325         ast_slinfactory_destroy(&slinfactory[0]);
326         ast_slinfactory_destroy(&slinfactory[1]);
327
328         if (muxmon) {
329                 if (muxmon->filename) {
330                         free(muxmon->filename);
331                 }
332                 free(muxmon);
333         }
334
335         if (!ast_strlen_zero(post_process)) {
336                 if (option_verbose > 2) {
337                         ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", post_process);
338                 }
339                 ast_safe_system(post_process);
340         }
341
342         return NULL;
343 }
344
345 static void launch_monitor_thread(struct ast_channel *chan, char *filename, unsigned int flags, int readvol , int writevol, char *post_process) 
346 {
347         pthread_attr_t attr;
348         int result = 0;
349         pthread_t thread;
350         struct muxmon *muxmon;
351
352
353         if (!(muxmon = malloc(sizeof(struct muxmon)))) {
354                 ast_log(LOG_ERROR, "Memory Error!\n");
355                 return;
356         }
357
358         memset(muxmon, 0, sizeof(struct muxmon));
359         muxmon->chan = chan;
360         muxmon->filename = strdup(filename);
361         if(post_process) {
362                 muxmon->post_process = strdup(post_process);
363         }
364         muxmon->readvol = readvol;
365         muxmon->writevol = writevol;
366         muxmon->flags = flags;
367
368         result = pthread_attr_init(&attr);
369         pthread_attr_setschedpolicy(&attr, SCHED_RR);
370         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
371         result = ast_pthread_create(&thread, &attr, muxmon_thread, muxmon);
372         result = pthread_attr_destroy(&attr);
373 }
374
375
376 static int muxmon_exec(struct ast_channel *chan, void *data)
377 {
378         int res = 0, x = 0, readvol = 0, writevol = 0;
379         struct localuser *u;
380         struct ast_flags flags = {0};
381         int argc;
382         char *options = NULL,
383                 *args,
384                 *argv[3],
385                 *filename = NULL,
386                 *post_process = NULL;
387         
388         if (!data || ast_strlen_zero(data)) {
389                 ast_log(LOG_WARNING, "muxmon requires an argument\n");
390                 return -1;
391         }
392
393         LOCAL_USER_ADD(u);
394
395         args = ast_strdupa(data);       
396         if (!args) {
397                 ast_log(LOG_WARNING, "Memory Error!\n");
398                 LOCAL_USER_REMOVE(u);
399                 return -1;
400         }
401
402         if ((argc = ast_separate_app_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
403                 filename = argv[0];
404                 if (argc > 1) {
405                         options = argv[1];
406                 }
407                 if (argc > 2) {
408                         post_process = argv[2];
409                 }
410         }
411         
412         if (!filename || ast_strlen_zero(filename)) {
413                 ast_log(LOG_WARNING, "Muxmon requires an argument (filename)\n");
414                 LOCAL_USER_REMOVE(u);
415                 return -1;
416         }
417
418         if (options) {
419                 char *opts[3] = {};
420                 ast_parseoptions(muxmon_opts, &flags, opts, options);
421
422                 if (ast_test_flag(&flags, MUXFLAG_READVOLUME) && opts[0]) {
423                         if (sscanf(opts[0], "%d", &x) != 1)
424                                 ast_log(LOG_NOTICE, "volume must be a number between -4 and 4\n");
425                         else {
426                                 readvol = minmax(x, 4);
427                                 x = get_volfactor(readvol);
428                                 readvol = minmax(x, 16);
429                         }
430                 }
431                 
432                 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME) && opts[1]) {
433                         if (sscanf(opts[1], "%d", &x) != 1)
434                                 ast_log(LOG_NOTICE, "volume must be a number between -4 and 4\n");
435                         else {
436                                 writevol = minmax(x, 4);
437                                 x = get_volfactor(writevol);
438                                 writevol = minmax(x, 16);
439                         }
440                 }
441
442                 if (ast_test_flag(&flags, MUXFLAG_VOLUME) && opts[2]) {
443                         if (sscanf(opts[2], "%d", &x) != 1)
444                                 ast_log(LOG_NOTICE, "volume must be a number between -4 and 4\n");
445                         else {
446                                 readvol = writevol = minmax(x, 4);
447                                 x = get_volfactor(readvol);
448                                 readvol = minmax(x, 16);
449                                 x = get_volfactor(writevol);
450                                 writevol = minmax(x, 16);
451                         }
452                 }
453         }
454         pbx_builtin_setvar_helper(chan, "MUXMON_FILENAME", filename);
455         launch_monitor_thread(chan, filename, flags.flags, readvol, writevol, post_process);
456
457         LOCAL_USER_REMOVE(u);
458         return res;
459 }
460
461
462 static int muxmon_cli(int fd, int argc, char **argv) 
463 {
464         char *op, *chan_name = NULL, *args = NULL;
465         struct ast_channel *chan;
466
467         if (argc > 2) {
468                 op = argv[1];
469                 chan_name = argv[2];
470
471                 if (argv[3]) {
472                         args = argv[3];
473                 }
474
475                 if (!(chan = ast_get_channel_by_name_prefix_locked(chan_name, strlen(chan_name)))) {
476                         ast_cli(fd, "Invalid Channel!\n");
477                         return -1;
478                 }
479                 if (!strcasecmp(op, "start")) {
480                         muxmon_exec(chan, args);
481                 } else if (!strcasecmp(op, "stop")) {
482                         struct ast_channel_spy *cptr=NULL;
483                         for(cptr=chan->spiers; cptr; cptr=cptr->next) {
484                                 cptr->status = CHANSPY_DONE;
485                         }
486                 }
487                 ast_mutex_unlock(&chan->lock);
488                 return 0;
489         }
490
491         ast_cli(fd, "Usage: muxmon <start|stop> <chan_name> <args>\n");
492         return -1;
493 }
494
495
496 static struct ast_cli_entry cli_muxmon = {
497         { "muxmon", NULL, NULL }, muxmon_cli, 
498         "Execute a monitor command", "muxmon <start|stop> <chan_name> <args>"};
499
500
501 int unload_module(void)
502 {
503         int res;
504
505         res = ast_cli_unregister(&cli_muxmon);
506         res |= ast_unregister_application(app);
507         
508         STANDARD_HANGUP_LOCALUSERS;
509
510         return res;
511 }
512
513 int load_module(void)
514 {
515         int res;
516
517         res = ast_cli_register(&cli_muxmon);
518         res |= ast_register_application(app, muxmon_exec, synopsis, desc);
519
520         return res;
521 }
522
523 char *description(void)
524 {
525         return tdesc;
526 }
527
528 int usecount(void)
529 {
530         int res;
531         STANDARD_USECOUNT(res);
532         return res;
533 }
534
535 char *key()
536 {
537         return ASTERISK_GPL_KEY;
538 }
539