Index: pjproject/trunk/pjmedia/include/pjmedia/jbuf.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/jbuf.h	(revision 407)
+++ pjproject/trunk/pjmedia/include/pjmedia/jbuf.h	(revision 408)
@@ -64,4 +64,6 @@
  *
  * @param pool		The pool to allocate memory.
+ * @param name		Name to identify the jitter buffer for logging
+ *			purpose.
  * @param frame_size	The size of each frame that will be kept in the
  *			jitter buffer. The value here normaly corresponds
@@ -74,9 +76,10 @@
  * @return		PJ_SUCCESS on success.
  */
-PJ_DECL(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, 
-					int frame_size, 
-					int init_delay, 
-					int max_count,
-					pjmedia_jbuf **p_jb);
+PJ_DECL(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool,
+					 const pj_str_t *name,
+					 int frame_size, 
+					 int init_delay, 
+					 int max_count,
+					 pjmedia_jbuf **p_jb);
 
 /**
@@ -88,4 +91,15 @@
  */
 PJ_DECL(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb);
+
+
+/**
+ * Restart jitter. This function flushes all packets in the buffer and
+ * reset the internal sequence number.
+ *
+ * @param jb		The jitter buffer.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb);
 
 
Index: pjproject/trunk/pjmedia/include/pjmedia/rtcp.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/rtcp.h	(revision 407)
+++ pjproject/trunk/pjmedia/include/pjmedia/rtcp.h	(revision 408)
@@ -222,4 +222,5 @@
 struct pjmedia_rtcp_session
 {
+    char		   *name;	/**< Name identification.	    */
     pjmedia_rtcp_pkt	    rtcp_pkt;	/**< Cached RTCP packet.	    */
     
@@ -254,4 +255,6 @@
  *
  * @param session	    The session
+ * @param name		    Optional name to identify the session (for
+ *			    logging purpose).
  * @param clock_rate	    Codec clock rate in samples per second.
  * @param samples_per_frame Average number of samples per frame.
@@ -259,4 +262,5 @@
  */
 PJ_DECL(void) pjmedia_rtcp_init( pjmedia_rtcp_session *session, 
+				 char *name,
 				 unsigned clock_rate,
 				 unsigned samples_per_frame,
Index: pjproject/trunk/pjmedia/include/pjmedia/rtp.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/rtp.h	(revision 407)
+++ pjproject/trunk/pjmedia/include/pjmedia/rtp.h	(revision 408)
@@ -193,4 +193,5 @@
 				 in other flags.			    */
 	    int badpt:1;    /**< Bad payload type.			    */
+	    int badssrc:1;  /**< Bad SSRC				    */
 	    int	dup:1;	    /**< Indicates duplicate packet		    */
 	    int	outorder:1; /**< Indicates out of order packet		    */
Index: pjproject/trunk/pjmedia/src/pjmedia/jbuf.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 407)
+++ pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 408)
@@ -48,4 +48,5 @@
 struct pjmedia_jbuf
 {
+    pj_str_t	    name;		  // jitter buffer name
     jb_framelist    jb_framelist;
     pj_size_t	    jb_frame_size;	  // frame size	
@@ -94,10 +95,9 @@
 					     framelist->flist_max_count);
 
-    framelist->flist_frame_type = pj_pool_zalloc(pool, 
-						 sizeof(framelist->flist_frame_type[0]) * 
-						 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;
@@ -129,14 +129,18 @@
     if (!framelist->flist_empty) {
 	pj_memcpy(frame, 
-		  framelist->flist_buffer + framelist->flist_head * framelist->flist_frame_size,
+		  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,
+	*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;
+	framelist->flist_head = (framelist->flist_head + 1 ) % 
+				framelist->flist_max_count;
 	if (framelist->flist_head == framelist->flist_tail) 
 	    framelist->flist_empty = PJ_TRUE;
@@ -173,5 +177,6 @@
 	}
 
-	pj_memset(framelist->flist_buffer + framelist->flist_head * framelist->flist_frame_size,
+	pj_memset(framelist->flist_buffer + 
+		    framelist->flist_head * framelist->flist_frame_size,
 	          0,
 	          step1*framelist->flist_frame_size);
@@ -191,5 +196,6 @@
 	// update pointers
 	framelist->flist_origin += count;
-	framelist->flist_head = (framelist->flist_head+count) % framelist->flist_max_count;
+	framelist->flist_head = (framelist->flist_head + count) % 
+			        framelist->flist_max_count;
 	if (framelist->flist_head == framelist->flist_tail) 
 	    framelist->flist_empty = PJ_TRUE;
@@ -205,18 +211,21 @@
     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 max_index;
 	unsigned cur_size;
 
-	where = (index - framelist->flist_origin + framelist->flist_head) % framelist->flist_max_count;
+	// too late
+	if (index < framelist->flist_origin) 
+	    return PJ_FALSE;
+
+	// too soon
+	max_index = framelist->flist_origin + framelist->flist_max_count - 1;
+	if (index > max_index)
+	    return PJ_FALSE;
+
+	where = (index - framelist->flist_origin + framelist->flist_head) % 
+	        framelist->flist_max_count;
 
 	// update framelist->flist_tail pointer
@@ -224,10 +233,12 @@
 	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;
+	    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_tail = (framelist->flist_tail + 1) % 
+				framelist->flist_max_count;
 	framelist->flist_empty = PJ_FALSE;
     }
@@ -252,4 +263,5 @@
 
 PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, 
+					const pj_str_t *name,
 					int frame_size, 
 					int initial_prefetch, 
@@ -266,4 +278,5 @@
 	return status;
 
+    pj_strdup_with_null(pool, &jb->name, name);
     jb->jb_frame_size	 = frame_size;
     jb->jb_last_seq_no	 = -1;
@@ -284,4 +297,22 @@
 
 
+PJ_DEF(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb)
+{
+    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_cnt	 = 0;
+    jb->jb_stable_hist	 = 0;
+    jb->jb_status	 = JB_STATUS_INITIALIZING;
+    jb->jb_max_hist_jitter = 0;
+
+    jb_framelist_remove_head(&jb->jb_framelist, 
+			     jb_framelist_size(&jb->jb_framelist));
+    return PJ_SUCCESS;
+}
+
+
 PJ_DEF(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb)
 {
@@ -312,11 +343,12 @@
 
 	    if (jb->jb_op_count >= 100 &&
-		(int)jb_framelist_size(&jb->jb_framelist) > jb->jb_prefetch+2) 
+		(int)jb_framelist_size(&jb->jb_framelist) > jb->jb_prefetch+2)
 	    {
 		jb_framelist_remove_head(&jb->jb_framelist,1);
 
-		PJ_LOG(5,(THIS_FILE, "jbuf prefetch: %d, size=%d", 
-				 jb->jb_prefetch,
-				 jb_framelist_size(&jb->jb_framelist)));
+		PJ_LOG(5,(jb->name.ptr, 
+			  "jbuf optimizing, prefetch: %d, size=%d", 
+			  jb->jb_prefetch,
+			  jb_framelist_size(&jb->jb_framelist)));
 		jb->jb_op_count = 0;
 	    }
@@ -324,15 +356,18 @@
 	}
     } else {
-	jb->jb_prefetch = PJ_MIN(jb->jb_last_jitter,(int)(jb->jb_max_count*4/5));
+	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;
 
 	if (jb->jb_op_count >= 100) {
-	    if ((int)jb_framelist_size(&jb->jb_framelist) > jb->jb_prefetch+2) {
+	    if ((int)jb_framelist_size(&jb->jb_framelist) > jb->jb_prefetch+2) 
+	    {
 		jb_framelist_remove_head(&jb->jb_framelist,1);
 
-		PJ_LOG(5,(THIS_FILE, "jbuf prefetch: %d, size=%d", 
-				 jb->jb_prefetch,
-				 jb_framelist_size(&jb->jb_framelist)));
+		PJ_LOG(5,(jb->name.ptr, 
+			  "jbuf optimizing prefetch: %d, size=%d",
+			  jb->jb_prefetch,
+			  jb_framelist_size(&jb->jb_framelist)));
 	    }
 
@@ -378,6 +413,9 @@
     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));
+	while (jb_framelist_put_at(&jb->jb_framelist,
+				   frame_seq,frame,min_frame_size) ==PJ_FALSE)
+	{
+	    jb_framelist_remove_head(&jb->jb_framelist,
+				     PJ_MAX(jb->jb_max_count/4,1) );
 	}
 
@@ -408,5 +446,7 @@
     }
 
-    if ((jb->jb_prefetch_cnt < jb->jb_prefetch) || !jb_framelist_get(&jb->jb_framelist,frame,&ftype)) {
+    if ((jb->jb_prefetch_cnt < jb->jb_prefetch) || 
+	jb_framelist_get(&jb->jb_framelist,frame,&ftype) == PJ_FALSE) 
+    {
 	pj_memset(frame, 0, jb->jb_frame_size);
 	*p_frame_type = PJMEDIA_JB_ZERO_FRAME;
Index: pjproject/trunk/pjmedia/src/pjmedia/rtcp.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/rtcp.c	(revision 407)
+++ pjproject/trunk/pjmedia/src/pjmedia/rtcp.c	(revision 408)
@@ -110,5 +110,5 @@
 	if (PJ_TIME_VAL_MSEC(diff) >= MIN_DIFF) {
 
-	    TRACE_((THIS_FILE, "NTP timestamp corrected by %d ms",
+	    TRACE_((sess->name, "RTCP NTP timestamp corrected by %d ms",
 		    PJ_TIME_VAL_MSEC(diff)));
 
@@ -126,4 +126,5 @@
 
 PJ_DEF(void) pjmedia_rtcp_init(pjmedia_rtcp_session *sess, 
+			       char *name,
 			       unsigned clock_rate,
 			       unsigned samples_per_frame,
@@ -135,4 +136,7 @@
     pj_memset(rtcp_pkt, 0, sizeof(pjmedia_rtcp_pkt));
     
+    /* Name */
+    sess->name = name ? name : THIS_FILE,
+
     /* Set clock rate */
     sess->clock_rate = clock_rate;
@@ -238,45 +242,46 @@
 
     /*
-     * Calculate jitter (see RFC 3550 section A.8)
+     * Calculate jitter only when sequence is good (see RFC 3550 section A.8)
      */
-    
-    /* Get arrival time and convert timestamp to samples */
-    pj_get_timestamp(&ts);
-    ts.u64 = ts.u64 * sess->clock_rate / sess->ts_freq.u64;
-    arrival = ts.u32.lo;
-
-    transit = arrival - rtp_ts;
-    
-    /* Ignore the first N packets as they normally have bad jitter
-     * due to other threads working to establish the call
-     */
-    if (sess->transit == 0 || sess->received < 25 ) {
-	sess->transit = transit;
-	sess->stat.rx.jitter.min = 2000;
-    } else {
-	pj_int32_t d;
-	pj_uint32_t jitter;
-	
-	d = transit - sess->transit;
-	sess->transit = transit;
-	if (d < 0) 
-	    d = -d;
-	
-	sess->jitter += d - ((sess->jitter + 8) >> 4);
-
-	/* Get jitter in usec */
-	if (d < 4294)
-	    jitter = d * 1000000 / sess->clock_rate;
-	else {
-	    jitter = d * 1000 / sess->clock_rate;
-	    jitter *= 1000;
+    if (seq_st.diff == 1) {
+	/* Get arrival time and convert timestamp to samples */
+	pj_get_timestamp(&ts);
+	ts.u64 = ts.u64 * sess->clock_rate / sess->ts_freq.u64;
+	arrival = ts.u32.lo;
+
+	transit = arrival - rtp_ts;
+    
+	/* Ignore the first N packets as they normally have bad jitter
+	 * due to other threads working to establish the call
+	 */
+	if (sess->transit == 0 || sess->received < 25 ) {
+	    sess->transit = transit;
+	    sess->stat.rx.jitter.min = 2000;
+	} else {
+	    pj_int32_t d;
+	    pj_uint32_t jitter;
+	    
+	    d = transit - sess->transit;
+	    sess->transit = transit;
+	    if (d < 0) 
+		d = -d;
+	    
+	    sess->jitter += d - ((sess->jitter + 8) >> 4);
+
+	    /* Get jitter in usec */
+	    if (d < 4294)
+		jitter = d * 1000000 / sess->clock_rate;
+	    else {
+		jitter = d * 1000 / sess->clock_rate;
+		jitter *= 1000;
+	    }
+
+	    /* Update jitter stat */
+	    if (jitter < sess->stat.rx.jitter.min)
+		sess->stat.rx.jitter.min = jitter;
+	    if (jitter > sess->stat.rx.jitter.max)
+		sess->stat.rx.jitter.max = jitter;
+	    sess->stat.rx.jitter.last = jitter;
 	}
-
-	/* Update jitter stat */
-	if (jitter < sess->stat.rx.jitter.min)
-	    sess->stat.rx.jitter.min = jitter;
-	if (jitter > sess->stat.rx.jitter.max)
-	    sess->stat.rx.jitter.max = jitter;
-	sess->stat.rx.jitter.last = jitter;
     }
 }
@@ -308,5 +313,5 @@
     pj_get_timestamp(&sess->rx_lsr_time);
 
-    TRACE_((THIS_FILE, "Rx RTCP SR: ntp_ts=%p", 
+    TRACE_((sess->name, "Rx RTCP SR: ntp_ts=%p", 
 	    sess->rx_lsr,
 	    (pj_uint32_t)(sess->rx_lsr_time.u64*65536/sess->ts_freq.u64)));
@@ -405,5 +410,5 @@
 	}
 
-	TRACE_((THIS_FILE, "Rx RTCP RR: lsr=%p, dlsr=%p (%d:%03dms), "
+	TRACE_((sess->name, "Rx RTCP RR: lsr=%p, dlsr=%p (%d:%03dms), "
 			   "now=%p, rtt=%p",
 		lsr, dlsr, dlsr/65536, (dlsr%65536)*1000/65536,
@@ -416,5 +421,5 @@
 	    unsigned rtt = (pj_uint32_t)eedelay;
 	    
-	    TRACE_((THIS_FILE, "RTT is set to %d usec", rtt));
+	    TRACE_((sess->name, "RTCP RTT is set to %d usec", rtt));
 
 	    if (rtt >= 1000000) {
@@ -441,5 +446,5 @@
 
 	} else {
-	    PJ_LOG(5, (THIS_FILE, "Internal NTP clock skew detected: "
+	    PJ_LOG(5, (sess->name, "Internal RTCP NTP clock skew detected: "
 				   "lsr=%p, now=%p, dlsr=%p (%d:%03dms), "
 				   "diff=%d",
@@ -526,5 +531,5 @@
     rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo);
 
-    TRACE_((THIS_FILE, "TX RTCP SR: ntp_ts=%p", 
+    TRACE_((sess->name, "TX RTCP SR: ntp_ts=%p", 
 		       ((ntp.hi & 0xFFFF) << 16) + ((ntp.lo & 0xFFFF0000) 
 			    >> 16)));
@@ -559,5 +564,5 @@
 	rtcp_pkt->rr.dlsr = pj_htonl(dlsr);
 
-	TRACE_((THIS_FILE, "Tx RTCP RR: lsr=%p, lsr_time=%p, now=%p, dlsr=%p"
+	TRACE_((sess->name,"Tx RTCP RR: lsr=%p, lsr_time=%p, now=%p, dlsr=%p"
 			   "(%ds:%03dms)",
 			   lsr, 
Index: pjproject/trunk/pjmedia/src/pjmedia/rtp.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/rtp.c	(revision 407)
+++ pjproject/trunk/pjmedia/src/pjmedia/rtp.c	(revision 408)
@@ -20,7 +20,7 @@
 #include <pjmedia/errno.h>
 #include <pj/log.h>
-#include <pj/os.h>	/* pj_gettimeofday() */
 #include <pj/sock.h>	/* pj_htonx, pj_htonx */
 #include <pj/assert.h>
+#include <pj/rand.h>
 #include <pj/string.h>
 
@@ -40,7 +40,9 @@
 
 PJ_DEF(pj_status_t) pjmedia_rtp_session_init( pjmedia_rtp_session *ses,
-					 int default_pt, pj_uint32_t sender_ssrc )
-{
-    PJ_LOG(5, (THIS_FILE, "pjmedia_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x",
+					      int default_pt, 
+					      pj_uint32_t sender_ssrc )
+{
+    PJ_LOG(5, (THIS_FILE, 
+	       "pjmedia_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x",
 	       ses, default_pt, sender_ssrc));
 
@@ -51,19 +53,20 @@
     }
 
-    /* If sender_ssrc is not specified, create from time value. */
+    /* If sender_ssrc is not specified, create from random value. */
     if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) {
-	pj_time_val tv;
-
-	pj_gettimeofday(&tv);
-	sender_ssrc  = (pj_uint32_t) pj_htonl(tv.sec);
+	sender_ssrc = pj_htonl(pj_rand());
     } else {
 	sender_ssrc = pj_htonl(sender_ssrc);
     }
 
-    /* Initialize session. */
-    ses->out_extseq = 0;
+    /* Initialize session. 
+     * Initial sequence number SHOULD be random, according to RFC 3550.
+     */
+    ses->out_extseq = pj_rand();
     ses->peer_ssrc = 0;
     
-    /* Sequence number will be initialized when the first RTP packet is receieved. */
+    /* Sequence number will be initialized when the first RTP packet 
+     * is receieved.
+     */
 
     /* Build default header for outgoing RTP packet. */
@@ -86,13 +89,10 @@
 
 
-PJ_DEF(pj_status_t) pjmedia_rtp_encode_rtp( pjmedia_rtp_session *ses, int pt, int m,
-				       int payload_len, int ts_len,
-				       const void **rtphdr, int *hdrlen )
-{
-    PJ_UNUSED_ARG(payload_len)
-
-    PJ_LOG(6, (THIS_FILE, 
-	      "pjmedia_rtp_encode_rtp: ses=%p, pt=%d, m=%d, pt_len=%d, ts_len=%d",
-	      ses, pt, m, payload_len, ts_len));
+PJ_DEF(pj_status_t) pjmedia_rtp_encode_rtp( pjmedia_rtp_session *ses, 
+					    int pt, int m,
+					    int payload_len, int ts_len,
+					    const void **rtphdr, int *hdrlen )
+{
+    PJ_UNUSED_ARG(payload_len);
 
     /* Update timestamp */
@@ -122,16 +122,12 @@
 
 PJ_DEF(pj_status_t) pjmedia_rtp_decode_rtp( pjmedia_rtp_session *ses, 
-				       const void *pkt, int pkt_len,
-				       const pjmedia_rtp_hdr **hdr,
-				       const void **payload,
-				       unsigned *payloadlen)
+					    const void *pkt, int pkt_len,
+					    const pjmedia_rtp_hdr **hdr,
+					    const void **payload,
+					    unsigned *payloadlen)
 {
     int offset;
 
-    PJ_UNUSED_ARG(ses)
-
-    PJ_LOG(6, (THIS_FILE, 
-	      "pjmedia_rtp_decode_rtp: ses=%p, pkt=%p, pkt_len=%d",
-	      ses, pkt, pkt_len));
+    PJ_UNUSED_ARG(ses);
 
     /* Assume RTP header at the start of packet. We'll verify this later. */
@@ -140,5 +136,4 @@
     /* Check RTP header sanity. */
     if ((*hdr)->v != RTP_VERSION) {
-	PJ_LOG(4, (THIS_FILE, "  invalid RTP version!"));
 	return PJMEDIA_RTP_EINVER;
     }
@@ -149,5 +144,6 @@
     /* Adjust offset if RTP extension is used. */
     if ((*hdr)->x) {
-	pjmedia_rtp_ext_hdr *ext = (pjmedia_rtp_ext_hdr*) (((pj_uint8_t*)pkt) + offset);
+	pjmedia_rtp_ext_hdr *ext = (pjmedia_rtp_ext_hdr*) 
+				    (((pj_uint8_t*)pkt) + offset);
 	offset += (pj_ntohs(ext->length) * sizeof(pj_uint32_t));
     }
@@ -171,25 +167,20 @@
     pjmedia_rtp_status seq_st;
 
-    /* Check SSRC. */
-    if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
-    /*
-    if (pj_ntohl(ses->peer_ssrc) != hdr->ssrc) {
-	PJ_LOG(4, (THIS_FILE, "pjmedia_rtp_session_update: ses=%p, invalid ssrc 0x%p (!=0x%p)",
-		   ses, pj_ntohl(hdr->ssrc), ses->peer_ssrc));
-	return PJMEDIA_RTP_EINSSRC;
-    }
-    */
-
     /* Init status */
     seq_st.status.value = 0;
     seq_st.diff = 0;
 
+    /* Check SSRC. */
+    if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
+
+    if (pj_ntohl(hdr->ssrc) != ses->peer_ssrc) {
+	seq_st.status.flag.badssrc = 1;
+	ses->peer_ssrc = pj_ntohl(hdr->ssrc);
+    }
+
     /* Check payload type. */
     if (hdr->pt != ses->out_pt) {
-	PJ_LOG(4, (THIS_FILE, 
-		   "pjmedia_rtp_session_update: ses=%p, invalid payload "
-		   "type %d (expecting %d)",
-		   ses, hdr->pt, ses->out_pt));
 	if (p_seq_st) {
+	    p_seq_st->status.value = seq_st.status.value;
 	    p_seq_st->status.flag.bad = 1;
 	    p_seq_st->status.flag.badpt = 1;
Index: pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 407)
+++ pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 408)
@@ -38,6 +38,6 @@
 #define THIS_FILE			"stream.c"
 #define ERRLEVEL			1
-#define TRACE_(expr)			stream_perror expr
-#define TRC_(expr)			PJ_LOG(4,expr)
+#define LOGERR_(expr)			stream_perror expr
+#define TRC_(expr)			PJ_LOG(5,expr)
 
 /**
@@ -107,5 +107,6 @@
     pj_size_t		     rtcp_pkt_size; /**< Size of RTCP packet buf.   */
     char		     rtcp_pkt[512]; /**< RTCP packet buffer.	    */
-    pj_uint32_t		     rtcp_tx_time;  /**< RTCP tx time in timestamp  */
+    pj_uint32_t		     rtcp_last_tx;  /**< RTCP tx time in timestamp  */
+    pj_uint32_t		     rtcp_interval; /**< Interval, in timestamp.    */
     int			     rtcp_addrlen;  /**< Address length.	    */
 
@@ -184,5 +185,5 @@
 					channel->pcm_buf_size, &frame_out);
     if (status != 0) {
-	TRACE_((THIS_FILE, "codec decode() error", status));
+	LOGERR_((port->info.name.ptr, "codec decode() error", status));
 
 	frame->type = PJMEDIA_FRAME_TYPE_NONE;
@@ -192,5 +193,6 @@
     /* Put in sound buffer. */
     if (frame_out.size > frame->size) {
-	PJ_LOG(4,(THIS_FILE, "Sound playout buffer truncated %d bytes", 
+	PJ_LOG(4,(port->info.name.ptr, 
+		  "Sound playout buffer truncated %d bytes", 
 		  frame_out.size - frame->size));
 	frame_out.size = frame->size;
@@ -240,9 +242,10 @@
 
 	if (stream->tx_dtmf_count)
-	    PJ_LOG(5,(THIS_FILE,"Sending DTMF digit id %c", 
+	    PJ_LOG(5,(stream->port.info.name.ptr,
+		      "Sending DTMF digit id %c", 
 		      digitmap[stream->tx_dtmf_buf[0].event]));
 
     } else if (duration == 0) {
-	PJ_LOG(5,(THIS_FILE,"Sending DTMF digit id %c", 
+	PJ_LOG(5,(stream->port.info.name.ptr, "Sending DTMF digit id %c", 
 		  digitmap[digit->event]));
     }
@@ -252,100 +255,29 @@
 }
 
+
 /**
- * rec_callback()
+ * check_tx_rtcp()
  *
- * This callback is called when the mic device has gathered
- * enough audio samples. We will encode the audio samples and
- * send it to remote.
- */
-static pj_status_t put_frame( pjmedia_port *port, 
-			      const pjmedia_frame *frame )
-{
-    pjmedia_stream *stream = port->user_data;
-    pjmedia_channel *channel = stream->enc;
-    pj_status_t status = 0;
-    struct pjmedia_frame frame_out;
-    int ts_len;
-    pj_bool_t has_tx;
-    void *rtphdr;
-    int rtphdrlen;
-    pj_ssize_t sent;
-
-    /* Number of samples in the frame */
-    ts_len = frame->size / 2;
-
-    /* Init frame_out buffer. */
-    frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr);
-
-    /* Make compiler happy */
-    frame_out.size = 0;
-
-    /* If we have DTMF digits in the queue, transmit the digits. 
-     * Otherwise encode the PCM buffer.
+ * This function is can be called by either put_frame() or get_frame(),
+ * to transmit periodic RTCP SR/RR report.
+ */
+static void check_tx_rtcp(pjmedia_stream *stream, pj_uint32_t timestamp)
+{
+    /* Note that timestamp may represent local or remote timestamp, 
+     * depending on whether this function is called from put_frame()
+     * or get_frame().
      */
-    if (stream->tx_dtmf_count) {
-
-	has_tx = PJ_TRUE;
-	create_dtmf_payload(stream, &frame_out);
-
-	/* Encapsulate. */
-	status = pjmedia_rtp_encode_rtp( &channel->rtp, 
-					 stream->tx_event_pt, 0, 
-					 frame_out.size, ts_len, 
-					 (const void**)&rtphdr, 
-					 &rtphdrlen);
-
-    } else if (frame->type != PJMEDIA_FRAME_TYPE_NONE) {
-	unsigned max_size;
-
-	has_tx = PJ_TRUE;
-	max_size = channel->out_pkt_size - sizeof(pjmedia_rtp_hdr);
-	status = stream->codec->op->encode( stream->codec, frame, 
-					    max_size, 
-					    &frame_out);
-	if (status != 0) {
-	    TRACE_((THIS_FILE, "Codec encode() error", status));
-	    return status;
-	}
-
-	//printf("p"); fflush(stdout);
-
-	/* Encapsulate. */
-	status = pjmedia_rtp_encode_rtp( &channel->rtp, 
-					 channel->pt, 0, 
-					 frame_out.size, ts_len, 
-					 (const void**)&rtphdr, 
-					 &rtphdrlen);
-    } else {
-
-	/* Just update RTP session's timestamp. */
-	has_tx = PJ_FALSE;
-	status = pjmedia_rtp_encode_rtp( &channel->rtp, 
-					 0, 0, 
-					 0, ts_len, 
-					 (const void**)&rtphdr, 
-					 &rtphdrlen);
-
-    }
-
-    if (status != PJ_SUCCESS) {
-	TRACE_((THIS_FILE, "RTP encode_rtp() error", status));
-	return status;
-    }
-
-    /* Check if this is the time to transmit RTCP packet */
-    if (stream->rtcp_tx_time == 0) {
-	unsigned first_interval;
-
-	first_interval = PJMEDIA_RTCP_INTERVAL + (pj_rand() % 2000);
-	stream->rtcp_tx_time = pj_ntohl(channel->rtp.out_hdr.ts) +
-			       first_interval* stream->port.info.sample_rate /
-			       1000;
-    } else if (pj_ntohl(channel->rtp.out_hdr.ts) >= stream->rtcp_tx_time) {
+
+
+    if (stream->rtcp_last_tx == 0) {
+	
+	stream->rtcp_last_tx = timestamp;
+
+    } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
 	
 	pjmedia_rtcp_pkt *rtcp_pkt;
 	pj_ssize_t size;
-	unsigned interval;
 	int len;
+	pj_status_t status;
 
 	pjmedia_rtcp_build_rtcp(&stream->rtcp, &rtcp_pkt, &len);
@@ -359,13 +291,105 @@
 	    
 	    pj_strerror(status, errmsg, sizeof(errmsg));
-	    PJ_LOG(4,(THIS_FILE, "Error sending RTCP: %s [%d]",
+	    PJ_LOG(4,(port->info.name.ptr, "Error sending RTCP: %s [%d]",
 				 errmsg, status));
 	}
 #endif
 
-	interval = PJMEDIA_RTCP_INTERVAL + (pj_rand() % 500);
-	stream->rtcp_tx_time = pj_ntohl(channel->rtp.out_hdr.ts) +
-			       interval * stream->port.info.sample_rate /
-			       1000;
+	stream->rtcp_last_tx = timestamp;
+    }
+
+}
+
+
+/**
+ * put_frame()
+ *
+ * This callback is called by upstream component when it has PCM frame
+ * to transmit. This function encodes the PCM frame, pack it into
+ * RTP packet, and transmit to peer.
+ */
+static pj_status_t put_frame( pjmedia_port *port, 
+			      const pjmedia_frame *frame )
+{
+    pjmedia_stream *stream = port->user_data;
+    pjmedia_channel *channel = stream->enc;
+    pj_status_t status = 0;
+    struct pjmedia_frame frame_out;
+    int ts_len;
+    pj_bool_t has_tx;
+    void *rtphdr;
+    int rtphdrlen;
+    pj_ssize_t sent;
+
+    /* Number of samples in the frame */
+    ts_len = frame->size / 2;
+
+    /* Init frame_out buffer. */
+    frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr);
+
+    /* Make compiler happy */
+    frame_out.size = 0;
+
+    /* If we have DTMF digits in the queue, transmit the digits. 
+     * Otherwise encode the PCM buffer.
+     */
+    if (stream->tx_dtmf_count) {
+
+	has_tx = PJ_TRUE;
+	create_dtmf_payload(stream, &frame_out);
+
+	/* Encapsulate. */
+	status = pjmedia_rtp_encode_rtp( &channel->rtp, 
+					 stream->tx_event_pt, 0, 
+					 frame_out.size, ts_len, 
+					 (const void**)&rtphdr, 
+					 &rtphdrlen);
+
+    } else if (frame->type != PJMEDIA_FRAME_TYPE_NONE) {
+	unsigned max_size;
+
+	has_tx = PJ_TRUE;
+	max_size = channel->out_pkt_size - sizeof(pjmedia_rtp_hdr);
+	status = stream->codec->op->encode( stream->codec, frame, 
+					    max_size, 
+					    &frame_out);
+	if (status != 0) {
+	    LOGERR_((stream->port.info.name.ptr, 
+		    "Codec encode() error", status));
+	    return status;
+	}
+
+	//printf("p"); fflush(stdout);
+
+	/* Encapsulate. */
+	status = pjmedia_rtp_encode_rtp( &channel->rtp, 
+					 channel->pt, 0, 
+					 frame_out.size, ts_len, 
+					 (const void**)&rtphdr, 
+					 &rtphdrlen);
+    } else {
+
+	/* Just update RTP session's timestamp. */
+	has_tx = PJ_FALSE;
+	status = pjmedia_rtp_encode_rtp( &channel->rtp, 
+					 0, 0, 
+					 0, ts_len, 
+					 (const void**)&rtphdr, 
+					 &rtphdrlen);
+
+    }
+
+    if (status != PJ_SUCCESS) {
+	LOGERR_((stream->port.info.name.ptr, 
+		"RTP encode_rtp() error", status));
+	return status;
+    }
+
+    /* Check if now is the time to transmit RTCP SR/RR report. 
+     * We only do this when stream direction is not "decoding only", because
+     * when it is, check_tx_rtcp() will be handled by get_frame().
+     */
+    if (stream->dir != PJMEDIA_DIR_DECODING) {
+	check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts));
     }
 
@@ -450,13 +474,14 @@
     /* Ignore unknown event. */
     if (event->event > 15) {
-	PJ_LOG(5,(THIS_FILE, "Ignored RTP pkt with bad DTMF event %d",
-    			     event->event));
+	PJ_LOG(5,(stream->port.info.name.ptr, 
+		  "Ignored RTP pkt with bad DTMF event %d",
+    		  event->event));
 	return;
     }
 
     /* New event! */
-    PJ_LOG(5,(THIS_FILE, "Received DTMF digit %c, vol=%d",
-    			 digitmap[event->event],
-    			 (event->e_vol & 0x3F)));
+    PJ_LOG(5,(stream->port.info.name.ptr, "Received DTMF digit %c, vol=%d",
+    	      digitmap[event->event],
+    	      (event->e_vol & 0x3F)));
 
     stream->last_dtmf = event->event;
@@ -517,5 +542,5 @@
 					&hdr, &payload, &payloadlen);
 	if (status != PJ_SUCCESS) {
-	    TRACE_((THIS_FILE, "RTP decode error", status));
+	    LOGERR_((stream->port.info.name.ptr, "RTP decode error", status));
 	    goto read_next_packet;
 	}
@@ -537,15 +562,25 @@
 	 */
 	pjmedia_rtp_session_update(&channel->rtp, hdr, &seq_st);
-	if (seq_st.status.flag.bad) {
-	    TRC_  ((THIS_FILE, 
-		    "RTP session_update error: badpt=%d, dup=%d, outorder=%d, "
-		    "probation=%d, restart=%d", 
+	if (seq_st.status.value) {
+	    TRC_  ((stream->port.info.name.ptr, 
+		    "RTP status: badpt=%d, badssrc=%d, dup=%d, "
+		    "outorder=%d, probation=%d, restart=%d", 
 		    seq_st.status.flag.badpt,
+		    seq_st.status.flag.badssrc,
 		    seq_st.status.flag.dup,
 		    seq_st.status.flag.outorder,
 		    seq_st.status.flag.probation,
 		    seq_st.status.flag.restart));
+
+	    if (seq_st.status.flag.badpt) {
+		PJ_LOG(4,(stream->port.info.name.ptr,
+			  "Bad RTP pt %d (expecting %d)",
+			  hdr->pt, channel->rtp.out_pt));
+	    }
+	}
+
+	/* Skip bad RTP packet */
+	if (seq_st.status.flag.bad)
 	    goto read_next_packet;
-	}
 
 
@@ -564,5 +599,6 @@
 		stream->rtp_src_cnt = 0;
 
-		PJ_LOG(4,(THIS_FILE,"Remote RTP address switched to %s:%d",
+		PJ_LOG(4,(stream->port.info.name.ptr,
+			  "Remote RTP address switched to %s:%d",
 			  pj_inet_ntoa(stream->rtp_src_addr.sin_addr),
 			  pj_ntohs(stream->rtp_src_addr.sin_port)));
@@ -571,14 +607,34 @@
 
 
-	/* Put to jitter buffer. */
+
+	/* Put "good" packet to jitter buffer, or reset the jitter buffer
+	 * when RTP session is restarted.
+	 */
 	pj_mutex_lock( stream->jb_mutex );
-	status = pjmedia_jbuf_put_frame(stream->jb, payload, payloadlen, 
-					pj_ntohs(hdr->seq));
+	if (seq_st.status.flag.restart) {
+	    status = pjmedia_jbuf_reset(stream->jb);
+	    PJ_LOG(4,(stream->port.info.name.ptr, "Jitter buffer reset"));
+
+	} else {
+	    status = pjmedia_jbuf_put_frame(stream->jb, payload, payloadlen,
+					    pj_ntohs(hdr->seq));
+	}
 	pj_mutex_unlock( stream->jb_mutex );
 
+
+	/* Check if now is the time to transmit RTCP SR/RR report.
+	 * We only do this when stream direction is "decoding only", 
+	 * because otherwise check_tx_rtcp() will be handled by put_frame()
+	 */
+	if (stream->dir == PJMEDIA_DIR_DECODING) {
+	    check_tx_rtcp(stream, pj_ntohl(hdr->ts));
+	}
+
 	if (status != 0) {
-	    TRACE_((THIS_FILE, "Jitter buffer put() error", status));
+	    LOGERR_((stream->port.info.name.ptr, "Jitter buffer put() error", 
+		    status));
 	    goto read_next_packet;
 	}
+
 
 read_next_packet:
@@ -603,7 +659,8 @@
 
 	pj_strerror(status, errmsg, sizeof(errmsg));
-	PJ_LOG(4,(THIS_FILE, "Error reading RTP packet: %s [status=%d]. "
-			     "RTP stream thread quitting!",
-			     errmsg, status));
+	PJ_LOG(4,(stream->port.info.name.ptr, 
+		  "Error reading RTP packet: %s [status=%d]. "
+		  "RTP stream thread quitting!",
+		  errmsg, status));
     }
 }
@@ -644,6 +701,7 @@
 
 	pj_strerror(status, errmsg, sizeof(errmsg));
-	PJ_LOG(4,(THIS_FILE, "Error reading RTCP packet: %s [status=%d]",
-			     errmsg, status));
+	PJ_LOG(4,(stream->port.info.name.ptr, 
+		  "Error reading RTCP packet: %s [status=%d]",
+		  errmsg, status));
     }
 
@@ -743,6 +801,11 @@
     PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM);
 
+    /* Init stream/port name */
+    stream->port.info.name.ptr = pj_pool_alloc(pool, 24);
+    pj_ansi_sprintf(stream->port.info.name.ptr,
+		    "strm%p", stream);
+    stream->port.info.name.slen = pj_ansi_strlen(stream->port.info.name.ptr);
+
     /* Init port. */
-    stream->port.info.name = pj_str("stream");
     stream->port.info.signature = ('S'<<3 | 'T'<<2 | 'R'<<1 | 'M');
     stream->port.info.type = PJMEDIA_TYPE_AUDIO;
@@ -768,7 +831,11 @@
     stream->rem_rtcp_addr = stream->rem_rtp_addr;
     stream->rem_rtcp_addr.sin_port = pj_htons(rtcp_port);
-    stream->tx_event_pt = info->tx_event_pt;
-    stream->rx_event_pt = info->rx_event_pt;
+    stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL + (pj_rand() % 8000)) * 
+			    info->fmt.sample_rate / 1000;
+
+    stream->tx_event_pt = info->tx_event_pt ? info->tx_event_pt : -1;
+    stream->rx_event_pt = info->rx_event_pt ? info->rx_event_pt : -1;
     stream->last_dtmf = -1;
+
 
     /* Create mutex to protect jitter buffer: */
@@ -813,5 +880,6 @@
     /* Init RTCP session: */
 
-    pjmedia_rtcp_init(&stream->rtcp, info->fmt.sample_rate, 
+    pjmedia_rtcp_init(&stream->rtcp, stream->port.info.name.ptr,
+		      info->fmt.sample_rate, 
 		      stream->port.info.samples_per_frame, 
 		      info->ssrc);
@@ -820,5 +888,6 @@
     /* Create jitter buffer: */
 
-    status = pjmedia_jbuf_create(pool, stream->frame_size, 15, 100,
+    status = pjmedia_jbuf_create(pool, &stream->port.info.name,
+				 stream->frame_size, 15, 100,
 				 &stream->jb);
     if (status != PJ_SUCCESS)
@@ -883,4 +952,6 @@
     /* Success! */
     *p_stream = stream;
+
+    PJ_LOG(5,(THIS_FILE, "Stream %s created", stream->port.info.name.ptr));
     return PJ_SUCCESS;
 
@@ -958,7 +1029,7 @@
 	stream->enc->paused = 0;
 	//pjmedia_snd_stream_start(stream->enc->snd_stream);
-	PJ_LOG(4,(THIS_FILE, "Encoder stream started"));
+	PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream started"));
     } else {
-	PJ_LOG(4,(THIS_FILE, "Encoder stream paused"));
+	PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream paused"));
     }
 
@@ -966,7 +1037,7 @@
 	stream->dec->paused = 0;
 	//pjmedia_snd_stream_start(stream->dec->snd_stream);
-	PJ_LOG(4,(THIS_FILE, "Decoder stream started"));
+	PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream started"));
     } else {
-	PJ_LOG(4,(THIS_FILE, "Decoder stream paused"));
+	PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream paused"));
     }
 
@@ -998,10 +1069,10 @@
     if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {
 	stream->enc->paused = 1;
-	PJ_LOG(4,(THIS_FILE, "Encoder stream paused"));
+	PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream paused"));
     }
 
     if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {
 	stream->dec->paused = 1;
-	PJ_LOG(4,(THIS_FILE, "Decoder stream paused"));
+	PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream paused"));
     }
 
@@ -1020,10 +1091,10 @@
     if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {
 	stream->enc->paused = 1;
-	PJ_LOG(4,(THIS_FILE, "Encoder stream resumed"));
+	PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream resumed"));
     }
 
     if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {
 	stream->dec->paused = 1;
-	PJ_LOG(4,(THIS_FILE, "Decoder stream resumed"));
+	PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream resumed"));
     }
 
Index: pjproject/trunk/pjsip-apps/build/Samples-vc.mak
===================================================================
--- pjproject/trunk/pjsip-apps/build/Samples-vc.mak	(revision 407)
+++ pjproject/trunk/pjsip-apps/build/Samples-vc.mak	(revision 408)
@@ -41,5 +41,6 @@
 	  $(BINDIR)\confsample.exe $(BINDIR)\sndinfo.exe \
 	  $(BINDIR)\level.exe $(BINDIR)\recfile.exe  \
-	  $(BINDIR)\resampleplay.exe $(BINDIR)\siprtp.exe $(BINDIR)\stream.exe
+	  $(BINDIR)\resampleplay.exe $(BINDIR)\siprtp.exe \
+	  $(BINDIR)\streamutil.exe
 
 
Index: pjproject/trunk/pjsip-apps/build/Samples.mak
===================================================================
--- pjproject/trunk/pjsip-apps/build/Samples.mak	(revision 407)
+++ pjproject/trunk/pjsip-apps/build/Samples.mak	(revision 408)
@@ -40,5 +40,5 @@
 
 SAMPLES := simpleua playfile playsine confsample sndinfo level recfile resampleplay \
-	   siprtp stream
+	   siprtp streamutil
 
 EXES := $(foreach file, $(SAMPLES), $(BINDIR)/$(file)-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(HOST_EXE))
Index: pjproject/trunk/pjsip-apps/src/samples/debug.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/samples/debug.c	(revision 407)
+++ pjproject/trunk/pjsip-apps/src/samples/debug.c	(revision 408)
@@ -28,4 +28,4 @@
  *  #include "playfile.c"
  */
-#include "stream.c"
+#include "streamutil.c"
 
Index: pjproject/trunk/pjsip-apps/src/samples/siprtp.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/samples/siprtp.c	(revision 407)
+++ pjproject/trunk/pjsip-apps/src/samples/siprtp.c	(revision 408)
@@ -1247,5 +1247,5 @@
 			     pj_rand());
     pjmedia_rtp_session_init(&audio->in_sess, audio->si.fmt.pt, 0);
-    pjmedia_rtcp_init(&audio->rtcp, audio->clock_rate, 
+    pjmedia_rtcp_init(&audio->rtcp, "rtcp", audio->clock_rate, 
 		      audio->samples_per_frame, 0);
 
Index: pjproject/trunk/pjsip-apps/src/samples/stream.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/samples/stream.c	(revision 407)
+++ 	(revision )
@@ -1,681 +1,0 @@
-/* $Id$ */
-/* 
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
- */
-
-
-static const char *desc = 
- " stream.c								\n"
- "									\n"
- " PURPOSE:								\n"
- "  Demonstrate how to use pjmedia stream component to transmit/receive \n"
- "  RTP packets to/from sound device.		    			\n"
- "\n"
- "\n"
- " USAGE:								\n"
- "  stream [options]                                                    \n"
- "\n"
- "\n"
- " Options:\n"
- "  --codec=CODEC         Set the codec name. Valid codec names are:	\n"
- "                        pcma, pcmu, gsm, speexnb, speexwb, speexuwb.	\n"
- "                        (default: pcma).				\n"
- "  --local-port=PORT     Set local RTP port (default=4000)		\n"
- "  --remote=IP:PORT      Set the remote peer. If this option is set,	\n"
- "                        the program will transmit RTP audio to the	\n"
- "                        specified address. (default: recv only)	\n"
- "  --play-file=WAV       Send audio from the WAV file instead of from	\n"
- "                        the sound device.				\n"
-// "  --record-file=WAV     Record incoming audio to WAV file instead of	\n"
-// "                        playing it to sound device.			\n"
- "  --send-recv           Set stream direction to bidirectional.        \n"
- "  --send-only           Set stream direction to send only		\n"
- "  --recv-only           Set stream direction to recv only (default)   \n"
- "\n"
-;
-
-
-
-#include <pjlib.h>
-#include <pjlib-util.h>
-#include <pjmedia.h>
-#include <pjmedia-codec.h>
-
-#include <stdlib.h>	/* atoi() */
-#include <stdio.h>
-
-#include "util.h"
-
-
-#define THIS_FILE	"stream.c"
-
-
-struct codec
-{
-    char    *name;
-    char    *encoding_name;
-    int	     pt;
-    int	     clock_rate;
-} codec[] = 
-{
-    { "pcma",	  "pcma",  PJMEDIA_RTP_PT_PCMA, 8000 },
-    { "pcmu",	  "pcmu",  PJMEDIA_RTP_PT_PCMU, 8000 },
-    { "gsm",	  "gsm",   PJMEDIA_RTP_PT_GSM, 8000 },
-    { "speexnb",  "speex", 120, 8000 },
-    { "speexwb",  "speex", 121, 16000 },
-    { "speexuwb", "speex", 122, 32000 },
-};
-
-
-/* Prototype */
-static void print_stream_stat(pjmedia_stream *stream);
-
-
-/* 
- * Register all codecs. 
- */
-static pj_status_t init_codecs(pjmedia_endpt *med_endpt)
-{
-    pj_status_t status;
-
-    status = pjmedia_codec_g711_init(med_endpt);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
-    status = pjmedia_codec_gsm_init(med_endpt);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
-    status = pjmedia_codec_speex_init(med_endpt, 0, -1, -1);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
-    return PJ_SUCCESS;
-}
-
-
-/* 
- * Create stream based on the codec, dir, remote address, etc. 
- */
-static pj_status_t create_stream( pj_pool_t *pool,
-				  pjmedia_endpt *med_endpt,
-				  unsigned codec_index,
-				  pjmedia_dir dir,
-				  pj_uint16_t local_port,
-				  const pj_sockaddr_in *rem_addr,
-				  pjmedia_stream **p_stream )
-{
-    pjmedia_stream_info info;
-    pj_status_t status;
-
-
-    /* Reset stream info. */
-    pj_memset(&info, 0, sizeof(info));
-
-
-    /* Initialize stream info formats */
-    info.type = PJMEDIA_TYPE_AUDIO;
-    info.dir = dir;
-    info.fmt.encoding_name = pj_str(codec[codec_index].encoding_name);
-    info.fmt.type = PJMEDIA_TYPE_AUDIO;
-    info.fmt.sample_rate = codec[codec_index].clock_rate;
-    info.fmt.pt = codec[codec_index].pt;
-    info.tx_pt = codec[codec_index].pt;
-    info.ssrc = pj_rand();
-    
-
-    /* Copy remote address */
-    pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
-
-
-    /* Create RTP socket */
-    status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 
-			    &info.sock_info.rtp_sock);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
-
-    /* Bind RTP socket to local port */
-    info.sock_info.rtp_addr_name.sin_family = PJ_AF_INET;
-    info.sock_info.rtp_addr_name.sin_port = pj_htons(local_port);
-
-    status = pj_sock_bind(info.sock_info.rtp_sock, 
-			  &info.sock_info.rtp_addr_name,
-			  sizeof(pj_sockaddr_in));
-    if (status != PJ_SUCCESS) {
-	app_perror(THIS_FILE, "Unable to bind RTP socket", status);
-	pj_sock_close(info.sock_info.rtp_sock);
-	return status;
-    }
-
-
-    /* Create RTCP socket */
-    status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
-			    &info.sock_info.rtcp_sock);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
-
-    /* Bind RTP socket to local port + 1 */
-    ++local_port;
-    info.sock_info.rtcp_addr_name.sin_family = PJ_AF_INET;
-    info.sock_info.rtcp_addr_name.sin_port = pj_htons(local_port);
-
-    status = pj_sock_bind(info.sock_info.rtcp_sock, 
-			  &info.sock_info.rtcp_addr_name,
-			  sizeof(pj_sockaddr_in));
-    if (status != PJ_SUCCESS) {
-	app_perror(THIS_FILE, "Unable to bind RTCP socket", status);
-	pj_sock_close(info.sock_info.rtp_sock);
-	pj_sock_close(info.sock_info.rtcp_sock);
-	return status;
-    }
-
-
-    /* Now that the stream info is initialized, we can create the 
-     * stream.
-     */
-
-    status = pjmedia_stream_create( med_endpt, pool, &info, NULL, p_stream);
-
-    if (status != PJ_SUCCESS) {
-	app_perror(THIS_FILE, "Error creating stream", status);
-	pj_sock_close(info.sock_info.rtp_sock);
-	pj_sock_close(info.sock_info.rtcp_sock);
-	return status;
-    }
-
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * main()
- */
-int main(int argc, char *argv[])
-{
-    pj_caching_pool cp;
-    pjmedia_endpt *med_endpt;
-    pj_pool_t *pool;
-    pjmedia_port *rec_file_port = NULL, *play_file_port = NULL;
-    pjmedia_master_port *master_port = NULL;
-    pjmedia_snd_port *snd_port = NULL;
-    pjmedia_stream *stream = NULL;
-    pjmedia_port *stream_port;
-    char tmp[10];
-    pj_status_t status;
-
-
-    /* Default values */
-    int codec_index = 0;
-    pjmedia_dir dir = PJMEDIA_DIR_DECODING;
-    pj_sockaddr_in remote_addr;
-    pj_uint16_t local_port = 4000;
-    char *rec_file = NULL;
-    char *play_file = NULL;
-
-    enum {
-	OPT_CODEC	= 'c',
-	OPT_LOCAL_PORT	= 'p',
-	OPT_REMOTE	= 'r',
-	OPT_PLAY_FILE	= 'w',
-	OPT_RECORD_FILE	= 'R',
-	OPT_SEND_RECV	= 'b',
-	OPT_SEND_ONLY	= 's',
-	OPT_RECV_ONLY	= 'i',
-    };
-
-    struct pj_getopt_option long_options[] = {
-	{ "codec",	    1, 0, OPT_CODEC },
-	{ "local-port",	    1, 0, OPT_LOCAL_PORT },
-	{ "remote",	    1, 0, OPT_REMOTE },
-	{ "play-file",	    1, 0, OPT_PLAY_FILE },
-	{ "record-file",    1, 0, OPT_RECORD_FILE },
-	{ "send-recv",      0, 0, OPT_SEND_RECV },
-	{ "send-only",      0, 0, OPT_SEND_ONLY },
-	{ "recv-only",      0, 0, OPT_RECV_ONLY },
-	{ NULL, 0, 0, 0 },
-    };
-
-    int c;
-    int option_index;
-
-
-    pj_memset(&remote_addr, 0, sizeof(remote_addr));
-
-
-    /* init PJLIB : */
-    status = pj_init();
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
-
-
-    /* Parse arguments */
-    pj_optind = 0;
-    while((c=pj_getopt_long(argc,argv, "", long_options, &option_index))!=-1) {
-
-	switch (c) {
-	case OPT_CODEC:
-	    {
-		unsigned i;
-		for (i=0; i<PJ_ARRAY_SIZE(codec); ++i) {
-		    if (pj_ansi_stricmp(pj_optarg, codec[i].name)==0) {
-			break;
-		    }
-		}
-
-		if (i == PJ_ARRAY_SIZE(codec)) {
-		    printf("Error: unknown codec %s\n", pj_optarg);
-		    return 1;
-		}
-
-		codec_index = i;
-	    }
-	    break;
-
-	case OPT_LOCAL_PORT:
-	    local_port = (pj_uint16_t) atoi(pj_optarg);
-	    if (local_port < 1) {
-		printf("Error: invalid local port %s\n", pj_optarg);
-		return 1;
-	    }
-	    break;
-
-	case OPT_REMOTE:
-	    {
-		pj_str_t ip = pj_str(strtok(pj_optarg, ":"));
-		pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":"));
-
-		status = pj_sockaddr_in_init(&remote_addr, &ip, port);
-		if (status != PJ_SUCCESS) {
-		    app_perror(THIS_FILE, "Invalid remote address", status);
-		    return 1;
-		}
-	    }
-	    break;
-
-	case OPT_PLAY_FILE:
-	    play_file = pj_optarg;
-	    break;
-
-	case OPT_RECORD_FILE:
-	    rec_file = pj_optarg;
-	    break;
-
-	case OPT_SEND_RECV:
-	    dir = PJMEDIA_DIR_ENCODING_DECODING;
-	    break;
-
-	case OPT_SEND_ONLY:
-	    dir = PJMEDIA_DIR_ENCODING;
-	    break;
-
-	case OPT_RECV_ONLY:
-	    dir = PJMEDIA_DIR_DECODING;
-	    break;
-
-	default:
-	    printf("Invalid options %s\n", argv[pj_optind]);
-	    return 1;
-	}
-
-    }
-
-
-    /* Verify arguments. */
-    if (dir & PJMEDIA_DIR_ENCODING) {
-	if (remote_addr.sin_addr.s_addr == 0) {
-	    printf("Error: remote address must be set\n");
-	    return 1;
-	}
-    }
-
-    if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) {
-	printf("Direction is set to --send-only because of --play-file\n");
-	dir = PJMEDIA_DIR_ENCODING;
-    }
-
-
-    /* Must create a pool factory before we can allocate any memory. */
-    pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
-
-    /* 
-     * Initialize media endpoint.
-     * This will implicitly initialize PJMEDIA too.
-     */
-    status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
-
-    /* Create memory pool for application purpose */
-    pool = pj_pool_create( &cp.factory,	    /* pool factory	    */
-			   "app",	    /* pool name.	    */
-			   4000,	    /* init size	    */
-			   4000,	    /* increment size	    */
-			   NULL		    /* callback on error    */
-			   );
-
-
-    /* Register all supported codecs */
-    status = init_codecs(med_endpt);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
-
-
-    /* Create stream based on program arguments */
-    status = create_stream(pool, med_endpt, codec_index, dir, local_port, 
-			   &remote_addr, &stream);
-    if (status != PJ_SUCCESS)
-	goto on_exit;
-
-
-    /* Get the port interface of the stream */
-    status = pjmedia_stream_get_port( stream, &stream_port);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
-
-
-    if (play_file) {
-
-	status = pjmedia_file_player_port_create(pool, play_file, 0,
-						 -1, NULL, &play_file_port);
-	if (status != PJ_SUCCESS) {
-	    app_perror(THIS_FILE, "Unable to use file", status);
-	    goto on_exit;
-	}
-
-	status = pjmedia_master_port_create(pool, play_file_port, stream_port,
-					    0, &master_port);
-	if (status != PJ_SUCCESS) {
-	    app_perror(THIS_FILE, "Unable to create master port", status);
-	    goto on_exit;
-	}
-
-	status = pjmedia_master_port_start(master_port);
-	if (status != PJ_SUCCESS) {
-	    app_perror(THIS_FILE, "Error starting master port", status);
-	    goto on_exit;
-	}
-
-
-    } else {
-
-	/* Create sound device port. */
-	if (dir == PJMEDIA_DIR_ENCODING_DECODING)
-	    status = pjmedia_snd_port_create(pool, -1, -1, 
-					stream_port->info.sample_rate,
-					stream_port->info.channel_count,
-					stream_port->info.samples_per_frame,
-					stream_port->info.bits_per_sample,
-					0, &snd_port);
-	else if (dir == PJMEDIA_DIR_ENCODING)
-	    status = pjmedia_snd_port_create_rec(pool, -1, 
-					stream_port->info.sample_rate,
-					stream_port->info.channel_count,
-					stream_port->info.samples_per_frame,
-					stream_port->info.bits_per_sample,
-					0, &snd_port);
-	else
-	    status = pjmedia_snd_port_create_player(pool, -1, 
-					stream_port->info.sample_rate,
-					stream_port->info.channel_count,
-					stream_port->info.samples_per_frame,
-					stream_port->info.bits_per_sample,
-					0, &snd_port);
-
-
-	if (status != PJ_SUCCESS) {
-	    app_perror(THIS_FILE, "Unable to create sound port", status);
-	    goto on_exit;
-	}
-
-	/* Connect sound port to stream */
-	status = pjmedia_snd_port_connect( snd_port, stream_port );
-	PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
-
-    }
-
-
-    /* Done */
-
-    if (dir == PJMEDIA_DIR_DECODING)
-	printf("Stream is active, dir is recv-only, local port is %d\n",
-	       local_port);
-    else if (dir == PJMEDIA_DIR_ENCODING)
-	printf("Stream is active, dir is send-only, sending to %s:%d\n",
-	       pj_inet_ntoa(remote_addr.sin_addr),
-	       pj_ntohs(remote_addr.sin_port));
-    else
-	printf("Stream is active, send/recv, local port is %d, "
-	       "sending to %s:%d\n",
-	       local_port,
-	       pj_inet_ntoa(remote_addr.sin_addr),
-	       pj_ntohs(remote_addr.sin_port));
-
-
-    for (;;) {
-
-	puts("");
-	puts("Commands:");
-	puts("  s     Display media statistics");
-	puts("  q     Quit");
-	puts("");
-
-	printf("Command: "); fflush(stdout);
-
-	fgets(tmp, sizeof(tmp), stdin);
-
-	if (tmp[0] == 's')
-	    print_stream_stat(stream);
-	else if (tmp[0] == 'q')
-	    break;
-
-    }
-
-
-
-    /* Start deinitialization: */
-on_exit:
-
-    /* Destroy sound device */
-    if (snd_port) {
-	pjmedia_snd_port_destroy( snd_port );
-	PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
-    }
-
-    /* If there is master port, then we just need to destroy master port
-     * (it will recursively destroy upstream and downstream ports, which
-     * in this case are file_port and stream_port).
-     */
-    if (master_port) {
-	pjmedia_master_port_destroy(master_port);
-	play_file_port = NULL;
-	stream = NULL;
-    }
-
-    /* Destroy stream */
-    if (stream) {
-	pjmedia_stream_destroy(stream);
-    }
-
-    /* Destroy file ports */
-    if (play_file_port)
-	pjmedia_port_destroy( play_file_port );
-    if (rec_file_port)
-	pjmedia_port_destroy( rec_file_port );
-
-
-    /* Release application pool */
-    pj_pool_release( pool );
-
-    /* Destroy media endpoint. */
-    pjmedia_endpt_destroy( med_endpt );
-
-    /* Destroy pool factory */
-    pj_caching_pool_destroy( &cp );
-
-
-    return (status == PJ_SUCCESS) ? 0 : 1;
-}
-
-
-
-
-static const char *good_number(char *buf, pj_int32_t val)
-{
-    if (val < 1000) {
-	pj_ansi_sprintf(buf, "%d", val);
-    } else if (val < 1000000) {
-	pj_ansi_sprintf(buf, "%d.%dK", 
-			val / 1000,
-			(val % 1000) / 100);
-    } else {
-	pj_ansi_sprintf(buf, "%d.%02dM", 
-			val / 1000000,
-			(val % 1000000) / 10000);
-    }
-
-    return buf;
-}
-
-
-
-/*
- * Print stream statistics
- */
-static void print_stream_stat(pjmedia_stream *stream)
-{
-    char duration[80], last_update[80];
-    char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16];
-    pjmedia_port *port;
-    pjmedia_rtcp_stat stat;
-    pj_time_val now;
-
-
-    pj_gettimeofday(&now);
-    pjmedia_stream_get_stat(stream, &stat);
-    pjmedia_stream_get_port(stream, &port);
-
-    puts("Stream statistics:");
-
-    /* Print duration */
-    PJ_TIME_VAL_SUB(now, stat.start);
-    sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld",
-	    now.sec / 3600,
-	    (now.sec % 3600) / 60,
-	    (now.sec % 60),
-	    now.msec);
-
-
-    printf(" Info: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n",
-   	(int)port->info.encoding_name.slen,
-	port->info.encoding_name.ptr,
-	port->info.sample_rate,
-	port->info.samples_per_frame * 1000 / port->info.sample_rate,
-	good_number(bps, port->info.bytes_per_frame * port->info.sample_rate /
-		    port->info.sample_rate),
-	good_number(ipbps, (port->info.bytes_per_frame+32) * 
-			    port->info.sample_rate / port->info.sample_rate));
-
-    if (stat.rx.update_cnt == 0)
-	strcpy(last_update, "never");
-    else {
-	pj_gettimeofday(&now);
-	PJ_TIME_VAL_SUB(now, stat.rx.update);
-	sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
-		now.sec / 3600,
-		(now.sec % 3600) / 60,
-		now.sec % 60,
-		now.msec);
-    }
-
-    printf(" RX stat last update: %s\n"
-	   "    total %s packets %sB received (%sB +IP hdr)%s\n"
-	   "    pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
-	   "          (msec)    min     avg     max     last\n"
-	   "    loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
-	   "    jitter     : %7.3f %7.3f %7.3f %7.3f%s\n",
-	   last_update,
-	   good_number(packets, stat.rx.pkt),
-	   good_number(bytes, stat.rx.bytes),
-	   good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
-	   "",
-	   stat.rx.loss,
-	   stat.rx.loss * 100.0 / stat.rx.pkt,
-	   stat.rx.dup, 
-	   stat.rx.dup * 100.0 / stat.rx.pkt,
-	   stat.rx.reorder, 
-	   stat.rx.reorder * 100.0 / stat.rx.pkt,
-	   "",
-	   stat.rx.loss_period.min / 1000.0, 
-	   stat.rx.loss_period.avg / 1000.0, 
-	   stat.rx.loss_period.max / 1000.0,
-	   stat.rx.loss_period.last / 1000.0,
-	   "",
-	   stat.rx.jitter.min / 1000.0,
-	   stat.rx.jitter.avg / 1000.0,
-	   stat.rx.jitter.max / 1000.0,
-	   stat.rx.jitter.last / 1000.0,
-	   ""
-	   );
-
-
-    if (stat.tx.update_cnt == 0)
-	strcpy(last_update, "never");
-    else {
-	pj_gettimeofday(&now);
-	PJ_TIME_VAL_SUB(now, stat.tx.update);
-	sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
-		now.sec / 3600,
-		(now.sec % 3600) / 60,
-		now.sec % 60,
-		now.msec);
-    }
-
-    printf(" TX stat last update: %s\n"
-	   "    total %s packets %sB sent (%sB +IP hdr)%s\n"
-	   "    pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
-	   "          (msec)    min     avg     max     last\n"
-	   "    loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
-	   "    jitter     : %7.3f %7.3f %7.3f %7.3f%s\n",
-	   last_update,
-	   good_number(packets, stat.tx.pkt),
-	   good_number(bytes, stat.tx.bytes),
-	   good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
-	   "",
-	   stat.tx.loss,
-	   stat.tx.loss * 100.0 / stat.tx.pkt,
-	   stat.tx.dup, 
-	   stat.tx.dup * 100.0 / stat.tx.pkt,
-	   stat.tx.reorder, 
-	   stat.tx.reorder * 100.0 / stat.tx.pkt,
-	   "",
-	   stat.tx.loss_period.min / 1000.0, 
-	   stat.tx.loss_period.avg / 1000.0, 
-	   stat.tx.loss_period.max / 1000.0,
-	   stat.tx.loss_period.last / 1000.0,
-	   "",
-	   stat.tx.jitter.min / 1000.0,
-	   stat.tx.jitter.avg / 1000.0,
-	   stat.tx.jitter.max / 1000.0,
-	   stat.tx.jitter.last / 1000.0,
-	   ""
-	   );
-
-
-    printf(" RTT delay     : %7.3f %7.3f %7.3f %7.3f%s\n", 
-	   stat.rtt.min / 1000.0,
-	   stat.rtt.avg / 1000.0,
-	   stat.rtt.max / 1000.0,
-	   stat.rtt.last / 1000.0,
-	   ""
-	   );
-
-}
-
Index: pjproject/trunk/pjsip-apps/src/samples/streamutil.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/samples/streamutil.c	(revision 408)
+++ pjproject/trunk/pjsip-apps/src/samples/streamutil.c	(revision 408)
@@ -0,0 +1,681 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+
+static const char *desc = 
+ " streamutil								\n"
+ "									\n"
+ " PURPOSE:								\n"
+ "  Demonstrate how to use pjmedia stream component to transmit/receive \n"
+ "  RTP packets to/from sound device.		    			\n"
+ "\n"
+ "\n"
+ " USAGE:								\n"
+ "  streamutil [options]                                                \n"
+ "\n"
+ "\n"
+ " Options:\n"
+ "  --codec=CODEC         Set the codec name. Valid codec names are:	\n"
+ "                        pcma, pcmu, gsm, speexnb, speexwb, speexuwb.	\n"
+ "                        (default: pcma).				\n"
+ "  --local-port=PORT     Set local RTP port (default=4000)		\n"
+ "  --remote=IP:PORT      Set the remote peer. If this option is set,	\n"
+ "                        the program will transmit RTP audio to the	\n"
+ "                        specified address. (default: recv only)	\n"
+ "  --play-file=WAV       Send audio from the WAV file instead of from	\n"
+ "                        the sound device.				\n"
+// "  --record-file=WAV     Record incoming audio to WAV file instead of	\n"
+// "                        playing it to sound device.			\n"
+ "  --send-recv           Set stream direction to bidirectional.        \n"
+ "  --send-only           Set stream direction to send only		\n"
+ "  --recv-only           Set stream direction to recv only (default)   \n"
+ "\n"
+;
+
+
+
+#include <pjlib.h>
+#include <pjlib-util.h>
+#include <pjmedia.h>
+#include <pjmedia-codec.h>
+
+#include <stdlib.h>	/* atoi() */
+#include <stdio.h>
+
+#include "util.h"
+
+
+#define THIS_FILE	"stream.c"
+
+
+struct codec
+{
+    char    *name;
+    char    *encoding_name;
+    int	     pt;
+    int	     clock_rate;
+} codec[] = 
+{
+    { "pcma",	  "pcma",  PJMEDIA_RTP_PT_PCMA, 8000 },
+    { "pcmu",	  "pcmu",  PJMEDIA_RTP_PT_PCMU, 8000 },
+    { "gsm",	  "gsm",   PJMEDIA_RTP_PT_GSM, 8000 },
+    { "speexnb",  "speex", 120, 8000 },
+    { "speexwb",  "speex", 121, 16000 },
+    { "speexuwb", "speex", 122, 32000 },
+};
+
+
+/* Prototype */
+static void print_stream_stat(pjmedia_stream *stream);
+
+
+/* 
+ * Register all codecs. 
+ */
+static pj_status_t init_codecs(pjmedia_endpt *med_endpt)
+{
+    pj_status_t status;
+
+    status = pjmedia_codec_g711_init(med_endpt);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    status = pjmedia_codec_gsm_init(med_endpt);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    status = pjmedia_codec_speex_init(med_endpt, 0, -1, -1);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    return PJ_SUCCESS;
+}
+
+
+/* 
+ * Create stream based on the codec, dir, remote address, etc. 
+ */
+static pj_status_t create_stream( pj_pool_t *pool,
+				  pjmedia_endpt *med_endpt,
+				  unsigned codec_index,
+				  pjmedia_dir dir,
+				  pj_uint16_t local_port,
+				  const pj_sockaddr_in *rem_addr,
+				  pjmedia_stream **p_stream )
+{
+    pjmedia_stream_info info;
+    pj_status_t status;
+
+
+    /* Reset stream info. */
+    pj_memset(&info, 0, sizeof(info));
+
+
+    /* Initialize stream info formats */
+    info.type = PJMEDIA_TYPE_AUDIO;
+    info.dir = dir;
+    info.fmt.encoding_name = pj_str(codec[codec_index].encoding_name);
+    info.fmt.type = PJMEDIA_TYPE_AUDIO;
+    info.fmt.sample_rate = codec[codec_index].clock_rate;
+    info.fmt.pt = codec[codec_index].pt;
+    info.tx_pt = codec[codec_index].pt;
+    info.ssrc = pj_rand();
+    
+
+    /* Copy remote address */
+    pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
+
+
+    /* Create RTP socket */
+    status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 
+			    &info.sock_info.rtp_sock);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+
+    /* Bind RTP socket to local port */
+    info.sock_info.rtp_addr_name.sin_family = PJ_AF_INET;
+    info.sock_info.rtp_addr_name.sin_port = pj_htons(local_port);
+
+    status = pj_sock_bind(info.sock_info.rtp_sock, 
+			  &info.sock_info.rtp_addr_name,
+			  sizeof(pj_sockaddr_in));
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Unable to bind RTP socket", status);
+	pj_sock_close(info.sock_info.rtp_sock);
+	return status;
+    }
+
+
+    /* Create RTCP socket */
+    status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
+			    &info.sock_info.rtcp_sock);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+
+    /* Bind RTP socket to local port + 1 */
+    ++local_port;
+    info.sock_info.rtcp_addr_name.sin_family = PJ_AF_INET;
+    info.sock_info.rtcp_addr_name.sin_port = pj_htons(local_port);
+
+    status = pj_sock_bind(info.sock_info.rtcp_sock, 
+			  &info.sock_info.rtcp_addr_name,
+			  sizeof(pj_sockaddr_in));
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Unable to bind RTCP socket", status);
+	pj_sock_close(info.sock_info.rtp_sock);
+	pj_sock_close(info.sock_info.rtcp_sock);
+	return status;
+    }
+
+
+    /* Now that the stream info is initialized, we can create the 
+     * stream.
+     */
+
+    status = pjmedia_stream_create( med_endpt, pool, &info, NULL, p_stream);
+
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Error creating stream", status);
+	pj_sock_close(info.sock_info.rtp_sock);
+	pj_sock_close(info.sock_info.rtcp_sock);
+	return status;
+    }
+
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * main()
+ */
+int main(int argc, char *argv[])
+{
+    pj_caching_pool cp;
+    pjmedia_endpt *med_endpt;
+    pj_pool_t *pool;
+    pjmedia_port *rec_file_port = NULL, *play_file_port = NULL;
+    pjmedia_master_port *master_port = NULL;
+    pjmedia_snd_port *snd_port = NULL;
+    pjmedia_stream *stream = NULL;
+    pjmedia_port *stream_port;
+    char tmp[10];
+    pj_status_t status;
+
+
+    /* Default values */
+    int codec_index = 0;
+    pjmedia_dir dir = PJMEDIA_DIR_DECODING;
+    pj_sockaddr_in remote_addr;
+    pj_uint16_t local_port = 4000;
+    char *rec_file = NULL;
+    char *play_file = NULL;
+
+    enum {
+	OPT_CODEC	= 'c',
+	OPT_LOCAL_PORT	= 'p',
+	OPT_REMOTE	= 'r',
+	OPT_PLAY_FILE	= 'w',
+	OPT_RECORD_FILE	= 'R',
+	OPT_SEND_RECV	= 'b',
+	OPT_SEND_ONLY	= 's',
+	OPT_RECV_ONLY	= 'i',
+    };
+
+    struct pj_getopt_option long_options[] = {
+	{ "codec",	    1, 0, OPT_CODEC },
+	{ "local-port",	    1, 0, OPT_LOCAL_PORT },
+	{ "remote",	    1, 0, OPT_REMOTE },
+	{ "play-file",	    1, 0, OPT_PLAY_FILE },
+	{ "record-file",    1, 0, OPT_RECORD_FILE },
+	{ "send-recv",      0, 0, OPT_SEND_RECV },
+	{ "send-only",      0, 0, OPT_SEND_ONLY },
+	{ "recv-only",      0, 0, OPT_RECV_ONLY },
+	{ NULL, 0, 0, 0 },
+    };
+
+    int c;
+    int option_index;
+
+
+    pj_memset(&remote_addr, 0, sizeof(remote_addr));
+
+
+    /* init PJLIB : */
+    status = pj_init();
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+    /* Parse arguments */
+    pj_optind = 0;
+    while((c=pj_getopt_long(argc,argv, "", long_options, &option_index))!=-1) {
+
+	switch (c) {
+	case OPT_CODEC:
+	    {
+		unsigned i;
+		for (i=0; i<PJ_ARRAY_SIZE(codec); ++i) {
+		    if (pj_ansi_stricmp(pj_optarg, codec[i].name)==0) {
+			break;
+		    }
+		}
+
+		if (i == PJ_ARRAY_SIZE(codec)) {
+		    printf("Error: unknown codec %s\n", pj_optarg);
+		    return 1;
+		}
+
+		codec_index = i;
+	    }
+	    break;
+
+	case OPT_LOCAL_PORT:
+	    local_port = (pj_uint16_t) atoi(pj_optarg);
+	    if (local_port < 1) {
+		printf("Error: invalid local port %s\n", pj_optarg);
+		return 1;
+	    }
+	    break;
+
+	case OPT_REMOTE:
+	    {
+		pj_str_t ip = pj_str(strtok(pj_optarg, ":"));
+		pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":"));
+
+		status = pj_sockaddr_in_init(&remote_addr, &ip, port);
+		if (status != PJ_SUCCESS) {
+		    app_perror(THIS_FILE, "Invalid remote address", status);
+		    return 1;
+		}
+	    }
+	    break;
+
+	case OPT_PLAY_FILE:
+	    play_file = pj_optarg;
+	    break;
+
+	case OPT_RECORD_FILE:
+	    rec_file = pj_optarg;
+	    break;
+
+	case OPT_SEND_RECV:
+	    dir = PJMEDIA_DIR_ENCODING_DECODING;
+	    break;
+
+	case OPT_SEND_ONLY:
+	    dir = PJMEDIA_DIR_ENCODING;
+	    break;
+
+	case OPT_RECV_ONLY:
+	    dir = PJMEDIA_DIR_DECODING;
+	    break;
+
+	default:
+	    printf("Invalid options %s\n", argv[pj_optind]);
+	    return 1;
+	}
+
+    }
+
+
+    /* Verify arguments. */
+    if (dir & PJMEDIA_DIR_ENCODING) {
+	if (remote_addr.sin_addr.s_addr == 0) {
+	    printf("Error: remote address must be set\n");
+	    return 1;
+	}
+    }
+
+    if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) {
+	printf("Direction is set to --send-only because of --play-file\n");
+	dir = PJMEDIA_DIR_ENCODING;
+    }
+
+
+    /* Must create a pool factory before we can allocate any memory. */
+    pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+
+    /* 
+     * Initialize media endpoint.
+     * This will implicitly initialize PJMEDIA too.
+     */
+    status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+    /* Create memory pool for application purpose */
+    pool = pj_pool_create( &cp.factory,	    /* pool factory	    */
+			   "app",	    /* pool name.	    */
+			   4000,	    /* init size	    */
+			   4000,	    /* increment size	    */
+			   NULL		    /* callback on error    */
+			   );
+
+
+    /* Register all supported codecs */
+    status = init_codecs(med_endpt);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+    /* Create stream based on program arguments */
+    status = create_stream(pool, med_endpt, codec_index, dir, local_port, 
+			   &remote_addr, &stream);
+    if (status != PJ_SUCCESS)
+	goto on_exit;
+
+
+    /* Get the port interface of the stream */
+    status = pjmedia_stream_get_port( stream, &stream_port);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+    if (play_file) {
+
+	status = pjmedia_file_player_port_create(pool, play_file, 0,
+						 -1, NULL, &play_file_port);
+	if (status != PJ_SUCCESS) {
+	    app_perror(THIS_FILE, "Unable to use file", status);
+	    goto on_exit;
+	}
+
+	status = pjmedia_master_port_create(pool, play_file_port, stream_port,
+					    0, &master_port);
+	if (status != PJ_SUCCESS) {
+	    app_perror(THIS_FILE, "Unable to create master port", status);
+	    goto on_exit;
+	}
+
+	status = pjmedia_master_port_start(master_port);
+	if (status != PJ_SUCCESS) {
+	    app_perror(THIS_FILE, "Error starting master port", status);
+	    goto on_exit;
+	}
+
+
+    } else {
+
+	/* Create sound device port. */
+	if (dir == PJMEDIA_DIR_ENCODING_DECODING)
+	    status = pjmedia_snd_port_create(pool, -1, -1, 
+					stream_port->info.sample_rate,
+					stream_port->info.channel_count,
+					stream_port->info.samples_per_frame,
+					stream_port->info.bits_per_sample,
+					0, &snd_port);
+	else if (dir == PJMEDIA_DIR_ENCODING)
+	    status = pjmedia_snd_port_create_rec(pool, -1, 
+					stream_port->info.sample_rate,
+					stream_port->info.channel_count,
+					stream_port->info.samples_per_frame,
+					stream_port->info.bits_per_sample,
+					0, &snd_port);
+	else
+	    status = pjmedia_snd_port_create_player(pool, -1, 
+					stream_port->info.sample_rate,
+					stream_port->info.channel_count,
+					stream_port->info.samples_per_frame,
+					stream_port->info.bits_per_sample,
+					0, &snd_port);
+
+
+	if (status != PJ_SUCCESS) {
+	    app_perror(THIS_FILE, "Unable to create sound port", status);
+	    goto on_exit;
+	}
+
+	/* Connect sound port to stream */
+	status = pjmedia_snd_port_connect( snd_port, stream_port );
+	PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+    }
+
+
+    /* Done */
+
+    if (dir == PJMEDIA_DIR_DECODING)
+	printf("Stream is active, dir is recv-only, local port is %d\n",
+	       local_port);
+    else if (dir == PJMEDIA_DIR_ENCODING)
+	printf("Stream is active, dir is send-only, sending to %s:%d\n",
+	       pj_inet_ntoa(remote_addr.sin_addr),
+	       pj_ntohs(remote_addr.sin_port));
+    else
+	printf("Stream is active, send/recv, local port is %d, "
+	       "sending to %s:%d\n",
+	       local_port,
+	       pj_inet_ntoa(remote_addr.sin_addr),
+	       pj_ntohs(remote_addr.sin_port));
+
+
+    for (;;) {
+
+	puts("");
+	puts("Commands:");
+	puts("  s     Display media statistics");
+	puts("  q     Quit");
+	puts("");
+
+	printf("Command: "); fflush(stdout);
+
+	fgets(tmp, sizeof(tmp), stdin);
+
+	if (tmp[0] == 's')
+	    print_stream_stat(stream);
+	else if (tmp[0] == 'q')
+	    break;
+
+    }
+
+
+
+    /* Start deinitialization: */
+on_exit:
+
+    /* Destroy sound device */
+    if (snd_port) {
+	pjmedia_snd_port_destroy( snd_port );
+	PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+    }
+
+    /* If there is master port, then we just need to destroy master port
+     * (it will recursively destroy upstream and downstream ports, which
+     * in this case are file_port and stream_port).
+     */
+    if (master_port) {
+	pjmedia_master_port_destroy(master_port);
+	play_file_port = NULL;
+	stream = NULL;
+    }
+
+    /* Destroy stream */
+    if (stream) {
+	pjmedia_stream_destroy(stream);
+    }
+
+    /* Destroy file ports */
+    if (play_file_port)
+	pjmedia_port_destroy( play_file_port );
+    if (rec_file_port)
+	pjmedia_port_destroy( rec_file_port );
+
+
+    /* Release application pool */
+    pj_pool_release( pool );
+
+    /* Destroy media endpoint. */
+    pjmedia_endpt_destroy( med_endpt );
+
+    /* Destroy pool factory */
+    pj_caching_pool_destroy( &cp );
+
+
+    return (status == PJ_SUCCESS) ? 0 : 1;
+}
+
+
+
+
+static const char *good_number(char *buf, pj_int32_t val)
+{
+    if (val < 1000) {
+	pj_ansi_sprintf(buf, "%d", val);
+    } else if (val < 1000000) {
+	pj_ansi_sprintf(buf, "%d.%dK", 
+			val / 1000,
+			(val % 1000) / 100);
+    } else {
+	pj_ansi_sprintf(buf, "%d.%02dM", 
+			val / 1000000,
+			(val % 1000000) / 10000);
+    }
+
+    return buf;
+}
+
+
+
+/*
+ * Print stream statistics
+ */
+static void print_stream_stat(pjmedia_stream *stream)
+{
+    char duration[80], last_update[80];
+    char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16];
+    pjmedia_port *port;
+    pjmedia_rtcp_stat stat;
+    pj_time_val now;
+
+
+    pj_gettimeofday(&now);
+    pjmedia_stream_get_stat(stream, &stat);
+    pjmedia_stream_get_port(stream, &port);
+
+    puts("Stream statistics:");
+
+    /* Print duration */
+    PJ_TIME_VAL_SUB(now, stat.start);
+    sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld",
+	    now.sec / 3600,
+	    (now.sec % 3600) / 60,
+	    (now.sec % 60),
+	    now.msec);
+
+
+    printf(" Info: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n",
+   	(int)port->info.encoding_name.slen,
+	port->info.encoding_name.ptr,
+	port->info.sample_rate,
+	port->info.samples_per_frame * 1000 / port->info.sample_rate,
+	good_number(bps, port->info.bytes_per_frame * port->info.sample_rate /
+		    port->info.sample_rate),
+	good_number(ipbps, (port->info.bytes_per_frame+32) * 
+			    port->info.sample_rate / port->info.sample_rate));
+
+    if (stat.rx.update_cnt == 0)
+	strcpy(last_update, "never");
+    else {
+	pj_gettimeofday(&now);
+	PJ_TIME_VAL_SUB(now, stat.rx.update);
+	sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
+		now.sec / 3600,
+		(now.sec % 3600) / 60,
+		now.sec % 60,
+		now.msec);
+    }
+
+    printf(" RX stat last update: %s\n"
+	   "    total %s packets %sB received (%sB +IP hdr)%s\n"
+	   "    pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
+	   "          (msec)    min     avg     max     last\n"
+	   "    loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
+	   "    jitter     : %7.3f %7.3f %7.3f %7.3f%s\n",
+	   last_update,
+	   good_number(packets, stat.rx.pkt),
+	   good_number(bytes, stat.rx.bytes),
+	   good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
+	   "",
+	   stat.rx.loss,
+	   stat.rx.loss * 100.0 / stat.rx.pkt,
+	   stat.rx.dup, 
+	   stat.rx.dup * 100.0 / stat.rx.pkt,
+	   stat.rx.reorder, 
+	   stat.rx.reorder * 100.0 / stat.rx.pkt,
+	   "",
+	   stat.rx.loss_period.min / 1000.0, 
+	   stat.rx.loss_period.avg / 1000.0, 
+	   stat.rx.loss_period.max / 1000.0,
+	   stat.rx.loss_period.last / 1000.0,
+	   "",
+	   stat.rx.jitter.min / 1000.0,
+	   stat.rx.jitter.avg / 1000.0,
+	   stat.rx.jitter.max / 1000.0,
+	   stat.rx.jitter.last / 1000.0,
+	   ""
+	   );
+
+
+    if (stat.tx.update_cnt == 0)
+	strcpy(last_update, "never");
+    else {
+	pj_gettimeofday(&now);
+	PJ_TIME_VAL_SUB(now, stat.tx.update);
+	sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
+		now.sec / 3600,
+		(now.sec % 3600) / 60,
+		now.sec % 60,
+		now.msec);
+    }
+
+    printf(" TX stat last update: %s\n"
+	   "    total %s packets %sB sent (%sB +IP hdr)%s\n"
+	   "    pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
+	   "          (msec)    min     avg     max     last\n"
+	   "    loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
+	   "    jitter     : %7.3f %7.3f %7.3f %7.3f%s\n",
+	   last_update,
+	   good_number(packets, stat.tx.pkt),
+	   good_number(bytes, stat.tx.bytes),
+	   good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
+	   "",
+	   stat.tx.loss,
+	   stat.tx.loss * 100.0 / stat.tx.pkt,
+	   stat.tx.dup, 
+	   stat.tx.dup * 100.0 / stat.tx.pkt,
+	   stat.tx.reorder, 
+	   stat.tx.reorder * 100.0 / stat.tx.pkt,
+	   "",
+	   stat.tx.loss_period.min / 1000.0, 
+	   stat.tx.loss_period.avg / 1000.0, 
+	   stat.tx.loss_period.max / 1000.0,
+	   stat.tx.loss_period.last / 1000.0,
+	   "",
+	   stat.tx.jitter.min / 1000.0,
+	   stat.tx.jitter.avg / 1000.0,
+	   stat.tx.jitter.max / 1000.0,
+	   stat.tx.jitter.last / 1000.0,
+	   ""
+	   );
+
+
+    printf(" RTT delay     : %7.3f %7.3f %7.3f %7.3f%s\n", 
+	   stat.rtt.min / 1000.0,
+	   stat.rtt.avg / 1000.0,
+	   stat.rtt.max / 1000.0,
+	   stat.rtt.last / 1000.0,
+	   ""
+	   );
+
+}
+
