Add support for using a jitterbuffer for RTP on bridged calls. This includes
a new implementation of a fixed size jitterbuffer, as well as support for the existing adaptive jitterbuffer implementation. (issue #3854, Slav Klenov) Thank you very much to Slav Klenov of Securax and all of the people involved in the testing of this feature for all of your hard work! git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@31052 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
parent
503486e99d
commit
bb7dd96cfe
|
@ -1 +1 @@
|
|||
17
|
||||
18
|
||||
|
|
2
Makefile
2
Makefile
|
@ -282,7 +282,7 @@ OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \
|
|||
astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \
|
||||
utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \
|
||||
netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
|
||||
cryptostub.o sha1.o http.o
|
||||
cryptostub.o sha1.o http.o scx_jitterbuf.o abstract_jb.o
|
||||
|
||||
# we need to link in the objects statically, not as a library, because
|
||||
# otherwise modules will not have them available if none of the static
|
||||
|
|
29
channel.c
29
channel.c
|
@ -1012,6 +1012,9 @@ void ast_channel_free(struct ast_channel *chan)
|
|||
while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
|
||||
ast_var_delete(vardata);
|
||||
|
||||
/* Destroy the jitterbuffer */
|
||||
ast_jb_destroy(chan);
|
||||
|
||||
ast_string_field_free_all(chan);
|
||||
free(chan);
|
||||
AST_LIST_UNLOCK(&channels);
|
||||
|
@ -3303,6 +3306,9 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
|
|||
int watch_c0_dtmf;
|
||||
int watch_c1_dtmf;
|
||||
void *pvt0, *pvt1;
|
||||
/* Indicates whether a frame was queued into a jitterbuffer */
|
||||
int frame_put_in_jb = 0;
|
||||
int jb_in_use;
|
||||
int to;
|
||||
|
||||
cs[0] = c0;
|
||||
|
@ -3314,6 +3320,9 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
|
|||
watch_c0_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_0;
|
||||
watch_c1_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_1;
|
||||
|
||||
/* Check the need of a jitterbuffer for each channel */
|
||||
jb_in_use = ast_jb_do_usecheck(c0, c1);
|
||||
|
||||
for (;;) {
|
||||
struct ast_channel *who, *other;
|
||||
|
||||
|
@ -3332,9 +3341,15 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
|
|||
}
|
||||
} else
|
||||
to = -1;
|
||||
/* Calculate the appropriate max sleep interval - in general, this is the time,
|
||||
left to the closest jb delivery moment */
|
||||
if (jb_in_use)
|
||||
to = ast_jb_get_when_to_wakeup(c0, c1, to);
|
||||
who = ast_waitfor_n(cs, 2, &to);
|
||||
if (!who) {
|
||||
ast_log(LOG_DEBUG, "Nobody there, continuing...\n");
|
||||
/* No frame received within the specified timeout - check if we have to deliver now */
|
||||
if (jb_in_use)
|
||||
ast_jb_get_and_deliver(c0, c1);
|
||||
if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
|
||||
if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
|
||||
c0->_softhangup = 0;
|
||||
|
@ -3354,6 +3369,9 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
|
|||
}
|
||||
|
||||
other = (who == c0) ? c1 : c0; /* the 'other' channel */
|
||||
/* Try add the frame info the who's bridged channel jitterbuff */
|
||||
if (jb_in_use)
|
||||
frame_put_in_jb = !ast_jb_put(other, f);
|
||||
|
||||
if ((f->frametype == AST_FRAME_CONTROL) && !(config->flags & AST_BRIDGE_IGNORE_SIGS)) {
|
||||
int bridge_exit = 0;
|
||||
|
@ -3390,8 +3408,13 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
|
|||
ast_log(LOG_DEBUG, "Got DTMF on channel (%s)\n", who->name);
|
||||
break;
|
||||
}
|
||||
/* other frames go to the other side */
|
||||
ast_write(other, f);
|
||||
/* Write immediately frames, not passed through jb */
|
||||
if (!frame_put_in_jb)
|
||||
ast_write(other, f);
|
||||
|
||||
/* Check if we have to deliver now */
|
||||
if (jb_in_use)
|
||||
ast_jb_get_and_deliver(c0, c1);
|
||||
}
|
||||
/* XXX do we want to pass on also frames not matched above ? */
|
||||
ast_frfree(f);
|
||||
|
|
|
@ -70,6 +70,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include "alsa-monitor.h"
|
||||
#endif
|
||||
|
||||
#include "asterisk/abstract_jb.h"
|
||||
/* Global jitterbuffer configuration - by default, jb is disabled */
|
||||
static struct ast_jb_conf default_jbconf =
|
||||
{
|
||||
.flags = 0,
|
||||
.max_size = -1,
|
||||
.resync_threshold = -1,
|
||||
.impl = ""
|
||||
};
|
||||
static struct ast_jb_conf global_jbconf;
|
||||
|
||||
#define DEBUG 0
|
||||
/* Which device to use */
|
||||
#define ALSA_INDEV "hw:0,0"
|
||||
|
@ -812,6 +823,8 @@ static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state)
|
|||
tmp = NULL;
|
||||
}
|
||||
}
|
||||
if (tmp)
|
||||
ast_jb_configure(tmp, &global_jbconf);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
@ -1051,9 +1064,18 @@ static int load_module(void *mod)
|
|||
int x;
|
||||
struct ast_config *cfg;
|
||||
struct ast_variable *v;
|
||||
|
||||
/* Copy the default jb config over global_jbconf */
|
||||
memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
|
||||
|
||||
if ((cfg = ast_config_load(config))) {
|
||||
v = ast_variable_browse(cfg, "general");
|
||||
while(v) {
|
||||
/* handle jb conf */
|
||||
if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
|
||||
v = v->next;
|
||||
continue;
|
||||
}
|
||||
if (!strcasecmp(v->name, "autoanswer"))
|
||||
autoanswer = ast_true(v->value);
|
||||
else if (!strcasecmp(v->name, "silencesuppression"))
|
||||
|
|
|
@ -1543,6 +1543,7 @@ static int __do_deliver(void *data)
|
|||
the IAX thread with the iaxsl lock held. */
|
||||
struct iax_frame *fr = data;
|
||||
fr->retrans = -1;
|
||||
fr->af.has_timing_info = 0;
|
||||
if (iaxs[fr->callno] && !ast_test_flag(iaxs[fr->callno], IAX_ALREADYGONE))
|
||||
iax2_queue_frame(fr->callno, &fr->af);
|
||||
/* Free our iax frame */
|
||||
|
|
|
@ -82,6 +82,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include "ring10.h"
|
||||
#include "answer.h"
|
||||
|
||||
#include "asterisk/abstract_jb.h"
|
||||
/* Global jitterbuffer configuration - by default, jb is disabled */
|
||||
static struct ast_jb_conf default_jbconf =
|
||||
{
|
||||
.flags = 0,
|
||||
.max_size = -1,
|
||||
.resync_threshold = -1,
|
||||
.impl = ""
|
||||
};
|
||||
static struct ast_jb_conf global_jbconf;
|
||||
|
||||
/*
|
||||
* Basic mode of operation:
|
||||
*
|
||||
|
@ -141,6 +152,33 @@ START_CONFIG
|
|||
; queuesize = 10 ; frames in device driver
|
||||
; frags = 8 ; argument to SETFRAGMENT
|
||||
|
||||
;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
|
||||
; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an
|
||||
; OSS channel. Defaults to "no". An enabled jitterbuffer will
|
||||
; be used only if the sending side can create and the receiving
|
||||
; side can not accept jitter. The ZAP channel can't accept jitter,
|
||||
; thus an enabled jitterbuffer on the receive ZAP side will always
|
||||
; be used if the sending side can create jitter or if ZAP jb is
|
||||
; forced.
|
||||
|
||||
; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a ZAP
|
||||
; channel. Defaults to "no".
|
||||
|
||||
; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
|
||||
|
||||
; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
|
||||
; resynchronized. Useful to improve the quality of the voice, with
|
||||
; big jumps in/broken timestamps, usualy sent from exotic devices
|
||||
; and programs. Defaults to 1000.
|
||||
|
||||
; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP
|
||||
; channel. Two implementation are currenlty available - "fixed"
|
||||
; (with size always equals to jbmax-size) and "adaptive" (with
|
||||
; variable size, actually the new jb of IAX2). Defaults to fixed.
|
||||
|
||||
; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
|
||||
;-----------------------------------------------------------------------------------
|
||||
|
||||
[card1]
|
||||
; device = /dev/dsp1 ; alternate device
|
||||
|
||||
|
@ -981,6 +1019,9 @@ static struct ast_channel *oss_new(struct chan_oss_pvt *o,
|
|||
/* XXX what about usecnt ? */
|
||||
}
|
||||
}
|
||||
if (c)
|
||||
ast_jb_configure(c, &global_jbconf);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
@ -1407,6 +1448,10 @@ static struct chan_oss_pvt * store_config(struct ast_config *cfg, char *ctg)
|
|||
for (v = ast_variable_browse(cfg, ctg);v; v=v->next) {
|
||||
M_START(v->name, v->value);
|
||||
|
||||
/* handle jb conf */
|
||||
if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
|
||||
continue;
|
||||
|
||||
M_BOOL("autoanswer", o->autoanswer)
|
||||
M_BOOL("autohangup", o->autohangup)
|
||||
M_BOOL("overridecontext", o->overridecontext)
|
||||
|
@ -1472,6 +1517,9 @@ static int load_module(void *mod)
|
|||
int i;
|
||||
struct ast_config *cfg;
|
||||
|
||||
/* Copy the default jb config over global_jbconf */
|
||||
memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
|
||||
|
||||
/* load config file */
|
||||
cfg = ast_config_load(config);
|
||||
if (cfg != NULL) {
|
||||
|
|
|
@ -202,6 +202,17 @@ static int expiry = DEFAULT_EXPIRY;
|
|||
|
||||
#define INITIAL_CSEQ 101 /*!< our initial sip sequence number */
|
||||
|
||||
#include "asterisk/abstract_jb.h"
|
||||
/* Global jitterbuffer configuration - by default, jb is disabled */
|
||||
static struct ast_jb_conf default_jbconf =
|
||||
{
|
||||
.flags = 0,
|
||||
.max_size = -1,
|
||||
.resync_threshold = -1,
|
||||
.impl = ""
|
||||
};
|
||||
static struct ast_jb_conf global_jbconf;
|
||||
|
||||
static const char tdesc[] = "Session Initiation Protocol (SIP)";
|
||||
static const char config[] = "sip.conf";
|
||||
static const char notify_config[] = "sip_notify.conf";
|
||||
|
@ -850,6 +861,7 @@ static struct sip_pvt {
|
|||
struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */
|
||||
struct sip_pvt *next; /*!< Next dialog in chain */
|
||||
struct sip_invite_param *options; /*!< Options for INVITE */
|
||||
struct ast_jb_conf jbconf;
|
||||
} *iflist = NULL;
|
||||
|
||||
#define FLAG_RESPONSE (1 << 0)
|
||||
|
@ -1256,7 +1268,7 @@ static const struct ast_channel_tech sip_tech = {
|
|||
.type = "SIP",
|
||||
.description = "Session Initiation Protocol (SIP)",
|
||||
.capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
|
||||
.properties = AST_CHAN_TP_WANTSJITTER,
|
||||
.properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
|
||||
.requester = sip_request_call,
|
||||
.devicestate = sip_devicestate,
|
||||
.call = sip_call,
|
||||
|
@ -3312,7 +3324,11 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
|
|||
|
||||
if (recordhistory)
|
||||
append_history(i, "NewChan", "Channel %s - from %s", tmp->name, i->callid);
|
||||
|
||||
|
||||
/* Configure the new channel jb */
|
||||
if (tmp && i && i->rtp)
|
||||
ast_jb_configure(tmp, &i->jbconf);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
@ -3647,6 +3663,9 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
|
|||
p->noncodeccapability |= AST_RTP_DTMF;
|
||||
ast_string_field_set(p, context, default_context);
|
||||
|
||||
/* Assign default jb conf to the new sip_pvt */
|
||||
memcpy(&p->jbconf, &global_jbconf, sizeof(struct ast_jb_conf));
|
||||
|
||||
/* Add to active dialog list */
|
||||
ast_mutex_lock(&iflock);
|
||||
p->next = iflist;
|
||||
|
@ -13247,7 +13266,6 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, int
|
|||
for (; v; v = v->next) {
|
||||
if (handle_common_options(&peerflags[0], &mask[0], v))
|
||||
continue;
|
||||
|
||||
if (realtime && !strcasecmp(v->name, "regseconds")) {
|
||||
ast_get_time_t(v->value, ®seconds, 0, NULL);
|
||||
} else if (realtime && !strcasecmp(v->name, "ipaddr") && !ast_strlen_zero(v->value) ) {
|
||||
|
@ -13540,12 +13558,19 @@ static int reload_config(enum channelreloadreason reason)
|
|||
global_relaxdtmf = FALSE;
|
||||
global_callevents = FALSE;
|
||||
global_t1min = DEFAULT_T1MIN;
|
||||
|
||||
/* Copy the default jb config over global_jbconf */
|
||||
memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
|
||||
|
||||
ast_clear_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT);
|
||||
|
||||
/* Read the [general] config section of sip.conf (or from realtime config) */
|
||||
for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
|
||||
if (handle_common_options(&global_flags[0], &dummy[0], v))
|
||||
continue;
|
||||
/* handle jb conf */
|
||||
if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
|
||||
continue;
|
||||
|
||||
/* Create the interface list */
|
||||
if (!strcasecmp(v->name, "context")) {
|
||||
|
|
|
@ -109,6 +109,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#define SMDI_MD_WAIT_TIMEOUT 1500 /* 1.5 seconds */
|
||||
#endif
|
||||
|
||||
#include "asterisk/abstract_jb.h"
|
||||
/* Global jitterbuffer configuration - by default, jb is disabled */
|
||||
static struct ast_jb_conf default_jbconf =
|
||||
{
|
||||
.flags = 0,
|
||||
.max_size = -1,
|
||||
.resync_threshold = -1,
|
||||
.impl = ""
|
||||
};
|
||||
static struct ast_jb_conf global_jbconf;
|
||||
|
||||
#if !defined(ZT_SIG_EM_E1) || (defined(HAVE_LIBPRI) && !defined(ZT_SIG_HARDHDLC))
|
||||
#error "Your zaptel is too old. please update"
|
||||
#endif
|
||||
|
@ -684,6 +695,7 @@ static struct zt_pvt {
|
|||
#endif
|
||||
int polarity;
|
||||
int dsp_features;
|
||||
struct ast_jb_conf jbconf;
|
||||
|
||||
} *iflist = NULL, *ifend = NULL;
|
||||
|
||||
|
@ -5195,6 +5207,9 @@ static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int
|
|||
}
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
|
||||
/* Configure the new channel jb */
|
||||
if (tmp && i)
|
||||
ast_jb_configure(tmp, &i->jbconf);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
@ -6988,6 +7003,8 @@ static struct zt_pvt *mkintf(int channel, int signalling, int outsignalling, int
|
|||
for (x=0;x<3;x++)
|
||||
tmp->subs[x].zfd = -1;
|
||||
tmp->channel = channel;
|
||||
/* Assign default jb conf to the new zt_pvt */
|
||||
memcpy(&tmp->jbconf, &global_jbconf, sizeof(struct ast_jb_conf));
|
||||
}
|
||||
|
||||
if (tmp) {
|
||||
|
@ -10200,6 +10217,7 @@ static int setup_zap(int reload)
|
|||
{
|
||||
struct ast_config *cfg;
|
||||
struct ast_variable *v;
|
||||
struct ast_variable *vjb;
|
||||
struct zt_pvt *tmp;
|
||||
char *chan;
|
||||
char *c;
|
||||
|
@ -10289,6 +10307,11 @@ static int setup_zap(int reload)
|
|||
}
|
||||
#endif
|
||||
v = ast_variable_browse(cfg, "channels");
|
||||
/* Copy the default jb config over global_jbconf */
|
||||
memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
|
||||
/* Traverse all variables to handle jb conf */
|
||||
for (vjb = v; vjb; vjb = vjb->next)
|
||||
ast_jb_read_conf(&global_jbconf, vjb->name, vjb->value);
|
||||
while(v) {
|
||||
/* Create the interface list */
|
||||
if (!strcasecmp(v->name, "channel")
|
||||
|
|
|
@ -29,3 +29,31 @@ extension=s
|
|||
; To set which ALSA device to use, change this parameter
|
||||
;input_device=hw:0,0
|
||||
;output_device=hw:0,0
|
||||
|
||||
;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
|
||||
; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an
|
||||
; ALSA channel. Defaults to "no". An enabled jitterbuffer will
|
||||
; be used only if the sending side can create and the receiving
|
||||
; side can not accept jitter. The ZAP channel can't accept jitter,
|
||||
; thus an enabled jitterbuffer on the receive ZAP side will always
|
||||
; be used if the sending side can create jitter or if ZAP jb is
|
||||
; forced.
|
||||
|
||||
; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a ZAP
|
||||
; channel. Defaults to "no".
|
||||
|
||||
; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
|
||||
|
||||
; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
|
||||
; resynchronized. Useful to improve the quality of the voice, with
|
||||
; big jumps in/broken timestamps, usualy sent from exotic devices
|
||||
; and programs. Defaults to 1000.
|
||||
|
||||
; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP
|
||||
; channel. Two implementation are currenlty available - "fixed"
|
||||
; (with size always equals to jbmax-size) and "adaptive" (with
|
||||
; variable size, actually the new jb of IAX2). Defaults to fixed.
|
||||
|
||||
; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
|
||||
;-----------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -46,6 +46,34 @@
|
|||
; queuesize = 10 ; frames in device driver
|
||||
; frags = 8 ; argument to SETFRAGMENT
|
||||
|
||||
;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
|
||||
; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an
|
||||
; OSS channel. Defaults to "no". An enabled jitterbuffer will
|
||||
; be used only if the sending side can create and the receiving
|
||||
; side can not accept jitter. The ZAP channel can't accept jitter,
|
||||
; thus an enabled jitterbuffer on the receive ZAP side will always
|
||||
; be used if the sending side can create jitter or if ZAP jb is
|
||||
; forced.
|
||||
|
||||
; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a ZAP
|
||||
; channel. Defaults to "no".
|
||||
|
||||
; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
|
||||
|
||||
; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
|
||||
; resynchronized. Useful to improve the quality of the voice, with
|
||||
; big jumps in/broken timestamps, usualy sent from exotic devices
|
||||
; and programs. Defaults to 1000.
|
||||
|
||||
; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP
|
||||
; channel. Two implementation are currenlty available - "fixed"
|
||||
; (with size always equals to jbmax-size) and "adaptive" (with
|
||||
; variable size, actually the new jb of IAX2). Defaults to fixed.
|
||||
|
||||
; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
|
||||
;-----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[card1]
|
||||
; device = /dev/dsp1 ; alternate device
|
||||
|
||||
|
|
|
@ -302,6 +302,32 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
|||
; destinations which do not have a prior
|
||||
; account relationship with your server.
|
||||
|
||||
;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
|
||||
; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a
|
||||
; SIP channel. Defaults to "no". An enabled jitterbuffer will
|
||||
; be used only if the sending side can create and the receiving
|
||||
; side can not accept jitter. The SIP channel can accept jitter,
|
||||
; thus a jitterbuffer on the receive SIP side will be used only
|
||||
; if it is forced and enabled.
|
||||
|
||||
; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a SIP
|
||||
; channel. Defaults to "no".
|
||||
|
||||
; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
|
||||
|
||||
; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
|
||||
; resynchronized. Useful to improve the quality of the voice, with
|
||||
; big jumps in/broken timestamps, usualy sent from exotic devices
|
||||
; and programs. Defaults to 1000.
|
||||
|
||||
; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP
|
||||
; channel. Two implementation are currenlty available - "fixed"
|
||||
; (with size always equals to jbmaxsize) and "adaptive" (with
|
||||
; variable size, actually the new jb of IAX2). Defaults to fixed.
|
||||
|
||||
; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
|
||||
;-----------------------------------------------------------------------------------
|
||||
|
||||
[authentication]
|
||||
; Global credentials for outbound calls, i.e. when a proxy challenges your
|
||||
; Asterisk server for authentication. These credentials override
|
||||
|
|
|
@ -495,6 +495,33 @@ immediate=no
|
|||
;
|
||||
;jitterbuffers=4
|
||||
;
|
||||
;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
|
||||
; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a
|
||||
; ZAP channel. Defaults to "no". An enabled jitterbuffer will
|
||||
; be used only if the sending side can create and the receiving
|
||||
; side can not accept jitter. The ZAP channel can't accept jitter,
|
||||
; thus an enabled jitterbuffer on the receive ZAP side will always
|
||||
; be used if the sending side can create jitter or if ZAP jb is
|
||||
; forced.
|
||||
|
||||
; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a ZAP
|
||||
; channel. Defaults to "no".
|
||||
|
||||
; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
|
||||
|
||||
; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
|
||||
; resynchronized. Useful to improve the quality of the voice, with
|
||||
; big jumps in/broken timestamps, usualy sent from exotic devices
|
||||
; and programs. Defaults to 1000.
|
||||
|
||||
; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP
|
||||
; channel. Two implementation are currenlty available - "fixed"
|
||||
; (with size always equals to jbmax-size) and "adaptive" (with
|
||||
; variable size, actually the new jb of IAX2). Defaults to fixed.
|
||||
|
||||
; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
|
||||
;-----------------------------------------------------------------------------------
|
||||
;
|
||||
; You can define your own custom ring cadences here. You can define up to 8
|
||||
; pairs. If the silence is negative, it indicates where the callerid spill is
|
||||
; to be placed. Also, if you define any custom cadences, the default cadences
|
||||
|
|
13
frame.c
13
frame.c
|
@ -316,6 +316,13 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr)
|
|||
out->samples = fr->samples;
|
||||
out->offset = fr->offset;
|
||||
out->data = fr->data;
|
||||
/* Copy the timing data */
|
||||
out->has_timing_info = fr->has_timing_info;
|
||||
if (fr->has_timing_info) {
|
||||
out->ts = fr->ts;
|
||||
out->len = fr->len;
|
||||
out->seqno = fr->seqno;
|
||||
}
|
||||
} else
|
||||
out = fr;
|
||||
|
||||
|
@ -380,6 +387,12 @@ struct ast_frame *ast_frdup(struct ast_frame *f)
|
|||
out->prev = NULL;
|
||||
out->next = NULL;
|
||||
memcpy(out->data, f->data, out->datalen);
|
||||
out->has_timing_info = f->has_timing_info;
|
||||
if (f->has_timing_info) {
|
||||
out->ts = f->ts;
|
||||
out->len = f->len;
|
||||
out->seqno = f->seqno;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* abstract_jb: common implementation-independent jitterbuffer stuff
|
||||
*
|
||||
* Copyright (C) 2005, Attractel OOD
|
||||
*
|
||||
* Contributors:
|
||||
* Slav Klenov <slav@securax.org>
|
||||
*
|
||||
* Copyright on this file is disclaimed to Digium for inclusion in Asterisk
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Common implementation-independent jitterbuffer stuff.
|
||||
*
|
||||
* \author Slav Klenov <slav@securax.org>
|
||||
*/
|
||||
|
||||
#ifndef _ABSTRACT_JB_H_
|
||||
#define _ABSTRACT_JB_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ast_channel;
|
||||
struct ast_frame;
|
||||
|
||||
|
||||
/* Configuration flags */
|
||||
enum {
|
||||
AST_JB_ENABLED = (1 << 0),
|
||||
AST_JB_FORCED = (1 << 1),
|
||||
AST_JB_LOG = (1 << 2)
|
||||
};
|
||||
|
||||
#define AST_JB_IMPL_NAME_SIZE 12
|
||||
|
||||
/*!
|
||||
* \brief General jitterbuffer configuration.
|
||||
*/
|
||||
struct ast_jb_conf
|
||||
{
|
||||
/*! \brief Combination of the AST_JB_ENABLED, AST_JB_FORCED and AST_JB_LOG flags. */
|
||||
unsigned int flags;
|
||||
/*! \brief Max size of the jitterbuffer implementation. */
|
||||
long max_size;
|
||||
/*! \brief Resynchronization threshold of the jitterbuffer implementation. */
|
||||
long resync_threshold;
|
||||
/*! \brief Name of the jitterbuffer implementation to be used. */
|
||||
char impl[AST_JB_IMPL_NAME_SIZE];
|
||||
};
|
||||
|
||||
|
||||
/* Jitterbuffer configuration property names */
|
||||
#define AST_JB_CONF_PREFIX "jb"
|
||||
#define AST_JB_CONF_ENABLE "enable"
|
||||
#define AST_JB_CONF_FORCE "force"
|
||||
#define AST_JB_CONF_MAX_SIZE "maxsize"
|
||||
#define AST_JB_CONF_RESYNCH_THRESHOLD "resyncthreshold"
|
||||
#define AST_JB_CONF_IMPL "impl"
|
||||
#define AST_JB_CONF_LOG "log"
|
||||
|
||||
|
||||
struct ast_jb_impl;
|
||||
|
||||
|
||||
/*!
|
||||
* \brief General jitterbuffer state.
|
||||
*/
|
||||
struct ast_jb
|
||||
{
|
||||
/*! \brief Jitterbuffer configuration. */
|
||||
struct ast_jb_conf conf;
|
||||
/*! \brief Jitterbuffer implementation to be used. */
|
||||
struct ast_jb_impl *impl;
|
||||
/*! \brief Jitterbuffer object, passed to the implementation. */
|
||||
void *jbobj;
|
||||
/*! \brief The time the jitterbuffer was created. */
|
||||
struct timeval timebase;
|
||||
/*! \brief The time the next frame should be played. */
|
||||
long next;
|
||||
/*! \brief Voice format of the last frame in. */
|
||||
int last_format;
|
||||
/*! \brief File for frame timestamp tracing. */
|
||||
FILE *logfile;
|
||||
/*! \brief Jitterbuffer internal state flags. */
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Checks the need of a jb use in a generic bridge.
|
||||
* \param c0 first bridged channel.
|
||||
* \param c1 second bridged channel.
|
||||
*
|
||||
* Called from ast_generic_bridge() when two channels are entering in a bridge.
|
||||
* The function checks the need of a jitterbuffer, depending on both channel's
|
||||
* configuration and technology properties. As a result, this function sets
|
||||
* appropriate internal jb flags to the channels, determining further behaviour
|
||||
* of the bridged jitterbuffers.
|
||||
*
|
||||
* \return zero if there are no jitter buffers in use, non-zero if there are
|
||||
*/
|
||||
int ast_jb_do_usecheck(struct ast_channel *c0, struct ast_channel *c1);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Calculates the time, left to the closest delivery moment in a bridge.
|
||||
* \param c0 first bridged channel.
|
||||
* \param c1 second bridged channel.
|
||||
* \param time_left bridge time limit, or -1 if not set.
|
||||
*
|
||||
* Called from ast_generic_bridge() to determine the maximum time to wait for
|
||||
* activity in ast_waitfor_n() call. If neihter of the channels is using jb,
|
||||
* this function returns the time limit passed.
|
||||
*
|
||||
* \return maximum time to wait.
|
||||
*/
|
||||
int ast_jb_get_when_to_wakeup(struct ast_channel *c0, struct ast_channel *c1, int time_left);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Puts a frame into a channel jitterbuffer.
|
||||
* \param chan channel.
|
||||
* \param frame frame.
|
||||
*
|
||||
* Called from ast_generic_bridge() to put a frame into a channel's jitterbuffer.
|
||||
* The function will successfuly enqueue a frame if and only if:
|
||||
* 1. the channel is using a jitterbuffer (as determined by ast_jb_do_usecheck()),
|
||||
* 2. the frame's type is AST_FRAME_VOICE,
|
||||
* 3. the frame has timing info set and has length >= 2 ms,
|
||||
* 4. there is no some internal error happened (like failed memory allocation).
|
||||
* Frames, successfuly queued, should be delivered by the channel's jitterbuffer,
|
||||
* when their delivery time has came.
|
||||
* Frames, not successfuly queued, should be delivered immediately.
|
||||
* Dropped by the jb implementation frames are considered successfuly enqueued as
|
||||
* far as they should not be delivered at all.
|
||||
*
|
||||
* \return zero if the frame was queued, -1 if not.
|
||||
*/
|
||||
int ast_jb_put(struct ast_channel *chan, struct ast_frame *f);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Deliver the queued frames that should be delivered now for both channels.
|
||||
* \param c0 first bridged channel.
|
||||
* \param c1 second bridged channel.
|
||||
*
|
||||
* Called from ast_generic_bridge() to deliver any frames, that should be delivered
|
||||
* for the moment of invocation. Does nothing if neihter of the channels is using jb
|
||||
* or has any frames currently queued in. The function delivers frames usig ast_write()
|
||||
* each of the channels.
|
||||
*/
|
||||
void ast_jb_get_and_deliver(struct ast_channel *c0, struct ast_channel *c1);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Destroys jitterbuffer on a channel.
|
||||
* \param chan channel.
|
||||
*
|
||||
* Called from ast_channel_free() when a channel is destroyed.
|
||||
*/
|
||||
void ast_jb_destroy(struct ast_channel *chan);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Sets jitterbuffer configuration property.
|
||||
* \param conf configuration to store the property in.
|
||||
* \param varname property name.
|
||||
* \param value property value.
|
||||
*
|
||||
* Called from a channel driver to build a jitterbuffer configuration tipically when
|
||||
* reading a configuration file. It is not neccessary for a channel driver to know
|
||||
* each of the jb configuration property names. The jitterbuffer itself knows them.
|
||||
* The channel driver can pass each config var it reads through this function. It will
|
||||
* return 0 if the variable was consumed from the jb conf.
|
||||
*
|
||||
* \return zero if the property was set to the configuration, -1 if not.
|
||||
*/
|
||||
int ast_jb_read_conf(struct ast_jb_conf *conf, char *varname, char *value);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Configures a jitterbuffer on a channel.
|
||||
* \param chan channel to configure.
|
||||
* \param conf configuration to apply.
|
||||
*
|
||||
* Called from a channel driver when a channel is created and its jitterbuffer needs
|
||||
* to be configured.
|
||||
*/
|
||||
void ast_jb_configure(struct ast_channel *chan, const struct ast_jb_conf *conf);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Copies a channel's jitterbuffer configuration.
|
||||
* \param chan channel.
|
||||
* \param conf destination.
|
||||
*/
|
||||
void ast_jb_get_config(const struct ast_channel *chan, struct ast_jb_conf *conf);
|
||||
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ABSTRACT_JB_H_ */
|
|
@ -86,6 +86,8 @@
|
|||
#ifndef _ASTERISK_CHANNEL_H
|
||||
#define _ASTERISK_CHANNEL_H
|
||||
|
||||
#include "asterisk/abstract_jb.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#ifdef POLLCOMPAT
|
||||
#include "asterisk/poll-compat.h"
|
||||
|
@ -445,6 +447,9 @@ struct ast_channel {
|
|||
|
||||
/*! For easy linking */
|
||||
AST_LIST_ENTRY(ast_channel) chan_list;
|
||||
|
||||
/*! The jitterbuffer state */
|
||||
struct ast_jb jb;
|
||||
};
|
||||
|
||||
/* \defgroup chanprop Channel tech properties:
|
||||
|
@ -452,6 +457,11 @@ struct ast_channel {
|
|||
/* @{ */
|
||||
#define AST_CHAN_TP_WANTSJITTER (1 << 0)
|
||||
|
||||
/* \defgroup chanprop Channel tech properties:
|
||||
\brief Channels have this property if they can create jitter; i.e. most VoIP channels */
|
||||
/* @{ */
|
||||
#define AST_CHAN_TP_CREATESJITTER (1 << 1)
|
||||
|
||||
/* This flag has been deprecated by the transfercapbilty data member in struct ast_channel */
|
||||
/* #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 */
|
||||
|
|
|
@ -109,6 +109,14 @@ struct ast_frame {
|
|||
struct ast_frame *prev;
|
||||
/*! Next/Prev for linking stand alone frames */
|
||||
struct ast_frame *next;
|
||||
/*! Timing data flag */
|
||||
int has_timing_info;
|
||||
/*! Timestamp in milliseconds */
|
||||
long ts;
|
||||
/*! Length in milliseconds */
|
||||
long len;
|
||||
/*! Sequence number */
|
||||
int seqno;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
20
rtp.c
20
rtp.c
|
@ -696,7 +696,6 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
|
|||
int padding;
|
||||
int mark;
|
||||
int ext;
|
||||
int x;
|
||||
char iabuf[INET_ADDRSTRLEN];
|
||||
unsigned int ssrc;
|
||||
unsigned int timestamp;
|
||||
|
@ -835,17 +834,6 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
|
|||
if (!rtp->lastrxts)
|
||||
rtp->lastrxts = timestamp;
|
||||
|
||||
if (rtp->rxseqno) {
|
||||
for (x=rtp->rxseqno + 1; x < seqno; x++) {
|
||||
/* Queue empty frames */
|
||||
rtp->f.mallocd = 0;
|
||||
rtp->f.datalen = 0;
|
||||
rtp->f.data = NULL;
|
||||
rtp->f.offset = 0;
|
||||
rtp->f.samples = 0;
|
||||
rtp->f.src = "RTPMissedFrame";
|
||||
}
|
||||
}
|
||||
rtp->rxseqno = seqno;
|
||||
|
||||
if (rtp->dtmfcount) {
|
||||
|
@ -877,6 +865,11 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
|
|||
if (rtp->f.subclass == AST_FORMAT_SLINEAR)
|
||||
ast_frame_byteswap_be(&rtp->f);
|
||||
calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark);
|
||||
/* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */
|
||||
rtp->f.has_timing_info = 1;
|
||||
rtp->f.ts = timestamp / 8;
|
||||
rtp->f.len = rtp->f.samples / 8;
|
||||
rtp->f.seqno = seqno;
|
||||
} else {
|
||||
/* Video -- samples is # of samples vs. 90000 */
|
||||
if (!rtp->lastividtimestamp)
|
||||
|
@ -1701,6 +1694,9 @@ static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec
|
|||
if (rtp->lastts > rtp->lastdigitts)
|
||||
rtp->lastdigitts = rtp->lastts;
|
||||
|
||||
if (f->has_timing_info)
|
||||
rtp->lastts = f->ts * 8;
|
||||
|
||||
/* Get a pointer to the header */
|
||||
rtpheader = (unsigned char *)(f->data - hdrlen);
|
||||
|
||||
|
|
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
* scx_jitterbuf: jitterbuffering algorithm
|
||||
*
|
||||
* Copyright (C) 2005, Attractel OOD
|
||||
*
|
||||
* Contributors:
|
||||
* Slav Klenov <slav@securax.org>
|
||||
*
|
||||
* Copyright on this file is disclaimed to Digium for inclusion in Asterisk
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Jitterbuffering algorithm.
|
||||
*
|
||||
* \author Slav Klenov <slav@securax.org>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "asterisk.h"
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision $")
|
||||
|
||||
#include "asterisk/utils.h"
|
||||
#include "scx_jitterbuf.h"
|
||||
|
||||
#undef SCX_JB_DEBUG
|
||||
|
||||
#ifdef SCX_JB_DEBUG
|
||||
#define ASSERT(a)
|
||||
#else
|
||||
#define ASSERT(a) assert(a)
|
||||
#endif
|
||||
|
||||
/*! \brief private scx_jb structure */
|
||||
struct scx_jb
|
||||
{
|
||||
struct scx_jb_frame *frames;
|
||||
struct scx_jb_frame *tail;
|
||||
struct scx_jb_conf conf;
|
||||
long rxcore;
|
||||
long delay;
|
||||
long next_delivery;
|
||||
int force_resynch;
|
||||
};
|
||||
|
||||
|
||||
static struct scx_jb_frame *alloc_jb_frame(struct scx_jb *jb);
|
||||
static void release_jb_frame(struct scx_jb *jb, struct scx_jb_frame *frame);
|
||||
static void get_jb_head(struct scx_jb *jb, struct scx_jb_frame *frame);
|
||||
static int resynch_jb(struct scx_jb *jb, void *data, long ms, long ts, long now);
|
||||
|
||||
static inline struct scx_jb_frame *alloc_jb_frame(struct scx_jb *jb)
|
||||
{
|
||||
return ast_calloc(1, sizeof(struct scx_jb_frame));
|
||||
}
|
||||
|
||||
static inline void release_jb_frame(struct scx_jb *jb, struct scx_jb_frame *frame)
|
||||
{
|
||||
free(frame);
|
||||
}
|
||||
|
||||
static void get_jb_head(struct scx_jb *jb, struct scx_jb_frame *frame)
|
||||
{
|
||||
struct scx_jb_frame *fr;
|
||||
|
||||
/* unlink the frame */
|
||||
fr = jb->frames;
|
||||
jb->frames = fr->next;
|
||||
if (jb->frames) {
|
||||
jb->frames->prev = NULL;
|
||||
} else {
|
||||
/* the jb is empty - update tail */
|
||||
jb->tail = NULL;
|
||||
}
|
||||
|
||||
/* update next */
|
||||
jb->next_delivery = fr->delivery + fr->ms;
|
||||
|
||||
/* copy the destination */
|
||||
memcpy(frame, fr, sizeof(struct scx_jb_frame));
|
||||
|
||||
/* and release the frame */
|
||||
release_jb_frame(jb, fr);
|
||||
}
|
||||
|
||||
|
||||
struct scx_jb *scx_jb_new(struct scx_jb_conf *conf)
|
||||
{
|
||||
struct scx_jb *jb;
|
||||
|
||||
if (!(jb = ast_calloc(1, sizeof(*jb))))
|
||||
return NULL;
|
||||
|
||||
/* First copy our config */
|
||||
memcpy(&jb->conf, conf, sizeof(struct scx_jb_conf));
|
||||
|
||||
/* we dont need the passed config anymore - continue working with the saved one */
|
||||
conf = &jb->conf;
|
||||
|
||||
/* validate the configuration */
|
||||
if (conf->jbsize < 1)
|
||||
conf->jbsize = SCX_JB_SIZE_DEFAULT;
|
||||
|
||||
if (conf->resync_threshold < 1)
|
||||
conf->resync_threshold = SCX_JB_RESYNCH_THRESHOLD_DEFAULT;
|
||||
|
||||
/* Set the constant delay to the jitterbuf */
|
||||
jb->delay = conf->jbsize;
|
||||
|
||||
return jb;
|
||||
}
|
||||
|
||||
|
||||
void scx_jb_destroy(struct scx_jb *jb)
|
||||
{
|
||||
/* jitterbuf MUST be empty before it can be destroyed */
|
||||
ASSERT(jb->frames == NULL);
|
||||
|
||||
free(jb);
|
||||
}
|
||||
|
||||
|
||||
static int resynch_jb(struct scx_jb *jb, void *data, long ms, long ts, long now)
|
||||
{
|
||||
long diff, offset;
|
||||
struct scx_jb_frame *frame;
|
||||
|
||||
/* If jb is empty, just reinitialize the jb */
|
||||
if (!jb->frames) {
|
||||
/* debug check: tail should also be NULL */
|
||||
ASSERT(jb->tail == NULL);
|
||||
|
||||
return scx_jb_put_first(jb, data, ms, ts, now);
|
||||
}
|
||||
|
||||
/* Adjust all jb state just as the new frame is with delivery = the delivery of the last
|
||||
frame (e.g. this one with max delivery) + the length of the last frame. */
|
||||
|
||||
/* Get the diff in timestamps */
|
||||
diff = ts - jb->tail->ts;
|
||||
|
||||
/* Ideally this should be just the length of the last frame. The deviation is the desired
|
||||
offset */
|
||||
offset = diff - jb->tail->ms;
|
||||
|
||||
/* Do we really need to resynch, or this is just a frame for dropping? */
|
||||
if (!jb->force_resynch && (offset < jb->conf.resync_threshold && offset > -jb->conf.resync_threshold))
|
||||
return SCX_JB_DROP;
|
||||
|
||||
/* Reset the force resynch flag */
|
||||
jb->force_resynch = 0;
|
||||
|
||||
/* apply the offset to the jb state */
|
||||
jb->rxcore -= offset;
|
||||
frame = jb->frames;
|
||||
while (frame) {
|
||||
frame->ts += offset;
|
||||
frame = frame->next;
|
||||
}
|
||||
|
||||
/* now jb_put() should add the frame at a last position */
|
||||
return scx_jb_put(jb, data, ms, ts, now);
|
||||
}
|
||||
|
||||
|
||||
void scx_jb_set_force_resynch(struct scx_jb *jb)
|
||||
{
|
||||
jb->force_resynch = 1;
|
||||
}
|
||||
|
||||
|
||||
int scx_jb_put_first(struct scx_jb *jb, void *data, long ms, long ts, long now)
|
||||
{
|
||||
/* this is our first frame - set the base of the receivers time */
|
||||
jb->rxcore = now - ts;
|
||||
|
||||
/* init next for a first time - it should be the time the first frame should be played */
|
||||
jb->next_delivery = now + jb->delay;
|
||||
|
||||
/* put the frame */
|
||||
return scx_jb_put(jb, data, ms, ts, now);
|
||||
}
|
||||
|
||||
|
||||
int scx_jb_put(struct scx_jb *jb, void *data, long ms, long ts, long now)
|
||||
{
|
||||
struct scx_jb_frame *frame, *next, *newframe;
|
||||
long delivery;
|
||||
|
||||
/* debug check the validity of the input params */
|
||||
ASSERT(data != NULL);
|
||||
/* do not allow frames shorter than 2 ms */
|
||||
ASSERT(ms >= 2);
|
||||
ASSERT(ts >= 0);
|
||||
ASSERT(now >= 0);
|
||||
|
||||
delivery = jb->rxcore + jb->delay + ts;
|
||||
|
||||
/* check if the new frame is not too late */
|
||||
if (delivery < jb->next_delivery) {
|
||||
/* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
|
||||
the force resynch flag was not set. */
|
||||
return resynch_jb(jb, data, ms, ts, now);
|
||||
}
|
||||
|
||||
/* what if the delivery time is bigger than next + delay? Seems like a frame for the future.
|
||||
However, allow more resync_threshold ms in advance */
|
||||
if (delivery > jb->next_delivery + jb->delay + jb->conf.resync_threshold) {
|
||||
/* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
|
||||
the force resynch flag was not set. */
|
||||
return resynch_jb(jb, data, ms, ts, now);
|
||||
}
|
||||
|
||||
/* find the right place in the frames list, sorted by delivery time */
|
||||
frame = jb->tail;
|
||||
while (frame && frame->delivery > delivery) {
|
||||
frame = frame->prev;
|
||||
}
|
||||
|
||||
/* Check if the new delivery time is not covered already by the chosen frame */
|
||||
if (frame && (frame->delivery == delivery ||
|
||||
delivery < frame->delivery + frame->ms ||
|
||||
(frame->next && delivery + ms > frame->next->delivery)))
|
||||
{
|
||||
/* TODO: Should we check for resynch here? Be careful to do not allow threshold smaller than
|
||||
the size of the jb */
|
||||
|
||||
/* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
|
||||
the force resynch flag was not set. */
|
||||
return resynch_jb(jb, data, ms, ts, now);
|
||||
}
|
||||
|
||||
/* Reset the force resynch flag */
|
||||
jb->force_resynch = 0;
|
||||
|
||||
/* Get a new frame */
|
||||
newframe = alloc_jb_frame(jb);
|
||||
newframe->data = data;
|
||||
newframe->ts = ts;
|
||||
newframe->ms = ms;
|
||||
newframe->delivery = delivery;
|
||||
|
||||
/* and insert it right on place */
|
||||
if (frame) {
|
||||
next = frame->next;
|
||||
frame->next = newframe;
|
||||
if (next) {
|
||||
newframe->next = next;
|
||||
next->prev = newframe;
|
||||
} else {
|
||||
/* insert after the last frame - should update tail */
|
||||
jb->tail = newframe;
|
||||
newframe->next = NULL;
|
||||
}
|
||||
newframe->prev = frame;
|
||||
|
||||
return SCX_JB_OK;
|
||||
} else if (!jb->frames) {
|
||||
/* the frame list is empty or thats just the first frame ever */
|
||||
/* tail should also be NULL is that case */
|
||||
ASSERT(jb->tail == NULL);
|
||||
jb->frames = jb->tail = newframe;
|
||||
newframe->next = NULL;
|
||||
newframe->prev = NULL;
|
||||
|
||||
return SCX_JB_OK;
|
||||
} else {
|
||||
/* insert on a first position - should update frames head */
|
||||
newframe->next = jb->frames;
|
||||
newframe->prev = NULL;
|
||||
jb->frames->prev = newframe;
|
||||
jb->frames = newframe;
|
||||
|
||||
return SCX_JB_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int scx_jb_get(struct scx_jb *jb, struct scx_jb_frame *frame, long now, long interpl)
|
||||
{
|
||||
ASSERT(now >= 0);
|
||||
ASSERT(interpl >= 2);
|
||||
|
||||
if (now < jb->next_delivery) {
|
||||
/* too early for the next frame */
|
||||
return SCX_JB_NOFRAME;
|
||||
}
|
||||
|
||||
/* Is the jb empty? */
|
||||
if (!jb->frames) {
|
||||
/* should interpolate a frame */
|
||||
/* update next */
|
||||
jb->next_delivery += interpl;
|
||||
|
||||
return SCX_JB_INTERP;
|
||||
}
|
||||
|
||||
/* Isn't it too late for the first frame available in the jb? */
|
||||
if (now > jb->frames->delivery + jb->frames->ms) {
|
||||
/* yes - should drop this frame and update next to point the next frame (get_jb_head() does it) */
|
||||
get_jb_head(jb, frame);
|
||||
|
||||
return SCX_JB_DROP;
|
||||
}
|
||||
|
||||
/* isn't it too early to play the first frame available? */
|
||||
if (now < jb->frames->delivery) {
|
||||
/* yes - should interpolate one frame */
|
||||
/* update next */
|
||||
jb->next_delivery += interpl;
|
||||
|
||||
return SCX_JB_INTERP;
|
||||
}
|
||||
|
||||
/* we have a frame for playing now (get_jb_head() updates next) */
|
||||
get_jb_head(jb, frame);
|
||||
|
||||
return SCX_JB_OK;
|
||||
}
|
||||
|
||||
|
||||
long scx_jb_next(struct scx_jb *jb)
|
||||
{
|
||||
return jb->next_delivery;
|
||||
}
|
||||
|
||||
|
||||
int scx_jb_remove(struct scx_jb *jb, struct scx_jb_frame *frameout)
|
||||
{
|
||||
if (!jb->frames)
|
||||
return SCX_JB_NOFRAME;
|
||||
|
||||
get_jb_head(jb, frameout);
|
||||
|
||||
return SCX_JB_OK;
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* scx_jitterbuf: jitterbuffering algorithm
|
||||
*
|
||||
* Copyright (C) 2005, Attractel OOD
|
||||
*
|
||||
* Contributors:
|
||||
* Slav Klenov <slav@securax.org>
|
||||
*
|
||||
* Copyright on this file is disclaimed to Digium for inclusion in Asterisk
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Jitterbuffering algorithm.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SCX_JITTERBUF_H_
|
||||
#define _SCX_JITTERBUF_H_
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* return codes */
|
||||
#define SCX_JB_OK 0
|
||||
#define SCX_JB_DROP 1
|
||||
#define SCX_JB_INTERP 2
|
||||
#define SCX_JB_NOFRAME 3
|
||||
|
||||
|
||||
/* defaults */
|
||||
#define SCX_JB_SIZE_DEFAULT 200
|
||||
#define SCX_JB_RESYNCH_THRESHOLD_DEFAULT 1000
|
||||
|
||||
|
||||
/* jb configuration properties */
|
||||
struct scx_jb_conf
|
||||
{
|
||||
long jbsize;
|
||||
long resync_threshold;
|
||||
};
|
||||
|
||||
|
||||
struct scx_jb_frame
|
||||
{
|
||||
void *data;
|
||||
long ts;
|
||||
long ms;
|
||||
long delivery;
|
||||
struct scx_jb_frame *next;
|
||||
struct scx_jb_frame *prev;
|
||||
};
|
||||
|
||||
|
||||
struct scx_jb;
|
||||
|
||||
|
||||
/* jb interface */
|
||||
|
||||
struct scx_jb * scx_jb_new(struct scx_jb_conf *conf);
|
||||
|
||||
void scx_jb_destroy(struct scx_jb *jb);
|
||||
|
||||
int scx_jb_put_first(struct scx_jb *jb, void *data, long ms, long ts, long now);
|
||||
|
||||
int scx_jb_put(struct scx_jb *jb, void *data, long ms, long ts, long now);
|
||||
|
||||
int scx_jb_get(struct scx_jb *jb, struct scx_jb_frame *frame, long now, long interpl);
|
||||
|
||||
long scx_jb_next(struct scx_jb *jb);
|
||||
|
||||
int scx_jb_remove(struct scx_jb *jb, struct scx_jb_frame *frameout);
|
||||
|
||||
void scx_jb_set_force_resynch(struct scx_jb *jb);
|
||||
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SCX_JITTERBUF_H_ */
|
21
translate.c
21
translate.c
|
@ -154,6 +154,12 @@ static int framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
|
|||
int16_t *dst = (int16_t *)pvt->outbuf;
|
||||
int ret;
|
||||
int samples = pvt->samples; /* initial value */
|
||||
|
||||
/* Copy the last in jb timing info to the pvt */
|
||||
pvt->f.has_timing_info = f->has_timing_info;
|
||||
pvt->f.ts = f->ts;
|
||||
pvt->f.len = f->len;
|
||||
pvt->f.seqno = f->seqno;
|
||||
|
||||
if (f->samples == 0) {
|
||||
ast_log(LOG_WARNING, "no samples for %s\n", pvt->t->name);
|
||||
|
@ -285,6 +291,15 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f,
|
|||
struct ast_trans_pvt *p = path;
|
||||
struct ast_frame *out = f;
|
||||
struct timeval delivery;
|
||||
int has_timing_info;
|
||||
long ts;
|
||||
long len;
|
||||
int seqno;
|
||||
|
||||
has_timing_info = f->has_timing_info;
|
||||
ts = f->ts;
|
||||
len = f->len;
|
||||
seqno = f->seqno;
|
||||
|
||||
/* XXX hmmm... check this below */
|
||||
if (!ast_tvzero(f->delivery)) {
|
||||
|
@ -331,6 +346,12 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f,
|
|||
path->nextout = ast_tvadd(path->nextout, ast_samp2tv( out->samples, 8000));
|
||||
} else {
|
||||
out->delivery = ast_tv(0, 0);
|
||||
out->has_timing_info = has_timing_info;
|
||||
if (has_timing_info) {
|
||||
out->ts = ts;
|
||||
out->len = len;
|
||||
out->seqno = seqno;
|
||||
}
|
||||
}
|
||||
/* Invalidate prediction if we're entering a silence period */
|
||||
if (out->frametype == AST_FRAME_CNG)
|
||||
|
|
Loading…
Reference in New Issue