Index: /pjproject/branches/projects/jbuf2/pjmedia/include/pjmedia/jbuf2.h
===================================================================
--- /pjproject/branches/projects/jbuf2/pjmedia/include/pjmedia/jbuf2.h	(revision 1777)
+++ /pjproject/branches/projects/jbuf2/pjmedia/include/pjmedia/jbuf2.h	(revision 1778)
@@ -56,6 +56,6 @@
 {
     PJMEDIA_JB_PH_IDLE	    = 0,	/**< No activity in PUT/GET or both */
-    PJMEDIA_JB_PH_LEARNING  = 1,	/**< Normal encoded frame */
-    PJMEDIA_JB_PH_RUNNING   = 2,	/**< Normal PCM frame */
+    PJMEDIA_JB_PH_LEARNING  = 1,	/**< Learning */
+    PJMEDIA_JB_PH_RUNNING   = 2,	/**< Running */
 } pjmedia_jb2_phase;
 
@@ -137,4 +137,16 @@
 
 /**
+ * This structure describes jitter buffer callback
+ */
+typedef struct pjmedia_jb2_cb
+{
+    pj_status_t (*decode) (pjmedia_jb2_frame *frame, void *userdata);
+    pj_status_t (*plc)    (pjmedia_jb2_frame *frame, void *userdata);
+    pj_status_t (*cng)    (pjmedia_jb2_frame *frame, void *userdata);
+    void		  *user_data;
+} pjmedia_jb2_cb;
+
+
+/**
  * This structure describes jitter buffer current status.
  */
@@ -162,11 +174,4 @@
 
 } pjmedia_jb2_setting;
-
-typedef struct pjmedia_jb2_cb
-{
-    pj_status_t (*decode) (pjmedia_jb2_frame *frame);
-    pj_status_t (*plc)    (pjmedia_jb2_frame *frame);
-    pj_status_t (*cng)    (pjmedia_jb2_frame *frame);
-} pjmedia_jb2_cb;
 
 
Index: /pjproject/branches/projects/jbuf2/pjmedia/src/pjmedia/jbuf2.c
===================================================================
--- /pjproject/branches/projects/jbuf2/pjmedia/src/pjmedia/jbuf2.c	(revision 1777)
+++ /pjproject/branches/projects/jbuf2/pjmedia/src/pjmedia/jbuf2.c	(revision 1778)
@@ -42,5 +42,5 @@
 /* Max frames specifies maximum frames allowed to be allocated.
  */
-#define MAX_FRAMES		100
+#define MAX_FRAMES		20
 
 /* History constants specify entries number of history,
@@ -133,4 +133,5 @@
     PJ_DECL_LIST_MEMBER(struct jb_frame_list);
     pjmedia_jb2_frame	frame;
+    void		*orig_buffer;
 } jb_frame_list;
 
@@ -168,6 +169,7 @@
     pjmedia_jb2_state	     state;
 
-    /* last get frame */
+    /* frame history */
     pjmedia_jb2_frame	     last_frame_out;
+    pjmedia_jb2_frame	     last_frame_cn;
 
     /* state update */
@@ -183,5 +185,4 @@
 };
 
-static void init_jb(pjmedia_jb2_t *jb);
 static void update_state(pjmedia_jb2_t *jb, pj_bool_t on_get);
 static void compensate_drift(pjmedia_jb2_t *jb);
@@ -201,6 +202,7 @@
 	pj_list_erase(f);
     } else if (jb->state.frame_cnt >= jb->setting.max_frames) {
-	TRACE__((THIS_FILE, "Frame numbers(%d) exceeds max(%d)!",
-	    jb->state.frame_cnt, jb->setting.max_frames));
+	TRACE__((THIS_FILE, "Frame numbers %d(=%d?) exceeds max(%d)!",
+	    jb->state.frame_cnt, pj_list_size(&jb->frames), 
+	    jb->setting.max_frames));
 	return NULL;
     } else {
@@ -210,6 +212,10 @@
 	    return NULL;
 	}
-	f->frame.buffer = pj_pool_alloc(jb->pool, jb->setting.frame_size);
-	if (NULL == f->frame.buffer) return NULL;
+	f->orig_buffer = pj_pool_alloc(jb->pool, jb->setting.frame_size);
+	f->frame.buffer = f->orig_buffer;
+	if (NULL == f->frame.buffer) {
+	    TRACE__((THIS_FILE, "Failed to allocate frame buffer!"));
+	    return NULL;
+	}
 	++jb->state.frame_cnt;
     }
@@ -223,4 +229,5 @@
     pj_list_erase(f);
     pj_list_push_back(&jb->frames_pool, f);
+    f->frame.buffer = f->orig_buffer;
 }
 
@@ -327,8 +334,9 @@
 }
 
-static void jb_vbuf_expand(jb_vbuf *buf, unsigned size)
+static pj_bool_t jb_vbuf_expand(jb_vbuf *buf, unsigned size)
 {
     jb_frame_list *f;
     int step;
+    int orig_buf_cnt = buf->cnt;
 
     pj_assert(buf);
@@ -338,4 +346,16 @@
 
 	f = alloc_frame(buf->jb);
+	if (!f) {
+	    unsigned i;
+	    /* Rollback */
+	    for (i=orig_buf_cnt; i<buf->cnt; ++i) {
+		buf->size -= buf->f[i]->frame.size;
+		buf->jb->state.cur_size -= buf->f[i]->frame.size/2;
+		release_frame(buf->jb, buf->f[i]);
+	    }
+	    buf->cnt = orig_buf_cnt;
+	    return PJ_FALSE;
+	}
+
 	f->frame.pt = buf->f[buf->cnt-1]->frame.pt;
 	f->frame.seq = buf->f[buf->cnt-1]->frame.seq;
@@ -353,10 +373,149 @@
 	size -= step;
     }
-}
-
-static void init_jb(pjmedia_jb2_t *jb)
+    return PJ_TRUE;
+}
+
+/* Decode consecutive undecoded frames. */
+static pj_status_t decode_frames(pjmedia_jb2_t *jb, jb_frame_list* start,
+				 int max)
+{
+    jb_frame_list* tmp = start;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(jb && start && jb->cb.decode, PJ_EINVAL);
+
+    /* Decoded already */
+    if (start->frame.type != PJMEDIA_JB_FT_NORMAL_FRAME)
+	return PJ_SUCCESS;
+
+    /* In the beginning, make sure it is sequenced from last_frame_out,
+     * otherwise we cannot detect packet loss.
+     */
+    if (start->prev == &jb->frames &&			        /* beginning */
+	jb->last_frame_out.seq &&			 /* last out defined */
+	start->frame.seq > jb->last_frame_out.seq &&	     /* bad sequence */
+	(start->frame.seq - jb->last_frame_out.seq > 1))
+    {
+	return PJ_SUCCESS;
+    }
+
+    /* Not in the beginning, make sure previous frame is sequenced & decoded */
+    if (start->prev != &jb->frames &&			    /* not beginning */
+	((start->frame.seq > start->prev->frame.seq &&	     /* bad sequence */
+	 (start->frame.seq - start->prev->frame.seq > 1)) ||
+	start->prev->frame.type == PJMEDIA_JB_FT_NORMAL_FRAME)) /* undecoded */
+    {
+	return PJ_SUCCESS;
+    }
+
+    do {
+	status = jb->cb.decode(&tmp->frame, jb->cb.user_data);
+        
+	if (status != PJ_SUCCESS) {
+	    TRACE__((THIS_FILE,"Failed to decode frame!"));
+	    release_frame(jb, tmp);
+	    return PJMEDIA_JB2_EFAILDECODE;
+	}
+	tmp->frame.type = PJMEDIA_JB_FT_NORMAL_RAW_FRAME;
+	tmp = tmp->next;
+        
+	jb->state.cur_size += jb->setting.samples_per_frame;
+
+    } while (tmp != &jb->frames && 
+	     tmp->frame.type == PJMEDIA_JB_FT_NORMAL_FRAME &&
+	     (tmp->frame.ts - tmp->prev->frame.ts <= 
+	     jb->setting.samples_per_frame) &&
+	     (!max || --max > 0));
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_jb2_create( pj_pool_t *pool,
+					  const pj_str_t *name,
+					  const pjmedia_jb2_setting *setting,
+					  const pjmedia_jb2_cb *cb,
+					  pjmedia_jb2_t **p_jb )
+{
+    pjmedia_jb2_t *jb;
+
+    PJ_ASSERT_RETURN(pool && setting && cb && p_jb, PJ_EINVAL);
+
+    if (setting->max_frames > MAX_FRAMES) {
+	PJ_LOG(4,(THIS_FILE, "Max frames requested is too big!"));
+	return PJ_EINVAL;
+    }
+
+    jb = PJ_POOL_ZALLOC_T(pool, pjmedia_jb2_t);
+    jb->pool = pool;
+
+    if (name)
+	pj_strdup_with_null(jb->pool, &jb->name, name);
+    else
+	pj_strdup2_with_null(pool, &jb->name, "jb2");
+
+    pj_list_init(&jb->frames_pool);
+    pj_list_init(&jb->frames);
+
+    jb->setting = *setting;
+    jb->cb = *cb;
+
+    /* Init CN frame */
+    pj_bzero(&jb->last_frame_cn, sizeof(jb->last_frame_cn));
+    jb->last_frame_cn.buffer = pj_pool_zalloc(pool, setting->frame_size);
+    jb->last_frame_cn.size = 0;
+
+    if (!jb->setting.max_frames)
+	jb->setting.max_frames = MAX_FRAMES;
+    
+    pjmedia_jb2_reset(jb);
+
+    *p_jb = jb;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_jb2_destroy(pjmedia_jb2_t *jb)
+{
+    pjmedia_jb2_stat jb_stat;
+    pjmedia_jb2_state jb_state;
+
+    PJ_ASSERT_RETURN(jb, PJ_EINVAL);
+
+    pjmedia_jb2_get_state(jb, &jb_state);
+    PJ_LOG(5, (jb->name.ptr, "JB state:"));
+    PJ_LOG(5, (jb->name.ptr, "Drift\t = %5d/%d", jb_state.drift, 
+						 jb_state.drift_span));
+    PJ_LOG(5, (jb->name.ptr, "Level\t = %5d ticks", jb_state.level));
+    PJ_LOG(5, (jb->name.ptr, "Size\t = %5d samples", jb_state.cur_size));
+    PJ_LOG(5, (jb->name.ptr, "Opt.Size\t = %5d samples", jb_state.opt_size));
+    PJ_LOG(5, (jb->name.ptr, "Pool\t = %5d frames", jb_state.frame_cnt));
+
+    pjmedia_jb2_get_stat(jb, &jb_stat);
+    PJ_LOG(5, (jb->name.ptr, "JB statistic:"));
+    PJ_LOG(5, (jb->name.ptr, "in \t = %5d frames", jb_stat.in));
+    PJ_LOG(5, (jb->name.ptr, "out\t = %5d frames", jb_stat.out));
+    PJ_LOG(5, (jb->name.ptr, "lost\t = %5d frames", jb_stat.lost));
+    PJ_LOG(5, (jb->name.ptr, "late\t = %5d frames", jb_stat.late));
+    PJ_LOG(5, (jb->name.ptr, "ooo\t = %5d frames", jb_stat.ooo));
+    PJ_LOG(5, (jb->name.ptr, "empty\t = %5d times", jb_stat.empty));
+    PJ_LOG(5, (jb->name.ptr, "full\t = %5d times", jb_stat.full));
+    PJ_LOG(5, (jb->name.ptr, "max_size\t = %5d samples", jb_stat.max_size));
+    PJ_LOG(5, (jb->name.ptr, "max_level\t = %5d", jb_stat.max_level));
+    PJ_LOG(5, (jb->name.ptr, "max_comp\t = %5d samples", jb_stat.max_comp));
+    PJ_LOG(5, (jb->name.ptr, "max_drift\t = %5d/%d", jb_stat.max_drift, 
+				    jb_stat.max_drift_span));
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_jb2_reset(pjmedia_jb2_t *jb)
 {
     jb_frame_list *f, *next_f;
     int i;
+
+    PJ_ASSERT_RETURN(jb, PJ_EINVAL);
+
+    /* Reset tick */
+    jb->tick = 0;
 
     /* Reset frames */
@@ -369,5 +528,5 @@
     pj_list_init(&jb->frames);
 
-    /* Init last frame out */
+    /* Init frame history */
     pj_bzero(&jb->last_frame_out, sizeof(jb->last_frame_out));
 
@@ -400,103 +559,4 @@
     jb->drift_state.acc_mod_span = 1;
 
-    /* Inform user about JB (re)init */
-}
-
-/* Decode all consecutive undecoded frames. */
-static pj_status_t decode_frames(pjmedia_jb2_t *jb, jb_frame_list* start,
-				 int max)
-{
-    jb_frame_list* tmp = start;
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(jb && start && jb->cb.decode, PJ_EINVAL);
-
-    /* Decoded already */
-    if (start->frame.type != PJMEDIA_JB_FT_NORMAL_FRAME)
-	return PJ_SUCCESS;
-
-    do {
-	status = jb->cb.decode(&tmp->frame);
-        
-	pj_assert(tmp->frame.size == jb->setting.frame_size);
-
-	if (status != PJ_SUCCESS) {
-	    TRACE__((THIS_FILE,"Failed to decode frame!"));
-	    return PJMEDIA_JB2_EFAILDECODE;
-	}
-	tmp->frame.type = PJMEDIA_JB_FT_NORMAL_RAW_FRAME;
-	tmp = tmp->next;
-        
-	jb->state.cur_size += jb->setting.samples_per_frame;
-
-    } while (tmp != &jb->frames && 
-	     tmp->frame.type == PJMEDIA_JB_FT_NORMAL_FRAME &&
-	     (tmp->frame.ts - tmp->prev->frame.ts == 
-	     jb->setting.samples_per_frame) &&
-	     (!max || --max > 0));
-
-    return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pjmedia_jb2_create( pj_pool_t *pool,
-					  const pj_str_t *name,
-					  const pjmedia_jb2_setting *setting,
-					  const pjmedia_jb2_cb *cb,
-					  pjmedia_jb2_t **p_jb )
-{
-    pjmedia_jb2_t *jb;
-
-    PJ_ASSERT_RETURN(pool && setting && cb && p_jb, PJ_EINVAL);
-
-    if (setting->max_frames > MAX_FRAMES) {
-	PJ_LOG(4,(THIS_FILE, "Max frames requested is too big!"));
-	return PJ_EINVAL;
-    }
-
-    jb = PJ_POOL_ZALLOC_T(pool, pjmedia_jb2_t);
-    jb->pool = pool;
-
-    if (name)
-	pj_strdup_with_null(jb->pool, &jb->name, name);
-    else
-	pj_strdup2(pool, &jb->name, "jb2");
-
-    pj_list_init(&jb->frames_pool);
-    pj_list_init(&jb->frames);
-
-    jb->setting = *setting;
-    jb->cb = *cb;
-
-    pjmedia_jb2_reset(jb);
-
-    *p_jb = jb;
-
-    return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pjmedia_jb2_destroy(pjmedia_jb2_t *jb)
-{
-    PJ_UNUSED_ARG(jb);
-    PJ_ASSERT_RETURN(jb, PJ_EINVAL);
-
-    return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pjmedia_jb2_reset(pjmedia_jb2_t *jb)
-{
-    PJ_ASSERT_RETURN(jb, PJ_EINVAL);
-
-    /* Reset tick */
-    jb->tick = 0;
-
-    /* Reset statistic */
-    pj_bzero(&jb->stat, sizeof(pjmedia_jb2_stat));
-
-    if (!jb->setting.max_frames)
-	jb->setting.max_frames = MAX_FRAMES;
-    
-    /* Init buffers */
-    init_jb(jb);
-
     return PJ_SUCCESS;
 }
@@ -593,10 +653,5 @@
     if (tmp->frame.type == PJMEDIA_JB_FT_NORMAL_RAW_FRAME) {
 	jb->state.cur_size += jb->setting.samples_per_frame;
-    }
-    else if (tmp->prev != &jb->frames && 
-	     (tmp->frame.ts - tmp->prev->frame.ts == 
-	     jb->setting.samples_per_frame) &&
-	     tmp->prev->frame.type != PJMEDIA_JB_FT_NORMAL_FRAME)
-    {
+    } else {
 	status = decode_frames(jb, tmp, 0);
     }
@@ -620,5 +675,5 @@
 
     if (jb->state.phase == PJMEDIA_JB_PH_IDLE)
-	goto ON_RETURN;
+	goto RETURN_EMPTY_FRAME;
 
     /* Check first frame */
@@ -632,16 +687,15 @@
 	    jb_frame_list *tmp_plc;
 
+	    ++jb->stat.in;
+	    ++jb->stat.lost;
+
 	    /* Generate PLC frame*/
 	    tmp_plc = alloc_frame(jb);
-	    if (!tmp_plc) return PJ_ENOMEM;
-
-	    /* Fill buffer with PLC */
-	    if (jb->cb.plc) {
-		status = jb->cb.plc(&tmp_plc->frame);
-		if (status != PJ_SUCCESS)
-		    return status;
+	    if (!tmp_plc) {
+		status = PJ_ENOMEM;
+		jb->last_frame_out = tmp->frame;
+		release_frame(jb, tmp);
+		goto RETURN_EMPTY_FRAME;
 	    }
-
-	    tmp_plc->frame.type = PJMEDIA_JB_FT_INTERP_RAW_FRAME;
 	    tmp_plc->frame.pt = jb->last_frame_out.pt;
 	    tmp_plc->frame.seq = jb->last_frame_out.seq + 1;
@@ -649,52 +703,54 @@
 	    tmp_plc->frame.ts = jb->last_frame_out.ts + 
 				jb->setting.samples_per_frame;
-
 	    pj_list_push_front(&jb->frames, tmp_plc);
-	    ++jb->stat.in;
-	    ++jb->stat.lost;
-	    TRACE__((THIS_FILE,"Frame lost!"));
+
+	    /* Fill buffer with PLC */
+	    if (jb->cb.plc) {
+		status = jb->cb.plc(&tmp_plc->frame, jb->cb.user_data);
+		if (status != PJ_SUCCESS) {
+		    TRACE__((THIS_FILE,"PLC failed"));
+		    jb->last_frame_out = tmp->frame;
+		    release_frame(jb, tmp_plc);
+		    goto RETURN_EMPTY_FRAME;
+		}
+		tmp_plc->frame.type = PJMEDIA_JB_FT_INTERP_RAW_FRAME;
+
+		TRACE__((THIS_FILE,"Lost frame recovered!"));
+	    } else {
+		pj_bzero(tmp_plc->frame.buffer, jb->setting.frame_size);
+		tmp_plc->frame.type = PJMEDIA_JB_FT_NULL_FRAME;
+		TRACE__((THIS_FILE,"Lost frame!"));
+	    }
 
 	    jb->state.cur_size += jb->setting.samples_per_frame;
 	}
 
-	/* Or there is CN packet */
-	else if (tmp->frame.pt == PT_CN) {
-	    unsigned cn_samples;
-	    jb_frame_list *tmp_cn, *pos;
-
-	    /* Generate CN frames */
-	    if (jb->last_frame_out.ts)
-		cn_samples = tmp->frame.ts - jb->last_frame_out.ts;
+	/* Or there is CN packet/jumping timestamp */
+	else if (tmp->frame.pt == PT_CN || (jb->last_frame_out.ts && 
+		(tmp->frame.ts - jb->last_frame_out.ts > 
+		jb->setting.samples_per_frame)))
+	{
+	    unsigned cn_frames;
+
+	    /* Count CN frames */
+	    if (jb->last_frame_out.ts && tmp->frame.ts > jb->last_frame_out.ts)
+		cn_frames = (tmp->frame.ts - jb->last_frame_out.ts) / 
+			    jb->setting.samples_per_frame;
 	    else
-		cn_samples = jb->setting.samples_per_frame;
-
-	    pos = &jb->frames;
-	    for (i = 0; i<cn_samples; i += jb->setting.samples_per_frame)
-	    {
-		tmp_cn = alloc_frame(jb);
-		if (!tmp_cn) return PJ_ENOMEM;
-
-		/* Fill buffer with CNG */
-		status = jb->cb.cng(&tmp_cn->frame);
-		if (status != PJ_SUCCESS)
-		    return status;
-
-		tmp_cn->frame.type = PJMEDIA_JB_FT_INTERP_RAW_FRAME;
-		tmp_cn->frame.pt = jb->last_frame_out.pt;
-		tmp_cn->frame.seq = tmp->frame.seq;
-		tmp_cn->frame.size = jb->setting.frame_size;
-		tmp_cn->frame.ts = jb->last_frame_out.seq + i +
-				   jb->setting.samples_per_frame;
-
-		pj_list_insert_after(pos, tmp_cn);
-		pos = tmp_cn;
-		jb->state.cur_size += jb->setting.samples_per_frame;
+		cn_frames = 1;
+
+	    jb->last_frame_out = tmp->frame;
+
+	    if (tmp->frame.pt == PT_CN) {
+		/* Save CN info */
+		jb->last_frame_cn.size = tmp->frame.size;
+		pj_memcpy(jb->last_frame_cn.buffer, tmp->frame.buffer, 
+			  tmp->frame.size);
+		jb->stat.in += cn_frames;
+		release_frame(jb, tmp);
+	    } else {
+		jb->stat.in += cn_frames - 1;
+		status = decode_frames(jb, tmp, 2);
 	    }
-
-	    /* Discard this CN frame, replaced by real PCM frames */
-	    release_frame(jb, tmp);
-
-	    /* Update statistic */
-	    jb->stat.in += cn_samples - 1;
 	}
 
@@ -705,27 +761,25 @@
 	     */
 	    status = decode_frames(jb, tmp, 2);
-	    if (status != PJ_SUCCESS)
-		return status;
-	}
-    }
-
-    /* Compensate drift right before any frame fetched */
-    compensate_drift(jb);
+	}
+    }
 
     /* Consistency checking, when cur_size!=0, there should be frame(s)
      * in the JB frames list.
      */
-    pj_assert((jb->state.cur_size==0) || 
+    pj_assert((jb->state.cur_size == 0) || 
 	      (!pj_list_empty(&jb->frames) && jb->state.cur_size));
 
     /* Not enough samples in PCM buffer */
-    if (jb->state.cur_size < jb->setting.samples_per_frame) {
+    if (jb->state.cur_size == 0) {
 	if (jb->state.phase != PJMEDIA_JB_PH_LEARNING) {
-	    TRACE__((THIS_FILE,"Not enough PCM samples (%d)!", 
-		     jb->state.cur_size));
+	    TRACE__((THIS_FILE,"JB empty!"));
 	    ++jb->stat.empty;
 	}
+
 	goto RETURN_EMPTY_FRAME;
     }
+
+    /* Compensate drift right before any frame fetched */
+    compensate_drift(jb);
 
     /* Prepare the requested frame */
@@ -739,5 +793,5 @@
     
     /* Fill frame buffer, make sure size == setting.frame_size */
-    while (f->size < jb->setting.frame_size) {
+    while (f->size < jb->setting.frame_size && jb->state.cur_size) {
 	next_frm = tmp->next;
 
@@ -754,9 +808,11 @@
 	    pj_memcpy((pj_int8_t*)f->buffer + f->size, tmp->frame.buffer, i);
 	    tmp->frame.size -= i;
-
-	    /* Shift the buffer, don't shift the pointer! */
-	    pj_memmove(tmp->frame.buffer, (pj_int8_t*)tmp->frame.buffer + i, 
-		       tmp->frame.size);
-	}
+	    tmp->frame.buffer = (pj_int8_t*)tmp->frame.buffer + i;
+	}
+
+	pj_assert(jb->state.cur_size >= (i/
+		    (jb->setting.frame_size/jb->setting.samples_per_frame)));
+	jb->state.cur_size -= i/
+		    (jb->setting.frame_size/jb->setting.samples_per_frame);
 
 	f->size += i;
@@ -764,9 +820,20 @@
     }
 
-    jb->state.cur_size -= jb->setting.samples_per_frame;
+    if (jb->tick % 100 == 0) {
+	pjmedia_jb2_state jb_state;
+
+	pjmedia_jb2_get_state(jb, &jb_state);
+	PJ_LOG(5, (jb->name.ptr, "JB state:"));
+	PJ_LOG(5, (jb->name.ptr, "Drift\t = %5d/%d", jb_state.drift, 
+						     jb_state.drift_span));
+	PJ_LOG(5, (jb->name.ptr, "Level\t = %5d ticks", jb_state.level));
+	PJ_LOG(5, (jb->name.ptr, "Size\t = %5d samples", jb_state.cur_size));
+	PJ_LOG(5, (jb->name.ptr, "Opt.Size\t = %5d samples", jb_state.opt_size));
+	PJ_LOG(5, (jb->name.ptr, "Pool\t = %5d frames", jb_state.frame_cnt));
+    }
 
     /* JB still learning the level */
-    if (jb->state.phase == PJMEDIA_JB_PH_LEARNING)
-	goto RETURN_EMPTY_FRAME;
+    //if (jb->state.phase == PJMEDIA_JB_PH_LEARNING)
+	//goto RETURN_EMPTY_FRAME;
 
     goto ON_RETURN;
@@ -774,12 +841,25 @@
 RETURN_EMPTY_FRAME:
 
-    f->type = PJMEDIA_JB_FT_NULL_FRAME;
     f->pt = jb->last_frame_out.pt;
     f->seq = jb->last_frame_out.seq;
-    f->size = jb->setting.frame_size;
     f->ts = jb->last_frame_out.ts;
-    pj_bzero(f->buffer, f->size);
 
     /* Instead of returning zero samples, it'd be better to return noise */
+    if (jb->cb.cng && jb->last_frame_cn.size) {
+	f->type = PJMEDIA_JB_FT_INTERP_RAW_FRAME;
+	f->size = jb->last_frame_cn.size;
+	pj_memcpy(f->buffer, jb->last_frame_cn.buffer, f->size);
+	status = jb->cb.cng(f, jb->cb.user_data);
+	if (status != PJ_SUCCESS) {
+	    f->type = PJMEDIA_JB_FT_NULL_FRAME;
+	    f->size = jb->setting.frame_size;
+	    pj_bzero(f->buffer, f->size);
+	    TRACE__((THIS_FILE,"CNG failed"));
+	}
+    } else {
+	f->type = PJMEDIA_JB_FT_NULL_FRAME;
+	f->size = jb->setting.frame_size;
+	pj_bzero(f->buffer, f->size);
+    }
 
 ON_RETURN:
@@ -800,5 +880,5 @@
 
 	    /* Reinit JB */
-	    init_jb(jb);
+	    pjmedia_jb2_reset(jb);
 	}
 
@@ -866,6 +946,7 @@
 #endif
 
-	    TRACE__((THIS_FILE, "History=%s Level=%d Opt.size=%d", dbg_hist, 
-				jb->state.level, jb->state.opt_size));
+	    TRACE__((THIS_FILE, "History=%s level=%d opt.size=%d cur.size=%d", 
+				dbg_hist, jb->state.level, 
+				jb->state.opt_size, jb->state.cur_size));
 	}
 	/* Reset level counter */
@@ -902,4 +983,23 @@
 	jb->state.drift = new_drift;
 	jb->state.drift_span = new_drift_span;
+
+	/* Validate drift by comparing current drift to drift margin constant.
+	 * Note that the drift value need to be rescaled to the same span before
+	 * comparing.
+	 */
+       // drift_span = PJ_MIN(jb->stat.in - jb->hist[HISTORY_COUNT-1].in,
+			    //jb->stat.out - jb->hist[HISTORY_COUNT-1].out)
+		     //* (int)jb->setting.samples_per_frame;
+       // drift = jb->state.drift * MAX_DRIFT_MARGIN_SPAN / drift_span;
+	new_drift = jb->state.drift * MAX_DRIFT_MARGIN_SPAN / 
+		(int)jb->state.drift_span;
+	if (PJ_ABS(new_drift) > MAX_DRIFT_MARGIN) {
+	    TRACE__((THIS_FILE, "Abnormal drift (%d/%d), reinit jitter buffer!",
+		     new_drift, MAX_DRIFT_MARGIN_SPAN));
+
+	    /* Reinit JB */
+	    pjmedia_jb2_reset(jb);
+	}
+
 
 	TRACE__((THIS_FILE, "Drift info update: %d/%d (in=%d out=%d)",
@@ -937,5 +1037,5 @@
 				       unsigned *ref_, unsigned *match_)
 {
-    const int MATCH_THRESHOLD = MATCH_WINDOW_LEN*1200;
+    const int MATCH_THRESHOLD = MATCH_WINDOW_LEN*1000;
     unsigned ref, ptr, end;
     int i, similarity, s1, s2;
@@ -1068,5 +1168,6 @@
 
     dest = buf->size/2;
-    jb_vbuf_expand(buf, distance*2);
+    if (!jb_vbuf_expand(buf, distance*2))
+	return 0;
 
     // memcpy
@@ -1132,29 +1233,9 @@
     jb_vbuf buf;
     int sample_size, uncomp_cnt;
-    pj_int32_t drift, itmp;
+    pj_int32_t itmp;
 
     /* No drift, just leave */
     if (!jb->state.drift || !jb->state.drift_span) {
 	jb->drift_state.last_ts = 0;
-	return;
-    }
-
-    /* Validate drift by comparing current drift to drift margin constant.
-     * Note that the drift value need to be rescaled to the same span before
-     * comparing.
-     */
-   // drift_span = PJ_MIN(jb->stat.in - jb->hist[HISTORY_COUNT-1].in,
-			//jb->stat.out - jb->hist[HISTORY_COUNT-1].out)
-		 //* (int)jb->setting.samples_per_frame;
-   // drift = jb->state.drift * MAX_DRIFT_MARGIN_SPAN / drift_span;
-    drift = jb->state.drift * MAX_DRIFT_MARGIN_SPAN / 
-	    (int)jb->state.drift_span;
-    if (PJ_ABS(drift) > MAX_DRIFT_MARGIN) {
-	TRACE__((THIS_FILE, "Abnormal drift (%d/%d), reinit jitter buffer!",
-		 drift, MAX_DRIFT_MARGIN_SPAN));
-
-	/* Reinit JB */
-	init_jb(jb);
-
 	return;
     }
@@ -1195,6 +1276,6 @@
     itmp *= uncomp_cnt;
 
-    TRACE__((THIS_FILE, "Introducing new culprit:%d/%d mod:%d cur_size:%d", 
-	     culprit, buf.cnt, itmp, jb->state.cur_size));
+    //TRACE__((THIS_FILE, "Introducing new culprit:%d/%d mod:%d cur_size:%d", 
+	     //culprit, buf.cnt, itmp, jb->state.cur_size));
     pj_assert(buf.size/2 <= (int)jb->state.cur_size);
 
@@ -1224,4 +1305,14 @@
     culprit += jb->drift_state.pending;
 
+    /* Normalization here, drift calculation is not always accurate,
+     * a bit hacky.
+     */
+    if (culprit > (int) jb->state.opt_size)
+	//&& 
+	//PJ_ABS(jb->state.cur_size-jb->state.opt_size) <= jb->setting.frame_size)
+    {
+	culprit = 0;
+    }
+
     /* Culprit may be negative when pending is negative,
      * which means last compensation was more than needed,
@@ -1246,5 +1337,4 @@
     
     /* IN > OUT (need to shrink) */
-    /* Simple method: just drop samples */
     if (jb->state.drift > 0) {
 
@@ -1264,5 +1354,4 @@
 
     /* IN < OUT ==> (need to grow) */
-    /* Another simple method: insert null samples */
     else {
 
