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: 237657 $")
00039
00040 #include "asterisk/paths.h"
00041 #include "asterisk/file.h"
00042 #include "asterisk/audiohook.h"
00043 #include "asterisk/pbx.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/app.h"
00047 #include "asterisk/channel.h"
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
00130
00131 static const char *app = "MixMonitor";
00132
00133 static const char *stop_app = "StopMixMonitor";
00134
00135 struct module_symbols *me;
00136
00137 static const char *mixmonitor_spy_type = "MixMonitor";
00138
00139 struct mixmonitor {
00140 struct ast_audiohook audiohook;
00141 char *filename;
00142 char *post_process;
00143 char *name;
00144 unsigned int flags;
00145 struct mixmonitor_ds *mixmonitor_ds;
00146 };
00147
00148 enum {
00149 MUXFLAG_APPEND = (1 << 1),
00150 MUXFLAG_BRIDGED = (1 << 2),
00151 MUXFLAG_VOLUME = (1 << 3),
00152 MUXFLAG_READVOLUME = (1 << 4),
00153 MUXFLAG_WRITEVOLUME = (1 << 5),
00154 } mixmonitor_flags;
00155
00156 enum {
00157 OPT_ARG_READVOLUME = 0,
00158 OPT_ARG_WRITEVOLUME,
00159 OPT_ARG_VOLUME,
00160 OPT_ARG_ARRAY_SIZE,
00161 } mixmonitor_args;
00162
00163 AST_APP_OPTIONS(mixmonitor_opts, {
00164 AST_APP_OPTION('a', MUXFLAG_APPEND),
00165 AST_APP_OPTION('b', MUXFLAG_BRIDGED),
00166 AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
00167 AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
00168 AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
00169 });
00170
00171
00172
00173
00174
00175 struct mixmonitor_ds {
00176 struct ast_channel *chan;
00177
00178
00179
00180
00181
00182
00183
00184 unsigned int destruction_ok;
00185 ast_cond_t destruction_condition;
00186 ast_mutex_t lock;
00187
00188
00189
00190 int fs_quit;
00191 struct ast_filestream *fs;
00192 struct ast_audiohook *audiohook;
00193 };
00194
00195
00196
00197
00198
00199 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
00200 {
00201 if (mixmonitor_ds->fs) {
00202 ast_closestream(mixmonitor_ds->fs);
00203 mixmonitor_ds->fs = NULL;
00204 mixmonitor_ds->fs_quit = 1;
00205 ast_verb(2, "MixMonitor close filestream\n");
00206 }
00207 }
00208
00209 static void mixmonitor_ds_destroy(void *data)
00210 {
00211 struct mixmonitor_ds *mixmonitor_ds = data;
00212
00213 ast_mutex_lock(&mixmonitor_ds->lock);
00214 mixmonitor_ds->chan = NULL;
00215 mixmonitor_ds->audiohook = NULL;
00216 mixmonitor_ds->destruction_ok = 1;
00217 ast_cond_signal(&mixmonitor_ds->destruction_condition);
00218 ast_mutex_unlock(&mixmonitor_ds->lock);
00219 }
00220
00221 static void mixmonitor_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00222 {
00223 struct mixmonitor_ds *mixmonitor_ds = data;
00224
00225 ast_mutex_lock(&mixmonitor_ds->lock);
00226 mixmonitor_ds->chan = new_chan;
00227 ast_mutex_unlock(&mixmonitor_ds->lock);
00228 }
00229
00230 static struct ast_datastore_info mixmonitor_ds_info = {
00231 .type = "mixmonitor",
00232 .destroy = mixmonitor_ds_destroy,
00233 .chan_fixup = mixmonitor_ds_chan_fixup,
00234 };
00235
00236 static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
00237 {
00238 if (mixmonitor->mixmonitor_ds) {
00239 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00240 mixmonitor->mixmonitor_ds->audiohook = NULL;
00241 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00242 }
00243
00244 ast_audiohook_lock(&mixmonitor->audiohook);
00245 ast_audiohook_detach(&mixmonitor->audiohook);
00246 ast_audiohook_unlock(&mixmonitor->audiohook);
00247 ast_audiohook_destroy(&mixmonitor->audiohook);
00248 }
00249
00250 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
00251 {
00252 struct ast_channel *peer = NULL;
00253 int res = 0;
00254
00255 if (!chan)
00256 return -1;
00257
00258 ast_audiohook_attach(chan, audiohook);
00259
00260 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
00261 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00262
00263 return res;
00264 }
00265
00266 #define SAMPLES_PER_FRAME 160
00267
00268 static void mixmonitor_free(struct mixmonitor *mixmonitor)
00269 {
00270 if (mixmonitor) {
00271 if (mixmonitor->mixmonitor_ds) {
00272 ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
00273 ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
00274 ast_free(mixmonitor->mixmonitor_ds);
00275 }
00276 ast_free(mixmonitor);
00277 }
00278 }
00279
00280 static void *mixmonitor_thread(void *obj)
00281 {
00282 struct mixmonitor *mixmonitor = obj;
00283 struct ast_filestream **fs = NULL;
00284 unsigned int oflags;
00285 char *ext;
00286 int errflag = 0;
00287
00288 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
00289
00290 fs = &mixmonitor->mixmonitor_ds->fs;
00291
00292
00293 ast_audiohook_lock(&mixmonitor->audiohook);
00294 while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
00295 struct ast_frame *fr = NULL;
00296
00297 ast_audiohook_trigger_wait(&mixmonitor->audiohook);
00298
00299 if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
00300 break;
00301
00302 if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR)))
00303 continue;
00304
00305
00306
00307 ast_audiohook_unlock(&mixmonitor->audiohook);
00308
00309 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00310 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) {
00311
00312 if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
00313 oflags = O_CREAT | O_WRONLY;
00314 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00315
00316 if ((ext = strrchr(mixmonitor->filename, '.')))
00317 *(ext++) = '\0';
00318 else
00319 ext = "raw";
00320
00321 if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) {
00322 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
00323 errflag = 1;
00324 }
00325 }
00326
00327
00328 if (*fs) {
00329 struct ast_frame *cur;
00330
00331 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00332 ast_writestream(*fs, cur);
00333 }
00334 }
00335 }
00336 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00337
00338
00339 ast_frame_free(fr, 0);
00340 ast_audiohook_lock(&mixmonitor->audiohook);
00341 }
00342
00343 ast_audiohook_unlock(&mixmonitor->audiohook);
00344
00345
00346 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00347 mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
00348 if (!mixmonitor->mixmonitor_ds->destruction_ok) {
00349 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
00350 }
00351 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00352
00353
00354 destroy_monitor_audiohook(mixmonitor);
00355
00356 if (mixmonitor->post_process) {
00357 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
00358 ast_safe_system(mixmonitor->post_process);
00359 }
00360
00361 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
00362 mixmonitor_free(mixmonitor);
00363 return NULL;
00364 }
00365
00366 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
00367 {
00368 struct ast_datastore *datastore = NULL;
00369 struct mixmonitor_ds *mixmonitor_ds;
00370
00371 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
00372 return -1;
00373 }
00374
00375 ast_mutex_init(&mixmonitor_ds->lock);
00376 ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
00377
00378 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) {
00379 ast_mutex_destroy(&mixmonitor_ds->lock);
00380 ast_cond_destroy(&mixmonitor_ds->destruction_condition);
00381 ast_free(mixmonitor_ds);
00382 return -1;
00383 }
00384
00385
00386 mixmonitor_ds->chan = chan;
00387 mixmonitor_ds->audiohook = &mixmonitor->audiohook;
00388 datastore->data = mixmonitor_ds;
00389
00390 ast_channel_lock(chan);
00391 ast_channel_datastore_add(chan, datastore);
00392 ast_channel_unlock(chan);
00393
00394 mixmonitor->mixmonitor_ds = mixmonitor_ds;
00395 return 0;
00396 }
00397
00398 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
00399 int readvol, int writevol, const char *post_process)
00400 {
00401 pthread_t thread;
00402 struct mixmonitor *mixmonitor;
00403 char postprocess2[1024] = "";
00404 size_t len;
00405
00406 len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
00407
00408 postprocess2[0] = 0;
00409
00410 if (!ast_strlen_zero(post_process)) {
00411 char *p1, *p2;
00412
00413 p1 = ast_strdupa(post_process);
00414 for (p2 = p1; *p2 ; p2++) {
00415 if (*p2 == '^' && *(p2+1) == '{') {
00416 *p2 = '$';
00417 }
00418 }
00419 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00420 if (!ast_strlen_zero(postprocess2))
00421 len += strlen(postprocess2) + 1;
00422 }
00423
00424
00425 if (!(mixmonitor = ast_calloc(1, len))) {
00426 return;
00427 }
00428
00429
00430 if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
00431 mixmonitor_free(mixmonitor);
00432 return;
00433 }
00434
00435
00436 mixmonitor->flags = flags;
00437 if (setup_mixmonitor_ds(mixmonitor, chan)) {
00438 mixmonitor_free(mixmonitor);
00439 return;
00440 }
00441 mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
00442 strcpy(mixmonitor->name, chan->name);
00443 if (!ast_strlen_zero(postprocess2)) {
00444 mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
00445 strcpy(mixmonitor->post_process, postprocess2);
00446 }
00447
00448 mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
00449 strcpy(mixmonitor->filename, filename);
00450
00451 ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
00452
00453 if (readvol)
00454 mixmonitor->audiohook.options.read_volume = readvol;
00455 if (writevol)
00456 mixmonitor->audiohook.options.write_volume = writevol;
00457
00458 if (startmon(chan, &mixmonitor->audiohook)) {
00459 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00460 mixmonitor_spy_type, chan->name);
00461 ast_audiohook_destroy(&mixmonitor->audiohook);
00462 mixmonitor_free(mixmonitor);
00463 return;
00464 }
00465
00466 ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
00467 }
00468
00469 static int mixmonitor_exec(struct ast_channel *chan, void *data)
00470 {
00471 int x, readvol = 0, writevol = 0;
00472 struct ast_flags flags = {0};
00473 char *parse, *tmp, *slash;
00474 AST_DECLARE_APP_ARGS(args,
00475 AST_APP_ARG(filename);
00476 AST_APP_ARG(options);
00477 AST_APP_ARG(post_process);
00478 );
00479
00480 if (ast_strlen_zero(data)) {
00481 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00482 return -1;
00483 }
00484
00485 parse = ast_strdupa(data);
00486
00487 AST_STANDARD_APP_ARGS(args, parse);
00488
00489 if (ast_strlen_zero(args.filename)) {
00490 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00491 return -1;
00492 }
00493
00494 if (args.options) {
00495 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
00496
00497 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
00498
00499 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
00500 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
00501 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
00502 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00503 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
00504 } else {
00505 readvol = get_volfactor(x);
00506 }
00507 }
00508
00509 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
00510 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
00511 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
00512 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00513 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
00514 } else {
00515 writevol = get_volfactor(x);
00516 }
00517 }
00518
00519 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
00520 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
00521 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
00522 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00523 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
00524 } else {
00525 readvol = writevol = get_volfactor(x);
00526 }
00527 }
00528 }
00529
00530
00531 if (args.filename[0] != '/') {
00532 char *build;
00533
00534 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
00535 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
00536 args.filename = build;
00537 }
00538
00539 tmp = ast_strdupa(args.filename);
00540 if ((slash = strrchr(tmp, '/')))
00541 *slash = '\0';
00542 ast_mkdir(tmp, 0777);
00543
00544 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
00545 launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
00546
00547 return 0;
00548 }
00549
00550 static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
00551 {
00552 struct ast_datastore *datastore = NULL;
00553
00554 ast_channel_lock(chan);
00555 ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00556 if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) {
00557 struct mixmonitor_ds *mixmonitor_ds = datastore->data;
00558
00559 ast_mutex_lock(&mixmonitor_ds->lock);
00560
00561
00562
00563 mixmonitor_ds_close_fs(mixmonitor_ds);
00564
00565
00566
00567
00568 if (mixmonitor_ds->audiohook) {
00569 ast_audiohook_lock(mixmonitor_ds->audiohook);
00570 ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
00571 ast_audiohook_unlock(mixmonitor_ds->audiohook);
00572 mixmonitor_ds->audiohook = NULL;
00573 }
00574
00575 ast_mutex_unlock(&mixmonitor_ds->lock);
00576
00577
00578 if (!ast_channel_datastore_remove(chan, datastore)) {
00579 ast_datastore_free(datastore);
00580 }
00581 }
00582 ast_channel_unlock(chan);
00583
00584 return 0;
00585 }
00586
00587 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00588 {
00589 struct ast_channel *chan;
00590
00591 switch (cmd) {
00592 case CLI_INIT:
00593 e->command = "mixmonitor {start|stop}";
00594 e->usage =
00595 "Usage: mixmonitor <start|stop> <chan_name> [args]\n"
00596 " The optional arguments are passed to the MixMonitor\n"
00597 " application when the 'start' command is used.\n";
00598 return NULL;
00599 case CLI_GENERATE:
00600 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00601 }
00602
00603 if (a->argc < 3)
00604 return CLI_SHOWUSAGE;
00605
00606 if (!(chan = ast_get_channel_by_name_prefix_locked(a->argv[2], strlen(a->argv[2])))) {
00607 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
00608
00609 return CLI_SUCCESS;
00610 }
00611
00612 if (!strcasecmp(a->argv[1], "start")) {
00613 mixmonitor_exec(chan, a->argv[3]);
00614 ast_channel_unlock(chan);
00615 } else {
00616 ast_channel_unlock(chan);
00617 ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00618 }
00619
00620 return CLI_SUCCESS;
00621 }
00622
00623 static struct ast_cli_entry cli_mixmonitor[] = {
00624 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
00625 };
00626
00627 static int unload_module(void)
00628 {
00629 int res;
00630
00631 ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
00632 res = ast_unregister_application(stop_app);
00633 res |= ast_unregister_application(app);
00634
00635 return res;
00636 }
00637
00638 static int load_module(void)
00639 {
00640 int res;
00641
00642 ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
00643 res = ast_register_application_xml(app, mixmonitor_exec);
00644 res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
00645
00646 return res;
00647 }
00648
00649 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");