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: 11778 $")
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 *chan;
00083 char *filename;
00084 char *post_process;
00085 unsigned int flags;
00086 int readvol;
00087 int writevol;
00088 };
00089
00090 enum {
00091 MUXFLAG_APPEND = (1 << 1),
00092 MUXFLAG_BRIDGED = (1 << 2),
00093 MUXFLAG_VOLUME = (1 << 3),
00094 MUXFLAG_READVOLUME = (1 << 4),
00095 MUXFLAG_WRITEVOLUME = (1 << 5),
00096 } mixmonitor_flags;
00097
00098 enum {
00099 OPT_ARG_READVOLUME = 0,
00100 OPT_ARG_WRITEVOLUME,
00101 OPT_ARG_VOLUME,
00102 OPT_ARG_ARRAY_SIZE,
00103 } mixmonitor_args;
00104
00105 AST_APP_OPTIONS(mixmonitor_opts, {
00106 AST_APP_OPTION('a', MUXFLAG_APPEND),
00107 AST_APP_OPTION('b', MUXFLAG_BRIDGED),
00108 AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
00109 AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
00110 AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
00111 });
00112
00113 static void stopmon(struct ast_channel *chan, struct ast_channel_spy *spy)
00114 {
00115
00116
00117 if (spy->status == CHANSPY_DONE)
00118 return;
00119
00120 if (!chan)
00121 return;
00122
00123 ast_mutex_lock(&chan->lock);
00124 ast_channel_spy_remove(chan, spy);
00125 ast_mutex_unlock(&chan->lock);
00126 }
00127
00128 static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy)
00129 {
00130 struct ast_channel *peer;
00131 int res;
00132
00133 if (!chan)
00134 return -1;
00135
00136 ast_mutex_lock(&chan->lock);
00137 res = ast_channel_spy_add(chan, spy);
00138 ast_mutex_unlock(&chan->lock);
00139
00140 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
00141 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00142
00143 return res;
00144 }
00145
00146 #define SAMPLES_PER_FRAME 160
00147
00148 static void *mixmonitor_thread(void *obj)
00149 {
00150 struct mixmonitor *mixmonitor = obj;
00151 struct ast_channel_spy spy;
00152 struct ast_filestream *fs = NULL;
00153 char *ext, *name;
00154 unsigned int oflags;
00155 struct ast_frame *f;
00156 char post_process[1024] = "";
00157
00158 STANDARD_INCREMENT_USECOUNT;
00159
00160 name = ast_strdupa(mixmonitor->chan->name);
00161
00162 oflags = O_CREAT|O_WRONLY;
00163 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00164
00165 if ((ext = strrchr(mixmonitor->filename, '.'))) {
00166 *(ext++) = '\0';
00167 } else {
00168 ext = "raw";
00169 }
00170
00171 fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644);
00172 if (!fs) {
00173 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
00174 goto out;
00175 }
00176
00177 if (ast_test_flag(mixmonitor, MUXFLAG_APPEND))
00178 ast_seekstream(fs, 0, SEEK_END);
00179
00180 memset(&spy, 0, sizeof(spy));
00181 ast_set_flag(&spy, CHANSPY_FORMAT_AUDIO);
00182 ast_set_flag(&spy, CHANSPY_MIXAUDIO);
00183 spy.type = mixmonitor_spy_type;
00184 spy.status = CHANSPY_RUNNING;
00185 spy.read_queue.format = AST_FORMAT_SLINEAR;
00186 spy.write_queue.format = AST_FORMAT_SLINEAR;
00187 if (mixmonitor->readvol) {
00188 ast_set_flag(&spy, CHANSPY_READ_VOLADJUST);
00189 spy.read_vol_adjustment = mixmonitor->readvol;
00190 }
00191 if (mixmonitor->writevol) {
00192 ast_set_flag(&spy, CHANSPY_WRITE_VOLADJUST);
00193 spy.write_vol_adjustment = mixmonitor->writevol;
00194 }
00195 ast_mutex_init(&spy.lock);
00196
00197 if (startmon(mixmonitor->chan, &spy)) {
00198 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00199 spy.type, mixmonitor->chan->name);
00200 goto out2;
00201 }
00202
00203 if (option_verbose > 1)
00204 ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", name);
00205
00206 if (mixmonitor->post_process) {
00207 char *p;
00208
00209 for (p = mixmonitor->post_process; *p ; p++) {
00210 if (*p == '^' && *(p+1) == '{') {
00211 *p = '$';
00212 }
00213 }
00214 pbx_substitute_variables_helper(mixmonitor->chan, mixmonitor->post_process, post_process, sizeof(post_process) - 1);
00215 }
00216
00217 while (1) {
00218 struct ast_frame *next;
00219 int write;
00220
00221 ast_mutex_lock(&spy.lock);
00222
00223 ast_channel_spy_trigger_wait(&spy);
00224
00225 if (ast_check_hangup(mixmonitor->chan) || spy.status != CHANSPY_RUNNING) {
00226 ast_mutex_unlock(&spy.lock);
00227 break;
00228 }
00229
00230 while (1) {
00231 if (!(f = ast_channel_spy_read_frame(&spy, SAMPLES_PER_FRAME)))
00232 break;
00233
00234 write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
00235 ast_bridged_channel(mixmonitor->chan));
00236
00237
00238
00239
00240 for (; f; f = next) {
00241 next = f->next;
00242 if (write)
00243 ast_writestream(fs, f);
00244 ast_frfree(f);
00245 }
00246 }
00247
00248 ast_mutex_unlock(&spy.lock);
00249 }
00250
00251 stopmon(mixmonitor->chan, &spy);
00252
00253 if (option_verbose > 1)
00254 ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", name);
00255
00256 if (!ast_strlen_zero(post_process)) {
00257 if (option_verbose > 2)
00258 ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", post_process);
00259 ast_safe_system(post_process);
00260 }
00261
00262 out2:
00263 ast_mutex_destroy(&spy.lock);
00264
00265 if (fs)
00266 ast_closestream(fs);
00267
00268 out:
00269 free(mixmonitor);
00270
00271 STANDARD_DECREMENT_USECOUNT;
00272
00273 return NULL;
00274 }
00275
00276 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
00277 int readvol, int writevol, const char *post_process)
00278 {
00279 pthread_attr_t attr;
00280 pthread_t thread;
00281 struct mixmonitor *mixmonitor;
00282 int len;
00283
00284 len = sizeof(*mixmonitor) + strlen(filename) + 1;
00285 if (!ast_strlen_zero(post_process))
00286 len += strlen(post_process) + 1;
00287
00288 if (!(mixmonitor = calloc(1, len))) {
00289 ast_log(LOG_ERROR, "Memory Error!\n");
00290 return;
00291 }
00292
00293 mixmonitor->chan = chan;
00294 mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor);
00295 strcpy(mixmonitor->filename, filename);
00296 if (!ast_strlen_zero(post_process)) {
00297 mixmonitor->post_process = mixmonitor->filename + strlen(filename) + 1;
00298 strcpy(mixmonitor->post_process, post_process);
00299 }
00300 mixmonitor->readvol = readvol;
00301 mixmonitor->writevol = writevol;
00302 mixmonitor->flags = flags;
00303
00304 pthread_attr_init(&attr);
00305 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00306 ast_pthread_create(&thread, &attr, mixmonitor_thread, mixmonitor);
00307 pthread_attr_destroy(&attr);
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 }