Index: pjproject/trunk/pjmedia/src/pjmedia/jbuf.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 3553)
+++ pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 3664)
@@ -69,4 +69,5 @@
     pj_size_t	    *content_len;	/**< frame length array		    */
     pj_uint32_t	    *bit_info;		/**< frame bit info array	    */
+    pj_uint32_t	    *ts;		/**< timestamp array		    */
     
     /* States */
@@ -198,5 +199,8 @@
 					    sizeof(framelist->bit_info[0])*
 					    framelist->max_count);
-
+    framelist->ts	    = (pj_uint32_t*)
+			      pj_pool_alloc(pool, 
+					    sizeof(framelist->ts[0])*
+					    framelist->max_count);
 
     return jb_framelist_reset(framelist);
@@ -259,5 +263,7 @@
 				  void *frame, pj_size_t *size,
 				  pjmedia_jb_frame_type *p_type,
-				  pj_uint32_t *bit_info) 
+				  pj_uint32_t *bit_info,
+				  pj_uint32_t *ts,
+				  int *seq) 
 {
     if (framelist->size) {
@@ -295,4 +301,8 @@
 		    *bit_info = framelist->bit_info[framelist->head];
 	    }
+	    if (ts)
+		*ts = framelist->ts[framelist->head];
+	    if (seq)
+		*seq = framelist->origin;
 
 	    //pj_bzero(framelist->content + 
@@ -302,4 +312,5 @@
 	    framelist->content_len[framelist->head] = 0;
 	    framelist->bit_info[framelist->head] = 0;
+	    framelist->ts[framelist->head] = 0;
 
 	    framelist->origin++;
@@ -315,4 +326,51 @@
 
     return PJ_FALSE;
+}
+
+
+static pj_bool_t jb_framelist_peek(jb_framelist_t *framelist,
+				   unsigned offset,
+				   const void **frame,
+				   pj_size_t *size,
+				   pjmedia_jb_frame_type *type,
+				   pj_uint32_t *bit_info,
+				   pj_uint32_t *ts,
+				   int *seq) 
+{
+    unsigned pos, idx;
+
+    if (offset >= jb_framelist_eff_size(framelist))
+	return PJ_FALSE;
+
+    pos = framelist->head;
+    idx = offset;
+
+    /* Find actual peek position, note there may be discarded frames */
+    while (1) {
+	if (framelist->frame_type[pos] != PJMEDIA_JB_DISCARDED_FRAME) {
+	    if (idx == 0)
+		break;
+	    else
+		--idx;
+	}
+	pos = (pos + 1) % framelist->max_count;
+    }
+
+    /* Return the frame pointer */
+    if (frame)
+	*frame = framelist->content + pos*framelist->frame_size;
+    if (type)
+	*type = (pjmedia_jb_frame_type) 
+		framelist->frame_type[pos];
+    if (size)
+	*size = framelist->content_len[pos];
+    if (bit_info)
+	*bit_info = framelist->bit_info[pos];
+    if (ts)
+	*ts = framelist->ts[pos];
+    if (seq)
+	*seq = framelist->origin + offset;
+
+    return PJ_TRUE;
 }
 
@@ -386,4 +444,5 @@
 				       unsigned frame_size,
 				       pj_uint32_t bit_info,
+				       pj_uint32_t ts,
 				       unsigned frame_type)
 {
@@ -439,4 +498,5 @@
     framelist->content_len[pos] = frame_size;
     framelist->bit_info[pos] = bit_info;
+    framelist->ts[pos] = ts;
 
     /* update framelist size */
@@ -748,5 +808,5 @@
 				     int frame_seq)
 {
-    pjmedia_jbuf_put_frame2(jb, frame, frame_size, 0, frame_seq, NULL);
+    pjmedia_jbuf_put_frame3(jb, frame, frame_size, 0, frame_seq, 0, NULL);
 }
 
@@ -756,4 +816,16 @@
 				     pj_uint32_t bit_info,
 				     int frame_seq,
+				     pj_bool_t *discarded)
+{
+    pjmedia_jbuf_put_frame3(jb, frame, frame_size, bit_info, frame_seq, 0, 
+			    discarded);
+}
+
+PJ_DEF(void) pjmedia_jbuf_put_frame3(pjmedia_jbuf *jb, 
+				     const void *frame, 
+				     pj_size_t frame_size, 
+				     pj_uint32_t bit_info,
+				     int frame_seq,
+				     pj_uint32_t ts,
 				     pj_bool_t *discarded)
 {
@@ -825,5 +897,5 @@
     min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size);
     status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
-				 min_frame_size, bit_info, frame_type);
+				 min_frame_size, bit_info, ts, frame_type);
     
     /* Jitter buffer is full, remove some older frames */
@@ -848,5 +920,5 @@
 	removed = jb_framelist_remove_head(&jb->jb_framelist, distance);
 	status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
-				     min_frame_size, bit_info, frame_type);
+				     min_frame_size, bit_info, ts, frame_type);
 
 	jb->jb_discard += removed;
@@ -880,5 +952,6 @@
 				     char *p_frame_type)
 {
-    pjmedia_jbuf_get_frame2(jb, frame, NULL, p_frame_type, NULL);
+    pjmedia_jbuf_get_frame3(jb, frame, NULL, p_frame_type, NULL,
+			    NULL, NULL);
 }
 
@@ -892,4 +965,19 @@
 				     pj_uint32_t *bit_info)
 {
+    pjmedia_jbuf_get_frame3(jb, frame, size, p_frame_type, bit_info,
+			    NULL, NULL);
+}
+
+/*
+ * Get frame from jitter buffer.
+ */
+PJ_DEF(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb, 
+				     void *frame, 
+				     pj_size_t *size,
+				     char *p_frame_type,
+				     pj_uint32_t *bit_info,
+				     pj_uint32_t *ts,
+				     int *seq)
+{
     if (jb->jb_status == JB_STATUS_PREFETCHING) {
 
@@ -915,5 +1003,5 @@
 	/* Try to retrieve a frame from frame list */
 	res = jb_framelist_get(&jb->jb_framelist, frame, size, &ftype, 
-			       bit_info);
+			       bit_info, ts, seq);
 	if (res) {
 	    /* We've successfully retrieved a frame from the frame list, but
@@ -983,2 +1071,49 @@
 }
 
+
+PJ_DEF(void) pjmedia_jbuf_peek_frame( pjmedia_jbuf *jb,
+				      unsigned offset,
+				      const void **frame, 
+				      pj_size_t *size, 
+				      char *p_frm_type,
+				      pj_uint32_t *bit_info,
+				      pj_uint32_t *ts,
+				      int *seq)
+{
+    pjmedia_jb_frame_type ftype;
+    pj_bool_t res;
+
+    res = jb_framelist_peek(&jb->jb_framelist, offset, frame, size, &ftype,
+			    bit_info, ts, seq);
+    if (!res)
+	*p_frm_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
+    else if (ftype == PJMEDIA_JB_NORMAL_FRAME)
+	*p_frm_type = PJMEDIA_JB_NORMAL_FRAME;
+    else
+	*p_frm_type = PJMEDIA_JB_MISSING_FRAME;
+}
+
+
+PJ_DEF(unsigned) pjmedia_jbuf_remove_frame(pjmedia_jbuf *jb, 
+					   unsigned frame_cnt)
+{
+    unsigned count, last_discard_num;
+
+    last_discard_num = jb->jb_framelist.discarded_num;
+    count = jb_framelist_remove_head(&jb->jb_framelist, frame_cnt);
+
+    /* Remove some more when there were discarded frames included */
+    while (jb->jb_framelist.discarded_num < last_discard_num) {
+	/* Calculate frames count to be removed next */
+	frame_cnt = last_discard_num - jb->jb_framelist.discarded_num;
+
+	/* Normalize non-discarded frames count just been removed */
+	count -= frame_cnt;
+
+	/* Remove more frames */
+	last_discard_num = jb->jb_framelist.discarded_num;
+	count += jb_framelist_remove_head(&jb->jb_framelist, frame_cnt);
+    }
+
+    return count;
+}
