Wed Aug 18 22:33:41 2010

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 - 2006, 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  *
00026  * \brief MixMonitor() - Record a call and mix the audio during the recording
00027  * \ingroup applications
00028  *
00029  * \author Mark Spencer <markster@digium.com>
00030  * \author Kevin P. Fleming <kpfleming@digium.com>
00031  *
00032  * \note Based on app_muxmon.c provided by
00033  * Anthony Minessale II <anthmct@yahoo.com>
00034  */
00035 
00036 #include "asterisk.h"
00037 
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 257739 $")
00039 
00040 #include "asterisk/paths.h"   /* use ast_config_AST_MONITOR_DIR */
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.  Use of StopMixMonitor is required to guarantee\n"
00059 "the audio file is available for processing during dialplan execution.\n\n"
00060 "Valid options:\n"
00061 " a      - Append to the file instead of overwriting it.\n"
00062 " b      - Only save audio to the file while the channel is bridged.\n"
00063 "          Note: Does not include conferences or sounds played to each bridged\n"
00064 "                party.\n"
00065 "          Note: If you utilize this option inside a Local channel, you must\n"
00066 "                 make sure the Local channel is not optimized away. To do this,\n"
00067 "                 be sure to call your Local channel with the '/n' option.\n"
00068 "                 For example: Dial(Local/start@mycontext/n)\n"
00069 " v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"   
00070 " V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"  
00071 " W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
00072 "         (range -4 to 4)\n\n"   
00073 "<command> will be executed when the recording is over\n"
00074 "Any strings matching ^{X} will be unescaped to ${X}.\n"
00075 "All variables will be evaluated at the time MixMonitor is called.\n"
00076 "The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
00077 "";
00078 
00079 static const char *stop_app = "StopMixMonitor";
00080 static const char *stop_synopsis = "Stop recording a call through MixMonitor";
00081 static const char *stop_desc = ""
00082 "  StopMixMonitor():\n"
00083 "Stop recording a call through MixMonitor, and free the recording's file handle.\n"
00084 "";
00085 
00086 struct module_symbols *me;
00087 
00088 static const char *mixmonitor_spy_type = "MixMonitor";
00089 
00090 struct mixmonitor {
00091    struct ast_audiohook audiohook;
00092    char *filename;
00093    char *post_process;
00094    char *name;
00095    unsigned int flags;
00096    struct mixmonitor_ds *mixmonitor_ds;
00097 };
00098 
00099 enum {
00100    MUXFLAG_APPEND = (1 << 1),
00101    MUXFLAG_BRIDGED = (1 << 2),
00102    MUXFLAG_VOLUME = (1 << 3),
00103    MUXFLAG_READVOLUME = (1 << 4),
00104    MUXFLAG_WRITEVOLUME = (1 << 5),
00105 } mixmonitor_flags;
00106 
00107 enum {
00108    OPT_ARG_READVOLUME = 0,
00109    OPT_ARG_WRITEVOLUME,
00110    OPT_ARG_VOLUME,
00111    OPT_ARG_ARRAY_SIZE,
00112 } mixmonitor_args;
00113 
00114 AST_APP_OPTIONS(mixmonitor_opts, {
00115    AST_APP_OPTION('a', MUXFLAG_APPEND),
00116    AST_APP_OPTION('b', MUXFLAG_BRIDGED),
00117    AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
00118    AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
00119    AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
00120 });
00121 
00122 /* This structure is used as a means of making sure that our pointer to
00123  * the channel we are monitoring remains valid. This is very similar to 
00124  * what is used in app_chanspy.c.
00125  */
00126 struct mixmonitor_ds {
00127    struct ast_channel *chan;
00128    /* These condition variables are used to be sure that the channel
00129     * hangup code completes before the mixmonitor thread attempts to
00130     * free this structure. The combination of a bookean flag and a
00131     * ast_cond_t ensure that no matter what order the threads run in,
00132     * we are guaranteed to never have the waiting thread block forever
00133     * in the case that the signaling thread runs first.
00134     */
00135    unsigned int destruction_ok;
00136    ast_cond_t destruction_condition;
00137    ast_mutex_t lock;
00138 
00139    /* The filestream is held in the datastore so it can be stopped
00140     * immediately during stop_mixmonitor or channel destruction. */
00141    int fs_quit;
00142    struct ast_filestream *fs;
00143    struct ast_audiohook *audiohook;
00144 };
00145 
00146  /*!
00147   * \internal
00148   * \pre mixmonitor_ds must be locked before calling this function
00149   */
00150 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
00151 {
00152    if (mixmonitor_ds->fs) {
00153       ast_closestream(mixmonitor_ds->fs);
00154       mixmonitor_ds->fs = NULL;
00155       mixmonitor_ds->fs_quit = 1;
00156       ast_verb(2, "MixMonitor close filestream\n");
00157    }
00158 }
00159 
00160 static void mixmonitor_ds_destroy(void *data)
00161 {
00162    struct mixmonitor_ds *mixmonitor_ds = data;
00163 
00164    ast_mutex_lock(&mixmonitor_ds->lock);
00165    mixmonitor_ds->chan = NULL;
00166    mixmonitor_ds->audiohook = NULL;
00167    mixmonitor_ds->destruction_ok = 1;
00168    ast_cond_signal(&mixmonitor_ds->destruction_condition);
00169    ast_mutex_unlock(&mixmonitor_ds->lock);
00170 }
00171 
00172 static void mixmonitor_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00173 {
00174    struct mixmonitor_ds *mixmonitor_ds = data;
00175 
00176    ast_mutex_lock(&mixmonitor_ds->lock);
00177    mixmonitor_ds->chan = new_chan;
00178    ast_mutex_unlock(&mixmonitor_ds->lock);
00179 }
00180 
00181 static struct ast_datastore_info mixmonitor_ds_info = {
00182    .type = "mixmonitor",
00183    .destroy = mixmonitor_ds_destroy,
00184    .chan_fixup = mixmonitor_ds_chan_fixup,
00185 };
00186 
00187 static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
00188 {
00189    if (mixmonitor->mixmonitor_ds) {
00190       ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00191       mixmonitor->mixmonitor_ds->audiohook = NULL;
00192       ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00193    }
00194    /* kill the audiohook.*/
00195    ast_audiohook_lock(&mixmonitor->audiohook);
00196    ast_audiohook_detach(&mixmonitor->audiohook);
00197    ast_audiohook_unlock(&mixmonitor->audiohook);
00198    ast_audiohook_destroy(&mixmonitor->audiohook);
00199 }
00200 
00201 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook) 
00202 {
00203    struct ast_channel *peer = NULL;
00204    int res = 0;
00205 
00206    if (!chan)
00207       return -1;
00208 
00209    ast_audiohook_attach(chan, audiohook);
00210 
00211    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
00212       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
00213 
00214    return res;
00215 }
00216 
00217 #define SAMPLES_PER_FRAME 160
00218 
00219 static void mixmonitor_free(struct mixmonitor *mixmonitor)
00220 {
00221    if (mixmonitor) {
00222       if (mixmonitor->mixmonitor_ds) {
00223          ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
00224          ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
00225          ast_free(mixmonitor->mixmonitor_ds);
00226       }
00227       ast_free(mixmonitor);
00228    }
00229 }
00230 
00231 static void *mixmonitor_thread(void *obj) 
00232 {
00233    struct mixmonitor *mixmonitor = obj;
00234    struct ast_filestream **fs = NULL;
00235    unsigned int oflags;
00236    char *ext;
00237    int errflag = 0;
00238 
00239    ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
00240 
00241    fs = &mixmonitor->mixmonitor_ds->fs;
00242 
00243    /* The audiohook must enter and exit the loop locked */
00244    ast_audiohook_lock(&mixmonitor->audiohook);
00245    while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
00246       struct ast_frame *fr = NULL;
00247 
00248       if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) {
00249          ast_audiohook_trigger_wait(&mixmonitor->audiohook);
00250 
00251          if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00252             break;
00253          }
00254          continue;
00255       }
00256 
00257       /* audiohook lock is not required for the next block.
00258        * Unlock it, but remember to lock it before looping or exiting */
00259       ast_audiohook_unlock(&mixmonitor->audiohook);
00260 
00261       ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00262       if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) {
00263          /* Initialize the file if not already done so */
00264          if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
00265             oflags = O_CREAT | O_WRONLY;
00266             oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00267 
00268             if ((ext = strrchr(mixmonitor->filename, '.')))
00269                *(ext++) = '\0';
00270             else
00271                ext = "raw";
00272 
00273             if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) {
00274                ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
00275                errflag = 1;
00276             }
00277          }
00278 
00279          /* Write out the frame(s) */
00280          if (*fs) {
00281             struct ast_frame *cur;
00282 
00283             for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00284                ast_writestream(*fs, cur);
00285             }
00286          }
00287       }
00288       ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00289 
00290       /* All done! free it. */
00291       ast_frame_free(fr, 0);
00292       ast_audiohook_lock(&mixmonitor->audiohook);
00293    }
00294 
00295    ast_audiohook_unlock(&mixmonitor->audiohook);
00296 
00297    /* Datastore cleanup.  close the filestream and wait for ds destruction */
00298    ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00299    mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
00300    if (!mixmonitor->mixmonitor_ds->destruction_ok) {
00301       ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
00302    }
00303    ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00304 
00305    /* kill the audiohook */
00306    destroy_monitor_audiohook(mixmonitor);
00307 
00308    if (mixmonitor->post_process) {
00309       ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
00310       ast_safe_system(mixmonitor->post_process);
00311    }
00312 
00313    ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
00314    mixmonitor_free(mixmonitor);
00315    return NULL;
00316 }
00317 
00318 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
00319 {
00320    struct ast_datastore *datastore = NULL;
00321    struct mixmonitor_ds *mixmonitor_ds;
00322 
00323    if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
00324       return -1;
00325    }
00326 
00327    ast_mutex_init(&mixmonitor_ds->lock);
00328    ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
00329 
00330    if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) {
00331       ast_mutex_destroy(&mixmonitor_ds->lock);
00332       ast_cond_destroy(&mixmonitor_ds->destruction_condition);
00333       ast_free(mixmonitor_ds);
00334       return -1;
00335    }
00336 
00337    /* No need to lock mixmonitor_ds since this is still operating in the channel's thread */
00338    mixmonitor_ds->chan = chan;
00339    mixmonitor_ds->audiohook = &mixmonitor->audiohook;
00340    datastore->data = mixmonitor_ds;
00341 
00342    ast_channel_lock(chan);
00343    ast_channel_datastore_add(chan, datastore);
00344    ast_channel_unlock(chan);
00345 
00346    mixmonitor->mixmonitor_ds = mixmonitor_ds;
00347    return 0;
00348 }
00349 
00350 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
00351               int readvol, int writevol, const char *post_process) 
00352 {
00353    pthread_t thread;
00354    struct mixmonitor *mixmonitor;
00355    char postprocess2[1024] = "";
00356    size_t len;
00357 
00358    len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
00359 
00360    postprocess2[0] = 0;
00361    /* If a post process system command is given attach it to the structure */
00362    if (!ast_strlen_zero(post_process)) {
00363       char *p1, *p2;
00364 
00365       p1 = ast_strdupa(post_process);
00366       for (p2 = p1; *p2 ; p2++) {
00367          if (*p2 == '^' && *(p2+1) == '{') {
00368             *p2 = '$';
00369          }
00370       }
00371       pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00372       if (!ast_strlen_zero(postprocess2))
00373          len += strlen(postprocess2) + 1;
00374    }
00375 
00376    /* Pre-allocate mixmonitor structure and spy */
00377    if (!(mixmonitor = ast_calloc(1, len))) {
00378       return;
00379    }
00380 
00381    /* Setup the actual spy before creating our thread */
00382    if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
00383       mixmonitor_free(mixmonitor);
00384       return;
00385    }
00386 
00387    /* Copy over flags and channel name */
00388    mixmonitor->flags = flags;
00389    if (setup_mixmonitor_ds(mixmonitor, chan)) {
00390       mixmonitor_free(mixmonitor);
00391       return;
00392    }
00393    mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
00394    strcpy(mixmonitor->name, chan->name);
00395    if (!ast_strlen_zero(postprocess2)) {
00396       mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
00397       strcpy(mixmonitor->post_process, postprocess2);
00398    }
00399 
00400    mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
00401    strcpy(mixmonitor->filename, filename);
00402 
00403    ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
00404 
00405    if (readvol)
00406       mixmonitor->audiohook.options.read_volume = readvol;
00407    if (writevol)
00408       mixmonitor->audiohook.options.write_volume = writevol;
00409 
00410    if (startmon(chan, &mixmonitor->audiohook)) {
00411       ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00412          mixmonitor_spy_type, chan->name);
00413       ast_audiohook_destroy(&mixmonitor->audiohook);
00414       mixmonitor_free(mixmonitor);
00415       return;
00416    }
00417 
00418    ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
00419 }
00420 
00421 static int mixmonitor_exec(struct ast_channel *chan, void *data)
00422 {
00423    int x, readvol = 0, writevol = 0;
00424    struct ast_flags flags = {0};
00425    char *parse, *tmp, *slash;
00426    AST_DECLARE_APP_ARGS(args,
00427       AST_APP_ARG(filename);
00428       AST_APP_ARG(options);
00429       AST_APP_ARG(post_process);
00430    );
00431    
00432    if (ast_strlen_zero(data)) {
00433       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00434       return -1;
00435    }
00436 
00437    parse = ast_strdupa(data);
00438 
00439    AST_STANDARD_APP_ARGS(args, parse);
00440    
00441    if (ast_strlen_zero(args.filename)) {
00442       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00443       return -1;
00444    }
00445 
00446    if (args.options) {
00447       char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
00448 
00449       ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
00450 
00451       if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
00452          if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
00453             ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
00454          } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00455             ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
00456          } else {
00457             readvol = get_volfactor(x);
00458          }
00459       }
00460       
00461       if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
00462          if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
00463             ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
00464          } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00465             ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
00466          } else {
00467             writevol = get_volfactor(x);
00468          }
00469       }
00470       
00471       if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
00472          if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
00473             ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
00474          } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00475             ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
00476          } else {
00477             readvol = writevol = get_volfactor(x);
00478          }
00479       }
00480    }
00481 
00482    /* if not provided an absolute path, use the system-configured monitoring directory */
00483    if (args.filename[0] != '/') {
00484       char *build;
00485 
00486       build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
00487       sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
00488       args.filename = build;
00489    }
00490 
00491    tmp = ast_strdupa(args.filename);
00492    if ((slash = strrchr(tmp, '/')))
00493       *slash = '\0';
00494    ast_mkdir(tmp, 0777);
00495 
00496    pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
00497    launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
00498 
00499    return 0;
00500 }
00501 
00502 static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
00503 {
00504    struct ast_datastore *datastore = NULL;
00505 
00506    ast_channel_lock(chan);
00507    ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00508    if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) {
00509       struct mixmonitor_ds *mixmonitor_ds = datastore->data;
00510 
00511       ast_mutex_lock(&mixmonitor_ds->lock);
00512 
00513       /* closing the filestream here guarantees the file is avaliable to the dialplan
00514        * after calling StopMixMonitor */
00515       mixmonitor_ds_close_fs(mixmonitor_ds);
00516 
00517       /* The mixmonitor thread may be waiting on the audiohook trigger.
00518        * In order to exit from the mixmonitor loop before waiting on channel
00519        * destruction, poke the audiohook trigger. */
00520       if (mixmonitor_ds->audiohook) {
00521          ast_audiohook_lock(mixmonitor_ds->audiohook);
00522          ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
00523          ast_audiohook_unlock(mixmonitor_ds->audiohook);
00524          mixmonitor_ds->audiohook = NULL;
00525       }
00526 
00527       ast_mutex_unlock(&mixmonitor_ds->lock);
00528 
00529       /* Remove the datastore so the monitor thread can exit */
00530       if (!ast_channel_datastore_remove(chan, datastore)) {
00531          ast_datastore_free(datastore);
00532       }
00533    }
00534    ast_channel_unlock(chan);
00535 
00536    return 0;
00537 }
00538 
00539 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00540 {
00541    struct ast_channel *chan;
00542 
00543    switch (cmd) {
00544    case CLI_INIT:
00545       e->command = "mixmonitor [start|stop]";
00546       e->usage =
00547          "Usage: mixmonitor <start|stop> <chan_name> [args]\n"
00548          "       The optional arguments are passed to the MixMonitor\n"
00549          "       application when the 'start' command is used.\n";
00550       return NULL;
00551    case CLI_GENERATE:
00552       return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00553    }
00554 
00555    if (a->argc < 3)
00556       return CLI_SHOWUSAGE;
00557 
00558    if (!(chan = ast_get_channel_by_name_prefix_locked(a->argv[2], strlen(a->argv[2])))) {
00559       ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
00560       /* Technically this is a failure, but we don't want 2 errors printing out */
00561       return CLI_SUCCESS;
00562    }
00563 
00564    if (!strcasecmp(a->argv[1], "start")) {
00565       mixmonitor_exec(chan, a->argv[3]);
00566       ast_channel_unlock(chan);
00567    } else {
00568       ast_channel_unlock(chan);
00569       ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00570    }
00571 
00572    return CLI_SUCCESS;
00573 }
00574 
00575 static struct ast_cli_entry cli_mixmonitor[] = {
00576    AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
00577 };
00578 
00579 static int unload_module(void)
00580 {
00581    int res;
00582 
00583    ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
00584    res = ast_unregister_application(stop_app);
00585    res |= ast_unregister_application(app);
00586    
00587    return res;
00588 }
00589 
00590 static int load_module(void)
00591 {
00592    int res;
00593 
00594    ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
00595    res = ast_register_application(app, mixmonitor_exec, synopsis, desc);
00596    res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc);
00597 
00598    return res;
00599 }
00600 
00601 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");

Generated on Wed Aug 18 22:33:41 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7