#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "asterisk.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/chanspy.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 dependency graph for app_mixmonitor.c:
Go to the source code of this file.
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),}) | |
char * | description (void) |
Provides a description of the module. | |
char * | key () |
Returns the ASTERISK_GPL_KEY. | |
static void | launch_monitor_thread (struct ast_channel *chan, const char *filename, unsigned int flags, int readvol, int writevol, const char *post_process) |
int | load_module (void) |
Initialize the module. | |
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_channel_spy *spy) |
static void | stopmon (struct ast_channel_spy *spy) |
int | unload_module (void) |
Cleanup all module structures, sockets, etc. | |
int | usecount (void) |
Provides a usecount. | |
Variables | |
static const char * | app = "MixMonitor" |
static struct ast_cli_entry | cli_mixmonitor |
static const char * | desc |
LOCAL_USER_DECL | |
enum { ... } | mixmonitor_args |
enum { ... } | mixmonitor_flags |
static const char * | mixmonitor_spy_type = "MixMonitor" |
STANDARD_LOCAL_USER | |
static const char * | synopsis = "Record a call and mix the audio during the recording" |
static const char * | tdesc = "Mixed Audio Monitoring Application" |
Definition in file app_mixmonitor.c.
|
Definition at line 50 of file app_mixmonitor.c. |
|
Definition at line 147 of file app_mixmonitor.c. Referenced by mixmonitor_thread(). |
|
Definition at line 89 of file app_mixmonitor.c. 00089 { 00090 MUXFLAG_APPEND = (1 << 1), 00091 MUXFLAG_BRIDGED = (1 << 2), 00092 MUXFLAG_VOLUME = (1 << 3), 00093 MUXFLAG_READVOLUME = (1 << 4), 00094 MUXFLAG_WRITEVOLUME = (1 << 5), 00095 } mixmonitor_flags;
|
|
Definition at line 97 of file app_mixmonitor.c. 00097 { 00098 OPT_ARG_READVOLUME = 0, 00099 OPT_ARG_WRITEVOLUME, 00100 OPT_ARG_VOLUME, 00101 OPT_ARG_ARRAY_SIZE, 00102 } mixmonitor_args;
|
|
|
|
Provides a description of the module.
Definition at line 449 of file app_mixmonitor.c. References tdesc. 00450 { 00451 return (char *) tdesc; 00452 }
|
|
Returns the ASTERISK_GPL_KEY. This returns the ASTERISK_GPL_KEY, signifiying that you agree to the terms of the GPL stated in the ASTERISK_GPL_KEY. Your module will not load if it does not return the EXACT message:
char *key(void) { return ASTERISK_GPL_KEY; }
Definition at line 463 of file app_mixmonitor.c. References ASTERISK_GPL_KEY. 00464 { 00465 return ASTERISK_GPL_KEY; 00466 }
|
|
Definition at line 214 of file app_mixmonitor.c. References ast_closestream(), AST_FORMAT_SLINEAR, ast_log(), ast_mutex_destroy(), ast_mutex_init(), ast_pthread_create, ast_set_flag, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_writefile(), calloc, CHANSPY_FORMAT_AUDIO, CHANSPY_MIXAUDIO, CHANSPY_READ_VOLADJUST, CHANSPY_RUNNING, CHANSPY_WRITE_VOLADJUST, mixmonitor::flags, ast_channel_spy_queue::format, free, mixmonitor::fs, ast_channel_spy::lock, LOG_ERROR, LOG_WARNING, mixmonitor_spy_type, mixmonitor_thread(), MUXFLAG_APPEND, mixmonitor::name, ast_channel::name, pbx_substitute_variables_helper(), mixmonitor::post_process, ast_channel_spy::read_queue, ast_channel_spy::read_vol_adjustment, mixmonitor::spy, startmon(), ast_channel_spy::status, ast_channel_spy::type, ast_channel_spy::write_queue, and ast_channel_spy::write_vol_adjustment. Referenced by mixmonitor_exec(). 00216 { 00217 pthread_attr_t attr; 00218 pthread_t thread; 00219 struct mixmonitor *mixmonitor; 00220 char *file_name, *ext; 00221 char postprocess2[1024] = ""; 00222 unsigned int oflags; 00223 size_t len; 00224 00225 len = sizeof(*mixmonitor) + strlen(chan->name) + 1; 00226 00227 /* If a post process system command is given attach it to the structure */ 00228 if (!ast_strlen_zero(post_process)) { 00229 char *p1, *p2; 00230 00231 p1 = ast_strdupa(post_process); 00232 for (p2 = p1; *p2 ; p2++) { 00233 if (*p2 == '^' && *(p2+1) == '{') { 00234 *p2 = '$'; 00235 } 00236 } 00237 00238 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1); 00239 if (!ast_strlen_zero(postprocess2)) 00240 len += strlen(postprocess2) + 1; 00241 } 00242 00243 /* Pre-allocate mixmonitor structure and spy */ 00244 if (!(mixmonitor = calloc(1, len))) { 00245 ast_log(LOG_ERROR, "Memory Error!\n"); 00246 return; 00247 } 00248 00249 /* Copy over flags and channel name */ 00250 mixmonitor->flags = flags; 00251 mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor); 00252 strcpy(mixmonitor->name, chan->name); 00253 if (!ast_strlen_zero(postprocess2)) { 00254 mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + 1; 00255 strcpy(mixmonitor->post_process, postprocess2); 00256 } 00257 00258 /* Determine creation flags and filename plus extension for filestream */ 00259 oflags = O_CREAT | O_WRONLY; 00260 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC; 00261 file_name = ast_strdupa(filename); 00262 if ((ext = strrchr(file_name, '.'))) { 00263 *(ext++) = '\0'; 00264 } else { 00265 ext = "raw"; 00266 } 00267 00268 /* Move onto actually creating the filestream */ 00269 mixmonitor->fs = ast_writefile(file_name, ext, NULL, oflags, 0, 0644); 00270 if (!mixmonitor->fs) { 00271 ast_log(LOG_ERROR, "Cannot open %s.%s\n", file_name, ext); 00272 free(mixmonitor); 00273 return; 00274 } 00275 00276 /* Setup the actual spy before creating our thread */ 00277 ast_set_flag(&mixmonitor->spy, CHANSPY_FORMAT_AUDIO); 00278 ast_set_flag(&mixmonitor->spy, CHANSPY_MIXAUDIO); 00279 mixmonitor->spy.type = mixmonitor_spy_type; 00280 mixmonitor->spy.status = CHANSPY_RUNNING; 00281 mixmonitor->spy.read_queue.format = AST_FORMAT_SLINEAR; 00282 mixmonitor->spy.write_queue.format = AST_FORMAT_SLINEAR; 00283 if (readvol) { 00284 ast_set_flag(&mixmonitor->spy, CHANSPY_READ_VOLADJUST); 00285 mixmonitor->spy.read_vol_adjustment = readvol; 00286 } 00287 if (writevol) { 00288 ast_set_flag(&mixmonitor->spy, CHANSPY_WRITE_VOLADJUST); 00289 mixmonitor->spy.write_vol_adjustment = writevol; 00290 } 00291 ast_mutex_init(&mixmonitor->spy.lock); 00292 00293 if (startmon(chan, &mixmonitor->spy)) { 00294 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n", 00295 mixmonitor->spy.type, chan->name); 00296 /* Since we couldn't add ourselves - bail out! */ 00297 ast_mutex_destroy(&mixmonitor->spy.lock); 00298 ast_closestream(mixmonitor->fs); 00299 free(mixmonitor); 00300 return; 00301 } 00302 00303 pthread_attr_init(&attr); 00304 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 00305 ast_pthread_create(&thread, &attr, mixmonitor_thread, mixmonitor); 00306 pthread_attr_destroy(&attr); 00307 00308 }
|
|
Initialize the module. Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.
Definition at line 439 of file app_mixmonitor.c. References app, ast_cli_register(), ast_register_application(), desc, mixmonitor_exec(), and synopsis. 00440 { 00441 int res; 00442 00443 res = ast_cli_register(&cli_mixmonitor); 00444 res |= ast_register_application(app, mixmonitor_exec, synopsis, desc); 00445 00446 return res; 00447 }
|
|
Definition at line 396 of file app_mixmonitor.c. References ast_channel_spy_stop_by_type(), ast_cli(), ast_get_channel_by_name_prefix_locked(), ast_mutex_unlock(), ast_channel::lock, mixmonitor_exec(), mixmonitor_spy_type, RESULT_SHOWUSAGE, and RESULT_SUCCESS. 00397 { 00398 struct ast_channel *chan; 00399 00400 if (argc < 3) 00401 return RESULT_SHOWUSAGE; 00402 00403 if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) { 00404 ast_cli(fd, "No channel matching '%s' found.\n", argv[2]); 00405 return RESULT_SUCCESS; 00406 } 00407 00408 if (!strcasecmp(argv[1], "start")) 00409 mixmonitor_exec(chan, argv[3]); 00410 else if (!strcasecmp(argv[1], "stop")) 00411 ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type); 00412 00413 ast_mutex_unlock(&chan->lock); 00414 00415 return RESULT_SUCCESS; 00416 }
|
|
Definition at line 310 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_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_flags::flags, get_volfactor, launch_monitor_thread(), LOCAL_USER_ADD, LOCAL_USER_REMOVE, 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(). 00311 { 00312 int x, readvol = 0, writevol = 0; 00313 struct localuser *u; 00314 struct ast_flags flags = {0}; 00315 char *parse; 00316 AST_DECLARE_APP_ARGS(args, 00317 AST_APP_ARG(filename); 00318 AST_APP_ARG(options); 00319 AST_APP_ARG(post_process); 00320 ); 00321 00322 if (ast_strlen_zero(data)) { 00323 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00324 return -1; 00325 } 00326 00327 LOCAL_USER_ADD(u); 00328 00329 if (!(parse = ast_strdupa(data))) { 00330 ast_log(LOG_WARNING, "Memory Error!\n"); 00331 LOCAL_USER_REMOVE(u); 00332 return -1; 00333 } 00334 00335 AST_STANDARD_APP_ARGS(args, parse); 00336 00337 if (ast_strlen_zero(args.filename)) { 00338 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00339 LOCAL_USER_REMOVE(u); 00340 return -1; 00341 } 00342 00343 if (args.options) { 00344 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, }; 00345 00346 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options); 00347 00348 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) { 00349 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) { 00350 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n"); 00351 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00352 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]); 00353 } else { 00354 readvol = get_volfactor(x); 00355 } 00356 } 00357 00358 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) { 00359 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) { 00360 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n"); 00361 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00362 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]); 00363 } else { 00364 writevol = get_volfactor(x); 00365 } 00366 } 00367 00368 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) { 00369 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) { 00370 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n"); 00371 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00372 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]); 00373 } else { 00374 readvol = writevol = get_volfactor(x); 00375 } 00376 } 00377 } 00378 00379 /* if not provided an absolute path, use the system-configured monitoring directory */ 00380 if (args.filename[0] != '/') { 00381 char *build; 00382 00383 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3); 00384 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename); 00385 args.filename = build; 00386 } 00387 00388 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename); 00389 launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process); 00390 00391 LOCAL_USER_REMOVE(u); 00392 00393 return 0; 00394 }
|
|
Definition at line 149 of file app_mixmonitor.c. References ast_bridged_channel(), ast_channel_spy_read_frame(), ast_channel_spy_trigger_wait(), ast_closestream(), ast_frfree(), ast_mutex_destroy(), ast_mutex_lock(), ast_mutex_unlock(), ast_safe_system(), ast_test_flag, ast_verbose(), ast_writestream(), ast_channel_spy::chan, CHANSPY_RUNNING, free, mixmonitor::fs, ast_channel_spy::lock, MUXFLAG_BRIDGED, mixmonitor::name, ast_frame::next, option_verbose, mixmonitor::post_process, SAMPLES_PER_FRAME, mixmonitor::spy, STANDARD_DECREMENT_USECOUNT, STANDARD_INCREMENT_USECOUNT, ast_channel_spy::status, stopmon(), and VERBOSE_PREFIX_2. Referenced by launch_monitor_thread(). 00150 { 00151 struct mixmonitor *mixmonitor = obj; 00152 struct ast_frame *f = NULL; 00153 00154 STANDARD_INCREMENT_USECOUNT; 00155 00156 if (option_verbose > 1) 00157 ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name); 00158 00159 ast_mutex_lock(&mixmonitor->spy.lock); 00160 00161 while (mixmonitor->spy.chan) { 00162 struct ast_frame *next; 00163 int write; 00164 00165 ast_channel_spy_trigger_wait(&mixmonitor->spy); 00166 00167 if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING) { 00168 break; 00169 } 00170 00171 while (1) { 00172 if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME))) 00173 break; 00174 00175 write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || 00176 ast_bridged_channel(mixmonitor->spy.chan)); 00177 00178 /* it is possible for ast_channel_spy_read_frame() to return a chain 00179 of frames if a queue flush was necessary, so process them 00180 */ 00181 for (; f; f = next) { 00182 next = f->next; 00183 if (write) 00184 ast_writestream(mixmonitor->fs, f); 00185 ast_frfree(f); 00186 } 00187 } 00188 } 00189 00190 ast_mutex_unlock(&mixmonitor->spy.lock); 00191 00192 stopmon(&mixmonitor->spy); 00193 00194 if (option_verbose > 1) 00195 ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name); 00196 00197 if (mixmonitor->post_process) { 00198 if (option_verbose > 2) 00199 ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process); 00200 ast_safe_system(mixmonitor->post_process); 00201 } 00202 00203 ast_mutex_destroy(&mixmonitor->spy.lock); 00204 00205 ast_closestream(mixmonitor->fs); 00206 00207 free(mixmonitor); 00208 00209 STANDARD_DECREMENT_USECOUNT; 00210 00211 return NULL; 00212 }
|
|
Definition at line 129 of file app_mixmonitor.c. References ast_bridged_channel(), ast_channel_spy_add(), AST_FLAG_NBRIDGE, ast_mutex_lock(), ast_mutex_unlock(), ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, ast_test_flag, and ast_channel::lock. Referenced by launch_monitor_thread(). 00130 { 00131 struct ast_channel *peer; 00132 int res; 00133 00134 if (!chan) 00135 return -1; 00136 00137 ast_mutex_lock(&chan->lock); 00138 res = ast_channel_spy_add(chan, spy); 00139 ast_mutex_unlock(&chan->lock); 00140 00141 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) 00142 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00143 00144 return res; 00145 }
|
|
Definition at line 112 of file app_mixmonitor.c. References ast_channel_spy_remove(), ast_mutex_lock(), ast_mutex_unlock(), ast_channel_spy::chan, CHANSPY_DONE, ast_channel::lock, and ast_channel_spy::status. Referenced by mixmonitor_thread(). 00113 { 00114 struct ast_channel *chan = spy->chan; 00115 00116 /* If our status has changed to DONE, then the channel we're spying on is gone.... 00117 DON'T TOUCH IT!!! RUN AWAY!!! */ 00118 if (spy->status == CHANSPY_DONE) 00119 return; 00120 00121 if (!chan) 00122 return; 00123 00124 ast_mutex_lock(&chan->lock); 00125 ast_channel_spy_remove(chan, spy); 00126 ast_mutex_unlock(&chan->lock); 00127 }
|
|
Cleanup all module structures, sockets, etc. This is called at exit. Any registrations and memory allocations need to be unregistered and free'd here. Nothing else will do these for you (until exit).
Definition at line 427 of file app_mixmonitor.c. References app, ast_cli_unregister(), ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS. 00428 { 00429 int res; 00430 00431 res = ast_cli_unregister(&cli_mixmonitor); 00432 res |= ast_unregister_application(app); 00433 00434 STANDARD_HANGUP_LOCALUSERS; 00435 00436 return res; 00437 }
|
|
Provides a usecount. This function will be called by various parts of asterisk. Basically, all it has to do is to return a usecount when called. You will need to maintain your usecount within the module somewhere. The usecount should be how many channels provided by this module are in use.
Definition at line 454 of file app_mixmonitor.c. References STANDARD_USECOUNT. 00455 { 00456 int res; 00457 00458 STANDARD_USECOUNT(res); 00459 00460 return res; 00461 }
|
|
Definition at line 53 of file app_mixmonitor.c. |
|
Definition at line 419 of file app_mixmonitor.c. |
|
Definition at line 55 of file app_mixmonitor.c. |
|
Definition at line 77 of file app_mixmonitor.c. |
|
|
|
|
|
Definition at line 79 of file app_mixmonitor.c. Referenced by launch_monitor_thread(), and mixmonitor_cli(). |
|
Definition at line 75 of file app_mixmonitor.c. |
|
Definition at line 54 of file app_mixmonitor.c. |
|
Definition at line 52 of file app_mixmonitor.c. |