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: 257740 $")
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 if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) {
00298 ast_audiohook_trigger_wait(&mixmonitor->audiohook);
00299
00300 if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00301 break;
00302 }
00303 continue;
00304 }
00305
00306
00307
00308 ast_audiohook_unlock(&mixmonitor->audiohook);
00309
00310 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00311 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) {
00312
00313 if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
00314 oflags = O_CREAT | O_WRONLY;
00315 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00316
00317 if ((ext = strrchr(mixmonitor->filename, '.')))
00318 *(ext++) = '\0';
00319 else
00320 ext = "raw";
00321
00322 if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) {
00323 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
00324 errflag = 1;
00325 }
00326 }
00327
00328
00329 if (*fs) {
00330 struct ast_frame *cur;
00331
00332 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00333 ast_writestream(*fs, cur);
00334 }
00335 }
00336 }
00337 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00338
00339
00340 ast_frame_free(fr, 0);
00341 ast_audiohook_lock(&mixmonitor->audiohook);
00342 }
00343
00344 ast_audiohook_unlock(&mixmonitor->audiohook);
00345
00346
00347 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00348 mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
00349 if (!mixmonitor->mixmonitor_ds->destruction_ok) {
00350 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
00351 }
00352 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00353
00354
00355 destroy_monitor_audiohook(mixmonitor);
00356
00357 if (mixmonitor->post_process) {
00358 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
00359 ast_safe_system(mixmonitor->post_process);
00360 }
00361
00362 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
00363 mixmonitor_free(mixmonitor);
00364 return NULL;
00365 }
00366
00367 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
00368 {
00369 struct ast_datastore *datastore = NULL;
00370 struct mixmonitor_ds *mixmonitor_ds;
00371
00372 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
00373 return -1;
00374 }
00375
00376 ast_mutex_init(&mixmonitor_ds->lock);
00377 ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
00378
00379 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) {
00380 ast_mutex_destroy(&mixmonitor_ds->lock);
00381 ast_cond_destroy(&mixmonitor_ds->destruction_condition);
00382 ast_free(mixmonitor_ds);
00383 return -1;
00384 }
00385
00386
00387 mixmonitor_ds->chan = chan;
00388 mixmonitor_ds->audiohook = &mixmonitor->audiohook;
00389 datastore->data = mixmonitor_ds;
00390
00391 ast_channel_lock(chan);
00392 ast_channel_datastore_add(chan, datastore);
00393 ast_channel_unlock(chan);
00394
00395 mixmonitor->mixmonitor_ds = mixmonitor_ds;
00396 return 0;
00397 }
00398
00399 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
00400 int readvol, int writevol, const char *post_process)
00401 {
00402 pthread_t thread;
00403 struct mixmonitor *mixmonitor;
00404 char postprocess2[1024] = "";
00405 size_t len;
00406
00407 len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
00408
00409 postprocess2[0] = 0;
00410
00411 if (!ast_strlen_zero(post_process)) {
00412 char *p1, *p2;
00413
00414 p1 = ast_strdupa(post_process);
00415 for (p2 = p1; *p2 ; p2++) {
00416 if (*p2 == '^' && *(p2+1) == '{') {
00417 *p2 = '$';
00418 }
00419 }
00420 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00421 if (!ast_strlen_zero(postprocess2))
00422 len += strlen(postprocess2) + 1;
00423 }
00424
00425
00426 if (!(mixmonitor = ast_calloc(1, len))) {
00427 return;
00428 }
00429
00430
00431 if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
00432 mixmonitor_free(mixmonitor);
00433 return;
00434 }
00435
00436
00437 mixmonitor->flags = flags;
00438 if (setup_mixmonitor_ds(mixmonitor, chan)) {
00439 mixmonitor_free(mixmonitor);
00440 return;
00441 }
00442 mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
00443 strcpy(mixmonitor->name, chan->name);
00444 if (!ast_strlen_zero(postprocess2)) {
00445 mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
00446 strcpy(mixmonitor->post_process, postprocess2);
00447 }
00448
00449 mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
00450 strcpy(mixmonitor->filename, filename);
00451
00452 ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
00453
00454 if (readvol)
00455 mixmonitor->audiohook.options.read_volume = readvol;
00456 if (writevol)
00457 mixmonitor->audiohook.options.write_volume = writevol;
00458
00459 if (startmon(chan, &mixmonitor->audiohook)) {
00460 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00461 mixmonitor_spy_type, chan->name);
00462 ast_audiohook_destroy(&mixmonitor->audiohook);
00463 mixmonitor_free(mixmonitor);
00464 return;
00465 }
00466
00467 ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
00468 }
00469
00470 static int mixmonitor_exec(struct ast_channel *chan, void *data)
00471 {
00472 int x, readvol = 0, writevol = 0;
00473 struct ast_flags flags = {0};
00474 char *parse, *tmp, *slash;
00475 AST_DECLARE_APP_ARGS(args,
00476 AST_APP_ARG(filename);
00477 AST_APP_ARG(options);
00478 AST_APP_ARG(post_process);
00479 );
00480
00481 if (ast_strlen_zero(data)) {
00482 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00483 return -1;
00484 }
00485
00486 parse = ast_strdupa(data);
00487
00488 AST_STANDARD_APP_ARGS(args, parse);
00489
00490 if (ast_strlen_zero(args.filename)) {
00491 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00492 return -1;
00493 }
00494
00495 if (args.options) {
00496 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
00497
00498 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
00499
00500 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
00501 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
00502 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
00503 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00504 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
00505 } else {
00506 readvol = get_volfactor(x);
00507 }
00508 }
00509
00510 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
00511 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
00512 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
00513 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00514 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
00515 } else {
00516 writevol = get_volfactor(x);
00517 }
00518 }
00519
00520 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
00521 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
00522 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
00523 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00524 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
00525 } else {
00526 readvol = writevol = get_volfactor(x);
00527 }
00528 }
00529 }
00530
00531
00532 if (args.filename[0] != '/') {
00533 char *build;
00534
00535 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
00536 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
00537 args.filename = build;
00538 }
00539
00540 tmp = ast_strdupa(args.filename);
00541 if ((slash = strrchr(tmp, '/')))
00542 *slash = '\0';
00543 ast_mkdir(tmp, 0777);
00544
00545 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
00546 launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
00547
00548 return 0;
00549 }
00550
00551 static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
00552 {
00553 struct ast_datastore *datastore = NULL;
00554
00555 ast_channel_lock(chan);
00556 ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00557 if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) {
00558 struct mixmonitor_ds *mixmonitor_ds = datastore->data;
00559
00560 ast_mutex_lock(&mixmonitor_ds->lock);
00561
00562
00563
00564 mixmonitor_ds_close_fs(mixmonitor_ds);
00565
00566
00567
00568
00569 if (mixmonitor_ds->audiohook) {
00570 ast_audiohook_lock(mixmonitor_ds->audiohook);
00571 ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
00572 ast_audiohook_unlock(mixmonitor_ds->audiohook);
00573 mixmonitor_ds->audiohook = NULL;
00574 }
00575
00576 ast_mutex_unlock(&mixmonitor_ds->lock);
00577
00578
00579 if (!ast_channel_datastore_remove(chan, datastore)) {
00580 ast_datastore_free(datastore);
00581 }
00582 }
00583 ast_channel_unlock(chan);
00584
00585 return 0;
00586 }
00587
00588 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00589 {
00590 struct ast_channel *chan;
00591
00592 switch (cmd) {
00593 case CLI_INIT:
00594 e->command = "mixmonitor {start|stop}";
00595 e->usage =
00596 "Usage: mixmonitor <start|stop> <chan_name> [args]\n"
00597 " The optional arguments are passed to the MixMonitor\n"
00598 " application when the 'start' command is used.\n";
00599 return NULL;
00600 case CLI_GENERATE:
00601 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00602 }
00603
00604 if (a->argc < 3)
00605 return CLI_SHOWUSAGE;
00606
00607 if (!(chan = ast_get_channel_by_name_prefix_locked(a->argv[2], strlen(a->argv[2])))) {
00608 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
00609
00610 return CLI_SUCCESS;
00611 }
00612
00613 if (!strcasecmp(a->argv[1], "start")) {
00614 mixmonitor_exec(chan, a->argv[3]);
00615 ast_channel_unlock(chan);
00616 } else {
00617 ast_channel_unlock(chan);
00618 ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00619 }
00620
00621 return CLI_SUCCESS;
00622 }
00623
00624 static struct ast_cli_entry cli_mixmonitor[] = {
00625 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
00626 };
00627
00628 static int unload_module(void)
00629 {
00630 int res;
00631
00632 ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
00633 res = ast_unregister_application(stop_app);
00634 res |= ast_unregister_application(app);
00635
00636 return res;
00637 }
00638
00639 static int load_module(void)
00640 {
00641 int res;
00642
00643 ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
00644 res = ast_register_application_xml(app, mixmonitor_exec);
00645 res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
00646
00647 return res;
00648 }
00649
00650 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");