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

Generated on Sun Aug 15 20:33:26 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7