Update of /usr/cvsroot/asterisk/apps
In directory mongoose.digium.com:/tmp/cvs-serv7234/apps

Modified Files:
        Makefile 
Added Files:
        app_muxmon.c 
Log Message:
Add muxmon application (bug #4735 with mods, thanks tony!)


--- NEW FILE: app_muxmon.c ---
/*
 * Asterisk -- A telephony toolkit for Linux.
 *
 * muxmon Application For Asterisk
 *
 * Copyright (C) 2005, Anthony Minessale II
 *
 * Anthony Minessale II <[EMAIL PROTECTED]>
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License
 */

#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/channel.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/translate.h>
#include <asterisk/slinfactory.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
#define minmax(x,y) x ? (x > y) ? y : ((x < (y * -1)) ? (y * -1) : x) : 0 

AST_MUTEX_DEFINE_STATIC(modlock);

static char *tdesc = "Native Channel Monitoring Module";
static char *app = "MuxMon";
static char *synopsis = "Record A Call Natively";
static char *desc = ""
"  MuxMon(<file>.<ext>[|<options>[|<command>]])\n\n"
"Records The audio on the current channel to the specified file.\n\n"
"Valid Options:\n"
" b    - Only save audio to the file while the channel is bridged. Note: does\n"
"        not include conferences\n"
" a    - Append to the file instead of overwriting it.\n"
" v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"        
" V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"       
" W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
"         (range -4 to 4)\n\n"  
"<command> will be executed when the recording is over\n"
"Any strings matching ^{X} will be unescaped to ${X} and \n"
"all variables will be evaluated at that time.\n"
"The variable MUXMON_FILENAME will contain the filename used to record.\n"
"";

STANDARD_LOCAL_USER;

LOCAL_USER_DECL;

struct muxmon {
        struct ast_channel *chan;
        char *filename;
        char *post_process;
        unsigned int flags;
        int readvol;
        int writevol;
};

typedef enum {
    MUXFLAG_RUNNING = (1 << 0),
    MUXFLAG_APPEND = (1 << 1),
    MUXFLAG_BRIDGED = (1 << 2),
    MUXFLAG_VOLUME = (1 << 3),
    MUXFLAG_READVOLUME = (1 << 4),
    MUXFLAG_WRITEVOLUME = (1 << 5)
} muxflags;


AST_DECLARE_OPTIONS(muxmon_opts,{
    ['a'] = { MUXFLAG_APPEND },
        ['b'] = { MUXFLAG_BRIDGED },
        ['v'] = { MUXFLAG_READVOLUME, 1 },
        ['V'] = { MUXFLAG_WRITEVOLUME, 2 },
        ['W'] = { MUXFLAG_VOLUME, 3 },
});


static void stopmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
{
        struct ast_channel_spy *cptr=NULL, *prev=NULL;
        int count = 0;

        if (chan) {
                while(ast_mutex_trylock(&chan->lock)) {
                        if (chan->spiers == spy) {
                                chan->spiers = NULL;
                                return;
                        }
                        count++;
                        if (count > 10) {
                                return;
                        }
                        sched_yield();
                }
                
                for(cptr=chan->spiers; cptr; cptr=cptr->next) {
                        if (cptr == spy) {
                                if (prev) {
                                        prev->next = cptr->next;
                                        cptr->next = NULL;
                                } else
                                        chan->spiers = NULL;
                        }
                        prev = cptr;
                }

                ast_mutex_unlock(&chan->lock);
        }
}

static void startmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
{

        struct ast_channel_spy *cptr=NULL;
        struct ast_channel *peer;

        if (chan) {
                ast_mutex_lock(&chan->lock);
                if (chan->spiers) {
                        for(cptr=chan->spiers;cptr->next;cptr=cptr->next);
                        cptr->next = spy;
                } else {
                        chan->spiers = spy;
                }
                ast_mutex_unlock(&chan->lock);
                
                if (ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = 
ast_bridged_channel(chan))) {
                        ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
                }
        }
}

static int spy_queue_translate(struct ast_channel_spy *spy,
                                                           struct 
ast_slinfactory *slinfactory0,
                                                           struct 
ast_slinfactory *slinfactory1)
{
        int res = 0;
        struct ast_frame *f;
        
        ast_mutex_lock(&spy->lock);
        while((f = spy->queue[0])) {
                spy->queue[0] = f->next;
                ast_slinfactory_feed(slinfactory0, f);
                ast_frfree(f);
        }
        ast_mutex_unlock(&spy->lock);
        ast_mutex_lock(&spy->lock);
        while((f = spy->queue[1])) {
                spy->queue[1] = f->next;
                ast_slinfactory_feed(slinfactory1, f);
                ast_frfree(f);
        }
        ast_mutex_unlock(&spy->lock);
        return res;
}

static void *muxmon_thread(void *obj) 
{

        int len0 = 0, len1 = 0, samp0 = 0, samp1 = 0, framelen, maxsamp = 0, x 
= 0;
        short buf0[1280], buf1[1280], buf[1280];
        struct ast_frame frame;
        struct muxmon *muxmon = obj;
        struct ast_channel_spy spy;
        struct ast_filestream *fs = NULL;
        char *ext, *name;
        unsigned int oflags;
        struct ast_slinfactory slinfactory[2];
        char post_process[1024] = "";
        
        name = ast_strdupa(muxmon->chan->name);

        framelen = 320;
        frame.frametype = AST_FRAME_VOICE;
        frame.subclass = AST_FORMAT_SLINEAR;
        frame.data = buf;
        ast_set_flag(muxmon, MUXFLAG_RUNNING);
        oflags = O_CREAT|O_WRONLY;
        ast_slinfactory_init(&slinfactory[0]);
        ast_slinfactory_init(&slinfactory[1]);
        


        /* for efficiency, use a flag to bypass volume logic when it's not 
needed */
        if (muxmon->readvol || muxmon->writevol) {
                ast_set_flag(muxmon, MUXFLAG_VOLUME);
        }

        if ((ext = strchr(muxmon->filename, '.'))) {
                *(ext++) = '\0';
        } else {
                ext = "raw";
        }

        memset(&spy, 0, sizeof(spy));
        spy.status = CHANSPY_RUNNING;
        ast_mutex_init(&spy.lock);
        startmon(muxmon->chan, &spy);
        if (ast_test_flag(muxmon, MUXFLAG_RUNNING)) {
                if (option_verbose > 1) {
                        ast_verbose(VERBOSE_PREFIX_2 "Begin Muxmon Recording 
%s\n", name);
                }

                oflags |= ast_test_flag(muxmon, MUXFLAG_APPEND) ? O_APPEND : 
O_TRUNC;
                
                if (!(fs = ast_writefile(muxmon->filename, ext, NULL, oflags, 
0, 0644))) {
                        ast_log(LOG_ERROR, "Cannot open %s\n", 
muxmon->filename);
                        spy.status = CHANSPY_DONE;
                }  else {

                        if (ast_test_flag(muxmon, MUXFLAG_APPEND)) {
                                ast_seekstream(fs, 0, SEEK_END);
                        }

                        while (ast_test_flag(muxmon, MUXFLAG_RUNNING)) {
                                samp0 = samp1 = len0 = len1 = 0;

                                if (ast_check_hangup(muxmon->chan) || 
spy.status != CHANSPY_RUNNING) {
                                        ast_clear_flag(muxmon, MUXFLAG_RUNNING);
                                        break;
                                }

                                if (ast_test_flag(muxmon, MUXFLAG_BRIDGED) && 
!ast_bridged_channel(muxmon->chan)) {
                                        usleep(1000);
                                        sched_yield();
                                        continue;
                                }
                                
                                spy_queue_translate(&spy, &slinfactory[0], 
&slinfactory[1]);
                                
                                if (slinfactory[0].size < framelen || 
slinfactory[1].size < framelen) {
                                        usleep(1000);
                                        sched_yield();
                                        continue;
                                }

                                if ((len0 = 
ast_slinfactory_read(&slinfactory[0], buf0, framelen))) {
                                        samp0 = len0 / 2;
                                }
                                if((len1 = 
ast_slinfactory_read(&slinfactory[1], buf1, framelen))) {
                                        samp1 = len1 / 2;
                                }
                                
                                if (ast_test_flag(muxmon, MUXFLAG_VOLUME)) {
                                        if (samp0 && muxmon->readvol > 0) {
                                                for(x=0; x < samp0 / 2; x++) {
                                                        buf0[x] *= 
muxmon->readvol;
                                                }
                                        } else if (samp0 && muxmon->readvol < 
0) {
                                                for(x=0; x < samp0 / 2; x++) {
                                                        buf0[x] /= 
muxmon->readvol;
                                                }
                                        }
                                        if (samp1 && muxmon->writevol > 0) {
                                                for(x=0; x < samp1 / 2; x++) {
                                                        buf1[x] *= 
muxmon->writevol;
                                                }
                                        } else if (muxmon->writevol < 0) {
                                                for(x=0; x < samp1 / 2; x++) {
                                                        buf1[x] /= 
muxmon->writevol;
                                                }
                                        }
                                }
                                
                                maxsamp = (samp0 > samp1) ? samp0 : samp1;

                                if (samp0 && samp1) {
                                        for(x=0; x < maxsamp; x++) {
                                                if (x < samp0 && x < samp1) {
                                                        buf[x] = buf0[x] + 
buf1[x];
                                                } else if (x < samp0) {
                                                        buf[x] = buf0[x];
                                                } else if (x < samp1) {
                                                        buf[x] = buf1[x];
                                                }
                                        }
                                } else if(samp0) {
                                        memcpy(buf, buf0, len0);
                                        x = samp0;
                                } else if(samp1) {
                                        memcpy(buf, buf1, len1);
                                        x = samp1;
                                }

                                frame.samples = x;
                                frame.datalen = x * 2;
                                ast_writestream(fs, &frame);
                
                                usleep(1000);
                                sched_yield();
                        }
                }
        }

        if (muxmon->post_process) {
                char *p;
                for(p = muxmon->post_process; *p ; p++) {
                        if (*p == '^' && *(p+1) == '{') {
                                *p = '$';
                        }
                }
                pbx_substitute_variables_helper(muxmon->chan, 
muxmon->post_process, post_process, sizeof(post_process) - 1);
                free(muxmon->post_process);
                muxmon->post_process = NULL;
        }

        stopmon(muxmon->chan, &spy);
        if (option_verbose > 1) {
                ast_verbose(VERBOSE_PREFIX_2 "Finished Recording %s\n", name);
        }
        ast_mutex_destroy(&spy.lock);
        
        if(fs) {
                ast_closestream(fs);
        }
        
        ast_slinfactory_destroy(&slinfactory[0]);
        ast_slinfactory_destroy(&slinfactory[1]);

        if (muxmon) {
                if (muxmon->filename) {
                        free(muxmon->filename);
                }
                free(muxmon);
        }

        if (!ast_strlen_zero(post_process)) {
                if (option_verbose > 2) {
                        ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", 
post_process);
                }
                ast_safe_system(post_process);
        }

        return NULL;
}

static void launch_monitor_thread(struct ast_channel *chan, char *filename, 
unsigned int flags, int readvol , int writevol, char *post_process) 
{
        pthread_attr_t attr;
        int result = 0;
        pthread_t thread;
        struct muxmon *muxmon;


        if (!(muxmon = malloc(sizeof(struct muxmon)))) {
                ast_log(LOG_ERROR, "Memory Error!\n");
                return;
        }

        memset(muxmon, 0, sizeof(struct muxmon));
        muxmon->chan = chan;
        muxmon->filename = strdup(filename);
        if(post_process) {
                muxmon->post_process = strdup(post_process);
        }
        muxmon->readvol = readvol;
        muxmon->writevol = writevol;
        muxmon->flags = flags;

        result = pthread_attr_init(&attr);
        pthread_attr_setschedpolicy(&attr, SCHED_RR);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        result = ast_pthread_create(&thread, &attr, muxmon_thread, muxmon);
        result = pthread_attr_destroy(&attr);
}


static int muxmon_exec(struct ast_channel *chan, void *data)
{
        int res = 0, x = 0, readvol = 0, writevol = 0;
        struct localuser *u;
        struct ast_flags flags = {0};
        int argc;
        char *options = NULL,
                *args,
                *argv[3],
                *filename = NULL,
                *post_process = NULL;
        
        if (!data) {
                ast_log(LOG_WARNING, "muxmon requires an argument\n");
                return -1;
        }

        if (!(args = ast_strdupa(data))) {
                ast_log(LOG_WARNING, "Memory Error!\n");
        return -1;
        }
        

        if ((argc = ast_separate_app_args(args, '|', argv, sizeof(argv) / 
sizeof(argv[0])))) {
                filename = argv[0];
                if ( argc > 1) {
                        options = argv[1];
                }
                if ( argc > 2) {
                        post_process = argv[2];
                }
        }
        
        if (!filename || ast_strlen_zero(filename)) {
                ast_log(LOG_WARNING, "Muxmon requires an argument 
(filename)\n");
        return -1;
        }

        LOCAL_USER_ADD(u);

        if (options) {
                char *opts[3] = {};
                ast_parseoptions(muxmon_opts, &flags, opts, options);

                if (ast_test_flag(&flags, MUXFLAG_READVOLUME) && opts[0]) {
                        if (sscanf(opts[0], "%d", &x) != 1)
                                ast_log(LOG_NOTICE, "volume must be a number 
between -4 and 4\n");
                        else {
                                readvol = minmax(x, 4);
                                x = get_volfactor(readvol);
                                readvol = minmax(x, 16);
                        }
                }
                
                if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME) && opts[1]) {
                        if (sscanf(opts[1], "%d", &x) != 1)
                                ast_log(LOG_NOTICE, "volume must be a number 
between -4 and 4\n");
                        else {
                                writevol = minmax(x, 4);
                                x = get_volfactor(writevol);
                                writevol = minmax(x, 16);
                        }
                }

                if (ast_test_flag(&flags, MUXFLAG_VOLUME) && opts[2]) {
                        if (sscanf(opts[2], "%d", &x) != 1)
                                ast_log(LOG_NOTICE, "volume must be a number 
between -4 and 4\n");
                        else {
                                readvol = writevol = minmax(x, 4);
                                x = get_volfactor(readvol);
                readvol = minmax(x, 16);
                                x = get_volfactor(writevol);
                writevol = minmax(x, 16);
                        }
                }
        }
                pbx_builtin_setvar_helper(chan, "MUXMON_FILENAME", filename);
        launch_monitor_thread(chan, filename, flags.flags, readvol, writevol, 
post_process);

        LOCAL_USER_REMOVE(u);
        return res;
}


static int muxmon_cli(int fd, int argc, char **argv) 
{
        char *op, *chan_name = NULL, *args = NULL;
        struct ast_channel *chan;
        int count = 0;

        if (argc > 2) {
                op = argv[1];
                chan_name = argv[2];

                if (argv[3]) {
                        args = argv[3];
                }

                if (!(chan = ast_get_channel_by_name_prefix_locked(chan_name, 
strlen(chan_name)))) {
                        ast_cli(fd, "Invalid Channel!\n");
                        return -1;
                }
                if (!strcasecmp(op, "start")) {
                        muxmon_exec(chan, args);
                } else if (!strcasecmp(op, "stop")) {
                        struct ast_channel_spy *cptr=NULL;
                        for(cptr=chan->spiers; cptr; cptr=cptr->next) {
                                cptr->status = CHANSPY_DONE;
                        }
                }
                ast_mutex_unlock(&chan->lock);
                return 0;
        }

        ast_cli(fd, "Usage: muxmon <start|stop> <chan_name> <args>\n");
        return -1;
}


static struct ast_cli_entry cli_muxmon = {
        { "muxmon", NULL, NULL }, muxmon_cli, 
        "Execute a monitor command", "muxmon <start|stop> <chan_name> <args>"};


int unload_module(void)
{
        STANDARD_HANGUP_LOCALUSERS;
        ast_cli_unregister(&cli_muxmon);
        return ast_unregister_application(app);
}

int load_module(void)
{
        ast_cli_register(&cli_muxmon);
        return ast_register_application(app, muxmon_exec, synopsis, desc);
}

char *description(void)
{
        return tdesc;
}

int usecount(void)
{
        int res;
        STANDARD_USECOUNT(res);
        return res;
}

char *key()
{
        return ASTERISK_GPL_KEY;
}


Index: Makefile
===================================================================
RCS file: /usr/cvsroot/asterisk/apps/Makefile,v
retrieving revision 1.113
retrieving revision 1.114
diff -u -d -r1.113 -r1.114
--- Makefile    12 Oct 2005 22:56:53 -0000      1.113
+++ Makefile    16 Oct 2005 02:59:13 -0000      1.114
@@ -28,7 +28,8 @@
      app_test.so app_forkcdr.so app_math.so app_realtime.so \
      app_dumpchan.so app_waitforsilence.so app_while.so app_setrdnis.so \
      app_md5.so app_readfile.so app_chanspy.so app_settransfercapability.so \
-     app_dictate.so app_externalivr.so app_directed_pickup.so app_page.so
+     app_dictate.so app_externalivr.so app_directed_pickup.so app_page.so \
+     app_muxmon.so
 
 #
 # Obsolete things...

_______________________________________________
Asterisk-Cvs mailing list
[email protected]
http://lists.digium.com/mailman/listinfo/asterisk-cvs

Reply via email to