#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/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 "asterisk/utils.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),}) | |
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_channel_spy *spy) |
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 |
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 103 of file app_mixmonitor.c.
00103 { 00104 MUXFLAG_APPEND = (1 << 1), 00105 MUXFLAG_BRIDGED = (1 << 2), 00106 MUXFLAG_VOLUME = (1 << 3), 00107 MUXFLAG_READVOLUME = (1 << 4), 00108 MUXFLAG_WRITEVOLUME = (1 << 5), 00109 } mixmonitor_flags;
anonymous enum |
Definition at line 111 of file app_mixmonitor.c.
00111 { 00112 OPT_ARG_READVOLUME = 0, 00113 OPT_ARG_WRITEVOLUME, 00114 OPT_ARG_VOLUME, 00115 OPT_ARG_ARRAY_SIZE, 00116 } 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 425 of file app_mixmonitor.c.
References ast_complete_channels().
00426 { 00427 return ast_complete_channels(line, word, pos, state, 2); 00428 }
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 229 of file app_mixmonitor.c.
References AST_FORMAT_SLINEAR, ast_log(), ast_mutex_destroy(), ast_mutex_init(), ast_pthread_create_background, ast_set_flag, ast_strdupa, ast_strlen_zero(), calloc, CHANSPY_FORMAT_AUDIO, CHANSPY_MIXAUDIO, CHANSPY_READ_VOLADJUST, CHANSPY_RUNNING, CHANSPY_WRITE_VOLADJUST, mixmonitor::flags, free, len, LOG_WARNING, mixmonitor_thread(), mixmonitor::name, pbx_substitute_variables_helper(), startmon(), and thread.
Referenced by mixmonitor_exec().
00231 { 00232 pthread_attr_t attr; 00233 pthread_t thread; 00234 struct mixmonitor *mixmonitor; 00235 char postprocess2[1024] = ""; 00236 size_t len; 00237 00238 len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2; 00239 00240 /* If a post process system command is given attach it to the structure */ 00241 if (!ast_strlen_zero(post_process)) { 00242 char *p1, *p2; 00243 00244 p1 = ast_strdupa(post_process); 00245 for (p2 = p1; *p2 ; p2++) { 00246 if (*p2 == '^' && *(p2+1) == '{') { 00247 *p2 = '$'; 00248 } 00249 } 00250 00251 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1); 00252 if (!ast_strlen_zero(postprocess2)) 00253 len += strlen(postprocess2) + 1; 00254 } 00255 00256 /* Pre-allocate mixmonitor structure and spy */ 00257 if (!(mixmonitor = calloc(1, len))) { 00258 return; 00259 } 00260 00261 /* Copy over flags and channel name */ 00262 mixmonitor->flags = flags; 00263 mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor); 00264 strcpy(mixmonitor->name, chan->name); 00265 if (!ast_strlen_zero(postprocess2)) { 00266 mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2; 00267 strcpy(mixmonitor->post_process, postprocess2); 00268 } 00269 00270 mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1; 00271 strcpy(mixmonitor->filename, filename); 00272 00273 /* Setup the actual spy before creating our thread */ 00274 ast_set_flag(&mixmonitor->spy, CHANSPY_FORMAT_AUDIO); 00275 ast_set_flag(&mixmonitor->spy, CHANSPY_MIXAUDIO); 00276 mixmonitor->spy.type = mixmonitor_spy_type; 00277 mixmonitor->spy.status = CHANSPY_RUNNING; 00278 mixmonitor->spy.read_queue.format = AST_FORMAT_SLINEAR; 00279 mixmonitor->spy.write_queue.format = AST_FORMAT_SLINEAR; 00280 if (readvol) { 00281 ast_set_flag(&mixmonitor->spy, CHANSPY_READ_VOLADJUST); 00282 mixmonitor->spy.read_vol_adjustment = readvol; 00283 } 00284 if (writevol) { 00285 ast_set_flag(&mixmonitor->spy, CHANSPY_WRITE_VOLADJUST); 00286 mixmonitor->spy.write_vol_adjustment = writevol; 00287 } 00288 ast_mutex_init(&mixmonitor->spy.lock); 00289 00290 if (startmon(chan, &mixmonitor->spy)) { 00291 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n", 00292 mixmonitor->spy.type, chan->name); 00293 /* Since we couldn't add ourselves - bail out! */ 00294 ast_mutex_destroy(&mixmonitor->spy.lock); 00295 free(mixmonitor); 00296 return; 00297 } 00298 00299 pthread_attr_init(&attr); 00300 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 00301 ast_pthread_create_background(&thread, &attr, mixmonitor_thread, mixmonitor); 00302 pthread_attr_destroy(&attr); 00303 00304 }
static int load_module | ( | void | ) | [static] |
Definition at line 452 of file app_mixmonitor.c.
References ast_cli_register_multiple(), ast_register_application(), cli_mixmonitor, mixmonitor_exec(), and stop_mixmonitor_exec().
00453 { 00454 int res; 00455 00456 ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry)); 00457 res = ast_register_application(app, mixmonitor_exec, synopsis, desc); 00458 res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc); 00459 00460 return res; 00461 }
static int mixmonitor_cli | ( | int | fd, | |
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 403 of file app_mixmonitor.c.
References ast_channel_spy_stop_by_type(), ast_channel_unlock, ast_cli(), ast_get_channel_by_name_prefix_locked(), mixmonitor_exec(), RESULT_SHOWUSAGE, and RESULT_SUCCESS.
00404 { 00405 struct ast_channel *chan; 00406 00407 if (argc < 3) 00408 return RESULT_SHOWUSAGE; 00409 00410 if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) { 00411 ast_cli(fd, "No channel matching '%s' found.\n", argv[2]); 00412 return RESULT_SUCCESS; 00413 } 00414 00415 if (!strcasecmp(argv[1], "start")) 00416 mixmonitor_exec(chan, argv[3]); 00417 else if (!strcasecmp(argv[1], "stop")) 00418 ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type); 00419 00420 ast_channel_unlock(chan); 00421 00422 return RESULT_SUCCESS; 00423 }
static int mixmonitor_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 306 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().
00307 { 00308 int x, readvol = 0, writevol = 0; 00309 struct ast_module_user *u; 00310 struct ast_flags flags = {0}; 00311 char *parse; 00312 AST_DECLARE_APP_ARGS(args, 00313 AST_APP_ARG(filename); 00314 AST_APP_ARG(options); 00315 AST_APP_ARG(post_process); 00316 ); 00317 00318 if (ast_strlen_zero(data)) { 00319 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00320 return -1; 00321 } 00322 00323 u = ast_module_user_add(chan); 00324 00325 parse = ast_strdupa(data); 00326 00327 AST_STANDARD_APP_ARGS(args, parse); 00328 00329 if (ast_strlen_zero(args.filename)) { 00330 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00331 ast_module_user_remove(u); 00332 return -1; 00333 } 00334 00335 if (args.options) { 00336 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, }; 00337 00338 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options); 00339 00340 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) { 00341 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) { 00342 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n"); 00343 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00344 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]); 00345 } else { 00346 readvol = get_volfactor(x); 00347 } 00348 } 00349 00350 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) { 00351 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) { 00352 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n"); 00353 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00354 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]); 00355 } else { 00356 writevol = get_volfactor(x); 00357 } 00358 } 00359 00360 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) { 00361 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) { 00362 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n"); 00363 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00364 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]); 00365 } else { 00366 readvol = writevol = get_volfactor(x); 00367 } 00368 } 00369 } 00370 00371 /* if not provided an absolute path, use the system-configured monitoring directory */ 00372 if (args.filename[0] != '/') { 00373 char *build; 00374 00375 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3); 00376 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename); 00377 args.filename = build; 00378 } 00379 00380 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename); 00381 launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process); 00382 00383 ast_module_user_remove(u); 00384 00385 return 0; 00386 }
static void* mixmonitor_thread | ( | void * | obj | ) | [static] |
Definition at line 146 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_frame_free(), AST_LIST_NEXT, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_safe_system(), ast_test_flag, ast_verbose(), ast_writefile(), ast_writestream(), ast_channel_spy::chan, CHANSPY_RUNNING, ext, f, mixmonitor::filename, free, ast_channel_spy::lock, LOG_ERROR, MUXFLAG_APPEND, MUXFLAG_BRIDGED, mixmonitor::name, option_verbose, mixmonitor::post_process, SAMPLES_PER_FRAME, mixmonitor::spy, ast_channel_spy::status, and VERBOSE_PREFIX_2.
Referenced by launch_monitor_thread().
00147 { 00148 struct mixmonitor *mixmonitor = obj; 00149 struct ast_frame *f = NULL; 00150 struct ast_filestream *fs = NULL; 00151 unsigned int oflags; 00152 char *ext; 00153 int errflag = 0; 00154 00155 if (option_verbose > 1) 00156 ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name); 00157 00158 ast_mutex_lock(&mixmonitor->spy.lock); 00159 00160 while (mixmonitor->spy.chan) { 00161 struct ast_frame *next; 00162 int write; 00163 00164 ast_channel_spy_trigger_wait(&mixmonitor->spy); 00165 00166 if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING) 00167 break; 00168 00169 while (1) { 00170 if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME))) 00171 break; 00172 00173 write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || 00174 ast_bridged_channel(mixmonitor->spy.chan)); 00175 00176 /* it is possible for ast_channel_spy_read_frame() to return a chain 00177 of frames if a queue flush was necessary, so process them 00178 */ 00179 for (; f; f = next) { 00180 next = AST_LIST_NEXT(f, frame_list); 00181 if (write && errflag == 0) { 00182 if (!fs) { 00183 /* Determine creation flags and filename plus extension for filestream */ 00184 oflags = O_CREAT | O_WRONLY; 00185 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC; 00186 00187 if ((ext = strrchr(mixmonitor->filename, '.'))) 00188 *(ext++) = '\0'; 00189 else 00190 ext = "raw"; 00191 00192 /* Move onto actually creating the filestream */ 00193 if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) { 00194 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext); 00195 errflag = 1; 00196 } 00197 00198 } 00199 if (fs) 00200 ast_writestream(fs, f); 00201 } 00202 ast_frame_free(f, 0); 00203 } 00204 } 00205 } 00206 00207 ast_mutex_unlock(&mixmonitor->spy.lock); 00208 00209 ast_channel_spy_free(&mixmonitor->spy); 00210 00211 if (option_verbose > 1) 00212 ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name); 00213 00214 if (mixmonitor->post_process) { 00215 if (option_verbose > 2) 00216 ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process); 00217 ast_safe_system(mixmonitor->post_process); 00218 } 00219 00220 if (fs) 00221 ast_closestream(fs); 00222 00223 free(mixmonitor); 00224 00225 00226 return NULL; 00227 }
static int startmon | ( | struct ast_channel * | chan, | |
struct ast_channel_spy * | spy | |||
) | [static] |
Definition at line 126 of file app_mixmonitor.c.
References ast_bridged_channel(), ast_channel_lock, ast_channel_spy_add(), ast_channel_unlock, AST_FLAG_NBRIDGE, ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, and ast_test_flag.
Referenced by launch_monitor_thread().
00127 { 00128 struct ast_channel *peer; 00129 int res; 00130 00131 if (!chan) 00132 return -1; 00133 00134 ast_channel_lock(chan); 00135 res = ast_channel_spy_add(chan, spy); 00136 ast_channel_unlock(chan); 00137 00138 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) 00139 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00140 00141 return res; 00142 }
static int stop_mixmonitor_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 388 of file app_mixmonitor.c.
References ast_channel_lock, ast_channel_spy_stop_by_type(), ast_channel_unlock, ast_module_user_add, ast_module_user_remove, and ast_module_user::chan.
Referenced by load_module().
00389 { 00390 struct ast_module_user *u; 00391 00392 u = ast_module_user_add(chan); 00393 00394 ast_channel_lock(chan); 00395 ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type); 00396 ast_channel_unlock(chan); 00397 00398 ast_module_user_remove(u); 00399 00400 return 0; 00401 }
static int unload_module | ( | void | ) | [static] |
Definition at line 439 of file app_mixmonitor.c.
References ast_cli_unregister_multiple(), ast_module_user_hangup_all, ast_unregister_application(), and cli_mixmonitor.
00440 { 00441 int res; 00442 00443 ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry)); 00444 res = ast_unregister_application(stop_app); 00445 res |= ast_unregister_application(app); 00446 00447 ast_module_user_hangup_all(); 00448 00449 return res; 00450 }
const char* app = "MixMonitor" [static] |
Definition at line 60 of file app_mixmonitor.c.
struct ast_cli_entry cli_mixmonitor[] [static] |
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.