00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include <stdlib.h>
00030 #include <stdio.h>
00031 #include <string.h>
00032 #include <unistd.h>
00033
00034 #include "asterisk.h"
00035
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 33841 $")
00037
00038 #include "asterisk/file.h"
00039 #include "asterisk/logger.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/chanspy.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/options.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/linkedlists.h"
00049
00050 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
00051
00052 static const char *tdesc = "Mixed Audio Monitoring Application";
00053 static const char *app = "MixMonitor";
00054 static const char *synopsis = "Record a call and mix the audio during the recording";
00055 static const char *desc = ""
00056 " MixMonitor(<file>.<ext>[|<options>[|<command>]])\n\n"
00057 "Records the audio on the current channel to the specified file.\n"
00058 "If the filename is an absolute path, uses that path, otherwise\n"
00059 "creates the file in the configured monitoring directory from\n"
00060 "asterisk.conf.\n\n"
00061 "Valid options:\n"
00062 " a - Append to the file instead of overwriting it.\n"
00063 " b - Only save audio to the file while the channel is bridged.\n"
00064 " Note: does not include conferences.\n"
00065 " v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"
00066 " V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"
00067 " W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
00068 " (range -4 to 4)\n\n"
00069 "<command> will be executed when the recording is over\n"
00070 "Any strings matching ^{X} will be unescaped to ${X} and \n"
00071 "all variables will be evaluated at that time.\n"
00072 "The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
00073 "";
00074
00075 STANDARD_LOCAL_USER;
00076
00077 LOCAL_USER_DECL;
00078
00079 static const char *mixmonitor_spy_type = "MixMonitor";
00080
00081 struct mixmonitor {
00082 struct ast_channel_spy spy;
00083 struct ast_filestream *fs;
00084 char *post_process;
00085 char *name;
00086 unsigned int flags;
00087 };
00088
00089 enum {
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;
00096
00097 enum {
00098 OPT_ARG_READVOLUME = 0,
00099 OPT_ARG_WRITEVOLUME,
00100 OPT_ARG_VOLUME,
00101 OPT_ARG_ARRAY_SIZE,
00102 } mixmonitor_args;
00103
00104 AST_APP_OPTIONS(mixmonitor_opts, {
00105 AST_APP_OPTION('a', MUXFLAG_APPEND),
00106 AST_APP_OPTION('b', MUXFLAG_BRIDGED),
00107 AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
00108 AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
00109 AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
00110 });
00111
00112 static void stopmon(struct ast_channel_spy *spy)
00113 {
00114 struct ast_channel *chan = spy->chan;
00115
00116
00117
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 }
00128
00129 static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy)
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 }
00146
00147 #define SAMPLES_PER_FRAME 160
00148
00149 static void *mixmonitor_thread(void *obj)
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
00179
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 }
00213
00214 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
00215 int readvol, int writevol, const char *post_process)
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
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
00244 if (!(mixmonitor = calloc(1, len))) {
00245 ast_log(LOG_ERROR, "Memory Error!\n");
00246 return;
00247 }
00248
00249
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
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
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
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
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 }
00309
00310 static int mixmonitor_exec(struct ast_channel *chan, void *data)
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
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 }
00395
00396 static int mixmonitor_cli(int fd, int argc, char **argv)
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 }
00417
00418
00419 static struct ast_cli_entry cli_mixmonitor = {
00420 { "mixmonitor", NULL, NULL },
00421 mixmonitor_cli,
00422 "Execute a MixMonitor command",
00423 "mixmonitor <start|stop> <chan_name> [<args>]\n"
00424 };
00425
00426
00427 int unload_module(void)
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 }
00438
00439 int load_module(void)
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 }
00448
00449 char *description(void)
00450 {
00451 return (char *) tdesc;
00452 }
00453
00454 int usecount(void)
00455 {
00456 int res;
00457
00458 STANDARD_USECOUNT(res);
00459
00460 return res;
00461 }
00462
00463 char *key()
00464 {
00465 return ASTERISK_GPL_KEY;
00466 }