#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.
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),}) | |
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) |
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.
#define get_volfactor | ( | x | ) | x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0 |
Definition at line 50 of file app_mixmonitor.c.
#define SAMPLES_PER_FRAME 160 |
anonymous enum |
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;
anonymous enum |
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;
AST_APP_OPTIONS | ( | mixmonitor_opts | ) |
char* description | ( | void | ) |
Provides a description of the module.
Definition at line 429 of file app_mixmonitor.c.
00430 { 00431 return (char *) tdesc; 00432 }
char* key | ( | void | ) |
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 443 of file app_mixmonitor.c.
References ASTERISK_GPL_KEY.
00444 { 00445 return ASTERISK_GPL_KEY; 00446 }
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 194 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, free, LOG_ERROR, LOG_WARNING, mixmonitor_thread(), MUXFLAG_APPEND, mixmonitor::name, ast_channel::name, pbx_substitute_variables_helper(), and startmon().
Referenced by mixmonitor_exec().
00196 { 00197 pthread_attr_t attr; 00198 pthread_t thread; 00199 struct mixmonitor *mixmonitor; 00200 char *file_name, *ext; 00201 char postprocess2[1024] = ""; 00202 unsigned int oflags; 00203 size_t len; 00204 00205 len = sizeof(*mixmonitor) + strlen(chan->name) + 1; 00206 00207 /* If a post process system command is given attach it to the structure */ 00208 if (!ast_strlen_zero(post_process)) { 00209 char *p1, *p2; 00210 00211 p1 = ast_strdupa(post_process); 00212 for (p2 = p1; *p2 ; p2++) { 00213 if (*p2 == '^' && *(p2+1) == '{') { 00214 *p2 = '$'; 00215 } 00216 } 00217 00218 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1); 00219 if (!ast_strlen_zero(postprocess2)) 00220 len += strlen(postprocess2) + 1; 00221 } 00222 00223 /* Pre-allocate mixmonitor structure and spy */ 00224 if (!(mixmonitor = calloc(1, len))) { 00225 ast_log(LOG_ERROR, "Memory Error!\n"); 00226 return; 00227 } 00228 00229 /* Copy over flags and channel name */ 00230 mixmonitor->flags = flags; 00231 mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor); 00232 strcpy(mixmonitor->name, chan->name); 00233 if (!ast_strlen_zero(postprocess2)) { 00234 mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + 1; 00235 strcpy(mixmonitor->post_process, postprocess2); 00236 } 00237 00238 /* Determine creation flags and filename plus extension for filestream */ 00239 oflags = O_CREAT | O_WRONLY; 00240 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC; 00241 file_name = ast_strdupa(filename); 00242 if ((ext = strrchr(file_name, '.'))) { 00243 *(ext++) = '\0'; 00244 } else { 00245 ext = "raw"; 00246 } 00247 00248 /* Move onto actually creating the filestream */ 00249 mixmonitor->fs = ast_writefile(file_name, ext, NULL, oflags, 0, 0644); 00250 if (!mixmonitor->fs) { 00251 ast_log(LOG_ERROR, "Cannot open %s.%s\n", file_name, ext); 00252 free(mixmonitor); 00253 return; 00254 } 00255 00256 /* Setup the actual spy before creating our thread */ 00257 ast_set_flag(&mixmonitor->spy, CHANSPY_FORMAT_AUDIO); 00258 ast_set_flag(&mixmonitor->spy, CHANSPY_MIXAUDIO); 00259 mixmonitor->spy.type = mixmonitor_spy_type; 00260 mixmonitor->spy.status = CHANSPY_RUNNING; 00261 mixmonitor->spy.read_queue.format = AST_FORMAT_SLINEAR; 00262 mixmonitor->spy.write_queue.format = AST_FORMAT_SLINEAR; 00263 if (readvol) { 00264 ast_set_flag(&mixmonitor->spy, CHANSPY_READ_VOLADJUST); 00265 mixmonitor->spy.read_vol_adjustment = readvol; 00266 } 00267 if (writevol) { 00268 ast_set_flag(&mixmonitor->spy, CHANSPY_WRITE_VOLADJUST); 00269 mixmonitor->spy.write_vol_adjustment = writevol; 00270 } 00271 ast_mutex_init(&mixmonitor->spy.lock); 00272 00273 if (startmon(chan, &mixmonitor->spy)) { 00274 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n", 00275 mixmonitor->spy.type, chan->name); 00276 /* Since we couldn't add ourselves - bail out! */ 00277 ast_mutex_destroy(&mixmonitor->spy.lock); 00278 ast_closestream(mixmonitor->fs); 00279 free(mixmonitor); 00280 return; 00281 } 00282 00283 pthread_attr_init(&attr); 00284 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 00285 ast_pthread_create(&thread, &attr, mixmonitor_thread, mixmonitor); 00286 pthread_attr_destroy(&attr); 00287 00288 }
int load_module | ( | void | ) |
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 419 of file app_mixmonitor.c.
References ast_cli_register(), ast_register_application(), cli_mixmonitor, and mixmonitor_exec().
00420 { 00421 int res; 00422 00423 res = ast_cli_register(&cli_mixmonitor); 00424 res |= ast_register_application(app, mixmonitor_exec, synopsis, desc); 00425 00426 return res; 00427 }
static int mixmonitor_cli | ( | int | fd, | |
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 376 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(), RESULT_SHOWUSAGE, and RESULT_SUCCESS.
00377 { 00378 struct ast_channel *chan; 00379 00380 if (argc < 3) 00381 return RESULT_SHOWUSAGE; 00382 00383 if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) { 00384 ast_cli(fd, "No channel matching '%s' found.\n", argv[2]); 00385 return RESULT_SUCCESS; 00386 } 00387 00388 if (!strcasecmp(argv[1], "start")) 00389 mixmonitor_exec(chan, argv[3]); 00390 else if (!strcasecmp(argv[1], "stop")) 00391 ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type); 00392 00393 ast_mutex_unlock(&chan->lock); 00394 00395 return RESULT_SUCCESS; 00396 }
static int mixmonitor_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 290 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().
00291 { 00292 int x, readvol = 0, writevol = 0; 00293 struct localuser *u; 00294 struct ast_flags flags = {0}; 00295 char *parse; 00296 AST_DECLARE_APP_ARGS(args, 00297 AST_APP_ARG(filename); 00298 AST_APP_ARG(options); 00299 AST_APP_ARG(post_process); 00300 ); 00301 00302 if (ast_strlen_zero(data)) { 00303 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00304 return -1; 00305 } 00306 00307 LOCAL_USER_ADD(u); 00308 00309 if (!(parse = ast_strdupa(data))) { 00310 ast_log(LOG_WARNING, "Memory Error!\n"); 00311 LOCAL_USER_REMOVE(u); 00312 return -1; 00313 } 00314 00315 AST_STANDARD_APP_ARGS(args, parse); 00316 00317 if (ast_strlen_zero(args.filename)) { 00318 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00319 LOCAL_USER_REMOVE(u); 00320 return -1; 00321 } 00322 00323 if (args.options) { 00324 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, }; 00325 00326 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options); 00327 00328 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) { 00329 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) { 00330 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n"); 00331 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00332 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]); 00333 } else { 00334 readvol = get_volfactor(x); 00335 } 00336 } 00337 00338 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) { 00339 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) { 00340 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n"); 00341 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00342 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]); 00343 } else { 00344 writevol = get_volfactor(x); 00345 } 00346 } 00347 00348 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) { 00349 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) { 00350 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n"); 00351 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00352 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]); 00353 } else { 00354 readvol = writevol = get_volfactor(x); 00355 } 00356 } 00357 } 00358 00359 /* if not provided an absolute path, use the system-configured monitoring directory */ 00360 if (args.filename[0] != '/') { 00361 char *build; 00362 00363 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3); 00364 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename); 00365 args.filename = build; 00366 } 00367 00368 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename); 00369 launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process); 00370 00371 LOCAL_USER_REMOVE(u); 00372 00373 return 0; 00374 }
static void* mixmonitor_thread | ( | void * | obj | ) | [static] |
Definition at line 132 of file app_mixmonitor.c.
References ast_bridged_channel(), ast_channel_spy_free(), ast_channel_spy_read_frame(), ast_channel_spy_trigger_wait(), ast_closestream(), ast_frfree(), 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, and VERBOSE_PREFIX_2.
Referenced by launch_monitor_thread().
00133 { 00134 struct mixmonitor *mixmonitor = obj; 00135 struct ast_frame *f = NULL; 00136 00137 STANDARD_INCREMENT_USECOUNT; 00138 00139 if (option_verbose > 1) 00140 ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name); 00141 00142 ast_mutex_lock(&mixmonitor->spy.lock); 00143 00144 while (mixmonitor->spy.chan) { 00145 struct ast_frame *next; 00146 int write; 00147 00148 ast_channel_spy_trigger_wait(&mixmonitor->spy); 00149 00150 if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING) 00151 break; 00152 00153 while (1) { 00154 if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME))) 00155 break; 00156 00157 write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || 00158 ast_bridged_channel(mixmonitor->spy.chan)); 00159 00160 /* it is possible for ast_channel_spy_read_frame() to return a chain 00161 of frames if a queue flush was necessary, so process them 00162 */ 00163 for (; f; f = next) { 00164 next = f->next; 00165 if (write) 00166 ast_writestream(mixmonitor->fs, f); 00167 ast_frfree(f); 00168 } 00169 } 00170 } 00171 00172 ast_mutex_unlock(&mixmonitor->spy.lock); 00173 00174 ast_channel_spy_free(&mixmonitor->spy); 00175 00176 if (option_verbose > 1) 00177 ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name); 00178 00179 if (mixmonitor->post_process) { 00180 if (option_verbose > 2) 00181 ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process); 00182 ast_safe_system(mixmonitor->post_process); 00183 } 00184 00185 ast_closestream(mixmonitor->fs); 00186 00187 free(mixmonitor); 00188 00189 STANDARD_DECREMENT_USECOUNT; 00190 00191 return NULL; 00192 }
static int startmon | ( | struct ast_channel * | chan, | |
struct ast_channel_spy * | spy | |||
) | [static] |
Definition at line 112 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().
00113 { 00114 struct ast_channel *peer; 00115 int res; 00116 00117 if (!chan) 00118 return -1; 00119 00120 ast_mutex_lock(&chan->lock); 00121 res = ast_channel_spy_add(chan, spy); 00122 ast_mutex_unlock(&chan->lock); 00123 00124 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) 00125 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00126 00127 return res; 00128 }
int unload_module | ( | void | ) |
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 407 of file app_mixmonitor.c.
References ast_cli_unregister(), ast_unregister_application(), cli_mixmonitor, and STANDARD_HANGUP_LOCALUSERS.
00408 { 00409 int res; 00410 00411 res = ast_cli_unregister(&cli_mixmonitor); 00412 res |= ast_unregister_application(app); 00413 00414 STANDARD_HANGUP_LOCALUSERS; 00415 00416 return res; 00417 }
int usecount | ( | void | ) |
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 434 of file app_mixmonitor.c.
References STANDARD_USECOUNT.
00435 { 00436 int res; 00437 00438 STANDARD_USECOUNT(res); 00439 00440 return res; 00441 }
const char* app = "MixMonitor" [static] |
Definition at line 53 of file app_mixmonitor.c.
struct ast_cli_entry cli_mixmonitor [static] |
const char* desc [static] |
Definition at line 55 of file app_mixmonitor.c.
Definition at line 77 of file app_mixmonitor.c.
enum { ... } mixmonitor_args |
enum { ... } mixmonitor_flags |
const char* mixmonitor_spy_type = "MixMonitor" [static] |
Definition at line 79 of file app_mixmonitor.c.
Definition at line 75 of file app_mixmonitor.c.
const char* synopsis = "Record a call and mix the audio during the recording" [static] |
Definition at line 54 of file app_mixmonitor.c.
const char* tdesc = "Mixed Audio Monitoring Application" [static] |
Definition at line 52 of file app_mixmonitor.c.