aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xMakefile2
-rwxr-xr-xchannels/chan_h323.c1
-rwxr-xr-xchannels/chan_iax2.c526
-rwxr-xr-xchannels/chan_mgcp.c1
-rwxr-xr-xchannels/chan_sip.c1
-rwxr-xr-xchannels/chan_skinny.c1
-rwxr-xr-xchannels/iax2.h9
-rwxr-xr-xcodecs/codec_adpcm.c53
-rwxr-xr-xcodecs/codec_alaw.c48
-rwxr-xr-xcodecs/codec_g726.c47
-rwxr-xr-xcodecs/codec_gsm.c48
-rwxr-xr-xcodecs/codec_ilbc.c14
-rwxr-xr-xcodecs/codec_lpc10.c47
-rwxr-xr-xcodecs/codec_speex.c20
-rwxr-xr-xcodecs/codec_ulaw.c48
-rwxr-xr-xconfigs/codecs.conf.sample6
-rwxr-xr-xconfigs/iax.conf.sample27
-rwxr-xr-xinclude/asterisk/channel.h8
-rwxr-xr-xinclude/asterisk/translate.h2
-rwxr-xr-xjitterbuf.c698
-rwxr-xr-xjitterbuf.h140
-rwxr-xr-xplc.c251
22 files changed, 1952 insertions, 46 deletions
diff --git a/Makefile b/Makefile
index 9d63b4be8..8eee585c3 100755
--- a/Makefile
+++ b/Makefile
@@ -231,7 +231,7 @@ OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \
cdr.o tdd.o acl.o rtp.o manager.o asterisk.o ast_expr.o \
dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \
astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \
- utils.o config_old.o
+ utils.o config_old.o plc.o jitterbuf.o
ifeq (${OSARCH},Darwin)
OBJS+=poll.o dlfcn.o
ASTLINK=-Wl,-dynamic
diff --git a/channels/chan_h323.c b/channels/chan_h323.c
index 8ba7bd98a..da8d1cc10 100755
--- a/channels/chan_h323.c
+++ b/channels/chan_h323.c
@@ -197,6 +197,7 @@ static const struct ast_channel_tech oh323_tech = {
.type = type,
.description = tdesc,
.capabilities = AST_FORMAT_ULAW,
+ .properties = AST_CHAN_TP_WANTSJITTER,
.requester = oh323_request,
.send_digit = oh323_digit,
.call = oh323_call,
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 3bdcc7577..e48258a3e 100755
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -70,6 +70,14 @@
#include "iax2-provision.h"
#include "../astconf.h"
+/* Define NEWJB to use the new channel independent jitterbuffer,
+ * otherwise, use the old jitterbuffer */
+#define NEWJB
+
+#ifdef NEWJB
+#include "../jitterbuf.h"
+#endif
+
#ifndef IPTOS_MINCOST
#define IPTOS_MINCOST 0x02
#endif
@@ -118,6 +126,7 @@ static int maxnontrunkcall = 1;
static int maxjitterbuffer=1000;
static int jittershrinkrate=2;
static int trunkfreq = 20;
+static int send_trunktimestamps = 1;
static int authdebug = 1;
static int autokill = 0;
static int iaxcompat = 0;
@@ -171,6 +180,8 @@ static int iaxdebug = 0;
static int iaxtrunkdebug = 0;
+static int test_losspct = 0;
+
static char accountcode[20];
static int amaflags = 0;
static int delayreject = 0;
@@ -209,6 +220,7 @@ struct iax2_context {
#define IAX_RTCACHEFRIENDS (1 << 17) /* let realtime stay till your reload */
#define IAX_RTNOUPDATE (1 << 18) /* Don't send a realtime update */
#define IAX_RTAUTOCLEAR (1 << 19) /* erase me on expire */
+#define IAX_FORCEJITTERBUF (1 << 20) /* Force jitterbuffer, even when bridged to a channel that can take jitter */
static int global_rtautoclear = 120;
@@ -409,6 +421,12 @@ struct chan_iax2_pvt {
struct timeval offset;
/* timeval that we base our delivery on */
struct timeval rxcore;
+#ifdef NEWJB
+ /* The jitterbuffer */
+ jitterbuf *jb;
+ /* active jb read scheduler id */
+ int jbid;
+#else
/* Historical delivery time */
int history[MEMORY_SIZE];
/* Current base jitterbuffer */
@@ -417,6 +435,7 @@ struct chan_iax2_pvt {
int jitter;
/* Historic jitter value */
int historicjitter;
+#endif
/* LAG */
int lag;
/* Error, as discovered by the manager */
@@ -588,6 +607,46 @@ static void iax_error_output(const char *data)
ast_log(LOG_WARNING, "%s", data);
}
+#ifdef NEWJB
+static void jb_error_output(const char *fmt, ...)
+{
+ va_list args;
+ char buf[1024];
+
+ va_start(args, fmt);
+ vsnprintf(buf, 1024, fmt, args);
+ va_end(args);
+
+ ast_log(LOG_ERROR, buf);
+}
+
+static void jb_warning_output(const char *fmt, ...)
+{
+ va_list args;
+ char buf[1024];
+
+ va_start(args, fmt);
+ vsnprintf(buf, 1024, fmt, args);
+ va_end(args);
+
+ ast_log(LOG_WARNING, buf);
+}
+
+static void jb_debug_output(const char *fmt, ...)
+{
+ va_list args;
+ char buf[1024];
+ if(!iaxdebug) return;
+
+ va_start(args, fmt);
+ vsnprintf(buf, 1024, fmt, args);
+ va_end(args);
+
+ ast_verbose(buf);
+}
+#endif
+
+
/* XXX We probably should use a mutex when working with this XXX */
static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS];
static ast_mutex_t iaxsl[IAX_MAX_CALLS];
@@ -629,6 +688,7 @@ static const struct ast_channel_tech iax2_tech = {
.type = channeltype,
.description = tdesc,
.capabilities = IAX_CAPABILITY_FULLBANDWIDTH,
+ .properties = AST_CHAN_TP_WANTSJITTER,
.requester = iax2_request,
.devicestate = iax2_devicestate,
.send_digit = iax2_digit,
@@ -777,6 +837,16 @@ static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer, cons
/* strncpy(tmp->context, context, sizeof(tmp->context)-1); */
strncpy(tmp->exten, "s", sizeof(tmp->exten)-1);
strncpy(tmp->host, host, sizeof(tmp->host)-1);
+#ifdef NEWJB
+ {
+ jb_info jbinfo;
+
+ tmp->jb = jb_new();
+ tmp->jbid = -1;
+ jbinfo.max_jitterbuf = maxjitterbuffer;
+ jb_setinfo(tmp->jb,&jbinfo);
+ }
+#endif
}
return tmp;
}
@@ -998,7 +1068,7 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
iaxs[x]->pingid = ast_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
iaxs[x]->amaflags = amaflags;
- ast_copy_flags(iaxs[x], (&globalflags), IAX_NOTRANSFER | IAX_USEJITTERBUF);
+ ast_copy_flags(iaxs[x], (&globalflags), IAX_NOTRANSFER | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
strncpy(iaxs[x]->accountcode, accountcode, sizeof(iaxs[x]->accountcode)-1);
} else {
ast_log(LOG_WARNING, "Out of resources\n");
@@ -1438,6 +1508,11 @@ static int iax2_predestroy(int callno)
ast_sched_del(sched, pvt->authid);
if (pvt->initid > -1)
ast_sched_del(sched, pvt->initid);
+#ifdef NEWJB
+ if (pvt->jbid > -1)
+ ast_sched_del(sched, pvt->jbid);
+ pvt->jbid = -1;
+#endif
pvt->pingid = -1;
pvt->lagid = -1;
pvt->autoid = -1;
@@ -1510,6 +1585,11 @@ retry:
ast_sched_del(sched, pvt->authid);
if (pvt->initid > -1)
ast_sched_del(sched, pvt->initid);
+#ifdef NEWJB
+ if (pvt->jbid > -1)
+ ast_sched_del(sched, pvt->jbid);
+ pvt->jbid = -1;
+#endif
pvt->pingid = -1;
pvt->lagid = -1;
pvt->autoid = -1;
@@ -1541,6 +1621,14 @@ retry:
ast_variables_destroy(pvt->vars);
pvt->vars = NULL;
}
+#ifdef NEWJB
+ {
+ jb_frame frame;
+ while(jb_getall(pvt->jb,&frame) == JB_OK)
+ iax2_frame_free(frame.data);
+ jb_destroy(pvt->jb);
+ }
+#endif
free(pvt);
}
}
@@ -1663,6 +1751,10 @@ static int attempt_transmit(void *data)
static int iax2_set_jitter(int fd, int argc, char *argv[])
{
+#ifdef NEWJB
+ ast_cli(fd, "sorry, this command is deprecated\n");
+ return RESULT_SUCCESS;
+#else
if ((argc != 4) && (argc != 5))
return RESULT_SHOWUSAGE;
if (argc == 4) {
@@ -1683,6 +1775,7 @@ static int iax2_set_jitter(int fd, int argc, char *argv[])
}
}
return RESULT_SUCCESS;
+#endif
}
static char jitter_usage[] =
@@ -1716,6 +1809,16 @@ static int iax2_prune_realtime(int fd, int argc, char *argv[])
return RESULT_SUCCESS;
}
+static int iax2_test_losspct(int fd, int argc, char *argv[])
+{
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ test_losspct = atoi(argv[3]);
+
+ return RESULT_SUCCESS;
+}
+
/*--- iax2_show_peer: Show one peer in detail ---*/
static int iax2_show_peer(int fd, int argc, char *argv[])
{
@@ -1763,6 +1866,15 @@ static int iax2_show_peer(int fd, int argc, char *argv[])
ast_cli(fd, "none");
ast_cli(fd, ")\n");
+static int iax2_test_losspct(int fd, int argc, char *argv[])
+{
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ test_losspct = atoi(argv[3]);
+
+ return RESULT_SUCCESS;
+}
ast_cli(fd, " Status : ");
if (peer->lastms < 0)
@@ -1986,9 +2098,119 @@ static void unwrap_timestamp(struct iax_frame *fr)
}
}
+#ifdef NEWJB
+static int get_from_jb(void *p);
+
+static void update_jbsched(struct chan_iax2_pvt *pvt) {
+ int when;
+ struct timeval tv;
+
+ gettimeofday(&tv,NULL);
+
+ when = (tv.tv_sec - pvt->rxcore.tv_sec) * 1000 +
+ (tv.tv_usec - pvt->rxcore.tv_usec) / 1000;
+
+ /* fprintf(stderr, "now = %d, next=%d\n", when, jb_next(pvt->jb)); */
+
+ when = jb_next(pvt->jb) - when;
+ /* fprintf(stderr, "when = %d\n", when); */
+
+ if(pvt->jbid > -1) ast_sched_del(sched, pvt->jbid);
+
+ if(when <= 0) {
+ /* XXX should really just empty until when > 0.. */
+ when = 1;
+ }
+
+ pvt->jbid = ast_sched_add(sched, when, get_from_jb, (void *)pvt);
+}
+
+static int get_from_jb(void *p) {
+ /* make sure pvt is valid! */
+ struct chan_iax2_pvt *pvt = p;
+ struct iax_frame *fr;
+ jb_frame frame;
+ int ret;
+ long now;
+ long next;
+ struct timeval tv;
+
+ ast_mutex_lock(&iaxsl[pvt->callno]);
+ /* fprintf(stderr, "get_from_jb called\n"); */
+ pvt->jbid = -1;
+
+ gettimeofday(&tv,NULL);
+
+ now = (tv.tv_sec - pvt->rxcore.tv_sec) * 1000 +
+ (tv.tv_usec - pvt->rxcore.tv_usec) / 1000;
+
+ if(now > (next = jb_next(pvt->jb))) {
+ ret = jb_get(pvt->jb,&frame,now);
+ switch(ret) {
+ case JB_OK:
+ /*if(frame.type == JB_TYPE_VOICE && next + 20 != jb_next(pvt->jb)) fprintf(stderr, "NEXT %ld is not %ld+20!\n", jb_next(pvt->jb), next); */
+ fr = frame.data;
+ __do_deliver(fr);
+ break;
+ case JB_INTERP:
+ {
+ struct ast_frame af;
+
+ /*if(next + 20 != jb_next(pvt->jb)) fprintf(stderr, "NEXT %ld is not %ld+20!\n", jb_next(pvt->jb), next); */
+
+ /* create an interpolation frame */
+ /*fprintf(stderr, "Making Interpolation frame\n"); */
+ af.frametype = AST_FRAME_VOICE;
+ af.subclass = pvt->voiceformat;
+ af.datalen = 0;
+ af.samples = frame.ms * 8;
+ af.mallocd = 0;
+ af.src = "IAX2 JB interpolation";
+ af.data = NULL;
+ af.delivery.tv_sec = pvt->rxcore.tv_sec;
+ af.delivery.tv_usec = pvt->rxcore.tv_usec;
+ af.delivery.tv_sec += next / 1000;
+ af.delivery.tv_usec += (next % 1000) * 1000;
+ af.offset=AST_FRIENDLY_OFFSET;
+ if (af.delivery.tv_usec >= 1000000) {
+ af.delivery.tv_usec -= 1000000;
+ af.delivery.tv_sec += 1;
+ }
+
+ /* queue the frame: For consistency, we would call __do_deliver here, but __do_deliver wants an iax_frame,
+ * which we'd need to malloc, and then it would free it. That seems like a drag */
+ if (iaxs[pvt->callno] && !ast_test_flag(iaxs[pvt->callno], IAX_ALREADYGONE))
+ iax2_queue_frame(pvt->callno, &af);
+ }
+ break;
+ case JB_DROP:
+ /*if(next != jb_next(pvt->jb)) fprintf(stderr, "NEXT %ld is not next %ld!\n", jb_next(pvt->jb), next); */
+ iax2_frame_free(frame.data);
+ break;
+ case JB_NOFRAME:
+ case JB_EMPTY:
+ /* do nothing */
+ break;
+ default:
+ /* shouldn't happen */
+ break;
+ }
+ }
+ update_jbsched(pvt);
+ ast_mutex_unlock(&iaxsl[pvt->callno]);
+ return 0;
+}
+#endif
+
+/* while we transition from the old JB to the new one, we can either make two schedule_delivery functions, or
+ * make preprocessor swiss-cheese out of this one. I'm not sure which is less revolting.. */
static int schedule_delivery(struct iax_frame *fr, int reallydeliver, int updatehistory, int fromtrunk)
{
- int ms,x;
+ int x;
+#ifdef NEWJB
+ int type, len;
+#else
+ int ms;
int delay;
unsigned int orig_ts;
int drops[MEMORY_SIZE];
@@ -1998,6 +2220,7 @@ static int schedule_delivery(struct iax_frame *fr, int reallydeliver, int update
prevjitterbuffer = iaxs[fr->callno]->jitterbuffer;
/* Similarly for the frame timestamp */
orig_ts = fr->ts;
+#endif
#if 0
if (option_debug)
@@ -2030,7 +2253,7 @@ static int schedule_delivery(struct iax_frame *fr, int reallydeliver, int update
iaxs[fr->callno]->last = 0;
/* should we also empty history? */
}
-
+#ifndef NEWJB
/* ms is a measure of the "lateness" of the frame relative to the "reference"
frame we received. (initially the very first, but also see code just above here).
Understand that "ms" can easily be -ve if lag improves since the reference frame.
@@ -2043,10 +2266,13 @@ static int schedule_delivery(struct iax_frame *fr, int reallydeliver, int update
iaxs[fr->callno]->history[x] = iaxs[fr->callno]->history[x+1];
/* Add a history entry for this one */
iaxs[fr->callno]->history[x] = ms;
-
+#endif
}
+#ifndef NEWJB
else
ms = 0;
+#endif
+
/* delivery time is sender's sent timestamp converted back into absolute time according to our clock */
if ( (!fromtrunk) && (iaxs[fr->callno]->rxcore.tv_sec || iaxs[fr->callno]->rxcore.tv_usec) ) {
@@ -2068,6 +2294,7 @@ static int schedule_delivery(struct iax_frame *fr, int reallydeliver, int update
fr->af.delivery.tv_usec = 0;
}
+#ifndef NEWJB
/* Initialize the minimum to reasonable values. It's too much
work to do the same for the maximum, repeatedly */
min=iaxs[fr->callno]->history[0];
@@ -2098,6 +2325,60 @@ static int schedule_delivery(struct iax_frame *fr, int reallydeliver, int update
drops[z] = maxone;
#endif
}
+#endif
+
+#ifdef NEWJB
+ if(!reallydeliver)
+ return 0;
+
+ type = JB_TYPE_CONTROL;
+ len = 0;
+
+ if(fr->af.frametype == AST_FRAME_VOICE) {
+ type = JB_TYPE_VOICE;
+ len = get_samples(&fr->af)/8;
+ } else if(fr->af.frametype == AST_FRAME_CNG) {
+ type = JB_TYPE_SILENCE;
+ }
+
+ if ( (!ast_test_flag(iaxs[fr->callno], IAX_USEJITTERBUF)) ) {
+ __do_deliver(fr);
+ return 0;
+ }
+
+ /* if the user hasn't requested we force the use of the jitterbuffer, and we're bridged to
+ * a channel that can accept jitter, then flush and suspend the jb, and send this frame straight through */
+ if( (!ast_test_flag(iaxs[fr->callno], IAX_FORCEJITTERBUF)) &&
+ iaxs[fr->callno]->owner && ast_bridged_channel(iaxs[fr->callno]->owner) &&
+ (ast_bridged_channel(iaxs[fr->callno]->owner)->tech->properties & AST_CHAN_TP_WANTSJITTER)) {
+ jb_frame frame;
+
+ /* deliver any frames in the jb */
+ while(jb_getall(iaxs[fr->callno]->jb,&frame) == JB_OK)
+ __do_deliver(frame.data);
+
+ jb_reset(iaxs[fr->callno]->jb);
+
+ if (iaxs[fr->callno]->jbid > -1)
+ ast_sched_del(sched, iaxs[fr->callno]->jbid);
+
+ iaxs[fr->callno]->jbid = -1;
+
+ /* deliver this frame now */
+ __do_deliver(fr);
+ return 0;
+
+ }
+
+
+ /* insert into jitterbuffer */
+ /* TODO: Perhaps we could act immediately if it's not droppable and late */
+ if(jb_put(iaxs[fr->callno]->jb, fr, type, len, fr->ts,
+ calc_rxstamp(iaxs[fr->callno],fr->ts)) == JB_DROP) {
+ iax2_frame_free(fr);
+ }
+ update_jbsched(iaxs[fr->callno]);
+#else
/* Just for reference, keep the "jitter" value, the difference between the
earliest and the latest. */
if (max >= min)
@@ -2179,6 +2460,7 @@ static int schedule_delivery(struct iax_frame *fr, int reallydeliver, int update
ast_log(LOG_DEBUG, "schedule_delivery: Scheduling delivery in %d ms\n", delay);
fr->retrans = ast_sched_add(sched, delay, do_deliver, fr);
}
+#endif
return 0;
}
@@ -2370,7 +2652,7 @@ static void realtime_update_peer(const char *peername, struct sockaddr_in *sin)
static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani,
int *maxtime, char *peer, char *context, int *trunk,
- int *notransfer, int *usejitterbuf, int *encmethods,
+ int *notransfer, int *usejitterbuf, int *forcejitterbuf, int *encmethods,
char *username, int usernlen, char *secret, int seclen,
int *ofound, char *peercontext, char *timezone, int tzlen, char *pref_str, size_t pref_size,
int *sockfd)
@@ -2425,6 +2707,8 @@ static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani,
*notransfer = ast_test_flag(p, IAX_NOTRANSFER);
if (usejitterbuf)
*usejitterbuf = ast_test_flag(p, IAX_USEJITTERBUF);
+ if (forcejitterbuf)
+ *forcejitterbuf = ast_test_flag(p, IAX_FORCEJITTERBUF);
if (secret) {
if (!ast_strlen_zero(p->dbsecret)) {
char *family, *key=NULL;
@@ -2574,7 +2858,7 @@ static int iax2_call(struct ast_channel *c, char *dest, int timeout)
strsep(&stringp, ":");
portno = strsep(&stringp, ":");
}
- if (create_addr(&sin, NULL, NULL, NULL, hname, context, NULL, NULL, NULL, &encmethods, storedusern, sizeof(storedusern) - 1, storedsecret, sizeof(storedsecret) - 1, NULL, peercontext, tz, sizeof(tz), out_prefs, sizeof(out_prefs), NULL)) {
+ if (create_addr(&sin, NULL, NULL, NULL, hname, context, NULL, NULL, NULL, NULL, &encmethods, storedusern, sizeof(storedusern) - 1, storedsecret, sizeof(storedsecret) - 1, NULL, peercontext, tz, sizeof(tz), out_prefs, sizeof(out_prefs), NULL)) {
ast_log(LOG_WARNING, "No address associated with '%s'\n", hname);
return -1;
}
@@ -3112,6 +3396,7 @@ static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, str
int genuine = 0;
struct timeval *delivery = NULL;
+
/* What sort of frame do we have?: voice is self-explanatory
"genuine" means an IAX frame - things like LAGRQ/RP, PING/PONG, ACK
non-genuine frames are CONTROL frames [ringing etc], DTMF
@@ -3297,12 +3582,16 @@ static struct iax2_trunk_peer *find_tpeer(struct sockaddr_in *sin, int fd)
return tpeer;
}
-static int iax2_trunk_queue(struct chan_iax2_pvt *pvt, struct ast_frame *f)
+static int iax2_trunk_queue(struct chan_iax2_pvt *pvt, struct iax_frame *fr)
{
+ struct ast_frame *f;
struct iax2_trunk_peer *tpeer;
void *tmp, *ptr;
struct ast_iax2_meta_trunk_entry *met;
+ struct ast_iax2_meta_trunk_mini *mtm;
char iabuf[INET_ADDRSTRLEN];
+
+ f = &fr->af;
tpeer = find_tpeer(&pvt->addr, pvt->sockfd);
if (tpeer) {
if (tpeer->trunkdatalen + f->datalen + 4 >= tpeer->trunkdataalloc) {
@@ -3324,19 +3613,29 @@ static int iax2_trunk_queue(struct chan_iax2_pvt *pvt, struct ast_frame *f)
return -1;
}
}
-
+
/* Append to meta frame */
ptr = tpeer->trunkdata + IAX2_TRUNK_PREFACE + tpeer->trunkdatalen;
- met = (struct ast_iax2_meta_trunk_entry *)ptr;
- /* Store call number and length in meta header */
- met->callno = htons(pvt->callno);
- met->len = htons(f->datalen);
- /* Advance pointers/decrease length past trunk entry header */
- ptr += sizeof(struct ast_iax2_meta_trunk_entry);
- tpeer->trunkdatalen += sizeof(struct ast_iax2_meta_trunk_entry);
+ if(send_trunktimestamps) {
+ mtm = (struct ast_iax2_meta_trunk_mini *)ptr;
+ mtm->len = htons(f->datalen);
+ mtm->mini.callno = htons(pvt->callno);
+ mtm->mini.ts = htons(0xffff & fr->ts);
+ ptr += sizeof(struct ast_iax2_meta_trunk_mini);
+ tpeer->trunkdatalen += sizeof(struct ast_iax2_meta_trunk_mini);
+ } else {
+ met = (struct ast_iax2_meta_trunk_entry *)ptr;
+ /* Store call number and length in meta header */
+ met->callno = htons(pvt->callno);
+ met->len = htons(f->datalen);
+ /* Advance pointers/decrease length past trunk entry header */
+ ptr += sizeof(struct ast_iax2_meta_trunk_entry);
+ tpeer->trunkdatalen += sizeof(struct ast_iax2_meta_trunk_entry);
+ }
/* Copy actual trunk data */
memcpy(ptr, f->data, f->datalen);
tpeer->trunkdatalen += f->datalen;
+
tpeer->calls++;
ast_mutex_unlock(&tpeer->lock);
}
@@ -3519,6 +3818,13 @@ static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned in
/* Calculate actual timestamp */
fts = calc_timestamp(pvt, ts, f);
+ /* Bail here if this is an "interp" frame; we don't want or need to send these placeholders out
+ * (the endpoint should detect the lost packet itself). But, we want to do this here, so that we
+ * increment the "predicted timestamps" for voice, if we're predecting */
+ if(f->frametype == AST_FRAME_VOICE && f->datalen == 0)
+ return 0;
+
+
if ((ast_test_flag(pvt, IAX_TRUNK) || ((fts & 0xFFFF0000L) == (lastsent & 0xFFFF0000L)))
/* High two bytes are the same on timestamp, or sending on a trunk */ &&
(f->frametype == AST_FRAME_VOICE)
@@ -3609,7 +3915,7 @@ static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned in
res = iax2_transmit(fr);
} else {
if (ast_test_flag(pvt, IAX_TRUNK)) {
- iax2_trunk_queue(pvt, &fr->af);
+ iax2_trunk_queue(pvt, fr);
res = 0;
} else if (fr->af.frametype == AST_FRAME_VIDEO) {
/* Video frame have no sequence number */
@@ -3899,6 +4205,7 @@ static int iax2_show_registry(int fd, int argc, char *argv[])
#undef FORMAT2
}
+#ifndef NEWJB
static int jitterbufsize(struct chan_iax2_pvt *pvt) {
int min, i;
min = 99999999;
@@ -3911,6 +4218,7 @@ static int jitterbufsize(struct chan_iax2_pvt *pvt) {
else
return pvt->jitterbuffer - min;
}
+#endif
static int iax2_show_channels(int fd, int argc, char *argv[])
{
@@ -3920,6 +4228,7 @@ static int iax2_show_channels(int fd, int argc, char *argv[])
int x;
int numchans = 0;
char iabuf[INET_ADDRSTRLEN];
+
if (argc != 3)
return RESULT_SHOWUSAGE;
ast_cli(fd, FORMAT2, "Channel", "Peer", "Username", "ID (Lo/Rem)", "Seq (Tx/Rx)", "Lag", "Jitter", "JitBuf", "Format");
@@ -3937,16 +4246,35 @@ static int iax2_show_channels(int fd, int argc, char *argv[])
iaxs[x]->bridgecallno );
else
#endif
+ {
+ int lag, jitter, localdelay;
+#ifdef NEWJB
+ jb_info jbinfo;
+
+ if(ast_test_flag(iaxs[x], IAX_USEJITTERBUF)) {
+ jb_getinfo(iaxs[x]->jb, &jbinfo);
+ jitter = jbinfo.jitter;
+ localdelay = jbinfo.current - jbinfo.min;
+ } else {
+ jitter = -1;
+ localdelay = 0;
+ }
+#else
+ jitter = iaxs[x]->jitter;
+ localdelay = ast_test_flag(iaxs[x], IAX_USEJITTERBUF) ? jitterbufsize(iaxs[x]) : 0;
+#endif
+ lag = iaxs[x]->remote_rr.delay;
ast_cli(fd, FORMAT,
iaxs[x]->owner ? iaxs[x]->owner->name : "(None)",
ast_inet_ntoa(iabuf, sizeof(iabuf), iaxs[x]->addr.sin_addr),
!ast_strlen_zero(iaxs[x]->username) ? iaxs[x]->username : "(None)",
iaxs[x]->callno, iaxs[x]->peercallno,
iaxs[x]->oseqno, iaxs[x]->iseqno,
- iaxs[x]->lag,
- iaxs[x]->jitter,
- ast_test_flag(iaxs[x], IAX_USEJITTERBUF) ? jitterbufsize(iaxs[x]) : 0,
+ lag,
+ jitter,
+ localdelay,
ast_getformatname(iaxs[x]->voiceformat) );
+ }
numchans++;
}
ast_mutex_unlock(&iaxsl[x]);
@@ -3975,15 +4303,47 @@ static int iax2_show_netstats(int fd, int argc, char *argv[])
iaxs[x]->owner ? iaxs[x]->owner->name : "(None)");
else
#endif
+ {
+ int localjitter, localdelay, locallost, locallosspct, localdropped, localooo;
+#ifdef NEWJB
+ jb_info jbinfo;
+
+ if(ast_test_flag(iaxs[x], IAX_USEJITTERBUF)) {
+ jb_getinfo(iaxs[x]->jb, &jbinfo);
+ localjitter = jbinfo.jitter;
+ localdelay = jbinfo.current - jbinfo.min;
+ locallost = jbinfo.frames_lost;
+ locallosspct = jbinfo.losspct/1000;
+ localdropped = jbinfo.frames_dropped;
+ localooo = jbinfo.frames_ooo;
+ } else {
+ localjitter = -1;
+ localdelay = 0;
+ locallost = -1;
+ locallosspct = -1;
+ localdropped = 0;
+ localooo = -1;
+ }
+#else
+ localjitter = iaxs[x]->jitter;
+ if(ast_test_flag(iaxs[x], IAX_USEJITTERBUF))
+ {
+ localdelay = jitterbufsize(iaxs[x]);
+ localdropped = iaxs[x]->frames_dropped;
+ } else {
+ localdelay = localdropped = 0;
+ }
+ locallost = locallosspct = localooo = -1;
+#endif
ast_cli(fd, "%-25.25s %4d %4d %4d %5d %3d %5d %4d %6d %4d %4d %5d %3d %5d %4d %6d\n",
iaxs[x]->owner ? iaxs[x]->owner->name : "(None)",
iaxs[x]->pingtime,
- iaxs[x]->jitter,
- ast_test_flag(iaxs[x], IAX_USEJITTERBUF) ? jitterbufsize(iaxs[x]) : 0,
- -1,
- -1,
- -1,
- -1,
+ localjitter,
+ localdelay,
+ locallost,
+ locallosspct,
+ localdropped,
+ localooo,
iaxs[x]->frames_received/1000,
iaxs[x]->remote_rr.jitter,
iaxs[x]->remote_rr.delay,
@@ -3993,6 +4353,7 @@ static int iax2_show_netstats(int fd, int argc, char *argv[])
iaxs[x]->remote_rr.ooo,
iaxs[x]->remote_rr.packets/1000
);
+ }
numchans++;
}
ast_mutex_unlock(&iaxsl[x]);
@@ -4015,6 +4376,9 @@ static int iax2_do_debug(int fd, int argc, char *argv[])
if (argc != 2)
return RESULT_SHOWUSAGE;
iaxdebug = 1;
+#ifdef NEWJB
+ jb_setoutput(jb_error_output, jb_warning_output, jb_debug_output);
+#endif
ast_cli(fd, "IAX2 Debugging Enabled\n");
return RESULT_SUCCESS;
}
@@ -4024,6 +4388,9 @@ static int iax2_no_debug(int fd, int argc, char *argv[])
if (argc != 3)
return RESULT_SHOWUSAGE;
iaxdebug = 0;
+#ifdef NEWJB
+ jb_setoutput(jb_error_output, jb_warning_output, NULL);
+#endif
ast_cli(fd, "IAX2 Debugging Disabled\n");
return RESULT_SUCCESS;
}
@@ -4069,6 +4436,10 @@ static char debug_trunk_usage[] =
"Usage: iax2 trunk debug\n"
" Requests current status of IAX trunking\n";
+static char iax2_test_losspct_usage[] =
+"Usage: iax2 test losspct <percentage>\n"
+" For testing, throws away <percentage> percent of incoming packets\n";
+
static struct ast_cli_entry cli_show_users =
{ { "iax2", "show", "users", NULL }, iax2_show_users, "Show defined IAX users", show_users_usage };
static struct ast_cli_entry cli_show_firmware =
@@ -4087,6 +4458,8 @@ static struct ast_cli_entry cli_trunk_debug =
{ { "iax2", "trunk", "debug", NULL }, iax2_do_trunk_debug, "Request IAX trunk debug", debug_trunk_usage };
static struct ast_cli_entry cli_no_debug =
{ { "iax2", "no", "debug", NULL }, iax2_no_debug, "Disable IAX debugging", no_debug_usage };
+static struct ast_cli_entry cli_test_losspct =
+ { { "iax2", "test", "losspct", NULL }, iax2_test_losspct, "Set IAX2 incoming frame loss percentage", iax2_test_losspct_usage };
static int iax2_write(struct ast_channel *c, struct ast_frame *f)
{
@@ -4345,7 +4718,7 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
iaxs[callno]->amaflags = user->amaflags;
if (!ast_strlen_zero(user->language))
strncpy(iaxs[callno]->language, user->language, sizeof(iaxs[callno]->language)-1);
- ast_copy_flags(iaxs[callno], user, IAX_NOTRANSFER | IAX_USEJITTERBUF);
+ ast_copy_flags(iaxs[callno], user, IAX_NOTRANSFER | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
/* Keep this check last */
if (!ast_strlen_zero(user->dbsecret)) {
char *family, *key=NULL;
@@ -4858,10 +5231,20 @@ static int complete_transfer(int callno, struct iax_ies *ies)
pvt->transfercallno = -1;
memset(&pvt->rxcore, 0, sizeof(pvt->rxcore));
memset(&pvt->offset, 0, sizeof(pvt->offset));
+#ifdef NEWJB
+ { /* reset jitterbuffer */
+ jb_frame frame;
+ while(jb_getall(pvt->jb,&frame) == JB_OK)
+ iax2_frame_free(frame.data);
+
+ jb_reset(pvt->jb);
+ }
+#else
memset(&pvt->history, 0, sizeof(pvt->history));
pvt->jitterbuffer = 0;
pvt->jitter = 0;
pvt->historicjitter = 0;
+#endif
pvt->lag = 0;
pvt->last = 0;
pvt->lastsent = 0;
@@ -5250,6 +5633,11 @@ static int stop_stuff(int callno)
if (iaxs[callno]->authid > -1)
ast_sched_del(sched, iaxs[callno]->authid);
iaxs[callno]->authid = -1;
+#ifdef NEWJB
+ if (iaxs[callno]->jbid > -1)
+ ast_sched_del(sched, iaxs[callno]->jbid);
+ iaxs[callno]->jbid = -1;
+#endif
return 0;
}
@@ -5369,7 +5757,10 @@ static int send_trunk(struct iax2_trunk_peer *tpeer, struct timeval *now)
/* We're actually sending a frame, so fill the meta trunk header and meta header */
meta->zeros = 0;
meta->metacmd = IAX_META_TRUNK;
- meta->cmddata = 0;
+ if(send_trunktimestamps)
+ meta->cmddata = IAX_META_TRUNK_MINI;
+ else
+ meta->cmddata = IAX_META_TRUNK_SUPERMINI;
mth->ts = htonl(calc_txpeerstamp(tpeer, trunkfreq, now));
/* And the rest of the ast_iax2 header */
fr->direction = DIRECTION_OUTGRESS;
@@ -5643,6 +6034,20 @@ static int check_provisioning(struct sockaddr_in *sin, char *si, unsigned int ve
static void construct_rr(struct chan_iax2_pvt *pvt, struct iax_ie_data *iep)
{
+#ifdef NEWJB
+ jb_info stats;
+ jb_getinfo(pvt->jb, &stats);
+
+ memset(iep, 0, sizeof(*iep));
+
+ iax_ie_append_int(iep,IAX_IE_RR_JITTER, stats.jitter);
+ if(stats.frames_in == 0) stats.frames_in = 1;
+ iax_ie_append_int(iep,IAX_IE_RR_LOSS, ((0xff & (stats.losspct/1000)) << 24 | (stats.frames_lost & 0x00ffffff)));
+ iax_ie_append_int(iep,IAX_IE_RR_PKTS, stats.frames_in);
+ iax_ie_append_short(iep,IAX_IE_RR_DELAY, stats.current - stats.min);
+ iax_ie_append_int(iep,IAX_IE_RR_DROPPED, stats.frames_dropped);
+ iax_ie_append_int(iep,IAX_IE_RR_OOO, stats.frames_ooo);
+#else
memset(iep, 0, sizeof(*iep));
iax_ie_append_int(iep,IAX_IE_RR_JITTER, pvt->jitter);
iax_ie_append_int(iep,IAX_IE_RR_PKTS, pvt->frames_received);
@@ -5653,7 +6058,7 @@ static void construct_rr(struct chan_iax2_pvt *pvt, struct iax_ie_data *iep)
iax_ie_append_int(iep,IAX_IE_RR_DROPPED, pvt->frames_dropped);
/* don't know, don't send! iax_ie_append_int(&ied,IAX_IE_RR_OOO, 0); */
/* don't know, don't send! iax_ie_append_int(&ied,IAX_IE_RR_LOSS, 0); */
-
+#endif
}
static void save_rr(struct iax_frame *fr, struct iax_ies *ies)
@@ -5682,6 +6087,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
struct ast_iax2_video_hdr *vh = (struct ast_iax2_video_hdr *)buf;
struct ast_iax2_meta_trunk_hdr *mth;
struct ast_iax2_meta_trunk_entry *mte;
+ struct ast_iax2_meta_trunk_mini *mtm;
char dblbuf[4096]; /* Declaration of dblbuf must immediately *preceed* fr on the stack */
struct iax_frame fr;
struct iax_frame *cur;
@@ -5715,6 +6121,11 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
handle_error();
return 1;
}
+ if(test_losspct) { /* simulate random loss condition */
+ if( (100.0*rand()/(RAND_MAX+1.0)) < test_losspct)
+ return 1;
+
+ }
if (res < sizeof(struct ast_iax2_mini_hdr)) {
ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct ast_iax2_mini_hdr));
return 1;
@@ -5724,6 +6135,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
fr.callno = find_callno(ntohs(vh->callno) & ~0x8000, dcallno, &sin, new, 1, fd);
minivid = 1;
} else if (meta->zeros == 0) {
+ unsigned char metatype;
/* This is a meta header */
switch(meta->metacmd) {
case IAX_META_TRUNK:
@@ -5733,6 +6145,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
}
mth = (struct ast_iax2_meta_trunk_hdr *)(meta->data);
ts = ntohl(mth->ts);
+ metatype = meta->cmddata;
res -= (sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr));
ptr = mth->data;
tpeer = find_tpeer(&sin, fd);
@@ -5749,14 +6162,30 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
ast_mutex_unlock(&tpeer->lock);
while(res >= sizeof(struct ast_iax2_meta_trunk_entry)) {
/* Process channels */
- mte = (struct ast_iax2_meta_trunk_entry *)ptr;
- ptr += sizeof(struct ast_iax2_meta_trunk_entry);
- res -= sizeof(struct ast_iax2_meta_trunk_entry);
- len = ntohs(mte->len);
+ unsigned short callno, trunked_ts, len;
+
+ if(metatype == IAX_META_TRUNK_MINI) {
+ mtm = (struct ast_iax2_meta_trunk_mini *)ptr;
+ ptr += sizeof(struct ast_iax2_meta_trunk_mini);
+ res -= sizeof(struct ast_iax2_meta_trunk_mini);
+ len = ntohs(mtm->len);
+ callno = ntohs(mtm->mini.callno);
+ trunked_ts = ntohs(mtm->mini.ts);
+ } else if ( metatype == IAX_META_TRUNK_SUPERMINI ) {
+ mte = (struct ast_iax2_meta_trunk_entry *)ptr;
+ ptr += sizeof(struct ast_iax2_meta_trunk_entry);
+ res -= sizeof(struct ast_iax2_meta_trunk_entry);
+ len = ntohs(mte->len);
+ callno = ntohs(mte->callno);
+ trunked_ts = 0;
+ } else {
+ ast_log(LOG_WARNING, "Unknown meta trunk cmd from '%s:%d': dropping\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
+ break;
+ }
/* Stop if we don't have enough data */
if (len > res)
break;
- fr.callno = find_callno(ntohs(mte->callno) & ~IAX_FLAG_FULL, 0, &sin, NEW_PREVENT, 1, fd);
+ fr.callno = find_callno(callno & ~IAX_FLAG_FULL, 0, &sin, NEW_PREVENT, 1, fd);
if (fr.callno) {
ast_mutex_lock(&iaxsl[fr.callno]);
/* If it's a valid call, deliver the contents. If not, we
@@ -5772,7 +6201,10 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
f.data = ptr;
else
f.data = NULL;
- fr.ts = fix_peerts(&rxtrunktime, fr.callno, ts);
+ if(trunked_ts)
+ fr.ts = trunked_ts;
+ else
+ fr.ts = fix_peerts(&rxtrunktime, fr.callno, ts);
/* Don't pass any packets until we're started */
if ((iaxs[fr.callno]->state & IAX_STATE_STARTED)) {
/* Common things */
@@ -6987,7 +7419,7 @@ static int iax2_provision(struct sockaddr_in *end, char *dest, const char *templ
if (end)
memcpy(&sin, end, sizeof(sin));
else {
- if (create_addr(&sin, NULL, NULL, NULL, dest, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL, 0, NULL, 0, &sockfd))
+ if (create_addr(&sin, NULL, NULL, NULL, dest, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL, 0, NULL, 0, &sockfd))
return -1;
}
/* Build the rest of the message */
@@ -7162,6 +7594,8 @@ static struct ast_channel *iax2_request(const char *type, int format, void *data
int sockfd = defaultsockfd;
int notransfer = ast_test_flag((&globalflags), IAX_NOTRANSFER);
int usejitterbuf = ast_test_flag((&globalflags), IAX_USEJITTERBUF);
+ int forcejitterbuf = ast_test_flag((&globalflags), IAX_FORCEJITTERBUF);
+
strncpy(s, (char *)data, sizeof(s)-1);
/* FIXME The next two lines seem useless */
stringp=s;
@@ -7183,7 +7617,7 @@ static struct ast_channel *iax2_request(const char *type, int format, void *data
}
/* Populate our address from the given */
- if (create_addr(&sin, &capability, &sendani, &maxtime, hostname, NULL, &trunk, &notransfer, &usejitterbuf, NULL, NULL, 0, NULL, 0, &found, NULL, NULL, 0, NULL, 0, &sockfd)) {
+ if (create_addr(&sin, &capability, &sendani, &maxtime, hostname, NULL, &trunk, &notransfer, &usejitterbuf, &forcejitterbuf, NULL, NULL, 0, NULL, 0, &found, NULL, NULL, 0, NULL, 0, &sockfd)) {
*cause = AST_CAUSE_UNREGISTERED;
return NULL;
}
@@ -7206,6 +7640,7 @@ static struct ast_channel *iax2_request(const char *type, int format, void *data
iaxs[callno]->maxtime = maxtime;
ast_set2_flag(iaxs[callno], notransfer, IAX_NOTRANSFER);
ast_set2_flag(iaxs[callno], usejitterbuf, IAX_USEJITTERBUF);
+ ast_set2_flag(iaxs[callno], forcejitterbuf, IAX_FORCEJITTERBUF);
if (found)
strncpy(iaxs[callno]->host, hostname, sizeof(iaxs[callno]->host) - 1);
c = ast_iax2_new(callno, AST_STATE_DOWN, capability);
@@ -7358,7 +7793,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, in
}
}
if (peer) {
- ast_copy_flags(peer, (&globalflags), IAX_MESSAGEDETAIL | IAX_USEJITTERBUF);
+ ast_copy_flags(peer, (&globalflags), IAX_MESSAGEDETAIL | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
peer->encmethods = iax2_encryption;
peer->secret[0] = '\0';
if (!found) {
@@ -7381,6 +7816,8 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, in
strncpy(peer->dbsecret, v->value, sizeof(peer->dbsecret)-1);
else if (!strcasecmp(v->name, "mailboxdetail"))
ast_set2_flag(peer, ast_true(v->value), IAX_MESSAGEDETAIL);
+ else if (!strcasecmp(v->name, "trunktimestamps"))
+ send_trunktimestamps = ast_true(v->value);
else if (!strcasecmp(v->name, "trunk")) {
ast_set2_flag(peer, ast_true(v->value), IAX_TRUNK);
if (ast_test_flag(peer, IAX_TRUNK) && (timingfd < 0)) {
@@ -7395,6 +7832,8 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, in
ast_set2_flag(peer, ast_true(v->value), IAX_NOTRANSFER);
} else if (!strcasecmp(v->name, "jitterbuffer")) {
ast_set2_flag(peer, ast_true(v->value), IAX_USEJITTERBUF);
+ } else if (!strcasecmp(v->name, "forcejitterbuffer")) {
+ ast_set2_flag(peer, ast_true(v->value), IAX_FORCEJITTERBUF);
} else if (!strcasecmp(v->name, "host")) {
if (!strcasecmp(v->value, "dynamic")) {
/* They'll register with us */
@@ -7543,6 +7982,7 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, in
strncpy(user->name, name, sizeof(user->name)-1);
strncpy(user->language, language, sizeof(user->language) - 1);
ast_copy_flags(user, (&globalflags), IAX_USEJITTERBUF);
+ ast_copy_flags(user, (&globalflags), IAX_FORCEJITTERBUF);
ast_copy_flags(user, (&globalflags), IAX_CODEC_USER_FIRST);
ast_copy_flags(user, (&globalflags), IAX_CODEC_NOPREFS);
ast_copy_flags(user, (&globalflags), IAX_CODEC_NOCAP);
@@ -7596,6 +8036,8 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, in
}
} else if (!strcasecmp(v->name, "jitterbuffer")) {
ast_set2_flag(user, ast_true(v->value), IAX_USEJITTERBUF);
+ } else if (!strcasecmp(v->name, "forcejitterbuffer")) {
+ ast_set2_flag(user, ast_true(v->value), IAX_FORCEJITTERBUF);
} else if (!strcasecmp(v->name, "dbsecret")) {
strncpy(user->dbsecret, v->value, sizeof(user->dbsecret)-1);
} else if (!strcasecmp(v->name, "secret")) {
@@ -7854,6 +8296,8 @@ static int set_config(char *config_file, int reload)
}
} else if (!strcasecmp(v->name, "jitterbuffer"))
ast_set2_flag((&globalflags), ast_true(v->value), IAX_USEJITTERBUF);
+ else if (!strcasecmp(v->name, "forcejitterbuffer"))
+ ast_set2_flag((&globalflags), ast_true(v->value), IAX_FORCEJITTERBUF);
else if (!strcasecmp(v->name, "delayreject"))
delayreject = ast_true(v->value);
else if (!strcasecmp(v->name, "mailboxdetail"))
@@ -7983,6 +8427,7 @@ static int reload_config(void)
delayreject = 0;
ast_clear_flag((&globalflags), IAX_NOTRANSFER);
ast_clear_flag((&globalflags), IAX_USEJITTERBUF);
+ ast_clear_flag((&globalflags), IAX_FORCEJITTERBUF);
srand(time(NULL));
delete_users();
set_config(config,1);
@@ -8054,7 +8499,7 @@ static int cache_get_callno_locked(const char *data)
host = st;
}
/* Populate our address from the given */
- if (create_addr(&sin, NULL, NULL, NULL, host, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL, 0, NULL, 0, &sockfd)) {
+ if (create_addr(&sin, NULL, NULL, NULL, host, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL, 0, NULL, 0, &sockfd)) {
return -1;
}
ast_log(LOG_DEBUG, "host: %s, user: %s, password: %s, context: %s\n", host, username, password, context);
@@ -8446,6 +8891,7 @@ static int __unload_module(void)
ast_cli_unregister(&cli_debug);
ast_cli_unregister(&cli_trunk_debug);
ast_cli_unregister(&cli_no_debug);
+ ast_cli_unregister(&cli_test_losspct);
ast_cli_unregister(&cli_set_jitter);
ast_cli_unregister(&cli_show_stats);
ast_cli_unregister(&cli_show_cache);
@@ -8483,6 +8929,9 @@ int load_module(void)
iax_set_output(iax_debug_output);
iax_set_error(iax_error_output);
+#ifdef NEWJB
+ jb_setoutput(jb_error_output, jb_warning_output, NULL);
+#endif
/* Seed random number generator */
srand(time(NULL));
@@ -8530,6 +8979,7 @@ int load_module(void)
ast_cli_register(&cli_debug);
ast_cli_register(&cli_trunk_debug);
ast_cli_register(&cli_no_debug);
+ ast_cli_register(&cli_test_losspct);
ast_cli_register(&cli_set_jitter);
ast_cli_register(&cli_show_stats);
ast_cli_register(&cli_show_cache);
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 16b7ee423..75e6c6696 100755
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -479,6 +479,7 @@ static const struct ast_channel_tech mgcp_tech = {
.type = type,
.description = tdesc,
.capabilities = AST_FORMAT_ULAW,
+ .properties = AST_CHAN_TP_WANTSJITTER,
.requester = mgcp_request,
.call = mgcp_call,
.hangup = mgcp_hangup,
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 536a0ecf4..5bd01a1fe 100755
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -631,6 +631,7 @@ static const struct ast_channel_tech sip_tech = {
.type = channeltype,
.description = "Session Initiation Protocol (SIP)",
.capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
+ .properties = AST_CHAN_TP_WANTSJITTER,
.requester = sip_request,
.devicestate = sip_devicestate,
.call = sip_call,
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index 3b4d5269f..601634f49 100755
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -809,6 +809,7 @@ static const struct ast_channel_tech skinny_tech = {
.type = type,
.description = tdesc,
.capabilities = AST_FORMAT_ULAW,
+ .properties = AST_CHAN_TP_WANTSJITTER,
.requester = skinny_request,
.call = skinny_call,
.hangup = skinny_hangup,
diff --git a/channels/iax2.h b/channels/iax2.h
index 4e71c7611..c08d54994 100755
--- a/channels/iax2.h
+++ b/channels/iax2.h
@@ -138,6 +138,9 @@
#define IAX_META_TRUNK 1 /* Trunk meta-message */
#define IAX_META_VIDEO 2 /* Video frame */
+#define IAX_META_TRUNK_SUPERMINI 0 /* This trunk frame contains classic supermini frames */
+#define IAX_META_TRUNK_MINI 1 /* This trunk frame contains trunked mini frames */
+
#define IAX_RATE_8KHZ (1 << 0) /* 8khz sampling (default if absent) */
#define IAX_RATE_11KHZ (1 << 1) /* 11.025khz sampling */
#define IAX_RATE_16KHZ (1 << 2) /* 16khz sampling */
@@ -209,6 +212,12 @@ struct ast_iax2_meta_trunk_entry {
unsigned short len; /* Length of data for this callno */
} __attribute__ ((__packed__));
+/* When trunktimestamps are used, we use this format instead */
+struct ast_iax2_meta_trunk_mini {
+ unsigned short len;
+ struct ast_iax2_mini_hdr mini; /* this is an actual miniframe */
+} __attribute__ ((__packed__));
+
#define IAX_FIRMWARE_MAGIC 0x69617879
struct ast_iax2_firmware_header {
diff --git a/codecs/codec_adpcm.c b/codecs/codec_adpcm.c
index 703792649..7ec1571da 100755
--- a/codecs/codec_adpcm.c
+++ b/codecs/codec_adpcm.c
@@ -17,6 +17,8 @@
#include <asterisk/lock.h>
#include <asterisk/logger.h>
#include <asterisk/module.h>
+#include <asterisk/config.h>
+#include <asterisk/options.h>
#include <asterisk/translate.h>
#include <asterisk/channel.h>
#include <fcntl.h>
@@ -36,6 +38,8 @@ static int localusecnt = 0;
static char *tdesc = "Adaptive Differential PCM Coder/Decoder";
+static int useplc = 0;
+
/* Sample frame data */
#include "slin_adpcm_ex.h"
@@ -236,6 +240,7 @@ struct adpcm_decoder_pvt
short outbuf[BUFFER_SIZE]; /* Decoded signed linear values */
struct adpcm_state state;
int tail;
+ plc_state_t plc;
};
/*
@@ -258,6 +263,7 @@ adpcmtolin_new (void)
{
memset(tmp, 0, sizeof(*tmp));
tmp->tail = 0;
+ plc_init(&tmp->plc);
localusecnt++;
ast_update_use_count ();
}
@@ -292,8 +298,8 @@ lintoadpcm_new (void)
/*
* AdpcmToLin_FrameIn
- * Fill an input buffer with packed 4-bit ADPCM values if there is room
- * left.
+ * Take an input buffer with packed 4-bit ADPCM values and put decoded PCM in outbuf,
+ * if there is room left.
*
* Results:
* Foo
@@ -309,7 +315,19 @@ adpcmtolin_framein (struct ast_translator_pvt *pvt, struct ast_frame *f)
int x;
unsigned char *b;
- if (f->datalen * 4 > sizeof(tmp->outbuf)) {
+ if(f->datalen == 0) { /* perform PLC with nominal framesize of 20ms/160 samples */
+ if((tmp->tail + 160) > sizeof(tmp->outbuf) / 2) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ if(useplc) {
+ plc_fillin(&tmp->plc, tmp->outbuf+tmp->tail, 160);
+ tmp->tail += 160;
+ }
+ return 0;
+ }
+
+ if (f->datalen * 4 + tmp->tail * 2 > sizeof(tmp->outbuf)) {
ast_log(LOG_WARNING, "Out of buffer space\n");
return -1;
}
@@ -321,6 +339,8 @@ adpcmtolin_framein (struct ast_translator_pvt *pvt, struct ast_frame *f)
tmp->outbuf[tmp->tail++] = decode(b[x] & 0x0f, &tmp->state);
}
+ if(useplc) plc_rx(&tmp->plc, tmp->outbuf+tmp->tail-f->datalen*2, f->datalen*2);
+
return 0;
}
@@ -538,6 +558,32 @@ static struct ast_translator lintoadpcm = {
lintoadpcm_sample
};
+static void
+parse_config(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ if ((cfg = ast_config_load("codecs.conf"))) {
+ if ((var = ast_variable_browse(cfg, "plc"))) {
+ while (var) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ useplc = ast_true(var->value) ? 1 : 0;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "CODEC ULAW: %susing generic PLC\n", useplc ? "" : "not ");
+ }
+ var = var->next;
+ }
+ }
+ }
+}
+
+int
+reload(void)
+{
+ parse_config();
+ return 0;
+}
+
int
unload_module (void)
{
@@ -556,6 +602,7 @@ int
load_module (void)
{
int res;
+ parse_config();
res = ast_register_translator (&adpcmtolin);
if (!res)
res = ast_register_translator (&lintoadpcm);
diff --git a/codecs/codec_alaw.c b/codecs/codec_alaw.c
index 6ba4e1da9..6705dff21 100755
--- a/codecs/codec_alaw.c
+++ b/codecs/codec_alaw.c
@@ -13,6 +13,8 @@
#include <asterisk/lock.h>
#include <asterisk/logger.h>
#include <asterisk/module.h>
+#include <asterisk/config.h>
+#include <asterisk/options.h>
#include <asterisk/translate.h>
#include <asterisk/channel.h>
#include <asterisk/alaw.h>
@@ -30,6 +32,8 @@ static int localusecnt = 0;
static char *tdesc = "A-law Coder/Decoder";
+static int useplc = 0;
+
/* Sample frame data (Mu data is okay) */
#include "slin_ulaw_ex.h"
@@ -57,6 +61,7 @@ struct alaw_decoder_pvt
char offset[AST_FRIENDLY_OFFSET]; /* Space to build offset */
short outbuf[BUFFER_SIZE]; /* Decoded signed linear values */
int tail;
+ plc_state_t plc;
};
/*
@@ -79,6 +84,7 @@ alawtolin_new (void)
{
memset(tmp, 0, sizeof(*tmp));
tmp->tail = 0;
+ plc_init(&tmp->plc);
localusecnt++;
ast_update_use_count ();
}
@@ -130,6 +136,18 @@ alawtolin_framein (struct ast_translator_pvt *pvt, struct ast_frame *f)
int x;
unsigned char *b;
+ if(f->datalen == 0) { /* perform PLC with nominal framesize of 20ms/160 samples */
+ if((tmp->tail + 160) * 2 > sizeof(tmp->outbuf)) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ if(useplc) {
+ plc_fillin(&tmp->plc, tmp->outbuf+tmp->tail, 160);
+ tmp->tail += 160;
+ }
+ return 0;
+ }
+
if ((tmp->tail + f->datalen) * 2 > sizeof(tmp->outbuf)) {
ast_log(LOG_WARNING, "Out of buffer space\n");
return -1;
@@ -140,6 +158,8 @@ alawtolin_framein (struct ast_translator_pvt *pvt, struct ast_frame *f)
for (x=0;x<f->datalen;x++)
tmp->outbuf[tmp->tail + x] = AST_ALAW(b[x]);
+ if(useplc) plc_rx(&tmp->plc, tmp->outbuf+tmp->tail, f->datalen);
+
tmp->tail += f->datalen;
return 0;
}
@@ -327,6 +347,33 @@ static struct ast_translator lintoalaw = {
lintoalaw_sample
};
+static void
+parse_config(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+
+ if ((cfg = ast_config_load("codecs.conf"))) {
+ if ((var = ast_variable_browse(cfg, "plc"))) {
+ while (var) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ useplc = ast_true(var->value) ? 1 : 0;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "CODEC ULAW: %susing generic PLC\n", useplc ? "" : "not ");
+ }
+ var = var->next;
+ }
+ }
+ }
+}
+
+int
+reload(void)
+{
+ parse_config();
+ return 0;
+}
+
int
unload_module (void)
{
@@ -345,6 +392,7 @@ int
load_module (void)
{
int res;
+ parse_config();
res = ast_register_translator (&alawtolin);
if (!res)
res = ast_register_translator (&lintoalaw);
diff --git a/codecs/codec_g726.c b/codecs/codec_g726.c
index 8ac405854..80d978c80 100755
--- a/codecs/codec_g726.c
+++ b/codecs/codec_g726.c
@@ -16,6 +16,8 @@
#include <asterisk/lock.h>
#include <asterisk/logger.h>
#include <asterisk/module.h>
+#include <asterisk/config.h>
+#include <asterisk/options.h>
#include <asterisk/translate.h>
#include <asterisk/channel.h>
#include <fcntl.h>
@@ -49,6 +51,8 @@ static int localusecnt = 0;
static char *tdesc = "ITU G.726-32kbps G726 Transcoder";
+static int useplc = 0;
+
/* Sample frame data */
#include "slin_g726_ex.h"
@@ -694,6 +698,7 @@ struct g726_decoder_pvt
short outbuf[BUFFER_SIZE]; /* Decoded signed linear values */
struct g726_state g726;
int tail;
+ plc_state_t plc;
};
/*
@@ -716,6 +721,7 @@ g726tolin_new (void)
{
memset(tmp, 0, sizeof(*tmp));
tmp->tail = 0;
+ plc_init(&tmp->plc);
localusecnt++;
g726_init_state(&tmp->g726);
ast_update_use_count ();
@@ -769,6 +775,18 @@ g726tolin_framein (struct ast_translator_pvt *pvt, struct ast_frame *f)
unsigned char *b;
int x;
+ if(f->datalen == 0) { /* perform PLC with nominal framesize of 20ms/160 samples */
+ if((tmp->tail + 160) > BUFFER_SIZE) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ if(useplc) {
+ plc_fillin(&tmp->plc, tmp->outbuf+tmp->tail, 160);
+ tmp->tail += 160;
+ }
+ return 0;
+ }
+
b = f->data;
for (x=0;x<f->datalen;x++) {
if (tmp->tail >= BUFFER_SIZE) {
@@ -783,6 +801,8 @@ g726tolin_framein (struct ast_translator_pvt *pvt, struct ast_frame *f)
tmp->outbuf[tmp->tail++] = g726_decode(b[x] & 0x0f, &tmp->g726);
}
+ if(useplc) plc_rx(&tmp->plc, tmp->outbuf+tmp->tail-f->datalen*2, f->datalen*2);
+
return 0;
}
@@ -974,6 +994,32 @@ static struct ast_translator lintog726 = {
lintog726_sample
};
+static void
+parse_config(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ if ((cfg = ast_config_load("codecs.conf"))) {
+ if ((var = ast_variable_browse(cfg, "plc"))) {
+ while (var) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ useplc = ast_true(var->value) ? 1 : 0;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "CODEC ULAW: %susing generic PLC\n", useplc ? "" : "not ");
+ }
+ var = var->next;
+ }
+ }
+ }
+}
+
+int
+reload(void)
+{
+ parse_config();
+ return 0;
+}
+
int
unload_module (void)
{
@@ -992,6 +1038,7 @@ int
load_module (void)
{
int res;
+ parse_config();
res = ast_register_translator (&g726tolin);
if (!res)
res = ast_register_translator (&lintog726);
diff --git a/codecs/codec_gsm.c b/codecs/codec_gsm.c
index 286dc4fb8..30dc2d498 100755
--- a/codecs/codec_gsm.c
+++ b/codecs/codec_gsm.c
@@ -21,6 +21,8 @@
#include <asterisk/lock.h>
#include <asterisk/translate.h>
+#include <asterisk/config.h>
+#include <asterisk/options.h>
#include <asterisk/module.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
@@ -43,6 +45,8 @@ static int localusecnt=0;
static char *tdesc = "GSM/PCM16 (signed linear) Codec Translator";
+static int useplc = 0;
+
struct ast_translator_pvt {
gsm gsm;
struct ast_frame f;
@@ -53,6 +57,7 @@ struct ast_translator_pvt {
/* Enough to store a full second */
short buf[8000];
int tail;
+ plc_state_t plc;
};
#define gsm_coder_pvt ast_translator_pvt
@@ -67,6 +72,7 @@ static struct ast_translator_pvt *gsm_new(void)
tmp = NULL;
}
tmp->tail = 0;
+ plc_init(&tmp->plc);
localusecnt++;
}
return tmp;
@@ -131,6 +137,18 @@ static int gsmtolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
unsigned char data[66];
int msgsm=0;
+ if(f->datalen == 0) { /* perform PLC with nominal framesize of 20ms/160 samples */
+ if((tmp->tail + 160) > sizeof(tmp->buf) / 2) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ if(useplc) {
+ plc_fillin(&tmp->plc, tmp->buf+tmp->tail, 160);
+ tmp->tail += 160;
+ }
+ return 0;
+ }
+
if ((f->datalen % 33) && (f->datalen % 65)) {
ast_log(LOG_WARNING, "Huh? A GSM frame that isn't a multiple of 33 or 65 bytes long from %s (%d)?\n", f->src, f->datalen);
return -1;
@@ -171,6 +189,10 @@ static int gsmtolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
}
}
}
+
+ /* just add the last 20ms frame; there must have been at least one */
+ if(useplc) plc_rx(&tmp->plc, tmp->buf+tmp->tail-160, 160);
+
return 0;
}
@@ -249,6 +271,31 @@ static struct ast_translator lintogsm =
lintogsm_sample
};
+
+static void parse_config(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ if ((cfg = ast_config_load("codecs.conf"))) {
+ if ((var = ast_variable_browse(cfg, "plc"))) {
+ while (var) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ useplc = ast_true(var->value) ? 1 : 0;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "CODEC ULAW: %susing generic PLC\n", useplc ? "" : "not ");
+ }
+ var = var->next;
+ }
+ }
+ }
+}
+
+int reload(void)
+{
+ parse_config();
+ return 0;
+}
+
int unload_module(void)
{
int res;
@@ -265,6 +312,7 @@ int unload_module(void)
int load_module(void)
{
int res;
+ parse_config();
res=ast_register_translator(&gsmtolin);
if (!res)
res=ast_register_translator(&lintogsm);
diff --git a/codecs/codec_ilbc.c b/codecs/codec_ilbc.c
index 4bbb28c49..1c7428e8f 100755
--- a/codecs/codec_ilbc.c
+++ b/codecs/codec_ilbc.c
@@ -141,7 +141,19 @@ static int ilbctolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *f
the tail location. Read in as many frames as there are */
int x,i;
float tmpf[240];
-
+
+ if (f->datalen == 0) { /* native PLC */
+ if (tmp->tail + 240 < sizeof(tmp->buf)/2) {
+ iLBC_decode(tmpf, NULL, &tmp->dec, 0);
+ for (i=0;i<240;i++)
+ tmp->buf[tmp->tail + i] = tmpf[i];
+ tmp->tail+=240;
+ } else {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ }
+
if (f->datalen % 50) {
ast_log(LOG_WARNING, "Huh? An ilbc frame that isn't a multiple of 50 bytes long from %s (%d)?\n", f->src, f->datalen);
return -1;
diff --git a/codecs/codec_lpc10.c b/codecs/codec_lpc10.c
index 4a6925949..904f0266a 100755
--- a/codecs/codec_lpc10.c
+++ b/codecs/codec_lpc10.c
@@ -19,6 +19,8 @@
#include <asterisk/lock.h>
#include <asterisk/translate.h>
+#include <asterisk/config.h>
+#include <asterisk/options.h>
#include <asterisk/module.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
@@ -47,6 +49,8 @@ static int localusecnt=0;
static char *tdesc = "LPC10 2.4kbps (signed linear) Voice Coder";
+static int useplc = 0;
+
struct ast_translator_pvt {
union {
struct lpc10_encoder_state *enc;
@@ -61,6 +65,7 @@ struct ast_translator_pvt {
short buf[8000];
int tail;
int longer;
+ plc_state_t plc; /* god only knows why I bothered to implement PLC for LPC10 :) */
};
#define lpc10_coder_pvt ast_translator_pvt
@@ -92,6 +97,7 @@ static struct ast_translator_pvt *lpc10_dec_new(void)
}
tmp->tail = 0;
tmp->longer = 0;
+ plc_init(&tmp->plc);
localusecnt++;
}
return tmp;
@@ -199,6 +205,19 @@ static int lpc10tolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *
float tmpbuf[LPC10_SAMPLES_PER_FRAME];
short *sd;
INT32 bits[LPC10_BITS_IN_COMPRESSED_FRAME];
+
+ if(f->datalen == 0) { /* perform PLC with nominal framesize of LPC10_SAMPLES_PER_FRAME */
+ if((tmp->tail + LPC10_SAMPLES_PER_FRAME) > sizeof(tmp->buf)/2) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ if(useplc) {
+ plc_fillin(&tmp->plc, tmp->buf+tmp->tail, LPC10_SAMPLES_PER_FRAME);
+ tmp->tail += LPC10_SAMPLES_PER_FRAME;
+ }
+ return 0;
+ }
+
while(len + LPC10_BYTES_IN_COMPRESSED_FRAME <= f->datalen) {
if (tmp->tail + LPC10_SAMPLES_PER_FRAME < sizeof(tmp->buf)/2) {
sd = tmp->buf + tmp->tail;
@@ -211,6 +230,8 @@ static int lpc10tolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *
/* Convert to a real between -1.0 and 1.0 */
sd[x] = 32768.0 * tmpbuf[x];
}
+
+ if(useplc) plc_rx(&tmp->plc, tmp->buf + tmp->tail, LPC10_SAMPLES_PER_FRAME);
tmp->tail+=LPC10_SAMPLES_PER_FRAME;
} else {
@@ -326,6 +347,31 @@ static struct ast_translator lintolpc10 =
lintolpc10_sample
};
+static void parse_config(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ if ((cfg = ast_config_load("codecs.conf"))) {
+ if ((var = ast_variable_browse(cfg, "plc"))) {
+ while (var) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ useplc = ast_true(var->value) ? 1 : 0;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "CODEC ULAW: %susing generic PLC\n", useplc ? "" : "not ");
+ }
+ var = var->next;
+ }
+ }
+ }
+}
+
+int reload(void)
+{
+ parse_config();
+ return 0;
+}
+
+
int unload_module(void)
{
int res;
@@ -342,6 +388,7 @@ int unload_module(void)
int load_module(void)
{
int res;
+ parse_config();
res=ast_register_translator(&lpc10tolin);
if (!res)
res=ast_register_translator(&lintolpc10);
diff --git a/codecs/codec_speex.c b/codecs/codec_speex.c
index 0f7bdd6c7..d59a849b4 100755
--- a/codecs/codec_speex.c
+++ b/codecs/codec_speex.c
@@ -181,6 +181,21 @@ static int speextolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *
int x;
int res;
float fout[1024];
+
+ if(f->datalen == 0) { /* Native PLC interpolation */
+ if(tmp->tail + tmp->framesize > sizeof(tmp->buf) / 2) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ speex_decode(tmp->speex, NULL, fout);
+ for (x=0;x<tmp->framesize;x++) {
+ tmp->buf[tmp->tail + x] = fout[x];
+ }
+ tmp->tail += tmp->framesize;
+ return 0;
+ }
+
+
/* Read in bits */
speex_bits_read_from(&tmp->bits, f->data, f->datalen);
for(;;) {
@@ -357,11 +372,12 @@ static void parse_config(void)
ast_mutex_unlock(&localuser_lock);
} else if (!strcasecmp(var->name, "abr")) {
res = abs(atoi(var->value));
- if (option_verbose > 2)
+ if (option_verbose > 2) {
if(res > 0)
ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Setting ABR target bitrate to %d\n",res);
else
- ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Disabling ABR\n",res);
+ ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Disabling ABR\n");
+ }
if (res >= 0) {
ast_mutex_lock(&localuser_lock);
abr = res;
diff --git a/codecs/codec_ulaw.c b/codecs/codec_ulaw.c
index 698733a3c..4f2b24c7d 100755
--- a/codecs/codec_ulaw.c
+++ b/codecs/codec_ulaw.c
@@ -13,6 +13,8 @@
#include <asterisk/lock.h>
#include <asterisk/logger.h>
#include <asterisk/module.h>
+#include <asterisk/config.h>
+#include <asterisk/options.h>
#include <asterisk/translate.h>
#include <asterisk/channel.h>
#include <asterisk/ulaw.h>
@@ -30,6 +32,8 @@ static int localusecnt = 0;
static char *tdesc = "Mu-law Coder/Decoder";
+static int useplc = 0;
+
/* Sample frame data */
#include "slin_ulaw_ex.h"
@@ -57,6 +61,7 @@ struct ulaw_decoder_pvt
char offset[AST_FRIENDLY_OFFSET]; /* Space to build offset */
short outbuf[BUFFER_SIZE]; /* Decoded signed linear values */
int tail;
+ plc_state_t plc;
};
/*
@@ -79,6 +84,7 @@ ulawtolin_new (void)
{
memset(tmp, 0, sizeof(*tmp));
tmp->tail = 0;
+ plc_init(&tmp->plc);
localusecnt++;
ast_update_use_count ();
}
@@ -130,6 +136,18 @@ ulawtolin_framein (struct ast_translator_pvt *pvt, struct ast_frame *f)
int x;
unsigned char *b;
+ if(f->datalen == 0) { /* perform PLC with nominal framesize of 20ms/160 samples */
+ if((tmp->tail + 160) * 2 > sizeof(tmp->outbuf)) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ if(useplc) {
+ plc_fillin(&tmp->plc, tmp->outbuf+tmp->tail, 160);
+ tmp->tail += 160;
+ }
+ return 0;
+ }
+
if ((tmp->tail + f->datalen) * 2 > sizeof(tmp->outbuf)) {
ast_log(LOG_WARNING, "Out of buffer space\n");
return -1;
@@ -140,6 +158,8 @@ ulawtolin_framein (struct ast_translator_pvt *pvt, struct ast_frame *f)
for (x=0;x<f->datalen;x++)
tmp->outbuf[tmp->tail + x] = AST_MULAW(b[x]);
+ if(useplc) plc_rx(&tmp->plc, tmp->outbuf+tmp->tail, f->datalen);
+
tmp->tail += f->datalen;
return 0;
}
@@ -327,6 +347,33 @@ static struct ast_translator lintoulaw = {
lintoulaw_sample
};
+static void
+parse_config(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ if ((cfg = ast_config_load("codecs.conf"))) {
+ if ((var = ast_variable_browse(cfg, "plc"))) {
+ while (var) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ useplc = ast_true(var->value) ? 1 : 0;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "CODEC ULAW: %susing generic PLC\n", useplc ? "" : "not ");
+ }
+ var = var->next;
+ }
+ }
+ }
+}
+
+int
+reload(void)
+{
+ parse_config();
+ return 0;
+}
+
+
int
unload_module (void)
{
@@ -345,6 +392,7 @@ int
load_module (void)
{
int res;
+ parse_config();
res = ast_register_translator (&ulawtolin);
if (!res)
res = ast_register_translator (&lintoulaw);
diff --git a/configs/codecs.conf.sample b/configs/codecs.conf.sample
index a6d7ea16a..ec3c496e3 100755
--- a/configs/codecs.conf.sample
+++ b/configs/codecs.conf.sample
@@ -15,3 +15,9 @@ abr => 0
vbr_quality => 5
; true / false
dtx => false
+
+[plc]
+; for all codecs which do not support native PLC
+; this determines whether to perform generic PLC
+; there is a minor performance penalty for this
+genericplc => true
diff --git a/configs/iax.conf.sample b/configs/iax.conf.sample
index 6da635789..b0ed7e1e7 100755
--- a/configs/iax.conf.sample
+++ b/configs/iax.conf.sample
@@ -61,6 +61,12 @@ disallow=lpc10 ; Icky sound quality... Mr. Roboto.
; The jitter buffer's function is to compensate for varying
; network delay.
;
+; There are presently two jitterbuffer implementations available for * and chan_iax2;
+; the classic and the new, channel/application independent implementation. These
+; are controlled at compile-time. The new jitterbuffer additionally has support for PLC
+; which greatly improves quality as the jitterbuffer adapts size, and in compensating for lost
+; packets.
+;
; All the jitter buffer settings except dropcount are in milliseconds.
; The jitter buffer works for INCOMING audio - the outbound audio
; will be dejittered by the jitter buffer at the other end.
@@ -68,9 +74,16 @@ disallow=lpc10 ; Icky sound quality... Mr. Roboto.
; jitterbuffer=yes|no: global default as to whether you want
; the jitter buffer at all.
;
+; forcejitterbuffer=yes|no: in the ideal world, when we bridge VoIP channels
+; we don't want to do jitterbuffering on the switch, since the endpoints
+; can each handle this. However, some endpoints may have poor jitterbuffers
+; themselves, so this option will force * to always jitterbuffer, even in this case.
+; [This option presently applies only to the new jitterbuffer implementation]
+;
; dropcount: the jitter buffer is sized such that no more than "dropcount"
; frames would have been "too late" over the last 2 seconds.
; Set to a small number. "3" represents 1.5% of frames dropped
+; [This option is not applicable to, and ignored by the new jitterbuffer implementation]
;
; maxjitterbuffer: a maximum size for the jitter buffer.
; Setting a reasonable maximum here will prevent the call delay
@@ -81,18 +94,22 @@ disallow=lpc10 ; Icky sound quality... Mr. Roboto.
; the jitter buffer can end up bigger than necessary. If it ends up
; more than "maxexcessbuffer" bigger than needed, Asterisk will start
; gradually decreasing the amount of jitter buffering.
+; [This option is not applicable to, and ignored by the new jitterbuffer implementation]
;
; minexcessbuffer: Sets a desired mimimum amount of headroom in
; the jitter buffer. If Asterisk has less headroom than this, then
; it will start gradually increasing the amount of jitter buffering.
+; [This option is not applicable to, and ignored by the new jitterbuffer implementation]
;
; jittershrinkrate: when the jitter buffer is being gradually shrunk
; (or enlarged), how many millisecs shall we take off per 20ms frame
; received? Use a small number, or you will be able to hear it
; changing. An example: if you set this to 2, then the jitter buffer
; size will change by 100 millisecs per second.
+; [This option is not applicable to, and ignored by the new jitterbuffer implementation]
jitterbuffer=no
+forcejitterbuffer=no
;dropcount=2
;maxjitterbuffer=500
;maxexcessbuffer=80
@@ -100,6 +117,16 @@ jitterbuffer=no
;jittershrinkrate=1
;trunkfreq=20 ; How frequently to send trunk msgs (in ms)
+
+; Should we send timestamps for the individual sub-frames within trunk frames?
+; There is a small bandwidth use for these (less than 1kbps/call), but they ensure
+; that frame timestamps get sent end-to-end properly. If both ends of all your trunks
+; go directly to TDM, _and_ your trunkfreq equals the frame length for your codecs, you
+; can probably suppress these. The receiver must also support this feature, although
+; they do not also need to have it enabled.
+;
+; trunktimestamps=yes
+
;
;
; We can register with another IAX server to let him know where we are
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index e0a2c64ef..1ebcb3064 100755
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -82,8 +82,12 @@ struct ast_channel_tech {
const char * const type;
const char * const description;
+ /*! Bitmap of formats this channel can handle */
int capabilities;
+ /*! Technology Properties */
+ int properties;
+
struct ast_channel *(* const requester)(const char *type, int format, void *data, int *cause);
int (* const devicestate)(void *data);
@@ -311,6 +315,10 @@ struct ast_channel {
};
+/* Channel tech properties: */
+/* Channels have this property if they can accept input with jitter; i.e. most VoIP channels */
+#define AST_CHAN_TP_WANTSJITTER (1 << 0)
+
#define AST_FLAG_DIGITAL (1 << 0) /* if the call is a digital ISDN call */
#define AST_FLAG_DEFER_DTMF (1 << 1) /* if dtmf should be deferred */
#define AST_FLAG_WRITE_INT (1 << 2) /* if write should be interrupt generator */
diff --git a/include/asterisk/translate.h b/include/asterisk/translate.h
index 2bcd4ef40..b33cec629 100755
--- a/include/asterisk/translate.h
+++ b/include/asterisk/translate.h
@@ -21,6 +21,7 @@ extern "C" {
#endif
#include <asterisk/frame.h>
+#include <asterisk/plc.h>
/* Declared by individual translators */
struct ast_translator_pvt;
@@ -103,7 +104,6 @@ extern void ast_translator_free_path(struct ast_trans_pvt *tr);
*/
extern struct ast_frame *ast_translate(struct ast_trans_pvt *tr, struct ast_frame *f, int consume);
-
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/jitterbuf.c b/jitterbuf.c
new file mode 100755
index 000000000..1a315b5f1
--- /dev/null
+++ b/jitterbuf.c
@@ -0,0 +1,698 @@
+/*
+ * jitterbuf: an application-independent jitterbuffer
+ *
+ * Copyrights:
+ * Copyright (C) 2004-2005, Horizon Wimba, Inc.
+ *
+ * Contributors:
+ * Steve Kann <stevek@stevek.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU Lesser (Library) General Public License
+ *
+ * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
+ */
+
+#include "jitterbuf.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* define these here, just for ancient compiler systems */
+#define JB_LONGMAX 2147483647L
+#define JB_LONGMIN (-JB_LONGMAX - 1L)
+
+#define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0)
+#define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0)
+#define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
+
+#ifdef DEEP_DEBUG
+#define jb_dbg2(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
+#else
+#define jb_dbg2(...) ((void)0)
+#endif
+
+static jb_output_function_t warnf, errf, dbgf;
+
+void jb_setoutput(jb_output_function_t warn, jb_output_function_t err, jb_output_function_t dbg)
+{
+ warnf = warn;
+ errf = err;
+ dbgf = dbg;
+}
+
+static void increment_losspct(jitterbuf *jb)
+{
+ jb->info.losspct = (100000 + 499 * jb->info.losspct)/500;
+}
+
+static void decrement_losspct(jitterbuf *jb)
+{
+ jb->info.losspct = (499 * jb->info.losspct)/500;
+}
+
+
+static void jb_dbginfo(jitterbuf *jb);
+
+
+void jb_reset(jitterbuf *jb)
+{
+ memset(jb,0,sizeof(jitterbuf));
+
+ /* initialize length */
+ jb->info.current = jb->info.target = 0;
+ jb->info.silence = 1;
+}
+
+jitterbuf * jb_new()
+{
+ jitterbuf *jb;
+
+
+ jb = malloc(sizeof(jitterbuf));
+ if(!jb) return NULL;
+
+ jb_reset(jb);
+
+ jb_dbg2("jb_new() = %x\n", jb);
+ return jb;
+}
+
+void jb_destroy(jitterbuf *jb)
+{
+ jb_frame *frame;
+ jb_dbg2("jb_destroy(%x)\n", jb);
+
+ /* free all the frames on the "free list" */
+ frame = jb->free;
+ while(frame != NULL) {
+ jb_frame *next = frame->next;
+ free(frame);
+ frame = next;
+ }
+
+ /* free ourselves! */
+ free(jb);
+}
+
+
+
+/* simple history manipulation */
+/* maybe later we can make the history buckets variable size, or something? */
+/* drop parameter determines whether we will drop outliers to minimize
+ * delay */
+static int longcmp(const void *a, const void *b)
+{
+ return *(long *)a - *(long *)b;
+}
+
+static void history_put(jitterbuf *jb, long ts, long now)
+{
+ long delay = now - ts;
+ long kicked;
+
+ /* don't add special/negative times to history */
+ if(ts <= 0) return;
+
+ kicked = jb->history[jb->hist_ptr & JB_HISTORY_SZ];
+
+ jb->history[(jb->hist_ptr++) % JB_HISTORY_SZ] = delay;
+
+ /* optimization; the max/min buffers don't need to be recalculated, if this packet's
+ * entry doesn't change them. This happens if this packet is not involved, _and_ any packet
+ * that got kicked out of the history is also not involved
+ * We do a number of comparisons, but it's probably still worthwhile, because it will usually
+ * succeed, and should be a lot faster than going through all 500 packets in history */
+ if(!jb->hist_maxbuf_valid)
+ return;
+
+ /* don't do this until we've filled history
+ * (reduces some edge cases below) */
+ if(jb->hist_ptr < JB_HISTORY_SZ)
+ goto invalidate;
+
+ /* if the new delay would go into min */
+ if(delay < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ /* or max.. */
+ if(delay > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ /* or the kicked delay would be in min */
+ if(kicked <= jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ if(kicked >= jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ /* if we got here, we don't need to invalidate, 'cause this delay didn't
+ * affect things */
+ return;
+ /* end optimization */
+
+
+invalidate:
+ jb->hist_maxbuf_valid = 0;
+ return;
+}
+
+static void history_calc_maxbuf(jitterbuf *jb)
+{
+ int i,j;
+
+ if(jb->hist_ptr == 0) return;
+
+
+ /* initialize maxbuf/minbuf to the latest value */
+ for(i=0;i<JB_HISTORY_MAXBUF_SZ;i++) {
+/*
+ * jb->hist_maxbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
+ * jb->hist_minbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
+ */
+ jb->hist_maxbuf[i] = JB_LONGMIN;
+ jb->hist_minbuf[i] = JB_LONGMAX;
+ }
+
+ /* use insertion sort to populate maxbuf */
+ /* we want it to be the top "n" values, in order */
+
+ /* start at the beginning, or JB_HISTORY_SZ frames ago */
+ i = (jb->hist_ptr > JB_HISTORY_SZ) ? (jb->hist_ptr - JB_HISTORY_SZ) : 0;
+
+ for(;i<jb->hist_ptr;i++) {
+ long toins = jb->history[i % JB_HISTORY_SZ];
+
+ /* if the maxbuf should get this */
+ if(toins > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1]) {
+
+ /* insertion-sort it into the maxbuf */
+ for(j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
+ /* found where it fits */
+ if(toins > jb->hist_maxbuf[j]) {
+ /* move over */
+ memmove(jb->hist_maxbuf+j+1,jb->hist_maxbuf+j, (JB_HISTORY_MAXBUF_SZ-(j+1)) * sizeof(long));
+ /* insert */
+ jb->hist_maxbuf[j] = toins;
+
+ break;
+ }
+ }
+ }
+
+ /* if the minbuf should get this */
+ if(toins < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1]) {
+
+ /* insertion-sort it into the maxbuf */
+ for(j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
+ /* found where it fits */
+ if(toins < jb->hist_minbuf[j]) {
+ /* move over */
+ memmove(jb->hist_minbuf+j+1,jb->hist_minbuf+j, (JB_HISTORY_MAXBUF_SZ-(j+1)) * sizeof(long));
+ /* insert */
+ jb->hist_minbuf[j] = toins;
+
+ break;
+ }
+ }
+ }
+
+ if(0) {
+ int k;
+ fprintf(stderr, "toins = %ld\n", toins);
+ fprintf(stderr, "maxbuf =");
+ for(k=0;k<JB_HISTORY_MAXBUF_SZ;k++)
+ fprintf(stderr, "%ld ", jb->hist_maxbuf[k]);
+ fprintf(stderr, "\nminbuf =");
+ for(k=0;k<JB_HISTORY_MAXBUF_SZ;k++)
+ fprintf(stderr, "%ld ", jb->hist_minbuf[k]);
+ fprintf(stderr, "\n");
+ }
+ }
+
+ jb->hist_maxbuf_valid = 1;
+}
+
+static void history_get(jitterbuf *jb)
+{
+ long max, min, jitter;
+ int index;
+ int count;
+
+ if(!jb->hist_maxbuf_valid)
+ history_calc_maxbuf(jb);
+
+ /* count is how many items in history we're examining */
+ count = (jb->hist_ptr < JB_HISTORY_SZ) ? jb->hist_ptr : JB_HISTORY_SZ;
+
+ /* index is the "n"ths highest/lowest that we'll look for */
+ index = count * JB_HISTORY_DROPPCT / 100;
+
+ /* sanity checks for index */
+ if(index > (JB_HISTORY_MAXBUF_SZ - 1)) index = JB_HISTORY_MAXBUF_SZ - 1;
+
+
+ if(index < 0) {
+ jb->info.min = 0;
+ jb->info.jitter = 0;
+ return;
+ }
+
+ max = jb->hist_maxbuf[index];
+ min = jb->hist_minbuf[index];
+
+ jitter = max - min;
+
+ /* these debug stmts compare the difference between looking at the absolute jitter, and the
+ * values we get by throwing away the outliers */
+ /*
+ fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", index, min, max, jitter);
+ fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", 0, jb->hist_minbuf[0], jb->hist_maxbuf[0], jb->hist_maxbuf[0]-jb->hist_minbuf[0]);
+ */
+
+ jb->info.min = min;
+ jb->info.jitter = jitter;
+}
+
+static void queue_put(jitterbuf *jb, void *data, int type, long ms, long ts)
+{
+ jb_frame *frame;
+ jb_frame *p;
+
+ frame = jb->free;
+ if(frame) {
+ jb->free = frame->next;
+ } else {
+ frame = malloc(sizeof(jb_frame));
+ }
+
+ if(!frame) {
+ jb_err("cannot allocate frame\n");
+ return;
+ }
+
+ jb->info.frames_cur++;
+
+ frame->data = data;
+ frame->ts = ts;
+ frame->ms = ms;
+ frame->type = type;
+
+ /*
+ * frames are a circular list, jb-frames points to to the lowest ts,
+ * jb->frames->prev points to the highest ts
+ */
+
+ if(!jb->frames) { /* queue is empty */
+ jb->frames = frame;
+ frame->next = frame;
+ frame->prev = frame;
+ } else if(ts < jb->frames->ts) {
+ frame->next = jb->frames;
+ frame->prev = jb->frames->prev;
+
+ frame->next->prev = frame;
+ frame->prev->next = frame;
+
+ jb->frames = frame;
+ } else {
+ p = jb->frames;
+
+ /* frame is out of order */
+ if(ts < p->prev->ts) jb->info.frames_ooo++;
+
+ while(ts < p->prev->ts && p->prev != jb->frames)
+ p = p->prev;
+
+ frame->next = p;
+ frame->prev = p->prev;
+
+ frame->next->prev = frame;
+ frame->prev->next = frame;
+ }
+}
+
+static long queue_next(jitterbuf *jb)
+{
+ if(jb->frames) return jb->frames->ts;
+ else return -1;
+}
+
+static long queue_last(jitterbuf *jb)
+{
+ if(jb->frames) return jb->frames->prev->ts;
+ else return -1;
+}
+
+static jb_frame *_queue_get(jitterbuf *jb, long ts, int all)
+{
+ jb_frame *frame;
+ frame = jb->frames;
+
+ if(!frame)
+ return NULL;
+
+ /*jb_warn("queue_get: ASK %ld FIRST %ld\n", ts, frame->ts); */
+
+ if(all || ts > frame->ts) {
+ /* remove this frame */
+ frame->prev->next = frame->next;
+ frame->next->prev = frame->prev;
+
+ if(frame->next == frame)
+ jb->frames = NULL;
+ else
+ jb->frames = frame->next;
+
+
+ /* insert onto "free" single-linked list */
+ frame->next = jb->free;
+ jb->free = frame;
+
+ jb->info.frames_cur--;
+
+ /* we return the frame pointer, even though it's on free list,
+ * but caller must copy data */
+ return frame;
+ }
+
+ return NULL;
+}
+
+static jb_frame *queue_get(jitterbuf *jb, long ts)
+{
+ return _queue_get(jb,ts,0);
+}
+
+static jb_frame *queue_getall(jitterbuf *jb)
+{
+ return _queue_get(jb,0,1);
+}
+
+/* some diagnostics */
+static void jb_dbginfo(jitterbuf *jb)
+{
+ if(dbgf == NULL) return;
+
+ jb_dbg("\njb info: fin=%ld fout=%ld flate=%ld flost=%ld fdrop=%ld fcur=%ld\n",
+ jb->info.frames_in, jb->info.frames_out, jb->info.frames_late, jb->info.frames_lost, jb->info.frames_dropped, jb->info.frames_cur);
+
+ jb_dbg(" jitter=%ld current=%ld target=%ld min=%ld sil=%d len=%d len/fcur=%ld\n",
+ jb->info.jitter, jb->info.current, jb->info.target, jb->info.min, jb->info.silence, jb->info.current - jb->info.min,
+ jb->info.frames_cur ? (jb->info.current - jb->info.min)/jb->info.frames_cur : -8);
+ if(jb->info.frames_in > 0)
+ jb_dbg("jb info: Loss PCT = %ld%%, Late PCT = %ld%%\n",
+ jb->info.frames_lost * 100/(jb->info.frames_in + jb->info.frames_lost),
+ jb->info.frames_late * 100/jb->info.frames_in);
+ jb_dbg("jb info: queue %d -> %d. last_ts %d (queue len: %d) last_ms %d\n",
+ queue_next(jb),
+ queue_last(jb),
+ jb->info.last_voice_ts,
+ queue_last(jb) - queue_next(jb),
+ jb->info.last_voice_ms);
+}
+
+#ifdef DEEP_DEBUG
+static void jb_chkqueue(jitterbuf *jb)
+{
+ int i=0;
+ jb_frame *p = jb->frames;
+
+ if(!p) {
+ return;
+ }
+
+ do {
+ if(p->next == NULL) {
+ jb_err("Queue is BROKEN at item [%d]", i);
+ }
+ i++;
+ p=p->next;
+ } while (p->next != jb->frames);
+}
+
+static void jb_dbgqueue(jitterbuf *jb)
+{
+ int i=0;
+ jb_frame *p = jb->frames;
+
+ jb_dbg("queue: ");
+
+ if(!p) {
+ jb_dbg("EMPTY\n");
+ return;
+ }
+
+ do {
+ jb_dbg("[%d]=%ld ", i++, p->ts);
+ p=p->next;
+ } while (p->next != jb->frames);
+
+ jb_dbg("\n");
+}
+#endif
+
+int jb_put(jitterbuf *jb, void *data, int type, long ms, long ts, long now)
+{
+ jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb, data, ms, ts, now);
+
+ jb->info.frames_in++;
+
+ if(type == JB_TYPE_VOICE) {
+ /* presently, I'm only adding VOICE frames to history and drift calculations; mostly because with the
+ * IAX integrations, I'm sending retransmitted control frames with their awkward timestamps through */
+ history_put(jb,ts,now);
+ }
+
+ queue_put(jb,data,type,ms,ts);
+
+ return JB_OK;
+}
+
+
+static int _jb_get(jitterbuf *jb, jb_frame *frameout, long now)
+{
+ jb_frame *frame;
+ long diff;
+
+ /*if((now - jb_next(jb)) > 2 * jb->info.last_voice_ms) jb_warn("SCHED: %ld", (now - jb_next(jb))); */
+ /* get jitter info */
+ history_get(jb);
+
+
+ /* target */
+ jb->info.target = jb->info.jitter + jb->info.min + 2 * jb->info.last_voice_ms;
+
+ /* if a hard clamp was requested, use it */
+ if((jb->info.max_jitterbuf) && ((jb->info.target - jb->info.min) > jb->info.max_jitterbuf)) {
+ jb_dbg("clamping target from %d to %d\n", (jb->info.target - jb->info.min), jb->info.max_jitterbuf);
+ jb->info.target = jb->info.min + jb->info.max_jitterbuf;
+ }
+
+ diff = jb->info.target - jb->info.current;
+
+ /* jb_warn("diff = %d lms=%d last = %d now = %d\n", diff, */
+ /* jb->info.last_voice_ms, jb->info.last_adjustment, now); */
+
+ /* move up last_voice_ts; it is now the expected voice ts */
+ jb->info.last_voice_ts += jb->info.last_voice_ms;
+
+ /* let's work on non-silent case first */
+ if(!jb->info.silence) {
+ /* we want to grow */
+ if( (diff > 0) &&
+ /* we haven't grown in 2 frames' length */
+ (((jb->info.last_adjustment + 2 * jb->info.last_voice_ms ) < now) ||
+ /* we need to grow more than the "length" we have left */
+ (diff > queue_last(jb) - queue_next(jb)) ) ) {
+ jb->info.current += jb->info.last_voice_ms;
+ jb->info.last_adjustment = now;
+ jb_dbg("G");
+ return JB_INTERP;
+ }
+
+ frame = queue_get(jb, jb->info.last_voice_ts - jb->info.current);
+
+ /* not a voice frame; just return it. */
+ if(frame && frame->type != JB_TYPE_VOICE) {
+ /* rewind last_voice_ts, since this isn't voice */
+ jb->info.last_voice_ts -= jb->info.last_voice_ms;
+
+ if(frame->type == JB_TYPE_SILENCE)
+ jb->info.silence = 1;
+
+ *frameout = *frame;
+ jb->info.frames_out++;
+ jb_dbg("o");
+ return JB_OK;
+ }
+
+
+ /* voice frame is late */
+ if(frame && frame->ts + jb->info.current < jb->info.last_voice_ts - jb->info.last_voice_ms ) {
+ *frameout = *frame;
+ /* rewind last_voice, since we're just dumping */
+ jb->info.last_voice_ts -= jb->info.last_voice_ms;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ jb->info.frames_late++;
+ jb->info.frames_lost--;
+ jb_dbg("l");
+ /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.last_voice_ts - jb->info.current, frame->ts, queue_next(jb));
+ jb_warninfo(jb); */
+ return JB_DROP;
+ }
+
+ /* keep track of frame sizes, to allow for variable sized-frames */
+ if(frame && frame->ms > 0) {
+ jb->info.last_voice_ms = frame->ms;
+ }
+
+ /* we want to shrink; shrink at 1 frame / 500ms */
+ if(diff < -2 * jb->info.last_voice_ms &&
+ ((!frame && jb->info.last_adjustment + 80 < now) ||
+ (jb->info.last_adjustment + 500 < now))) {
+
+ /* don't increment last_ts ?? */
+ jb->info.last_voice_ts -= jb->info.last_voice_ms;
+ jb->info.current -= jb->info.last_voice_ms;
+ jb->info.last_adjustment = now;
+
+ if(frame) {
+ *frameout = *frame;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ jb->info.frames_dropped++;
+ jb_dbg("s");
+ return JB_DROP;
+ } else {
+ increment_losspct(jb);
+ jb_dbg("S");
+ return JB_NOFRAME;
+ }
+ }
+
+ /* lost frame */
+ if(!frame) {
+ /* this is a bit of a hack for now, but if we're close to
+ * target, and we find a missing frame, it makes sense to
+ * grow, because the frame might just be a bit late;
+ * otherwise, we presently get into a pattern where we return
+ * INTERP for the lost frame, then it shows up next, and we
+ * throw it away because it's late */
+ /* I've recently only been able to replicate this using
+ * iaxclient talking to app_echo on asterisk. In this case,
+ * my outgoing packets go through asterisk's (old)
+ * jitterbuffer, and then might get an unusual increasing delay
+ * there if it decides to grow?? */
+ /* Update: that might have been a different bug, that has been fixed..
+ * But, this still seemed like a good idea, except that it ended up making a single actual
+ * lost frame get interpolated two or more times, when there was "room" to grow, so it might
+ * be a bit of a bad idea overall */
+ /*if(diff > -1 * jb->info.last_voice_ms) {
+ jb->info.current += jb->info.last_voice_ms;
+ jb->info.last_adjustment = now;
+ jb_warn("g");
+ return JB_INTERP;
+ } */
+ jb->info.frames_lost++;
+ increment_losspct(jb);
+ jb_dbg("L");
+ return JB_INTERP;
+ }
+
+ /* normal case; return the frame, increment stuff */
+ *frameout = *frame;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ jb_dbg("v");
+ return JB_OK;
+ } else {
+ /* TODO: after we get the non-silent case down, we'll make the
+ * silent case -- basically, we'll just grow and shrink faster
+ * here, plus handle last_voice_ts a bit differently */
+
+ /* to disable silent special case altogether, just uncomment this: */
+ /* jb->info.silence = 0; */
+
+ frame = queue_get(jb, now - jb->info.current);
+ if(!frame) {
+ return JB_NOFRAME;
+ }
+ if(frame && frame->type == JB_TYPE_VOICE) {
+ /* try setting current to target right away here */
+ jb->info.current = jb->info.target;
+ jb->info.silence = 0;
+ jb->info.last_voice_ts = frame->ts + jb->info.current + frame->ms;
+ jb->info.last_voice_ms = frame->ms;
+ *frameout = *frame;
+ jb_dbg("V");
+ return JB_OK;
+ }
+ /* normal case; in silent mode, got a non-voice frame */
+ *frameout = *frame;
+ return JB_OK;
+ }
+}
+
+long jb_next(jitterbuf *jb)
+{
+ if(jb->info.silence) {
+ long next = queue_next(jb);
+ if(next > 0) {
+ history_get(jb);
+ return next + jb->info.target;
+ }
+ else return JB_LONGMAX;
+ } else {
+ return jb->info.last_voice_ts + jb->info.last_voice_ms;
+ }
+}
+
+int jb_get(jitterbuf *jb, jb_frame *frameout, long now)
+{
+ int ret = _jb_get(jb,frameout,now);
+#if 0
+ static int lastts=0;
+ int thists = ((ret == JB_OK) || (ret == JB_DROP)) ? frameout->ts : 0;
+ jb_warn("jb_get(%x,%x,%ld) = %d (%d)\n", jb, frameout, now, ret, thists);
+ if(thists && thists < lastts) jb_warn("XXXX timestamp roll-back!!!\n");
+ lastts = thists;
+#endif
+ return ret;
+}
+
+int jb_getall(jitterbuf *jb, jb_frame *frameout)
+{
+ jb_frame *frame;
+ frame = queue_getall(jb);
+
+ if(!frame) {
+ return JB_NOFRAME;
+ }
+
+ *frameout = *frame;
+ return JB_OK;
+}
+
+
+int jb_getinfo(jitterbuf *jb, jb_info *stats)
+{
+
+ history_get(jb);
+
+ *stats = jb->info;
+
+ return JB_OK;
+}
+
+int jb_setinfo(jitterbuf *jb, jb_info *settings)
+{
+ /* take selected settings from the struct */
+
+ jb->info.max_jitterbuf = settings->max_jitterbuf;
+
+ return JB_OK;
+}
+
+
diff --git a/jitterbuf.h b/jitterbuf.h
new file mode 100755
index 000000000..926a3d19a
--- /dev/null
+++ b/jitterbuf.h
@@ -0,0 +1,140 @@
+/*
+ * jitterbuf: an application-independent jitterbuffer
+ *
+ * Copyrights:
+ * Copyright (C) 2004-2005, Horizon Wimba, Inc.
+ *
+ * Contributors:
+ * Steve Kann <stevek@stevek.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU Lesser (Library) General Public License
+ *
+ * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
+ */
+
+#ifndef _JITTERBUF_H_
+#define _JITTERBUF_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* configuration constants */
+ /* Number of historical timestamps to use in calculating jitter and drift */
+#define JB_HISTORY_SZ 500
+ /* what percentage of timestamps should we drop from the history when we examine it;
+ * this might eventually be something made configurable */
+#define JB_HISTORY_DROPPCT 3
+ /* the maximum droppct we can handle (say it was configurable). */
+#define JB_HISTORY_DROPPCT_MAX 4
+ /* the size of the buffer we use to keep the top and botton timestamps for dropping */
+#define JB_HISTORY_MAXBUF_SZ JB_HISTORY_SZ * JB_HISTORY_DROPPCT_MAX / 100
+
+
+/* return codes */
+#define JB_OK 0
+#define JB_EMPTY 1
+#define JB_NOFRAME 2
+#define JB_INTERP 3
+#define JB_DROP 4
+
+/* frame types */
+#define JB_TYPE_CONTROL 0
+#define JB_TYPE_VOICE 1
+#define JB_TYPE_VIDEO 2 /* reserved */
+#define JB_TYPE_SILENCE 3
+
+typedef struct jb_info {
+ /* statistics */
+ long frames_in; /* number of frames input to the jitterbuffer.*/
+ long frames_out; /* number of frames output from the jitterbuffer.*/
+ long frames_late; /* number of frames which were too late, and dropped.*/
+ long frames_lost; /* number of missing frames.*/
+ long frames_dropped; /* number of frames dropped (shrinkage) */
+ long frames_ooo; /* number of frames received out-of-order */
+ long frames_cur; /* number of frames presently in jb, awaiting delivery.*/
+ long jitter; /* jitter measured within current history interval*/
+ long min; /* minimum lateness within current history interval */
+ long current; /* the present jitterbuffer adjustment */
+ long target; /* the target jitterbuffer adjustment */
+ long losspct; /* recent lost frame percentage (* 1000) */
+ long last_voice_ts; /* the last ts that was read from the jb - in receiver's time */
+ long last_voice_ms; /* the duration of the last voice frame */
+ long silence; /* we are presently playing out silence */
+ long last_adjustment; /* the time of the last adjustment */
+
+ /* settings */
+ long max_jitterbuf; /* defines a hard clamp to use in setting the jitter buffer delay */
+} jb_info;
+
+typedef struct jb_frame {
+ void *data; /* the frame data */
+ long ts; /* the relative delivery time expected */
+ long ms; /* the time covered by this frame, in sec/8000 */
+ int type; /* the type of frame */
+ struct jb_frame *next, *prev;
+} jb_frame;
+
+typedef struct jitterbuf {
+ jb_info info;
+
+ /* history */
+ long history[JB_HISTORY_SZ]; /* history */
+ int hist_ptr; /* points to index in history for next entry */
+ long hist_maxbuf[JB_HISTORY_MAXBUF_SZ]; /* a sorted buffer of the max delays (highest first) */
+ long hist_minbuf[JB_HISTORY_MAXBUF_SZ]; /* a sorted buffer of the min delays (lowest first) */
+ int hist_maxbuf_valid; /* are the "maxbuf"/minbuf valid? */
+
+
+ jb_frame *frames; /* queued frames */
+ jb_frame *free; /* free frames (avoid malloc?) */
+} jitterbuf;
+
+
+/* new jitterbuf */
+jitterbuf * jb_new(void);
+
+/* destroy jitterbuf */
+void jb_destroy(jitterbuf *jb);
+
+/* reset jitterbuf */
+/* NOTE: The jitterbuffer should be empty before you call this, otherwise
+ * you will leak queued frames, and some internal structures */
+void jb_reset(jitterbuf *jb);
+
+/* queue a frame data=frame data, timings (in ms): ms=length of frame (for voice), ts=ts (sender's time)
+ * now=now (in receiver's time)*/
+int jb_put(jitterbuf *jb, void *data, int type, long ms, long ts, long now);
+
+/* get a frame for time now (receiver's time) return value is one of
+ * JB_OK: You've got frame!
+ * JB_DROP: Here's an audio frame you should just drop. Ask me again for this time..
+ * JB_NOFRAME: There's no frame scheduled for this time.
+ * JB_INTERP: Please interpolate an audio frame for this time (either we need to grow, or there was a lost frame
+ * JB_EMPTY: The jb is empty.
+ */
+int jb_get(jitterbuf *jb, jb_frame *frame, long now);
+
+/* unconditionally get frames from jitterbuf until empty */
+int jb_getall(jitterbuf *jb, jb_frame *frameout);
+
+/* when is the next frame due out, in receiver's time (0=EMPTY)
+ * This value may change as frames are added (esp non-audio frames) */
+long jb_next(jitterbuf *jb);
+
+/* get jitterbuf info: only "statistics" may be valid */
+int jb_getinfo(jitterbuf *jb, jb_info *stats);
+
+/* set jitterbuf info: only "settings" may be honored */
+int jb_setinfo(jitterbuf *jb, jb_info *settings);
+
+typedef void (*jb_output_function_t)(const char *fmt, ...);
+void jb_setoutput(jb_output_function_t warn, jb_output_function_t err, jb_output_function_t dbg);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/plc.c b/plc.c
new file mode 100755
index 000000000..1f6d8a061
--- /dev/null
+++ b/plc.c
@@ -0,0 +1,251 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * plc.c
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2004 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This version may be optionally licenced under the GNU LGPL licence.
+ * This version is disclaimed to DIGIUM for inclusion in the Asterisk project.
+ */
+
+/*! \file */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <limits.h>
+
+#include <asterisk/plc.h>
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+#if !defined(TRUE)
+#define TRUE (!FALSE)
+#endif
+
+/* We do a straight line fade to zero volume in 50ms when we are filling in for missing data. */
+#define ATTENUATION_INCREMENT 0.0025 /* Attenuation per sample */
+
+#define ms_to_samples(t) (((t)*SAMPLE_RATE)/1000)
+
+static inline int16_t fsaturate(double damp)
+{
+ if (damp > 32767.0)
+ return INT16_MAX;
+ if (damp < -32768.0)
+ return INT16_MIN;
+ return (int16_t) rint(damp);
+}
+
+static void save_history(plc_state_t *s, int16_t *buf, int len)
+{
+ if (len >= PLC_HISTORY_LEN)
+ {
+ /* Just keep the last part of the new data, starting at the beginning of the buffer */
+ memcpy(s->history, buf + len - PLC_HISTORY_LEN, sizeof(int16_t)*PLC_HISTORY_LEN);
+ s->buf_ptr = 0;
+ return;
+ }
+ if (s->buf_ptr + len > PLC_HISTORY_LEN)
+ {
+ /* Wraps around - must break into two sections */
+ memcpy(s->history + s->buf_ptr, buf, sizeof(int16_t)*(PLC_HISTORY_LEN - s->buf_ptr));
+ len -= (PLC_HISTORY_LEN - s->buf_ptr);
+ memcpy(s->history, buf + (PLC_HISTORY_LEN - s->buf_ptr), sizeof(int16_t)*len);
+ s->buf_ptr = len;
+ return;
+ }
+ /* Can use just one section */
+ memcpy(s->history + s->buf_ptr, buf, sizeof(int16_t)*len);
+ s->buf_ptr += len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void normalise_history(plc_state_t *s)
+{
+ int16_t tmp[PLC_HISTORY_LEN];
+
+ if (s->buf_ptr == 0)
+ return;
+ memcpy(tmp, s->history, sizeof(int16_t)*s->buf_ptr);
+ memcpy(s->history, s->history + s->buf_ptr, sizeof(int16_t)*(PLC_HISTORY_LEN - s->buf_ptr));
+ memcpy(s->history + PLC_HISTORY_LEN - s->buf_ptr, tmp, sizeof(int16_t)*s->buf_ptr);
+ s->buf_ptr = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int __inline__ amdf_pitch(int min_pitch, int max_pitch, int16_t amp[], int len)
+{
+ int i;
+ int j;
+ int acc;
+ int min_acc;
+ int pitch;
+
+ pitch = min_pitch;
+ min_acc = INT_MAX;
+ for (i = max_pitch; i <= min_pitch; i++)
+ {
+ acc = 0;
+ for (j = 0; j < len; j++)
+ acc += abs(amp[i + j] - amp[j]);
+ if (acc < min_acc)
+ {
+ min_acc = acc;
+ pitch = i;
+ }
+ }
+ return pitch;
+}
+/*- End of function --------------------------------------------------------*/
+
+int plc_rx(plc_state_t *s, int16_t amp[], int len)
+{
+ int i;
+ int overlap_len;
+ int pitch_overlap;
+ float old_step;
+ float new_step;
+ float old_weight;
+ float new_weight;
+ float gain;
+
+ if (s->missing_samples)
+ {
+ /* Although we have a real signal, we need to smooth it to fit well
+ with the synthetic signal we used for the previous block */
+
+ /* The start of the real data is overlapped with the next 1/4 cycle
+ of the synthetic data. */
+ pitch_overlap = s->pitch >> 2;
+ if (pitch_overlap > len)
+ pitch_overlap = len;
+ gain = 1.0 - s->missing_samples*ATTENUATION_INCREMENT;
+ if (gain < 0.0)
+ gain = 0.0;
+ new_step = 1.0/pitch_overlap;
+ old_step = new_step*gain;
+ new_weight = new_step;
+ old_weight = (1.0 - new_step)*gain;
+ for (i = 0; i < pitch_overlap; i++)
+ {
+ amp[i] = fsaturate(old_weight*s->pitchbuf[s->pitch_offset] + new_weight*amp[i]);
+ if (++s->pitch_offset >= s->pitch)
+ s->pitch_offset = 0;
+ new_weight += new_step;
+ old_weight -= old_step;
+ if (old_weight < 0.0)
+ old_weight = 0.0;
+ }
+ s->missing_samples = 0;
+ }
+ save_history(s, amp, len);
+ return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+int plc_fillin(plc_state_t *s, int16_t amp[], int len)
+{
+ int16_t tmp[PLC_PITCH_OVERLAP_MAX];
+ int i;
+ int pitch_overlap;
+ float old_step;
+ float new_step;
+ float old_weight;
+ float new_weight;
+ float gain;
+ int16_t *orig_amp;
+ int orig_len;
+
+ orig_amp = amp;
+ orig_len = len;
+ if (s->missing_samples == 0)
+ {
+ /* As the gap in real speech starts we need to assess the last known pitch,
+ and prepare the synthetic data we will use for fill-in */
+ normalise_history(s);
+ s->pitch = amdf_pitch(PLC_PITCH_MIN, PLC_PITCH_MAX, s->history + PLC_HISTORY_LEN - CORRELATION_SPAN - PLC_PITCH_MIN, CORRELATION_SPAN);
+ /* We overlap a 1/4 wavelength */
+ pitch_overlap = s->pitch >> 2;
+ /* Cook up a single cycle of pitch, using a single of the real signal with 1/4
+ cycle OLA'ed to make the ends join up nicely */
+ /* The first 3/4 of the cycle is a simple copy */
+ for (i = 0; i < s->pitch - pitch_overlap; i++)
+ s->pitchbuf[i] = s->history[PLC_HISTORY_LEN - s->pitch + i];
+ /* The last 1/4 of the cycle is overlapped with the end of the previous cycle */
+ new_step = 1.0/pitch_overlap;
+ new_weight = new_step;
+ for ( ; i < s->pitch; i++)
+ {
+ s->pitchbuf[i] = s->history[PLC_HISTORY_LEN - s->pitch + i]*(1.0 - new_weight) + s->history[PLC_HISTORY_LEN - 2*s->pitch + i]*new_weight;
+ new_weight += new_step;
+ }
+ /* We should now be ready to fill in the gap with repeated, decaying cycles
+ of what is in pitchbuf */
+
+ /* We need to OLA the first 1/4 wavelength of the synthetic data, to smooth
+ it into the previous real data. To avoid the need to introduce a delay
+ in the stream, reverse the last 1/4 wavelength, and OLA with that. */
+ gain = 1.0;
+ new_step = 1.0/pitch_overlap;
+ old_step = new_step;
+ new_weight = new_step;
+ old_weight = 1.0 - new_step;
+ for (i = 0; i < pitch_overlap; i++)
+ {
+ amp[i] = fsaturate(old_weight*s->history[PLC_HISTORY_LEN - 1 - i] + new_weight*s->pitchbuf[i]);
+ new_weight += new_step;
+ old_weight -= old_step;
+ if (old_weight < 0.0)
+ old_weight = 0.0;
+ }
+ s->pitch_offset = i;
+ }
+ else
+ {
+ gain = 1.0 - s->missing_samples*ATTENUATION_INCREMENT;
+ i = 0;
+ }
+ for ( ; gain > 0.0 && i < len; i++)
+ {
+ amp[i] = s->pitchbuf[s->pitch_offset]*gain;
+ gain -= ATTENUATION_INCREMENT;
+ if (++s->pitch_offset >= s->pitch)
+ s->pitch_offset = 0;
+ }
+ for ( ; i < len; i++)
+ amp[i] = 0;
+ s->missing_samples += orig_len;
+ save_history(s, amp, len);
+ return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+plc_state_t *plc_init(plc_state_t *s)
+{
+ memset(s, 0, sizeof(*s));
+ return s;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/