diff --git a/sflphone-common/src/audio/Makefile.am b/sflphone-common/src/audio/Makefile.am
index 116c324380899d58a1a9e434f69f8014d367407d..fcf2c2e9b7c22d91d5f70402124026168fa8fdce 100644
--- a/sflphone-common/src/audio/Makefile.am
+++ b/sflphone-common/src/audio/Makefile.am
@@ -21,6 +21,7 @@ libaudio_la_SOURCES = \
 		speexechocancel.cpp \
 		audioprocessing.cpp \
 		dcblocker.cpp \
+		jitterbuf.cpp \
 		$(SPEEX_SOURCES_CPP)
 
 noinst_HEADERS = \
@@ -37,6 +38,7 @@ noinst_HEADERS = \
 		speexechocancel.h \
 		audioprocessing.h \
 		dcblocker.h \
+		jitterbuf.h \
 		samplerateconverter.h
 
 libaudio_la_LIBADD = \
diff --git a/sflphone-common/src/audio/audiortp/AudioRtpSession.h b/sflphone-common/src/audio/audiortp/AudioRtpSession.h
index 0de99baa0108d19e4cd1bc4d0ad33b3edd1f1e87..48a44b1d1a60241151b50a16c5bed3ae90bc2231 100644
--- a/sflphone-common/src/audio/audiortp/AudioRtpSession.h
+++ b/sflphone-common/src/audio/audiortp/AudioRtpSession.h
@@ -52,6 +52,8 @@
 
 #include <speex/speex_jitter.h>
 
+#include "audio/jitterbuf.h"
+
 // Frequency (in packet number)
 #define RTP_TIMESTAMP_RESET_FREQ 100
 
@@ -232,11 +234,22 @@ namespace sfl {
               */
              EventQueue _eventQueue;
 
-	     JitterBuffer *_jbuffer;
+	     /**
+	      * Adaptive jitter buffer
+	      */
+	     jitterbuf * _jbuffer;
+
+	     /**
+	      * Packet size in ms
+	      */
+	     int _packetLength;
 
 	     int _ts;
 
-	     int jitterSeqNum;
+	     /**
+	      * Current time in ms
+	      */
+	     int _currentTime;
 	     
         protected:
 
@@ -283,13 +296,15 @@ namespace sfl {
         _layerFrameSize = _audiolayer->getFrameSize(); // in ms
         _layerSampleRate = _audiolayer->getSampleRate();
 
-	_jbuffer = jitter_buffer_init(20);
-	_ts = 0;
-	jitterSeqNum = 0;
+	_jbuffer = jb_new();
 
-	// int i = 160;
-	// jitter_buffer_ctl(_jbuffer, JITTER_BUFFER_SET_MARGIN, &i);
-	// jitter_buffer_ctl(_jbuffer, JITTER_BUFFER_SET_CONCEALMENT_SIZE, &i);
+	_jbuffer->info.conf.max_jitterbuf = 1000;
+	_jbuffer->info.conf.target_extra = 100;
+	_jbuffer->info.silence_begin_ts = 0;
+
+	_ts= 0;
+	_packetLength = 20;
+	_currentTime = 0;
     }
     
     template <typename D>
@@ -318,7 +333,7 @@ namespace sfl {
 	    delete _audiocodec; _audiocodec = NULL;
         }
 
-	jitter_buffer_destroy(_jbuffer);
+	jb_destroy(_jbuffer);
     }
     
     template <typename D>
@@ -653,68 +668,51 @@ namespace sfl {
         }
 
         const ost::AppDataUnit* adu = NULL;
-
 	int packetTimestamp = static_cast<D*>(this)->getFirstTimestamp();
         adu = static_cast<D*>(this)->getData(packetTimestamp);
-	// packetTimestamp = adu->getgetTimestamp();
 
-        if (adu == NULL) {
-            // _debug("No RTP audio stream\n");
-            return;
-        }
-
-        unsigned char* spkrData  = (unsigned char*) adu->getData(); // data in char
-        unsigned int size = adu->getSize(); // size in char
+	if(!adu)
+	  return;
 
-	JitterBufferPacket jPacketIn;
-	jPacketIn.data = (char *)spkrData;
-	jPacketIn.len = size;
-	// jPacketIn.timestamp = static_cast<D*>(this)->getFirstTimestamp();
-	jPacketIn.timestamp = jitterSeqNum * _timestampIncrement;
-	jPacketIn.span = _timestampIncrement;
-	jPacketIn.sequence = ++jitterSeqNum;
+	unsigned char* spkrDataIn = NULL;
+	unsigned int size = 0;
+	int result;
 
-	jitter_buffer_put(_jbuffer, &jPacketIn);
+	jb_frame frame;
 
-	JitterBufferPacket jPacketOut;
-	jPacketOut.data = new char[size];
-	jPacketOut.len = size;
-	jPacketIn.timestamp = 0;
-	jPacketIn.span = 0;
-	jPacketIn.sequence = 0;
-
-	int desiredSpan = _timestampIncrement;
-	spx_int32_t offs = 0;
+	if (adu) {
+        
+	  spkrDataIn  = (unsigned char*) adu->getData(); // data in char
+	  size = adu->getSize(); // size in char
+	  result = jb_put(_jbuffer, spkrDataIn, JB_TYPE_VOICE, _packetLength, _ts+=20, _currentTime);
+	  // result = jb_get(_jbuffer, &frame, _currentTime+=20, _packetLength);
+	}
+	else {
+	    _debug("No RTP packet available !!!!!!!!!!!!!!!!!!!!!!!\n");
+	}
 
-	int result = JITTER_BUFFER_INTERNAL_ERROR;
-	result = jitter_buffer_get(_jbuffer, &jPacketOut, desiredSpan, &offs);
-	jitter_buffer_tick(_jbuffer);
+	result = jb_get(_jbuffer, &frame, _currentTime+=20, _packetLength);
 
 	/*
 	switch(result) {
-	  case JITTER_BUFFER_OK:
-	    _debug("JITTER_BUFFER_OK");
-	    break;
-	  case JITTER_BUFFER_MISSING:
-	    _debug("JITTER_BUFFER_MISSING");
-	    break;
-	  case JITTER_BUFFER_INTERNAL_ERROR:
-	    _debug("JITTER_BUFFER_INTERNAL_ERROR");
-	    break;
-	  case JITTER_BUFFER_BAD_ARGUMENT:
-	    _debug("JITTER_BUFFER_BAD_ARGUMENT");
-	    break;
-	  default:
-	    _debug("Unknown error");
-	    break;
+	case 0: printf("You\'ve got frame!\n"); break;
+	case 1: printf("Here\'s an audio frame you should just drop.  Ask me again for this time..\n"); break;
+	case 2: printf("There\'s no frame scheduled for this time.\n"); break;
+	case 3: printf("Please interpolate an interpl-length frame for this time (either we need to grow, or there was a lost frame)\n"); break;
+	case 4: printf("Empty\n"); break;
+	default: printf("Unknown returned value\n"); break;
 	}
 	*/
 
         // DTMF over RTP, size must be over 4 in order to process it as voice data
         if(size > 4) {
-	    // processDataDecode(spkrData, size);
-	    if(result == JITTER_BUFFER_OK)
-	        processDataDecode ((unsigned char *)jPacketOut.data, jPacketOut.len);
+	    // processDataDecode(spkrDataIn, size);
+	    if(result == 0) {
+	      processDataDecode((unsigned char *)(frame.data), 160);
+	    }
+	    else {
+	      _debug("bad data");
+	    }
         }
         else {
 	    // _debug("RTP: Received an RTP event with payload: %d", adu->getType());
@@ -722,8 +720,7 @@ namespace sfl {
 	    // _debug("RTP: Data received %d", dtmf->event);
         }
 
-	delete jPacketOut.data;
-	delete adu;
+	// delete adu;
     }
     
     template <typename D>
@@ -742,8 +739,6 @@ namespace sfl {
 	// Timestamp must be initialized randomly
 	_timestamp = static_cast<D*>(this)->getCurrentTimestamp();
 
-	_ts = 0;
-
         int sessionWaiting;
         int threadSleep = 0;
 
diff --git a/sflphone-common/src/audio/jitterbuf.cpp b/sflphone-common/src/audio/jitterbuf.cpp
new file mode 100755
index 0000000000000000000000000000000000000000..06a418090f874e92005c8d722932b980a8e3b40c
--- /dev/null
+++ b/sflphone-common/src/audio/jitterbuf.cpp
@@ -0,0 +1,876 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004-2005, Horizon Wimba, Inc.
+ *
+ * Contributors:
+ * Steve Kann <stevek@stevek.com>
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ *
+ * 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 jitterbuf: an application-independent jitterbuffer
+ * \author Steve Kann <stevek@stevek.com>
+ *
+ */
+
+#include "jitterbuf.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/*! define these here, just for ancient compiler systems */
+#define JB_LONGMAX 2147483647L
+#define JB_LONGMIN (-JB_LONGMAX - 1L)
+
+#define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0)
+#define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0)
+#define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
+
+#ifdef DEEP_DEBUG
+#define jb_dbg2(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
+#else
+#define jb_dbg2(...) ((void)0)
+#endif
+
+static jb_output_function_t warnf, errf, dbgf;
+
+void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg) 
+{
+	errf = err;
+	warnf = warn;
+	dbgf = dbg;
+}
+
+static void increment_losspct(jitterbuf *jb) 
+{
+	jb->info.losspct = (100000 + 499 * jb->info.losspct)/500;    
+}
+
+static void decrement_losspct(jitterbuf *jb) 
+{
+	jb->info.losspct = (499 * jb->info.losspct)/500;    
+}
+
+void jb_reset(jitterbuf *jb) 
+{
+	/* only save settings */
+	jb_conf s = jb->info.conf;
+	memset(jb, 0, sizeof(*jb));
+	jb->info.conf = s;
+
+	/* initialize length, using the default value */
+	jb->info.current = jb->info.target = jb->info.conf.target_extra = JB_TARGET_EXTRA;
+	jb->info.silence_begin_ts = -1; 
+}
+
+jitterbuf * jb_new() 
+{
+	jitterbuf *jb;
+
+	if (!(jb = (jitterbuf *)malloc(sizeof(*jb)))) 
+		return NULL;
+
+	jb_reset(jb);
+
+	jb_dbg2("jb_new() = %x\n", jb);
+	return jb;
+}
+
+void jb_destroy(jitterbuf *jb) 
+{
+	jb_frame *frame; 
+	jb_dbg2("jb_destroy(%x)\n", jb);
+
+	/* free all the frames on the "free list" */
+	frame = jb->free;
+	while (frame != NULL) {
+		jb_frame *next = frame->next;
+		free(frame);
+		frame = next;
+	}
+
+	/* free ourselves! */ 
+	free(jb);
+}
+
+
+
+#if 0
+static int longcmp(const void *a, const void *b) 
+{
+	return *(long *)a - *(long *)b;
+}
+#endif
+
+/*!	\brief simple history manipulation 
+ 	\note maybe later we can make the history buckets variable size, or something? */
+/* drop parameter determines whether we will drop outliers to minimize
+ * delay */
+static int history_put(jitterbuf *jb, long ts, long now, long ms) 
+{
+	long delay = now - (ts - jb->info.resync_offset);
+	long threshold = 2 * jb->info.jitter + jb->info.conf.resync_threshold;
+	long kicked;
+
+	/* don't add special/negative times to history */
+	if (ts <= 0) 
+		return 0;
+
+	/* check for drastic change in delay */
+	if (jb->info.conf.resync_threshold != -1) {
+		if (abs(delay - jb->info.last_delay) > threshold) {
+			jb->info.cnt_delay_discont++;
+			if (jb->info.cnt_delay_discont > 3) {
+				/* resync the jitterbuffer */
+				jb->info.cnt_delay_discont = 0;
+				jb->hist_ptr = 0;
+				jb->hist_maxbuf_valid = 0;
+
+				jb_warn("Resyncing the jb. last_delay %ld, this delay %ld, threshold %ld, new offset %ld\n", jb->info.last_delay, delay, threshold, ts - now);
+				jb->info.resync_offset = ts - now;
+				jb->info.last_delay = delay = 0; /* after resync, frame is right on time */
+			} else {
+				return -1;
+			}
+		} else {
+			jb->info.last_delay = delay;
+			jb->info.cnt_delay_discont = 0;
+		}
+	}
+
+	kicked = jb->history[jb->hist_ptr % JB_HISTORY_SZ];
+
+	jb->history[(jb->hist_ptr++) % JB_HISTORY_SZ] = delay;
+
+	/* optimization; the max/min buffers don't need to be recalculated, if this packet's
+	 * entry doesn't change them.  This happens if this packet is not involved, _and_ any packet
+	 * that got kicked out of the history is also not involved 
+	 * We do a number of comparisons, but it's probably still worthwhile, because it will usually
+	 * succeed, and should be a lot faster than going through all 500 packets in history */
+	if (!jb->hist_maxbuf_valid)
+		return 0;
+
+	/* don't do this until we've filled history 
+	 * (reduces some edge cases below) */
+	if (jb->hist_ptr < JB_HISTORY_SZ)
+		goto invalidate;
+
+	/* if the new delay would go into min */
+	if (delay < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])
+		goto invalidate;
+
+	/* or max.. */
+	if (delay > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])
+		goto invalidate;
+
+	/* or the kicked delay would be in min */
+	if (kicked <= jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1]) 
+		goto invalidate;
+
+	if (kicked >= jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1]) 
+		goto invalidate;
+
+	/* if we got here, we don't need to invalidate, 'cause this delay didn't 
+	 * affect things */
+	return 0;
+	/* end optimization */
+
+
+invalidate:
+	jb->hist_maxbuf_valid = 0;
+	return 0;
+}
+
+static void history_calc_maxbuf(jitterbuf *jb) 
+{
+	int i,j;
+
+	if (jb->hist_ptr == 0) 
+		return;
+
+
+	/* initialize maxbuf/minbuf to the latest value */
+	for (i=0;i<JB_HISTORY_MAXBUF_SZ;i++) {
+/*
+ * jb->hist_maxbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
+ * jb->hist_minbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
+ */
+		jb->hist_maxbuf[i] = JB_LONGMIN;
+		jb->hist_minbuf[i] = JB_LONGMAX;
+	}
+
+	/* use insertion sort to populate maxbuf */
+	/* we want it to be the top "n" values, in order */
+
+	/* start at the beginning, or JB_HISTORY_SZ frames ago */
+	i = (jb->hist_ptr > JB_HISTORY_SZ) ? (jb->hist_ptr - JB_HISTORY_SZ) : 0; 
+
+	for (;i<jb->hist_ptr;i++) {
+		long toins = jb->history[i % JB_HISTORY_SZ];
+
+		/* if the maxbuf should get this */
+		if (toins > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])  {
+
+			/* insertion-sort it into the maxbuf */
+			for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
+				/* found where it fits */
+				if (toins > jb->hist_maxbuf[j]) {
+					/* move over */
+					memmove(jb->hist_maxbuf + j + 1, jb->hist_maxbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_maxbuf[0]));
+					/* insert */
+					jb->hist_maxbuf[j] = toins;
+
+					break;
+				}
+			}
+		}
+
+		/* if the minbuf should get this */
+		if (toins < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])  {
+
+			/* insertion-sort it into the maxbuf */
+			for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
+				/* found where it fits */
+				if (toins < jb->hist_minbuf[j]) {
+					/* move over */
+					memmove(jb->hist_minbuf + j + 1, jb->hist_minbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_minbuf[0]));
+					/* insert */
+					jb->hist_minbuf[j] = toins;
+
+					break;
+				}
+			}
+		}
+
+		if (0) { 
+			int k;
+			fprintf(stderr, "toins = %ld\n", toins);
+			fprintf(stderr, "maxbuf =");
+			for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++) 
+				fprintf(stderr, "%ld ", jb->hist_maxbuf[k]);
+			fprintf(stderr, "\nminbuf =");
+			for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++) 
+				fprintf(stderr, "%ld ", jb->hist_minbuf[k]);
+			fprintf(stderr, "\n");
+		}
+	}
+
+	jb->hist_maxbuf_valid = 1;
+}
+
+static void history_get(jitterbuf *jb) 
+{
+	long max, min, jitter;
+	int idx;
+	int count;
+
+	if (!jb->hist_maxbuf_valid) 
+		history_calc_maxbuf(jb);
+
+	/* count is how many items in history we're examining */
+	count = (jb->hist_ptr < JB_HISTORY_SZ) ? jb->hist_ptr : JB_HISTORY_SZ;
+
+	/* idx is the "n"ths highest/lowest that we'll look for */
+	idx = count * JB_HISTORY_DROPPCT / 100;
+
+	/* sanity checks for idx */
+	if (idx > (JB_HISTORY_MAXBUF_SZ - 1)) 
+		idx = JB_HISTORY_MAXBUF_SZ - 1;
+
+	if (idx < 0) {
+		jb->info.min = 0;
+		jb->info.jitter = 0;
+		return;
+	}
+
+	max = jb->hist_maxbuf[idx];
+	min = jb->hist_minbuf[idx];
+
+	jitter = max - min;
+
+	/* these debug stmts compare the difference between looking at the absolute jitter, and the
+	 * values we get by throwing away the outliers */
+	// printf("[%ld] min=%ld, max=%ld, jitter=%ld\n", index, min, max, jitter);
+	// printf("[%ld] min=%ld, max=%ld, jitter=%ld\n", 0, jb->hist_minbuf[0], jb->hist_maxbuf[0], jb->hist_maxbuf[0]-jb->hist_minbuf[0]);
+
+	jb->info.min = min;
+	jb->info.jitter = jitter;
+}
+
+/* returns 1 if frame was inserted into head of queue, 0 otherwise */
+static int queue_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts) 
+{
+	jb_frame *frame;
+	jb_frame *p;
+	int head = 0;
+	long resync_ts = ts - jb->info.resync_offset;
+
+	if ((frame = jb->free)) {
+		jb->free = frame->next;
+	} else if (!(frame = (jb_frame *)malloc(sizeof(*frame)))) {
+		jb_err("cannot allocate frame\n");
+		return 0;
+	}
+
+	jb->info.frames_cur++;
+
+	frame->data = data;
+	frame->ts = resync_ts;
+	frame->ms = ms;
+	frame->type = type;
+
+	/* 
+	 * frames are a circular list, jb-frames points to to the lowest ts, 
+	 * jb->frames->prev points to the highest ts
+	 */
+
+	if (!jb->frames) {  /* queue is empty */
+		jb->frames = frame;
+		frame->next = frame;
+		frame->prev = frame;
+		head = 1;
+	} else if (resync_ts < jb->frames->ts) {
+		frame->next = jb->frames;
+		frame->prev = jb->frames->prev;
+
+		frame->next->prev = frame;
+		frame->prev->next = frame;
+
+		/* frame is out of order */
+		jb->info.frames_ooo++;
+
+		jb->frames = frame;
+		head = 1;
+	} else { 
+		p = jb->frames;
+
+		/* frame is out of order */
+		if (resync_ts < p->prev->ts) jb->info.frames_ooo++;
+
+		while (resync_ts < p->prev->ts && p->prev != jb->frames) 
+			p = p->prev;
+
+		frame->next = p;
+		frame->prev = p->prev;
+
+		frame->next->prev = frame;
+		frame->prev->next = frame;
+	}
+	return head;
+}
+
+static long queue_next(jitterbuf *jb) 
+{
+	if (jb->frames) 
+		return jb->frames->ts;
+	else 
+		return -1;
+}
+
+static long queue_last(jitterbuf *jb) 
+{
+	if (jb->frames) 
+		return jb->frames->prev->ts;
+	else 
+		return -1;
+}
+
+static jb_frame *_queue_get(jitterbuf *jb, long ts, int all) 
+{
+	jb_frame *frame;
+	frame = jb->frames;
+
+	if (!frame)
+		return NULL;
+
+	printf("queue_get: ASK %ld FIRST %ld\n", ts, frame->ts);
+
+	if (all || ts >= frame->ts) {
+		/* remove this frame */
+		frame->prev->next = frame->next;
+		frame->next->prev = frame->prev;
+
+		if (frame->next == frame)
+			jb->frames = NULL;
+		else
+			jb->frames = frame->next;
+
+
+		/* insert onto "free" single-linked list */
+		frame->next = jb->free;
+		jb->free = frame;
+
+		jb->info.frames_cur--;
+
+		/* we return the frame pointer, even though it's on free list, 
+		 * but caller must copy data */
+		return frame;
+	} 
+
+	return NULL;
+}
+
+static jb_frame *queue_get(jitterbuf *jb, long ts) 
+{
+	return _queue_get(jb,ts,0);
+}
+
+static jb_frame *queue_getall(jitterbuf *jb) 
+{
+	return _queue_get(jb,0,1);
+}
+
+/* some diagnostics */
+void jb_dbginfo(jitterbuf *jb) 
+{
+
+	printf("\njb info: fin=%ld fout=%ld flate=%ld flost=%ld fdrop=%ld fcur=%ld\n",
+		jb->info.frames_in, jb->info.frames_out, jb->info.frames_late, jb->info.frames_lost, jb->info.frames_dropped, jb->info.frames_cur);
+	
+	printf("jitter=%ld current=%ld target=%ld min=%ld sil=%ld len=%ld len/fcur=%ld\n",
+		jb->info.jitter, jb->info.current, jb->info.target, jb->info.min, jb->info.silence_begin_ts, jb->info.current - jb->info.min, 
+		jb->info.frames_cur ? (jb->info.current - jb->info.min)/jb->info.frames_cur : -8);
+	if (jb->info.frames_in > 0) 
+		printf("jb info: Loss PCT = %ld%%, Late PCT = %ld%%\n",
+			jb->info.frames_lost * 100/(jb->info.frames_in + jb->info.frames_lost), 
+			jb->info.frames_late * 100/jb->info.frames_in);
+	printf("jb info: queue %ld -> %ld.  last_ts %ld (queue len: %ld) last_ms %ld\n\n",
+		queue_next(jb), 
+		queue_last(jb),
+		jb->info.next_voice_ts, 
+		queue_last(jb) - queue_next(jb),
+		jb->info.last_voice_ms);
+}
+
+
+#ifdef DEEP_DEBUG
+static void jb_chkqueue(jitterbuf *jb) 
+{
+	int i=0;
+	jb_frame *p = jb->frames;
+
+	if (!p) {
+		return;
+	}
+
+	do {
+		if (p->next == NULL)  {
+			jb_err("Queue is BROKEN at item [%d]", i);	
+		}
+		i++;
+		p=p->next;
+	} while (p->next != jb->frames);
+}
+
+static void jb_dbgqueue(jitterbuf *jb) 
+{
+	int i=0;
+	jb_frame *p = jb->frames;
+
+	jb_dbg("queue: ");
+
+	if (!p) {
+		jb_dbg("EMPTY\n");
+		return;
+	}
+
+	do {
+		jb_dbg("[%d]=%ld ", i++, p->ts);
+		p=p->next;
+	} while (p->next != jb->frames);
+
+	jb_dbg("\n");
+}
+#endif
+
+enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts, long now) 
+{
+	long numts;
+
+	printf("jb_put(%p,%p,%ld,%ld,%ld)\n", jb, data, ms, ts, now);
+
+	numts = 0;
+	if (jb->frames)
+		numts = jb->frames->prev->ts - jb->frames->ts;
+
+	printf("numts %ld\n", numts);
+
+	if (numts >= jb->info.conf.max_jitterbuf) {
+		if (!jb->dropem) {
+			printf("Attempting to exceed Jitterbuf max %ld timeslots\n",
+				jb->info.conf.max_jitterbuf);
+			jb->dropem = 1;
+		}
+		jb->info.frames_dropped++;
+		return JB_DROP;
+	} else {
+		jb->dropem = 0;
+	}
+
+	if (type == JB_TYPE_VOICE) {
+		/* presently, I'm only adding VOICE frames to history and drift calculations; mostly because with the
+		 * IAX integrations, I'm sending retransmitted control frames with their awkward timestamps through */
+		if (history_put(jb,ts,now,ms)) {
+			jb->info.frames_dropped++;
+			return JB_DROP;
+		}
+	}
+
+	jb->info.frames_in++;
+
+	/* if put into head of queue, caller needs to reschedule */
+	if (queue_put(jb,data,type,ms,ts)) {
+		return JB_SCHED;
+	}
+	return JB_OK;
+}
+
+
+static enum jb_return_code _jb_get(jitterbuf *jb, jb_frame *frameout, long now, long interpl) 
+{
+	jb_frame *frame;
+	long diff;
+	static int dbg_cnt = 0;
+
+	printf("_jb_get\n");
+
+	/*if ((now - jb_next(jb)) > 2 * jb->info.last_voice_ms) jb_warn("SCHED: %ld", (now - jb_next(jb))); */
+	/* get jitter info */
+	history_get(jb);
+
+	if (dbg_cnt && dbg_cnt % 50 == 0) {
+		jb_dbg("\n");
+	}
+	dbg_cnt++;
+
+	/* target */
+	jb->info.target = jb->info.jitter + jb->info.min + jb->info.conf.target_extra; 
+
+	/* if a hard clamp was requested, use it */
+	if ((jb->info.conf.max_jitterbuf) && ((jb->info.target - jb->info.min) > jb->info.conf.max_jitterbuf)) {
+		printf("clamping target from %ld to %ld\n", (jb->info.target - jb->info.min), jb->info.conf.max_jitterbuf);
+		jb->info.target = jb->info.min + jb->info.conf.max_jitterbuf;
+	}
+
+	diff = jb->info.target - jb->info.current;
+
+	printf("diff = %ld lms=%ld last = %ld now = %ld, djust delay = %ld\n", diff,
+		jb->info.last_voice_ms, jb->info.last_adjustment, now);
+
+	/* let's work on non-silent case first */
+	if (!jb->info.silence_begin_ts) {
+
+	  /*
+	        if(diff > 0)
+		    printf("diff > 0\n");
+
+		if((jb->info.last_adjustment + JB_ADJUST_DELAY) < now)
+		    printf("(jb->info.last_adjustment + JB_ADJUST_DELAY) < now\n");
+
+		if((diff > queue_last(jb)  - queue_next(jb)))
+		    printf("diff > queue_last(jb)  - queue_next(jb)\n");
+	  */
+	  printf("** Non-silent case update timing info **\n");
+	  printf("diff %ld\n", diff);
+	  printf("jb->info.last_adjustment %ld\n", jb->info.last_adjustment);
+	  printf("JB_ADJUST_DELAY %ld\n", JB_ADJUST_DELAY);
+	  printf("now %ld\n", now);
+
+		/* we want to grow */
+		if ((diff > 0) && 
+			/* we haven't grown in the delay length */
+			(((jb->info.last_adjustment + JB_ADJUST_DELAY) < now) || 
+			/* we need to grow more than the "length" we have left */
+			(diff > queue_last(jb)  - queue_next(jb)) ) ) {
+
+		        
+		        
+
+			/* grow by interp frame length */
+			jb->info.current += interpl;
+			jb->info.next_voice_ts += interpl;
+			jb->info.last_voice_ms = interpl;
+			jb->info.last_adjustment = now;
+			jb->info.cnt_contig_interp++;
+			if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) {
+				jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;
+			}
+
+			printf("Non silent case\n");
+			return JB_INTERP;
+		}
+
+		printf("queue get\n");
+
+		frame = queue_get(jb, jb->info.next_voice_ts - jb->info.current);
+
+		if(!frame)
+		  printf("frame not valid\n");
+
+		/* not a voice frame; just return it. */
+		if (frame && frame->type != JB_TYPE_VOICE) {
+			if (frame->type == JB_TYPE_SILENCE) {
+				jb->info.silence_begin_ts = frame->ts;
+				jb->info.cnt_contig_interp = 0;
+			}
+
+			*frameout = *frame;
+			jb->info.frames_out++;
+			printf("Not a voice packet\n");
+			return JB_OK;
+		}
+
+
+		/* voice frame is later than expected */
+		if (frame && frame->ts + jb->info.current < jb->info.next_voice_ts) {
+			if (frame->ts + jb->info.current > jb->info.next_voice_ts - jb->info.last_voice_ms) {
+				/* either we interpolated past this frame in the last jb_get */
+				/* or the frame is still in order, but came a little too quick */ 
+				*frameout = *frame;
+				/* reset expectation for next frame */
+				jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;
+				jb->info.frames_out++;
+				decrement_losspct(jb);
+				jb->info.cnt_contig_interp = 0;
+				printf("Either we interpolated past this frame in the last jb_get" \
+				       "or the frame is still in order, but came a little too quick\n");
+				return JB_OK;
+			} else {
+				/* voice frame is late */
+				*frameout = *frame;
+				jb->info.frames_out++;
+				decrement_losspct(jb);
+				jb->info.frames_late++;
+				jb->info.frames_lost--;
+				printf("Voice frame is late\n");
+				printf("late: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
+				return JB_DROP;
+			}
+		}
+
+		/* keep track of frame sizes, to allow for variable sized-frames */
+		if (frame && frame->ms > 0) {
+			jb->info.last_voice_ms = frame->ms;
+		}
+
+		/* we want to shrink; shrink at 1 frame / 500ms */
+		/* unless we don't have a frame, then shrink 1 frame */
+		/* every 80ms (though perhaps we can shrink even faster */
+		/* in this case) */
+		if (diff < -jb->info.conf.target_extra && 
+			((!frame && jb->info.last_adjustment + 80 < now) || 
+			(jb->info.last_adjustment + 500 < now))) {
+
+			jb->info.last_adjustment = now;
+			jb->info.cnt_contig_interp = 0;
+
+			if (frame) {
+				*frameout = *frame;
+				/* shrink by frame size we're throwing out */
+				jb->info.current -= frame->ms;
+				jb->info.frames_out++;
+				decrement_losspct(jb);
+				jb->info.frames_dropped++;
+				printf("Shrink by frame size we're throwing out");
+				return JB_DROP;
+			} else {
+				/* shrink by last_voice_ms */
+				jb->info.current -= jb->info.last_voice_ms;
+				jb->info.frames_lost++;
+				increment_losspct(jb);
+				jb_dbg("S");
+				printf("No frames, shrink by last_voice_ms");
+				return JB_NOFRAME;
+			}
+		}
+
+		/* lost frame */
+		if (!frame) {
+			/* this is a bit of a hack for now, but if we're close to
+			 * target, and we find a missing frame, it makes sense to
+			 * grow, because the frame might just be a bit late;
+			 * otherwise, we presently get into a pattern where we return
+			 * INTERP for the lost frame, then it shows up next, and we
+			 * throw it away because it's late */
+	  		/* I've recently only been able to replicate this using
+			 * iaxclient talking to app_echo on asterisk.  In this case,
+			 * my outgoing packets go through asterisk's (old)
+			 * jitterbuffer, and then might get an unusual increasing delay 
+			 * there if it decides to grow?? */
+			/* Update: that might have been a different bug, that has been fixed..
+			 * But, this still seemed like a good idea, except that it ended up making a single actual
+			 * lost frame get interpolated two or more times, when there was "room" to grow, so it might
+			 * be a bit of a bad idea overall */
+			/*if (diff > -1 * jb->info.last_voice_ms) { 
+				jb->info.current += jb->info.last_voice_ms;
+				jb->info.last_adjustment = now;
+				jb_warn("g");
+				return JB_INTERP;
+			} */
+			jb->info.frames_lost++;
+			increment_losspct(jb);
+			jb->info.next_voice_ts += interpl;
+			jb->info.last_voice_ms = interpl;
+			jb->info.cnt_contig_interp++;
+			if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) {
+				jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;
+			}
+			
+			return JB_INTERP;
+		}
+
+		/* normal case; return the frame, increment stuff */
+		*frameout = *frame;
+		jb->info.next_voice_ts += frame->ms;
+		jb->info.frames_out++;
+		jb->info.cnt_contig_interp = 0;
+		decrement_losspct(jb);
+		jb_dbg("v");
+		return JB_OK;
+	} else {     
+
+	        printf("Silence???\n");
+		/* TODO: after we get the non-silent case down, we'll make the
+		 * silent case -- basically, we'll just grow and shrink faster
+		 * here, plus handle next_voice_ts a bit differently */
+
+		/* to disable silent special case altogether, just uncomment this: */
+		/* jb->info.silence_begin_ts = 0; */
+
+ 		/* shrink interpl len every 10ms during silence */
+ 		if (diff < -jb->info.conf.target_extra &&
+ 			jb->info.last_adjustment + 10 <= now) {
+ 			jb->info.current -= interpl;
+ 			jb->info.last_adjustment = now;
+ 		}
+
+		frame = queue_get(jb, now - jb->info.current);
+		if (!frame) {
+			return JB_NOFRAME;
+		} else if (frame->type != JB_TYPE_VOICE) {
+			/* normal case; in silent mode, got a non-voice frame */
+			*frameout = *frame;
+			jb->info.frames_out++;
+			return JB_OK;
+		}
+		if (frame->ts < jb->info.silence_begin_ts) {
+			/* voice frame is late */
+			*frameout = *frame;
+			jb->info.frames_out++;
+			decrement_losspct(jb);
+			jb->info.frames_late++;
+			jb->info.frames_lost--;
+			jb_dbg("l");
+			/*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
+			jb_warninfo(jb); */
+			return JB_DROP;
+		} else {
+			/* voice frame */
+			/* try setting current to target right away here */
+			jb->info.current = jb->info.target;
+			jb->info.silence_begin_ts = 0;
+			jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;
+			jb->info.last_voice_ms = frame->ms;
+			jb->info.frames_out++;
+			decrement_losspct(jb);
+			*frameout = *frame;
+			jb_dbg("V");
+			return JB_OK;
+		}
+	}
+}
+
+long jb_next(jitterbuf *jb) 
+{
+	if (jb->info.silence_begin_ts) {
+		if (jb->frames) {
+			long next = queue_next(jb);
+			history_get(jb);
+			/* shrink during silence */
+			if (jb->info.target - jb->info.current < -jb->info.conf.target_extra)
+				return jb->info.last_adjustment + 10;
+			return next + jb->info.target;
+		}
+		else 
+			return JB_LONGMAX;
+	} else {
+		return jb->info.next_voice_ts;
+	}
+}
+
+enum jb_return_code jb_get(jitterbuf *jb, jb_frame *frameout, long now, long interpl) 
+{
+	enum jb_return_code ret = _jb_get(jb, frameout, now, interpl);
+#if 0
+	static int lastts=0;
+	int thists = ((ret == JB_OK) || (ret == JB_DROP)) ? frameout->ts : 0;
+	jb_warn("jb_get(%x,%x,%ld) = %d (%d)\n", jb, frameout, now, ret, thists);
+	if (thists && thists < lastts) jb_warn("XXXX timestamp roll-back!!!\n");
+	lastts = thists;
+#endif
+	if (ret == JB_INTERP) 
+		frameout->ms = jb->info.last_voice_ms;
+	
+	return ret;
+}
+
+enum jb_return_code jb_getall(jitterbuf *jb, jb_frame *frameout) 
+{
+	jb_frame *frame;
+	frame = queue_getall(jb);
+
+	if (!frame) {
+		return JB_NOFRAME;
+	}
+
+	*frameout = *frame;
+	return JB_OK;
+}
+
+
+enum jb_return_code jb_getinfo(jitterbuf *jb, jb_info *stats) 
+{
+
+	history_get(jb);
+
+	*stats = jb->info;
+
+	return JB_OK;
+}
+
+enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf) 
+{
+	/* take selected settings from the struct */
+
+	jb->info.conf.max_jitterbuf = conf->max_jitterbuf;
+ 	jb->info.conf.resync_threshold = conf->resync_threshold;
+	jb->info.conf.max_contig_interp = conf->max_contig_interp;
+
+	/* -1 indicates use of the default JB_TARGET_EXTRA value */
+	jb->info.conf.target_extra = ( conf->target_extra == -1 )
+		? JB_TARGET_EXTRA
+		: conf->target_extra
+		;
+		
+	/* update these to match new target_extra setting */
+	jb->info.current = jb->info.conf.target_extra;
+	jb->info.target = jb->info.conf.target_extra;
+
+	return JB_OK;
+}
+
+
diff --git a/sflphone-common/src/audio/jitterbuf.h b/sflphone-common/src/audio/jitterbuf.h
new file mode 100755
index 0000000000000000000000000000000000000000..8de8bf0885d87f476550bc27c5201065d677b51d
--- /dev/null
+++ b/sflphone-common/src/audio/jitterbuf.h
@@ -0,0 +1,177 @@
+/*
+ * jitterbuf: an application-independent jitterbuffer
+ *
+ * Copyrights:
+ * Copyright (C) 2004-2005, Horizon Wimba, Inc.
+ *
+ * Contributors:
+ * Steve Kann <stevek@stevek.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU Lesser (Library) General Public License
+ *
+ * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
+ */
+
+/*! \file
+ * \brief 
+ * jitterbuf: an application-independent jitterbuffer
+ * \ref jitterbuf.c
+ */
+
+
+#ifndef _JITTERBUF_H_
+#define _JITTERBUF_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! \name configuration constants */
+/*@{ */
+	/*! Number of historical timestamps to use in calculating jitter and drift */
+#define JB_HISTORY_SZ		500	
+	/*! what percentage of timestamps should we drop from the history when we examine it;
+	 * this might eventually be something made configurable */
+#define JB_HISTORY_DROPPCT	3
+	/*! the maximum droppct we can handle (say it was configurable). */
+#define JB_HISTORY_DROPPCT_MAX	4
+	/*! the size of the buffer we use to keep the top and botton timestamps for dropping */
+#define JB_HISTORY_MAXBUF_SZ	JB_HISTORY_SZ * JB_HISTORY_DROPPCT_MAX / 100 
+	/*! amount of additional jitterbuffer adjustment  */
+#define JB_TARGET_EXTRA 40
+	/*! ms between growing and shrinking; may not be honored if jitterbuffer runs out of space */
+#define JB_ADJUST_DELAY 40
+/*@} */
+
+enum jb_return_code {
+	/* return codes */
+	JB_OK,            /* 0 */
+	JB_EMPTY,         /* 1 */
+	JB_NOFRAME,       /* 2 */
+	JB_INTERP,        /* 3 */
+	JB_DROP,          /* 4 */
+	JB_SCHED          /* 5 */
+};
+
+enum jb_frame_type {
+	/* frame types */
+	JB_TYPE_CONTROL,  /*!< 0            */
+	JB_TYPE_VOICE,    /*!< 1            */
+	JB_TYPE_VIDEO,    /*!< 2 - reserved */
+	JB_TYPE_SILENCE   /*!< 3            */
+};
+
+typedef struct jb_conf {
+	/* settings */
+	long max_jitterbuf;	/*!< defines a hard clamp to use in setting the jitter buffer delay */
+ 	long resync_threshold;  /*!< the jb will resync when delay increases to (2 * jitter) + this param */
+	long max_contig_interp; /*!< the max interp frames to return in a row */
+	long target_extra ;      /*!< amount of additional jitterbuffer adjustment, overrides JB_TARGET_EXTRA */
+} jb_conf;
+
+typedef struct jb_info {
+	jb_conf conf;
+
+	/* statistics */
+	long frames_in;  	/*!< number of frames input to the jitterbuffer.*/
+	long frames_out;  	/*!< number of frames output from the jitterbuffer.*/
+	long frames_late; 	/*!< number of frames which were too late, and dropped.*/
+	long frames_lost; 	/*!< number of missing frames.*/
+	long frames_dropped; 	/*!< number of frames dropped (shrinkage) */
+	long frames_ooo; 	/*!< number of frames received out-of-order */
+	long frames_cur; 	/*!< number of frames presently in jb, awaiting delivery.*/
+	long jitter; 		/*!< jitter measured within current history interval*/
+	long min;		/*!< minimum lateness within current history interval */
+	long current; 		/*!< the present jitterbuffer adjustment */
+	long target; 		/*!< the target jitterbuffer adjustment */
+	long losspct; 		/*!< recent lost frame percentage (* 1000) */
+	long next_voice_ts;	/*!< the ts of the next frame to be read from the jb - in receiver's time */
+	long last_voice_ms;	/*!< the duration of the last voice frame */
+	long silence_begin_ts;	/*!< the time of the last CNG frame, when in silence */
+	long last_adjustment;   /*!< the time of the last adjustment */
+ 	long last_delay;        /*!< the last now added to history */
+ 	long cnt_delay_discont;	/*!< the count of discontinuous delays */
+ 	long resync_offset;     /*!< the amount to offset ts to support resyncs */
+	long cnt_contig_interp; /*!< the number of contiguous interp frames returned */
+} jb_info;
+
+typedef struct jb_frame {
+	void *data;               /* the frame data */
+	long ts;                  /* the relative delivery time expected */
+	long ms;                  /* the time covered by this frame, in sec/8000 */
+	enum jb_frame_type type;  /* the type of frame */
+	struct jb_frame *next, *prev;
+} jb_frame;
+
+typedef struct jitterbuf {
+	jb_info info;
+
+	/* history */
+	long history[JB_HISTORY_SZ];   		/*!< history */
+	int  hist_ptr;				/*!< points to index in history for next entry */
+	long hist_maxbuf[JB_HISTORY_MAXBUF_SZ];	/*!< a sorted buffer of the max delays (highest first) */
+	long hist_minbuf[JB_HISTORY_MAXBUF_SZ];	/*!< a sorted buffer of the min delays (lowest first) */
+	int  hist_maxbuf_valid;			/*!< are the "maxbuf"/minbuf valid? */
+	unsigned int dropem:1;                  /*!< flag to indicate dropping frames (overload) */
+
+	jb_frame *frames; 		/*!< queued frames */
+	jb_frame *free; 		/*!< free frames (avoid malloc?) */
+} jitterbuf;
+
+
+/*! \brief new jitterbuf */
+jitterbuf *		jb_new(void);
+
+/*! \brief destroy jitterbuf */
+void			jb_destroy(jitterbuf *jb);
+
+/*! \brief reset jitterbuf
+ * \note The jitterbuffer should be empty before you call this, otherwise
+ * you will leak queued frames, and some internal structures */
+void			jb_reset(jitterbuf *jb);
+
+/*!\brief  queue a frame 
+ * 
+ * data=frame data, timings (in ms): ms=length of frame (for voice), ts=ts (sender's time) 
+ * now=now (in receiver's time) return value is one of 
+ * JB_OK: Frame added. Last call to jb_next() still valid
+ * JB_DROP: Drop this frame immediately
+ * JB_SCHED: Frame added. Call jb_next() to get a new time for the next frame
+ */
+enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts, long now);
+
+/*! \brief get a frame for time now (receiver's time)  return value is one of
+ * JB_OK:  You've got frame!
+ * JB_DROP: Here's an audio frame you should just drop.  Ask me again for this time..
+ * JB_NOFRAME: There's no frame scheduled for this time.
+ * JB_INTERP: Please interpolate an interpl-length frame for this time (either we need to grow, or there was a lost frame) 
+ * JB_EMPTY: The jb is empty.
+ */
+enum jb_return_code jb_get(jitterbuf *jb, jb_frame *frame, long now, long interpl);
+
+/*! \brief unconditionally get frames from jitterbuf until empty */
+enum jb_return_code jb_getall(jitterbuf *jb, jb_frame *frameout);
+
+/*! \brief when is the next frame due out, in receiver's time (0=EMPTY) 
+ * This value may change as frames are added (esp non-audio frames) */
+long	jb_next(jitterbuf *jb);
+
+/*! \brief get jitterbuf info: only "statistics" may be valid */
+enum jb_return_code jb_getinfo(jitterbuf *jb, jb_info *stats);
+
+/* some diagnostics */
+void jb_dbginfo(jitterbuf *jb);
+
+/*! \brief set jitterbuf conf */
+enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf);
+
+typedef void __attribute__((format(printf, 1, 2))) (*jb_output_function_t)(const char *fmt, ...);
+void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif