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: 42054 $")
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 int startmon(struct ast_channel *chan, struct ast_channel_spy *spy)
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 }
00129
00130 #define SAMPLES_PER_FRAME 160
00131
00132 static void *mixmonitor_thread(void *obj)
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
00161
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 }
00193
00194 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
00195 int readvol, int writevol, const char *post_process)
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
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
00224 if (!(mixmonitor = calloc(1, len))) {
00225 ast_log(LOG_ERROR, "Memory Error!\n");
00226 return;
00227 }
00228
00229
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
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
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
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
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 }
00289
00290 static int mixmonitor_exec(struct ast_channel *chan, void *data)
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
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 }
00375
00376 static int mixmonitor_cli(int fd, int argc, char **argv)
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 }
00397
00398
00399 static struct ast_cli_entry cli_mixmonitor = {
00400 { "mixmonitor", NULL, NULL },
00401 mixmonitor_cli,
00402 "Execute a MixMonitor command",
00403 "mixmonitor <start|stop> <chan_name> [<args>]\n"
00404 };
00405
00406
00407 int unload_module(void)
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 }
00418
00419 int load_module(void)
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 }
00428
00429 char *description(void)
00430 {
00431 return (char *) tdesc;
00432 }
00433
00434 int usecount(void)
00435 {
00436 int res;
00437
00438 STANDARD_USECOUNT(res);
00439
00440 return res;
00441 }
00442
00443 char *key()
00444 {
00445 return ASTERISK_GPL_KEY;
00446 }