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: 72381 $")
00039
00040 #include <stdlib.h>
00041 #include <stdio.h>
00042 #include <string.h>
00043 #include <unistd.h>
00044
00045 #include "asterisk/file.h"
00046 #include "asterisk/logger.h"
00047 #include "asterisk/channel.h"
00048 #include "asterisk/chanspy.h"
00049 #include "asterisk/pbx.h"
00050 #include "asterisk/module.h"
00051 #include "asterisk/lock.h"
00052 #include "asterisk/cli.h"
00053 #include "asterisk/options.h"
00054 #include "asterisk/app.h"
00055 #include "asterisk/linkedlists.h"
00056 #include "asterisk/utils.h"
00057
00058 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
00059
00060 static const char *app = "MixMonitor";
00061 static const char *synopsis = "Record a call and mix the audio during the recording";
00062 static const char *desc = ""
00063 " MixMonitor(<file>.<ext>[|<options>[|<command>]])\n\n"
00064 "Records the audio on the current channel to the specified file.\n"
00065 "If the filename is an absolute path, uses that path, otherwise\n"
00066 "creates the file in the configured monitoring directory from\n"
00067 "asterisk.conf.\n\n"
00068 "Valid options:\n"
00069 " a - Append to the file instead of overwriting it.\n"
00070 " b - Only save audio to the file while the channel is bridged.\n"
00071 " Note: Does not include conferences or sounds played to each bridged\n"
00072 " party.\n"
00073 " v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"
00074 " V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"
00075 " W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
00076 " (range -4 to 4)\n\n"
00077 "<command> will be executed when the recording is over\n"
00078 "Any strings matching ^{X} will be unescaped to ${X}.\n"
00079 "All variables will be evaluated at the time MixMonitor is called.\n"
00080 "The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
00081 "";
00082
00083 static const char *stop_app = "StopMixMonitor";
00084 static const char *stop_synopsis = "Stop recording a call through MixMonitor";
00085 static const char *stop_desc = ""
00086 " StopMixMonitor()\n\n"
00087 "Stops the audio recording that was started with a call to MixMonitor()\n"
00088 "on the current channel.\n"
00089 "";
00090
00091 struct module_symbols *me;
00092
00093 static const char *mixmonitor_spy_type = "MixMonitor";
00094
00095 struct mixmonitor {
00096 struct ast_channel_spy spy;
00097 char *filename;
00098 char *post_process;
00099 char *name;
00100 unsigned int flags;
00101 };
00102
00103 enum {
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;
00110
00111 enum {
00112 OPT_ARG_READVOLUME = 0,
00113 OPT_ARG_WRITEVOLUME,
00114 OPT_ARG_VOLUME,
00115 OPT_ARG_ARRAY_SIZE,
00116 } mixmonitor_args;
00117
00118 AST_APP_OPTIONS(mixmonitor_opts, {
00119 AST_APP_OPTION('a', MUXFLAG_APPEND),
00120 AST_APP_OPTION('b', MUXFLAG_BRIDGED),
00121 AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
00122 AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
00123 AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
00124 });
00125
00126 static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy)
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 }
00143
00144 #define SAMPLES_PER_FRAME 160
00145
00146 static void *mixmonitor_thread(void *obj)
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
00177
00178
00179 for (; f; f = next) {
00180 next = AST_LIST_NEXT(f, frame_list);
00181 if (write && errflag == 0) {
00182 if (!fs) {
00183
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
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 }
00228
00229 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
00230 int readvol, int writevol, const char *post_process)
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
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
00257 if (!(mixmonitor = calloc(1, len))) {
00258 return;
00259 }
00260
00261
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
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
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 }
00305
00306 static int mixmonitor_exec(struct ast_channel *chan, void *data)
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
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 }
00387
00388 static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
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 }
00402
00403 static int mixmonitor_cli(int fd, int argc, char **argv)
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 }
00424
00425 static char *complete_mixmonitor_cli(const char *line, const char *word, int pos, int state)
00426 {
00427 return ast_complete_channels(line, word, pos, state, 2);
00428 }
00429
00430 static struct ast_cli_entry cli_mixmonitor[] = {
00431 { { "mixmonitor", NULL, NULL },
00432 mixmonitor_cli, "Execute a MixMonitor command.",
00433 "mixmonitor <start|stop> <chan_name> [args]\n\n"
00434 "The optional arguments are passed to the\n"
00435 "MixMonitor application when the 'start' command is used.\n",
00436 complete_mixmonitor_cli },
00437 };
00438
00439 static int unload_module(void)
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 }
00451
00452 static int load_module(void)
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 }
00462
00463 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");