Sat Sep 16 05:47:49 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"

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)
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 130 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 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

Enumerator:
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 429 of file app_mixmonitor.c.

00430 {
00431    return (char *) tdesc;
00432 }

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

References ASTERISK_GPL_KEY.

00444 {
00445    return ASTERISK_GPL_KEY;
00446 }

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 194 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, free, LOG_ERROR, LOG_WARNING, mixmonitor_thread(), MUXFLAG_APPEND, mixmonitor::name, ast_channel::name, pbx_substitute_variables_helper(), and startmon().

Referenced by mixmonitor_exec().

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

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

References ast_cli_register(), ast_register_application(), cli_mixmonitor, and mixmonitor_exec().

00420 {
00421    int res;
00422 
00423    res = ast_cli_register(&cli_mixmonitor);
00424    res |= ast_register_application(app, mixmonitor_exec, synopsis, desc);
00425 
00426    return res;
00427 }

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

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

00377 {
00378    struct ast_channel *chan;
00379 
00380    if (argc < 3)
00381       return RESULT_SHOWUSAGE;
00382 
00383    if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
00384       ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
00385       return RESULT_SUCCESS;
00386    }
00387 
00388    if (!strcasecmp(argv[1], "start"))
00389       mixmonitor_exec(chan, argv[3]);
00390    else if (!strcasecmp(argv[1], "stop"))
00391       ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
00392 
00393    ast_mutex_unlock(&chan->lock);
00394 
00395    return RESULT_SUCCESS;
00396 }

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

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

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

static void* mixmonitor_thread ( void *  obj  )  [static]

Definition at line 132 of file app_mixmonitor.c.

References ast_bridged_channel(), ast_channel_spy_free(), ast_channel_spy_read_frame(), ast_channel_spy_trigger_wait(), ast_closestream(), ast_frfree(), 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, and VERBOSE_PREFIX_2.

Referenced by launch_monitor_thread().

00133 {
00134    struct mixmonitor *mixmonitor = obj;
00135    struct ast_frame *f = NULL;
00136    
00137    STANDARD_INCREMENT_USECOUNT;
00138    
00139    if (option_verbose > 1)
00140       ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name);
00141    
00142    ast_mutex_lock(&mixmonitor->spy.lock);
00143 
00144    while (mixmonitor->spy.chan) {
00145       struct ast_frame *next;
00146       int write;
00147 
00148       ast_channel_spy_trigger_wait(&mixmonitor->spy);
00149       
00150       if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING)
00151          break;
00152       
00153       while (1) {
00154          if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME)))
00155             break;
00156 
00157          write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
00158              ast_bridged_channel(mixmonitor->spy.chan));
00159 
00160          /* it is possible for ast_channel_spy_read_frame() to return a chain
00161             of frames if a queue flush was necessary, so process them
00162          */
00163          for (; f; f = next) {
00164             next = f->next;
00165             if (write)
00166                ast_writestream(mixmonitor->fs, f);
00167             ast_frfree(f);
00168          }
00169       }
00170    }
00171 
00172    ast_mutex_unlock(&mixmonitor->spy.lock);
00173    
00174    ast_channel_spy_free(&mixmonitor->spy);
00175 
00176    if (option_verbose > 1)
00177       ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name);
00178 
00179    if (mixmonitor->post_process) {
00180       if (option_verbose > 2)
00181          ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process);
00182       ast_safe_system(mixmonitor->post_process);
00183    }
00184       
00185    ast_closestream(mixmonitor->fs);
00186 
00187    free(mixmonitor);
00188 
00189    STANDARD_DECREMENT_USECOUNT;
00190 
00191    return NULL;
00192 }

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

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

00113 {
00114    struct ast_channel *peer;
00115    int res;
00116 
00117    if (!chan)
00118       return -1;
00119 
00120    ast_mutex_lock(&chan->lock);
00121    res = ast_channel_spy_add(chan, spy);
00122    ast_mutex_unlock(&chan->lock);
00123 
00124    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
00125       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
00126 
00127    return res;
00128 }

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

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

00408 {
00409    int res;
00410 
00411    res = ast_cli_unregister(&cli_mixmonitor);
00412    res |= ast_unregister_application(app);
00413    
00414    STANDARD_HANGUP_LOCALUSERS;
00415 
00416    return res;
00417 }

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

References STANDARD_USECOUNT.

00435 {
00436    int res;
00437 
00438    STANDARD_USECOUNT(res);
00439 
00440    return res;
00441 }


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 399 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 Sat Sep 16 05:47:50 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7