00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "asterisk.h"
00037
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211596 $")
00039
00040 #include <stdlib.h>
00041 #include <stdio.h>
00042 #include <string.h>
00043 #include <unistd.h>
00044
00045 #include "asterisk/file.h"
00046 #include "asterisk/logger.h"
00047 #include "asterisk/channel.h"
00048 #include "asterisk/audiohook.h"
00049 #include "asterisk/pbx.h"
00050 #include "asterisk/module.h"
00051 #include "asterisk/lock.h"
00052 #include "asterisk/cli.h"
00053 #include "asterisk/options.h"
00054 #include "asterisk/app.h"
00055 #include "asterisk/linkedlists.h"
00056 #include "asterisk/utils.h"
00057
00058 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
00059
00060 static const char *app = "MixMonitor";
00061 static const char *synopsis = "Record a call and mix the audio during the recording";
00062 static const char *desc = ""
00063 " MixMonitor(<file>.<ext>[|<options>[|<command>]])\n\n"
00064 "Records the audio on the current channel to the specified file.\n"
00065 "If the filename is an absolute path, uses that path, otherwise\n"
00066 "creates the file in the configured monitoring directory from\n"
00067 "asterisk.conf. Use of StopMixMonitor is required to guarantee\n"
00068 "the audio file is available for processing during dialplan execution.\n\n"
00069 "Valid options:\n"
00070 " a - Append to the file instead of overwriting it.\n"
00071 " b - Only save audio to the file while the channel is bridged.\n"
00072 " Note: Does not include conferences or sounds played to each bridged\n"
00073 " party.\n"
00074 " Note: If you utilize this option inside a Local channel, you must\n"
00075 " make sure the Local channel is not optimized away. To do this,\n"
00076 " be sure to call your Local channel with the '/n' option.\n"
00077 " For example: Dial(Local/start@mycontext/n)\n"
00078 " v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"
00079 " V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"
00080 " W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
00081 " (range -4 to 4)\n\n"
00082 "<command> will be executed when the recording is over\n"
00083 "Any strings matching ^{X} will be unescaped to ${X}.\n"
00084 "All variables will be evaluated at the time MixMonitor is called.\n"
00085 "The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
00086 "";
00087
00088 static const char *stop_app = "StopMixMonitor";
00089 static const char *stop_synopsis = "Stop recording a call through MixMonitor";
00090 static const char *stop_desc = ""
00091 " StopMixMonitor()\n\n"
00092 "Stop recording a call through MixMonitor, and free the recording's file handle.\n"
00093 "";
00094
00095 struct module_symbols *me;
00096
00097 static const char *mixmonitor_spy_type = "MixMonitor";
00098
00099 struct mixmonitor {
00100 struct ast_audiohook audiohook;
00101 char *filename;
00102 char *post_process;
00103 char *name;
00104 unsigned int flags;
00105 struct mixmonitor_ds *mixmonitor_ds;
00106 };
00107
00108 enum {
00109 MUXFLAG_APPEND = (1 << 1),
00110 MUXFLAG_BRIDGED = (1 << 2),
00111 MUXFLAG_VOLUME = (1 << 3),
00112 MUXFLAG_READVOLUME = (1 << 4),
00113 MUXFLAG_WRITEVOLUME = (1 << 5),
00114 } mixmonitor_flags;
00115
00116 enum {
00117 OPT_ARG_READVOLUME = 0,
00118 OPT_ARG_WRITEVOLUME,
00119 OPT_ARG_VOLUME,
00120 OPT_ARG_ARRAY_SIZE,
00121 } mixmonitor_args;
00122
00123 AST_APP_OPTIONS(mixmonitor_opts, {
00124 AST_APP_OPTION('a', MUXFLAG_APPEND),
00125 AST_APP_OPTION('b', MUXFLAG_BRIDGED),
00126 AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
00127 AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
00128 AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
00129 });
00130
00131
00132
00133
00134
00135 struct mixmonitor_ds {
00136 struct ast_channel *chan;
00137
00138
00139
00140
00141
00142
00143
00144 unsigned int destruction_ok;
00145 ast_cond_t destruction_condition;
00146 ast_mutex_t lock;
00147
00148
00149
00150 int fs_quit;
00151 struct ast_filestream *fs;
00152 };
00153
00154 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
00155 {
00156 ast_mutex_lock(&mixmonitor_ds->lock);
00157 if (mixmonitor_ds->fs) {
00158 ast_closestream(mixmonitor_ds->fs);
00159 mixmonitor_ds->fs = NULL;
00160 mixmonitor_ds->fs_quit = 1;
00161 if (option_verbose > 1)
00162 ast_verbose(VERBOSE_PREFIX_2 "MixMonitor close filestream\n");
00163 }
00164 ast_mutex_unlock(&mixmonitor_ds->lock);
00165 }
00166
00167 static void mixmonitor_ds_destroy(void *data)
00168 {
00169 struct mixmonitor_ds *mixmonitor_ds = data;
00170
00171 ast_mutex_lock(&mixmonitor_ds->lock);
00172 mixmonitor_ds->chan = NULL;
00173 mixmonitor_ds->destruction_ok = 1;
00174 ast_cond_signal(&mixmonitor_ds->destruction_condition);
00175 ast_mutex_unlock(&mixmonitor_ds->lock);
00176 }
00177
00178 static void mixmonitor_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00179 {
00180 struct mixmonitor_ds *mixmonitor_ds = data;
00181
00182 ast_mutex_lock(&mixmonitor_ds->lock);
00183 mixmonitor_ds->chan = new_chan;
00184 ast_mutex_unlock(&mixmonitor_ds->lock);
00185 }
00186
00187 static struct ast_datastore_info mixmonitor_ds_info = {
00188 .type = "mixmonitor",
00189 .destroy = mixmonitor_ds_destroy,
00190 .chan_fixup = mixmonitor_ds_chan_fixup,
00191 };
00192
00193 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
00194 {
00195 struct ast_channel *peer;
00196 int res;
00197
00198 if (!chan)
00199 return -1;
00200
00201 res = ast_audiohook_attach(chan, audiohook);
00202
00203 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
00204 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00205
00206 return res;
00207 }
00208
00209 #define SAMPLES_PER_FRAME 160
00210
00211 static void mixmonitor_free(struct mixmonitor *mixmonitor)
00212 {
00213 if (mixmonitor) {
00214 if (mixmonitor->mixmonitor_ds) {
00215 ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
00216 ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
00217 ast_free(mixmonitor->mixmonitor_ds);
00218 }
00219 ast_free(mixmonitor);
00220 }
00221 }
00222
00223 static void *mixmonitor_thread(void *obj)
00224 {
00225 struct mixmonitor *mixmonitor = obj;
00226 struct ast_filestream **fs = NULL;
00227 unsigned int oflags;
00228 char *ext;
00229 int errflag = 0;
00230
00231 if (option_verbose > 1)
00232 ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name);
00233
00234 ast_audiohook_lock(&mixmonitor->audiohook);
00235
00236 fs = &mixmonitor->mixmonitor_ds->fs;
00237
00238 while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
00239 struct ast_frame *fr = NULL;
00240
00241 ast_audiohook_trigger_wait(&mixmonitor->audiohook);
00242
00243 if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
00244 break;
00245
00246 if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR)))
00247 continue;
00248
00249 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00250 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) {
00251 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00252
00253 if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
00254 oflags = O_CREAT | O_WRONLY;
00255 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00256
00257 if ((ext = strrchr(mixmonitor->filename, '.')))
00258 *(ext++) = '\0';
00259 else
00260 ext = "raw";
00261
00262 if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) {
00263 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
00264 errflag = 1;
00265 }
00266 }
00267
00268
00269 if (*fs) {
00270 struct ast_frame *cur;
00271
00272 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00273 ast_writestream(*fs, cur);
00274 }
00275 }
00276 } else {
00277 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00278 }
00279
00280
00281 ast_frame_free(fr, 0);
00282 }
00283
00284 ast_audiohook_detach(&mixmonitor->audiohook);
00285 ast_audiohook_unlock(&mixmonitor->audiohook);
00286 ast_audiohook_destroy(&mixmonitor->audiohook);
00287
00288 mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
00289
00290 if (mixmonitor->post_process) {
00291 if (option_verbose > 2)
00292 ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process);
00293 ast_safe_system(mixmonitor->post_process);
00294 }
00295
00296 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00297 if (!mixmonitor->mixmonitor_ds->destruction_ok) {
00298 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
00299 }
00300 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00301
00302 if (option_verbose > 1)
00303 ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name);
00304
00305 mixmonitor_free(mixmonitor);
00306
00307 return NULL;
00308 }
00309
00310 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
00311 {
00312 struct ast_datastore *datastore = NULL;
00313 struct mixmonitor_ds *mixmonitor_ds;
00314
00315 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
00316 return -1;
00317 }
00318
00319 ast_mutex_init(&mixmonitor_ds->lock);
00320 ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
00321
00322 if (!(datastore = ast_channel_datastore_alloc(&mixmonitor_ds_info, NULL))) {
00323 ast_mutex_destroy(&mixmonitor_ds->lock);
00324 ast_cond_destroy(&mixmonitor_ds->destruction_condition);
00325 ast_free(mixmonitor_ds);
00326 return -1;
00327 }
00328
00329
00330 mixmonitor_ds->chan = chan;
00331 datastore->data = mixmonitor_ds;
00332
00333 ast_channel_lock(chan);
00334 ast_channel_datastore_add(chan, datastore);
00335 ast_channel_unlock(chan);
00336
00337 mixmonitor->mixmonitor_ds = mixmonitor_ds;
00338 return 0;
00339 }
00340
00341 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
00342 int readvol, int writevol, const char *post_process)
00343 {
00344 pthread_attr_t attr;
00345 pthread_t thread;
00346 struct mixmonitor *mixmonitor;
00347 char postprocess2[1024] = "";
00348 size_t len;
00349
00350 len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
00351
00352
00353 if (!ast_strlen_zero(post_process)) {
00354 char *p1, *p2;
00355
00356 p1 = ast_strdupa(post_process);
00357 for (p2 = p1; *p2 ; p2++) {
00358 if (*p2 == '^' && *(p2+1) == '{') {
00359 *p2 = '$';
00360 }
00361 }
00362
00363 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00364 if (!ast_strlen_zero(postprocess2))
00365 len += strlen(postprocess2) + 1;
00366 }
00367
00368
00369 if (!(mixmonitor = calloc(1, len))) {
00370 return;
00371 }
00372
00373
00374 mixmonitor->flags = flags;
00375 if (setup_mixmonitor_ds(mixmonitor, chan)) {
00376 mixmonitor_free(mixmonitor);
00377 return;
00378 }
00379 mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
00380 strcpy(mixmonitor->name, chan->name);
00381 if (!ast_strlen_zero(postprocess2)) {
00382 mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
00383 strcpy(mixmonitor->post_process, postprocess2);
00384 }
00385
00386 mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
00387 strcpy(mixmonitor->filename, filename);
00388
00389
00390 if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
00391 free(mixmonitor);
00392 return;
00393 }
00394
00395 ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
00396
00397 if (readvol)
00398 mixmonitor->audiohook.options.read_volume = readvol;
00399 if (writevol)
00400 mixmonitor->audiohook.options.write_volume = writevol;
00401
00402 if (startmon(chan, &mixmonitor->audiohook)) {
00403 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00404 mixmonitor_spy_type, chan->name);
00405
00406 ast_audiohook_destroy(&mixmonitor->audiohook);
00407 free(mixmonitor);
00408 return;
00409 }
00410
00411 pthread_attr_init(&attr);
00412 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00413 ast_pthread_create_background(&thread, &attr, mixmonitor_thread, mixmonitor);
00414 pthread_attr_destroy(&attr);
00415 }
00416
00417 static int mixmonitor_exec(struct ast_channel *chan, void *data)
00418 {
00419 int x, readvol = 0, writevol = 0;
00420 struct ast_module_user *u;
00421 struct ast_flags flags = {0};
00422 char *parse;
00423 AST_DECLARE_APP_ARGS(args,
00424 AST_APP_ARG(filename);
00425 AST_APP_ARG(options);
00426 AST_APP_ARG(post_process);
00427 );
00428
00429 if (ast_strlen_zero(data)) {
00430 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00431 return -1;
00432 }
00433
00434 u = ast_module_user_add(chan);
00435
00436 parse = ast_strdupa(data);
00437
00438 AST_STANDARD_APP_ARGS(args, parse);
00439
00440 if (ast_strlen_zero(args.filename)) {
00441 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00442 ast_module_user_remove(u);
00443 return -1;
00444 }
00445
00446 if (args.options) {
00447 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
00448
00449 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
00450
00451 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
00452 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
00453 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
00454 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00455 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
00456 } else {
00457 readvol = get_volfactor(x);
00458 }
00459 }
00460
00461 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
00462 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
00463 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
00464 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00465 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
00466 } else {
00467 writevol = get_volfactor(x);
00468 }
00469 }
00470
00471 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
00472 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
00473 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
00474 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00475 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
00476 } else {
00477 readvol = writevol = get_volfactor(x);
00478 }
00479 }
00480 }
00481
00482
00483 if (args.filename[0] != '/') {
00484 char *build;
00485
00486 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
00487 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
00488 args.filename = build;
00489 }
00490
00491 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
00492 launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
00493
00494 ast_module_user_remove(u);
00495
00496 return 0;
00497 }
00498
00499 static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
00500 {
00501 struct ast_module_user *u;
00502 struct ast_datastore *datastore = NULL;
00503
00504
00505
00506 if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) {
00507 mixmonitor_ds_close_fs(datastore->data);
00508 }
00509
00510 u = ast_module_user_add(chan);
00511
00512 ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00513
00514 ast_module_user_remove(u);
00515
00516 return 0;
00517 }
00518
00519 static int mixmonitor_cli(int fd, int argc, char **argv)
00520 {
00521 struct ast_channel *chan;
00522
00523 if (argc < 3)
00524 return RESULT_SHOWUSAGE;
00525
00526 if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
00527 ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
00528 return RESULT_SUCCESS;
00529 }
00530
00531 if (!strcasecmp(argv[1], "start"))
00532 mixmonitor_exec(chan, argv[3]);
00533 else if (!strcasecmp(argv[1], "stop"))
00534 ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00535
00536 ast_channel_unlock(chan);
00537
00538 return RESULT_SUCCESS;
00539 }
00540
00541 static char *complete_mixmonitor_cli(const char *line, const char *word, int pos, int state)
00542 {
00543 return ast_complete_channels(line, word, pos, state, 2);
00544 }
00545
00546 static struct ast_cli_entry cli_mixmonitor[] = {
00547 { { "mixmonitor", NULL, NULL },
00548 mixmonitor_cli, "Execute a MixMonitor command.",
00549 "mixmonitor <start|stop> <chan_name> [args]\n\n"
00550 "The optional arguments are passed to the\n"
00551 "MixMonitor application when the 'start' command is used.\n",
00552 complete_mixmonitor_cli },
00553 };
00554
00555 static int unload_module(void)
00556 {
00557 int res;
00558
00559 ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
00560 res = ast_unregister_application(stop_app);
00561 res |= ast_unregister_application(app);
00562
00563 ast_module_user_hangup_all();
00564
00565 return res;
00566 }
00567
00568 static int load_module(void)
00569 {
00570 int res;
00571
00572 ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
00573 res = ast_register_application(app, mixmonitor_exec, synopsis, desc);
00574 res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc);
00575
00576 return res;
00577 }
00578
00579 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");