Index: pjproject/trunk/pjmedia/src/pjmedia/jbuf.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 129)
+++ pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 169)
@@ -18,5 +18,5 @@
  */
 #include <pjmedia/jbuf.h>
-#include <pj/log.h>
+#include <pjmedia/errno.h>
 #include <pj/pool.h>
 #include <pj/assert.h>
@@ -24,399 +24,380 @@
 
 
-/*
- * At the current state, this is basicly an ugly jitter buffer.
- * It worked before by observing level, bit it doesn't work.
- * Then I used the size, which makes the level obsolete.
- * That's why it's ugly!
- */
-
-#define MAX_SEQ_RANGE	1000	/* Range in which sequence is considered still within session */
-#define UPDATE_DURATION	20	/* Number of frames retrieved before jitter is updated */
-
-#define THIS_FILE   "jbuf"
-
-/* Individual frame in the frame list. */ 
-struct pj_jbframe
-{
-    pj_uint32_t  extseq;
-    void	*buf;
+
+struct jb_framelist
+{
+    char	*flist_buffer;
+    int		*flist_frame_type;
+    unsigned	 flist_frame_size;
+    unsigned	 flist_max_count;
+    unsigned	 flist_empty;
+    unsigned	 flist_head;
+    unsigned	 flist_tail;
+    unsigned	 flist_origin;
 };
 
 
-/* Jitter buffer state. */ 
-typedef enum jb_state_t
-{
-    JB_PREFETCH,
-    JB_NORMAL,
-} jb_state_t;
-
-
-/* Jitter buffer last operation. */ 
-typedef enum jb_op_t
-{
-    JB_PUT,
-    JB_GET,
-} jb_op_t;
-
-
-/* Short name for convenience. */ 
-typedef struct pj_jitter_buffer JB;
-
-
-/* Initialize framelist. */ 
-static pj_status_t
-pj_framelist_init( pj_jbframelist *lst, pj_pool_t *pool, unsigned maxcount )
-{
-    PJ_LOG(5, (THIS_FILE, "..pj_frame_list_init [lst=%p], maxcount=%d", lst, maxcount));
-
-    pj_memset(lst, 0, sizeof(*lst));
-    lst->maxcount = maxcount;
-    lst->frames = pj_pool_calloc( pool, maxcount, sizeof(*lst->frames) );
-    if (lst->frames == NULL) {
-	PJ_LOG(1,(THIS_FILE, "Unable to allocate frame list!"));
-	return -1;
-    }
-    return 0;    
-}
-
-/* Reset framelist. */
-static void 
-pj_framelist_reset( pj_jbframelist *lst )
-{
-    PJ_LOG(6, (THIS_FILE, "..pj_frame_list_reset [lst=%p]", lst));
-
-    lst->count = 0;
-    lst->head = 0;
-    lst->frames[0].extseq = 0;
-}
-
-/* Put a buffer with the specified sequence into the ordered list. */ 
-static int 
-pj_framelist_put( pj_jbframelist *lst, pj_uint32_t extseq, void *buf )
-{
-    unsigned pos = (unsigned)-1;
-    pj_uint32_t startseq = lst->frames[lst->head].extseq;
-
-    if (lst->count == 0) {
-	/* Empty list. Initialize frame list. */
-	PJ_LOG(6, (THIS_FILE, "    ..pj_frame_list_put [lst=%p], empty, seq=%u@pos=%d", 
-		   lst, extseq, lst->head));
-
-	lst->head = 0;
-	lst->count = 1;
-	lst->frames[0].buf = buf;
-	lst->frames[0].extseq = extseq;
-	return 0;
+typedef struct jb_framelist jb_framelist;
+
+struct pjmedia_jbuf
+{
+    jb_framelist    jb_framelist;
+    pj_size_t	    jb_frame_size;	  // frame size	
+    pj_size_t	    jb_max_count;	  // max frames in the jitter framelist->flist_buffer
+
+    int		    jb_level;		  // delay between source & destination
+					  // (calculated according of the number of get/put operations)
+    int		    jb_last_level;	  // last delay	
+    int		    jb_last_jitter;	  // last jitter calculated
+    int		    jb_max_hist_jitter;   // max jitter	during the last	jitter calculations
+    int		    jb_stable_hist;	  // num of times the delay has	been lower then	the prefetch num
+    int		    jb_last_op;		  // last operation executed on	the framelist->flist_buffer (put/get)
+    int		    jb_last_seq_no;	  // seq no. of	the last frame inserted	to the framelist->flist_buffer
+    int		    jb_prefetch;	  // no. of frame to insert before removing some
+					  // (at the beginning of the framelist->flist_buffer operation)
+    int		    jb_prefetch_cnt;	  // prefetch counter
+    int		    jb_status;		  // status is 'init' until the	first 'put' operation
+
+
+};
+
+
+#define JB_STATUS_INITIALIZING	0
+#define JB_STATUS_PROCESSING	1
+
+#define	PJ_ABS(x)	((x > 0) ? (x) : -(x))
+#define	PJ_MAX(x, y)	((x > y) ? (x) : (y))
+#define	PJ_MIN(x, y)	((x < y) ? (x) : (y))
+
+
+
+static pj_status_t jb_framelist_init( pj_pool_t *pool,
+				      jb_framelist *framelist,
+				      unsigned frame_size,
+				      unsigned max_count) 
+{
+    PJ_ASSERT_RETURN(pool && framelist, PJ_EINVAL);
+
+    pj_memset(framelist, 0, sizeof(jb_framelist));
+
+    framelist->flist_frame_size = frame_size;
+    framelist->flist_max_count = max_count;
+    framelist->flist_buffer = pj_pool_zalloc(pool,
+					     framelist->flist_frame_size * 
+					     framelist->flist_max_count);
+
+    framelist->flist_frame_type = pj_pool_zalloc(pool, 
+						 sizeof(framelist->flist_frame_type[0]) * 
+						 framelist->flist_max_count);
+
+    framelist->flist_empty = 1;
+    framelist->flist_head = framelist->flist_tail = framelist->flist_origin = 0;
+
+    return PJ_SUCCESS;
+
+}
+
+static pj_status_t jb_framelist_destroy(jb_framelist *framelist) 
+{
+    PJ_UNUSED_ARG(framelist);
+    return PJ_SUCCESS;
+}
+
+
+static unsigned jb_framelist_size(jb_framelist *framelist) 
+{
+    if (framelist->flist_tail == framelist->flist_head) {
+	return framelist->flist_empty ? 0 : framelist->flist_max_count;
+    } else {
+	return (framelist->flist_tail - framelist->flist_head + 
+		framelist->flist_max_count) % framelist->flist_max_count;
+    }
+}
+
+
+static pj_bool_t jb_framelist_get(jb_framelist *framelist,
+				  void *frame,
+				  pjmedia_jb_frame_type *p_type) 
+{
+    if (!framelist->flist_empty) {
+	pj_memcpy(frame, 
+		  framelist->flist_buffer + framelist->flist_head * framelist->flist_frame_size,
+		  framelist->flist_frame_size);
+	*p_type = (pjmedia_jb_frame_type) framelist->flist_frame_type[framelist->flist_head];
+
+	pj_memset(framelist->flist_buffer + framelist->flist_head * framelist->flist_frame_size,
+		  0, framelist->flist_frame_size);
+	framelist->flist_frame_type[framelist->flist_head] = 0;
+
+	framelist->flist_origin++;
+	framelist->flist_head = ++framelist->flist_head % framelist->flist_max_count;
+	if (framelist->flist_head == framelist->flist_tail) 
+	    framelist->flist_empty = PJ_TRUE;
 	
-    } else if (extseq < startseq) {
-	/* The sequence number is lower than our oldest packet. This can mean
-	   two things:
-	    - old packet has been receieved, or
-	    - the sequence number has wrapped around.
-	 */  
-	if (startseq + lst->maxcount <= extseq) {
-	    /* The sequence number has wrapped around, but it is beyond
-	       the capacity of the list (i.e. too soon).
-	     */
-	    PJ_LOG(5, (THIS_FILE, "    ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d", 
-		       lst, extseq, startseq));
-	    return PJ_JB_STATUS_TOO_SOON;
-
-	} else if (startseq-extseq > lst->maxcount && startseq+lst->maxcount > extseq) {
-	    /* The sequence number has wrapped around, and it is still inside
-	       the 'window' of the framelist.
-	     */
-	    pos = extseq - startseq;
+	return PJ_TRUE;
+
+    } else {
+	pj_memset(frame, 0, framelist->flist_frame_size);
+	return PJ_FALSE;
+    }
+}
+
+
+static void jb_framelist_remove_head( jb_framelist *framelist,
+				      unsigned count) 
+{
+    unsigned cur_size;
+
+    cur_size = jb_framelist_size(framelist);
+    if (count > cur_size) 
+	count = cur_size;
+
+    if (count) {
+	// may be done in two steps if overlapping
+	unsigned step1,step2;
+	unsigned tmp = framelist->flist_head+count;
+
+	if (tmp > framelist->flist_max_count) {
+	    step1 = framelist->flist_max_count - framelist->flist_head;
+	    step2 = count-step1;
 	} else {
-	    /* The new frame is too old. */
-	    PJ_LOG(5, (THIS_FILE, "    ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d", 
-		       lst, extseq, startseq));
-	    return PJ_JB_STATUS_TOO_OLD;
+	    step1 = count;
+	    step2 = 0;
 	}
-	
-    } else if (extseq > startseq + lst->maxcount) {
-	/* Two possibilities here. Either:
-	    - packet is really too soon, or
-	    - sequence number of startseq has just wrapped around, and old packet
-	      which hasn't wrapped is received.
-	 */
-	if (extseq < MAX_SEQ_RANGE /*approx 20 seconds with 50 fps*/) {
-	    PJ_LOG(5, (THIS_FILE, "    ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d", 
-		       lst, extseq, startseq));
-	    return PJ_JB_STATUS_TOO_SOON;
-	} else {
-	    PJ_LOG(5, (THIS_FILE, "    ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d", 
-		       lst, extseq, startseq));
-	    return PJ_JB_STATUS_TOO_OLD;
+
+	pj_memset(framelist->flist_buffer + framelist->flist_head * framelist->flist_frame_size,
+	          0,
+	          step1*framelist->flist_frame_size);
+	pj_memset(framelist->flist_frame_type+framelist->flist_head,
+		  0,
+		  step1*sizeof(framelist->flist_frame_type[0]));
+
+	if (step2) {
+	    pj_memset(framelist->flist_buffer,
+		      0,
+		      step2*framelist->flist_frame_size);
+	    pj_memset(framelist->flist_frame_type,
+		      0,
+		      step2*sizeof(framelist->flist_frame_type[0]));
 	}
-    } 
-
-    /* The new frame is within the framelist capacity.
-       Calculate position where to put it in the list.
-     */
-    if (pos == (unsigned)-1)
-	pos = ((extseq - startseq) + lst->head) % lst->maxcount;
-
-    pj_assert(pos < lst->maxcount);
-    
-    /* Update count only if we're not overwriting existing frame. */
-    if (lst->frames[pos].buf == NULL)
-        ++lst->count;
-
-    lst->frames[pos].buf = buf;
-    lst->frames[pos].extseq = extseq;
-
-    PJ_LOG(6, (THIS_FILE, "    ..pj_frame_list_put [lst=%p] seq=%u, startseq=%d, head=%d, pos=%d", 
-	       lst, extseq, startseq, lst->head, pos));
-    return 0;
-}
-
-/* Get the first element of the list. */ 
-static int 
-pj_framelist_get( pj_jbframelist *lst, pj_uint32_t *extseq, void **buf )
-{
-    if (lst->count == 0) {
-	/* Empty. */
-	*buf = NULL;
-	*extseq = 0;
-	PJ_LOG(6, (THIS_FILE, "    ..pj_frame_list_get [lst=%p], empty!", lst));
-	return -1;
-	
-    } else {
-	*buf = lst->frames[lst->head].buf;
-	*extseq = lst->frames[lst->head].extseq;
-	lst->frames[lst->head].buf = NULL;
-
-	PJ_LOG(6, (THIS_FILE, "    ..pj_frame_list_get [lst=%p] seq=%u, head=%d", 
-		   lst, *extseq, lst->head));
-
-	lst->head = (lst->head + 1) % lst->maxcount;
-	--lst->count;
-	return 0;
-    }
-}
-
-
-/*****************************************************************************
- * Reset jitter buffer. 
- ****************************************************************************
-*/
-PJ_DEF(void) pj_jb_reset(JB *jb)
-{
-    PJ_LOG(6, (THIS_FILE, "pj_jb_reset [jb=%p]", jb));
-
-    jb->level = jb->max_level = 1;
-    jb->prefetch = jb->min;
-    jb->get_cnt = 0;
-    jb->lastseq = 0;
-    jb->state = JB_PREFETCH;
-    jb->upd_count = 0;
-    jb->last_op = -1;
-    pj_framelist_reset( &jb->lst );
-}
-
-
-/*****************************************************************************
- * Create jitter buffer.
- *****************************************************************************
- */ 
-PJ_DEF(pj_status_t) pj_jb_init( pj_jitter_buffer *jb, pj_pool_t *pool, 
-			        unsigned min, unsigned max, unsigned maxcount)
-{
+
+	// update pointers
+	framelist->flist_origin += count;
+	framelist->flist_head = (framelist->flist_head+count) % framelist->flist_max_count;
+	if (framelist->flist_head == framelist->flist_tail) 
+	    framelist->flist_empty = PJ_TRUE;
+    }
+}
+
+
+static pj_bool_t jb_framelist_put_at(jb_framelist *framelist,
+				     unsigned index,
+				     const void *frame,
+				     unsigned frame_size) 
+{
+    unsigned where;
+
+    // too late
+    if (index < framelist->flist_origin) 
+	return PJ_FALSE;
+
+    // too soon
+    if ((index > (framelist->flist_origin + framelist->flist_max_count - 1)) && !framelist->flist_empty) 
+	return PJ_FALSE;
+
+    assert(frame_size <= framelist->flist_frame_size);
+
+    if (!framelist->flist_empty) {
+	unsigned cur_size;
+
+	where = (index - framelist->flist_origin + framelist->flist_head) % framelist->flist_max_count;
+
+	// update framelist->flist_tail pointer
+	cur_size = jb_framelist_size(framelist);
+	if (index >= framelist->flist_origin + cur_size) {
+	    unsigned diff = (index - (framelist->flist_origin + cur_size));
+	    framelist->flist_tail = (framelist->flist_tail + diff + 1) % framelist->flist_max_count;
+	}
+    } else {
+	where = framelist->flist_tail;
+	framelist->flist_origin = index;
+	framelist->flist_tail = (++framelist->flist_tail % framelist->flist_max_count);
+	framelist->flist_empty = PJ_FALSE;
+    }
+
+    pj_memcpy(framelist->flist_buffer + where * framelist->flist_frame_size, 
+	      frame, frame_size);
+
+    framelist->flist_frame_type[where] = PJMEDIA_JB_NORMAL_FRAME;
+
+    return PJ_TRUE;
+}
+
+
+
+enum pjmedia_jb_op
+{
+    JB_OP_INIT  = -1,
+    JB_OP_PUT   = 1,
+    JB_OP_GET   = 2
+};
+
+
+PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, 
+					int frame_size, 
+					int initial_prefetch, 
+					int max_count,
+					pjmedia_jbuf **p_jb)
+{
+    pjmedia_jbuf *jb;
     pj_status_t status;
 
-    if (maxcount <= max) {
-	maxcount = max * 5 / 4;
-	PJ_LOG(3,(THIS_FILE, "Jitter buffer maximum count was adjusted."));
-    }
-
-    jb->min = min;
-    jb->max = max;
-
-    status = pj_framelist_init( &jb->lst, pool, maxcount );
+    jb = pj_pool_zalloc(pool, sizeof(pjmedia_jbuf));
+
+    status = jb_framelist_init(pool, &jb->jb_framelist, frame_size, max_count);
     if (status != PJ_SUCCESS)
 	return status;
 
-    pj_jb_reset(jb);
-
-    PJ_LOG(4, (THIS_FILE, "pj_jb_init success [jb=%p], min=%d, max=%d, maxcount=%d", 
-			  jb, min, max, maxcount));
+    jb->jb_frame_size	 = frame_size;
+    jb->jb_last_seq_no	 = -1;
+    jb->jb_level	 = 0;
+    jb->jb_last_level	 = 0;
+    jb->jb_last_jitter	 = 0;
+    jb->jb_last_op	 = JB_OP_INIT;
+    jb->jb_prefetch	 = PJ_MIN(initial_prefetch, max_count*4/5);
+    jb->jb_prefetch_cnt	 = 0;
+    jb->jb_stable_hist	 = 0;
+    jb->jb_status	 = JB_STATUS_INITIALIZING;
+    jb->jb_max_hist_jitter = 0;
+    jb->jb_max_count	 = max_count;
+
+    *p_jb = jb;
     return PJ_SUCCESS;
 }
 
 
-/*****************************************************************************
- * Put a packet to the jitter buffer.
- *****************************************************************************
- */ 
-PJ_DEF(pj_status_t) pj_jb_put( JB *jb, pj_uint32_t extseq, void *buf )
-{
-    unsigned distance;
-    int status;
+PJ_DEF(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb)
+{
+    return jb_framelist_destroy(&jb->jb_framelist);
+}
+
+
+static void jbuf_calculate_jitter(pjmedia_jbuf *jb)
+{
+    enum { STABLE_HISTORY_LIMIT = (500/20) };
+
+    jb->jb_last_jitter = PJ_ABS(jb->jb_level-jb->jb_last_level);
+    jb->jb_last_level = jb->jb_level;
+    jb->jb_max_hist_jitter = PJ_MAX(jb->jb_max_hist_jitter,jb->jb_last_jitter);
     
-    PJ_LOG(6, (THIS_FILE, "==> pj_jb_put [jb=%p], seq=%u, buf=%p", jb, extseq, buf));
-
-    if (jb->lastseq == 0)
-	jb->lastseq = extseq - 1;
-
-    /* Calculate distance between this packet and last received packet
-       to detect long jump (indicating probably remote has just been
-       restarted.
-     */
-    distance = (extseq > jb->lastseq) ? extseq - jb->lastseq : jb->lastseq - extseq;
-    if (distance > MAX_SEQ_RANGE) {
-	/* Distance is out of range, reset jitter while maintaining current jitter
-	   level.
-	 */
-	int old_level = jb->level;
-	int old_prefetch = jb->prefetch;
-
-	PJ_LOG(4, (THIS_FILE, "    ..[jb=%p] distance out of range, resetting", jb));
-
-	pj_jb_reset(jb);
-	jb->level = old_level;
-	jb->prefetch = old_prefetch;
-	distance = 1;
-	jb->lastseq = extseq - 1;
-    }
-    
-    jb->lastseq = extseq;
-
-    status = pj_framelist_put( &jb->lst, extseq, buf );
-    if (status == PJ_JB_STATUS_TOO_OLD)
-	return -1;
-
-    if (status == PJ_JB_STATUS_TOO_SOON) {
-	/* TODO: discard old packets.. */
-	/* No, don't do it without putting a way to inform application so that
-	   it can free the memory */
-    }
-
-
-    if (jb->last_op != JB_PUT) {
-	if (jb->state != JB_PREFETCH)
-	    jb->level--;
-    } else {
-	jb->level++;
-    }
-
-    if ((int)jb->lst.count > jb->max_level)
-	jb->max_level++;
-
-    jb->last_op = JB_PUT;
-    return 0;
-}
-
-
-/*
- * Update jitter buffer algorithm.
- */
-static void jb_update(JB *jb, int apply, int log_info)
-{
-    unsigned abs_level = jb->max_level > 0 ? jb->max_level : -jb->max_level;
-    unsigned new_prefetch;
-
-    /* Update prefetch count */
-    if (abs_level > jb->prefetch)
-	new_prefetch = (jb->prefetch + abs_level*9 + 1) / 10;
-    else {
-	new_prefetch = (jb->prefetch*4 + abs_level) / 5;
-	pj_assert(new_prefetch <= jb->prefetch);
-    }
-
-    if (log_info) {
-	PJ_LOG(5, (THIS_FILE, "    ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d", 
-			      jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
-    } else {
-	PJ_LOG(6, (THIS_FILE, "    ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d", 
-			      jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
-    }
-
-    if (new_prefetch < jb->min) new_prefetch = jb->min;
-    if (new_prefetch > jb->max) new_prefetch = jb->max;
-
-    /* If jitter buffer is empty, set state to JB_PREFETCH, taking care of the
-       new prefetch setting.
-     */
-    if (jb->lst.count == 0) {
-	jb->state = JB_PREFETCH;
-	jb->get_cnt = 0;
-    } else {
-	/* Check if delay is too long, which in this case probably better to
-	   discard some frames..
-	 */
-	/* No, don't do it without putting a way to inform application so that
-	   it can free the memory */
-    }
-
-
-    if (apply) {
-	jb->prefetch = new_prefetch;
-	if (jb->max_level > 0)
-	    jb->max_level--;
-    } else {
-	jb->level = new_prefetch;
-    }
-}
-
-
-/*****************************************************************************
- * Get the oldest frame from jitter buffer.
- *****************************************************************************
- */ 
-PJ_DEF(pj_status_t) pj_jb_get( JB *jb, pj_uint32_t *extseq, void **buf )
-{
-    pj_status_t status;
-    
-    PJ_LOG(6, (THIS_FILE, "<== pj_jb_get [jb=%p]", jb));
-
-    /*
-     * Check whether we're ready to give frame. When we're in JB_PREFETCH state,
-     * only give frames only when:
-     *	- the buffer has enough frames in it (jb->list.count > jb->prefetch), OR
-     *	- after 'prefetch' attempts, there's still no frame, which in this
-     *	  case PJ_JB_STATUS_FRAME_NULL will be returned by the next check.
-     */
-    if (jb->state == JB_PREFETCH && jb->lst.count <= jb->prefetch && jb->get_cnt < jb->prefetch) {
-	jb->get_cnt++;   
-	jb->last_op = JB_GET;
-	PJ_LOG(5, (THIS_FILE, "    ..[jb=%p] bufferring...", jb));
-	return PJ_JB_STATUS_FRAME_NULL;
-    }
-
-    /* Attempt to get one frame from the list. */
-    status = pj_framelist_get( &jb->lst, extseq, buf );
-    if (status != 0) {
-	PJ_LOG(6, (THIS_FILE, "    ..[jb=%p] no packet!", jb));
-	status = jb->lst.count ? PJ_JB_STATUS_FRAME_MISSING : PJ_JB_STATUS_FRAME_NULL;
-	jb_update(jb, 1, 0);
-	return status;
-    }
-
-    /* Force state to NORMAL */
-    jb->state = JB_NORMAL;
-
-    /* Increase level only when last operation is GET.
-     * This is to prevent level from increasing during silence period, which
-     * no packets is receieved.
-     */
-    if (jb->last_op != JB_GET) {
-	int apply;
-
-	//jb->level++;
-	jb->last_op = JB_GET;
-
-	apply = (++jb->upd_count > UPDATE_DURATION);
-	if (apply)
-	    jb->upd_count = 0;
-
-	jb_update(jb, apply, apply);
-    }
-
-    PJ_LOG(6, (THIS_FILE, "    ..[jb=%p] seq=%u, level=%d, prefetch=%d, size=%u, delay=%d", 
-			  jb, *extseq, jb->level, jb->prefetch, jb->lst.count,
-			  jb->lastseq - *extseq));
-    return 0;
-}
-
-
+    if (jb->jb_last_jitter< jb->jb_prefetch) {
+	jb->jb_stable_hist += jb->jb_last_jitter;
+	if (jb->jb_stable_hist > STABLE_HISTORY_LIMIT) {
+	    int seq_diff = (jb->jb_prefetch - jb->jb_max_hist_jitter)/3;
+	    if (seq_diff<1) seq_diff = 1;
+
+	    jb->jb_prefetch -= seq_diff;
+	    if (jb->jb_prefetch < 1) jb->jb_prefetch = 1;
+
+	    jb->jb_stable_hist = 0;
+	    jb->jb_max_hist_jitter = 0;
+	}
+    } else {
+	jb->jb_prefetch = PJ_MIN(jb->jb_last_jitter,(int)(jb->jb_max_count*4/5));
+	jb->jb_stable_hist = 0;
+	jb->jb_max_hist_jitter = 0;
+    }
+}
+
+static void jbuf_update(pjmedia_jbuf *jb, int oper)
+{
+    if(jb->jb_last_op != oper) {
+	jbuf_calculate_jitter(jb);
+	jb->jb_last_op = oper;
+    }
+}
+
+PJ_DEF(pj_status_t) pjmedia_jbuf_put_frame(pjmedia_jbuf *jb, 
+					   const void *frame, 
+					   pj_size_t frame_size, 
+					   int frame_seq)
+{
+    pj_size_t min_frame_size;
+    int seq_diff;
+
+    if (jb->jb_last_seq_no == -1)	{
+	jb->jb_last_seq_no = frame_seq - 1;
+    }
+
+    seq_diff = frame_seq - jb->jb_last_seq_no;
+    jb->jb_last_seq_no = PJ_MAX(jb->jb_last_seq_no, frame_seq);
+    if (seq_diff > 0) jb->jb_level += seq_diff;
+
+    if(jb->jb_status ==	JB_STATUS_INITIALIZING) {
+	jb->jb_status = JB_STATUS_PROCESSING;
+	jb->jb_level = 0;
+	jb->jb_last_level = 0;
+	jb->jb_last_jitter = 0;
+    } else {
+	jbuf_update(jb, JB_OP_PUT);
+    }
+
+    min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size);
+    if (seq_diff > 0) {
+
+	while (!jb_framelist_put_at(&jb->jb_framelist,frame_seq,frame,min_frame_size)) {
+	    jb_framelist_remove_head(&jb->jb_framelist,PJ_MAX(jb->jb_max_count/4,1));
+	}
+
+	if (jb->jb_prefetch_cnt < jb->jb_prefetch)	
+	    jb->jb_prefetch_cnt += seq_diff;
+
+    }
+    else
+    {
+	jb_framelist_put_at(&jb->jb_framelist,frame_seq,frame,min_frame_size);
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb, 
+					    void *frame, 
+					    char *p_frame_type)
+{
+    pjmedia_jb_frame_type ftype;
+
+    jb->jb_level--;
+
+    jbuf_update(jb, JB_OP_GET);
+
+    if (jb_framelist_size(&jb->jb_framelist) == 0) {
+	jb->jb_prefetch_cnt = 0;
+    }
+
+    if ((jb->jb_prefetch_cnt < jb->jb_prefetch) || !jb_framelist_get(&jb->jb_framelist,frame,&ftype)) {
+	pj_memset(frame, 0, jb->jb_frame_size);
+	*p_frame_type = PJMEDIA_JB_ZERO_FRAME;
+	return PJ_SUCCESS;
+    }
+
+    if (ftype == PJMEDIA_JB_NORMAL_FRAME) {
+	*p_frame_type	= PJMEDIA_JB_NORMAL_FRAME;
+    } else {
+	*p_frame_type	= PJMEDIA_JB_MISSING_FRAME;
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(unsigned) pjmedia_jbuf_get_prefetch_size(pjmedia_jbuf *jb)
+{
+    return jb->jb_prefetch;
+}
+
+PJ_DEF(unsigned) pjmedia_jbuf_get_current_size(pjmedia_jbuf *jb)
+{
+    return jb_framelist_size(&jb->jb_framelist);
+}
+
+
