Fri May 26 01:46:02 2006

Asterisk developer's documentation


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.

Data Structures

struct  mixmonitor

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 *chan, 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
struct {
   int   alarm
   char *   description
   unsigned int   event_log:1
   enum queue_result   id
   char *   name
   char *   name
   char *   name
   rtpPayloadType   payloadType
   unsigned int   queue_log:1
   char *   subtype
   char *   text
   char *   type
   int   val
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 146 of file app_mixmonitor.c.

Referenced by mixmonitor_thread().


Enumeration Type Documentation

anonymous enum
 

Enumerator:
MUXFLAG_APPEND 
MUXFLAG_BRIDGED 
MUXFLAG_VOLUME 
MUXFLAG_READVOLUME 
MUXFLAG_WRITEVOLUME 

Definition at line 90 of file app_mixmonitor.c.

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

anonymous enum
 

Enumerator:
OPT_ARG_READVOLUME 
OPT_ARG_WRITEVOLUME 
OPT_ARG_VOLUME 
OPT_ARG_ARRAY_SIZE 

Definition at line 98 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.

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 276 of file app_mixmonitor.c.

References ast_log(), ast_pthread_create, ast_strlen_zero(), calloc, mixmonitor::chan, mixmonitor::filename, LOG_ERROR, and mixmonitor_thread().

Referenced by mixmonitor_exec().

00278 {
00279    pthread_attr_t attr;
00280    pthread_t thread;
00281    struct mixmonitor *mixmonitor;
00282    int len;
00283 
00284    len = sizeof(*mixmonitor) + strlen(filename) + 1;
00285    if (!ast_strlen_zero(post_process))
00286       len += strlen(post_process) + 1;
00287 
00288    if (!(mixmonitor = calloc(1, len))) {
00289       ast_log(LOG_ERROR, "Memory Error!\n");
00290       return;
00291    }
00292 
00293    mixmonitor->chan = chan;
00294    mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor);
00295    strcpy(mixmonitor->filename, filename);
00296    if (!ast_strlen_zero(post_process)) {
00297       mixmonitor->post_process = mixmonitor->filename + strlen(filename) + 1;
00298       strcpy(mixmonitor->post_process, post_process);
00299    }
00300    mixmonitor->readvol = readvol;
00301    mixmonitor->writevol = writevol;
00302    mixmonitor->flags = flags;
00303 
00304    pthread_attr_init(&attr);
00305    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00306    ast_pthread_create(&thread, &attr, mixmonitor_thread, mixmonitor);
00307    pthread_attr_destroy(&attr);
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 ast_cli_register(), ast_register_application(), cli_mixmonitor, and mixmonitor_exec().

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(), 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(), pbx_builtin_setvar_helper(), mixmonitor::readvol, and mixmonitor::writevol.

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 148 of file app_mixmonitor.c.

References ast_bridged_channel(), ast_channel_spy_read_frame(), ast_channel_spy_trigger_wait(), ast_check_hangup(), AST_FORMAT_SLINEAR, ast_frfree(), ast_log(), ast_mutex_init(), ast_mutex_lock(), ast_mutex_unlock(), ast_seekstream(), ast_set_flag, ast_strdupa, ast_test_flag, ast_verbose(), ast_writefile(), ast_writestream(), mixmonitor::chan, CHANSPY_FORMAT_AUDIO, CHANSPY_MIXAUDIO, CHANSPY_READ_VOLADJUST, CHANSPY_RUNNING, CHANSPY_WRITE_VOLADJUST, mixmonitor::filename, LOG_ERROR, LOG_WARNING, MUXFLAG_APPEND, MUXFLAG_BRIDGED, ast_channel::name, name, ast_frame::next, option_verbose, pbx_substitute_variables_helper(), mixmonitor::post_process, mixmonitor::readvol, SAMPLES_PER_FRAME, STANDARD_INCREMENT_USECOUNT, startmon(), VERBOSE_PREFIX_2, and mixmonitor::writevol.

Referenced by launch_monitor_thread().

00149 {
00150    struct mixmonitor *mixmonitor = obj;
00151    struct ast_channel_spy spy;
00152    struct ast_filestream *fs = NULL;
00153    char *ext, *name;
00154    unsigned int oflags;
00155    struct ast_frame *f;
00156    char post_process[1024] = "";
00157    
00158    STANDARD_INCREMENT_USECOUNT;
00159 
00160    name = ast_strdupa(mixmonitor->chan->name);
00161 
00162    oflags = O_CREAT|O_WRONLY;
00163    oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00164       
00165    if ((ext = strrchr(mixmonitor->filename, '.'))) {
00166       *(ext++) = '\0';
00167    } else {
00168       ext = "raw";
00169    }
00170 
00171    fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644);
00172    if (!fs) {
00173       ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
00174       goto out;
00175    }
00176 
00177    if (ast_test_flag(mixmonitor, MUXFLAG_APPEND))
00178       ast_seekstream(fs, 0, SEEK_END);
00179    
00180    memset(&spy, 0, sizeof(spy));
00181    ast_set_flag(&spy, CHANSPY_FORMAT_AUDIO);
00182    ast_set_flag(&spy, CHANSPY_MIXAUDIO);
00183    spy.type = mixmonitor_spy_type;
00184    spy.status = CHANSPY_RUNNING;
00185    spy.read_queue.format = AST_FORMAT_SLINEAR;
00186    spy.write_queue.format = AST_FORMAT_SLINEAR;
00187    if (mixmonitor->readvol) {
00188       ast_set_flag(&spy, CHANSPY_READ_VOLADJUST);
00189       spy.read_vol_adjustment = mixmonitor->readvol;
00190    }
00191    if (mixmonitor->writevol) {
00192       ast_set_flag(&spy, CHANSPY_WRITE_VOLADJUST);
00193       spy.write_vol_adjustment = mixmonitor->writevol;
00194    }
00195    ast_mutex_init(&spy.lock);
00196 
00197    if (startmon(mixmonitor->chan, &spy)) {
00198       ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00199          spy.type, mixmonitor->chan->name);
00200       goto out2;
00201    }
00202 
00203    if (option_verbose > 1)
00204       ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", name);
00205    
00206    if (mixmonitor->post_process) {
00207       char *p;
00208 
00209       for (p = mixmonitor->post_process; *p ; p++) {
00210          if (*p == '^' && *(p+1) == '{') {
00211             *p = '$';
00212          }
00213       }
00214       pbx_substitute_variables_helper(mixmonitor->chan, mixmonitor->post_process, post_process, sizeof(post_process) - 1);
00215    }
00216 
00217    while (1) {
00218       struct ast_frame *next;
00219       int write;
00220 
00221       ast_mutex_lock(&spy.lock);
00222 
00223       ast_channel_spy_trigger_wait(&spy);
00224       
00225       if (ast_check_hangup(mixmonitor->chan) || spy.status != CHANSPY_RUNNING) {
00226          ast_mutex_unlock(&spy.lock);
00227          break;
00228       }
00229       
00230       while (1) {
00231          if (!(f = ast_channel_spy_read_frame(&spy, SAMPLES_PER_FRAME)))
00232             break;
00233 
00234          write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
00235              ast_bridged_channel(mixmonitor->chan));
00236 
00237          /* it is possible for ast_channel_spy_read_frame() to return a chain
00238             of frames if a queue flush was necessary, so process them
00239          */
00240          for (; f; f = next) {
00241             next = f->next;
00242             if (write)
00243                ast_writestream(fs, f);
00244             ast_frfree(f);
00245          }
00246       }
00247 
00248       ast_mutex_unlock(&spy.lock);
00249    }
00250    
00251    stopmon(mixmonitor->chan, &spy);
00252 
00253    if (option_verbose > 1)
00254       ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", name);
00255 
00256    if (!ast_strlen_zero(post_process)) {
00257       if (option_verbose > 2)
00258          ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", post_process);
00259       ast_safe_system(post_process);
00260    }
00261 
00262 out2:
00263    ast_mutex_destroy(&spy.lock);
00264 
00265    if (fs)
00266       ast_closestream(fs);
00267 
00268 out:
00269    free(mixmonitor);
00270 
00271    STANDARD_DECREMENT_USECOUNT;
00272 
00273    return NULL;
00274 }

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

Definition at line 128 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 mixmonitor_thread().

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

static void stopmon struct ast_channel chan,
struct ast_channel_spy spy
[static]
 

Definition at line 113 of file app_mixmonitor.c.

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

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

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 ast_cli_unregister(), ast_unregister_application(), cli_mixmonitor, 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.

Referenced by load_module(), and unload_module().

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.

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 Fri May 26 01:46:03 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6