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
00037
00038
00039
00040 #include "asterisk.h"
00041
00042 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 360787 $")
00043
00044 #include "asterisk/paths.h"
00045 #include "asterisk/file.h"
00046 #include "asterisk/audiohook.h"
00047 #include "asterisk/pbx.h"
00048 #include "asterisk/module.h"
00049 #include "asterisk/cli.h"
00050 #include "asterisk/app.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/autochan.h"
00053 #include "asterisk/manager.h"
00054 #include "asterisk/mod_format.h"
00055 #include "asterisk/linkedlists.h"
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
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
00234
00235 static const char * const app = "MixMonitor";
00236
00237 static const char * const stop_app = "StopMixMonitor";
00238
00239 static const char * const mixmonitor_spy_type = "MixMonitor";
00240
00241 struct mixmonitor {
00242 struct ast_audiohook audiohook;
00243 struct ast_callid *callid;
00244 char *filename;
00245 char *filename_read;
00246 char *filename_write;
00247 char *post_process;
00248 char *name;
00249 unsigned int flags;
00250 struct ast_autochan *autochan;
00251 struct mixmonitor_ds *mixmonitor_ds;
00252 };
00253
00254 enum mixmonitor_flags {
00255 MUXFLAG_APPEND = (1 << 1),
00256 MUXFLAG_BRIDGED = (1 << 2),
00257 MUXFLAG_VOLUME = (1 << 3),
00258 MUXFLAG_READVOLUME = (1 << 4),
00259 MUXFLAG_WRITEVOLUME = (1 << 5),
00260 MUXFLAG_READ = (1 << 6),
00261 MUXFLAG_WRITE = (1 << 7),
00262 MUXFLAG_COMBINED = (1 << 8),
00263 MUXFLAG_UID = (1 << 9),
00264 };
00265
00266 enum mixmonitor_args {
00267 OPT_ARG_READVOLUME = 0,
00268 OPT_ARG_WRITEVOLUME,
00269 OPT_ARG_VOLUME,
00270 OPT_ARG_WRITENAME,
00271 OPT_ARG_READNAME,
00272 OPT_ARG_UID,
00273 OPT_ARG_ARRAY_SIZE,
00274 };
00275
00276 AST_APP_OPTIONS(mixmonitor_opts, {
00277 AST_APP_OPTION('a', MUXFLAG_APPEND),
00278 AST_APP_OPTION('b', MUXFLAG_BRIDGED),
00279 AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
00280 AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
00281 AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
00282 AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
00283 AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
00284 AST_APP_OPTION_ARG('i', MUXFLAG_UID, OPT_ARG_UID),
00285 });
00286
00287 struct mixmonitor_ds {
00288 unsigned int destruction_ok;
00289 ast_cond_t destruction_condition;
00290 ast_mutex_t lock;
00291
00292
00293
00294 int fs_quit;
00295
00296 struct ast_filestream *fs;
00297 struct ast_filestream *fs_read;
00298 struct ast_filestream *fs_write;
00299
00300 struct ast_audiohook *audiohook;
00301
00302 unsigned int samp_rate;
00303 };
00304
00305
00306
00307
00308
00309 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
00310 {
00311 unsigned char quitting = 0;
00312
00313 if (mixmonitor_ds->fs) {
00314 quitting = 1;
00315 ast_closestream(mixmonitor_ds->fs);
00316 mixmonitor_ds->fs = NULL;
00317 ast_verb(2, "MixMonitor close filestream (mixed)\n");
00318 }
00319
00320 if (mixmonitor_ds->fs_read) {
00321 quitting = 1;
00322 ast_closestream(mixmonitor_ds->fs_read);
00323 mixmonitor_ds->fs_read = NULL;
00324 ast_verb(2, "MixMonitor close filestream (read)\n");
00325 }
00326
00327 if (mixmonitor_ds->fs_write) {
00328 quitting = 1;
00329 ast_closestream(mixmonitor_ds->fs_write);
00330 mixmonitor_ds->fs_write = NULL;
00331 ast_verb(2, "MixMonitor close filestream (write)\n");
00332 }
00333
00334 if (quitting) {
00335 mixmonitor_ds->fs_quit = 1;
00336 }
00337 }
00338
00339 static void mixmonitor_ds_destroy(void *data)
00340 {
00341 struct mixmonitor_ds *mixmonitor_ds = data;
00342
00343 ast_mutex_lock(&mixmonitor_ds->lock);
00344 mixmonitor_ds->audiohook = NULL;
00345 mixmonitor_ds->destruction_ok = 1;
00346 ast_cond_signal(&mixmonitor_ds->destruction_condition);
00347 ast_mutex_unlock(&mixmonitor_ds->lock);
00348 }
00349
00350 static struct ast_datastore_info mixmonitor_ds_info = {
00351 .type = "mixmonitor",
00352 .destroy = mixmonitor_ds_destroy,
00353 };
00354
00355 static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
00356 {
00357 if (mixmonitor->mixmonitor_ds) {
00358 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00359 mixmonitor->mixmonitor_ds->audiohook = NULL;
00360 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00361 }
00362
00363 ast_audiohook_lock(&mixmonitor->audiohook);
00364 ast_audiohook_detach(&mixmonitor->audiohook);
00365 ast_audiohook_unlock(&mixmonitor->audiohook);
00366 ast_audiohook_destroy(&mixmonitor->audiohook);
00367 }
00368
00369 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
00370 {
00371 struct ast_channel *peer = NULL;
00372 int res = 0;
00373
00374 if (!chan)
00375 return -1;
00376
00377 ast_audiohook_attach(chan, audiohook);
00378
00379 if (!res && ast_test_flag(ast_channel_flags(chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
00380 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00381
00382 return res;
00383 }
00384
00385 #define SAMPLES_PER_FRAME 160
00386
00387 static void mixmonitor_free(struct mixmonitor *mixmonitor)
00388 {
00389 if (mixmonitor) {
00390 if (mixmonitor->mixmonitor_ds) {
00391 ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
00392 ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
00393 ast_free(mixmonitor->filename_write);
00394 ast_free(mixmonitor->filename_read);
00395 ast_free(mixmonitor->mixmonitor_ds);
00396 ast_free(mixmonitor->name);
00397 ast_free(mixmonitor->post_process);
00398 }
00399
00400 if (mixmonitor->callid) {
00401 ast_callid_unref(mixmonitor->callid);
00402 }
00403 ast_free(mixmonitor);
00404 }
00405 }
00406
00407 static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag)
00408 {
00409
00410 char *ext = NULL;
00411 char *last_slash = NULL;
00412 if (!ast_strlen_zero(filename)) {
00413 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
00414 *oflags = O_CREAT | O_WRONLY;
00415 *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00416
00417 last_slash = strrchr(filename, '/');
00418
00419 if ((ext = strrchr(filename, '.')) && (ext > last_slash)) {
00420 *(ext++) = '\0';
00421 } else {
00422 ext = "raw";
00423 }
00424
00425 if (!(*fs = ast_writefile(filename, ext, NULL, *oflags, 0, 0666))) {
00426 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, ext);
00427 *errflag = 1;
00428 } else {
00429 struct ast_filestream *tmp = *fs;
00430 mixmonitor->mixmonitor_ds->samp_rate = MAX(mixmonitor->mixmonitor_ds->samp_rate, ast_format_rate(&tmp->fmt->format));
00431 }
00432 }
00433 }
00434 }
00435
00436 static void *mixmonitor_thread(void *obj)
00437 {
00438 struct mixmonitor *mixmonitor = obj;
00439
00440 struct ast_filestream **fs = NULL;
00441 struct ast_filestream **fs_read = NULL;
00442 struct ast_filestream **fs_write = NULL;
00443
00444 unsigned int oflags;
00445 int errflag = 0;
00446 struct ast_format format_slin;
00447
00448
00449 if (mixmonitor->callid) {
00450 ast_callid_threadassoc_add(mixmonitor->callid);
00451 }
00452
00453 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
00454
00455 fs = &mixmonitor->mixmonitor_ds->fs;
00456 fs_read = &mixmonitor->mixmonitor_ds->fs_read;
00457 fs_write = &mixmonitor->mixmonitor_ds->fs_write;
00458
00459 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00460 mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag);
00461 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag);
00462 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag);
00463
00464 ast_format_set(&format_slin, ast_format_slin_by_rate(mixmonitor->mixmonitor_ds->samp_rate), 0);
00465
00466 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00467
00468
00469
00470 ast_audiohook_lock(&mixmonitor->audiohook);
00471 while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
00472 struct ast_frame *fr = NULL;
00473 struct ast_frame *fr_read = NULL;
00474 struct ast_frame *fr_write = NULL;
00475
00476 if (!(fr = ast_audiohook_read_frame_all(&mixmonitor->audiohook, SAMPLES_PER_FRAME, &format_slin,
00477 &fr_read, &fr_write))) {
00478 ast_audiohook_trigger_wait(&mixmonitor->audiohook);
00479
00480 if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00481 break;
00482 }
00483 continue;
00484 }
00485
00486
00487
00488 ast_audiohook_unlock(&mixmonitor->audiohook);
00489
00490 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
00491 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00492
00493
00494 if ((*fs_read) && (fr_read)) {
00495 struct ast_frame *cur;
00496
00497 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00498 ast_writestream(*fs_read, cur);
00499 }
00500 }
00501
00502 if ((*fs_write) && (fr_write)) {
00503 struct ast_frame *cur;
00504
00505 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00506 ast_writestream(*fs_write, cur);
00507 }
00508 }
00509
00510 if ((*fs) && (fr)) {
00511 struct ast_frame *cur;
00512
00513 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00514 ast_writestream(*fs, cur);
00515 }
00516 }
00517 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00518 }
00519
00520 if (fr) {
00521 ast_frame_free(fr, 0);
00522 }
00523 if (fr_read) {
00524 ast_frame_free(fr_read, 0);
00525 }
00526 if (fr_write) {
00527 ast_frame_free(fr_write, 0);
00528 }
00529
00530 fr = NULL;
00531 fr_write = NULL;
00532 fr_read = NULL;
00533
00534 ast_audiohook_lock(&mixmonitor->audiohook);
00535 }
00536 ast_audiohook_unlock(&mixmonitor->audiohook);
00537
00538 ast_autochan_destroy(mixmonitor->autochan);
00539
00540
00541 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00542 mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
00543 if (!mixmonitor->mixmonitor_ds->destruction_ok) {
00544 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
00545 }
00546 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00547
00548
00549 destroy_monitor_audiohook(mixmonitor);
00550
00551 if (mixmonitor->post_process) {
00552 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
00553 ast_safe_system(mixmonitor->post_process);
00554 }
00555
00556 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
00557 mixmonitor_free(mixmonitor);
00558 return NULL;
00559 }
00560
00561 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id)
00562 {
00563 struct ast_datastore *datastore = NULL;
00564 struct mixmonitor_ds *mixmonitor_ds;
00565
00566 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
00567 return -1;
00568 }
00569
00570 if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
00571 ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
00572 }
00573
00574 ast_mutex_init(&mixmonitor_ds->lock);
00575 ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
00576
00577 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
00578 ast_mutex_destroy(&mixmonitor_ds->lock);
00579 ast_cond_destroy(&mixmonitor_ds->destruction_condition);
00580 ast_free(mixmonitor_ds);
00581 return -1;
00582 }
00583
00584
00585 mixmonitor_ds->samp_rate = 8000;
00586 mixmonitor_ds->audiohook = &mixmonitor->audiohook;
00587 datastore->data = mixmonitor_ds;
00588
00589 ast_channel_lock(chan);
00590 ast_channel_datastore_add(chan, datastore);
00591 ast_channel_unlock(chan);
00592
00593 mixmonitor->mixmonitor_ds = mixmonitor_ds;
00594 return 0;
00595 }
00596
00597 static void launch_monitor_thread(struct ast_channel *chan, const char *filename,
00598 unsigned int flags, int readvol, int writevol,
00599 const char *post_process, const char *filename_write,
00600 char *filename_read, const char *uid_channel_var)
00601 {
00602 pthread_t thread;
00603 struct mixmonitor *mixmonitor;
00604 char postprocess2[1024] = "";
00605 char *datastore_id = NULL;
00606
00607 postprocess2[0] = 0;
00608
00609 if (!ast_strlen_zero(post_process)) {
00610 char *p1, *p2;
00611
00612 p1 = ast_strdupa(post_process);
00613 for (p2 = p1; *p2; p2++) {
00614 if (*p2 == '^' && *(p2+1) == '{') {
00615 *p2 = '$';
00616 }
00617 }
00618 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00619 }
00620
00621
00622 if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
00623 return;
00624 }
00625
00626
00627 if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) {
00628 mixmonitor_free(mixmonitor);
00629 return;
00630 }
00631
00632
00633 mixmonitor->flags = flags;
00634 if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
00635 mixmonitor_free(mixmonitor);
00636 return;
00637 }
00638
00639 if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id)) {
00640 ast_autochan_destroy(mixmonitor->autochan);
00641 mixmonitor_free(mixmonitor);
00642 ast_free(datastore_id);
00643 return;
00644 }
00645
00646 if (!ast_strlen_zero(uid_channel_var)) {
00647 if (datastore_id) {
00648 pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
00649 }
00650 }
00651 ast_free(datastore_id);
00652
00653
00654 mixmonitor->name = ast_strdup(ast_channel_name(chan));
00655
00656 if (!ast_strlen_zero(postprocess2)) {
00657 mixmonitor->post_process = ast_strdup(postprocess2);
00658 }
00659
00660 if (!ast_strlen_zero(filename)) {
00661 mixmonitor->filename = ast_strdup(filename);
00662 }
00663
00664 if (!ast_strlen_zero(filename_write)) {
00665 mixmonitor->filename_write = ast_strdup(filename_write);
00666 }
00667
00668 if (!ast_strlen_zero(filename_read)) {
00669 mixmonitor->filename_read = ast_strdup(filename_read);
00670 }
00671
00672 ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
00673
00674 if (readvol)
00675 mixmonitor->audiohook.options.read_volume = readvol;
00676 if (writevol)
00677 mixmonitor->audiohook.options.write_volume = writevol;
00678
00679 if (startmon(chan, &mixmonitor->audiohook)) {
00680 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00681 mixmonitor_spy_type, ast_channel_name(chan));
00682 ast_audiohook_destroy(&mixmonitor->audiohook);
00683 mixmonitor_free(mixmonitor);
00684 return;
00685 }
00686
00687
00688 mixmonitor->callid = ast_read_threadstorage_callid();
00689
00690 ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
00691 }
00692
00693
00694
00695 static char *filename_parse(char *filename, char *buffer, size_t len)
00696 {
00697 char *slash;
00698 if (ast_strlen_zero(filename)) {
00699 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
00700 } else if (filename[0] != '/') {
00701 char *build;
00702 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
00703 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
00704 filename = build;
00705 }
00706
00707 ast_copy_string(buffer, filename, len);
00708
00709 if ((slash = strrchr(filename, '/'))) {
00710 *slash = '\0';
00711 }
00712 ast_mkdir(filename, 0777);
00713
00714 return buffer;
00715 }
00716
00717 static int mixmonitor_exec(struct ast_channel *chan, const char *data)
00718 {
00719 int x, readvol = 0, writevol = 0;
00720 char *filename_read = NULL;
00721 char *filename_write = NULL;
00722 char filename_buffer[1024] = "";
00723 char *uid_channel_var = NULL;
00724
00725 struct ast_flags flags = { 0 };
00726 char *parse;
00727 AST_DECLARE_APP_ARGS(args,
00728 AST_APP_ARG(filename);
00729 AST_APP_ARG(options);
00730 AST_APP_ARG(post_process);
00731 );
00732
00733 if (ast_strlen_zero(data)) {
00734 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
00735 return -1;
00736 }
00737
00738 parse = ast_strdupa(data);
00739
00740 AST_STANDARD_APP_ARGS(args, parse);
00741
00742 if (args.options) {
00743 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
00744
00745 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
00746
00747 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
00748 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
00749 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
00750 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00751 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
00752 } else {
00753 readvol = get_volfactor(x);
00754 }
00755 }
00756
00757 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
00758 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
00759 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
00760 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00761 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
00762 } else {
00763 writevol = get_volfactor(x);
00764 }
00765 }
00766
00767 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
00768 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
00769 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
00770 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00771 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
00772 } else {
00773 readvol = writevol = get_volfactor(x);
00774 }
00775 }
00776
00777 if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
00778 filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
00779 }
00780
00781 if (ast_test_flag(&flags, MUXFLAG_READ)) {
00782 filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
00783 }
00784
00785 if (ast_test_flag(&flags, MUXFLAG_UID)) {
00786 uid_channel_var = opts[OPT_ARG_UID];
00787 }
00788 }
00789
00790
00791 if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
00792 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00793 return -1;
00794 }
00795
00796
00797 if (!(ast_strlen_zero(args.filename))) {
00798 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
00799 }
00800
00801 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
00802 launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process, filename_write, filename_read, uid_channel_var);
00803
00804 return 0;
00805 }
00806
00807 static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
00808 {
00809 struct ast_datastore *datastore = NULL;
00810 char *parse = "";
00811 struct mixmonitor_ds *mixmonitor_ds;
00812
00813 AST_DECLARE_APP_ARGS(args,
00814 AST_APP_ARG(mixmonid);
00815 );
00816
00817 if (!ast_strlen_zero(data)) {
00818 parse = ast_strdupa(data);
00819 }
00820
00821 AST_STANDARD_APP_ARGS(args, parse);
00822
00823 ast_channel_lock(chan);
00824
00825 if (!(datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.mixmonid))) {
00826 ast_channel_unlock(chan);
00827 return -1;
00828 }
00829 mixmonitor_ds = datastore->data;
00830
00831 ast_mutex_lock(&mixmonitor_ds->lock);
00832
00833
00834
00835 mixmonitor_ds_close_fs(mixmonitor_ds);
00836
00837
00838
00839
00840 if (mixmonitor_ds->audiohook) {
00841 if (mixmonitor_ds->audiohook->status != AST_AUDIOHOOK_STATUS_DONE) {
00842 ast_audiohook_update_status(mixmonitor_ds->audiohook, AST_AUDIOHOOK_STATUS_SHUTDOWN);
00843 }
00844 ast_audiohook_lock(mixmonitor_ds->audiohook);
00845 ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
00846 ast_audiohook_unlock(mixmonitor_ds->audiohook);
00847 mixmonitor_ds->audiohook = NULL;
00848 }
00849
00850 ast_mutex_unlock(&mixmonitor_ds->lock);
00851
00852
00853 if (!ast_channel_datastore_remove(chan, datastore)) {
00854 ast_datastore_free(datastore);
00855 }
00856 ast_channel_unlock(chan);
00857
00858 return 0;
00859 }
00860
00861 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00862 {
00863 struct ast_channel *chan;
00864 struct ast_datastore *datastore = NULL;
00865 struct mixmonitor_ds *mixmonitor_ds = NULL;
00866
00867 switch (cmd) {
00868 case CLI_INIT:
00869 e->command = "mixmonitor {start|stop|list}";
00870 e->usage =
00871 "Usage: mixmonitor <start|stop|list> <chan_name> [args]\n"
00872 " The optional arguments are passed to the MixMonitor\n"
00873 " application when the 'start' command is used.\n";
00874 return NULL;
00875 case CLI_GENERATE:
00876 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00877 }
00878
00879 if (a->argc < 3) {
00880 return CLI_SHOWUSAGE;
00881 }
00882
00883 if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
00884 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
00885
00886 return CLI_SUCCESS;
00887 }
00888
00889 ast_channel_lock(chan);
00890
00891 if (!strcasecmp(a->argv[1], "start")) {
00892 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
00893 ast_channel_unlock(chan);
00894 } else if (!strcasecmp(a->argv[1], "stop")){
00895 ast_channel_unlock(chan);
00896 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
00897 } else if (!strcasecmp(a->argv[1], "list")) {
00898 ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
00899 ast_cli(a->fd, "=========================================================================\n");
00900 AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
00901 if (datastore->info == &mixmonitor_ds_info) {
00902 char *filename = "";
00903 char *filename_read = "";
00904 char *filename_write = "";
00905 mixmonitor_ds = datastore->data;
00906 if (mixmonitor_ds->fs)
00907 filename = ast_strdupa(mixmonitor_ds->fs->filename);
00908 if (mixmonitor_ds->fs_read)
00909 filename_read = ast_strdupa(mixmonitor_ds->fs_read->filename);
00910 if (mixmonitor_ds->fs_write)
00911 filename_write = ast_strdupa(mixmonitor_ds->fs_write->filename);
00912 ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
00913 }
00914 }
00915 ast_channel_unlock(chan);
00916 } else {
00917 ast_channel_unlock(chan);
00918 chan = ast_channel_unref(chan);
00919 return CLI_SHOWUSAGE;
00920 }
00921
00922 chan = ast_channel_unref(chan);
00923
00924 return CLI_SUCCESS;
00925 }
00926
00927
00928 static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
00929 {
00930 struct ast_channel *c = NULL;
00931
00932 const char *name = astman_get_header(m, "Channel");
00933 const char *id = astman_get_header(m, "ActionID");
00934 const char *state = astman_get_header(m, "State");
00935 const char *direction = astman_get_header(m,"Direction");
00936
00937 int clearmute = 1;
00938
00939 enum ast_audiohook_flags flag;
00940
00941 if (ast_strlen_zero(direction)) {
00942 astman_send_error(s, m, "No direction specified. Must be read, write or both");
00943 return AMI_SUCCESS;
00944 }
00945
00946 if (!strcasecmp(direction, "read")) {
00947 flag = AST_AUDIOHOOK_MUTE_READ;
00948 } else if (!strcasecmp(direction, "write")) {
00949 flag = AST_AUDIOHOOK_MUTE_WRITE;
00950 } else if (!strcasecmp(direction, "both")) {
00951 flag = AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE;
00952 } else {
00953 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
00954 return AMI_SUCCESS;
00955 }
00956
00957 if (ast_strlen_zero(name)) {
00958 astman_send_error(s, m, "No channel specified");
00959 return AMI_SUCCESS;
00960 }
00961
00962 if (ast_strlen_zero(state)) {
00963 astman_send_error(s, m, "No state specified");
00964 return AMI_SUCCESS;
00965 }
00966
00967 clearmute = ast_false(state);
00968 c = ast_channel_get_by_name(name);
00969
00970 if (!c) {
00971 astman_send_error(s, m, "No such channel");
00972 return AMI_SUCCESS;
00973 }
00974
00975 if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
00976 c = ast_channel_unref(c);
00977 astman_send_error(s, m, "Cannot set mute flag");
00978 return AMI_SUCCESS;
00979 }
00980
00981 astman_append(s, "Response: Success\r\n");
00982
00983 if (!ast_strlen_zero(id)) {
00984 astman_append(s, "ActionID: %s\r\n", id);
00985 }
00986
00987 astman_append(s, "\r\n");
00988
00989 c = ast_channel_unref(c);
00990
00991 return AMI_SUCCESS;
00992 }
00993
00994 static int manager_mixmonitor(struct mansession *s, const struct message *m)
00995 {
00996 struct ast_channel *c = NULL;
00997
00998 const char *name = astman_get_header(m, "Channel");
00999 const char *id = astman_get_header(m, "ActionID");
01000 const char *file = astman_get_header(m, "File");
01001 const char *options = astman_get_header(m, "Options");
01002 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
01003 struct ast_flags flags = { 0 };
01004 char *uid_channel_var = NULL;
01005 const char *mixmonitor_id = NULL;
01006
01007 int res;
01008 char args[PATH_MAX] = "";
01009 if (ast_strlen_zero(name)) {
01010 astman_send_error(s, m, "No channel specified");
01011 return AMI_SUCCESS;
01012 }
01013
01014 c = ast_channel_get_by_name(name);
01015
01016 if (!c) {
01017 astman_send_error(s, m, "No such channel");
01018 return AMI_SUCCESS;
01019 }
01020
01021 if (!ast_strlen_zero(options)) {
01022 ast_app_parse_options(mixmonitor_opts, &flags, opts, ast_strdupa(options));
01023 }
01024
01025 snprintf(args, sizeof(args), "%s,%s", file, options);
01026
01027 ast_channel_lock(c);
01028 res = mixmonitor_exec(c, args);
01029
01030 if (ast_test_flag(&flags, MUXFLAG_UID)) {
01031 uid_channel_var = opts[OPT_ARG_UID];
01032 mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
01033 }
01034 ast_channel_unlock(c);
01035
01036 if (res) {
01037 astman_send_error(s, m, "Could not start monitoring channel");
01038 return AMI_SUCCESS;
01039 }
01040
01041 astman_append(s, "Response: Success\r\n");
01042
01043 if (!ast_strlen_zero(id)) {
01044 astman_append(s, "ActionID: %s\r\n", id);
01045 }
01046
01047 if (!ast_strlen_zero(mixmonitor_id)) {
01048 astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
01049 }
01050
01051 astman_append(s, "\r\n");
01052
01053 c = ast_channel_unref(c);
01054
01055 return AMI_SUCCESS;
01056 }
01057
01058 static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
01059 {
01060 struct ast_channel *c = NULL;
01061
01062 const char *name = astman_get_header(m, "Channel");
01063 const char *id = astman_get_header(m, "ActionID");
01064 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
01065
01066 int res;
01067 if (ast_strlen_zero(name)) {
01068 astman_send_error(s, m, "No channel specified");
01069 return AMI_SUCCESS;
01070 }
01071
01072 c = ast_channel_get_by_name(name);
01073
01074 if (!c) {
01075 astman_send_error(s, m, "No such channel");
01076 return AMI_SUCCESS;
01077 }
01078
01079 res = stop_mixmonitor_exec(c, mixmonitor_id);
01080
01081 if (res) {
01082 astman_send_error(s, m, "Could not stop monitoring channel");
01083 return AMI_SUCCESS;
01084 }
01085
01086 astman_append(s, "Response: Success\r\n");
01087
01088 if (!ast_strlen_zero(id)) {
01089 astman_append(s, "ActionID: %s\r\n", id);
01090 }
01091
01092 astman_append(s, "\r\n");
01093
01094 c = ast_channel_unref(c);
01095
01096 return AMI_SUCCESS;
01097 }
01098
01099 static struct ast_cli_entry cli_mixmonitor[] = {
01100 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
01101 };
01102
01103 static int unload_module(void)
01104 {
01105 int res;
01106
01107 ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
01108 res = ast_unregister_application(stop_app);
01109 res |= ast_unregister_application(app);
01110 res |= ast_manager_unregister("MixMonitorMute");
01111 res |= ast_manager_unregister("MixMonitor");
01112 res |= ast_manager_unregister("StopMixMonitor");
01113
01114 return res;
01115 }
01116
01117 static int load_module(void)
01118 {
01119 int res;
01120
01121 ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
01122 res = ast_register_application_xml(app, mixmonitor_exec);
01123 res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
01124 res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
01125 res |= ast_manager_register_xml("MixMonitor", 0, manager_mixmonitor);
01126 res |= ast_manager_register_xml("StopMixMonitor", 0, manager_stop_mixmonitor);
01127
01128 return res;
01129 }
01130
01131 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");