MixMonitor() - Record a call and mix the audio during the recording. More...
#include "asterisk.h"
#include "asterisk/paths.h"
#include "asterisk/file.h"
#include "asterisk/audiohook.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
#include "asterisk/autochan.h"
#include "asterisk/manager.h"
#include "asterisk/test.h"
Go to the source code of this file.
Data Structures | |
struct | mixmonitor |
struct | mixmonitor_ds |
Defines | |
#define | get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0 |
#define | SAMPLES_PER_FRAME 160 |
Enumerations | |
enum | mixmonitor_args { OPT_ARG_READVOLUME = 0, OPT_ARG_WRITEVOLUME, OPT_ARG_VOLUME, OPT_ARG_ARRAY_SIZE } |
enum | mixmonitor_flags { MUXFLAG_APPEND = (1 << 1), MUXFLAG_BRIDGED = (1 << 2), MUXFLAG_VOLUME = (1 << 3), MUXFLAG_READVOLUME = (1 << 4), MUXFLAG_WRITEVOLUME = (1 << 5) } |
Functions | |
AST_APP_OPTIONS (mixmonitor_opts,{AST_APP_OPTION('a', MUXFLAG_APPEND), AST_APP_OPTION('b', MUXFLAG_BRIDGED), AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME), AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME), AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),}) | |
AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY,"Mixed Audio Monitoring Application") | |
static void | destroy_monitor_audiohook (struct mixmonitor *mixmonitor) |
static char * | handle_cli_mixmonitor (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static int | launch_monitor_thread (struct ast_channel *chan, const char *filename, unsigned int flags, int readvol, int writevol, const char *post_process) |
static int | load_module (void) |
static int | manager_mute_mixmonitor (struct mansession *s, const struct message *m) |
Mute / unmute a MixMonitor channel. | |
static void | mixmonitor_ds_close_fs (struct mixmonitor_ds *mixmonitor_ds) |
static void | mixmonitor_ds_destroy (void *data) |
static int | mixmonitor_exec (struct ast_channel *chan, const char *data) |
static void | mixmonitor_free (struct mixmonitor *mixmonitor) |
static void * | mixmonitor_thread (void *obj) |
static int | setup_mixmonitor_ds (struct mixmonitor *mixmonitor, struct ast_channel *chan) |
static int | startmon (struct ast_channel *chan, struct ast_audiohook *audiohook) |
static int | stop_mixmonitor_exec (struct ast_channel *chan, const char *data) |
static int | unload_module (void) |
Variables | |
static const char *const | app = "MixMonitor" |
static struct ast_cli_entry | cli_mixmonitor [] |
static struct ast_datastore_info | mixmonitor_ds_info |
static const char *const | mixmonitor_spy_type = "MixMonitor" |
static const char *const | stop_app = "StopMixMonitor" |
MixMonitor() - Record a call and mix the audio during the recording.
Definition in file app_mixmonitor.c.
#define get_volfactor | ( | x | ) | x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0 |
Definition at line 164 of file app_mixmonitor.c.
Referenced by mixmonitor_exec().
#define SAMPLES_PER_FRAME 160 |
Definition at line 277 of file app_mixmonitor.c.
Referenced by mixmonitor_thread().
enum mixmonitor_args |
Definition at line 190 of file app_mixmonitor.c.
00190 { 00191 OPT_ARG_READVOLUME = 0, 00192 OPT_ARG_WRITEVOLUME, 00193 OPT_ARG_VOLUME, 00194 OPT_ARG_ARRAY_SIZE, 00195 };
enum mixmonitor_flags |
Definition at line 182 of file app_mixmonitor.c.
00182 { 00183 MUXFLAG_APPEND = (1 << 1), 00184 MUXFLAG_BRIDGED = (1 << 2), 00185 MUXFLAG_VOLUME = (1 << 3), 00186 MUXFLAG_READVOLUME = (1 << 4), 00187 MUXFLAG_WRITEVOLUME = (1 << 5), 00188 };
AST_APP_OPTIONS | ( | mixmonitor_opts | ) |
AST_MODULE_INFO_STANDARD | ( | ASTERISK_GPL_KEY | , | |
"Mixed Audio Monitoring Application" | ||||
) |
static void destroy_monitor_audiohook | ( | struct mixmonitor * | mixmonitor | ) | [static] |
Definition at line 247 of file app_mixmonitor.c.
References ast_audiohook_destroy(), ast_audiohook_detach(), ast_audiohook_lock, ast_audiohook_unlock, ast_mutex_lock, ast_mutex_unlock, mixmonitor::audiohook, mixmonitor_ds::audiohook, mixmonitor_ds::lock, and mixmonitor::mixmonitor_ds.
Referenced by mixmonitor_thread().
00248 { 00249 if (mixmonitor->mixmonitor_ds) { 00250 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); 00251 mixmonitor->mixmonitor_ds->audiohook = NULL; 00252 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); 00253 } 00254 /* kill the audiohook.*/ 00255 ast_audiohook_lock(&mixmonitor->audiohook); 00256 ast_audiohook_detach(&mixmonitor->audiohook); 00257 ast_audiohook_unlock(&mixmonitor->audiohook); 00258 ast_audiohook_destroy(&mixmonitor->audiohook); 00259 }
static char* handle_cli_mixmonitor | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 619 of file app_mixmonitor.c.
References ast_cli_args::argc, ast_cli_args::argv, ast_audiohook_detach_source(), ast_channel_get_by_name_prefix(), ast_channel_lock, ast_channel_unlock, ast_channel_unref, ast_cli(), ast_complete_channels(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, ast_cli_args::line, mixmonitor_exec(), ast_cli_args::n, ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.
00620 { 00621 struct ast_channel *chan; 00622 00623 switch (cmd) { 00624 case CLI_INIT: 00625 e->command = "mixmonitor {start|stop}"; 00626 e->usage = 00627 "Usage: mixmonitor <start|stop> <chan_name> [args]\n" 00628 " The optional arguments are passed to the MixMonitor\n" 00629 " application when the 'start' command is used.\n"; 00630 return NULL; 00631 case CLI_GENERATE: 00632 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2); 00633 } 00634 00635 if (a->argc < 3) 00636 return CLI_SHOWUSAGE; 00637 00638 if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) { 00639 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]); 00640 /* Technically this is a failure, but we don't want 2 errors printing out */ 00641 return CLI_SUCCESS; 00642 } 00643 00644 ast_channel_lock(chan); 00645 00646 if (!strcasecmp(a->argv[1], "start")) { 00647 mixmonitor_exec(chan, a->argv[3]); 00648 ast_channel_unlock(chan); 00649 } else { 00650 ast_channel_unlock(chan); 00651 ast_audiohook_detach_source(chan, mixmonitor_spy_type); 00652 } 00653 00654 chan = ast_channel_unref(chan); 00655 00656 return CLI_SUCCESS; 00657 }
static int launch_monitor_thread | ( | struct ast_channel * | chan, | |
const char * | filename, | |||
unsigned int | flags, | |||
int | readvol, | |||
int | writevol, | |||
const char * | post_process | |||
) | [static] |
Definition at line 419 of file app_mixmonitor.c.
References ast_audiohook_destroy(), ast_audiohook_init(), AST_AUDIOHOOK_TRIGGER_SYNC, AST_AUDIOHOOK_TYPE_SPY, ast_autochan_destroy(), ast_autochan_setup(), ast_calloc, ast_log(), ast_pthread_create_detached_background, ast_set_flag, ast_strdupa, ast_strlen_zero(), mixmonitor::audiohook, mixmonitor::autochan, mixmonitor::filename, mixmonitor::flags, len(), LOG_WARNING, mixmonitor_free(), mixmonitor_thread(), mixmonitor::name, ast_audiohook::options, pbx_substitute_variables_helper(), mixmonitor::post_process, ast_audiohook_options::read_volume, setup_mixmonitor_ds(), startmon(), thread, and ast_audiohook_options::write_volume.
Referenced by mixmonitor_exec().
00421 { 00422 pthread_t thread; 00423 struct mixmonitor *mixmonitor; 00424 char postprocess2[1024] = ""; 00425 size_t len; 00426 00427 len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2; 00428 00429 postprocess2[0] = 0; 00430 /* If a post process system command is given attach it to the structure */ 00431 if (!ast_strlen_zero(post_process)) { 00432 char *p1, *p2; 00433 00434 p1 = ast_strdupa(post_process); 00435 for (p2 = p1; *p2 ; p2++) { 00436 if (*p2 == '^' && *(p2+1) == '{') { 00437 *p2 = '$'; 00438 } 00439 } 00440 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1); 00441 if (!ast_strlen_zero(postprocess2)) 00442 len += strlen(postprocess2) + 1; 00443 } 00444 00445 /* Pre-allocate mixmonitor structure and spy */ 00446 if (!(mixmonitor = ast_calloc(1, len))) { 00447 return -1; 00448 } 00449 00450 /* Setup the actual spy before creating our thread */ 00451 if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) { 00452 mixmonitor_free(mixmonitor); 00453 return -1; 00454 } 00455 00456 /* Copy over flags and channel name */ 00457 mixmonitor->flags = flags; 00458 if (!(mixmonitor->autochan = ast_autochan_setup(chan))) { 00459 mixmonitor_free(mixmonitor); 00460 return -1; 00461 } 00462 00463 if (setup_mixmonitor_ds(mixmonitor, chan)) { 00464 ast_autochan_destroy(mixmonitor->autochan); 00465 mixmonitor_free(mixmonitor); 00466 return -1; 00467 } 00468 mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor); 00469 strcpy(mixmonitor->name, chan->name); 00470 if (!ast_strlen_zero(postprocess2)) { 00471 mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2; 00472 strcpy(mixmonitor->post_process, postprocess2); 00473 } 00474 00475 mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1; 00476 strcpy(mixmonitor->filename, filename); 00477 00478 ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC); 00479 00480 if (readvol) 00481 mixmonitor->audiohook.options.read_volume = readvol; 00482 if (writevol) 00483 mixmonitor->audiohook.options.write_volume = writevol; 00484 00485 if (startmon(chan, &mixmonitor->audiohook)) { 00486 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n", 00487 mixmonitor_spy_type, chan->name); 00488 ast_audiohook_destroy(&mixmonitor->audiohook); 00489 mixmonitor_free(mixmonitor); 00490 return -1; 00491 } 00492 00493 return ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor); 00494 }
static int load_module | ( | void | ) | [static] |
Definition at line 742 of file app_mixmonitor.c.
References ARRAY_LEN, ast_cli_register_multiple(), ast_manager_register_xml, ast_register_application_xml, manager_mute_mixmonitor(), mixmonitor_exec(), and stop_mixmonitor_exec().
00743 { 00744 int res; 00745 00746 ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor)); 00747 res = ast_register_application_xml(app, mixmonitor_exec); 00748 res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec); 00749 res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor); 00750 00751 return res; 00752 }
static int manager_mute_mixmonitor | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Mute / unmute a MixMonitor channel.
Definition at line 660 of file app_mixmonitor.c.
References AMI_SUCCESS, AST_AUDIOHOOK_MUTE_READ, AST_AUDIOHOOK_MUTE_WRITE, ast_audiohook_set_mute(), ast_channel_get_by_name(), ast_channel_unref, ast_false(), ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_error(), and name.
Referenced by load_module().
00661 { 00662 struct ast_channel *c = NULL; 00663 00664 const char *name = astman_get_header(m, "Channel"); 00665 const char *id = astman_get_header(m, "ActionID"); 00666 const char *state = astman_get_header(m, "State"); 00667 const char *direction = astman_get_header(m,"Direction"); 00668 00669 int clearmute = 1; 00670 00671 enum ast_audiohook_flags flag; 00672 00673 if (ast_strlen_zero(direction)) { 00674 astman_send_error(s, m, "No direction specified. Must be read, write or both"); 00675 return AMI_SUCCESS; 00676 } 00677 00678 if (!strcasecmp(direction, "read")) { 00679 flag = AST_AUDIOHOOK_MUTE_READ; 00680 } else if (!strcasecmp(direction, "write")) { 00681 flag = AST_AUDIOHOOK_MUTE_WRITE; 00682 } else if (!strcasecmp(direction, "both")) { 00683 flag = AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE; 00684 } else { 00685 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both"); 00686 return AMI_SUCCESS; 00687 } 00688 00689 if (ast_strlen_zero(name)) { 00690 astman_send_error(s, m, "No channel specified"); 00691 return AMI_SUCCESS; 00692 } 00693 00694 if (ast_strlen_zero(state)) { 00695 astman_send_error(s, m, "No state specified"); 00696 return AMI_SUCCESS; 00697 } 00698 00699 clearmute = ast_false(state); 00700 c = ast_channel_get_by_name(name); 00701 00702 if (!c) { 00703 astman_send_error(s, m, "No such channel"); 00704 return AMI_SUCCESS; 00705 } 00706 00707 if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) { 00708 c = ast_channel_unref(c); 00709 astman_send_error(s, m, "Cannot set mute flag"); 00710 return AMI_SUCCESS; 00711 } 00712 00713 astman_append(s, "Response: Success\r\n"); 00714 00715 if (!ast_strlen_zero(id)) { 00716 astman_append(s, "ActionID: %s\r\n", id); 00717 } 00718 00719 astman_append(s, "\r\n"); 00720 00721 c = ast_channel_unref(c); 00722 00723 return AMI_SUCCESS; 00724 }
static void mixmonitor_ds_close_fs | ( | struct mixmonitor_ds * | mixmonitor_ds | ) | [static] |
Definition at line 221 of file app_mixmonitor.c.
References ast_closestream(), ast_verb, mixmonitor_ds::fs, and mixmonitor_ds::fs_quit.
Referenced by mixmonitor_thread(), and stop_mixmonitor_exec().
static void mixmonitor_ds_destroy | ( | void * | data | ) | [static] |
Definition at line 231 of file app_mixmonitor.c.
References ast_cond_signal, ast_mutex_lock, ast_mutex_unlock, mixmonitor_ds::audiohook, mixmonitor_ds::destruction_condition, mixmonitor_ds::destruction_ok, and mixmonitor_ds::lock.
00232 { 00233 struct mixmonitor_ds *mixmonitor_ds = data; 00234 00235 ast_mutex_lock(&mixmonitor_ds->lock); 00236 mixmonitor_ds->audiohook = NULL; 00237 mixmonitor_ds->destruction_ok = 1; 00238 ast_cond_signal(&mixmonitor_ds->destruction_condition); 00239 ast_mutex_unlock(&mixmonitor_ds->lock); 00240 }
static int mixmonitor_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 496 of file app_mixmonitor.c.
References args, ast_alloca, AST_APP_ARG, ast_app_parse_options(), ast_config_AST_MONITOR_DIR, AST_DECLARE_APP_ARGS, ast_log(), ast_mkdir(), ast_module_ref(), ast_module_unref(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_flags::flags, get_volfactor, launch_monitor_thread(), LOG_NOTICE, LOG_WARNING, MUXFLAG_READVOLUME, MUXFLAG_VOLUME, MUXFLAG_WRITEVOLUME, OPT_ARG_ARRAY_SIZE, OPT_ARG_READVOLUME, OPT_ARG_VOLUME, OPT_ARG_WRITEVOLUME, parse(), and pbx_builtin_setvar_helper().
Referenced by handle_cli_mixmonitor(), and load_module().
00497 { 00498 int x, readvol = 0, writevol = 0; 00499 struct ast_flags flags = {0}; 00500 char *parse, *tmp, *slash; 00501 AST_DECLARE_APP_ARGS(args, 00502 AST_APP_ARG(filename); 00503 AST_APP_ARG(options); 00504 AST_APP_ARG(post_process); 00505 ); 00506 00507 if (ast_strlen_zero(data)) { 00508 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00509 return -1; 00510 } 00511 00512 parse = ast_strdupa(data); 00513 00514 AST_STANDARD_APP_ARGS(args, parse); 00515 00516 if (ast_strlen_zero(args.filename)) { 00517 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00518 return -1; 00519 } 00520 00521 if (args.options) { 00522 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, }; 00523 00524 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options); 00525 00526 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) { 00527 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) { 00528 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n"); 00529 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) { 00530 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]); 00531 } else { 00532 readvol = get_volfactor(x); 00533 } 00534 } 00535 00536 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) { 00537 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) { 00538 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n"); 00539 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) { 00540 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]); 00541 } else { 00542 writevol = get_volfactor(x); 00543 } 00544 } 00545 00546 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) { 00547 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) { 00548 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n"); 00549 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) { 00550 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]); 00551 } else { 00552 readvol = writevol = get_volfactor(x); 00553 } 00554 } 00555 } 00556 00557 /* if not provided an absolute path, use the system-configured monitoring directory */ 00558 if (args.filename[0] != '/') { 00559 char *build; 00560 00561 build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3); 00562 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename); 00563 args.filename = build; 00564 } 00565 00566 tmp = ast_strdupa(args.filename); 00567 if ((slash = strrchr(tmp, '/'))) 00568 *slash = '\0'; 00569 ast_mkdir(tmp, 0777); 00570 00571 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename); 00572 00573 /* If launch_monitor_thread works, the module reference must not be released until it is finished. */ 00574 ast_module_ref(ast_module_info->self); 00575 if (launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process)) { 00576 ast_module_unref(ast_module_info->self); 00577 } 00578 00579 return 0; 00580 }
static void mixmonitor_free | ( | struct mixmonitor * | mixmonitor | ) | [static] |
Definition at line 279 of file app_mixmonitor.c.
References ast_cond_destroy, ast_free, ast_mutex_destroy, mixmonitor_ds::destruction_condition, mixmonitor_ds::lock, and mixmonitor::mixmonitor_ds.
Referenced by launch_monitor_thread(), and mixmonitor_thread().
00280 { 00281 if (mixmonitor) { 00282 if (mixmonitor->mixmonitor_ds) { 00283 ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock); 00284 ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition); 00285 ast_free(mixmonitor->mixmonitor_ds); 00286 } 00287 ast_free(mixmonitor); 00288 } 00289 }
static void* mixmonitor_thread | ( | void * | obj | ) | [static] |
Definition at line 290 of file app_mixmonitor.c.
References AST_AUDIOHOOK_DIRECTION_BOTH, ast_audiohook_lock, ast_audiohook_read_frame(), AST_AUDIOHOOK_STATUS_RUNNING, ast_audiohook_trigger_wait(), ast_audiohook_unlock, ast_autochan_destroy(), ast_bridged_channel(), ast_cond_wait, AST_FORMAT_SLINEAR, ast_frame_free(), AST_LIST_NEXT, ast_log(), ast_module_unref(), ast_mutex_lock, ast_mutex_unlock, ast_safe_system(), ast_test_flag, ast_test_suite_event_notify, ast_verb, ast_writefile(), ast_writestream(), mixmonitor::audiohook, mixmonitor::autochan, ast_autochan::chan, destroy_monitor_audiohook(), mixmonitor_ds::destruction_condition, mixmonitor_ds::destruction_ok, ext, mixmonitor::filename, mixmonitor_ds::fs, mixmonitor_ds::fs_quit, mixmonitor_ds::lock, LOG_ERROR, mixmonitor::mixmonitor_ds, mixmonitor_ds_close_fs(), mixmonitor_free(), MUXFLAG_APPEND, MUXFLAG_BRIDGED, mixmonitor::name, mixmonitor::post_process, SAMPLES_PER_FRAME, and ast_audiohook::status.
Referenced by launch_monitor_thread().
00291 { 00292 struct mixmonitor *mixmonitor = obj; 00293 struct ast_filestream **fs = NULL; 00294 unsigned int oflags; 00295 char *ext; 00296 char *last_slash; 00297 int errflag = 0; 00298 00299 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name); 00300 00301 fs = &mixmonitor->mixmonitor_ds->fs; 00302 00303 /* The audiohook must enter and exit the loop locked */ 00304 ast_audiohook_lock(&mixmonitor->audiohook); 00305 while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) { 00306 struct ast_frame *fr = NULL; 00307 00308 if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) { 00309 ast_audiohook_trigger_wait(&mixmonitor->audiohook); 00310 00311 if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) { 00312 break; 00313 } 00314 continue; 00315 } 00316 00317 /* audiohook lock is not required for the next block. 00318 * Unlock it, but remember to lock it before looping or exiting */ 00319 ast_audiohook_unlock(&mixmonitor->audiohook); 00320 00321 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) { 00322 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); 00323 /* Initialize the file if not already done so */ 00324 if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) { 00325 oflags = O_CREAT | O_WRONLY; 00326 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC; 00327 00328 last_slash = strrchr(mixmonitor->filename, '/'); 00329 if ((ext = strrchr(mixmonitor->filename, '.')) && (ext > last_slash)) 00330 *(ext++) = '\0'; 00331 else 00332 ext = "raw"; 00333 00334 if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) { 00335 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext); 00336 errflag = 1; 00337 } 00338 } 00339 00340 /* Write out the frame(s) */ 00341 if (*fs) { 00342 struct ast_frame *cur; 00343 00344 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) { 00345 ast_writestream(*fs, cur); 00346 } 00347 } 00348 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); 00349 } 00350 /* All done! free it. */ 00351 ast_frame_free(fr, 0); 00352 00353 ast_audiohook_lock(&mixmonitor->audiohook); 00354 } 00355 00356 /* Test Event */ 00357 ast_test_suite_event_notify("MIXMONITOR_END", "Channel: %s\r\n" 00358 "File: %s\r\n", 00359 mixmonitor->autochan->chan->name, 00360 mixmonitor->filename); 00361 00362 ast_audiohook_unlock(&mixmonitor->audiohook); 00363 00364 ast_autochan_destroy(mixmonitor->autochan); 00365 00366 /* Datastore cleanup. close the filestream and wait for ds destruction */ 00367 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); 00368 mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds); 00369 if (!mixmonitor->mixmonitor_ds->destruction_ok) { 00370 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock); 00371 } 00372 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); 00373 00374 /* kill the audiohook */ 00375 destroy_monitor_audiohook(mixmonitor); 00376 00377 if (mixmonitor->post_process) { 00378 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process); 00379 ast_safe_system(mixmonitor->post_process); 00380 } 00381 00382 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name); 00383 mixmonitor_free(mixmonitor); 00384 00385 ast_module_unref(ast_module_info->self); 00386 return NULL; 00387 }
static int setup_mixmonitor_ds | ( | struct mixmonitor * | mixmonitor, | |
struct ast_channel * | chan | |||
) | [static] |
Definition at line 389 of file app_mixmonitor.c.
References ast_calloc, ast_channel_datastore_add(), ast_channel_lock, ast_channel_unlock, ast_cond_destroy, ast_cond_init, ast_datastore_alloc, ast_free, ast_mutex_destroy, ast_mutex_init, mixmonitor::audiohook, mixmonitor_ds::audiohook, ast_datastore::data, mixmonitor_ds::destruction_condition, mixmonitor_ds::lock, and mixmonitor::mixmonitor_ds.
Referenced by launch_monitor_thread().
00390 { 00391 struct ast_datastore *datastore = NULL; 00392 struct mixmonitor_ds *mixmonitor_ds; 00393 00394 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) { 00395 return -1; 00396 } 00397 00398 ast_mutex_init(&mixmonitor_ds->lock); 00399 ast_cond_init(&mixmonitor_ds->destruction_condition, NULL); 00400 00401 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) { 00402 ast_mutex_destroy(&mixmonitor_ds->lock); 00403 ast_cond_destroy(&mixmonitor_ds->destruction_condition); 00404 ast_free(mixmonitor_ds); 00405 return -1; 00406 } 00407 00408 mixmonitor_ds->audiohook = &mixmonitor->audiohook; 00409 datastore->data = mixmonitor_ds; 00410 00411 ast_channel_lock(chan); 00412 ast_channel_datastore_add(chan, datastore); 00413 ast_channel_unlock(chan); 00414 00415 mixmonitor->mixmonitor_ds = mixmonitor_ds; 00416 return 0; 00417 }
static int startmon | ( | struct ast_channel * | chan, | |
struct ast_audiohook * | audiohook | |||
) | [static] |
Definition at line 261 of file app_mixmonitor.c.
References ast_audiohook_attach(), ast_bridged_channel(), AST_FLAG_NBRIDGE, ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, and ast_test_flag.
Referenced by launch_monitor_thread().
00262 { 00263 struct ast_channel *peer = NULL; 00264 int res = 0; 00265 00266 if (!chan) 00267 return -1; 00268 00269 ast_audiohook_attach(chan, audiohook); 00270 00271 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) 00272 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00273 00274 return res; 00275 }
static int stop_mixmonitor_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 582 of file app_mixmonitor.c.
References ast_audiohook_detach_source(), ast_audiohook_lock, ast_audiohook_unlock, ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_unlock, ast_cond_signal, ast_datastore_free(), ast_mutex_lock, ast_mutex_unlock, mixmonitor_ds::audiohook, ast_datastore::data, mixmonitor_ds::lock, mixmonitor_ds_close_fs(), and ast_audiohook::trigger.
Referenced by load_module().
00583 { 00584 struct ast_datastore *datastore = NULL; 00585 00586 ast_channel_lock(chan); 00587 ast_audiohook_detach_source(chan, mixmonitor_spy_type); 00588 if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) { 00589 struct mixmonitor_ds *mixmonitor_ds = datastore->data; 00590 00591 ast_mutex_lock(&mixmonitor_ds->lock); 00592 00593 /* closing the filestream here guarantees the file is available to the dialplan 00594 * after calling StopMixMonitor */ 00595 mixmonitor_ds_close_fs(mixmonitor_ds); 00596 00597 /* The mixmonitor thread may be waiting on the audiohook trigger. 00598 * In order to exit from the mixmonitor loop before waiting on channel 00599 * destruction, poke the audiohook trigger. */ 00600 if (mixmonitor_ds->audiohook) { 00601 ast_audiohook_lock(mixmonitor_ds->audiohook); 00602 ast_cond_signal(&mixmonitor_ds->audiohook->trigger); 00603 ast_audiohook_unlock(mixmonitor_ds->audiohook); 00604 mixmonitor_ds->audiohook = NULL; 00605 } 00606 00607 ast_mutex_unlock(&mixmonitor_ds->lock); 00608 00609 /* Remove the datastore so the monitor thread can exit */ 00610 if (!ast_channel_datastore_remove(chan, datastore)) { 00611 ast_datastore_free(datastore); 00612 } 00613 } 00614 ast_channel_unlock(chan); 00615 00616 return 0; 00617 }
static int unload_module | ( | void | ) | [static] |
Definition at line 730 of file app_mixmonitor.c.
References ARRAY_LEN, ast_cli_unregister_multiple(), ast_manager_unregister(), and ast_unregister_application().
00731 { 00732 int res; 00733 00734 ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor)); 00735 res = ast_unregister_application(stop_app); 00736 res |= ast_unregister_application(app); 00737 res |= ast_manager_unregister("MixMonitorMute"); 00738 00739 return res; 00740 }
const char* const app = "MixMonitor" [static] |
Definition at line 166 of file app_mixmonitor.c.
struct ast_cli_entry cli_mixmonitor[] [static] |
{ }
Definition at line 726 of file app_mixmonitor.c.
struct ast_datastore_info mixmonitor_ds_info [static] |
{ .type = "mixmonitor", .destroy = mixmonitor_ds_destroy, }
Definition at line 242 of file app_mixmonitor.c.
const char* const mixmonitor_spy_type = "MixMonitor" [static] |
Definition at line 170 of file app_mixmonitor.c.
Referenced by builtin_automixmonitor().
const char* const stop_app = "StopMixMonitor" [static] |
Definition at line 168 of file app_mixmonitor.c.