Index: pjproject/trunk/pjmedia/include/pjmedia/rtcp.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/rtcp.h	(revision 389)
+++ pjproject/trunk/pjmedia/include/pjmedia/rtcp.h	(revision 390)
@@ -147,4 +147,71 @@
 
 /**
+ * Unidirectional RTP stream statistics.
+ */
+struct pjmedia_rtcp_stream_stat
+{
+    pj_time_val	    update;	/**< Time of last update.		    */
+    unsigned	    update_cnt;	/**< Number of updates (to calculate avg)   */
+    pj_uint32_t	    pkt;	/**< Total number of packets		    */
+    pj_uint32_t	    bytes;	/**< Total number of payload/bytes	    */
+    unsigned	    discard;	/**< Number of discarded packets.	    */
+    unsigned	    loss;	/**< Number of packets lost		    */
+    unsigned	    reorder;	/**< Number of out of order packets	    */
+    unsigned	    dup;	/**< Number of duplicates packets	    */
+
+    struct {
+	unsigned    min;	/**< Minimum loss period (in usec)	    */
+	unsigned    avg;	/**< Average loss period (in usec)	    */
+	unsigned    max;	/**< Maximum loss period (in usec)	    */
+	unsigned    last;	/**< Last loss period (in usec)		    */
+    } loss_period;		/**< Lost period history.		    */
+
+    struct {
+	unsigned    burst:1;	/**< Burst/sequential packet lost detected  */
+    	unsigned    random:1;	/**< Random packet lost detected.	    */
+    } loss_type;		/**< Types of loss detected.		    */
+
+    struct {
+	unsigned    min;	/**< Minimum jitter (in usec)		    */
+	unsigned    avg;	/**< Average jitter (in usec)		    */
+	unsigned    max;	/**< Maximum jitter (in usec)		    */
+	unsigned    last;	/**< Last jitter (in usec)		    */
+    } jitter;			/**< Jitter history.			    */
+};
+
+
+/**
+ * @see pjmedia_rtcp_stream_stat
+ */
+typedef struct pjmedia_rtcp_stream_stat pjmedia_rtcp_stream_stat;
+
+
+
+/**
+ * Bidirectional RTP stream statistics.
+ */
+struct pjmedia_rtcp_stat
+{
+    pjmedia_rtcp_stream_stat	tx; /**< Encoder stream statistics.	    */
+    pjmedia_rtcp_stream_stat	rx; /**< Decoder stream statistics.	    */
+    
+    struct {
+	unsigned    min;	    /**< Minimum round-trip delay (in usec) */
+	unsigned    avg;	    /**< Average round-trip delay (in usec) */
+	unsigned    max;	    /**< Maximum round-trip delay (in usec) */
+	unsigned    last;	    /**< Last round-trip delay (in usec)    */
+    } rtt;			    /**< Round trip delay history.	    */
+
+    unsigned	    rtt_update_cnt; /**< Nb of times rtt is updated.	    */
+};
+
+
+/**
+ * @see pjmedia_rtcp_stat
+ */
+typedef struct pjmedia_rtcp_stat pjmedia_rtcp_stat;
+
+
+/**
  * RTCP session is used to monitor the RTP session of one endpoint. There
  * should only be one RTCP session for a bidirectional RTP streams.
@@ -157,4 +224,5 @@
 
     unsigned		    clock_rate;	/**< Clock rate of the stream	    */
+    unsigned		    pkt_size;	/**< Avg pkt size, in samples.	    */
     pj_uint32_t		    received;   /**< # pkt received		    */
     pj_uint32_t		    exp_prior;	/**< # pkt expected at last interval*/
@@ -167,5 +235,6 @@
     pj_timestamp	    rx_lsr_time;/**< Time when last SR is received  */
     pj_uint32_t		    peer_ssrc;	/**< Peer SSRC			    */
-    unsigned		    rtt_us;	/**< End-to-end delay, in usec.	    */
+    
+    pjmedia_rtcp_stat	    stat;	/**< Bidirectional stream stat.	    */
 };
 
@@ -179,9 +248,12 @@
  * Initialize RTCP session.
  *
- * @param session   The session
- * @param ssrc	    The SSRC used in to identify the session.
+ * @param session	    The session
+ * @param clock_rate	    Codec clock rate in samples per second.
+ * @param samples_per_frame Average number of samples per frame.
+ * @param ssrc		    The SSRC used in to identify the session.
  */
 PJ_DECL(void) pjmedia_rtcp_init( pjmedia_rtcp_session *session, 
 				 unsigned clock_rate,
+				 unsigned samples_per_frame,
 				 pj_uint32_t ssrc );
 
@@ -202,8 +274,10 @@
  * @param seq	    The RTP packet sequence number, in host byte order.
  * @param ts	    The RTP packet timestamp, in host byte order.
+ * @param payload   Size of the payload.
  */
 PJ_DECL(void) pjmedia_rtcp_rx_rtp( pjmedia_rtcp_session *session, 
-				   pj_uint16_t seq, 
-				   pj_uint32_t ts );
+				   unsigned seq, 
+				   unsigned ts,
+				   unsigned payload);
 
 
@@ -217,5 +291,5 @@
  */
 PJ_DECL(void) pjmedia_rtcp_tx_rtp( pjmedia_rtcp_session *session, 
-				   pj_uint16_t ptsize );
+				   unsigned ptsize );
 
 
Index: pjproject/trunk/pjmedia/include/pjmedia/rtp.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/rtp.h	(revision 389)
+++ pjproject/trunk/pjmedia/include/pjmedia/rtp.h	(revision 390)
@@ -181,4 +181,46 @@
 
 /**
+ * This structure is used to receive additional information about the
+ * state of incoming RTP packet.
+ */
+struct pjmedia_rtp_status
+{
+    union {
+	struct flag {
+	    int	bad:1;	    /**< General flag to indicate that sequence is
+				 bad, and application should not process
+				 this packet. More information will be given
+				 in other flags.			    */
+	    int badpt:1;    /**< Bad payload type.			    */
+	    int	dup:1;	    /**< Indicates duplicate packet		    */
+	    int	outorder:1; /**< Indicates out of order packet		    */
+	    int	probation:1;/**< Indicates that session is in probation
+				 until more packets are received.	    */
+	    int	restart:1;  /**< Indicates that sequence number has made
+				 a large jump, and internal base sequence
+				 number has been adjusted.		    */
+	} flag;		    /**< Status flags.				    */
+
+	pj_uint16_t value;  /**< Status value, to conveniently address all
+				 flags.					    */
+
+    } status;		    /**< Status information union.		    */
+
+    pj_uint16_t	diff;	    /**< Sequence number difference from previous
+				 packet. Normally the value should be 1.    
+				 Value greater than one may indicate packet
+				 loss. If packet with lower sequence is
+				 received, the value will be set to zero.
+				 If base sequence has been restarted, the
+				 value will be one.			    */
+};
+
+/**
+ * @see pjmedia_rtp_status
+ */
+typedef struct pjmedia_rtp_status pjmedia_rtp_status;
+
+
+/**
  * This function will initialize the RTP session according to given parameters.
  *
@@ -242,10 +284,10 @@
  * @param ses	    The session.
  * @param hdr	    The RTP header of the incoming packet.
- *
- * @return	    PJ_SUCCESS if the packet is valid and can be processed, 
- *		    otherwise will return the appropriate status code.
- */
-PJ_DECL(pj_status_t) pjmedia_rtp_session_update( pjmedia_rtp_session *ses, 
-						 const pjmedia_rtp_hdr *hdr);
+ * @param seq_st    Optional structure to receive the status of the RTP packet
+ *		    processing.
+ */
+PJ_DECL(void) pjmedia_rtp_session_update( pjmedia_rtp_session *ses, 
+					  const pjmedia_rtp_hdr *hdr,
+					  pjmedia_rtp_status *seq_st);
 
 
@@ -266,23 +308,14 @@
 
 /** 
- * Internal function to restart the sequence number control, shared by RTCP 
- * implementation. 
- *
- * @param seq_ctrl  The sequence control instance.
- * @param seq	    Sequence number to restart.
- */
-void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *seq_ctrl, 
-			     pj_uint16_t seq);
-
-/** 
  * Internal function update sequence control, shared by RTCP implementation.
  *
- * @param seq_ctrl  The sequence control instance.
- * @param seq	    Sequence number to update.
- *
- * @return	    PJ_SUCCESS if the sequence number can be accepted.
- */
-pj_status_t pjmedia_rtp_seq_update(pjmedia_rtp_seq_session *seq_ctrl, 
-				   pj_uint16_t seq);
+ * @param seq_ctrl	The sequence control instance.
+ * @param seq		Sequence number to update.
+ * @param seq_status	Optional structure to receive additional information 
+ *			about the packet.
+ */
+void pjmedia_rtp_seq_update( pjmedia_rtp_seq_session *seq_ctrl, 
+			     pj_uint16_t seq,
+			     pjmedia_rtp_status *seq_status);
 
 /**
Index: pjproject/trunk/pjmedia/include/pjmedia/session.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/session.h	(revision 389)
+++ pjproject/trunk/pjmedia/include/pjmedia/session.h	(revision 390)
@@ -218,5 +218,5 @@
 PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat(pjmedia_session *session,
 						     unsigned index,
-						     pjmedia_stream_stat *sta);
+						     pjmedia_rtcp_stat *stat);
 
 /**
Index: pjproject/trunk/pjmedia/include/pjmedia/stream.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/stream.h	(revision 389)
+++ pjproject/trunk/pjmedia/include/pjmedia/stream.h	(revision 390)
@@ -30,4 +30,5 @@
 #include <pjmedia/endpoint.h>
 #include <pjmedia/port.h>
+#include <pjmedia/rtcp.h>
 #include <pj/sock.h>
 
@@ -83,25 +84,4 @@
 
 /**
- * Individual channel statistic.
- */
-struct pjmedia_channel_stat
-{
-    pj_uint32_t pkt;	    /**< Total number of packets.		    */
-    pj_uint32_t bytes;	    /**< Total number of bytes, including RTP hdr.  */
-    pj_uint32_t lost;	    /**< Total number of packet lost		    */
-};
-
-/**
- * Stream statistic.
- */
-struct pjmedia_stream_stat
-{
-    pjmedia_channel_stat    enc;    /**< Encoder statistics.		    */
-    pjmedia_channel_stat    dec;    /**< Decoder statistics.		    */
-};
-
-
-
-/**
  * Create a media stream based on the specified stream parameter.
  * All channels in the stream initially will be inactive.
@@ -165,5 +145,5 @@
  */
 PJ_DECL(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream,
-					      pjmedia_stream_stat *stat);
+					      pjmedia_rtcp_stat *stat);
 
 /**
Index: pjproject/trunk/pjmedia/src/pjmedia/rtcp.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/rtcp.c	(revision 389)
+++ pjproject/trunk/pjmedia/src/pjmedia/rtcp.c	(revision 390)
@@ -44,5 +44,5 @@
  * Get NTP time.
  */
-static void rtcp_get_ntp_time(const pjmedia_rtcp_session *s, 
+static void rtcp_get_ntp_time(const pjmedia_rtcp_session *sess, 
 			      struct pjmedia_rtcp_ntp_rec *ntp)
 {
@@ -57,6 +57,6 @@
     
     /* Calculate second fractions */
-    ts.u64 %= s->ts_freq.u64;
-    ts.u64 = (ts.u64 << 32) / s->ts_freq.u64;
+    ts.u64 %= sess->ts_freq.u64;
+    ts.u64 = (ts.u64 << 32) / sess->ts_freq.u64;
 
     /* Fill up the low 32bit part */
@@ -65,18 +65,20 @@
 
 
-PJ_DEF(void) pjmedia_rtcp_init(pjmedia_rtcp_session *s, 
+PJ_DEF(void) pjmedia_rtcp_init(pjmedia_rtcp_session *sess, 
 			       unsigned clock_rate,
+			       unsigned samples_per_frame,
 			       pj_uint32_t ssrc)
 {
-    pjmedia_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
+    pjmedia_rtcp_pkt *rtcp_pkt = &sess->rtcp_pkt;
     
     pj_memset(rtcp_pkt, 0, sizeof(pjmedia_rtcp_pkt));
     
     /* Set clock rate */
-    s->clock_rate = clock_rate;
+    sess->clock_rate = clock_rate;
+    sess->pkt_size = samples_per_frame;
 
     /* Init time */
-    s->rx_lsr = 0;
-    s->rx_lsr_time.u64 = 0;
+    sess->rx_lsr = 0;
+    sess->rx_lsr_time.u64 = 0;
     
     /* Init common RTCP header */
@@ -90,5 +92,5 @@
     
     /* Get timestamp frequency */
-    pj_get_timestamp_freq(&s->ts_freq);
+    pj_get_timestamp_freq(&sess->ts_freq);
 
     /* RR will be initialized on receipt of the first RTP packet. */
@@ -96,41 +98,54 @@
 
 
-PJ_DEF(void) pjmedia_rtcp_fini(pjmedia_rtcp_session *session)
+PJ_DEF(void) pjmedia_rtcp_fini(pjmedia_rtcp_session *sess)
 {
     /* Nothing to do. */
-    PJ_UNUSED_ARG(session);
-}
-
-static void rtcp_init_seq(pjmedia_rtcp_session *s, pj_uint16_t  seq)
-{
-    s->received = 0;
-    s->exp_prior = 0;
-    s->rx_prior = 0;
-    s->transit = 0;
-    s->jitter = 0;
-
-    pjmedia_rtp_seq_restart(&s->seq_ctrl, seq);
-}
-
-PJ_DEF(void) pjmedia_rtcp_rx_rtp(pjmedia_rtcp_session *s, 
-				 pj_uint16_t seq, 
-				 pj_uint32_t rtp_ts)
+    PJ_UNUSED_ARG(sess);
+}
+
+static void rtcp_init_seq(pjmedia_rtcp_session *sess)
+{
+    sess->received = 0;
+    sess->exp_prior = 0;
+    sess->rx_prior = 0;
+    sess->transit = 0;
+    sess->jitter = 0;
+}
+
+PJ_DEF(void) pjmedia_rtcp_rx_rtp(pjmedia_rtcp_session *sess, 
+				 unsigned seq, 
+				 unsigned rtp_ts,
+				 unsigned payload)
 {   
     pj_timestamp ts;
     pj_uint32_t arrival;
     pj_int32_t transit;
-    int status;
-
-    /* Update sequence numbers (received, lost, etc). */
-    status = pjmedia_rtp_seq_update(&s->seq_ctrl, seq);
-    if (status == PJMEDIA_RTP_ESESSRESTART) {
-	rtcp_init_seq(s, seq);
-	status = 0;
-    }
-    
-    if (status != 0)
+    pjmedia_rtp_status seq_st;
+    unsigned last_seq;
+
+    sess->stat.rx.pkt++;
+    sess->stat.rx.bytes += payload;
+
+    /* Update sequence numbers. */
+    last_seq = sess->seq_ctrl.max_seq;
+    pjmedia_rtp_seq_update(&sess->seq_ctrl, (pj_uint16_t)seq, &seq_st);
+    if (seq_st.status.flag.restart) {
+	rtcp_init_seq(sess);
+    }
+    
+    if (seq_st.status.flag.dup)
+	sess->stat.rx.dup++;
+    if (seq_st.status.flag.outorder)
+	sess->stat.rx.reorder++;
+
+    if (seq_st.status.flag.bad) {
+	sess->stat.rx.discard++;
 	return;
-
-    ++s->received;
+    }
+
+
+    /* Only mark "good" packets */
+    ++sess->received;
+
 
     /*
@@ -140,39 +155,53 @@
     /* Get arrival time and convert timestamp to samples */
     pj_get_timestamp(&ts);
-    ts.u64 = ts.u64 * s->clock_rate / s->ts_freq.u64;
+    ts.u64 = ts.u64 * sess->clock_rate / sess->ts_freq.u64;
     arrival = ts.u32.lo;
 
     transit = arrival - rtp_ts;
     
-    if (s->transit == 0) {
-	s->transit = transit;
+    /* 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;
-	
-	d = transit - s->transit;
-	s->transit = transit;
+	pj_uint32_t jitter;
+	
+	d = transit - sess->transit;
+	sess->transit = transit;
 	if (d < 0) 
 	    d = -d;
 	
-	s->jitter += d - ((s->jitter + 8) >> 4);
-    }
-}
-
-PJ_DEF(void) pjmedia_rtcp_tx_rtp(pjmedia_rtcp_session *s, 
-				 pj_uint16_t  bytes_payload_size)
-{
-    pjmedia_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
-
-    /* Update number of packets */
-    rtcp_pkt->sr.sender_pcount = 
-	pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_pcount) + 1);
-
-    /* Update number of bytes */
-    rtcp_pkt->sr.sender_bcount = 
-	pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_bcount) + bytes_payload_size );
-}
-
-
-PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *session,
+	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;
+    }
+}
+
+PJ_DEF(void) pjmedia_rtcp_tx_rtp(pjmedia_rtcp_session *sess, 
+				 unsigned bytes_payload_size)
+{
+    /* Update statistics */
+    sess->stat.tx.pkt++;
+    sess->stat.tx.bytes += bytes_payload_size;
+}
+
+
+PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *sess,
 				   const void *pkt,
 				   pj_size_t size)
@@ -184,17 +213,75 @@
 
     /* Save LSR from NTP timestamp of RTCP packet */
-    session->rx_lsr = ((pj_ntohl(rtcp->sr.ntp_sec) & 0x0000FFFF) << 16) | 
-			 ((pj_ntohl(rtcp->sr.ntp_frac) >> 16) & 0xFFFF);
+    sess->rx_lsr = ((pj_ntohl(rtcp->sr.ntp_sec) & 0x0000FFFF) << 16) | 
+		   ((pj_ntohl(rtcp->sr.ntp_frac) >> 16) & 0xFFFF);
 
     /* Calculate SR arrival time for DLSR */
-    pj_get_timestamp(&session->rx_lsr_time);
+    pj_get_timestamp(&sess->rx_lsr_time);
 
     TRACE_((THIS_FILE, "Rx RTCP SR: ntp-ts=%p, time=%p", 
-	    session->rx_lsr,
-	    (pj_uint32_t)(session->rx_lsr_time.u64*65536/session->ts_freq.u64)));
+	    sess->rx_lsr,
+	    (pj_uint32_t)(sess->rx_lsr_time.u64*65536/sess->ts_freq.u64)));
 
     /* Calculate RTT if it has RR */
     if (size >= sizeof(pjmedia_rtcp_pkt)) {
 	
+	pj_uint32_t last_loss, jitter_samp, jitter;
+
+	last_loss = sess->stat.tx.loss;
+
+	/* Get packet loss */
+	sess->stat.tx.loss = (rtcp->rr.total_lost_2 << 16) +
+			     (rtcp->rr.total_lost_1 << 8) +
+			      rtcp->rr.total_lost_0;
+	
+	/* We can't calculate the exact loss period for TX, so just give the
+	 * best estimation.
+	 */
+	if (sess->stat.tx.loss > last_loss) {
+	    unsigned period;
+
+	    /* Loss period in msec */
+	    period = (sess->stat.tx.loss - last_loss) * sess->pkt_size *
+		     1000 / sess->clock_rate;
+
+	    /* Loss period in usec */
+	    period *= 1000;
+
+	    if (sess->stat.tx.update_cnt==0||sess->stat.tx.loss_period.min==0)
+		sess->stat.tx.loss_period.min = period;
+	    if (period < sess->stat.tx.loss_period.min)
+		sess->stat.tx.loss_period.min = period;
+	    if (period > sess->stat.tx.loss_period.max)
+		sess->stat.tx.loss_period.max = period;
+
+	    sess->stat.tx.loss_period.avg = 
+		(sess->stat.tx.loss_period.avg*sess->stat.tx.update_cnt+period)
+		/ (sess->stat.tx.update_cnt + 1);
+	    sess->stat.tx.loss_period.last = period;
+	}
+
+	/* Get jitter value in usec */
+	jitter_samp = pj_ntohl(rtcp->rr.jitter);
+	/* Calculate jitter in usec, avoiding overflows */
+	if (jitter_samp <= 4294)
+	    jitter = jitter_samp * 1000000 / sess->clock_rate;
+	else {
+	    jitter = jitter_samp * 1000 / sess->clock_rate;
+	    jitter *= 1000;
+	}
+
+	/* Update jitter statistics */
+	if (sess->stat.tx.update_cnt == 0)
+	    sess->stat.tx.jitter.min = jitter;
+	if (jitter < sess->stat.tx.jitter.min && jitter)
+	    sess->stat.tx.jitter.min = jitter;
+	if (jitter > sess->stat.tx.jitter.max)
+	    sess->stat.tx.jitter.max = jitter;
+	sess->stat.tx.jitter.avg = 
+	    (sess->stat.tx.jitter.avg * sess->stat.tx.update_cnt + jitter) /
+	    (sess->stat.tx.update_cnt + 1);
+	sess->stat.tx.jitter.last = jitter;
+
+
 	/* Can only calculate if LSR and DLSR is present in RR */
 	if (rtcp->rr.lsr && rtcp->rr.dlsr) {
@@ -212,5 +299,5 @@
 
 	    /* Get current time, and convert to 1/65536 resolution */
-	    rtcp_get_ntp_time(session, &ntp);
+	    rtcp_get_ntp_time(sess, &ntp);
 	    now = ((ntp.hi & 0xFFFF) << 16) + 
 		  (ntp.lo >> 16);
@@ -221,5 +308,5 @@
 	    /* Convert end to end delay to usec (keeping the calculation in
              * 64bit space)::
-	     *   session->ee_delay = (eedelay * 1000) / 65536;
+	     *   sess->ee_delay = (eedelay * 1000) / 65536;
 	     */
 	    eedelay = (eedelay * 1000000) >> 16;
@@ -234,46 +321,88 @@
 	     */
 	    if (now-dlsr >= lsr) {
-		session->rtt_us = (pj_uint32_t)eedelay;
+		unsigned rtt = (pj_uint32_t)eedelay;
+		
+		if (sess->stat.rtt_update_cnt == 0)
+		    sess->stat.rtt.min = rtt;
+
+		if (rtt < sess->stat.rtt.min && rtt)
+		    sess->stat.rtt.min = rtt;
+		if (rtt > sess->stat.rtt.max)
+		    sess->stat.rtt.max = rtt;
+
+		sess->stat.rtt.avg = 
+		    (sess->stat.rtt.avg * sess->stat.rtt_update_cnt + rtt) / 
+		    (sess->stat.rtt_update_cnt + 1);
+
+		sess->stat.rtt.last = rtt;
+		sess->stat.rtt_update_cnt++;
+
 	    } else {
-		PJ_LOG(3, (THIS_FILE, "Internal NTP clock skew detected"));
+		PJ_LOG(3, (THIS_FILE, "Internal NTP clock skew detected: "
+				       "lsr=%p, now=%p, dlsr=%p (%d:%03dms)",
+				       lsr, now, dlsr, dlsr/65536,
+				       (dlsr%65536)*1000/65536));
 	    }
 	}
-    }
-}
-
-
-static void rtcp_build_rtcp(pjmedia_rtcp_session *s, 
-			    pj_uint32_t receiver_ssrc)
-{   
-    pj_uint32_t expected;
-    pj_uint32_t u32;
-    pj_uint32_t expected_interval, received_interval, lost_interval;
-    pjmedia_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
+
+	pj_gettimeofday(&sess->stat.tx.update);
+	sess->stat.tx.update_cnt++;
+    }
+}
+
+
+PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *sess, 
+				     pjmedia_rtcp_pkt **ret_p_pkt, 
+				     int *len)
+{
+    pj_uint32_t expected, expected_interval, received_interval, lost_interval;
+    pj_uint32_t jitter_samp, jitter;
+    pjmedia_rtcp_pkt *rtcp_pkt = &sess->rtcp_pkt;
+    pjmedia_rtcp_ntp_rec ntp;
+    
+    /* Packet count */
+    rtcp_pkt->sr.sender_pcount = pj_htonl(sess->stat.tx.pkt);
+
+    /* Octets count */
+    rtcp_pkt->sr.sender_bcount = pj_htonl(sess->stat.tx.bytes);
     
     /* SSRC and last_seq */
-    rtcp_pkt->rr.ssrc = pj_htonl(receiver_ssrc);
-    rtcp_pkt->rr.last_seq = (s->seq_ctrl.cycles & 0xFFFF0000L);
-    rtcp_pkt->rr.last_seq += s->seq_ctrl.max_seq;
+    rtcp_pkt->rr.ssrc = pj_htonl(sess->peer_ssrc);
+    rtcp_pkt->rr.last_seq = (sess->seq_ctrl.cycles & 0xFFFF0000L);
+    rtcp_pkt->rr.last_seq += sess->seq_ctrl.max_seq;
     rtcp_pkt->rr.last_seq = pj_htonl(rtcp_pkt->rr.last_seq);
 
+
     /* Jitter */
-    rtcp_pkt->rr.jitter = pj_htonl(s->jitter >> 4);
+    jitter_samp = (sess->jitter >> 4);
+    rtcp_pkt->rr.jitter = pj_htonl(jitter_samp);
+    
+    /* Calculate jitter in usec, avoiding overflows */
+    if (jitter_samp <= 4294)
+	jitter = jitter_samp * 1000000 / sess->clock_rate;
+    else {
+	jitter = jitter_samp * 1000 / sess->clock_rate;
+	jitter *= 1000;
+    }
+
+    /* Update jitter statistics */
+    sess->stat.rx.jitter.avg = 
+	(sess->stat.rx.jitter.avg * sess->stat.rx.update_cnt + jitter) /
+	(sess->stat.rx.update_cnt + 1);
     
     /* Total lost. */
-    expected = pj_ntohl(rtcp_pkt->rr.last_seq) - s->seq_ctrl.base_seq;
-    if (expected >= s->received)
-	u32 = expected - s->received;
-    else
-	u32 = 0;
-    rtcp_pkt->rr.total_lost_2 = (u32 >> 16) & 0x00FF;
-    rtcp_pkt->rr.total_lost_1 = (u32 >> 8) & 0x00FF;
-    rtcp_pkt->rr.total_lost_0 = u32 & 0x00FF;
+    expected = pj_ntohl(rtcp_pkt->rr.last_seq) - sess->seq_ctrl.base_seq;
+    if (expected >= sess->received)
+	sess->stat.rx.loss = expected - sess->received;
+    rtcp_pkt->rr.total_lost_2 = (sess->stat.rx.loss >> 16) & 0xFF;
+    rtcp_pkt->rr.total_lost_1 = (sess->stat.rx.loss >> 8) & 0xFF;
+    rtcp_pkt->rr.total_lost_0 = (sess->stat.rx.loss & 0xFF);
 
     /* Fraction lost calculation */
-    expected_interval = expected - s->exp_prior;
-    s->exp_prior = expected;
-    
-    received_interval = s->received - s->rx_prior;
-    s->rx_prior = s->received;
+    expected_interval = expected - sess->exp_prior;
+    sess->exp_prior = expected;
+    
+    received_interval = sess->received - sess->rx_prior;
+    sess->rx_prior = sess->received;
     
     lost_interval = expected_interval - received_interval;
@@ -284,17 +413,7 @@
 	rtcp_pkt->rr.fract_lost = (lost_interval << 8) / expected_interval;
     }
-}
-
-PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *session, 
-				     pjmedia_rtcp_pkt **ret_p_pkt, 
-				     int *len)
-{
-    pjmedia_rtcp_pkt *rtcp_pkt = &session->rtcp_pkt;
-    pjmedia_rtcp_ntp_rec ntp;
-    
-    rtcp_build_rtcp(session, session->peer_ssrc);
     
     /* Get current NTP time. */
-    rtcp_get_ntp_time(session, &ntp);
+    rtcp_get_ntp_time(sess, &ntp);
     
     /* Fill in NTP timestamp in SR. */
@@ -302,15 +421,15 @@
     rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo);
     
-    if (session->rx_lsr_time.u64 == 0 || session->rx_lsr == 0) {
+    if (sess->rx_lsr_time.u64 == 0 || sess->rx_lsr == 0) {
 	rtcp_pkt->rr.lsr = 0;
 	rtcp_pkt->rr.dlsr = 0;
     } else {
 	pj_timestamp ts;
-	pj_uint32_t lsr = session->rx_lsr;
-	pj_uint64_t lsr_time = session->rx_lsr_time.u64;
+	pj_uint32_t lsr = sess->rx_lsr;
+	pj_uint64_t lsr_time = sess->rx_lsr_time.u64;
 	pj_uint32_t dlsr;
 	
 	/* Convert LSR time to 1/65536 seconds resolution */
-	lsr_time = (lsr_time << 16) / session->ts_freq.u64;
+	lsr_time = (lsr_time << 16) / sess->ts_freq.u64;
 
 	/* Fill in LSR.
@@ -325,5 +444,5 @@
 
 	/* Convert interval to 1/65536 seconds value */
-	ts.u64 = (ts.u64 << 16) / session->ts_freq.u64;
+	ts.u64 = (ts.u64 << 16) / sess->ts_freq.u64;
 
 	/* Get DLSR */
@@ -341,4 +460,8 @@
     }
     
+    /* Update counter */
+    pj_gettimeofday(&sess->stat.rx.update);
+    sess->stat.rx.update_cnt++;
+
 
     /* Return pointer. */
Index: pjproject/trunk/pjmedia/src/pjmedia/rtp.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/rtp.c	(revision 389)
+++ pjproject/trunk/pjmedia/src/pjmedia/rtp.c	(revision 390)
@@ -35,4 +35,7 @@
 #define MIN_SEQUENTIAL  ((pj_int16_t)2)
 
+static void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *seq_ctrl, 
+				    pj_uint16_t seq);
+
 
 PJ_DEF(pj_status_t) pjmedia_rtp_session_init( pjmedia_rtp_session *ses,
@@ -162,7 +165,9 @@
 
 
-PJ_DEF(pj_status_t) pjmedia_rtp_session_update( pjmedia_rtp_session *ses, const pjmedia_rtp_hdr *hdr)
-{
-    int status;
+PJ_DEF(void) pjmedia_rtp_session_update( pjmedia_rtp_session *ses, 
+					 const pjmedia_rtp_hdr *hdr,
+					 pjmedia_rtp_status *p_seq_st)
+{
+    pjmedia_rtp_status seq_st;
 
     /* Check SSRC. */
@@ -176,9 +181,19 @@
     */
 
+    /* Init status */
+    seq_st.status.value = 0;
+    seq_st.diff = 0;
+
     /* Check payload type. */
     if (hdr->pt != ses->out_pt) {
-	PJ_LOG(4, (THIS_FILE, "pjmedia_rtp_session_update: ses=%p, invalid payload type %d (!=%d)",
+	PJ_LOG(4, (THIS_FILE, 
+		   "pjmedia_rtp_session_update: ses=%p, invalid payload "
+		   "type %d (expecting %d)",
 		   ses, hdr->pt, ses->out_pt));
-	return PJMEDIA_RTP_EINPT;
+	if (p_seq_st) {
+	    p_seq_st->status.flag.bad = 1;
+	    p_seq_st->status.flag.badpt = 1;
+	}
+	return;
     }
 
@@ -188,69 +203,96 @@
 
     /* Check sequence number to see if remote session has been restarted. */
-    status = pjmedia_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq));
-    if (status == PJMEDIA_RTP_ESESSRESTART) {
-	pjmedia_rtp_seq_restart( &ses->seq_ctrl, pj_ntohs(hdr->seq));
+    pjmedia_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq), &seq_st);
+    if (seq_st.status.flag.restart) {
 	++ses->received;
-    } else if (status == 0 || status == PJMEDIA_RTP_ESESSPROBATION) {
+
+    } else if (!seq_st.status.flag.bad) {
 	++ses->received;
     }
 
-
-    return status;
-}
-
-
-void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *sctrl, pj_uint16_t seq)
-{
-    sctrl->base_seq = seq;
-    sctrl->max_seq = seq;
-    sctrl->bad_seq = RTP_SEQ_MOD + 1;
-    sctrl->cycles = 0;
-}
-
-
-void pjmedia_rtp_seq_init(pjmedia_rtp_seq_session *sctrl, pj_uint16_t seq)
-{
-    pjmedia_rtp_seq_restart(sctrl, seq);
-
-    sctrl->max_seq = (pj_uint16_t) (seq - 1);
-    sctrl->probation = MIN_SEQUENTIAL;
-}
-
-
-pj_status_t pjmedia_rtp_seq_update(pjmedia_rtp_seq_session *sctrl, 
-				   pj_uint16_t seq)
-{
-    pj_uint16_t udelta = (pj_uint16_t) (seq - sctrl->max_seq);
+    if (p_seq_st) {
+	p_seq_st->status.value = seq_st.status.value;
+	p_seq_st->diff = seq_st.diff;
+    }
+}
+
+
+void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
+{
+    sess->base_seq = seq;
+    sess->max_seq = seq;
+    sess->bad_seq = RTP_SEQ_MOD + 1;
+    sess->cycles = 0;
+}
+
+
+void pjmedia_rtp_seq_init(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
+{
+    pjmedia_rtp_seq_restart(sess, seq);
+
+    sess->max_seq = (pj_uint16_t) (seq - 1);
+    sess->probation = MIN_SEQUENTIAL;
+}
+
+
+void pjmedia_rtp_seq_update( pjmedia_rtp_seq_session *sess, 
+			     pj_uint16_t seq,
+			     pjmedia_rtp_status *seq_status)
+{
+    pj_uint16_t udelta = (pj_uint16_t) (seq - sess->max_seq);
+    pjmedia_rtp_status st;
     
+    /* Init status */
+    st.status.value = 0;
+    st.diff = 0;
+
     /*
      * Source is not valid until MIN_SEQUENTIAL packets with
      * sequential sequence numbers have been received.
      */
-    if (sctrl->probation) {
-	/* packet is in sequence */
-        if (seq == sctrl->max_seq+ 1) {
-	    sctrl->probation--;
-            sctrl->max_seq = seq;
-            if (sctrl->probation == 0) {
-                return PJMEDIA_RTP_ESESSRESTART;
+    if (sess->probation) {
+
+	st.status.flag.probation = 1;
+	
+        if (seq == sess->max_seq+ 1) {
+	    /* packet is in sequence */
+	    st.diff = 1;
+	    sess->probation--;
+            sess->max_seq = seq;
+            if (sess->probation == 0) {
+		st.status.flag.probation = 0;
             }
 	} else {
-	    sctrl->probation = MIN_SEQUENTIAL - 1;
-	    sctrl->max_seq = seq;
+
+	    st.diff = 0;
+
+	    st.status.flag.bad = 1;
+	    if (seq == sess->max_seq)
+		st.status.flag.dup = 1;
+	    else
+		st.status.flag.outorder = 1;
+
+	    sess->probation = MIN_SEQUENTIAL - 1;
+	    sess->max_seq = seq;
         }
-        return PJMEDIA_RTP_ESESSPROBATION;
+
+
+    } else if (udelta == 0) {
+
+	st.status.flag.dup = 1;
 
     } else if (udelta < MAX_DROPOUT) {
 	/* in order, with permissible gap */
-	if (seq < sctrl->max_seq) {
+	if (seq < sess->max_seq) {
 	    /* Sequence number wrapped - count another 64K cycle. */
-	    sctrl->cycles += RTP_SEQ_MOD;
+	    sess->cycles += RTP_SEQ_MOD;
         }
-        sctrl->max_seq = seq;
+        sess->max_seq = seq;
+
+	st.diff = udelta;
 
     } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) {
 	/* the sequence number made a very large jump */
-        if (seq == sctrl->bad_seq) {
+        if (seq == sess->bad_seq) {
 	    /*
 	     * Two sequential packets -- assume that the other side
@@ -258,16 +300,27 @@
 	     * (i.e., pretend this was the first packet).
 	     */
-	    return PJMEDIA_RTP_ESESSRESTART;
+	    pjmedia_rtp_seq_restart(sess, seq);
+	    st.status.flag.restart = 1;
+	    st.status.flag.probation = 1;
+	    st.diff = 1;
 	}
         else {
-	    sctrl->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
-            return PJMEDIA_RTP_EBADSEQ;
+	    sess->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
+            st.status.flag.bad = 1;
+	    st.status.flag.outorder = 1;
         }
     } else {
-	/* duplicate or reordered packet */
+	/* old duplicate or reordered packet.
+	 * Not necessarily bad packet (?)
+	 */
+	st.status.flag.outorder = 1;
     }
     
-    return PJ_SUCCESS;
-}
-
-
+
+    if (seq_status) {
+	seq_status->diff = st.diff;
+	seq_status->status.value = st.status.value;
+    }
+}
+
+
Index: pjproject/trunk/pjmedia/src/pjmedia/session.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/session.c	(revision 389)
+++ pjproject/trunk/pjmedia/src/pjmedia/session.c	(revision 390)
@@ -526,5 +526,5 @@
 PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat( pjmedia_session *session,
 						     unsigned index,
-						     pjmedia_stream_stat *stat)
+						     pjmedia_rtcp_stat *stat)
 {
     PJ_ASSERT_RETURN(session && stat && index < session->stream_cnt, 
Index: pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 389)
+++ pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 390)
@@ -38,5 +38,5 @@
 #define ERRLEVEL			1
 #define TRACE_(expr)			stream_perror expr
-
+#define TRC_(expr)			PJ_LOG(4,expr)
 #define PJMEDIA_MAX_FRAME_DURATION_MS   200
 #define PJMEDIA_MAX_BUFFER_SIZE_MS	2000
@@ -44,4 +44,5 @@
 #define PJMEDIA_DTMF_DURATION		1600	/* in timestamp */
 #define PJMEDIA_RTP_NAT_PROBATION_CNT	10
+#define PJMEDIA_RTCP_INTERVAL		5	/* seconds	*/
 
 
@@ -88,5 +89,4 @@
 
     pjmedia_dir		     dir;	    /**< Stream direction.	    */
-    pjmedia_stream_stat	     stat;	    /**< Stream statistics.	    */
     void		    *user_data;	    /**< User data.		    */
 
@@ -111,5 +111,8 @@
     pj_ioqueue_key_t	    *rtcp_key;	    /**< RTCP ioqueue key.	    */
     pj_ioqueue_op_key_t	     rtcp_op_key;   /**< The pending read op key.   */
-
+    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  */
+    int			     rtcp_addrlen;  /**< Address length.	    */
 
     /* RFC 2833 DTMF transmission queue: */
@@ -270,4 +273,5 @@
     struct pjmedia_frame frame_out;
     int ts_len;
+    pj_bool_t has_tx;
     void *rtphdr;
     int rtphdrlen;
@@ -279,4 +283,7 @@
     /* 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. 
@@ -285,4 +292,5 @@
     if (stream->tx_dtmf_count) {
 
+	has_tx = PJ_TRUE;
 	create_dtmf_payload(stream, &frame_out);
 
@@ -297,4 +305,5 @@
 	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, 
@@ -317,4 +326,5 @@
 
 	/* Just update RTP session's timestamp. */
+	has_tx = PJ_FALSE;
 	status = pjmedia_rtp_encode_rtp( &channel->rtp, 
 					 0, 0, 
@@ -322,18 +332,45 @@
 					 (const void**)&rtphdr, 
 					 &rtphdrlen);
-	return PJ_SUCCESS;
-
-    }
-
-    if (status != 0) {
+
+    }
+
+    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) {
+	stream->rtcp_tx_time = pj_ntohl(channel->rtp.out_hdr.ts) +
+			       PJMEDIA_RTCP_INTERVAL * 
+			       stream->port.info.sample_rate;
+    } else if (pj_ntohl(channel->rtp.out_hdr.ts) >= stream->rtcp_tx_time) {
+	
+	pjmedia_rtcp_pkt *rtcp_pkt;
+	pj_ssize_t size;
+	int len;
+
+	pjmedia_rtcp_build_rtcp(&stream->rtcp, &rtcp_pkt, &len);
+	size = len;
+	status = pj_sock_sendto(stream->skinfo.rtcp_sock, rtcp_pkt, &size, 0,
+				&stream->rem_rtcp_addr, 
+				sizeof(stream->rem_rtcp_addr));
+	if (status != PJ_SUCCESS) {
+	    ;
+	}
+	
+	stream->rtcp_tx_time = pj_ntohl(channel->rtp.out_hdr.ts) +
+			       PJMEDIA_RTCP_INTERVAL * 
+			       stream->port.info.sample_rate;
+    }
+
+    /* Do nothing if we have nothing to transmit */
+    if (!has_tx)
+	return PJ_SUCCESS;
 
     if (rtphdrlen != sizeof(pjmedia_rtp_hdr)) {
 	/* We don't support RTP with extended header yet. */
 	PJ_TODO(SUPPORT_SENDING_RTP_WITH_EXTENDED_HEADER);
-	//TRACE_((THIS_FILE, "Unsupported extended RTP header for transmission"));
-	return 0;
+	return PJ_SUCCESS;
     }
 
@@ -342,12 +379,12 @@
     /* Send. */
     sent = frame_out.size+sizeof(pjmedia_rtp_hdr);
-    status = pj_sock_sendto(stream->skinfo.rtp_sock, channel->out_pkt, &sent, 0, 
-			    &stream->rem_rtp_addr, sizeof(stream->rem_rtp_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;
 
     /* Update stat */
-    stream->stat.enc.pkt++;
-    stream->stat.enc.bytes += frame_out.size+sizeof(pjmedia_rtp_hdr);
+    pjmedia_rtcp_tx_rtp(&stream->rtcp, frame_out.size);
 
     return PJ_SUCCESS;
@@ -460,4 +497,5 @@
 	const void *payload;
 	unsigned payloadlen;
+	pjmedia_rtp_status seq_st;
 
 	/* Go straight to read next packet if bytes_read == 0.
@@ -477,4 +515,8 @@
 
 
+	/* Inform RTCP session */
+	pjmedia_rtcp_rx_rtp(&stream->rtcp, pj_ntohs(hdr->seq),
+			    pj_ntohl(hdr->ts), payloadlen);
+
 	/* Handle incoming DTMF. */
 	if (hdr->pt == stream->rx_event_pt) {
@@ -487,25 +529,16 @@
 	 * the incoming packet.
 	 */
-	status = pjmedia_rtp_session_update(&channel->rtp, hdr);
-	if (status != 0 && 
-	    status != PJMEDIA_RTP_ESESSPROBATION && 
-	    status != PJMEDIA_RTP_ESESSRESTART) 
-	{
-	    TRACE_((THIS_FILE, "RTP session_update error (details follows)", 
-		    status));
-	    PJ_LOG(4,(THIS_FILE,"RTP packet detail: pt=%d, seq=%d",
-		      hdr->pt, pj_ntohs(hdr->seq)));
+	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", 
+		    seq_st.status.flag.badpt,
+		    seq_st.status.flag.dup,
+		    seq_st.status.flag.outorder,
+		    seq_st.status.flag.probation,
+		    seq_st.status.flag.restart));
 	    goto read_next_packet;
 	}
-
-
-	/* Update the RTCP session. */
-	pjmedia_rtcp_rx_rtp(&stream->rtcp, pj_ntohs(hdr->seq), 
-			    pj_ntohl(hdr->ts));
-
-
-	/* Update stat */
-	stream->stat.dec.pkt++;
-	stream->stat.dec.bytes += bytes_read;
 
 
@@ -572,7 +605,34 @@
                         pj_ssize_t bytes_read)
 {
-    PJ_UNUSED_ARG(key);
+    pjmedia_stream *stream = pj_ioqueue_get_user_data(key);
+    pj_status_t status;
+
     PJ_UNUSED_ARG(op_key);
-    PJ_UNUSED_ARG(bytes_read);
+
+    do {
+	if (bytes_read > 0) {
+	    pjmedia_rtcp_rx_rtcp(&stream->rtcp, stream->rtcp_pkt, 
+				 bytes_read);
+	}
+
+	bytes_read = stream->rtcp_pkt_size;
+	stream->rtcp_addrlen = sizeof(stream->rem_rtcp_addr);
+	status = pj_ioqueue_recvfrom( stream->rtcp_key,
+				      &stream->rtcp_op_key,
+				      stream->rtcp_pkt,
+				      &bytes_read, 0,
+				      &stream->rem_rtcp_addr,
+				      &stream->rtcp_addrlen);
+
+    } while (status == PJ_SUCCESS);
+
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(4,(THIS_FILE, "Error reading RTCP packet: %s [status=%d]",
+			     errmsg, status));
+    }
+
 }
 
@@ -659,4 +719,5 @@
     pjmedia_codec_param codec_param;
     pj_ioqueue_callback ioqueue_cb;
+    pj_uint16_t rtcp_port;
     pj_status_t status;
 
@@ -691,10 +752,10 @@
     stream->skinfo = info->sock_info;
     stream->rem_rtp_addr = info->rem_addr;
+    rtcp_port = (pj_uint16_t) (pj_ntohs(info->rem_addr.sin_port)+1);
+    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->last_dtmf = -1;
-
-
-    PJ_TODO(INITIALIZE_RTCP_REMOTE_ADDRESS);
 
     /* Create mutex to protect jitter buffer: */
@@ -739,5 +800,7 @@
     /* Init RTCP session: */
 
-    pjmedia_rtcp_init(&stream->rtcp, info->fmt.sample_rate, info->ssrc);
+    pjmedia_rtcp_init(&stream->rtcp, info->fmt.sample_rate, 
+		      stream->port.info.samples_per_frame, 
+		      info->ssrc);
 
 
@@ -800,4 +863,6 @@
     pj_ioqueue_op_key_init(&stream->rtcp_op_key, sizeof(stream->rtcp_op_key));
 
+    stream->rtcp_pkt_size = sizeof(stream->rtcp_pkt);
+
     /* Bootstrap the first recvfrom() operation. */
     on_rx_rtcp( stream->rtcp_key, &stream->rtcp_op_key, 0);
@@ -901,10 +966,9 @@
  */
 PJ_DEF(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream,
-					     pjmedia_stream_stat *stat)
+					     pjmedia_rtcp_stat *stat)
 {
     PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL);
 
-    pj_memcpy(stat, &stream->stat, sizeof(pjmedia_stream_stat));
-
+    pj_memcpy(stat, &stream->rtcp.stat, sizeof(pjmedia_rtcp_stat));
     return PJ_SUCCESS;
 }
Index: pjproject/trunk/pjsip-apps/src/samples/siprtp.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/samples/siprtp.c	(revision 389)
+++ pjproject/trunk/pjsip-apps/src/samples/siprtp.c	(revision 390)
@@ -52,16 +52,4 @@
 
 
-/* Unidirectional media stat: */
-struct stream_stat
-{
-    pj_uint32_t	    pkt, payload;
-    pj_uint32_t	    discard, reorder;
-    unsigned	    loss_min, loss_avg, loss_max;
-    char	   *loss_type;
-    unsigned	    jitter_min_us, jitter_avg_us, jitter_max_us;
-    unsigned	    rtcp_cnt;
-};
-
-
 /* A bidirectional media stream */
 struct media_stream
@@ -88,9 +76,4 @@
     /* RTCP stats: */
     pjmedia_rtcp_session rtcp;		    /* incoming RTCP session.	*/
-    pjmedia_rtcp_pkt	 rem_rtcp;	    /* received RTCP stat.	*/
-
-    /* More stats: */
-    struct stream_stat	 rx_stat;	    /* incoming stream stat	*/
-    struct stream_stat	 tx_stat;	    /* outgoing stream stat.	*/
 
     /* Thread: */
@@ -1074,6 +1057,4 @@
 	    }
 
-	    ++strm->rx_stat.pkt;
-	    strm->rx_stat.payload += (size - 12);
 
 	    /* Decode RTP packet. */
@@ -1084,25 +1065,13 @@
 	    if (status != PJ_SUCCESS) {
 		app_perror(THIS_FILE, "RTP decode error", status);
-		strm->rx_stat.discard++;
 		continue;
 	    }
 
-	    /* Update RTP session */
-	    status = pjmedia_rtp_session_update(&strm->in_sess, hdr);
-	    if (status != PJ_SUCCESS && 
-		status != PJMEDIA_RTP_ESESSPROBATION && 
-		status != PJMEDIA_RTP_ESESSRESTART) 
-	    {
-		app_perror(THIS_FILE, "RTP update error", status);
-		PJ_LOG(3,(THIS_FILE,"RTP packet detail: pt=%d, seq=%d",
-			  hdr->pt, pj_ntohs(hdr->seq)));
-		strm->rx_stat.discard++;
-		continue;
-	    }
-
 	    /* Update the RTCP session. */
 	    pjmedia_rtcp_rx_rtp(&strm->rtcp, pj_ntohs(hdr->seq),
-				pj_ntohl(hdr->ts));
-
+				pj_ntohl(hdr->ts), payload_len);
+
+	    /* Update RTP session */
+	    pjmedia_rtp_session_update(&strm->in_sess, hdr, NULL);
 	} 
 	
@@ -1119,35 +1088,6 @@
 	    if (status != PJ_SUCCESS)
 		app_perror(THIS_FILE, "Error receiving RTCP packet", status);
-	    else {
-		if (size != sizeof(strm->rem_rtcp)) {
-		    PJ_LOG(3,(THIS_FILE, "Error: RTCP packet size mismatch "
-					 "(recv %d bytes, expecting %d)",
-					 size, sizeof(strm->rem_rtcp)));
-		    status = -1;
-		} else {
-		    pj_memcpy(&strm->rem_rtcp, packet, size);
-		    status = PJ_SUCCESS;
-
-		    /* Report receipt of RTCP to RTCP session */
-		    pjmedia_rtcp_rx_rtcp(&strm->rtcp, packet, size);
-		}
-	    }
-
-	    if (status == PJ_SUCCESS) {
-		/* Process RTCP stats */
-		unsigned jitter;
-		
-		jitter = (unsigned)(pj_ntohl(strm->rem_rtcp.rr.jitter) * 
-                                    1000000.0 / strm->clock_rate);
-		if (jitter < strm->tx_stat.jitter_min_us)
-		    strm->tx_stat.jitter_min_us = jitter;
-		if (jitter > strm->tx_stat.jitter_max_us)
-		    strm->tx_stat.jitter_max_us = jitter;
-		strm->tx_stat.jitter_avg_us = 
-			(strm->tx_stat.jitter_avg_us * strm->tx_stat.rtcp_cnt +
-			 jitter) / (strm->tx_stat.rtcp_cnt + 1);
-
-		strm->tx_stat.rtcp_cnt++;
-	    }
+	    else
+		pjmedia_rtcp_rx_rtcp(&strm->rtcp, packet, size);
 	}
 
@@ -1194,8 +1134,4 @@
 	    /* Schedule next send */
 	    next_rtp.u64 += (msec_interval * freq.u64 / 1000);
-
-	    /* Update stats */
-	    strm->tx_stat.pkt++;
-	    strm->tx_stat.payload += strm->bytes_per_frame;
 	}
 
@@ -1229,22 +1165,5 @@
 	    }
 	    
-
-	    /* Process RTCP stats */
-	    {
-		unsigned jitter;
-		
-		jitter = (unsigned) (pj_ntohl(rtcp_pkt->rr.jitter) * 
-				     1000000.0 / strm->clock_rate);
-		if (jitter < strm->rx_stat.jitter_min_us)
-		    strm->rx_stat.jitter_min_us = jitter;
-		if (jitter > strm->rx_stat.jitter_max_us)
-		    strm->rx_stat.jitter_max_us = jitter;
-		strm->rx_stat.jitter_avg_us = 
-			(strm->rx_stat.jitter_avg_us * strm->rx_stat.rtcp_cnt +
-			 jitter) / (strm->rx_stat.rtcp_cnt + 1);
-
-		strm->rx_stat.rtcp_cnt++;
-	    }
-
+	    /* Schedule next send */
 	    next_rtcp.u64 += (freq.u64 * RTCP_INTERVAL);
 	}
@@ -1319,10 +1238,7 @@
 			     pj_rand());
     pjmedia_rtp_session_init(&audio->in_sess, audio->si.fmt.pt, 0);
-    pjmedia_rtcp_init(&audio->rtcp, audio->clock_rate, 0);
-
-
-    /* Clear media statistics */
-    pj_memset(&audio->rx_stat, 0, sizeof(audio->rx_stat));
-    pj_memset(&audio->tx_stat, 0, sizeof(audio->tx_stat));
+    pjmedia_rtcp_init(&audio->rtcp, audio->clock_rate, 
+		      audio->samples_per_frame, 0);
+
 
 
@@ -1411,14 +1327,13 @@
     struct media_stream *audio = &call->media[0];
     char userinfo[128];
-    char duration[80];
+    char duration[80], last_update[80];
     char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16];
-    pj_uint32_t total_loss;
-
+    pj_time_val now;
+
+    pj_gettimeofday(&now);
 
     /* Print duration */
     if (inv->state >= PJSIP_INV_STATE_CONFIRMED) {
-	pj_time_val now;
-
-	pj_gettimeofday(&now);
+
 	PJ_TIME_VAL_SUB(now, call->connect_time);
 
@@ -1489,61 +1404,95 @@
 	good_number(ipbps, (audio->bytes_per_frame+32) * audio->clock_rate / audio->samples_per_frame));
 
-    total_loss = (audio->rtcp.rtcp_pkt.rr.total_lost_2 << 16) +
-	         (audio->rtcp.rtcp_pkt.rr.total_lost_1 << 8) +
-		 audio->rtcp.rtcp_pkt.rr.total_lost_0;
-
-    printf("              RX total %s packets %sB received (%sB +IP hdr)%s\n"
-	   "                 pkt discards=%d (%3.1f%%), loss=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
-	   "                 loss period min=%dms, avg=%dms, max=%dms%s\n"
-	   "                 jitter min=%5.3fms, avg=%5.3fms, max=%5.3fms, curr=%5.3f ms%s\n",
-	   good_number(packets, audio->rx_stat.pkt),
-	   good_number(bytes, audio->rx_stat.payload),
-	   good_number(ipbytes, audio->rx_stat.payload + audio->rx_stat.pkt * 32),
+    if (audio->rtcp.stat.rx.update_cnt == 0)
+	strcpy(last_update, "never");
+    else {
+	pj_gettimeofday(&now);
+	PJ_TIME_VAL_SUB(now, audio->rtcp.stat.rx.update);
+	sprintf(last_update, "%02dh:%02dm:%02d.%03ds 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"
+	   "                 loss period min=%5.3fms, avg=%5.3fms, max=%5.3fms, last=%5.3f%s\n"
+	   "                 jitter min=%5.3fms, avg=%5.3fms, max=%5.3fms, last=%5.3fms%s\n",
+	   last_update,
+	   good_number(packets, audio->rtcp.stat.rx.pkt),
+	   good_number(bytes, audio->rtcp.stat.rx.bytes),
+	   good_number(ipbytes, audio->rtcp.stat.rx.bytes + audio->rtcp.stat.rx.pkt * 32),
 	   "",
-	   audio->rx_stat.discard, 
-	   audio->rx_stat.discard * 100.0 / audio->rx_stat.pkt,
-	   total_loss,
-	   total_loss * 100.0 / audio->rx_stat.pkt,
-	   0, 0.0,
+	   audio->rtcp.stat.rx.loss,
+	   audio->rtcp.stat.rx.loss * 100.0 / audio->rtcp.stat.rx.pkt,
+	   audio->rtcp.stat.rx.dup, 
+	   audio->rtcp.stat.rx.dup * 100.0 / audio->rtcp.stat.rx.pkt,
+	   audio->rtcp.stat.rx.reorder, 
+	   audio->rtcp.stat.rx.reorder * 100.0 / audio->rtcp.stat.rx.pkt,
 	   "",
-	   -1, -1, -1, 
+	   audio->rtcp.stat.rx.loss_period.min / 1000.0, 
+	   audio->rtcp.stat.rx.loss_period.avg / 1000.0, 
+	   audio->rtcp.stat.rx.loss_period.max / 1000.0,
+	   audio->rtcp.stat.rx.loss_period.last / 1000.0,
 	   "",
-	   (audio->rx_stat.rtcp_cnt? audio->rx_stat.jitter_min_us/1000.0 : -1.),
-	   (audio->rx_stat.rtcp_cnt? audio->rx_stat.jitter_avg_us/1000.0 : -1.),
-	   (audio->rx_stat.rtcp_cnt? audio->rx_stat.jitter_max_us/1000.0 : -1.),
-	   (audio->rx_stat.rtcp_cnt? pj_ntohl(audio->rtcp.rtcp_pkt.rr.jitter)*1000.0/audio->clock_rate : -1.),
+	   audio->rtcp.stat.rx.jitter.min / 1000.0,
+	   audio->rtcp.stat.rx.jitter.avg / 1000.0,
+	   audio->rtcp.stat.rx.jitter.max / 1000.0,
+	   audio->rtcp.stat.rx.jitter.last / 1000.0,
 	   ""
 	   );
 
 
-    total_loss = (audio->rem_rtcp.rr.total_lost_2 << 16) +
-	         (audio->rem_rtcp.rr.total_lost_1 << 8) +
-		  audio->rem_rtcp.rr.total_lost_0;
-
-    printf("              TX total %s packets %sB sent (%sB +IP hdr)%s\n"
-	   "                 pkt discards=%d (%3.1f%%), loss=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
-	   "                 loss period min=%dms, avg=%dms, max=%dms%s\n"
-	   "                 jitter min=%5.3fms, avg=%5.3fms, max=%5.3fms, curr=%5.3f ms%s\n",
-	   good_number(packets, audio->tx_stat.pkt),
-	   good_number(bytes, audio->tx_stat.payload),
-	   good_number(ipbytes, audio->tx_stat.payload + audio->tx_stat.pkt * 32),
+    if (audio->rtcp.stat.tx.update_cnt == 0)
+	strcpy(last_update, "never");
+    else {
+	pj_gettimeofday(&now);
+	PJ_TIME_VAL_SUB(now, audio->rtcp.stat.tx.update);
+	sprintf(last_update, "%02dh:%02dm:%02d.%03ds ago",
+		now.sec / 3600,
+		(now.sec % 3600) / 60,
+		now.sec % 60,
+		now.msec);
+    }
+
+    printf("              TX 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"
+	   "                 loss period min=%5.3fms, avg=%5.3fms, max=%5.3fms, last=%5.3f%s\n"
+	   "                 jitter min=%5.3fms, avg=%5.3fms, max=%5.3fms, last=%5.3fms%s\n",
+	   last_update,
+	   good_number(packets, audio->rtcp.stat.tx.pkt),
+	   good_number(bytes, audio->rtcp.stat.tx.bytes),
+	   good_number(ipbytes, audio->rtcp.stat.tx.bytes + audio->rtcp.stat.tx.pkt * 32),
 	   "",
-	   audio->tx_stat.discard, 
-	   audio->tx_stat.discard * 100.0 / audio->tx_stat.pkt,
-	   total_loss,
-	   total_loss * 100.0 / audio->tx_stat.pkt,
-	   0, 0.0,
+	   audio->rtcp.stat.tx.loss,
+	   audio->rtcp.stat.tx.loss * 100.0 / audio->rtcp.stat.tx.pkt,
+	   audio->rtcp.stat.tx.dup, 
+	   audio->rtcp.stat.tx.dup * 100.0 / audio->rtcp.stat.tx.pkt,
+	   audio->rtcp.stat.tx.reorder, 
+	   audio->rtcp.stat.tx.reorder * 100.0 / audio->rtcp.stat.tx.pkt,
 	   "",
-	   -1, -1, -1, 
+	   audio->rtcp.stat.tx.loss_period.min / 1000.0, 
+	   audio->rtcp.stat.tx.loss_period.avg / 1000.0, 
+	   audio->rtcp.stat.tx.loss_period.max / 1000.0,
+	   audio->rtcp.stat.tx.loss_period.last / 1000.0,
 	   "",
-	   (audio->tx_stat.rtcp_cnt? audio->tx_stat.jitter_min_us/1000.0 : -1.),
-	   (audio->tx_stat.rtcp_cnt? audio->tx_stat.jitter_avg_us/1000.0 : -1.),
-	   (audio->tx_stat.rtcp_cnt? audio->tx_stat.jitter_max_us/1000.0 : -1.),
-	   (audio->tx_stat.rtcp_cnt? pj_ntohl(audio->rem_rtcp.rr.jitter)*1000.0/audio->clock_rate : -1.),
+	   audio->rtcp.stat.tx.jitter.min / 1000.0,
+	   audio->rtcp.stat.tx.jitter.avg / 1000.0,
+	   audio->rtcp.stat.tx.jitter.max / 1000.0,
+	   audio->rtcp.stat.tx.jitter.last / 1000.0,
 	   ""
 	   );
 
-    printf("              End to end delay: %5.3f ms\n", 
-	   audio->rtcp.rtt_us / 1000.0);
+
+    printf("             RTT min=%5.3fms, avg=%5.3fms, max=%5.3fms, last=%5.3fms%s\n", 
+	   audio->rtcp.stat.rtt.min / 1000.0,
+	   audio->rtcp.stat.rtt.avg / 1000.0,
+	   audio->rtcp.stat.rtt.max / 1000.0,
+	   audio->rtcp.stat.rtt.last / 1000.0,
+	   ""
+	   );
 
 }
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_settings.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_settings.c	(revision 389)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_settings.c	(revision 390)
@@ -648,11 +648,13 @@
 
     for (i=0; i<info.stream_cnt; ++i) {
-	pjmedia_stream_stat strm_stat;
+	pjmedia_rtcp_stat stat;
 	const char *rem_addr;
 	int rem_port;
 	const char *dir;
-	char stxpkt[10], stxoct[10], srxpkt[10], srxoct[10];
-
-	pjmedia_session_get_stream_stat(session, i, &strm_stat);
+	char last_update[40];
+	char packets[16], bytes[16], ipbytes[16];
+	pj_time_val now;
+
+	pjmedia_session_get_stream_stat(session, i, &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);
@@ -677,13 +679,103 @@
 		  dir,
 		  rem_addr, rem_port));
+
+	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, "%02dh:%02dm:%02d.%03ds ago",
+		    now.sec / 3600,
+		    (now.sec % 3600) / 60,
+		    now.sec % 60,
+		    now.msec);
+	}
+
 	PJ_LOG(3,(THIS_FILE, 
-		  "%s tx{pt=%d,pkt=%s,oct=%s} rx{pt=%d,pkt=%s,oct=%s}",
-		  "                 ",
-		  info.stream_info[i].tx_pt,
-		  good_number(stxpkt, strm_stat.enc.pkt), 
-		  good_number(stxoct, strm_stat.enc.bytes),
-		  info.stream_info[i].fmt.pt,
-		  good_number(srxpkt, strm_stat.dec.pkt), 
-		  good_number(srxoct, strm_stat.dec.bytes)));
+	       "                  RX pt=%d, 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",
+	       info.stream_info[i].fmt.pt,
+	       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, "%02dh:%02dm:%02d.%03ds ago",
+		    now.sec / 3600,
+		    (now.sec % 3600) / 60,
+		    now.sec % 60,
+		    now.msec);
+	}
+
+	PJ_LOG(3,(THIS_FILE,
+	       "                  TX pt=%d, 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",
+	       info.stream_info[i].tx_pt,
+	       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,
+	       ""
+	       ));
+
+
+	PJ_LOG(3,(THIS_FILE,
+	       "                 RTT msec       : %7.3f %7.3f %7.3f %7.3f%s", 
+	       stat.rtt.min / 1000.0,
+	       stat.rtt.avg / 1000.0,
+	       stat.rtt.max / 1000.0,
+	       stat.rtt.last / 1000.0,
+	       ""
+	       ));
 
     }
@@ -713,25 +805,23 @@
 
     /* Dump all invite sessions: */
-    if (detail) {
-	PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
-
-	if (pjsua.call_cnt == 0) {
-
-	    PJ_LOG(3,(THIS_FILE, "  - no sessions -"));
-
-	} else {
-	    int i;
-
-	    for (i=0; i<pjsua.max_calls; ++i) {
-
-		if (pjsua.calls[i].inv == NULL)
-		    continue;
-
-		print_call("  ", i, buf, sizeof(buf));
-		PJ_LOG(3,(THIS_FILE, "%s", buf));
-
-		if (pjsua.calls[i].session)
-		    dump_media_session(pjsua.calls[i].session);
-	    }
+    PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
+
+    if (pjsua.call_cnt == 0) {
+
+	PJ_LOG(3,(THIS_FILE, "  - no sessions -"));
+
+    } else {
+	int i;
+
+	for (i=0; i<pjsua.max_calls; ++i) {
+
+	    if (pjsua.calls[i].inv == NULL)
+		continue;
+
+	    print_call("  ", i, buf, sizeof(buf));
+	    PJ_LOG(3,(THIS_FILE, "%s", buf));
+
+	    if (pjsua.calls[i].session)
+		dump_media_session(pjsua.calls[i].session);
 	}
     }
