Mon Mar 31 07:38:31 2008

Asterisk developer's documentation


app_mixmonitor.c File Reference

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

#include "asterisk.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/audiohook.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 "asterisk/utils.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),})
 AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY,"Mixed Audio Monitoring Application")
static char * complete_mixmonitor_cli (const char *line, const char *word, int pos, int state)
static void launch_monitor_thread (struct ast_channel *chan, const char *filename, unsigned int flags, int readvol, int writevol, const char *post_process)
static int load_module (void)
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_audiohook *audiohook)
static int stop_mixmonitor_exec (struct ast_channel *chan, void *data)
static int unload_module (void)

Variables

static const char * app = "MixMonitor"
static struct ast_cli_entry cli_mixmonitor []
static const char * desc
module_symbols * me
enum { ... }  mixmonitor_args
enum { ... }  mixmonitor_flags
static const char * mixmonitor_spy_type = "MixMonitor"
static const char * stop_app = "StopMixMonitor"
static const char * stop_desc
static const char * stop_synopsis = "Stop recording a call through MixMonitor"
static const char * synopsis = "Record a call and mix the audio during the recording"


Detailed Description

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

Author:
Mark Spencer <markster@digium.com>

Kevin P. Fleming <kpfleming@digium.com>

Note:
Based on app_muxmon.c provided by Anthony Minessale II <anthmct@yahoo.com>

Definition in file app_mixmonitor.c.


Define Documentation

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

Definition at line 58 of file app_mixmonitor.c.

Referenced by mixmonitor_exec().

#define SAMPLES_PER_FRAME   160

Definition at line 143 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 104 of file app_mixmonitor.c.

00104      {
00105    MUXFLAG_APPEND = (1 << 1),
00106    MUXFLAG_BRIDGED = (1 << 2),
00107    MUXFLAG_VOLUME = (1 << 3),
00108    MUXFLAG_READVOLUME = (1 << 4),
00109    MUXFLAG_WRITEVOLUME = (1 << 5),
00110 } mixmonitor_flags;

anonymous enum

Enumerator:
OPT_ARG_READVOLUME 
OPT_ARG_WRITEVOLUME 
OPT_ARG_VOLUME 
OPT_ARG_ARRAY_SIZE 

Definition at line 112 of file app_mixmonitor.c.


Function Documentation

AST_APP_OPTIONS ( mixmonitor_opts   ) 

AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY  ,
"Mixed Audio Monitoring Application"   
)

static char* complete_mixmonitor_cli ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 408 of file app_mixmonitor.c.

References ast_complete_channels().

00409 {
00410    return ast_complete_channels(line, word, pos, state, 2);
00411 }

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

References ast_audiohook_destroy(), ast_audiohook_init(), AST_AUDIOHOOK_TRIGGER_SYNC, AST_AUDIOHOOK_TYPE_SPY, ast_log(), ast_pthread_create_background, ast_set_flag, ast_strdupa, ast_strlen_zero(), calloc, mixmonitor::chan, mixmonitor::flags, free, len, LOG_WARNING, mixmonitor_thread(), mixmonitor::name, pbx_substitute_variables_helper(), startmon(), and thread.

Referenced by mixmonitor_exec().

00219 {
00220    pthread_attr_t attr;
00221    pthread_t thread;
00222    struct mixmonitor *mixmonitor;
00223    char postprocess2[1024] = "";
00224    size_t len;
00225 
00226    len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
00227 
00228    /* If a post process system command is given attach it to the structure */
00229    if (!ast_strlen_zero(post_process)) {
00230       char *p1, *p2;
00231 
00232       p1 = ast_strdupa(post_process);
00233       for (p2 = p1; *p2 ; p2++) {
00234          if (*p2 == '^' && *(p2+1) == '{') {
00235             *p2 = '$';
00236          }
00237       }
00238 
00239       pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00240       if (!ast_strlen_zero(postprocess2))
00241          len += strlen(postprocess2) + 1;
00242    }
00243 
00244    /* Pre-allocate mixmonitor structure and spy */
00245    if (!(mixmonitor = calloc(1, len))) {
00246       return;
00247    }
00248 
00249    /* Copy over flags and channel name */
00250    mixmonitor->flags = flags;
00251    mixmonitor->chan = chan;
00252    mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
00253    strcpy(mixmonitor->name, chan->name);
00254    if (!ast_strlen_zero(postprocess2)) {
00255       mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
00256       strcpy(mixmonitor->post_process, postprocess2);
00257    }
00258 
00259    mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
00260    strcpy(mixmonitor->filename, filename);
00261 
00262    /* Setup the actual spy before creating our thread */
00263    if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
00264       free(mixmonitor);
00265       return;
00266    }
00267    
00268    ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
00269    
00270    if (readvol)
00271       mixmonitor->audiohook.options.read_volume = readvol;
00272    if (writevol)
00273       mixmonitor->audiohook.options.write_volume = writevol;
00274 
00275    if (startmon(chan, &mixmonitor->audiohook)) {
00276       ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00277          mixmonitor_spy_type, chan->name);
00278       /* Since we couldn't add ourselves - bail out! */
00279       ast_audiohook_destroy(&mixmonitor->audiohook);
00280       free(mixmonitor);
00281       return;
00282    }
00283 
00284    pthread_attr_init(&attr);
00285    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00286    ast_pthread_create_background(&thread, &attr, mixmonitor_thread, mixmonitor);
00287    pthread_attr_destroy(&attr);
00288 
00289 }

static int load_module ( void   )  [static]

Definition at line 435 of file app_mixmonitor.c.

References ast_cli_register_multiple(), ast_register_application(), cli_mixmonitor, mixmonitor_exec(), and stop_mixmonitor_exec().

00436 {
00437    int res;
00438 
00439    ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
00440    res = ast_register_application(app, mixmonitor_exec, synopsis, desc);
00441    res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc);
00442 
00443    return res;
00444 }

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

Definition at line 386 of file app_mixmonitor.c.

References ast_audiohook_detach_source(), ast_channel_unlock, ast_cli(), ast_get_channel_by_name_prefix_locked(), mixmonitor_exec(), RESULT_SHOWUSAGE, and RESULT_SUCCESS.

00387 {
00388    struct ast_channel *chan;
00389 
00390    if (argc < 3)
00391       return RESULT_SHOWUSAGE;
00392 
00393    if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
00394       ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
00395       return RESULT_SUCCESS;
00396    }
00397 
00398    if (!strcasecmp(argv[1], "start"))
00399       mixmonitor_exec(chan, argv[3]);
00400    else if (!strcasecmp(argv[1], "stop"))
00401       ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00402 
00403    ast_channel_unlock(chan);
00404 
00405    return RESULT_SUCCESS;
00406 }

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

Definition at line 291 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_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_flags::flags, get_volfactor, launch_monitor_thread(), 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().

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

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

Definition at line 145 of file app_mixmonitor.c.

References ast_audiohook_destroy(), ast_audiohook_detach(), AST_AUDIOHOOK_DIRECTION_BOTH, ast_audiohook_lock, ast_audiohook_read_frame(), AST_AUDIOHOOK_STATUS_RUNNING, ast_audiohook_trigger_wait(), ast_audiohook_unlock, ast_bridged_channel(), ast_closestream(), AST_FORMAT_SLINEAR, ast_frame_free(), ast_log(), ast_safe_system(), ast_test_flag, ast_verbose(), ast_writefile(), ast_writestream(), mixmonitor::audiohook, mixmonitor::chan, ext, mixmonitor::filename, free, LOG_ERROR, MUXFLAG_APPEND, MUXFLAG_BRIDGED, mixmonitor::name, option_verbose, mixmonitor::post_process, SAMPLES_PER_FRAME, ast_audiohook::status, and VERBOSE_PREFIX_2.

Referenced by launch_monitor_thread().

00146 {
00147    struct mixmonitor *mixmonitor = obj;
00148    struct ast_filestream *fs = NULL;
00149    unsigned int oflags;
00150    char *ext;
00151    int errflag = 0;
00152 
00153    if (option_verbose > 1)
00154       ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name);
00155    
00156    ast_audiohook_lock(&mixmonitor->audiohook);
00157 
00158    while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00159       struct ast_frame *fr = NULL;
00160       
00161       ast_audiohook_trigger_wait(&mixmonitor->audiohook);
00162       
00163       if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
00164          break;
00165       
00166       if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR)))
00167          continue;
00168       
00169       if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || ast_bridged_channel(mixmonitor->chan)) {
00170          /* Initialize the file if not already done so */
00171          if (!fs && !errflag) {
00172             oflags = O_CREAT | O_WRONLY;
00173             oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00174             
00175             if ((ext = strrchr(mixmonitor->filename, '.')))
00176                *(ext++) = '\0';
00177             else
00178                ext = "raw";
00179             
00180             if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) {
00181                ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
00182                errflag = 1;
00183             }
00184          }
00185 
00186          /* Write out the frame */
00187          if (fs)
00188             ast_writestream(fs, fr);
00189       }
00190 
00191       /* All done! free it. */
00192       ast_frame_free(fr, 0);
00193    }
00194 
00195    ast_audiohook_detach(&mixmonitor->audiohook);
00196    ast_audiohook_unlock(&mixmonitor->audiohook);
00197    ast_audiohook_destroy(&mixmonitor->audiohook);
00198    
00199    if (option_verbose > 1)
00200       ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name);
00201 
00202    if (fs)
00203       ast_closestream(fs);
00204 
00205    if (mixmonitor->post_process) {
00206       if (option_verbose > 2)
00207          ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process);
00208       ast_safe_system(mixmonitor->post_process);
00209    }
00210 
00211    free(mixmonitor);
00212 
00213 
00214    return NULL;
00215 }

static int startmon ( struct ast_channel chan,
struct ast_audiohook audiohook 
) [static]

Definition at line 127 of file app_mixmonitor.c.

References ast_audiohook_attach(), ast_bridged_channel(), AST_FLAG_NBRIDGE, ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, and ast_test_flag.

Referenced by launch_monitor_thread().

00128 {
00129    struct ast_channel *peer;
00130    int res;
00131 
00132    if (!chan)
00133       return -1;
00134 
00135    res = ast_audiohook_attach(chan, audiohook);
00136 
00137    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
00138       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
00139 
00140    return res;
00141 }

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

Definition at line 373 of file app_mixmonitor.c.

References ast_audiohook_detach_source(), ast_module_user_add, ast_module_user_remove, and ast_module_user::chan.

Referenced by load_module().

00374 {
00375    struct ast_module_user *u;
00376 
00377    u = ast_module_user_add(chan);
00378 
00379    ast_audiohook_detach_source(chan, mixmonitor_spy_type);
00380 
00381    ast_module_user_remove(u);
00382 
00383    return 0;
00384 }

static int unload_module ( void   )  [static]

Definition at line 422 of file app_mixmonitor.c.

References ast_cli_unregister_multiple(), ast_module_user_hangup_all, ast_unregister_application(), and cli_mixmonitor.

00423 {
00424    int res;
00425 
00426    ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
00427    res = ast_unregister_application(stop_app);
00428    res |= ast_unregister_application(app);
00429    
00430    ast_module_user_hangup_all();
00431 
00432    return res;
00433 }


Variable Documentation

const char* app = "MixMonitor" [static]

Definition at line 60 of file app_mixmonitor.c.

struct ast_cli_entry cli_mixmonitor[] [static]

Definition at line 413 of file app_mixmonitor.c.

Referenced by load_module(), and unload_module().

const char* desc [static]

Definition at line 62 of file app_mixmonitor.c.

struct module_symbols* me

Definition at line 91 of file app_mixmonitor.c.

enum { ... } mixmonitor_args

enum { ... } mixmonitor_flags

const char* mixmonitor_spy_type = "MixMonitor" [static]

Definition at line 93 of file app_mixmonitor.c.

const char* stop_app = "StopMixMonitor" [static]

Definition at line 83 of file app_mixmonitor.c.

const char* stop_desc [static]

Initial value:

 ""
"  StopMixMonitor()\n\n"
"Stops the audio recording that was started with a call to MixMonitor()\n"
"on the current channel.\n"
""

Definition at line 85 of file app_mixmonitor.c.

const char* stop_synopsis = "Stop recording a call through MixMonitor" [static]

Definition at line 84 of file app_mixmonitor.c.

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

Definition at line 61 of file app_mixmonitor.c.


Generated on Mon Mar 31 07:38:31 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1