Wed May 16 06:33:20 2012

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 /*** MODULEINFO
00037    <support_level>core</support_level>
00038  ***/
00039 
00040 #include "asterisk.h"
00041 
00042 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 360787 $")
00043 
00044 #include "asterisk/paths.h"   /* use ast_config_AST_MONITOR_DIR */
00045 #include "asterisk/file.h"
00046 #include "asterisk/audiohook.h"
00047 #include "asterisk/pbx.h"
00048 #include "asterisk/module.h"
00049 #include "asterisk/cli.h"
00050 #include "asterisk/app.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/autochan.h"
00053 #include "asterisk/manager.h"
00054 #include "asterisk/mod_format.h"
00055 #include "asterisk/linkedlists.h"
00056 
00057 /*** DOCUMENTATION
00058    <application name="MixMonitor" language="en_US">
00059       <synopsis>
00060          Record a call and mix the audio during the recording.  Use of StopMixMonitor is required
00061          to guarantee the audio file is available for processing during dialplan execution.
00062       </synopsis>
00063       <syntax>
00064          <parameter name="file" required="true" argsep=".">
00065             <argument name="filename" required="true">
00066                <para>If <replaceable>filename</replaceable> is an absolute path, uses that path, otherwise
00067                creates the file in the configured monitoring directory from <filename>asterisk.conf.</filename></para>
00068             </argument>
00069             <argument name="extension" required="true" />
00070          </parameter>
00071          <parameter name="options">
00072             <optionlist>
00073                <option name="a">
00074                   <para>Append to the file instead of overwriting it.</para>
00075                </option>
00076                <option name="b">
00077                   <para>Only save audio to the file while the channel is bridged.</para>
00078                   <note><para>Does not include conferences or sounds played to each bridged party</para></note>
00079                   <note><para>If you utilize this option inside a Local channel, you must make sure the Local
00080                   channel is not optimized away. To do this, be sure to call your Local channel with the
00081                   <literal>/n</literal> option. For example: Dial(Local/start@mycontext/n)</para></note>
00082                </option>
00083                <option name="v">
00084                   <para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
00085                   (range <literal>-4</literal> to <literal>4</literal>)</para>
00086                   <argument name="x" required="true" />
00087                </option>
00088                <option name="V">
00089                   <para>Adjust the <emphasis>spoken</emphasis> volume by a factor
00090                   of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
00091                   <argument name="x" required="true" />
00092                </option>
00093                <option name="W">
00094                   <para>Adjust both, <emphasis>heard and spoken</emphasis> volumes by a factor
00095                   of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
00096                   <argument name="x" required="true" />
00097                </option>
00098                <option name="r">
00099                   <argument name="file" required="true" />
00100                   <para>Use the specified file to record the <emphasis>receive</emphasis> audio feed.
00101                   Like with the basic filename argument, if an absolute path isn't given, it will create
00102                   the file in the configured monitoring directory.</para>
00103 
00104                </option>
00105                <option name="t">
00106                   <argument name="file" required="true" />
00107                   <para>Use the specified file to record the <emphasis>transmit</emphasis> audio feed.
00108                   Like with the basic filename argument, if an absolute path isn't given, it will create
00109                   the file in the configured monitoring directory.</para>
00110                </option>
00111                <option name="i">
00112                   <argument name="chanvar" required="true" />
00113                   <para>Stores the MixMonitor's ID on this channel variable.</para>
00114                </option>
00115             </optionlist>
00116          </parameter>
00117          <parameter name="command">
00118             <para>Will be executed when the recording is over.</para>
00119             <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
00120             <para>All variables will be evaluated at the time MixMonitor is called.</para>
00121          </parameter>
00122       </syntax>
00123       <description>
00124          <para>Records the audio on the current channel to the specified file.</para>
00125          <para>This application does not automatically answer and should be preceeded by
00126          an application such as Answer or Progress().</para>
00127          <variablelist>
00128             <variable name="MIXMONITOR_FILENAME">
00129                <para>Will contain the filename used to record.</para>
00130             </variable>
00131          </variablelist>   
00132       </description>
00133       <see-also>
00134          <ref type="application">Monitor</ref>
00135          <ref type="application">StopMixMonitor</ref>
00136          <ref type="application">PauseMonitor</ref>
00137          <ref type="application">UnpauseMonitor</ref>
00138       </see-also>
00139    </application>
00140    <application name="StopMixMonitor" language="en_US">
00141       <synopsis>
00142          Stop recording a call through MixMonitor, and free the recording's file handle.
00143       </synopsis>
00144       <syntax>
00145          <parameter name="MixMonitorID" required="false">
00146             <para>If a valid ID is provided, then this command will stop only that specific
00147             MixMonitor.</para>
00148          </parameter>
00149       </syntax>
00150       <description>
00151          <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
00152          on the current channel.</para>
00153       </description>
00154       <see-also>
00155          <ref type="application">MixMonitor</ref>
00156       </see-also>
00157    </application>
00158    <manager name="MixMonitorMute" language="en_US">
00159       <synopsis>
00160          Mute / unMute a Mixmonitor recording.
00161       </synopsis>
00162       <syntax>
00163          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00164          <parameter name="Channel" required="true">
00165             <para>Used to specify the channel to mute.</para>
00166          </parameter>
00167          <parameter name="Direction">
00168             <para>Which part of the recording to mute:  read, write or both (from channel, to channel or both channels).</para>
00169          </parameter>
00170          <parameter name="State">
00171             <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
00172          </parameter>
00173       </syntax>
00174       <description>
00175          <para>This action may be used to mute a MixMonitor recording.</para>
00176       </description>
00177    </manager>
00178    <manager name="MixMonitor" language="en_US">
00179       <synopsis>
00180          Record a call and mix the audio during the recording.  Use of StopMixMonitor is required
00181          to guarantee the audio file is available for processing during dialplan execution.
00182       </synopsis>
00183       <syntax>
00184          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00185          <parameter name="Channel" required="true">
00186             <para>Used to specify the channel to record.</para>
00187          </parameter>
00188          <parameter name="File">
00189             <para>Is the name of the file created in the monitor spool directory.
00190             Defaults to the same name as the channel (with slashes replaced with dashes).
00191             This argument is optional if you specify to record unidirectional audio with
00192             either the r(filename) or t(filename) options in the options field. If
00193             neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
00194             be recorded.</para>
00195          </parameter>
00196          <parameter name="options">
00197             <para>Options that apply to the MixMonitor in the same way as they
00198             would apply if invoked from the MixMonitor application. For a list of
00199             available options, see the documentation for the mixmonitor application. </para>
00200          </parameter>
00201       </syntax>
00202       <description>
00203          <para>This action records the audio on the current channel to the specified file.</para>
00204          <variablelist>
00205             <variable name="MIXMONITOR_FILENAME">
00206                <para>Will contain the filename used to record the mixed stream.</para>
00207             </variable>
00208          </variablelist>
00209       </description>
00210    </manager>
00211    <manager name="StopMixMonitor" language="en_US">
00212       <synopsis>
00213          Stop recording a call through MixMonitor, and free the recording's file handle.
00214       </synopsis>
00215       <syntax>
00216          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00217          <parameter name="Channel" required="true">
00218             <para>The name of the channel monitored.</para>
00219          </parameter>
00220          <parameter name="MixMonitorID" required="false">
00221             <para>If a valid ID is provided, then this command will stop only that specific
00222             MixMonitor.</para>
00223          </parameter>
00224       </syntax>
00225       <description>
00226          <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
00227          action on the current channel.</para>
00228       </description>
00229    </manager>
00230 
00231  ***/
00232 
00233 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
00234 
00235 static const char * const app = "MixMonitor";
00236 
00237 static const char * const stop_app = "StopMixMonitor";
00238 
00239 static const char * const mixmonitor_spy_type = "MixMonitor";
00240 
00241 struct mixmonitor {
00242    struct ast_audiohook audiohook;
00243    struct ast_callid *callid;
00244    char *filename;
00245    char *filename_read;
00246    char *filename_write;
00247    char *post_process;
00248    char *name;
00249    unsigned int flags;
00250    struct ast_autochan *autochan;
00251    struct mixmonitor_ds *mixmonitor_ds;
00252 };
00253 
00254 enum mixmonitor_flags {
00255    MUXFLAG_APPEND = (1 << 1),
00256    MUXFLAG_BRIDGED = (1 << 2),
00257    MUXFLAG_VOLUME = (1 << 3),
00258    MUXFLAG_READVOLUME = (1 << 4),
00259    MUXFLAG_WRITEVOLUME = (1 << 5),
00260    MUXFLAG_READ = (1 << 6),
00261    MUXFLAG_WRITE = (1 << 7),
00262    MUXFLAG_COMBINED = (1 << 8),
00263         MUXFLAG_UID = (1 << 9),
00264 };
00265 
00266 enum mixmonitor_args {
00267    OPT_ARG_READVOLUME = 0,
00268    OPT_ARG_WRITEVOLUME,
00269    OPT_ARG_VOLUME,
00270    OPT_ARG_WRITENAME,
00271    OPT_ARG_READNAME,
00272         OPT_ARG_UID,
00273    OPT_ARG_ARRAY_SIZE,  /* Always last element of the enum */
00274 };
00275 
00276 AST_APP_OPTIONS(mixmonitor_opts, {
00277    AST_APP_OPTION('a', MUXFLAG_APPEND),
00278    AST_APP_OPTION('b', MUXFLAG_BRIDGED),
00279    AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
00280    AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
00281    AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
00282    AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
00283    AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
00284    AST_APP_OPTION_ARG('i', MUXFLAG_UID, OPT_ARG_UID),
00285 });
00286 
00287 struct mixmonitor_ds {
00288    unsigned int destruction_ok;
00289    ast_cond_t destruction_condition;
00290    ast_mutex_t lock;
00291 
00292    /* The filestream is held in the datastore so it can be stopped
00293     * immediately during stop_mixmonitor or channel destruction. */
00294    int fs_quit;
00295 
00296    struct ast_filestream *fs;
00297    struct ast_filestream *fs_read;
00298    struct ast_filestream *fs_write;
00299 
00300    struct ast_audiohook *audiohook;
00301 
00302    unsigned int samp_rate;
00303 };
00304 
00305 /*!
00306  * \internal
00307  * \pre mixmonitor_ds must be locked before calling this function
00308  */
00309 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
00310 {
00311    unsigned char quitting = 0;
00312 
00313    if (mixmonitor_ds->fs) {
00314       quitting = 1;
00315       ast_closestream(mixmonitor_ds->fs);
00316       mixmonitor_ds->fs = NULL;
00317       ast_verb(2, "MixMonitor close filestream (mixed)\n");
00318    }
00319 
00320    if (mixmonitor_ds->fs_read) {
00321       quitting = 1;
00322       ast_closestream(mixmonitor_ds->fs_read);
00323       mixmonitor_ds->fs_read = NULL;
00324       ast_verb(2, "MixMonitor close filestream (read)\n");
00325    }
00326 
00327    if (mixmonitor_ds->fs_write) {
00328       quitting = 1;
00329       ast_closestream(mixmonitor_ds->fs_write);
00330       mixmonitor_ds->fs_write = NULL;
00331       ast_verb(2, "MixMonitor close filestream (write)\n");
00332    }
00333 
00334    if (quitting) {
00335       mixmonitor_ds->fs_quit = 1;
00336    }
00337 }
00338 
00339 static void mixmonitor_ds_destroy(void *data)
00340 {
00341    struct mixmonitor_ds *mixmonitor_ds = data;
00342 
00343    ast_mutex_lock(&mixmonitor_ds->lock);
00344    mixmonitor_ds->audiohook = NULL;
00345    mixmonitor_ds->destruction_ok = 1;
00346    ast_cond_signal(&mixmonitor_ds->destruction_condition);
00347    ast_mutex_unlock(&mixmonitor_ds->lock);
00348 }
00349 
00350 static struct ast_datastore_info mixmonitor_ds_info = {
00351    .type = "mixmonitor",
00352    .destroy = mixmonitor_ds_destroy,
00353 };
00354 
00355 static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
00356 {
00357    if (mixmonitor->mixmonitor_ds) {
00358       ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00359       mixmonitor->mixmonitor_ds->audiohook = NULL;
00360       ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00361    }
00362    /* kill the audiohook.*/
00363    ast_audiohook_lock(&mixmonitor->audiohook);
00364    ast_audiohook_detach(&mixmonitor->audiohook);
00365    ast_audiohook_unlock(&mixmonitor->audiohook);
00366    ast_audiohook_destroy(&mixmonitor->audiohook);
00367 }
00368 
00369 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook) 
00370 {
00371    struct ast_channel *peer = NULL;
00372    int res = 0;
00373 
00374    if (!chan)
00375       return -1;
00376 
00377    ast_audiohook_attach(chan, audiohook);
00378 
00379    if (!res && ast_test_flag(ast_channel_flags(chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
00380       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
00381 
00382    return res;
00383 }
00384 
00385 #define SAMPLES_PER_FRAME 160
00386 
00387 static void mixmonitor_free(struct mixmonitor *mixmonitor)
00388 {
00389    if (mixmonitor) {
00390       if (mixmonitor->mixmonitor_ds) {
00391          ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
00392          ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
00393          ast_free(mixmonitor->filename_write);
00394          ast_free(mixmonitor->filename_read);
00395          ast_free(mixmonitor->mixmonitor_ds);
00396          ast_free(mixmonitor->name);
00397          ast_free(mixmonitor->post_process);
00398       }
00399 
00400       if (mixmonitor->callid) {
00401          ast_callid_unref(mixmonitor->callid);
00402       }
00403       ast_free(mixmonitor);
00404    }
00405 }
00406 
00407 static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag)
00408 {
00409    /* Initialize the file if not already done so */
00410    char *ext = NULL;
00411    char *last_slash = NULL;
00412    if (!ast_strlen_zero(filename)) {
00413       if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
00414          *oflags = O_CREAT | O_WRONLY;
00415          *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00416 
00417          last_slash = strrchr(filename, '/');
00418 
00419          if ((ext = strrchr(filename, '.')) && (ext > last_slash)) {
00420             *(ext++) = '\0';
00421          } else {
00422             ext = "raw";
00423          }
00424 
00425          if (!(*fs = ast_writefile(filename, ext, NULL, *oflags, 0, 0666))) {
00426             ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, ext);
00427             *errflag = 1;
00428          } else {
00429             struct ast_filestream *tmp = *fs;
00430             mixmonitor->mixmonitor_ds->samp_rate = MAX(mixmonitor->mixmonitor_ds->samp_rate, ast_format_rate(&tmp->fmt->format));
00431          }
00432       }
00433    }
00434 }
00435 
00436 static void *mixmonitor_thread(void *obj) 
00437 {
00438    struct mixmonitor *mixmonitor = obj;
00439 
00440    struct ast_filestream **fs = NULL;
00441    struct ast_filestream **fs_read = NULL;
00442    struct ast_filestream **fs_write = NULL;
00443 
00444    unsigned int oflags;
00445    int errflag = 0;
00446    struct ast_format format_slin;
00447 
00448    /* Keep callid association before any log messages */
00449    if (mixmonitor->callid) {
00450       ast_callid_threadassoc_add(mixmonitor->callid);
00451    }
00452 
00453    ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
00454 
00455    fs = &mixmonitor->mixmonitor_ds->fs;
00456    fs_read = &mixmonitor->mixmonitor_ds->fs_read;
00457    fs_write = &mixmonitor->mixmonitor_ds->fs_write;
00458 
00459    ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00460    mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag);
00461    mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag);
00462    mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag);
00463 
00464    ast_format_set(&format_slin, ast_format_slin_by_rate(mixmonitor->mixmonitor_ds->samp_rate), 0);
00465 
00466    ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00467 
00468 
00469    /* The audiohook must enter and exit the loop locked */
00470    ast_audiohook_lock(&mixmonitor->audiohook);
00471    while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
00472       struct ast_frame *fr = NULL;
00473       struct ast_frame *fr_read = NULL;
00474       struct ast_frame *fr_write = NULL;
00475 
00476       if (!(fr = ast_audiohook_read_frame_all(&mixmonitor->audiohook, SAMPLES_PER_FRAME, &format_slin,
00477                   &fr_read, &fr_write))) {
00478          ast_audiohook_trigger_wait(&mixmonitor->audiohook);
00479 
00480          if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00481             break;
00482          }
00483          continue;
00484       }
00485 
00486       /* audiohook lock is not required for the next block.
00487        * Unlock it, but remember to lock it before looping or exiting */
00488       ast_audiohook_unlock(&mixmonitor->audiohook);
00489 
00490       if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
00491          ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00492 
00493          /* Write out the frame(s) */
00494          if ((*fs_read) && (fr_read)) {
00495             struct ast_frame *cur;
00496 
00497             for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00498                ast_writestream(*fs_read, cur);
00499             }
00500          }
00501 
00502          if ((*fs_write) && (fr_write)) {
00503             struct ast_frame *cur;
00504 
00505             for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00506                ast_writestream(*fs_write, cur);
00507             }
00508          }
00509 
00510          if ((*fs) && (fr)) {
00511             struct ast_frame *cur;
00512 
00513             for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00514                ast_writestream(*fs, cur);
00515             }
00516          }
00517          ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00518       }
00519       /* All done! free it. */
00520       if (fr) {
00521          ast_frame_free(fr, 0);
00522       }
00523       if (fr_read) {
00524          ast_frame_free(fr_read, 0);
00525       }
00526       if (fr_write) {
00527          ast_frame_free(fr_write, 0);
00528       }
00529 
00530       fr = NULL;
00531       fr_write = NULL;
00532       fr_read = NULL;
00533 
00534       ast_audiohook_lock(&mixmonitor->audiohook);
00535    }
00536    ast_audiohook_unlock(&mixmonitor->audiohook);
00537 
00538    ast_autochan_destroy(mixmonitor->autochan);
00539 
00540    /* Datastore cleanup.  close the filestream and wait for ds destruction */
00541    ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00542    mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
00543    if (!mixmonitor->mixmonitor_ds->destruction_ok) {
00544       ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
00545    }
00546    ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00547 
00548    /* kill the audiohook */
00549    destroy_monitor_audiohook(mixmonitor);
00550 
00551    if (mixmonitor->post_process) {
00552       ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
00553       ast_safe_system(mixmonitor->post_process);
00554    }
00555 
00556    ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
00557    mixmonitor_free(mixmonitor);
00558    return NULL;
00559 }
00560 
00561 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id)
00562 {
00563    struct ast_datastore *datastore = NULL;
00564    struct mixmonitor_ds *mixmonitor_ds;
00565 
00566    if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
00567       return -1;
00568    }
00569 
00570    if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
00571       ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
00572    }
00573 
00574    ast_mutex_init(&mixmonitor_ds->lock);
00575    ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
00576 
00577    if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
00578       ast_mutex_destroy(&mixmonitor_ds->lock);
00579       ast_cond_destroy(&mixmonitor_ds->destruction_condition);
00580       ast_free(mixmonitor_ds);
00581       return -1;
00582    }
00583 
00584 
00585    mixmonitor_ds->samp_rate = 8000;
00586    mixmonitor_ds->audiohook = &mixmonitor->audiohook;
00587    datastore->data = mixmonitor_ds;
00588 
00589    ast_channel_lock(chan);
00590    ast_channel_datastore_add(chan, datastore);
00591    ast_channel_unlock(chan);
00592 
00593    mixmonitor->mixmonitor_ds = mixmonitor_ds;
00594    return 0;
00595 }
00596 
00597 static void launch_monitor_thread(struct ast_channel *chan, const char *filename,
00598               unsigned int flags, int readvol, int writevol,
00599               const char *post_process, const char *filename_write,
00600               char *filename_read, const char *uid_channel_var)
00601 {
00602    pthread_t thread;
00603    struct mixmonitor *mixmonitor;
00604    char postprocess2[1024] = "";
00605    char *datastore_id = NULL;
00606 
00607    postprocess2[0] = 0;
00608    /* If a post process system command is given attach it to the structure */
00609    if (!ast_strlen_zero(post_process)) {
00610       char *p1, *p2;
00611 
00612       p1 = ast_strdupa(post_process);
00613       for (p2 = p1; *p2; p2++) {
00614          if (*p2 == '^' && *(p2+1) == '{') {
00615             *p2 = '$';
00616          }
00617       }
00618       pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00619    }
00620 
00621    /* Pre-allocate mixmonitor structure and spy */
00622    if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
00623       return;
00624    }
00625 
00626    /* Setup the actual spy before creating our thread */
00627    if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) {
00628       mixmonitor_free(mixmonitor);
00629       return;
00630    }
00631 
00632    /* Copy over flags and channel name */
00633    mixmonitor->flags = flags;
00634    if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
00635       mixmonitor_free(mixmonitor);
00636       return;
00637    }
00638 
00639    if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id)) {
00640       ast_autochan_destroy(mixmonitor->autochan);
00641       mixmonitor_free(mixmonitor);
00642       ast_free(datastore_id);
00643       return;
00644    }
00645 
00646    if (!ast_strlen_zero(uid_channel_var)) {
00647       if (datastore_id) {
00648          pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
00649       }
00650    }
00651    ast_free(datastore_id);
00652 
00653 
00654    mixmonitor->name = ast_strdup(ast_channel_name(chan));
00655 
00656    if (!ast_strlen_zero(postprocess2)) {
00657       mixmonitor->post_process = ast_strdup(postprocess2);
00658    }
00659 
00660    if (!ast_strlen_zero(filename)) {
00661       mixmonitor->filename = ast_strdup(filename);
00662    }
00663 
00664    if (!ast_strlen_zero(filename_write)) {
00665       mixmonitor->filename_write = ast_strdup(filename_write);
00666    }
00667 
00668    if (!ast_strlen_zero(filename_read)) {
00669       mixmonitor->filename_read = ast_strdup(filename_read);
00670    }
00671 
00672    ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
00673 
00674    if (readvol)
00675       mixmonitor->audiohook.options.read_volume = readvol;
00676    if (writevol)
00677       mixmonitor->audiohook.options.write_volume = writevol;
00678 
00679    if (startmon(chan, &mixmonitor->audiohook)) {
00680       ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00681          mixmonitor_spy_type, ast_channel_name(chan));
00682       ast_audiohook_destroy(&mixmonitor->audiohook);
00683       mixmonitor_free(mixmonitor);
00684       return;
00685    }
00686 
00687    /* reference be released at mixmonitor destruction */
00688    mixmonitor->callid = ast_read_threadstorage_callid();
00689 
00690    ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
00691 }
00692 
00693 /* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
00694 /* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
00695 static char *filename_parse(char *filename, char *buffer, size_t len)
00696 {
00697    char *slash;
00698    if (ast_strlen_zero(filename)) {
00699       ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
00700    } else if (filename[0] != '/') {
00701       char *build;
00702       build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
00703       sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
00704       filename = build;
00705    }
00706 
00707    ast_copy_string(buffer, filename, len);
00708 
00709    if ((slash = strrchr(filename, '/'))) {
00710       *slash = '\0';
00711    }
00712    ast_mkdir(filename, 0777);
00713 
00714    return buffer;
00715 }
00716 
00717 static int mixmonitor_exec(struct ast_channel *chan, const char *data)
00718 {
00719    int x, readvol = 0, writevol = 0;
00720    char *filename_read = NULL;
00721    char *filename_write = NULL;
00722    char filename_buffer[1024] = "";
00723         char *uid_channel_var = NULL;
00724 
00725    struct ast_flags flags = { 0 };
00726    char *parse;
00727    AST_DECLARE_APP_ARGS(args,
00728       AST_APP_ARG(filename);
00729       AST_APP_ARG(options);
00730       AST_APP_ARG(post_process);
00731    );
00732 
00733    if (ast_strlen_zero(data)) {
00734       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
00735       return -1;
00736    }
00737 
00738    parse = ast_strdupa(data);
00739 
00740    AST_STANDARD_APP_ARGS(args, parse);
00741 
00742    if (args.options) {
00743       char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
00744 
00745       ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
00746 
00747       if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
00748          if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
00749             ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
00750          } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00751             ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
00752          } else {
00753             readvol = get_volfactor(x);
00754          }
00755       }
00756 
00757       if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
00758          if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
00759             ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
00760          } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00761             ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
00762          } else {
00763             writevol = get_volfactor(x);
00764          }
00765       }
00766 
00767       if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
00768          if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
00769             ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
00770          } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00771             ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
00772          } else {
00773             readvol = writevol = get_volfactor(x);
00774          }
00775       }
00776 
00777       if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
00778          filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
00779       }
00780 
00781       if (ast_test_flag(&flags, MUXFLAG_READ)) {
00782          filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
00783       }
00784 
00785       if (ast_test_flag(&flags, MUXFLAG_UID)) {
00786          uid_channel_var = opts[OPT_ARG_UID];
00787       }
00788    }
00789    /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
00790 
00791    if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
00792       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00793       return -1;
00794    }
00795 
00796    /* If filename exists, try to create directories for it */
00797    if (!(ast_strlen_zero(args.filename))) {
00798       args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
00799    }
00800 
00801    pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
00802    launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process, filename_write, filename_read, uid_channel_var);
00803 
00804    return 0;
00805 }
00806 
00807 static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
00808 {
00809    struct ast_datastore *datastore = NULL;
00810    char *parse = "";
00811    struct mixmonitor_ds *mixmonitor_ds;
00812 
00813    AST_DECLARE_APP_ARGS(args,
00814       AST_APP_ARG(mixmonid);
00815    );
00816 
00817    if (!ast_strlen_zero(data)) {
00818       parse = ast_strdupa(data);
00819    }
00820 
00821    AST_STANDARD_APP_ARGS(args, parse);
00822 
00823    ast_channel_lock(chan);
00824 
00825    if (!(datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.mixmonid))) {
00826       ast_channel_unlock(chan);
00827                 return -1;
00828    }
00829    mixmonitor_ds = datastore->data;
00830 
00831    ast_mutex_lock(&mixmonitor_ds->lock);
00832 
00833    /* closing the filestream here guarantees the file is avaliable to the dialplan
00834     * after calling StopMixMonitor */
00835    mixmonitor_ds_close_fs(mixmonitor_ds);
00836 
00837    /* The mixmonitor thread may be waiting on the audiohook trigger.
00838     * In order to exit from the mixmonitor loop before waiting on channel
00839     * destruction, poke the audiohook trigger. */
00840    if (mixmonitor_ds->audiohook) {
00841       if (mixmonitor_ds->audiohook->status != AST_AUDIOHOOK_STATUS_DONE) {
00842          ast_audiohook_update_status(mixmonitor_ds->audiohook, AST_AUDIOHOOK_STATUS_SHUTDOWN);
00843       }
00844       ast_audiohook_lock(mixmonitor_ds->audiohook);
00845       ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
00846       ast_audiohook_unlock(mixmonitor_ds->audiohook);
00847       mixmonitor_ds->audiohook = NULL;
00848    }
00849 
00850    ast_mutex_unlock(&mixmonitor_ds->lock);
00851 
00852    /* Remove the datastore so the monitor thread can exit */
00853    if (!ast_channel_datastore_remove(chan, datastore)) {
00854       ast_datastore_free(datastore);
00855    }
00856    ast_channel_unlock(chan);
00857 
00858    return 0;
00859 }
00860 
00861 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00862 {
00863    struct ast_channel *chan;
00864    struct ast_datastore *datastore = NULL;
00865    struct mixmonitor_ds *mixmonitor_ds = NULL;
00866 
00867    switch (cmd) {
00868    case CLI_INIT:
00869       e->command = "mixmonitor {start|stop|list}";
00870       e->usage =
00871          "Usage: mixmonitor <start|stop|list> <chan_name> [args]\n"
00872          "       The optional arguments are passed to the MixMonitor\n"
00873          "       application when the 'start' command is used.\n";
00874       return NULL;
00875    case CLI_GENERATE:
00876       return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00877    }
00878 
00879    if (a->argc < 3) {
00880       return CLI_SHOWUSAGE;
00881    }
00882 
00883    if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
00884       ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
00885       /* Technically this is a failure, but we don't want 2 errors printing out */
00886       return CLI_SUCCESS;
00887    }
00888 
00889    ast_channel_lock(chan);
00890 
00891    if (!strcasecmp(a->argv[1], "start")) {
00892       mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
00893       ast_channel_unlock(chan);
00894    } else if (!strcasecmp(a->argv[1], "stop")){
00895       ast_channel_unlock(chan);
00896       stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
00897    } else if (!strcasecmp(a->argv[1], "list")) {
00898       ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
00899       ast_cli(a->fd, "=========================================================================\n");
00900       AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
00901          if (datastore->info == &mixmonitor_ds_info) {
00902             char *filename = "";
00903             char *filename_read = "";
00904             char *filename_write = "";
00905             mixmonitor_ds = datastore->data;
00906             if (mixmonitor_ds->fs)
00907                filename = ast_strdupa(mixmonitor_ds->fs->filename);
00908             if (mixmonitor_ds->fs_read)
00909                filename_read = ast_strdupa(mixmonitor_ds->fs_read->filename);
00910             if (mixmonitor_ds->fs_write)
00911                filename_write = ast_strdupa(mixmonitor_ds->fs_write->filename);
00912             ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
00913          }
00914       }
00915       ast_channel_unlock(chan);
00916    } else {
00917       ast_channel_unlock(chan);
00918       chan = ast_channel_unref(chan);
00919       return CLI_SHOWUSAGE;
00920    }
00921 
00922    chan = ast_channel_unref(chan);
00923 
00924    return CLI_SUCCESS;
00925 }
00926 
00927 /*! \brief  Mute / unmute  a MixMonitor channel */
00928 static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
00929 {
00930    struct ast_channel *c = NULL;
00931 
00932    const char *name = astman_get_header(m, "Channel");
00933    const char *id = astman_get_header(m, "ActionID");
00934    const char *state = astman_get_header(m, "State");
00935    const char *direction = astman_get_header(m,"Direction");
00936 
00937    int clearmute = 1;
00938 
00939    enum ast_audiohook_flags flag;
00940 
00941    if (ast_strlen_zero(direction)) {
00942       astman_send_error(s, m, "No direction specified. Must be read, write or both");
00943       return AMI_SUCCESS;
00944    }
00945 
00946    if (!strcasecmp(direction, "read")) {
00947       flag = AST_AUDIOHOOK_MUTE_READ;
00948    } else  if (!strcasecmp(direction, "write")) {
00949       flag = AST_AUDIOHOOK_MUTE_WRITE;
00950    } else  if (!strcasecmp(direction, "both")) {
00951       flag = AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE;
00952    } else {
00953       astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
00954       return AMI_SUCCESS;
00955    }
00956 
00957    if (ast_strlen_zero(name)) {
00958       astman_send_error(s, m, "No channel specified");
00959       return AMI_SUCCESS;
00960    }
00961 
00962    if (ast_strlen_zero(state)) {
00963       astman_send_error(s, m, "No state specified");
00964       return AMI_SUCCESS;
00965    }
00966 
00967    clearmute = ast_false(state);
00968    c = ast_channel_get_by_name(name);
00969 
00970    if (!c) {
00971       astman_send_error(s, m, "No such channel");
00972       return AMI_SUCCESS;
00973    }
00974 
00975    if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
00976       c = ast_channel_unref(c);
00977       astman_send_error(s, m, "Cannot set mute flag");
00978       return AMI_SUCCESS;
00979    }
00980 
00981    astman_append(s, "Response: Success\r\n");
00982 
00983    if (!ast_strlen_zero(id)) {
00984       astman_append(s, "ActionID: %s\r\n", id);
00985    }
00986 
00987    astman_append(s, "\r\n");
00988 
00989    c = ast_channel_unref(c);
00990 
00991    return AMI_SUCCESS;
00992 }
00993 
00994 static int manager_mixmonitor(struct mansession *s, const struct message *m)
00995 {
00996    struct ast_channel *c = NULL;
00997 
00998    const char *name = astman_get_header(m, "Channel");
00999    const char *id = astman_get_header(m, "ActionID");
01000    const char *file = astman_get_header(m, "File");
01001    const char *options = astman_get_header(m, "Options");
01002    char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
01003    struct ast_flags flags = { 0 };
01004    char *uid_channel_var = NULL;
01005    const char *mixmonitor_id = NULL;
01006 
01007    int res;
01008    char args[PATH_MAX] = "";
01009    if (ast_strlen_zero(name)) {
01010       astman_send_error(s, m, "No channel specified");
01011       return AMI_SUCCESS;
01012    }
01013 
01014    c = ast_channel_get_by_name(name);
01015 
01016    if (!c) {
01017       astman_send_error(s, m, "No such channel");
01018       return AMI_SUCCESS;
01019    }
01020 
01021    if (!ast_strlen_zero(options)) {
01022       ast_app_parse_options(mixmonitor_opts, &flags, opts, ast_strdupa(options));
01023    }
01024 
01025    snprintf(args, sizeof(args), "%s,%s", file, options);
01026 
01027    ast_channel_lock(c);
01028    res = mixmonitor_exec(c, args);
01029 
01030    if (ast_test_flag(&flags, MUXFLAG_UID)) {
01031       uid_channel_var = opts[OPT_ARG_UID];
01032       mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
01033    }
01034    ast_channel_unlock(c);
01035 
01036    if (res) {
01037       astman_send_error(s, m, "Could not start monitoring channel");
01038       return AMI_SUCCESS;
01039    }
01040 
01041    astman_append(s, "Response: Success\r\n");
01042 
01043    if (!ast_strlen_zero(id)) {
01044       astman_append(s, "ActionID: %s\r\n", id);
01045    }
01046 
01047    if (!ast_strlen_zero(mixmonitor_id)) {
01048       astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
01049    }
01050 
01051    astman_append(s, "\r\n");
01052 
01053    c = ast_channel_unref(c);
01054 
01055    return AMI_SUCCESS;
01056 }
01057 
01058 static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
01059 {
01060    struct ast_channel *c = NULL;
01061 
01062    const char *name = astman_get_header(m, "Channel");
01063    const char *id = astman_get_header(m, "ActionID");
01064    const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
01065 
01066    int res;
01067    if (ast_strlen_zero(name)) {
01068       astman_send_error(s, m, "No channel specified");
01069       return AMI_SUCCESS;
01070    }
01071 
01072    c = ast_channel_get_by_name(name);
01073 
01074    if (!c) {
01075       astman_send_error(s, m, "No such channel");
01076       return AMI_SUCCESS;
01077    }
01078 
01079    res = stop_mixmonitor_exec(c, mixmonitor_id);
01080 
01081    if (res) {
01082       astman_send_error(s, m, "Could not stop monitoring channel");
01083       return AMI_SUCCESS;
01084    }
01085 
01086    astman_append(s, "Response: Success\r\n");
01087 
01088    if (!ast_strlen_zero(id)) {
01089       astman_append(s, "ActionID: %s\r\n", id);
01090    }
01091 
01092    astman_append(s, "\r\n");
01093 
01094    c = ast_channel_unref(c);
01095 
01096    return AMI_SUCCESS;
01097 }
01098 
01099 static struct ast_cli_entry cli_mixmonitor[] = {
01100    AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
01101 };
01102 
01103 static int unload_module(void)
01104 {
01105    int res;
01106 
01107    ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
01108    res = ast_unregister_application(stop_app);
01109    res |= ast_unregister_application(app);
01110    res |= ast_manager_unregister("MixMonitorMute");
01111    res |= ast_manager_unregister("MixMonitor");
01112    res |= ast_manager_unregister("StopMixMonitor");
01113 
01114    return res;
01115 }
01116 
01117 static int load_module(void)
01118 {
01119    int res;
01120 
01121    ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
01122    res = ast_register_application_xml(app, mixmonitor_exec);
01123    res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
01124    res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
01125    res |= ast_manager_register_xml("MixMonitor", 0, manager_mixmonitor);
01126    res |= ast_manager_register_xml("StopMixMonitor", 0, manager_stop_mixmonitor);
01127 
01128    return res;
01129 }
01130 
01131 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");

Generated on Wed May 16 06:33:20 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6