Major peformance improvements to meetme
authorMark Spencer <markster@digium.com>
Tue, 20 Dec 2005 13:07:02 +0000 (13:07 +0000)
committerMark Spencer <markster@digium.com>
Tue, 20 Dec 2005 13:07:02 +0000 (13:07 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@7547 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_meetme.c
channel.c
include/asterisk/channel.h

index 2c7970b..d966cfd 100644 (file)
@@ -54,6 +54,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/cli.h"
 #include "asterisk/say.h"
 #include "asterisk/utils.h"
+#include "asterisk/translate.h"
+#include "asterisk/ulaw.h"
 
 static const char *tdesc = "MeetMe conference bridge";
 
@@ -95,6 +97,10 @@ static const char *descrip =
 "      's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
 "      't' -- set talk only mode. (Talk only, no listening)\n"
 "      'T' -- set talker detection (sent to manager interface and meetme list)\n"
+"      'o' -- set talker optimization - treats talkers who aren't speaking as\n"
+"             being muted, meaning (a) No encode is done on transmission and\n"
+"             (b) Received audio that is not registered as talking is omitted\n"
+"             causing no buildup in background noise\n"
 "      'v' -- video mode\n"
 "      'w' -- wait until the marked user enters the conference\n"
 "      'x' -- close the conference when last marked user exits\n"
@@ -129,6 +135,8 @@ STANDARD_LOCAL_USER;
 LOCAL_USER_DECL;
 
 static struct ast_conference {
+       ast_mutex_t playlock;                           /* Conference specific lock (players) */
+       ast_mutex_t listenlock;                         /* Conference specific lock (listeners) */
        char confno[AST_MAX_EXTENSION];         /* Conference */
        struct ast_channel *chan;               /* Announcements channel */
        int fd;                                 /* Announcements fd */
@@ -147,6 +155,9 @@ static struct ast_conference {
        const char *recordingformat;                    /* Format to record the Conference in */
        char pin[AST_MAX_EXTENSION];            /* If protected by a PIN */
        char pinadmin[AST_MAX_EXTENSION];       /* If protected by a admin PIN */
+       struct ast_frame *transframe[32];
+       struct ast_frame *origframe;
+       struct ast_trans_pvt *transpath[32];
        struct ast_conference *next;
 } *confs;
 
@@ -182,6 +193,8 @@ static int audio_buffers;                   /* The number of audio buffers to be allocated on ps
 #define MEETME_DELAYDETECTTALK                 300
 #define MEETME_DELAYDETECTENDTALK      1000
 
+#define AST_FRAME_BITS 32
+
 enum volume_action {
        VOL_UP,
        VOL_DOWN,
@@ -190,6 +203,7 @@ enum volume_action {
 AST_MUTEX_DEFINE_STATIC(conflock);
 
 static int admin_exec(struct ast_channel *chan, void *data);
+static struct ast_frame null_frame = { AST_FRAME_NULL, };
 
 static void *recordthread(void *args);
 
@@ -200,8 +214,9 @@ static void *recordthread(void *args);
 #define LEAVE  1
 
 #define MEETME_RECORD_OFF      0
-#define MEETME_RECORD_ACTIVE   1
-#define MEETME_RECORD_TERMINATE        2
+#define MEETME_RECORD_STARTED  1
+#define MEETME_RECORD_ACTIVE   2
+#define MEETME_RECORD_TERMINATE        3
 
 #define CONF_SIZE 320
 
@@ -227,12 +242,14 @@ static void *recordthread(void *args);
 #define CONFFLAG_EMPTYNOPIN (1 << 20)
 #define CONFFLAG_ALWAYSPROMPT (1 << 21)
 #define CONFFLAG_ANNOUNCEUSERCOUNT (1 << 22)   /* If set, when user joins the conference, they will be told the number of users that are already in */
+#define CONFFLAG_OPTIMIZETALKER (1 << 23)      /* If set, treats talking users as muted users */
 
 
 AST_APP_OPTIONS(meetme_opts, {
        AST_APP_OPTION('a', CONFFLAG_ADMIN ),
        AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
        AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
+       AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
        AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
        AST_APP_OPTION('m', CONFFLAG_MONITOR ),
        AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
@@ -406,6 +423,8 @@ static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int
        unsigned char *data;
        int len;
        int res = -1;
+       short *data2;
+       int x;
 
        if (!chan->_softhangup)
                res = ast_autoservice_start(chan);
@@ -425,8 +444,12 @@ static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int
                data = NULL;
                len = 0;
        }
-       if (data) 
-               careful_write(conf->fd, data, len, 1);
+       if (data) {
+               data2 = alloca(len * 2);
+               for (x=0;x<len;x++)
+                       data2[x] = AST_MULAW(data[x]);
+               careful_write(conf->fd, (unsigned char *)data2, len << 1, 1);
+       }
 
        ast_mutex_unlock(&conflock);
 
@@ -450,12 +473,16 @@ static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin
                /* Make a new one */
                cnf = calloc(1, sizeof(*cnf));
                if (cnf) {
+                       ast_mutex_init(&cnf->playlock);
+                       ast_mutex_init(&cnf->listenlock);
                        ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
                        ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
                        ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
                        cnf->markedusers = 0;
-                       cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL);
+                       cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
                        if (cnf->chan) {
+                               ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
+                               ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
                                cnf->fd = cnf->chan->fds[0];    /* for use by conf_play() */
                        } else {
                                ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
@@ -482,6 +509,7 @@ static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin
                                cnf = NULL;
                                goto cnfout;
                        }
+
                        /* Fill the conference struct */
                        cnf->start = time(NULL);
                        cnf->zapconf = ztc.confno;
@@ -752,7 +780,8 @@ static void conf_flush(int fd, struct ast_channel *chan)
 static int conf_free(struct ast_conference *conf)
 {
        struct ast_conference *prev = NULL, *cur = confs;
-
+       int x;
+       
        while (cur) {
                if (cur == conf) {
                        if (prev)
@@ -779,6 +808,14 @@ static int conf_free(struct ast_conference *conf)
                }
        }
 
+       for (x=0;x<AST_FRAME_BITS;x++) {
+               if (conf->transframe[x])
+                       ast_frfree(conf->transframe[x]);
+               if (conf->transpath[x])
+                       ast_translator_free_path(conf->transpath[x]);
+               if (conf->origframe)
+                       ast_frfree(conf->origframe);
+       }
        if (conf->chan)
                ast_hangup(conf->chan);
        else
@@ -831,21 +868,26 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
                return ret;
        }
 
-       if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) {
-               conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
+       if (confflags & CONFFLAG_RECORDCONF) {
                if (!conf->recordingfilename) {
-                       snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
-                       conf->recordingfilename = ast_strdupa(recordingtmp);
-               }
-               conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
-               if (!conf->recordingformat) {
-                       snprintf(recordingtmp, sizeof(recordingtmp), "wav");
-                       conf->recordingformat = ast_strdupa(recordingtmp);
+                       conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
+                       if (!conf->recordingfilename) {
+                               snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
+                               conf->recordingfilename = ast_strdupa(recordingtmp);
+                       }
+                       conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
+                       if (!conf->recordingformat) {
+                               snprintf(recordingtmp, sizeof(recordingtmp), "wav");
+                               conf->recordingformat = ast_strdupa(recordingtmp);
+                       }
+                       ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
+                                   conf->confno, conf->recordingfilename, conf->recordingformat);
                }
+       }
+
+       if ((conf->recording == MEETME_RECORD_OFF) && ((confflags & CONFFLAG_RECORDCONF) || (conf->chan))) {
                pthread_attr_init(&conf->attr);
                pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
-               ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
-                           conf->confno, conf->recordingfilename, conf->recordingformat);
                ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
        }
 
@@ -861,7 +903,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
        if (confflags & CONFFLAG_MARKEDUSER)
                conf->markedusers++;
       
-       ast_mutex_lock(&conflock);
+       ast_mutex_lock(&conf->playlock);
        if (!conf->firstuser) {
                /* Fill the first new User struct */
                user->user_no = 1;
@@ -873,7 +915,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
                user->prevuser = conf->lastuser;
                if (conf->lastuser->nextuser) {
                        ast_log(LOG_WARNING, "Error in User Management!\n");
-                       ast_mutex_unlock(&conflock);
+                       ast_mutex_unlock(&conf->playlock);
                        goto outrun;
                } else {
                        conf->lastuser->nextuser = user;
@@ -886,7 +928,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
        user->adminflags = 0;
        user->talking = -1;
        conf->users++;
-       ast_mutex_unlock(&conflock);
+       ast_mutex_unlock(&conf->playlock);
 
        if (confflags & CONFFLAG_EXIT_CONTEXT) {
                if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) 
@@ -1029,7 +1071,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
        ztc.chan = 0;   
        ztc.confno = conf->zapconf;
 
-       ast_mutex_lock(&conflock);
+       ast_mutex_lock(&conf->playlock);
 
        if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
                if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
@@ -1050,7 +1092,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
        if (ioctl(fd, ZT_SETCONF, &ztc)) {
                ast_log(LOG_WARNING, "Error setting conference\n");
                close(fd);
-               ast_mutex_unlock(&conflock);
+               ast_mutex_unlock(&conf->playlock);
                goto outrun;
        }
        ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
@@ -1069,7 +1111,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
                                conf_play(chan, conf, ENTER);
        }
 
-       ast_mutex_unlock(&conflock);
+       ast_mutex_unlock(&conf->playlock);
 
        conf_flush(fd, chan);
 
@@ -1106,7 +1148,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
                        x = 1;
                        ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
                }       
-               if (confflags & CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
+               if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
                        ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
                        res = -1;
                }
@@ -1146,6 +1188,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
 
                        c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
                        
+                       
                        /* Update the struct with the actual confflags */
                        user->userflags = confflags;
                        
@@ -1269,14 +1312,17 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
                                        user->zapchannel = !retryzap;
                                        goto zapretry;
                                }
-                               f = ast_read(c);
+                               if (!(confflags & CONFFLAG_MONITOR))
+                                       f = ast_read(c);
+                               else
+                                       f = ast_read_noaudio(c);
                                if (!f)
                                        break;
                                if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
                                        if (user->talk.actual)
                                                ast_frame_adjust_volume(f, user->talk.actual);
 
-                                       if (confflags &  CONFFLAG_MONITORTALKER) {
+                                       if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
                                                int totalsilence;
 
                                                if (user->talking == -1)
@@ -1285,7 +1331,8 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
                                                res = ast_dsp_silence(dsp, f, &totalsilence);
                                                if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
                                                        user->talking = 1;
-                                                       manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
+                                                       if (confflags & CONFFLAG_MONITORTALKER)
+                                                               manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
                                                                      "Channel: %s\r\n"
                                                                      "Uniqueid: %s\r\n"
                                                                      "Meetme: %s\r\n"
@@ -1294,7 +1341,8 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
                                                }
                                                if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
                                                        user->talking = 0;
-                                                       manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
+                                                       if (confflags & CONFFLAG_MONITORTALKER)
+                                                               manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
                                                                      "Channel: %s\r\n"
                                                                      "Uniqueid: %s\r\n"
                                                                      "Meetme: %s\r\n"
@@ -1315,7 +1363,8 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
                                                   don't want to block, but we do want to at least *try*
                                                   to write out all the samples.
                                                 */
-                                               careful_write(fd, f->data, f->datalen, 0);
+                                               if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
+                                                       careful_write(fd, f->data, f->datalen, 0);
                                        }
                                } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
                                        char tmp[2];
@@ -1511,10 +1560,46 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
                                        fr.samples = res/2;
                                        fr.data = buf;
                                        fr.offset = AST_FRIENDLY_OFFSET;
-                                       if (user->listen.actual)
-                                               ast_frame_adjust_volume(&fr, user->listen.actual);
-                                       if (ast_write(chan, &fr) < 0) {
-                                               ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
+                                       if (!user->listen.actual && 
+                                               ((confflags & CONFFLAG_MONITOR) || 
+                                                (user->adminflags & ADMINFLAG_MUTED) ||
+                                                (user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
+                                                )) {
+                                               int index;
+                                               for (index=0;index<AST_FRAME_BITS;index++)
+                                                       if (chan->rawwriteformat & (1 << index))
+                                                               break;
+                                               if (index >= AST_FRAME_BITS)
+                                                       goto bailoutandtrynormal;
+                                               ast_mutex_lock(&conf->listenlock);
+                                               if (!conf->transframe[index]) {
+                                                       if (conf->origframe) {
+                                                               if (!conf->transpath[index])
+                                                                       conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
+                                                               if (conf->transpath[index]) {
+                                                                       conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
+                                                                       if (!conf->transframe[index])
+                                                                               conf->transframe[index] = &null_frame;
+                                                               }
+                                                       }
+                                               }
+                                               if (conf->transframe[index]) {
+                                                       if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
+                                                               if (ast_write(chan, conf->transframe[index]))
+                                                                       ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
+                                                       }
+                                               } else {
+                                                       ast_mutex_unlock(&conf->listenlock);
+                                                       goto bailoutandtrynormal;
+                                               }
+                                               ast_mutex_unlock(&conf->listenlock);
+                                       } else {
+bailoutandtrynormal:                                   
+                                               if (user->listen.actual)
+                                                       ast_frame_adjust_volume(&fr, user->listen.actual);
+                                               if (ast_write(chan, &fr) < 0) {
+                                                       ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
+                                               }
                                        }
                                } else 
                                        ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
@@ -1556,7 +1641,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
  outrun:
        ast_mutex_lock(&conflock);
 
-       if (confflags & CONFFLAG_MONITORTALKER && dsp)
+       if (dsp)
                ast_dsp_free(dsp);
        
        if (user->user_no) { /* Only cleanup users who really joined! */
@@ -1998,15 +2083,16 @@ static int conf_exec(struct ast_channel *chan, void *data)
        return res;
 }
 
-static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
+static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) 
+{
        struct ast_conf_user *user = NULL;
-       char usrno[1024] = "";
-
+       int cid;
+       
+       sscanf(callerident, "%i", &cid);
        if (conf && callerident) {
                user = conf->firstuser;
                while (user) {
-                       snprintf(usrno, sizeof(usrno), "%d", user->user_no);
-                       if (strcmp(usrno, callerident) == 0)
+                       if (cid == user->user_no)
                                return user;
                        user = user->nextuser;
                }
@@ -2100,7 +2186,7 @@ static int admin_exec(struct ast_channel *chan, void *data) {
                                if (user && (user->adminflags & ADMINFLAG_MUTED)) {
                                        user->adminflags ^= ADMINFLAG_MUTED;
                                } else {
-                                       ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
+                                       ast_log(LOG_NOTICE, "Specified User not found or he muted himself!\n");
                                }
                                break;
                        case  110: /* n: Unmute all users */
@@ -2140,39 +2226,62 @@ static void *recordthread(void *args)
        struct ast_conference *cnf = args;
        struct ast_frame *f=NULL;
        int flags;
-       struct ast_filestream *s;
+       struct ast_filestream *s=NULL;
        int res=0;
+       int x;
+       const char *oldrecordingfilename = NULL;
 
        if (!cnf || !cnf->chan) {
                pthread_exit(0);
        }
+
        ast_stopstream(cnf->chan);
        flags = O_CREAT|O_TRUNC|O_WRONLY;
-       s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
 
-       if (s) {
-               cnf->recording = MEETME_RECORD_ACTIVE;
-               while (ast_waitfor(cnf->chan, -1) > -1) {
-                       f = ast_read(cnf->chan);
-                       if (!f) {
-                               res = -1;
-                               break;
+
+       cnf->recording = MEETME_RECORD_ACTIVE;
+       while (ast_waitfor(cnf->chan, -1) > -1) {
+               if (cnf->recording == MEETME_RECORD_TERMINATE) {
+                       ast_mutex_lock(&conflock);
+                       ast_mutex_unlock(&conflock);
+                       break;
+               }
+               if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
+                       s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
+                       oldrecordingfilename = cnf->recordingfilename;
+               }
+               
+               f = ast_read(cnf->chan);
+               if (!f) {
+                       res = -1;
+                       break;
+               }
+               if (f->frametype == AST_FRAME_VOICE) {
+                       ast_mutex_lock(&cnf->listenlock);
+                       for (x=0;x<AST_FRAME_BITS;x++) {
+                               /* Free any translations that have occured */
+                               if (cnf->transframe[x]) {
+                                       ast_frfree(cnf->transframe[x]);
+                                       cnf->transframe[x] = NULL;
+                               }
+                               if (cnf->origframe)
+                                       ast_frfree(cnf->origframe);
+                               cnf->origframe = f;
                        }
-                       if (f->frametype == AST_FRAME_VOICE) {
+                       ast_mutex_unlock(&cnf->listenlock);
+                       if (s)
                                res = ast_writestream(s, f);
-                               if (res) 
-                                       break;
-                       }
-                       ast_frfree(f);
-                       if (cnf->recording == MEETME_RECORD_TERMINATE) {
-                               ast_mutex_lock(&conflock);
-                               ast_mutex_unlock(&conflock);
+                       if (res) {
+                               ast_frfree(f);
                                break;
                        }
                }
-               cnf->recording = MEETME_RECORD_OFF;
-               ast_closestream(s);
+               ast_frfree(f);
        }
+       cnf->recording = MEETME_RECORD_OFF;
+       if (s)
+               ast_closestream(s);
+       
        pthread_exit(0);
 }
 
index e573e85..078b9e5 100644 (file)
--- a/channel.c
+++ b/channel.c
@@ -1773,7 +1773,7 @@ int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd)
        return 0; /* Time is up */
 }
 
-struct ast_frame *ast_read(struct ast_channel *chan)
+static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 {
        struct ast_frame *f = NULL;
        int blah;
@@ -1897,7 +1897,10 @@ struct ast_frame *ast_read(struct ast_channel *chan)
 
 
        if (f && (f->frametype == AST_FRAME_VOICE)) {
-               if (!(f->subclass & chan->nativeformats)) {
+               if (dropaudio) {
+                       ast_frfree(f);
+                       f = &null_frame;
+               } else if (!(f->subclass & chan->nativeformats)) {
                        /* This frame can't be from the current native formats -- drop it on the
                           floor */
                        ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n", chan->name, ast_getformatname(f->subclass), ast_getformatname(chan->nativeformats));
@@ -1998,6 +2001,16 @@ struct ast_frame *ast_read(struct ast_channel *chan)
        return f;
 }
 
+struct ast_frame *ast_read(struct ast_channel *chan)
+{
+       return __ast_read(chan, 0);
+}
+
+struct ast_frame *ast_read_noaudio(struct ast_channel *chan)
+{
+       return __ast_read(chan, 1);
+}
+
 int ast_indicate(struct ast_channel *chan, int condition)
 {
        int res = -1;
@@ -2247,7 +2260,12 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                break;
        default:
                if (chan->tech->write) {
-                       f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr;
+                       /* Bypass translator if we're writing format in the raw write format.  This
+                          allows mixing of native / non-native formats */
+                       if (fr->subclass == chan->rawwriteformat)
+                               f = fr;
+                       else
+                               f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr;
                        if (f) {
                                if (f->frametype == AST_FRAME_VOICE && chan->spies)
                                        queue_frame_to_spies(chan, f, SPY_WRITE);
index 6829f83..373ab6a 100644 (file)
@@ -741,6 +741,15 @@ int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception);
    disconnected. */
 struct ast_frame *ast_read(struct ast_channel *chan);
 
+/*! Reads a frame, returning AST_FRAME_NULL frame if audio. */
+/*!
+ * \param chan channel to read a frame from
+ * Read a frame.  Returns a frame, or NULL on error.  If it returns NULL, you
+   best just stop reading frames and assume the channel has been
+   disconnected.  Audio is replaced with AST_FRAME_NULL to avoid 
+   transcode when the resulting audio is not necessary. */
+struct ast_frame *ast_read_noaudio(struct ast_channel *chan);
+
 /*! Write a frame to a channel */
 /*!
  * \param chan destination channel of the frame