Sun Aug 6 15:03:52 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

app_mixmonitor.c File Reference

MixMonitor() - Record a call and mix the audio during the recording. More...

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "asterisk.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/chanspy.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/cli.h"
#include "asterisk/options.h"
#include "asterisk/app.h"
#include "asterisk/linkedlists.h"

Include dependency graph for app_mixmonitor.c:

Go to the source code of this file.

Defines

#define get_volfactor(x)   x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
#define SAMPLES_PER_FRAME   160

Enumerations

enum  {
  MUXFLAG_APPEND = (1 << 1), MUXFLAG_BRIDGED = (1 << 2), MUXFLAG_VOLUME = (1 << 3), MUXFLAG_READVOLUME = (1 << 4),
  MUXFLAG_WRITEVOLUME = (1 << 5)
}
enum  { OPT_ARG_READVOLUME = 0, OPT_ARG_WRITEVOLUME, OPT_ARG_VOLUME, OPT_ARG_ARRAY_SIZE }

Functions

 AST_APP_OPTIONS (mixmonitor_opts,{AST_APP_OPTION('a', MUXFLAG_APPEND), AST_APP_OPTION('b', MUXFLAG_BRIDGED), AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME), AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME), AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),})
char * description (void)
 Provides a description of the module.
char * key ()
 Returns the ASTERISK_GPL_KEY.
static void launch_monitor_thread (struct ast_channel *chan, const char *filename, unsigned int flags, int readvol, int writevol, const char *post_process)
int load_module (void)
 Initialize the module.
static int mixmonitor_cli (int fd, int argc, char **argv)
static int mixmonitor_exec (struct ast_channel *chan, void *data)
static void * mixmonitor_thread (void *obj)
static int startmon (struct ast_channel *chan, struct ast_channel_spy *spy)
static void stopmon (struct ast_channel_spy *spy)
int unload_module (void)
 Cleanup all module structures, sockets, etc.
int usecount (void)
 Provides a usecount.

Variables

static const char * app = "MixMonitor"
static struct ast_cli_entry cli_mixmonitor
static const char * desc
 LOCAL_USER_DECL
enum { ... }  mixmonitor_args
enum { ... }  mixmonitor_flags
static const char * mixmonitor_spy_type = "MixMonitor"
 STANDARD_LOCAL_USER
static const char * synopsis = "Record a call and mix the audio during the recording"
static const char * tdesc = "Mixed Audio Monitoring Application"


Detailed Description

MixMonitor() - Record a call and mix the audio during the recording.

Definition in file app_mixmonitor.c.


Define Documentation

#define get_volfactor  )     x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
 

Definition at line 50 of file app_mixmonitor.c.

#define SAMPLES_PER_FRAME   160
 

Definition at line 147 of file app_mixmonitor.c.

Referenced by mixmonitor_thread().


Enumeration Type Documentation

anonymous enum
 

Enumeration values:
MUXFLAG_APPEND 
MUXFLAG_BRIDGED 
MUXFLAG_VOLUME 
MUXFLAG_READVOLUME 
MUXFLAG_WRITEVOLUME 

Definition at line 89 of file app_mixmonitor.c.

00089      {
00090    MUXFLAG_APPEND = (1 << 1),
00091    MUXFLAG_BRIDGED = (1 << 2),
00092    MUXFLAG_VOLUME = (1 << 3),
00093    MUXFLAG_READVOLUME = (1 << 4),
00094    MUXFLAG_WRITEVOLUME = (1 << 5),
00095 } mixmonitor_flags;

anonymous enum
 

Enumeration values:
OPT_ARG_READVOLUME 
OPT_ARG_WRITEVOLUME 
OPT_ARG_VOLUME 
OPT_ARG_ARRAY_SIZE 

Definition at line 97 of file app_mixmonitor.c.


Function Documentation

AST_APP_OPTIONS mixmonitor_opts   ) 
 

char* description void   ) 
 

Provides a description of the module.

Returns:
a short description of your module

Definition at line 449 of file app_mixmonitor.c.

References tdesc.

00450 {
00451    return (char *) tdesc;
00452 }

char* key void   ) 
 

Returns the ASTERISK_GPL_KEY.

This returns the ASTERISK_GPL_KEY, signifiying that you agree to the terms of the GPL stated in the ASTERISK_GPL_KEY. Your module will not load if it does not return the EXACT message:

 char *key(void) {
         return ASTERISK_GPL_KEY;
 }

Returns:
ASTERISK_GPL_KEY

Definition at line 463 of file app_mixmonitor.c.

References ASTERISK_GPL_KEY.

00464 {
00465    return ASTERISK_GPL_KEY;
00466 }

static void launch_monitor_thread struct ast_channel chan,
const char *  filename,
unsigned int  flags,
int  readvol,
int  writevol,
const char *  post_process
[static]
 

Definition at line 214 of file app_mixmonitor.c.

References ast_closestream(), AST_FORMAT_SLINEAR, ast_log(), ast_mutex_destroy(), ast_mutex_init(), ast_pthread_create, ast_set_flag, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_writefile(), calloc, CHANSPY_FORMAT_AUDIO, CHANSPY_MIXAUDIO, CHANSPY_READ_VOLADJUST, CHANSPY_RUNNING, CHANSPY_WRITE_VOLADJUST, mixmonitor::flags, ast_channel_spy_queue::format, free, mixmonitor::fs, ast_channel_spy::lock, LOG_ERROR, LOG_WARNING, mixmonitor_spy_type, mixmonitor_thread(), MUXFLAG_APPEND, mixmonitor::name, ast_channel::name, pbx_substitute_variables_helper(), mixmonitor::post_process, ast_channel_spy::read_queue, ast_channel_spy::read_vol_adjustment, mixmonitor::spy, startmon(), ast_channel_spy::status, ast_channel_spy::type, ast_channel_spy::write_queue, and ast_channel_spy::write_vol_adjustment.

Referenced by mixmonitor_exec().

00216 {
00217    pthread_attr_t attr;
00218    pthread_t thread;
00219    struct mixmonitor *mixmonitor;
00220    char *file_name, *ext;
00221    char postprocess2[1024] = "";
00222    unsigned int oflags;
00223    size_t len;
00224 
00225    len = sizeof(*mixmonitor) + strlen(chan->name) + 1;
00226 
00227    /* If a post process system command is given attach it to the structure */
00228    if (!ast_strlen_zero(post_process)) {
00229       char *p1, *p2;
00230 
00231       p1 = ast_strdupa(post_process);
00232       for (p2 = p1; *p2 ; p2++) {
00233          if (*p2 == '^' && *(p2+1) == '{') {
00234             *p2 = '$';
00235          }
00236       }
00237 
00238       pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00239       if (!ast_strlen_zero(postprocess2))
00240          len += strlen(postprocess2) + 1;
00241    }
00242 
00243    /* Pre-allocate mixmonitor structure and spy */
00244    if (!(mixmonitor = calloc(1, len))) {
00245       ast_log(LOG_ERROR, "Memory Error!\n");
00246       return;
00247    }
00248 
00249    /* Copy over flags and channel name */
00250    mixmonitor->flags = flags;
00251    mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
00252    strcpy(mixmonitor->name, chan->name);
00253    if (!ast_strlen_zero(postprocess2)) {
00254       mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + 1;
00255       strcpy(mixmonitor->post_process, postprocess2);
00256    }
00257 
00258    /* Determine creation flags and filename plus extension for filestream */
00259    oflags = O_CREAT | O_WRONLY;
00260    oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00261    file_name = ast_strdupa(filename);
00262    if ((ext = strrchr(file_name, '.'))) {
00263       *(ext++) = '\0';
00264    } else {
00265       ext = "raw";
00266    }
00267 
00268    /* Move onto actually creating the filestream */
00269    mixmonitor->fs = ast_writefile(file_name, ext, NULL, oflags, 0, 0644);
00270    if (!mixmonitor->fs) {
00271       ast_log(LOG_ERROR, "Cannot open %s.%s\n", file_name, ext);
00272       free(mixmonitor);
00273       return;
00274    }
00275 
00276    /* Setup the actual spy before creating our thread */
00277    ast_set_flag(&mixmonitor->spy, CHANSPY_FORMAT_AUDIO);
00278    ast_set_flag(&mixmonitor->spy, CHANSPY_MIXAUDIO);
00279    mixmonitor->spy.type = mixmonitor_spy_type;
00280    mixmonitor->spy.status = CHANSPY_RUNNING;
00281    mixmonitor->spy.read_queue.format = AST_FORMAT_SLINEAR;
00282    mixmonitor->spy.write_queue.format = AST_FORMAT_SLINEAR;
00283    if (readvol) {
00284       ast_set_flag(&mixmonitor->spy, CHANSPY_READ_VOLADJUST);
00285       mixmonitor->spy.read_vol_adjustment = readvol;
00286    }
00287    if (writevol) {
00288       ast_set_flag(&mixmonitor->spy, CHANSPY_WRITE_VOLADJUST);
00289       mixmonitor->spy.write_vol_adjustment = writevol;
00290    }
00291    ast_mutex_init(&mixmonitor->spy.lock);
00292 
00293    if (startmon(chan, &mixmonitor->spy)) {
00294       ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00295          mixmonitor->spy.type, chan->name);
00296       /* Since we couldn't add ourselves - bail out! */
00297       ast_mutex_destroy(&mixmonitor->spy.lock);
00298       ast_closestream(mixmonitor->fs);
00299       free(mixmonitor);
00300       return;
00301    }
00302 
00303    pthread_attr_init(&attr);
00304    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00305    ast_pthread_create(&thread, &attr, mixmonitor_thread, mixmonitor);
00306    pthread_attr_destroy(&attr);
00307 
00308 }

int load_module void   ) 
 

Initialize the module.

Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.

Returns:
int Always 0.

Definition at line 439 of file app_mixmonitor.c.

References app, ast_cli_register(), ast_register_application(), desc, mixmonitor_exec(), and synopsis.

00440 {
00441    int res;
00442 
00443    res = ast_cli_register(&cli_mixmonitor);
00444    res |= ast_register_application(app, mixmonitor_exec, synopsis, desc);
00445 
00446    return res;
00447 }

static int mixmonitor_cli int  fd,
int  argc,
char **  argv
[static]
 

Definition at line 396 of file app_mixmonitor.c.

References ast_channel_spy_stop_by_type(), ast_cli(), ast_get_channel_by_name_prefix_locked(), ast_mutex_unlock(), ast_channel::lock, mixmonitor_exec(), mixmonitor_spy_type, RESULT_SHOWUSAGE, and RESULT_SUCCESS.

00397 {
00398    struct ast_channel *chan;
00399 
00400    if (argc < 3)
00401       return RESULT_SHOWUSAGE;
00402 
00403    if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
00404       ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
00405       return RESULT_SUCCESS;
00406    }
00407 
00408    if (!strcasecmp(argv[1], "start"))
00409       mixmonitor_exec(chan, argv[3]);
00410    else if (!strcasecmp(argv[1], "stop"))
00411       ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
00412 
00413    ast_mutex_unlock(&chan->lock);
00414 
00415    return RESULT_SUCCESS;
00416 }

static int mixmonitor_exec struct ast_channel chan,
void *  data
[static]
 

Definition at line 310 of file app_mixmonitor.c.

References AST_APP_ARG, ast_app_parse_options(), ast_config_AST_MONITOR_DIR, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_flags::flags, get_volfactor, launch_monitor_thread(), LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_NOTICE, LOG_WARNING, MUXFLAG_READVOLUME, MUXFLAG_VOLUME, MUXFLAG_WRITEVOLUME, OPT_ARG_ARRAY_SIZE, OPT_ARG_READVOLUME, OPT_ARG_VOLUME, OPT_ARG_WRITEVOLUME, parse(), and pbx_builtin_setvar_helper().

Referenced by load_module(), and mixmonitor_cli().

00311 {
00312    int x, readvol = 0, writevol = 0;
00313    struct localuser *u;
00314    struct ast_flags flags = {0};
00315    char *parse;
00316    AST_DECLARE_APP_ARGS(args,
00317       AST_APP_ARG(filename);
00318       AST_APP_ARG(options);
00319       AST_APP_ARG(post_process);
00320    );
00321    
00322    if (ast_strlen_zero(data)) {
00323       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00324       return -1;
00325    }
00326 
00327    LOCAL_USER_ADD(u);
00328 
00329    if (!(parse = ast_strdupa(data))) {
00330       ast_log(LOG_WARNING, "Memory Error!\n");
00331       LOCAL_USER_REMOVE(u);
00332       return -1;
00333    }
00334 
00335    AST_STANDARD_APP_ARGS(args, parse);
00336    
00337    if (ast_strlen_zero(args.filename)) {
00338       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
00339       LOCAL_USER_REMOVE(u);
00340       return -1;
00341    }
00342 
00343    if (args.options) {
00344       char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
00345 
00346       ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
00347 
00348       if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
00349          if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
00350             ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
00351          } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
00352             ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
00353          } else {
00354             readvol = get_volfactor(x);
00355          }
00356       }
00357       
00358       if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
00359          if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
00360             ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
00361          } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
00362             ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
00363          } else {
00364             writevol = get_volfactor(x);
00365          }
00366       }
00367       
00368       if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
00369          if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
00370             ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
00371          } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
00372             ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
00373          } else {
00374             readvol = writevol = get_volfactor(x);
00375          }
00376       }
00377    }
00378 
00379    /* if not provided an absolute path, use the system-configured monitoring directory */
00380    if (args.filename[0] != '/') {
00381       char *build;
00382 
00383       build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
00384       sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
00385       args.filename = build;
00386    }
00387 
00388    pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
00389    launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
00390 
00391    LOCAL_USER_REMOVE(u);
00392 
00393    return 0;
00394 }

static void* mixmonitor_thread void *  obj  )  [static]
 

Definition at line 149 of file app_mixmonitor.c.

References ast_bridged_channel(), ast_channel_spy_read_frame(), ast_channel_spy_trigger_wait(), ast_closestream(), ast_frfree(), ast_mutex_destroy(), ast_mutex_lock(), ast_mutex_unlock(), ast_safe_system(), ast_test_flag, ast_verbose(), ast_writestream(), ast_channel_spy::chan, CHANSPY_RUNNING, free, mixmonitor::fs, ast_channel_spy::lock, MUXFLAG_BRIDGED, mixmonitor::name, ast_frame::next, option_verbose, mixmonitor::post_process, SAMPLES_PER_FRAME, mixmonitor::spy, STANDARD_DECREMENT_USECOUNT, STANDARD_INCREMENT_USECOUNT, ast_channel_spy::status, stopmon(), and VERBOSE_PREFIX_2.

Referenced by launch_monitor_thread().

00150 {
00151    struct mixmonitor *mixmonitor = obj;
00152    struct ast_frame *f = NULL;
00153    
00154    STANDARD_INCREMENT_USECOUNT;
00155    
00156    if (option_verbose > 1)
00157       ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name);
00158    
00159    ast_mutex_lock(&mixmonitor->spy.lock);
00160 
00161    while (mixmonitor->spy.chan) {
00162       struct ast_frame *next;
00163       int write;
00164 
00165       ast_channel_spy_trigger_wait(&mixmonitor->spy);
00166       
00167       if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING) {
00168          break;
00169       }
00170       
00171       while (1) {
00172          if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME)))
00173             break;
00174 
00175          write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
00176              ast_bridged_channel(mixmonitor->spy.chan));
00177 
00178          /* it is possible for ast_channel_spy_read_frame() to return a chain
00179             of frames if a queue flush was necessary, so process them
00180          */
00181          for (; f; f = next) {
00182             next = f->next;
00183             if (write)
00184                ast_writestream(mixmonitor->fs, f);
00185             ast_frfree(f);
00186          }
00187       }
00188    }
00189 
00190    ast_mutex_unlock(&mixmonitor->spy.lock);
00191    
00192    stopmon(&mixmonitor->spy);
00193 
00194    if (option_verbose > 1)
00195       ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name);
00196 
00197    if (mixmonitor->post_process) {
00198       if (option_verbose > 2)
00199          ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process);
00200       ast_safe_system(mixmonitor->post_process);
00201    }
00202 
00203    ast_mutex_destroy(&mixmonitor->spy.lock);
00204       
00205    ast_closestream(mixmonitor->fs);
00206 
00207    free(mixmonitor);
00208 
00209    STANDARD_DECREMENT_USECOUNT;
00210 
00211    return NULL;
00212 }

static int startmon struct ast_channel chan,
struct ast_channel_spy spy
[static]
 

Definition at line 129 of file app_mixmonitor.c.

References ast_bridged_channel(), ast_channel_spy_add(), AST_FLAG_NBRIDGE, ast_mutex_lock(), ast_mutex_unlock(), ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, ast_test_flag, and ast_channel::lock.

Referenced by launch_monitor_thread().

00130 {
00131    struct ast_channel *peer;
00132    int res;
00133 
00134    if (!chan)
00135       return -1;
00136 
00137    ast_mutex_lock(&chan->lock);
00138    res = ast_channel_spy_add(chan, spy);
00139    ast_mutex_unlock(&chan->lock);
00140 
00141    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
00142       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
00143 
00144    return res;
00145 }

static void stopmon struct ast_channel_spy spy  )  [static]
 

Definition at line 112 of file app_mixmonitor.c.

References ast_channel_spy_remove(), ast_mutex_lock(), ast_mutex_unlock(), ast_channel_spy::chan, CHANSPY_DONE, ast_channel::lock, and ast_channel_spy::status.

Referenced by mixmonitor_thread().

00113 {
00114    struct ast_channel *chan = spy->chan;
00115 
00116    /* If our status has changed to DONE, then the channel we're spying on is gone....
00117       DON'T TOUCH IT!!!  RUN AWAY!!! */
00118    if (spy->status == CHANSPY_DONE)
00119       return;
00120   
00121    if (!chan)
00122       return;
00123 
00124    ast_mutex_lock(&chan->lock);
00125    ast_channel_spy_remove(chan, spy);
00126    ast_mutex_unlock(&chan->lock);
00127 }

int unload_module void   ) 
 

Cleanup all module structures, sockets, etc.

This is called at exit. Any registrations and memory allocations need to be unregistered and free'd here. Nothing else will do these for you (until exit).

Returns:
Zero on success, or non-zero on error.

Definition at line 427 of file app_mixmonitor.c.

References app, ast_cli_unregister(), ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS.

00428 {
00429    int res;
00430 
00431    res = ast_cli_unregister(&cli_mixmonitor);
00432    res |= ast_unregister_application(app);
00433    
00434    STANDARD_HANGUP_LOCALUSERS;
00435 
00436    return res;
00437 }

int usecount void   ) 
 

Provides a usecount.

This function will be called by various parts of asterisk. Basically, all it has to do is to return a usecount when called. You will need to maintain your usecount within the module somewhere. The usecount should be how many channels provided by this module are in use.

Returns:
The module's usecount.

Definition at line 454 of file app_mixmonitor.c.

References STANDARD_USECOUNT.

00455 {
00456    int res;
00457 
00458    STANDARD_USECOUNT(res);
00459 
00460    return res;
00461 }


Variable Documentation

const char* app = "MixMonitor" [static]
 

Definition at line 53 of file app_mixmonitor.c.

struct ast_cli_entry cli_mixmonitor [static]
 

Definition at line 419 of file app_mixmonitor.c.

const char* desc [static]
 

Definition at line 55 of file app_mixmonitor.c.

LOCAL_USER_DECL
 

Definition at line 77 of file app_mixmonitor.c.

enum { ... } mixmonitor_args
 

enum { ... } mixmonitor_flags
 

const char* mixmonitor_spy_type = "MixMonitor" [static]
 

Definition at line 79 of file app_mixmonitor.c.

Referenced by launch_monitor_thread(), and mixmonitor_cli().

STANDARD_LOCAL_USER
 

Definition at line 75 of file app_mixmonitor.c.

const char* synopsis = "Record a call and mix the audio during the recording" [static]
 

Definition at line 54 of file app_mixmonitor.c.

const char* tdesc = "Mixed Audio Monitoring Application" [static]
 

Definition at line 52 of file app_mixmonitor.c.


Generated on Sun Aug 6 15:03:53 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.2