#include "asterisk.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/audiohook.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/cli.h"
#include "asterisk/options.h"
#include "asterisk/app.h"
#include "asterisk/linkedlists.h"
#include "asterisk/utils.h"
Go to the source code of this file.
Data Structures | |
struct | mixmonitor |
Defines | |
#define | get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0 |
#define | SAMPLES_PER_FRAME 160 |
Enumerations | |
enum | { MUXFLAG_APPEND = (1 << 1), MUXFLAG_BRIDGED = (1 << 2), MUXFLAG_VOLUME = (1 << 3), MUXFLAG_READVOLUME = (1 << 4), MUXFLAG_WRITEVOLUME = (1 << 5) } |
enum | { OPT_ARG_READVOLUME = 0, OPT_ARG_WRITEVOLUME, OPT_ARG_VOLUME, OPT_ARG_ARRAY_SIZE } |
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 char * | complete_mixmonitor_cli (const char *line, const char *word, int pos, int state) |
static void | 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 | mixmonitor_cli (int fd, int argc, char **argv) |
static int | mixmonitor_exec (struct ast_channel *chan, void *data) |
static void * | mixmonitor_thread (void *obj) |
static int | startmon (struct ast_channel *chan, struct ast_audiohook *audiohook) |
static int | stop_mixmonitor_exec (struct ast_channel *chan, void *data) |
static int | unload_module (void) |
Variables | |
static const char * | app = "MixMonitor" |
static struct ast_cli_entry | cli_mixmonitor [] |
static const char * | desc |
struct module_symbols * | me |
enum { ... } | mixmonitor_args |
enum { ... } | mixmonitor_flags |
static const char * | mixmonitor_spy_type = "MixMonitor" |
static const char * | stop_app = "StopMixMonitor" |
static const char * | stop_desc |
static const char * | stop_synopsis = "Stop recording a call through MixMonitor" |
static const char * | synopsis = "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 |
#define SAMPLES_PER_FRAME 160 |
anonymous enum |
Definition at line 104 of file app_mixmonitor.c.
00104 { 00105 MUXFLAG_APPEND = (1 << 1), 00106 MUXFLAG_BRIDGED = (1 << 2), 00107 MUXFLAG_VOLUME = (1 << 3), 00108 MUXFLAG_READVOLUME = (1 << 4), 00109 MUXFLAG_WRITEVOLUME = (1 << 5), 00110 } mixmonitor_flags;
anonymous enum |
Definition at line 112 of file app_mixmonitor.c.
00112 { 00113 OPT_ARG_READVOLUME = 0, 00114 OPT_ARG_WRITEVOLUME, 00115 OPT_ARG_VOLUME, 00116 OPT_ARG_ARRAY_SIZE, 00117 } mixmonitor_args;
AST_APP_OPTIONS | ( | mixmonitor_opts | ) |
AST_MODULE_INFO_STANDARD | ( | ASTERISK_GPL_KEY | , | |
"Mixed Audio Monitoring Application" | ||||
) |
static char* complete_mixmonitor_cli | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 408 of file app_mixmonitor.c.
References ast_complete_channels().
00409 { 00410 return ast_complete_channels(line, word, pos, state, 2); 00411 }
static void 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 217 of file app_mixmonitor.c.
References ast_audiohook_destroy(), ast_audiohook_init(), AST_AUDIOHOOK_TRIGGER_SYNC, AST_AUDIOHOOK_TYPE_SPY, ast_log(), ast_pthread_create_background, ast_set_flag, ast_strdupa, ast_strlen_zero(), mixmonitor::audiohook, calloc, mixmonitor::chan, mixmonitor::filename, mixmonitor::flags, free, len, LOG_WARNING, mixmonitor_thread(), mixmonitor::name, ast_audiohook::options, pbx_substitute_variables_helper(), mixmonitor::post_process, ast_audiohook_options::read_volume, startmon(), thread, and ast_audiohook_options::write_volume.
Referenced by mixmonitor_exec().
00219 { 00220 pthread_attr_t attr; 00221 pthread_t thread; 00222 struct mixmonitor *mixmonitor; 00223 char postprocess2[1024] = ""; 00224 size_t len; 00225 00226 len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2; 00227 00228 /* If a post process system command is given attach it to the structure */ 00229 if (!ast_strlen_zero(post_process)) { 00230 char *p1, *p2; 00231 00232 p1 = ast_strdupa(post_process); 00233 for (p2 = p1; *p2 ; p2++) { 00234 if (*p2 == '^' && *(p2+1) == '{') { 00235 *p2 = '$'; 00236 } 00237 } 00238 00239 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1); 00240 if (!ast_strlen_zero(postprocess2)) 00241 len += strlen(postprocess2) + 1; 00242 } 00243 00244 /* Pre-allocate mixmonitor structure and spy */ 00245 if (!(mixmonitor = calloc(1, len))) { 00246 return; 00247 } 00248 00249 /* Copy over flags and channel name */ 00250 mixmonitor->flags = flags; 00251 mixmonitor->chan = chan; 00252 mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor); 00253 strcpy(mixmonitor->name, chan->name); 00254 if (!ast_strlen_zero(postprocess2)) { 00255 mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2; 00256 strcpy(mixmonitor->post_process, postprocess2); 00257 } 00258 00259 mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1; 00260 strcpy(mixmonitor->filename, filename); 00261 00262 /* Setup the actual spy before creating our thread */ 00263 if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) { 00264 free(mixmonitor); 00265 return; 00266 } 00267 00268 ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC); 00269 00270 if (readvol) 00271 mixmonitor->audiohook.options.read_volume = readvol; 00272 if (writevol) 00273 mixmonitor->audiohook.options.write_volume = writevol; 00274 00275 if (startmon(chan, &mixmonitor->audiohook)) { 00276 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n", 00277 mixmonitor_spy_type, chan->name); 00278 /* Since we couldn't add ourselves - bail out! */ 00279 ast_audiohook_destroy(&mixmonitor->audiohook); 00280 free(mixmonitor); 00281 return; 00282 } 00283 00284 pthread_attr_init(&attr); 00285 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 00286 ast_pthread_create_background(&thread, &attr, mixmonitor_thread, mixmonitor); 00287 pthread_attr_destroy(&attr); 00288 00289 }
static int load_module | ( | void | ) | [static] |
Definition at line 435 of file app_mixmonitor.c.
References ast_cli_register_multiple(), ast_register_application(), mixmonitor_exec(), and stop_mixmonitor_exec().
00436 { 00437 int res; 00438 00439 ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry)); 00440 res = ast_register_application(app, mixmonitor_exec, synopsis, desc); 00441 res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc); 00442 00443 return res; 00444 }
static int mixmonitor_cli | ( | int | fd, | |
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 386 of file app_mixmonitor.c.
References ast_audiohook_detach_source(), ast_channel_unlock, ast_cli(), ast_get_channel_by_name_prefix_locked(), mixmonitor_exec(), RESULT_SHOWUSAGE, and RESULT_SUCCESS.
00387 { 00388 struct ast_channel *chan; 00389 00390 if (argc < 3) 00391 return RESULT_SHOWUSAGE; 00392 00393 if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) { 00394 ast_cli(fd, "No channel matching '%s' found.\n", argv[2]); 00395 return RESULT_SUCCESS; 00396 } 00397 00398 if (!strcasecmp(argv[1], "start")) 00399 mixmonitor_exec(chan, argv[3]); 00400 else if (!strcasecmp(argv[1], "stop")) 00401 ast_audiohook_detach_source(chan, mixmonitor_spy_type); 00402 00403 ast_channel_unlock(chan); 00404 00405 return RESULT_SUCCESS; 00406 }
static int mixmonitor_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 291 of file app_mixmonitor.c.
References AST_APP_ARG, ast_app_parse_options(), ast_config_AST_MONITOR_DIR, AST_DECLARE_APP_ARGS, ast_log(), ast_module_user_add, ast_module_user_remove, 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 load_module(), and mixmonitor_cli().
00292 { 00293 int x, readvol = 0, writevol = 0; 00294 struct ast_module_user *u; 00295 struct ast_flags flags = {0}; 00296 char *parse; 00297 AST_DECLARE_APP_ARGS(args, 00298 AST_APP_ARG(filename); 00299 AST_APP_ARG(options); 00300 AST_APP_ARG(post_process); 00301 ); 00302 00303 if (ast_strlen_zero(data)) { 00304 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00305 return -1; 00306 } 00307 00308 u = ast_module_user_add(chan); 00309 00310 parse = ast_strdupa(data); 00311 00312 AST_STANDARD_APP_ARGS(args, parse); 00313 00314 if (ast_strlen_zero(args.filename)) { 00315 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00316 ast_module_user_remove(u); 00317 return -1; 00318 } 00319 00320 if (args.options) { 00321 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, }; 00322 00323 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options); 00324 00325 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) { 00326 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) { 00327 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n"); 00328 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00329 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]); 00330 } else { 00331 readvol = get_volfactor(x); 00332 } 00333 } 00334 00335 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) { 00336 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) { 00337 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n"); 00338 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00339 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]); 00340 } else { 00341 writevol = get_volfactor(x); 00342 } 00343 } 00344 00345 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) { 00346 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) { 00347 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n"); 00348 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00349 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]); 00350 } else { 00351 readvol = writevol = get_volfactor(x); 00352 } 00353 } 00354 } 00355 00356 /* if not provided an absolute path, use the system-configured monitoring directory */ 00357 if (args.filename[0] != '/') { 00358 char *build; 00359 00360 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3); 00361 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename); 00362 args.filename = build; 00363 } 00364 00365 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename); 00366 launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process); 00367 00368 ast_module_user_remove(u); 00369 00370 return 0; 00371 }
static void* mixmonitor_thread | ( | void * | obj | ) | [static] |
Definition at line 145 of file app_mixmonitor.c.
References ast_audiohook_destroy(), ast_audiohook_detach(), AST_AUDIOHOOK_DIRECTION_BOTH, ast_audiohook_lock, ast_audiohook_read_frame(), AST_AUDIOHOOK_STATUS_RUNNING, ast_audiohook_trigger_wait(), ast_audiohook_unlock, ast_bridged_channel(), ast_closestream(), AST_FORMAT_SLINEAR, ast_frame_free(), ast_log(), ast_safe_system(), ast_test_flag, ast_verbose(), ast_writefile(), ast_writestream(), mixmonitor::audiohook, mixmonitor::chan, ext, mixmonitor::filename, free, LOG_ERROR, MUXFLAG_APPEND, MUXFLAG_BRIDGED, mixmonitor::name, option_verbose, mixmonitor::post_process, SAMPLES_PER_FRAME, ast_audiohook::status, and VERBOSE_PREFIX_2.
Referenced by launch_monitor_thread().
00146 { 00147 struct mixmonitor *mixmonitor = obj; 00148 struct ast_filestream *fs = NULL; 00149 unsigned int oflags; 00150 char *ext; 00151 int errflag = 0; 00152 00153 if (option_verbose > 1) 00154 ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name); 00155 00156 ast_audiohook_lock(&mixmonitor->audiohook); 00157 00158 while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { 00159 struct ast_frame *fr = NULL; 00160 00161 ast_audiohook_trigger_wait(&mixmonitor->audiohook); 00162 00163 if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) 00164 break; 00165 00166 if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) 00167 continue; 00168 00169 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || ast_bridged_channel(mixmonitor->chan)) { 00170 /* Initialize the file if not already done so */ 00171 if (!fs && !errflag) { 00172 oflags = O_CREAT | O_WRONLY; 00173 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC; 00174 00175 if ((ext = strrchr(mixmonitor->filename, '.'))) 00176 *(ext++) = '\0'; 00177 else 00178 ext = "raw"; 00179 00180 if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) { 00181 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext); 00182 errflag = 1; 00183 } 00184 } 00185 00186 /* Write out the frame */ 00187 if (fs) 00188 ast_writestream(fs, fr); 00189 } 00190 00191 /* All done! free it. */ 00192 ast_frame_free(fr, 0); 00193 } 00194 00195 ast_audiohook_detach(&mixmonitor->audiohook); 00196 ast_audiohook_unlock(&mixmonitor->audiohook); 00197 ast_audiohook_destroy(&mixmonitor->audiohook); 00198 00199 if (option_verbose > 1) 00200 ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name); 00201 00202 if (fs) 00203 ast_closestream(fs); 00204 00205 if (mixmonitor->post_process) { 00206 if (option_verbose > 2) 00207 ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process); 00208 ast_safe_system(mixmonitor->post_process); 00209 } 00210 00211 free(mixmonitor); 00212 00213 00214 return NULL; 00215 }
static int startmon | ( | struct ast_channel * | chan, | |
struct ast_audiohook * | audiohook | |||
) | [static] |
Definition at line 127 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().
00128 { 00129 struct ast_channel *peer; 00130 int res; 00131 00132 if (!chan) 00133 return -1; 00134 00135 res = ast_audiohook_attach(chan, audiohook); 00136 00137 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) 00138 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00139 00140 return res; 00141 }
static int stop_mixmonitor_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 373 of file app_mixmonitor.c.
References ast_audiohook_detach_source(), ast_module_user_add, and ast_module_user_remove.
Referenced by load_module().
00374 { 00375 struct ast_module_user *u; 00376 00377 u = ast_module_user_add(chan); 00378 00379 ast_audiohook_detach_source(chan, mixmonitor_spy_type); 00380 00381 ast_module_user_remove(u); 00382 00383 return 0; 00384 }
static int unload_module | ( | void | ) | [static] |
Definition at line 422 of file app_mixmonitor.c.
References ast_cli_unregister_multiple(), ast_module_user_hangup_all, and ast_unregister_application().
00423 { 00424 int res; 00425 00426 ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry)); 00427 res = ast_unregister_application(stop_app); 00428 res |= ast_unregister_application(app); 00429 00430 ast_module_user_hangup_all(); 00431 00432 return res; 00433 }
const char* app = "MixMonitor" [static] |
Definition at line 60 of file app_mixmonitor.c.
struct ast_cli_entry cli_mixmonitor[] [static] |
Definition at line 413 of file app_mixmonitor.c.
const char* desc [static] |
Definition at line 62 of file app_mixmonitor.c.
struct module_symbols* me |
Definition at line 91 of file app_mixmonitor.c.
enum { ... } mixmonitor_args |
enum { ... } mixmonitor_flags |
const char* mixmonitor_spy_type = "MixMonitor" [static] |
Definition at line 93 of file app_mixmonitor.c.
const char* stop_app = "StopMixMonitor" [static] |
Definition at line 83 of file app_mixmonitor.c.
const char* stop_desc [static] |
Initial value:
"" " StopMixMonitor()\n\n" "Stops the audio recording that was started with a call to MixMonitor()\n" "on the current channel.\n" ""
Definition at line 85 of file app_mixmonitor.c.
const char* stop_synopsis = "Stop recording a call through MixMonitor" [static] |
Definition at line 84 of file app_mixmonitor.c.
const char* synopsis = "Record a call and mix the audio during the recording" [static] |
Definition at line 61 of file app_mixmonitor.c.