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
00030
00031
00032
00033
00034
00035
00036 #include "asterisk.h"
00037
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 173595 $")
00039
00040 #include "asterisk/paths.h"
00041 #include "asterisk/file.h"
00042 #include "asterisk/audiohook.h"
00043 #include "asterisk/pbx.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/app.h"
00047 #include "asterisk/channel.h"
00048
00049 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
00050
00051 static const char *app = "MixMonitor";
00052 static const char *synopsis = "Record a call and mix the audio during the recording";
00053 static const char *desc = ""
00054 " MixMonitor(<file>.<ext>[,<options>[,<command>]]):\n"
00055 "Records the audio on the current channel to the specified file.\n"
00056 "If the filename is an absolute path, uses that path, otherwise\n"
00057 "creates the file in the configured monitoring directory from\n"
00058 "asterisk.conf.\n\n"
00059 "Valid options:\n"
00060 " a - Append to the file instead of overwriting it.\n"
00061 " b - Only save audio to the file while the channel is bridged.\n"
00062 " Note: Does not include conferences or sounds played to each bridged\n"
00063 " party.\n"
00064 " v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"
00065 " V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"
00066 " W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
00067 " (range -4 to 4)\n\n"
00068 "<command> will be executed when the recording is over\n"
00069 "Any strings matching ^{X} will be unescaped to ${X}.\n"
00070 "All variables will be evaluated at the time MixMonitor is called.\n"
00071 "The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
00072 "";
00073
00074 static const char *stop_app = "StopMixMonitor";
00075 static const char *stop_synopsis = "Stop recording a call through MixMonitor";
00076 static const char *stop_desc = ""
00077 " StopMixMonitor():\n"
00078 "Stops the audio recording that was started with a call to MixMonitor()\n"
00079 "on the current channel.\n"
00080 "";
00081
00082 struct module_symbols *me;
00083
00084 static const char *mixmonitor_spy_type = "MixMonitor";
00085
00086 struct mixmonitor {
00087 struct ast_audiohook audiohook;
00088 char *filename;
00089 char *post_process;
00090 char *name;
00091 unsigned int flags;
00092 struct mixmonitor_ds *mixmonitor_ds;
00093 };
00094
00095 enum {
00096 MUXFLAG_APPEND = (1 << 1),
00097 MUXFLAG_BRIDGED = (1 << 2),
00098 MUXFLAG_VOLUME = (1 << 3),
00099 MUXFLAG_READVOLUME = (1 << 4),
00100 MUXFLAG_WRITEVOLUME = (1 << 5),
00101 } mixmonitor_flags;
00102
00103 enum {
00104 OPT_ARG_READVOLUME = 0,
00105 OPT_ARG_WRITEVOLUME,
00106 OPT_ARG_VOLUME,
00107 OPT_ARG_ARRAY_SIZE,
00108 } mixmonitor_args;
00109
00110 AST_APP_OPTIONS(mixmonitor_opts, {
00111 AST_APP_OPTION('a', MUXFLAG_APPEND),
00112 AST_APP_OPTION('b', MUXFLAG_BRIDGED),
00113 AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
00114 AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
00115 AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
00116 });
00117
00118
00119
00120
00121
00122 struct mixmonitor_ds {
00123 struct ast_channel *chan;
00124
00125
00126
00127
00128
00129
00130
00131 unsigned int destruction_ok;
00132 ast_cond_t destruction_condition;
00133 ast_mutex_t lock;
00134 };
00135
00136 static void mixmonitor_ds_destroy(void *data)
00137 {
00138 struct mixmonitor_ds *mixmonitor_ds = data;
00139
00140 ast_mutex_lock(&mixmonitor_ds->lock);
00141 mixmonitor_ds->chan = NULL;
00142 mixmonitor_ds->destruction_ok = 1;
00143 ast_cond_signal(&mixmonitor_ds->destruction_condition);
00144 ast_mutex_unlock(&mixmonitor_ds->lock);
00145 }
00146
00147 static void mixmonitor_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00148 {
00149 struct mixmonitor_ds *mixmonitor_ds = data;
00150
00151 ast_mutex_lock(&mixmonitor_ds->lock);
00152 mixmonitor_ds->chan = new_chan;
00153 ast_mutex_unlock(&mixmonitor_ds->lock);
00154 }
00155
00156 static struct ast_datastore_info mixmonitor_ds_info = {
00157 .type = "mixmonitor",
00158 .destroy = mixmonitor_ds_destroy,
00159 .chan_fixup = mixmonitor_ds_chan_fixup,
00160 };
00161
00162 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
00163 {
00164 struct ast_channel *peer = NULL;
00165 int res = 0;
00166
00167 if (!chan)
00168 return -1;
00169
00170 ast_audiohook_attach(chan, audiohook);
00171
00172 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
00173 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00174
00175 return res;
00176 }
00177
00178 #define SAMPLES_PER_FRAME 160
00179
00180 static void *mixmonitor_thread(void *obj)
00181 {
00182 struct mixmonitor *mixmonitor = obj;
00183 struct ast_filestream *fs = NULL;
00184 unsigned int oflags;
00185 char *ext;
00186 int errflag = 0;
00187
00188 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
00189
00190 ast_audiohook_lock(&mixmonitor->audiohook);
00191
00192 while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00193 struct ast_frame *fr = NULL;
00194
00195 ast_audiohook_trigger_wait(&mixmonitor->audiohook);
00196
00197 if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
00198 break;
00199
00200 if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR)))
00201 continue;
00202
00203 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00204 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) {
00205 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00206
00207 if (!fs && !errflag) {
00208 oflags = O_CREAT | O_WRONLY;
00209 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00210
00211 if ((ext = strrchr(mixmonitor->filename, '.')))
00212 *(ext++) = '\0';
00213 else
00214 ext = "raw";
00215
00216 if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) {
00217 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
00218 errflag = 1;
00219 }
00220 }
00221
00222
00223 if (fs)
00224 ast_writestream(fs, fr);
00225 } else {
00226 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00227 }
00228
00229
00230 ast_frame_free(fr, 0);
00231
00232 }
00233
00234 ast_audiohook_detach(&mixmonitor->audiohook);
00235 ast_audiohook_unlock(&mixmonitor->audiohook);
00236 ast_audiohook_destroy(&mixmonitor->audiohook);
00237
00238 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
00239
00240 if (fs)
00241 ast_closestream(fs);
00242
00243 if (mixmonitor->post_process) {
00244 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
00245 ast_safe_system(mixmonitor->post_process);
00246 }
00247
00248 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00249 if (!mixmonitor->mixmonitor_ds->destruction_ok) {
00250 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
00251 }
00252 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00253 ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
00254 ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
00255 ast_free(mixmonitor->mixmonitor_ds);
00256 ast_free(mixmonitor);
00257
00258 return NULL;
00259 }
00260
00261 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
00262 {
00263 struct ast_datastore *datastore = NULL;
00264 struct mixmonitor_ds *mixmonitor_ds;
00265
00266 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
00267 return -1;
00268 }
00269
00270 ast_mutex_init(&mixmonitor_ds->lock);
00271 ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
00272
00273 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) {
00274 ast_free(mixmonitor_ds);
00275 return -1;
00276 }
00277
00278
00279 mixmonitor_ds->chan = chan;
00280 datastore->data = mixmonitor_ds;
00281
00282 ast_channel_lock(chan);
00283 ast_channel_datastore_add(chan, datastore);
00284 ast_channel_unlock(chan);
00285
00286 mixmonitor->mixmonitor_ds = mixmonitor_ds;
00287 return 0;
00288 }
00289
00290 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
00291 int readvol, int writevol, const char *post_process)
00292 {
00293 pthread_t thread;
00294 struct mixmonitor *mixmonitor;
00295 char postprocess2[1024] = "";
00296 size_t len;
00297
00298 len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
00299
00300 postprocess2[0] = 0;
00301
00302 if (!ast_strlen_zero(post_process)) {
00303 char *p1, *p2;
00304
00305 p1 = ast_strdupa(post_process);
00306 for (p2 = p1; *p2 ; p2++) {
00307 if (*p2 == '^' && *(p2+1) == '{') {
00308 *p2 = '$';
00309 }
00310 }
00311 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00312 if (!ast_strlen_zero(postprocess2))
00313 len += strlen(postprocess2) + 1;
00314 }
00315
00316
00317 if (!(mixmonitor = ast_calloc(1, len))) {
00318 return;
00319 }
00320
00321
00322 mixmonitor->flags = flags;
00323 if (setup_mixmonitor_ds(mixmonitor, chan)) {
00324 return;
00325 }
00326 mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
00327 strcpy(mixmonitor->name, chan->name);
00328 if (!ast_strlen_zero(postprocess2)) {
00329 mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
00330 strcpy(mixmonitor->post_process, postprocess2);
00331 }
00332
00333 mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
00334 strcpy(mixmonitor->filename, filename);
00335
00336
00337 if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
00338 ast_free(mixmonitor);
00339 return;
00340 }
00341
00342 ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
00343
00344 if (readvol)
00345 mixmonitor->audiohook.options.read_volume = readvol;
00346 if (writevol)
00347 mixmonitor->audiohook.options.write_volume = writevol;
00348
00349 if (startmon(chan, &mixmonitor->audiohook)) {
00350 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00351 mixmonitor_spy_type, chan->name);
00352 ast_audiohook_destroy(&mixmonitor->audiohook);
00353 ast_free(mixmonitor);
00354 return;
00355 }
00356
00357 ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
00358 }
00359
00360 static int mixmonitor_exec(struct ast_channel *chan, void *data)
00361 {
00362 int x, readvol = 0, writevol = 0;
00363 struct ast_flags flags = {0};
00364 char *parse, *tmp, *slash;
00365 AST_DECLARE_APP_ARGS(args,
00366 AST_APP_ARG(filename);
00367 AST_APP_ARG(options);
00368 AST_APP_ARG(post_process);
00369 );
00370
00371 if (ast_strlen_zero(data)) {
00372 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00373 return -1;
00374 }
00375
00376 parse = ast_strdupa(data);
00377
00378 AST_STANDARD_APP_ARGS(args, parse);
00379
00380 if (ast_strlen_zero(args.filename)) {
00381 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00382 return -1;
00383 }
00384
00385 if (args.options) {
00386 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
00387
00388 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
00389
00390 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
00391 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
00392 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
00393 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
00394 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
00395 } else {
00396 readvol = get_volfactor(x);
00397 }
00398 }
00399
00400 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
00401 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
00402 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
00403 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
00404 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
00405 } else {
00406 writevol = get_volfactor(x);
00407 }
00408 }
00409
00410 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
00411 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
00412 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
00413 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
00414 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
00415 } else {
00416 readvol = writevol = get_volfactor(x);
00417 }
00418 }
00419 }
00420
00421
00422 if (args.filename[0] != '/') {
00423 char *build;
00424
00425 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
00426 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
00427 args.filename = build;
00428 }
00429
00430 tmp = ast_strdupa(args.filename);
00431 if ((slash = strrchr(tmp, '/')))
00432 *slash = '\0';
00433 ast_mkdir(tmp, 0777);
00434
00435 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
00436 launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
00437
00438 return 0;
00439 }
00440
00441 static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
00442 {
00443 ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00444 return 0;
00445 }
00446
00447 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00448 {
00449 struct ast_channel *chan;
00450
00451 switch (cmd) {
00452 case CLI_INIT:
00453 e->command = "mixmonitor [start|stop]";
00454 e->usage =
00455 "Usage: mixmonitor <start|stop> <chan_name> [args]\n"
00456 " The optional arguments are passed to the MixMonitor\n"
00457 " application when the 'start' command is used.\n";
00458 return NULL;
00459 case CLI_GENERATE:
00460 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00461 }
00462
00463 if (a->argc < 3)
00464 return CLI_SHOWUSAGE;
00465
00466 if (!(chan = ast_get_channel_by_name_prefix_locked(a->argv[2], strlen(a->argv[2])))) {
00467 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
00468
00469 return CLI_SUCCESS;
00470 }
00471
00472 if (!strcasecmp(a->argv[1], "start")) {
00473 mixmonitor_exec(chan, a->argv[3]);
00474 ast_channel_unlock(chan);
00475 } else {
00476 ast_channel_unlock(chan);
00477 ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00478 }
00479
00480 return CLI_SUCCESS;
00481 }
00482
00483 static struct ast_cli_entry cli_mixmonitor[] = {
00484 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
00485 };
00486
00487 static int unload_module(void)
00488 {
00489 int res;
00490
00491 ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
00492 res = ast_unregister_application(stop_app);
00493 res |= ast_unregister_application(app);
00494
00495 return res;
00496 }
00497
00498 static int load_module(void)
00499 {
00500 int res;
00501
00502 ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
00503 res = ast_register_application(app, mixmonitor_exec, synopsis, desc);
00504 res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc);
00505
00506 return res;
00507 }
00508
00509 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");