Sat Apr 12 07:12:30 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/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 "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_channel_spy *spy)
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
struct 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 144 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 103 of file app_mixmonitor.c.

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

anonymous enum

Enumerator:
OPT_ARG_READVOLUME 
OPT_ARG_WRITEVOLUME 
OPT_ARG_VOLUME 
OPT_ARG_ARRAY_SIZE 

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

References ast_complete_channels().

00426 {
00427    return ast_complete_channels(line, word, pos, state, 2);
00428 }

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

References AST_FORMAT_SLINEAR, ast_log(), ast_mutex_destroy(), ast_mutex_init(), ast_pthread_create_background, ast_set_flag, ast_strdupa, ast_strlen_zero(), calloc, CHANSPY_FORMAT_AUDIO, CHANSPY_MIXAUDIO, CHANSPY_READ_VOLADJUST, CHANSPY_RUNNING, CHANSPY_WRITE_VOLADJUST, mixmonitor::filename, mixmonitor::flags, ast_channel_spy_queue::format, free, len, ast_channel_spy::lock, LOG_WARNING, mixmonitor_thread(), mixmonitor::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, thread, ast_channel_spy::type, ast_channel_spy::write_queue, and ast_channel_spy::write_vol_adjustment.

Referenced by mixmonitor_exec().

00231 {
00232    pthread_attr_t attr;
00233    pthread_t thread;
00234    struct mixmonitor *mixmonitor;
00235    char postprocess2[1024] = "";
00236    size_t len;
00237 
00238    len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
00239 
00240    /* If a post process system command is given attach it to the structure */
00241    if (!ast_strlen_zero(post_process)) {
00242       char *p1, *p2;
00243 
00244       p1 = ast_strdupa(post_process);
00245       for (p2 = p1; *p2 ; p2++) {
00246          if (*p2 == '^' && *(p2+1) == '{') {
00247             *p2 = '$';
00248          }
00249       }
00250 
00251       pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00252       if (!ast_strlen_zero(postprocess2))
00253          len += strlen(postprocess2) + 1;
00254    }
00255 
00256    /* Pre-allocate mixmonitor structure and spy */
00257    if (!(mixmonitor = calloc(1, len))) {
00258       return;
00259    }
00260 
00261    /* Copy over flags and channel name */
00262    mixmonitor->flags = flags;
00263    mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
00264    strcpy(mixmonitor->name, chan->name);
00265    if (!ast_strlen_zero(postprocess2)) {
00266       mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
00267       strcpy(mixmonitor->post_process, postprocess2);
00268    }
00269 
00270    mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
00271    strcpy(mixmonitor->filename, filename);
00272 
00273    /* Setup the actual spy before creating our thread */
00274    ast_set_flag(&mixmonitor->spy, CHANSPY_FORMAT_AUDIO);
00275    ast_set_flag(&mixmonitor->spy, CHANSPY_MIXAUDIO);
00276    mixmonitor->spy.type = mixmonitor_spy_type;
00277    mixmonitor->spy.status = CHANSPY_RUNNING;
00278    mixmonitor->spy.read_queue.format = AST_FORMAT_SLINEAR;
00279    mixmonitor->spy.write_queue.format = AST_FORMAT_SLINEAR;
00280    if (readvol) {
00281       ast_set_flag(&mixmonitor->spy, CHANSPY_READ_VOLADJUST);
00282       mixmonitor->spy.read_vol_adjustment = readvol;
00283    }
00284    if (writevol) {
00285       ast_set_flag(&mixmonitor->spy, CHANSPY_WRITE_VOLADJUST);
00286       mixmonitor->spy.write_vol_adjustment = writevol;
00287    }
00288    ast_mutex_init(&mixmonitor->spy.lock);
00289 
00290    if (startmon(chan, &mixmonitor->spy)) {
00291       ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00292          mixmonitor->spy.type, chan->name);
00293       /* Since we couldn't add ourselves - bail out! */
00294       ast_mutex_destroy(&mixmonitor->spy.lock);
00295       free(mixmonitor);
00296       return;
00297    }
00298 
00299    pthread_attr_init(&attr);
00300    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00301    ast_pthread_create_background(&thread, &attr, mixmonitor_thread, mixmonitor);
00302    pthread_attr_destroy(&attr);
00303 
00304 }

static int load_module ( void   )  [static]

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

Definition at line 403 of file app_mixmonitor.c.

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

00404 {
00405    struct ast_channel *chan;
00406 
00407    if (argc < 3)
00408       return RESULT_SHOWUSAGE;
00409 
00410    if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
00411       ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
00412       return RESULT_SUCCESS;
00413    }
00414 
00415    if (!strcasecmp(argv[1], "start"))
00416       mixmonitor_exec(chan, argv[3]);
00417    else if (!strcasecmp(argv[1], "stop"))
00418       ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
00419 
00420    ast_channel_unlock(chan);
00421 
00422    return RESULT_SUCCESS;
00423 }

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

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

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

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

Definition at line 146 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_frame_free(), AST_LIST_NEXT, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_safe_system(), ast_test_flag, ast_verbose(), ast_writefile(), ast_writestream(), ast_channel_spy::chan, CHANSPY_RUNNING, ext, f, mixmonitor::filename, free, ast_channel_spy::lock, LOG_ERROR, MUXFLAG_APPEND, MUXFLAG_BRIDGED, mixmonitor::name, option_verbose, mixmonitor::post_process, SAMPLES_PER_FRAME, mixmonitor::spy, ast_channel_spy::status, and VERBOSE_PREFIX_2.

Referenced by launch_monitor_thread().

00147 {
00148    struct mixmonitor *mixmonitor = obj;
00149    struct ast_frame *f = NULL;
00150    struct ast_filestream *fs = NULL;
00151    unsigned int oflags;
00152    char *ext;
00153    int errflag = 0;
00154 
00155    if (option_verbose > 1)
00156       ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name);
00157    
00158    ast_mutex_lock(&mixmonitor->spy.lock);
00159 
00160    while (mixmonitor->spy.chan) {
00161       struct ast_frame *next;
00162       int write;
00163 
00164       ast_channel_spy_trigger_wait(&mixmonitor->spy);
00165       
00166       if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING)
00167          break;
00168       
00169       while (1) {
00170          if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME)))
00171             break;
00172 
00173          write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
00174              ast_bridged_channel(mixmonitor->spy.chan));
00175 
00176          /* it is possible for ast_channel_spy_read_frame() to return a chain
00177             of frames if a queue flush was necessary, so process them
00178          */
00179          for (; f; f = next) {
00180             next = AST_LIST_NEXT(f, frame_list);
00181             if (write && errflag == 0) {
00182                if (!fs) {
00183                   /* Determine creation flags and filename plus extension for filestream */
00184                   oflags = O_CREAT | O_WRONLY;
00185                   oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00186 
00187                   if ((ext = strrchr(mixmonitor->filename, '.')))
00188                      *(ext++) = '\0';
00189                   else
00190                      ext = "raw";
00191 
00192                   /* Move onto actually creating the filestream */
00193                   if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) {
00194                      ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
00195                      errflag = 1;
00196                   }
00197 
00198                }
00199                if (fs)
00200                   ast_writestream(fs, f);
00201             }
00202             ast_frame_free(f, 0);
00203          }
00204       }
00205    }
00206 
00207    ast_mutex_unlock(&mixmonitor->spy.lock);
00208 
00209    ast_channel_spy_free(&mixmonitor->spy);
00210    
00211    if (option_verbose > 1)
00212       ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name);
00213 
00214    if (fs)
00215       ast_closestream(fs);
00216 
00217    if (mixmonitor->post_process) {
00218       if (option_verbose > 2)
00219          ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process);
00220       ast_safe_system(mixmonitor->post_process);
00221    }
00222 
00223    free(mixmonitor);
00224 
00225 
00226    return NULL;
00227 }

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

Definition at line 126 of file app_mixmonitor.c.

References ast_bridged_channel(), ast_channel_lock, ast_channel_spy_add(), ast_channel_unlock, AST_FLAG_NBRIDGE, ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, and ast_test_flag.

Referenced by launch_monitor_thread().

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

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

Definition at line 388 of file app_mixmonitor.c.

References ast_channel_lock, ast_channel_spy_stop_by_type(), ast_channel_unlock, ast_module_user_add, and ast_module_user_remove.

Referenced by load_module().

00389 {
00390    struct ast_module_user *u;
00391 
00392    u = ast_module_user_add(chan);
00393 
00394    ast_channel_lock(chan);
00395    ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
00396    ast_channel_unlock(chan);
00397 
00398    ast_module_user_remove(u);
00399 
00400    return 0;
00401 }

static int unload_module ( void   )  [static]

Definition at line 439 of file app_mixmonitor.c.

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

00440 {
00441    int res;
00442 
00443    ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
00444    res = ast_unregister_application(stop_app);
00445    res |= ast_unregister_application(app);
00446    
00447    ast_module_user_hangup_all();
00448 
00449    return res;
00450 }


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

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 Sat Apr 12 07:12:30 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.5