Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:48 2009

## Asterisk developer's documentation

# app_mixmonitor.c

Go to the documentation of this file.

    00001 /*
    00002  * Asterisk -- An open source telephony toolkit.
    00003  *
    00004  * Copyright (C) 2005, Anthony Minessale II
    00005  * Copyright (C) 2005, Digium, Inc.
    00006  *
    00007  * Mark Spencer <markster@digium.com>
    00008  * Kevin P. Fleming <kpfleming@digium.com>
    00009  *
    00010  * Based on app_muxmon.c provided by
    00011  * Anthony Minessale II <anthmct@yahoo.com>
    00012  *
    00013  * See http://www.asterisk.org for more information about
    00014  * the Asterisk project. Please do not directly contact
    00015  * any of the maintainers of this project for assistance;
    00016  * the project provides a web site, mailing lists and IRC
    00017  * channels for your use.
    00018  *
    00019  * This program is free software, distributed under the terms of
    00020  * the GNU General Public License Version 2. See the LICENSE file
    00021  * at the top of the source tree.
    00022  */
    00023
    00024 /*! \file
    00025  * \brief MixMonitor() - Record a call and mix the audio during the recording
    00026  * \ingroup applications
    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: 211526 $")
    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 or sounds played to each bridged\n"
    00065 "                party.\n"
    00066 " v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"
    00067 " V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"
    00068 " W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
    00069 "         (range -4 to 4)\n\n"
    00070 "<command> will be executed when the recording is over\n"
    00071 "Any strings matching ^{X} will be unescaped to ${X}.\n"
    00072 "All variables will be evaluated at the time MixMonitor is called.\n"
    00073 "The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
    00074 "";
    00075
    00076 STANDARD_LOCAL_USER;
    00077
    00078 LOCAL_USER_DECL;
    00079
    00080 static const char *mixmonitor_spy_type = "MixMonitor";
    00081
    00082 struct mixmonitor {
    00083    struct ast_channel_spy spy;
    00084    char *filename;
    00085    char *post_process;
    00086    char *name;
    00087    unsigned int flags;
    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 int startmon(struct ast_channel *chan, struct ast_channel_spy *spy)
    00114 {
    00115    struct ast_channel *peer;
    00116    int res;
    00117
    00118    if (!chan)
    00119       return -1;
    00120
    00121    ast_mutex_lock(&chan->lock);
    00122    res = ast_channel_spy_add(chan, spy);
    00123    ast_mutex_unlock(&chan->lock);
    00124
    00125    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
    00126       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
    00127
    00128    return res;
    00129 }
    00130
    00131 #define SAMPLES_PER_FRAME 160
    00132
    00133 static void *mixmonitor_thread(void *obj)
    00134 {
    00135    struct mixmonitor *mixmonitor = obj;
    00136    struct ast_frame *f = NULL;
    00137    struct ast_filestream *fs = NULL;
    00138    unsigned int oflags;
    00139    char *ext;
    00140    int errflag = 0;
    00141
    00142
    00143    STANDARD_INCREMENT_USECOUNT;
    00144
    00145    if (option_verbose > 1)
    00146       ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name);
    00147
    00148    ast_mutex_lock(&mixmonitor->spy.lock);
    00149
    00150    while (mixmonitor->spy.chan) {
    00151       struct ast_frame *next;
    00152       int write;
    00153
    00154       ast_channel_spy_trigger_wait(&mixmonitor->spy);
    00155
    00156       if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING)
    00157          break;
    00158
    00159       while (1) {
    00160          if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME)))
    00161             break;
    00162
    00163          write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
    00164              ast_bridged_channel(mixmonitor->spy.chan));
    00165
    00166          /* it is possible for ast_channel_spy_read_frame() to return a chain
    00167             of frames if a queue flush was necessary, so process them
    00168          */
    00169          for (; f; f = next) {
    00170             next = f->next;
    00171             if (write && errflag == 0) {
    00172                if (!fs) {
    00173                   /* Determine creation flags and filename plus extension for filestream */
    00174                   oflags = O_CREAT | O_WRONLY;
    00175                   oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
    00176
    00177                   if ((ext = strrchr(mixmonitor->filename, '.')))
    00178                      *(ext++) = '\0';
    00179                   else
    00180                      ext = "raw";
    00181
    00182                   /* Move onto actually creating the filestream */
    00183                   if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) {
    00184                      ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
    00185                      errflag = 1;
    00186                   }
    00187
    00188                }
    00189                if (fs)
    00190                   ast_writestream(fs, f);
    00191             }
    00192             ast_frfree(f);
    00193          }
    00194       }
    00195    }
    00196
    00197    ast_mutex_unlock(&mixmonitor->spy.lock);
    00198
    00199    ast_channel_spy_free(&mixmonitor->spy);
    00200
    00201    if (option_verbose > 1)
    00202       ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name);
    00203
    00204    if (mixmonitor->post_process) {
    00205       if (option_verbose > 2)
    00206          ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process);
    00207       ast_safe_system(mixmonitor->post_process);
    00208    }
    00209
    00210    if (fs)
    00211       ast_closestream(fs);
    00212
    00213    free(mixmonitor);
    00214
    00215    STANDARD_DECREMENT_USECOUNT;
    00216
    00217    return NULL;
    00218 }
    00219
    00220 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
    00221               int readvol, int writevol, const char *post_process)
    00222 {
    00223    pthread_attr_t attr;
    00224    pthread_t thread;
    00225    struct mixmonitor *mixmonitor;
    00226    char postprocess2[1024] = "";
    00227    size_t len;
    00228
    00229    len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
    00230
    00231    /* If a post process system command is given attach it to the structure */
    00232    if (!ast_strlen_zero(post_process)) {
    00233       char *p1, *p2;
    00234
    00235       p1 = ast_strdupa(post_process);
    00236       for (p2 = p1; *p2 ; p2++) {
    00237          if (*p2 == '^' && *(p2+1) == '{') {
    00238             *p2 = '$';
    00239          }
    00240       }
    00241
    00242       pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
    00243       if (!ast_strlen_zero(postprocess2))
    00244          len += strlen(postprocess2) + 1;
    00245    }
    00246
    00247    /* Pre-allocate mixmonitor structure and spy */
    00248    if (!(mixmonitor = calloc(1, len))) {
    00249       ast_log(LOG_ERROR, "Memory Error!\n");
    00250       return;
    00251    }
    00252
    00253    /* Copy over flags and channel name */
    00254    mixmonitor->flags = flags;
    00255    mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
    00256    strcpy(mixmonitor->name, chan->name);
    00257    if (!ast_strlen_zero(postprocess2)) {
    00258       mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
    00259       strcpy(mixmonitor->post_process, postprocess2);
    00260    }
    00261
    00262    mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
    00263    strcpy(mixmonitor->filename, filename);
    00264
    00265    /* Setup the actual spy before creating our thread */
    00266    ast_set_flag(&mixmonitor->spy, CHANSPY_FORMAT_AUDIO);
    00267    ast_set_flag(&mixmonitor->spy, CHANSPY_MIXAUDIO);
    00268    mixmonitor->spy.type = mixmonitor_spy_type;
    00269    mixmonitor->spy.status = CHANSPY_RUNNING;
    00270    mixmonitor->spy.read_queue.format = AST_FORMAT_SLINEAR;
    00271    mixmonitor->spy.write_queue.format = AST_FORMAT_SLINEAR;
    00272    if (readvol) {
    00273       ast_set_flag(&mixmonitor->spy, CHANSPY_READ_VOLADJUST);
    00274       mixmonitor->spy.read_vol_adjustment = readvol;
    00275    }
    00276    if (writevol) {
    00277       ast_set_flag(&mixmonitor->spy, CHANSPY_WRITE_VOLADJUST);
    00278       mixmonitor->spy.write_vol_adjustment = writevol;
    00279    }
    00280    ast_mutex_init(&mixmonitor->spy.lock);
    00281
    00282    if (startmon(chan, &mixmonitor->spy)) {
    00283       ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
    00284          mixmonitor->spy.type, chan->name);
    00285       /* Since we couldn't add ourselves - bail out! */
    00286       ast_mutex_destroy(&mixmonitor->spy.lock);
    00287       free(mixmonitor);
    00288       return;
    00289    }
    00290
    00291    pthread_attr_init(&attr);
    00292    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    00293    ast_pthread_create(&thread, &attr, mixmonitor_thread, mixmonitor);
    00294    pthread_attr_destroy(&attr);
    00295
    00296 }
    00297
    00298 static int mixmonitor_exec(struct ast_channel *chan, void *data)
    00299 {
    00300    int x, readvol = 0, writevol = 0;
    00301    struct localuser *u;
    00302    struct ast_flags flags = {0};
    00303    char *parse;
    00304    AST_DECLARE_APP_ARGS(args,
    00305       AST_APP_ARG(filename);
    00306       AST_APP_ARG(options);
    00307       AST_APP_ARG(post_process);
    00308    );
    00309
    00310    if (ast_strlen_zero(data)) {
    00311       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
    00312       return -1;
    00313    }
    00314
    00315    LOCAL_USER_ADD(u);
    00316
    00317    if (!(parse = ast_strdupa(data))) {
    00318       ast_log(LOG_WARNING, "Memory Error!\n");
    00319       LOCAL_USER_REMOVE(u);
    00320       return -1;
    00321    }
    00322
    00323    AST_STANDARD_APP_ARGS(args, parse);
    00324
    00325    if (ast_strlen_zero(args.filename)) {
    00326       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
    00327       LOCAL_USER_REMOVE(u);
    00328       return -1;
    00329    }
    00330
    00331    if (args.options) {
    00332       char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
    00333
    00334       ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
    00335
    00336       if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
    00337          if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
    00338             ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
    00339          } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
    00340             ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
    00341          } else {
    00342             readvol = get_volfactor(x);
    00343          }
    00344       }
    00345
    00346       if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
    00347          if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
    00348             ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
    00349          } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
    00350             ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
    00351          } else {
    00352             writevol = get_volfactor(x);
    00353          }
    00354       }
    00355
    00356       if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
    00357          if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
    00358             ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
    00359          } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
    00360             ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
    00361          } else {
    00362             readvol = writevol = get_volfactor(x);
    00363          }
    00364       }
    00365    }
    00366
    00367    /* if not provided an absolute path, use the system-configured monitoring directory */
    00368    if (args.filename[0] != '/') {
    00369       char *build;
    00370
    00371       build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
    00372       sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
    00373       args.filename = build;
    00374    }
    00375
    00376    pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
    00377    launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
    00378
    00379    LOCAL_USER_REMOVE(u);
    00380
    00381    return 0;
    00382 }
    00383
    00384 static int mixmonitor_cli(int fd, int argc, char **argv)
    00385 {
    00386    struct ast_channel *chan;
    00387
    00388    if (argc < 3)
    00389       return RESULT_SHOWUSAGE;
    00390
    00391    if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
    00392       ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
    00393       return RESULT_SUCCESS;
    00394    }
    00395
    00396    if (!strcasecmp(argv[1], "start"))
    00397       mixmonitor_exec(chan, argv[3]);
    00398    else if (!strcasecmp(argv[1], "stop"))
    00399       ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
    00400
    00401    ast_mutex_unlock(&chan->lock);
    00402
    00403    return RESULT_SUCCESS;
    00404 }
    00405
    00406
    00407 static struct ast_cli_entry cli_mixmonitor = {
    00408    { "mixmonitor", NULL, NULL },
    00409    mixmonitor_cli,
    00410    "Execute a MixMonitor command",
    00411    "mixmonitor <start|stop> <chan_name> [<args>]\n"
    00412 };
    00413
    00414
    00415 int unload_module(void)
    00416 {
    00417    int res;
    00418
    00419    res = ast_cli_unregister(&cli_mixmonitor);
    00420    res |= ast_unregister_application(app);
    00421
    00422    STANDARD_HANGUP_LOCALUSERS;
    00423
    00424    return res;
    00425 }
    00426
    00427 int load_module(void)
    00428 {
    00429    int res;
    00430
    00431    res = ast_cli_register(&cli_mixmonitor);
    00432    res |= ast_register_application(app, mixmonitor_exec, synopsis, desc);
    00433
    00434    return res;
    00435 }
    00436
    00437 char *description(void)
    00438 {
    00439    return (char *) tdesc;
    00440 }
    00441
    00442 int usecount(void)
    00443 {
    00444    int res;
    00445
    00446    STANDARD_USECOUNT(res);
    00447
    00448    return res;
    00449 }
    00450
    00451 char *key()
    00452 {
    00453    return ASTERISK_GPL_KEY;
    00454 }