From 9953ec3cbb059275d5b7c618e64ceebdb06ceccf Mon Sep 17 00:00:00 2001
From: Russell Bryant <russell@digium.com>
Date: Wed, 31 May 2006 16:56:50 +0000
Subject: [PATCH] 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: http://svn.asterisk.org/svn/asterisk/trunk@31052 f38db490-d61c-443f-a65b-d21fe96a405b
---
 .cleancount                    |   2 +-
 Makefile                       |   2 +-
 channel.c                      |  29 ++-
 channels/chan_alsa.c           |  22 +++
 channels/chan_iax2.c           |   1 +
 channels/chan_oss.c            |  48 +++++
 channels/chan_sip.c            |  31 ++-
 channels/chan_zap.c            |  23 +++
 configs/alsa.conf.sample       |  28 +++
 configs/oss.conf.sample        |  28 +++
 configs/sip.conf.sample        |  26 +++
 configs/zapata.conf.sample     |  27 +++
 frame.c                        |  13 ++
 include/asterisk/abstract_jb.h | 220 +++++++++++++++++++++
 include/asterisk/channel.h     |  10 +
 include/asterisk/frame.h       |   8 +
 rtp.c                          |  20 +-
 scx_jitterbuf.c                | 351 +++++++++++++++++++++++++++++++++
 scx_jitterbuf.h                |  93 +++++++++
 translate.c                    |  21 ++
 20 files changed, 983 insertions(+), 20 deletions(-)
 create mode 100644 include/asterisk/abstract_jb.h
 create mode 100644 scx_jitterbuf.c
 create mode 100644 scx_jitterbuf.h

diff --git a/.cleancount b/.cleancount
index 98d9bcb75a..3c032078a4 100644
--- a/.cleancount
+++ b/.cleancount
@@ -1 +1 @@
-17
+18
diff --git a/Makefile b/Makefile
index 3f62202511..540d7a5bc7 100644
--- a/Makefile
+++ b/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
diff --git a/channel.c b/channel.c
index 59789e93f3..545e00231c 100644
--- a/channel.c
+++ b/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);
diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c
index e85b051eed..a3cbfa2bc6 100644
--- a/channels/chan_alsa.c
+++ b/channels/chan_alsa.c
@@ -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"))
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index ae92d33601..9424c117bf 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -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 */
diff --git a/channels/chan_oss.c b/channels/chan_oss.c
index 415539a83a..a25e7819c7 100644
--- a/channels/chan_oss.c
+++ b/channels/chan_oss.c
@@ -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) {
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index d3f329deb6..732a6b141e 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -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, &regseconds, 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")) {
diff --git a/channels/chan_zap.c b/channels/chan_zap.c
index 60b24e791e..016bd1a39c 100644
--- a/channels/chan_zap.c
+++ b/channels/chan_zap.c
@@ -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")
diff --git a/configs/alsa.conf.sample b/configs/alsa.conf.sample
index cba49db178..bfe77101f0 100644
--- a/configs/alsa.conf.sample
+++ b/configs/alsa.conf.sample
@@ -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".
+;-----------------------------------------------------------------------------------
+
diff --git a/configs/oss.conf.sample b/configs/oss.conf.sample
index f2f90d3b5b..6997ccb9c2 100644
--- a/configs/oss.conf.sample
+++ b/configs/oss.conf.sample
@@ -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
 
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index a78bae17f3..8a52b07a80 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -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
diff --git a/configs/zapata.conf.sample b/configs/zapata.conf.sample
index ee6db17b83..aacbce0206 100644
--- a/configs/zapata.conf.sample
+++ b/configs/zapata.conf.sample
@@ -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
diff --git a/frame.c b/frame.c
index 9006d5f7cc..914d4b6b4e 100644
--- a/frame.c
+++ b/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;
 }
 
diff --git a/include/asterisk/abstract_jb.h b/include/asterisk/abstract_jb.h
new file mode 100644
index 0000000000..622f49b466
--- /dev/null
+++ b/include/asterisk/abstract_jb.h
@@ -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_ */
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 29e909e3a0..9d38dff5b4 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.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 */
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index 102a637f49..7029517d6a 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -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;
 };
 
 /*!
diff --git a/rtp.c b/rtp.c
index bfb2262038..210ed58616 100644
--- a/rtp.c
+++ b/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);
 
diff --git a/scx_jitterbuf.c b/scx_jitterbuf.c
new file mode 100644
index 0000000000..e0422f4692
--- /dev/null
+++ b/scx_jitterbuf.c
@@ -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;
+}
diff --git a/scx_jitterbuf.h b/scx_jitterbuf.h
new file mode 100644
index 0000000000..3344462942
--- /dev/null
+++ b/scx_jitterbuf.h
@@ -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_ */
diff --git a/translate.c b/translate.c
index d713817539..2db3525ab4 100644
--- a/translate.c
+++ b/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)