Thu Apr 8 01:20:52 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: 230511 $")
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       ast_audiohook_trigger_wait(&mixmonitor->audiohook);
00249 
00250       if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
00251          break;
00252 
00253       if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR)))
00254          continue;
00255 
00256       /* audiohook lock is not required for the next block.
00257        * Unlock it, but remember to lock it before looping or exiting */
00258       ast_audiohook_unlock(&mixmonitor->audiohook);
00259 
00260       ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00261       if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) {
00262          /* Initialize the file if not already done so */
00263          if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
00264             oflags = O_CREAT | O_WRONLY;
00265             oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00266 
00267             if ((ext = strrchr(mixmonitor->filename, '.')))
00268                *(ext++) = '\0';
00269             else
00270                ext = "raw";
00271 
00272             if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) {
00273                ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
00274                errflag = 1;
00275             }
00276          }
00277 
00278          /* Write out the frame(s) */
00279          if (*fs) {
00280             struct ast_frame *cur;
00281 
00282             for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00283                ast_writestream(*fs, cur);
00284             }
00285          }
00286       }
00287       ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00288 
00289       /* All done! free it. */
00290       ast_frame_free(fr, 0);
00291       ast_audiohook_lock(&mixmonitor->audiohook);
00292    }
00293 
00294    ast_audiohook_unlock(&mixmonitor->audiohook);
00295 
00296    /* Datastore cleanup.  close the filestream and wait for ds destruction */
00297    ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00298    mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
00299    if (!mixmonitor->mixmonitor_ds->destruction_ok) {
00300       ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
00301    }
00302    ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00303 
00304    /* kill the audiohook */
00305    destroy_monitor_audiohook(mixmonitor);
00306 
00307    if (mixmonitor->post_process) {
00308       ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
00309       ast_safe_system(mixmonitor->post_process);
00310    }
00311 
00312    ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
00313    mixmonitor_free(mixmonitor);
00314    return NULL;
00315 }
00316 
00317 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
00318 {
00319    struct ast_datastore *datastore = NULL;
00320    struct mixmonitor_ds *mixmonitor_ds;
00321 
00322    if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
00323       return -1;
00324    }
00325 
00326    ast_mutex_init(&mixmonitor_ds->lock);
00327    ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
00328 
00329    if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) {
00330       ast_mutex_destroy(&mixmonitor_ds->lock);
00331       ast_cond_destroy(&mixmonitor_ds->destruction_condition);
00332       ast_free(mixmonitor_ds);
00333       return -1;
00334    }
00335 
00336    /* No need to lock mixmonitor_ds since this is still operating in the channel's thread */
00337    mixmonitor_ds->chan = chan;
00338    mixmonitor_ds->audiohook = &mixmonitor->audiohook;
00339    datastore->data = mixmonitor_ds;
00340 
00341    ast_channel_lock(chan);
00342    ast_channel_datastore_add(chan, datastore);
00343    ast_channel_unlock(chan);
00344 
00345    mixmonitor->mixmonitor_ds = mixmonitor_ds;
00346    return 0;
00347 }
00348 
00349 static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
00350               int readvol, int writevol, const char *post_process) 
00351 {
00352    pthread_t thread;
00353    struct mixmonitor *mixmonitor;
00354    char postprocess2[1024] = "";
00355    size_t len;
00356 
00357    len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
00358 
00359    postprocess2[0] = 0;
00360    /* If a post process system command is given attach it to the structure */
00361    if (!ast_strlen_zero(post_process)) {
00362       char *p1, *p2;
00363 
00364       p1 = ast_strdupa(post_process);
00365       for (p2 = p1; *p2 ; p2++) {
00366          if (*p2 == '^' && *(p2+1) == '{') {
00367             *p2 = '$';
00368          }
00369       }
00370       pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00371       if (!ast_strlen_zero(postprocess2))
00372          len += strlen(postprocess2) + 1;
00373    }
00374 
00375    /* Pre-allocate mixmonitor structure and spy */
00376    if (!(mixmonitor = ast_calloc(1, len))) {
00377       return;
00378    }
00379 
00380    /* Setup the actual spy before creating our thread */
00381    if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
00382       mixmonitor_free(mixmonitor);
00383       return;
00384    }
00385 
00386    /* Copy over flags and channel name */
00387    mixmonitor->flags = flags;
00388    if (setup_mixmonitor_ds(mixmonitor, chan)) {
00389       mixmonitor_free(mixmonitor);
00390       return;
00391    }
00392    mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
00393    strcpy(mixmonitor->name, chan->name);
00394    if (!ast_strlen_zero(postprocess2)) {
00395       mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
00396       strcpy(mixmonitor->post_process, postprocess2);
00397    }
00398 
00399    mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
00400    strcpy(mixmonitor->filename, filename);
00401 
00402    ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
00403 
00404    if (readvol)
00405       mixmonitor->audiohook.options.read_volume = readvol;
00406    if (writevol)
00407       mixmonitor->audiohook.options.write_volume = writevol;
00408 
00409    if (startmon(chan, &mixmonitor->audiohook)) {
00410       ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00411          mixmonitor_spy_type, chan->name);
00412       ast_audiohook_destroy(&mixmonitor->audiohook);
00413       mixmonitor_free(mixmonitor);
00414       return;
00415    }
00416 
00417    ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
00418 }
00419 
00420 static int mixmonitor_exec(struct ast_channel *chan, void *data)
00421 {
00422    int x, readvol = 0, writevol = 0;
00423    struct ast_flags flags = {0};
00424    char *parse, *tmp, *slash;
00425    AST_DECLARE_APP_ARGS(args,
00426       AST_APP_ARG(filename);
00427       AST_APP_ARG(options);
00428       AST_APP_ARG(post_process);
00429    );
00430    
00431    if (ast_strlen_zero(data)) {
00432       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00433       return -1;
00434    }
00435 
00436    parse = ast_strdupa(data);
00437 
00438    AST_STANDARD_APP_ARGS(args, parse);
00439    
00440    if (ast_strlen_zero(args.filename)) {
00441       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00442       return -1;
00443    }
00444 
00445    if (args.options) {
00446       char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
00447 
00448       ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
00449 
00450       if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
00451          if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
00452             ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
00453          } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00454             ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
00455          } else {
00456             readvol = get_volfactor(x);
00457          }
00458       }
00459       
00460       if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
00461          if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
00462             ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
00463          } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00464             ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
00465          } else {
00466             writevol = get_volfactor(x);
00467          }
00468       }
00469       
00470       if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
00471          if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
00472             ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
00473          } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00474             ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
00475          } else {
00476             readvol = writevol = get_volfactor(x);
00477          }
00478       }
00479    }
00480 
00481    /* if not provided an absolute path, use the system-configured monitoring directory */
00482    if (args.filename[0] != '/') {
00483       char *build;
00484 
00485       build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
00486       sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
00487       args.filename = build;
00488    }
00489 
00490    tmp = ast_strdupa(args.filename);
00491    if ((slash = strrchr(tmp, '/')))
00492       *slash = '\0';
00493    ast_mkdir(tmp, 0777);
00494 
00495    pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
00496    launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
00497 
00498    return 0;
00499 }
00500 
00501 static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
00502 {
00503    struct ast_datastore *datastore = NULL;
00504 
00505    ast_channel_lock(chan);
00506    ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00507    if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) {
00508       struct mixmonitor_ds *mixmonitor_ds = datastore->data;
00509 
00510       ast_mutex_lock(&mixmonitor_ds->lock);
00511 
00512       /* closing the filestream here guarantees the file is avaliable to the dialplan
00513        * after calling StopMixMonitor */
00514       mixmonitor_ds_close_fs(mixmonitor_ds);
00515 
00516       /* The mixmonitor thread may be waiting on the audiohook trigger.
00517        * In order to exit from the mixmonitor loop before waiting on channel
00518        * destruction, poke the audiohook trigger. */
00519       if (mixmonitor_ds->audiohook) {
00520          ast_audiohook_lock(mixmonitor_ds->audiohook);
00521          ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
00522          ast_audiohook_unlock(mixmonitor_ds->audiohook);
00523          mixmonitor_ds->audiohook = NULL;
00524       }
00525 
00526       ast_mutex_unlock(&mixmonitor_ds->lock);
00527 
00528       /* Remove the datastore so the monitor thread can exit */
00529       if (!ast_channel_datastore_remove(chan, datastore)) {
00530          ast_datastore_free(datastore);
00531       }
00532    }
00533    ast_channel_unlock(chan);
00534 
00535    return 0;
00536 }
00537 
00538 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00539 {
00540    struct ast_channel *chan;
00541 
00542    switch (cmd) {
00543    case CLI_INIT:
00544       e->command = "mixmonitor [start|stop]";
00545       e->usage =
00546          "Usage: mixmonitor <start|stop> <chan_name> [args]\n"
00547          "       The optional arguments are passed to the MixMonitor\n"
00548          "       application when the 'start' command is used.\n";
00549       return NULL;
00550    case CLI_GENERATE:
00551       return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00552    }
00553 
00554    if (a->argc < 3)
00555       return CLI_SHOWUSAGE;
00556 
00557    if (!(chan = ast_get_channel_by_name_prefix_locked(a->argv[2], strlen(a->argv[2])))) {
00558       ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
00559       /* Technically this is a failure, but we don't want 2 errors printing out */
00560       return CLI_SUCCESS;
00561    }
00562 
00563    if (!strcasecmp(a->argv[1], "start")) {
00564       mixmonitor_exec(chan, a->argv[3]);
00565       ast_channel_unlock(chan);
00566    } else {
00567       ast_channel_unlock(chan);
00568       ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00569    }
00570 
00571    return CLI_SUCCESS;
00572 }
00573 
00574 static struct ast_cli_entry cli_mixmonitor[] = {
00575    AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
00576 };
00577 
00578 static int unload_module(void)
00579 {
00580    int res;
00581 
00582    ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
00583    res = ast_unregister_application(stop_app);
00584    res |= ast_unregister_application(app);
00585    
00586    return res;
00587 }
00588 
00589 static int load_module(void)
00590 {
00591    int res;
00592 
00593    ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
00594    res = ast_register_application(app, mixmonitor_exec, synopsis, desc);
00595    res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc);
00596 
00597    return res;
00598 }
00599 
00600 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");

Generated on 8 Apr 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1