mirror of
https://github.com/asterisk/asterisk.git
synced 2026-07-28 00:10:25 -07:00
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@5192 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
+488
-38
@@ -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]);
|
||||
@@ -3974,16 +4302,48 @@ static int iax2_show_netstats(int fd, int argc, char *argv[])
|
||||
ast_cli(fd, "%-25.25s <NATIVE BRIDGED>",
|
||||
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, ¬ransfer, &usejitterbuf, NULL, NULL, 0, NULL, 0, &found, NULL, NULL, 0, NULL, 0, &sockfd)) {
|
||||
if (create_addr(&sin, &capability, &sendani, &maxtime, hostname, NULL, &trunk, ¬ransfer, &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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+50
-3
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
+13
-1
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
+18
-2
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable
+698
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Executable
+140
@@ -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
|
||||
@@ -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 ------------------------------------------------------------*/
|
||||
Reference in New Issue
Block a user