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. */
