Index: /pjproject/trunk/pjmedia/include/pjmedia/errno.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/errno.h	(revision 168)
+++ /pjproject/trunk/pjmedia/include/pjmedia/errno.h	(revision 169)
@@ -277,4 +277,9 @@
 
 
+/************************************************************
+ * JITTER BUFFER
+ ***********************************************************/
+
+
 PJ_END_DECL
 
Index: /pjproject/trunk/pjmedia/include/pjmedia/jbuf.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/jbuf.h	(revision 168)
+++ /pjproject/trunk/pjmedia/include/pjmedia/jbuf.h	(revision 169)
@@ -31,5 +31,5 @@
  */
 
-#include <pj/types.h>
+#include <pjmedia/types.h>
 
 
@@ -37,98 +37,30 @@
 
 
-/**
- * Opaque declaration of internal frame type used by jitter buffer. 
- */
-struct pj_jbframe;
+enum pjmedia_jb_frame_type 
+{
+    PJMEDIA_JB_MISSING_FRAME   = 0,
+    PJMEDIA_JB_NORMAL_FRAME    = 1,
+    PJMEDIA_JB_ZERO_FRAME      = 2,
+};
 
 
-/**
- * Miscelaneous operation result/status. 
- */ 
-typedef enum jb_op_status
-{
-    PJ_JB_STATUS_TOO_OLD = -2,		/** The packet is too old. */
-    PJ_JB_STATUS_TOO_SOON = -3,		/** The packet is too soon. */
-    PJ_JB_STATUS_FRAME_NULL = -4,	/** No packet can be retrieved */
-    PJ_JB_STATUS_FRAME_MISSING = -5,	/** The specified packet is missing/lost */
-} jb_op_status;
+#define PJMEDIA_JB_DEFAULT_INIT_PREFETCH    15
 
 
-/*
- * Frame list, container abstraction for ordered list with fixed maximum
- * size. It is used internally by the jitter buffer.
- */  
-typedef struct pj_jbframelist
-{
-    unsigned		head, count, maxcount;
-    struct pj_jbframe  *frames;
-} pj_jbframelist;
-
-
-/**
- * Jitter buffer implementation.
- */ 
-typedef struct pj_jitter_buffer
-{
-    pj_jbframelist  lst;	    /** The frame list. */
-    int		    level;	    /** Current, real-time jitter level. */
-    int		    max_level;	    /** Maximum level for the period.	 */
-    unsigned	    prefetch;	    /** Prefetch count currently used. */
-    unsigned	    get_cnt;	    /** Number of get operation during prefetch state. */
-    unsigned	    min;	    /** Minimum jitter size, in packets. */
-    unsigned	    max;	    /** Maximum jitter size, in packets. */
-    pj_uint32_t	    lastseq;	    /** Last sequence number put to jitter buffer. */
-    unsigned	    upd_count;	    /** Internal counter to manage update interval. */
-    int		    state;	    /** Jitter buffer state (1==operational) */
-    int		    last_op;	    /** Last jitter buffer operation. */
-} pj_jitter_buffer;
-
-
-/**
- * Initialize jitter buffer with the specified parameters.
- * This function will allocate internal frame buffer from the specified pool.
- * @param jb The jitter buffer to be initialized.
- * @param pool Pool where memory will be allocated for the frame buffer.
- * @param min The minimum value of jitter buffer, in packets.
- * @param max The maximum value of jitter buffer, in packets.
- * @param maxcount The maximum number of delay, in packets, which must be
- *		   greater than max.
- * @return PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pj_jb_init(pj_jitter_buffer *jb, pj_pool_t *pool, 
-				unsigned min, unsigned max, unsigned maxcount);
-
-/**
- * Reset jitter buffer according to the parameters specified when the jitter
- * buffer was initialized. Any packets currently in the buffer will be 
- * discarded.
- */
-PJ_DECL(void) pj_jb_reset(pj_jitter_buffer *jb);
-
-/**
- * Put a pointer to the buffer with the specified sequence number. The pointer
- * normally points to a buffer held by application, and this pointer will be
- * returned later on when pj_jb_get() is called. The jitter buffer will not try
- * to interpret the content of this pointer.
- * @return:
- *   - PJ_SUCCESS on success.
- *   - PJ_JB_STATUS_TOO_OLD when the packet is too old.
- *   - PJ_JB_STATUS_TOO_SOON when the packet is too soon.
- */
-PJ_DECL(pj_status_t) pj_jb_put( pj_jitter_buffer *jb, pj_uint32_t extseq, void *buf );
-
-/**
- * Get the earliest data from the jitter buffer. ONLY when the operation succeeds,
- * the function returns both sequence number and a pointer in the parameters.
- * This returned data corresponds to sequence number and pointer that were
- * given to jitter buffer in the previous pj_jb_put operation.
- * @return 
- *  - PJ_SUCCESS on success
- *  - PJ_JB_STATUS_FRAME_NULL when there is no frames to be returned.
- *  - PJ_JB_STATUS_FRAME_MISSING if the jitter buffer detects that the packet 
- *     is lost. Application may run packet concealment algorithm when it 
- *     receives this status.
- */
-PJ_DECL(pj_status_t) pj_jb_get( pj_jitter_buffer *jb, pj_uint32_t *extseq, void **buf );
+PJ_DECL(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, 
+					int frame_size, 
+					int initial_prefetch, 
+					int max_count,
+					pjmedia_jbuf **p_jb);
+PJ_DECL(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb);
+PJ_DECL(pj_status_t) pjmedia_jbuf_put_frame(pjmedia_jbuf *jb, 
+					    const void *frame, 
+					    pj_size_t frame_size, 
+					    int frame_seq);
+PJ_DECL(pj_status_t) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb, 
+					     void *frame, 
+					     char *p_frame_type);
+PJ_DECL(unsigned)    pjmedia_jbuf_get_prefetch_size(pjmedia_jbuf *jb);
+PJ_DECL(unsigned)    pjmedia_jbuf_get_current_size(pjmedia_jbuf *jb);
 
 
Index: /pjproject/trunk/pjmedia/include/pjmedia/session.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/session.h	(revision 168)
+++ /pjproject/trunk/pjmedia/include/pjmedia/session.h	(revision 169)
@@ -40,4 +40,15 @@
 
 /**
+ * Session info, retrieved from a session by calling
+ * #pjmedia_session_get_info().
+ */
+struct pjmedia_session_info
+{
+    unsigned		stream_cnt;
+    pjmedia_stream_info	stream_info[PJSDP_MAX_MEDIA];
+};
+
+
+/**
  * Create new session offering based on the local and remote SDP.
  * The session will start immediately.
@@ -64,4 +75,16 @@
 					     const pjmedia_sdp_session *rem_sdp,
 					     pjmedia_session **p_session );
+
+
+/**
+ * Get session info.
+ *
+ * @param session	The session which info is being queried.
+ * @param info		Pointer to receive session info.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_session_get_info( pjmedia_session *session,
+					       pjmedia_session_info *info );
 
 
@@ -138,26 +161,10 @@
  *
  * @param session	The media session.
- * @param count		On input, specifies the number of elements in
- *			the array. On output, the number will be filled
- *			with number of streams in the session.
- * @param stat		Array of stream statistics.
+ * @param index		Stream index.
+ * @param stat		Stream statistic.
  *
  * @return		PJ_SUCCESS on success.
  */
-PJ_DECL(pj_status_t) pjmedia_session_get_stat(const pjmedia_session *session,
-					      unsigned *count,
-					      pjmedia_stream_stat stat[]);
-
-/**
- * Get individual stream statistics. The stream statistic shows various
- * indicators such as packet count, packet lost, jitter, delay, etc.
- *
- * @param s		The media session.
- * @param index		The stream index.
- * @param stat		Stream statistics.
- *
- * @return		PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat(const pjmedia_session *s,
+PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat(pjmedia_session *session,
 						     unsigned index,
 						     pjmedia_stream_stat *stat);
Index: /pjproject/trunk/pjmedia/include/pjmedia/types.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/types.h	(revision 168)
+++ /pjproject/trunk/pjmedia/include/pjmedia/types.h	(revision 169)
@@ -167,4 +167,9 @@
 
 /**
+ * Media session info.
+ */
+typedef struct pjmedia_session_info pjmedia_session_info;
+
+/**
  * Forward declaration for SDP attribute (sdp.h)
  */
@@ -206,4 +211,13 @@
 typedef struct pjmedia_sdp_neg pjmedia_sdp_neg;
 
+/**
+ * Types of frame returned from jitter buffer (jbuf.h).
+ */
+typedef enum pjmedia_jb_frame_type pjmedia_jb_frame_type;
+
+/**
+ * Opaque declaration for jitter buffer.
+ */
+typedef struct pjmedia_jbuf pjmedia_jbuf;
 
 #endif	/* __PJMEDIA_TYPES_H__ */
Index: /pjproject/trunk/pjmedia/src/pjmedia/jbuf.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 168)
+++ /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);
+}
+
+
Index: /pjproject/trunk/pjmedia/src/pjmedia/session.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/session.c	(revision 168)
+++ /pjproject/trunk/pjmedia/src/pjmedia/session.c	(revision 169)
@@ -234,5 +234,5 @@
 
     /*
-     * Now create the stream!
+     * Now create and start the stream!
      */
     for (i=0; i<(int)stream_cnt; ++i) {
@@ -241,4 +241,7 @@
 				       &session->stream_info[i],
 				       &session->stream[i]);
+	if (status == PJ_SUCCESS)
+	    status = pjmedia_stream_start(session->stream[i]);
+
 	if (status != PJ_SUCCESS) {
 
@@ -260,4 +263,20 @@
 
 
+/*
+ * Get session info.
+ */
+PJ_DEF(pj_status_t) pjmedia_session_get_info( pjmedia_session *session,
+					      pjmedia_session_info *info )
+{
+    PJ_ASSERT_RETURN(session && info, PJ_EINVAL);
+
+    info->stream_cnt = session->stream_cnt;
+    pj_memcpy(info->stream_info, session->stream_info,
+	      session->stream_cnt * sizeof(pjmedia_stream_info));
+
+    return PJ_SUCCESS;
+}
+
+
 /**
  * Destroy media session.
@@ -369,27 +388,13 @@
  * Get statistics
  */
-PJ_DEF(pj_status_t) pjmedia_session_get_stat(const pjmedia_session *session, 
-					     unsigned *count,
-					     pjmedia_stream_stat stat[])
-{
-    PJ_ASSERT_RETURN(session && count && *count && stat, PJ_EINVAL);
-
-    *count = 0;
-    pj_memset(stat, 0, *count * sizeof(pjmedia_stream_stat));
-    return PJ_EINVALIDOP;
-}
-
-
-/**
- * Get individual stream statistic.
- */
-PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat( const pjmedia_session *s,
+PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat( pjmedia_session *session,
 						     unsigned index,
 						     pjmedia_stream_stat *stat)
 {
-    PJ_ASSERT_RETURN(s && index < s->stream_cnt && stat, PJ_EINVAL);
-    pj_memset(stat, 0, sizeof(pjmedia_stream_stat));
-    return PJ_EINVALIDOP;
-}
-
-
+    PJ_ASSERT_RETURN(session && stat && index < session->stream_cnt, 
+		     PJ_EINVAL);
+
+    return pjmedia_stream_get_stat(session->stream[index], stat);
+}
+
+
Index: /pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 168)
+++ /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 169)
@@ -40,13 +40,4 @@
 #define PJMEDIA_MAX_MTU			1500
 
-struct jb_frame
-{
-    unsigned size;
-    void    *buf;
-};
-
-#define pj_fifobuf_alloc(fifo,size)	malloc(size)
-#define pj_fifobuf_unalloc(fifo,buf)	free(buf)
-#define pj_fifobuf_free(fifo, buf)	free(buf)
 
 
@@ -89,11 +80,11 @@
     pjmedia_codec_mgr	    *codec_mgr;	    /**< Codec manager instance.    */
     pjmedia_codec	    *codec;	    /**< Codec instance being used. */
-
+    pj_size_t		     frame_size;    /**< Size of encoded frame.	    */
     pj_mutex_t		    *jb_mutex;
-    pj_jitter_buffer	     jb;	    /**< Jitter buffer.		    */
-
-    pj_sock_t		     rtp_sock;	    /**< RTP socket.		    */
-    pj_sock_t		     rtcp_sock;	    /**< RTCP socket.		    */
-    pj_sockaddr_in	     dst_addr;	    /**< Destination RTP address.   */
+    pjmedia_jbuf	    *jb;	    /**< Jitter buffer.		    */
+
+    pjmedia_sock_info	     skinfo;	    /**< Transport info.	    */
+    pj_sockaddr_in	     rem_rtp_addr;  /**< Remote RTP address.	    */
+    pj_sockaddr_in	     rem_rtcp_addr; /**< Remote RTCP address.	    */
 
     pj_rtcp_session	     rtcp;	    /**< RTCP for incoming RTP.	    */
@@ -118,7 +109,5 @@
     pjmedia_channel *channel = user_data;
     pjmedia_stream *stream = channel->stream;
-    struct jb_frame *jb_frame;
-    void *p;
-    pj_uint32_t extseq;
+    char frame_type;
     pj_status_t status;
     struct pjmedia_frame frame_in, frame_out;
@@ -134,18 +123,21 @@
 
     /* Get frame from jitter buffer. */
-    status = pj_jb_get(&stream->jb, &extseq, &p);
+    status = pjmedia_jbuf_get_frame(stream->jb, channel->out_pkt,
+				    &frame_type);
 
     /* Unlock jitter buffer mutex. */
     pj_mutex_unlock( stream->jb_mutex );
 
-    jb_frame = p;
-    if (status != PJ_SUCCESS || jb_frame == NULL) {
+    if (status != PJ_SUCCESS || frame_type == PJMEDIA_JB_ZERO_FRAME ||
+	frame_type == PJMEDIA_JB_MISSING_FRAME) 
+    {
 	pj_memset(frame, 0, size);
 	return 0;
     }
 
+
     /* Decode */
-    frame_in.buf = jb_frame->buf;
-    frame_in.size = jb_frame->size;
+    frame_in.buf = channel->out_pkt;
+    frame_in.size = stream->frame_size;
     frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO;  /* ignored */
     frame_out.buf = channel->pcm_buf;
@@ -156,5 +148,4 @@
 
 	pj_memset(frame, 0, size);
-	pj_fifobuf_free (&channel->fifobuf, jb_frame);
 	return 0;
     }
@@ -168,5 +159,4 @@
 
     pj_memcpy(frame, frame_out.buf, size);
-    pj_fifobuf_free (&channel->fifobuf, jb_frame);
 
     return 0;
@@ -239,6 +229,6 @@
     /* Send. */
     sent = frame_out.size+sizeof(pj_rtp_hdr);
-    status = pj_sock_sendto(stream->rtp_sock, channel->out_pkt, &sent, 0, 
-			    &stream->dst_addr, sizeof(stream->dst_addr));
+    status = pj_sock_sendto(stream->skinfo.rtp_sock, channel->out_pkt, &sent, 0, 
+			    &stream->rem_rtp_addr, sizeof(stream->rem_rtp_addr));
     if (status != PJ_SUCCESS)
 	return status;
@@ -261,11 +251,11 @@
     pjmedia_channel *channel = stream->dec;
 
+
     while (!stream->quit_flag) {
-	pj_ssize_t len, size;
+	pj_ssize_t len;
 	const pj_rtp_hdr *hdr;
 	const void *payload;
 	unsigned payloadlen;
 	int status;
-	struct jb_frame *jb_frame;
 
 	/* Wait for packet. */
@@ -274,16 +264,24 @@
 
 	PJ_FD_ZERO (&fds);
-	PJ_FD_SET (stream->rtp_sock, &fds);
+	PJ_FD_SET (stream->skinfo.rtp_sock, &fds);
 	timeout.sec = 0;
 	timeout.msec = 1;
 
 	/* Wait with timeout. */
-	status = pj_sock_select(stream->rtp_sock, &fds, NULL, NULL, &timeout);
-	if (status != 1)
+	status = pj_sock_select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+	if (status < 0) {
+	    char errmsg[PJ_ERR_MSG_SIZE];
+	    pj_strerror(pj_get_netos_error(), errmsg, sizeof(errmsg));
+	    TRACE_((THIS_FILE, "Jitter buffer select() error: %s",
+		    errmsg));
+	    pj_thread_sleep(500);
+	    continue;
+	} else if (status == 0)
 	    continue;
 
 	/* Get packet from socket. */
 	len = channel->in_pkt_size;
-	status = pj_sock_recv(stream->rtp_sock, channel->in_pkt, &len, 0);
+	status = pj_sock_recv(stream->skinfo.rtp_sock, 
+			      channel->in_pkt, &len, 0);
 	if (len < 1 || status != PJ_SUCCESS) {
 	    if (pj_get_netos_error() == PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) {
@@ -326,26 +324,10 @@
 	stream->stat.dec.bytes += len;
 
-	/* Copy to FIFO buffer. */
-	size = payloadlen+sizeof(struct jb_frame);
-	jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size);
-	if (jb_frame == NULL) {
-	    TRACE_((THIS_FILE, "Unable to allocate %d bytes FIFO buffer", 
-		    size));
-	    continue;
-	}
-
-	/* Copy the payload */
-	jb_frame->size = payloadlen;
-	jb_frame->buf = ((char*)jb_frame) + sizeof(struct jb_frame);
-	pj_memcpy (jb_frame->buf, payload, payloadlen);
-
 	/* Put to jitter buffer. */
 	pj_mutex_lock( stream->jb_mutex );
-	status = pj_jb_put(&stream->jb, pj_ntohs(hdr->seq), jb_frame);
+	status = pjmedia_jbuf_put_frame(stream->jb, payload, payloadlen, pj_ntohs(hdr->seq));
 	pj_mutex_unlock( stream->jb_mutex );
 
 	if (status != 0) {
-	    pj_fifobuf_unalloc (&channel->fifobuf, jb_frame);
-	    
 	    TRACE_((THIS_FILE, 
 		    "Jitter buffer put() has returned error status %d", 
@@ -486,4 +468,8 @@
     stream->dir = info->dir;
     stream->codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+    stream->skinfo = info->sock_info;
+    stream->rem_rtp_addr = info->rem_addr;
+
+    PJ_TODO(INITIALIZE_RTCP_REMOTE_ADDRESS);
 
     /* Create mutex to protect jitter buffer: */
@@ -516,4 +502,9 @@
 
 
+    /* Get the frame size: */
+
+    stream->frame_size = (codec_param.avg_bps / 8) * codec_param.ptime / 1000;
+
+
     /* Init RTCP session: */
 
@@ -521,8 +512,8 @@
 
 
-    /* Init jitter buffer: */
-
-    status = pj_jb_init(&stream->jb, pool, 
-			info->jb_min, info->jb_max, info->jb_maxcnt);
+    /* Create jitter buffer: */
+
+    status = pjmedia_jbuf_create(pool, stream->frame_size, 15, 100,
+				 &stream->jb);
     if (status != PJ_SUCCESS)
 	goto err_cleanup;
@@ -533,5 +524,5 @@
     status = pj_thread_create(pool, "decode", 
 			      &jitter_buffer_thread, stream,
-			      0, 0, &stream->thread);
+			      0, PJ_THREAD_SUSPENDED, &stream->thread);
     if (status != PJ_SUCCESS)
 	goto err_cleanup;
@@ -553,4 +544,8 @@
 	goto err_cleanup;
 
+    /* Resume jitter buffer thread. */
+    status = pj_thread_resume( stream->thread );
+    if (status != PJ_SUCCESS)
+	goto err_cleanup;
 
     /* Success! */
Index: /pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h	(revision 168)
+++ /pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h	(revision 169)
@@ -200,4 +200,9 @@
 PJ_DECL(pjsip_module*) pjsip_inv_usage_instance(void);
 
+
+/**
+ * Dump user agent contents (e.g. all dialogs).
+ */
+PJ_DECL(void) pjsip_inv_usage_dump(void);
 
 
Index: /pjproject/trunk/pjsip/include/pjsip/sip_ua_layer.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_ua_layer.h	(revision 168)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_ua_layer.h	(revision 169)
@@ -74,4 +74,9 @@
 
 /**
+ * Dump user agent contents (e.g. all dialogs).
+ */
+PJ_DEF(void) pjsip_ua_dump(void);
+
+/**
  * Get the endpoint instance of a user agent module.
  *
Index: /pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 168)
+++ /pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 169)
@@ -321,4 +321,5 @@
 
 
+
 /*
  * Return the invite session for the specified dialog.
Index: /pjproject/trunk/pjsip/src/pjsip/sip_dialog.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_dialog.c	(revision 168)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_dialog.c	(revision 169)
@@ -930,4 +930,7 @@
 
 	to->tag = dlg->local.info->tag;
+
+	if (dlg->state == PJSIP_DIALOG_STATE_NULL)
+	    dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED;
     }
 }
@@ -1098,5 +1101,7 @@
 
     /* Create UAS transaction for this request. */
-    if (pjsip_rdata_get_tsx(rdata) == NULL) {
+    if (pjsip_rdata_get_tsx(rdata) == NULL && 
+	rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) 
+    {
 	status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx);
 	PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS,{goto on_return;});
@@ -1281,3 +1286,2 @@
 }
 
-
Index: /pjproject/trunk/pjsip/src/pjsip/sip_transport.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 168)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 169)
@@ -1045,4 +1045,9 @@
     pj_lock_acquire(mgr->lock);
 
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    PJ_LOG(3,(THIS_FILE, " Outstanding transmit buffers: %d",
+	      pj_atomic_get(mgr->tdata_counter)));
+#endif
+
     itr = pj_hash_first(mgr->table, &itr_val);
     if (itr) {
Index: /pjproject/trunk/pjsip/src/pjsip/sip_ua_layer.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_ua_layer.c	(revision 168)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_ua_layer.c	(revision 169)
@@ -721,2 +721,72 @@
 
 
+#if PJ_LOG_MAX_LEVEL >= 3
+static void print_dialog( const char *title,
+			  pjsip_dialog *dlg, char *buf, pj_size_t size)
+{
+    int len;
+    char userinfo[128];
+
+    len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
+    if (len < 1)
+	pj_native_strcpy(userinfo, "<--uri too long-->");
+    else
+	userinfo[len] = '\0';
+    
+    len = pj_snprintf(buf, size, "%s[%s]  %s",
+		      title,
+		      (dlg->state==PJSIP_DIALOG_STATE_NULL ? " - " :
+							     "est"),
+		      userinfo);
+    if (len < 1 || len >= (int)size) {
+	pj_native_strcpy(buf, "<--uri too long-->");
+    } else
+	buf[len] = '\0';
+}
+#endif
+
+/*
+ * Dump user agent contents (e.g. all dialogs).
+ */
+PJ_DEF(void) pjsip_ua_dump(void)
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+    pj_hash_iterator_t itbuf, *it;
+    char dlginfo[128];
+
+    pj_mutex_lock(mod_ua.mutex);
+
+    PJ_LOG(3, (THIS_FILE, "Number of dialog sets: %u", pj_hash_count(mod_ua.dlg_table)));
+    PJ_LOG(3, (THIS_FILE, "Dumping dialog sets:"));
+
+    it = pj_hash_first(mod_ua.dlg_table, &itbuf);
+    for (; it != NULL; it = pj_hash_next(mod_ua.dlg_table, it))  {
+	struct dlg_set *dlg_set;
+	pjsip_dialog *dlg;
+	const char *title;
+
+	dlg_set = pj_hash_this(mod_ua.dlg_table, it);
+	if (!dlg_set || pj_list_empty(&dlg_set->dlg_list)) continue;
+
+	/* First dialog in dialog set. */
+	dlg = dlg_set->dlg_list.next;
+	if (dlg->role == PJSIP_ROLE_UAC)
+	    title = "  [out] ";
+	else
+	    title = "  [in]  ";
+
+	print_dialog(title, dlg, dlginfo, sizeof(dlginfo));
+	PJ_LOG(3,(THIS_FILE, "%s", dlginfo));
+
+	/* Next dialog in dialog set (forked) */
+	dlg = dlg->next;
+	while (dlg != (pjsip_dialog*) &dlg_set->dlg_list) {
+	    print_dialog("    [forked] ", dlg, dlginfo, sizeof(dlginfo));
+	    dlg = dlg->next;
+	}
+    }
+
+    pj_mutex_unlock(mod_ua.mutex);
+#endif
+}
+
Index: /pjproject/trunk/pjsip/src/pjsua/main.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/main.c	(revision 168)
+++ /pjproject/trunk/pjsip/src/pjsua/main.c	(revision 169)
@@ -26,4 +26,16 @@
 static pjsip_inv_session *inv_session;
 
+static const char *inv_state_names[] =
+{
+    "NULL      ",
+    "CALLING   ",
+    "INCOMING  ",
+    "EARLY     ",
+    "CONNECTING",
+    "CONFIRMED ",
+    "DISCONNCTD",
+    "TERMINATED",
+};
+
 /*
  * Notify UI when invite state has changed.
@@ -31,19 +43,8 @@
 void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
 {
-    const char *state_names[] =
-    {
-	"NULL",
-	"CALLING",
-	"INCOMING",
-	"EARLY",
-	"CONNECTING",
-	"CONFIRMED",
-	"DISCONNECTED",
-	"TERMINATED",
-    };
-
     PJ_UNUSED_ARG(e);
 
-    PJ_LOG(3,(THIS_FILE, "INVITE session state changed to %s", state_names[inv->state]));
+    PJ_LOG(3,(THIS_FILE, "INVITE session state changed to %s", 
+	      inv_state_names[inv->state]));
 
     if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
@@ -58,4 +59,122 @@
 }
 
+
+static void print_invite_session(const char *title,
+				 struct pjsua_inv_data *inv_data, 
+				 char *buf, pj_size_t size)
+{
+    int len;
+    pjsip_inv_session *inv = inv_data->inv;
+    pjsip_dialog *dlg = inv->dlg;
+    char userinfo[128];
+
+    /* Dump invite sesion info. */
+
+    len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
+    if (len < 1)
+	pj_native_strcpy(userinfo, "<--uri too long-->");
+    else
+	userinfo[len] = '\0';
+    
+    len = pj_snprintf(buf, size, "%s[%s] %s",
+		      title,
+		      inv_state_names[inv->state],
+		      userinfo);
+    if (len < 1 || len >= (int)size) {
+	pj_native_strcpy(buf, "<--uri too long-->");
+	len = 18;
+    } else
+	buf[len] = '\0';
+}
+
+static void dump_media_session(pjmedia_session *session)
+{
+    unsigned i;
+    pjmedia_session_info info;
+
+    pjmedia_session_get_info(session, &info);
+
+    for (i=0; i<info.stream_cnt; ++i) {
+	pjmedia_stream_stat strm_stat;
+	const char *rem_addr;
+	int rem_port;
+	const char *dir;
+
+	pjmedia_session_get_stream_stat(session, i, &strm_stat);
+	rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
+	rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
+
+	if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
+	    dir = "sendonly";
+	else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
+	    dir = "recvonly";
+	else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
+	    dir = "sendrecv";
+	else
+	    dir = "inactive";
+
+	
+	PJ_LOG(3,(THIS_FILE, 
+		  "%s[Media strm#%d] %.*s, %s, peer=%s:%d",
+		  "               ",
+		  i,
+		  info.stream_info[i].fmt.encoding_name.slen,
+		  info.stream_info[i].fmt.encoding_name.ptr,
+		  dir,
+		  rem_addr, rem_port));
+	PJ_LOG(3,(THIS_FILE, 
+		  "%s tx {pkt=%u, bytes=%u} rx {pkt=%u, bytes=%u}",
+		  "                             ",
+		  strm_stat.enc.pkt, strm_stat.enc.bytes,
+		  strm_stat.dec.pkt, strm_stat.dec.bytes));
+
+    }
+}
+
+/*
+ * Dump application states.
+ */
+static void pjsua_dump(void)
+{
+    struct pjsua_inv_data *inv_data;
+    char buf[128];
+    unsigned log_decor;
+
+    log_decor = pj_log_get_decor();
+    pj_log_set_decor(PJ_LOG_HAS_NEWLINE);
+
+    pjsip_endpt_dump(pjsua.endpt, 1);
+    pjsip_ua_dump();
+
+    /* Dump all invite sessions: */
+    PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
+
+    if (pj_list_empty(&pjsua.inv_list)) {
+
+	PJ_LOG(3,(THIS_FILE, "  - no sessions -"));
+
+    } else {
+
+	inv_data = pjsua.inv_list.next;
+
+	while (inv_data != &pjsua.inv_list) {
+
+	    print_invite_session("  ", inv_data, buf, sizeof(buf));
+	    PJ_LOG(3,(THIS_FILE, "%s", buf));
+
+	    if (inv_data->session)
+		dump_media_session(inv_data->session);
+
+	    inv_data = inv_data->next;
+	}
+    }
+
+    pj_log_set_decor(log_decor);
+}
+
+
+/*
+ * Show a bit of help.
+ */
 static void ui_help(void)
 {
@@ -63,4 +182,5 @@
     puts("Console keys:");
     puts("  m    Make a call/another call");
+    puts("  d    Dump application states");
     puts("  a    Answer incoming call");
     puts("  h    Hangup current call");
@@ -122,4 +242,8 @@
 	    break;
 
+
+	case 'd':
+	    pjsua_dump();
+	    break;
 
 	case 'a':
Index: /pjproject/trunk/pjsip/src/pjsua/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/pjsua.h	(revision 168)
+++ /pjproject/trunk/pjsip/src/pjsua/pjsua.h	(revision 169)
@@ -37,4 +37,20 @@
 
 PJ_BEGIN_DECL
+
+
+/** 
+ * Structure to be attached to all dialog. 
+ * Given a dialog "dlg", application can retrieve this structure
+ * by accessing dlg->mod_data[pjsua.mod.id].
+ */
+struct pjsua_inv_data
+{
+    PJ_DECL_LIST_MEMBER(struct pjsua_inv_data);
+
+    pjsip_inv_session	*inv;
+    pjmedia_session	*session;
+};
+
+
 
 /* PJSUA application variables. */
@@ -110,4 +126,7 @@
     char	    *log_filename;  /**< Log filename.			*/
 
+    /* List of invite sessions: */
+
+    struct pjsua_inv_data inv_list;
 };
 
@@ -116,14 +135,4 @@
 extern struct pjsua pjsua;
 
-
-/** 
- * Structure to be attached to all dialog. 
- * Given a dialog "dlg", application can retrieve this structure
- * by accessing dlg->mod_data[pjsua.mod.id].
- */
-struct pjsua_inv_data
-{
-    pjmedia_session *session;
-};
 
 
Index: /pjproject/trunk/pjsip/src/pjsua/pjsua_core.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/pjsua_core.c	(revision 168)
+++ /pjproject/trunk/pjsip/src/pjsua/pjsua_core.c	(revision 169)
@@ -76,4 +76,8 @@
 
     pj_list_init(&pjsua.route_set);
+
+    /* Init invite session list: */
+
+    pj_list_init(&pjsua.inv_list);
 }
 
Index: /pjproject/trunk/pjsip/src/pjsua/pjsua_inv.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/pjsua_inv.c	(revision 168)
+++ /pjproject/trunk/pjsip/src/pjsua/pjsua_inv.c	(revision 169)
@@ -79,4 +79,5 @@
 
     inv_data = pj_pool_zalloc( dlg->pool, sizeof(struct pjsua_inv_data));
+    inv_data->inv = inv;
     dlg->mod_data[pjsua.mod.id] = inv_data;
 
@@ -110,4 +111,8 @@
 	goto on_error;
     }
+
+    /* Add invite session to the list. */
+    
+    pj_list_push_back(&pjsua.inv_list, inv_data);
 
 
@@ -215,5 +220,8 @@
 
 	    inv_data = pj_pool_zalloc(dlg->pool, sizeof(struct pjsua_inv_data));
+	    inv_data->inv = inv;
 	    dlg->mod_data[pjsua.mod.id] = inv_data;
+
+	    pj_list_push_back(&pjsua.inv_list, inv_data);
 
 
@@ -245,4 +253,7 @@
 
 	inv_data = inv->dlg->mod_data[pjsua.mod.id];
+
+	pj_assert(inv_data != NULL);
+
 	if (inv_data && inv_data->session) {
 	    pjmedia_session_destroy(inv_data->session);
@@ -252,4 +263,9 @@
 	}
 
+	if (inv_data) {
+
+	    pj_list_erase(inv_data);
+
+	}
     }
 
