summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-04-06 19:29:03 +0000
committerBenny Prijono <bennylp@teluu.com>2006-04-06 19:29:03 +0000
commit2c1984946280641641349515c08e4970afdf9eff (patch)
tree1002e70bb23bb37f00ca4179817c371cb9a0c6fa
parent303d33a1dbf4bc43a62720c2e673591916325e33 (diff)
Integrate (stream) quality monitoring into RTCP framework, and update all RTCP clients accordingly
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@390 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/include/pjmedia/rtcp.h86
-rw-r--r--pjmedia/include/pjmedia/rtp.h75
-rw-r--r--pjmedia/include/pjmedia/session.h2
-rw-r--r--pjmedia/include/pjmedia/stream.h24
-rw-r--r--pjmedia/src/pjmedia/rtcp.c315
-rw-r--r--pjmedia/src/pjmedia/rtp.c135
-rw-r--r--pjmedia/src/pjmedia/session.c2
-rw-r--r--pjmedia/src/pjmedia/stream.c142
-rw-r--r--pjsip-apps/src/samples/siprtp.c227
-rw-r--r--pjsip/src/pjsua-lib/pjsua_settings.c140
10 files changed, 757 insertions, 391 deletions
diff --git a/pjmedia/include/pjmedia/rtcp.h b/pjmedia/include/pjmedia/rtcp.h
index 78498a80..d327d4f6 100644
--- a/pjmedia/include/pjmedia/rtcp.h
+++ b/pjmedia/include/pjmedia/rtcp.h
@@ -146,6 +146,73 @@ typedef struct pjmedia_rtcp_ntp_rec pjmedia_rtcp_ntp_rec;
/**
+ * 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.
*/
@@ -156,6 +223,7 @@ struct pjmedia_rtcp_session
pjmedia_rtp_seq_session seq_ctrl; /**< RTCP sequence number control. */
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*/
pj_uint32_t rx_prior; /**< # pkt received at last interval*/
@@ -166,7 +234,8 @@ struct pjmedia_rtcp_session
pj_uint32_t rx_lsr; /**< NTP ts in last SR received */
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. */
};
/**
@@ -178,11 +247,14 @@ typedef struct pjmedia_rtcp_session pjmedia_rtcp_session;
/**
* 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 );
@@ -201,10 +273,12 @@ PJ_DECL(void) pjmedia_rtcp_fini( pjmedia_rtcp_session *session);
* @param session The session.
* @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);
/**
@@ -216,7 +290,7 @@ PJ_DECL(void) pjmedia_rtcp_rx_rtp( pjmedia_rtcp_session *session,
* RTP header) in bytes.
*/
PJ_DECL(void) pjmedia_rtcp_tx_rtp( pjmedia_rtcp_session *session,
- pj_uint16_t ptsize );
+ unsigned ptsize );
/**
diff --git a/pjmedia/include/pjmedia/rtp.h b/pjmedia/include/pjmedia/rtp.h
index 34c576bd..87a73c81 100644
--- a/pjmedia/include/pjmedia/rtp.h
+++ b/pjmedia/include/pjmedia/rtp.h
@@ -180,6 +180,48 @@ typedef struct pjmedia_rtp_session pjmedia_rtp_session;
/**
+ * 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.
*
* @param ses The session.
@@ -241,12 +283,12 @@ PJ_DECL(pj_status_t) pjmedia_rtp_decode_rtp( pjmedia_rtp_session *ses,
*
* @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.
+ * @param seq_st Optional structure to receive the status of the RTP packet
+ * processing.
*/
-PJ_DECL(pj_status_t) pjmedia_rtp_session_update( pjmedia_rtp_session *ses,
- const pjmedia_rtp_hdr *hdr);
+PJ_DECL(void) pjmedia_rtp_session_update( pjmedia_rtp_session *ses,
+ const pjmedia_rtp_hdr *hdr,
+ pjmedia_rtp_status *seq_st);
/*
@@ -265,25 +307,16 @@ void pjmedia_rtp_seq_init(pjmedia_rtp_seq_session *seq_ctrl,
/**
- * 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.
+ * @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.
*/
-pj_status_t pjmedia_rtp_seq_update(pjmedia_rtp_seq_session *seq_ctrl,
- pj_uint16_t seq);
+void pjmedia_rtp_seq_update( pjmedia_rtp_seq_session *seq_ctrl,
+ pj_uint16_t seq,
+ pjmedia_rtp_status *seq_status);
/**
* @}
diff --git a/pjmedia/include/pjmedia/session.h b/pjmedia/include/pjmedia/session.h
index 898744d4..762f9b8d 100644
--- a/pjmedia/include/pjmedia/session.h
+++ b/pjmedia/include/pjmedia/session.h
@@ -217,7 +217,7 @@ PJ_DECL(pj_status_t) pjmedia_session_get_port( pjmedia_session *session,
*/
PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat(pjmedia_session *session,
unsigned index,
- pjmedia_stream_stat *sta);
+ pjmedia_rtcp_stat *stat);
/**
* Dial DTMF digit to the stream, using RFC 2833 mechanism.
diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h
index 91f50dc3..90d2a2f6 100644
--- a/pjmedia/include/pjmedia/stream.h
+++ b/pjmedia/include/pjmedia/stream.h
@@ -29,6 +29,7 @@
#include <pjmedia/codec.h>
#include <pjmedia/endpoint.h>
#include <pjmedia/port.h>
+#include <pjmedia/rtcp.h>
#include <pj/sock.h>
PJ_BEGIN_DECL
@@ -82,27 +83,6 @@ struct pjmedia_stream_info
/**
- * 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.
*
@@ -164,7 +144,7 @@ PJ_DECL(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream);
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream,
- pjmedia_stream_stat *stat);
+ pjmedia_rtcp_stat *stat);
/**
* Pause the individual channel in the stream.
diff --git a/pjmedia/src/pjmedia/rtcp.c b/pjmedia/src/pjmedia/rtcp.c
index 6fc3a752..8755af52 100644
--- a/pjmedia/src/pjmedia/rtcp.c
+++ b/pjmedia/src/pjmedia/rtcp.c
@@ -43,7 +43,7 @@
/*
* 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)
{
pj_time_val tv;
@@ -56,28 +56,30 @@ static void rtcp_get_ntp_time(const pjmedia_rtcp_session *s,
ntp->hi = tv.sec;
/* 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 */
ntp->lo = ts.u32.lo;
}
-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 */
rtcp_pkt->common.version = 2;
@@ -89,49 +91,62 @@ PJ_DEF(void) pjmedia_rtcp_init(pjmedia_rtcp_session *s,
rtcp_pkt->sr.ssrc = pj_htonl(ssrc);
/* 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. */
}
-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);
+ PJ_UNUSED_ARG(sess);
}
-static void rtcp_init_seq(pjmedia_rtcp_session *s, pj_uint16_t seq)
+static void rtcp_init_seq(pjmedia_rtcp_session *sess)
{
- 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);
+ 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 *s,
- pj_uint16_t seq,
- pj_uint32_t rtp_ts)
+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;
+ pjmedia_rtp_status seq_st;
+ unsigned last_seq;
+
+ sess->stat.rx.pkt++;
+ sess->stat.rx.bytes += payload;
- /* 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;
+ /* 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 (status != 0)
+ 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;
+ }
+
+
+ /* Only mark "good" packets */
+ ++sess->received;
- ++s->received;
/*
* Calculate jitter (see RFC 3550 section A.8)
@@ -139,41 +154,55 @@ PJ_DEF(void) pjmedia_rtcp_rx_rtp(pjmedia_rtcp_session *s,
/* 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;
+ pj_uint32_t jitter;
- d = transit - s->transit;
- s->transit = transit;
+ d = transit - sess->transit;
+ sess->transit = transit;
if (d < 0)
d = -d;
- s->jitter += d - ((s->jitter + 8) >> 4);
+ 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 *s,
- pj_uint16_t bytes_payload_size)
+PJ_DEF(void) pjmedia_rtcp_tx_rtp(pjmedia_rtcp_session *sess,
+ unsigned 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 );
+ /* Update statistics */
+ sess->stat.tx.pkt++;
+ sess->stat.tx.bytes += bytes_payload_size;
}
-PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *session,
+PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *sess,
const void *pkt,
pj_size_t size)
{
@@ -183,19 +212,77 @@ PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *session,
pj_assert(size >= sizeof(pjmedia_rtcp_common)+sizeof(pjmedia_rtcp_sr));
/* 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) {
pj_uint32_t lsr, now, dlsr;
@@ -211,7 +298,7 @@ PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *session,
dlsr = pj_ntohl(rtcp->rr.dlsr);
/* 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);
@@ -220,7 +307,7 @@ PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *session,
/* 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;
@@ -233,48 +320,90 @@ PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *session,
* otherwise rtt will be invalid
*/
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));
}
}
+
+ pj_gettimeofday(&sess->stat.tx.update);
+ sess->stat.tx.update_cnt++;
}
}
-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_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;
+ expected_interval = expected - sess->exp_prior;
+ sess->exp_prior = expected;
- received_interval = s->received - s->rx_prior;
- s->rx_prior = s->received;
+ received_interval = sess->received - sess->rx_prior;
+ sess->rx_prior = sess->received;
lost_interval = expected_interval - received_interval;
@@ -283,35 +412,25 @@ static void rtcp_build_rtcp(pjmedia_rtcp_session *s,
} else {
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. */
rtcp_pkt->sr.ntp_sec = pj_htonl(ntp.hi);
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.
LSR is the middle 32bit of the last SR NTP time received.
@@ -324,7 +443,7 @@ PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *session,
pj_get_timestamp(&ts);
/* 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 */
dlsr = (pj_uint32_t)(ts.u64 - lsr_time);
@@ -340,6 +459,10 @@ PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *session,
(dlsr%65536)*1000/65536 ));
}
+ /* Update counter */
+ pj_gettimeofday(&sess->stat.rx.update);
+ sess->stat.rx.update_cnt++;
+
/* Return pointer. */
*ret_p_pkt = rtcp_pkt;
diff --git a/pjmedia/src/pjmedia/rtp.c b/pjmedia/src/pjmedia/rtp.c
index 17b4e712..5103c5ac 100644
--- a/pjmedia/src/pjmedia/rtp.c
+++ b/pjmedia/src/pjmedia/rtp.c
@@ -34,6 +34,9 @@
#define MAX_MISORDER ((pj_int16_t)100)
#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,
int default_pt, pj_uint32_t sender_ssrc )
@@ -161,9 +164,11 @@ PJ_DEF(pj_status_t) pjmedia_rtp_decode_rtp( pjmedia_rtp_session *ses,
}
-PJ_DEF(pj_status_t) pjmedia_rtp_session_update( pjmedia_rtp_session *ses, const pjmedia_rtp_hdr *hdr)
+PJ_DEF(void) pjmedia_rtp_session_update( pjmedia_rtp_session *ses,
+ const pjmedia_rtp_hdr *hdr,
+ pjmedia_rtp_status *p_seq_st)
{
- int status;
+ pjmedia_rtp_status seq_st;
/* Check SSRC. */
if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
@@ -175,11 +180,21 @@ PJ_DEF(pj_status_t) pjmedia_rtp_session_update( pjmedia_rtp_session *ses, const
}
*/
+ /* 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;
}
/* Initialize sequence number on first packet received. */
@@ -187,87 +202,125 @@ PJ_DEF(pj_status_t) pjmedia_rtp_session_update( pjmedia_rtp_session *ses, const
pjmedia_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) );
/* 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;
+ 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 *sctrl, pj_uint16_t seq)
+void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
{
- sctrl->base_seq = seq;
- sctrl->max_seq = seq;
- sctrl->bad_seq = RTP_SEQ_MOD + 1;
- sctrl->cycles = 0;
+ 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 *sctrl, pj_uint16_t seq)
+void pjmedia_rtp_seq_init(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
{
- pjmedia_rtp_seq_restart(sctrl, seq);
+ pjmedia_rtp_seq_restart(sess, seq);
- sctrl->max_seq = (pj_uint16_t) (seq - 1);
- sctrl->probation = MIN_SEQUENTIAL;
+ sess->max_seq = (pj_uint16_t) (seq - 1);
+ sess->probation = MIN_SEQUENTIAL;
}
-pj_status_t pjmedia_rtp_seq_update(pjmedia_rtp_seq_session *sctrl,
- pj_uint16_t seq)
+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 - sctrl->max_seq);
+ 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
* restarted without telling us so just re-sync
* (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;
+ }
}
diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c
index 5f00df53..2b50485e 100644
--- a/pjmedia/src/pjmedia/session.c
+++ b/pjmedia/src/pjmedia/session.c
@@ -525,7 +525,7 @@ PJ_DEF(pj_status_t) pjmedia_session_get_port( pjmedia_session *session,
*/
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,
PJ_EINVAL);
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 6f0d4881..378d3670 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -37,12 +37,13 @@
#define THIS_FILE "stream.c"
#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
#define PJMEDIA_MAX_MTU 1500
#define PJMEDIA_DTMF_DURATION 1600 /* in timestamp */
#define PJMEDIA_RTP_NAT_PROBATION_CNT 10
+#define PJMEDIA_RTCP_INTERVAL 5 /* seconds */
/**
@@ -87,7 +88,6 @@ struct pjmedia_stream
pjmedia_channel *dec; /**< Decoding channel. */
pjmedia_dir dir; /**< Stream direction. */
- pjmedia_stream_stat stat; /**< Stream statistics. */
void *user_data; /**< User data. */
pjmedia_codec *codec; /**< Codec instance being used. */
@@ -110,7 +110,10 @@ struct pjmedia_stream
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: */
int tx_event_pt; /**< Outgoing pt for dtmf. */
@@ -269,6 +272,7 @@ static pj_status_t put_frame( pjmedia_port *port,
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;
@@ -279,11 +283,15 @@ static pj_status_t put_frame( pjmedia_port *port,
/* 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. */
@@ -296,6 +304,7 @@ static pj_status_t put_frame( pjmedia_port *port,
} 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,
@@ -316,39 +325,67 @@ static pj_status_t put_frame( pjmedia_port *port,
} 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);
- 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;
}
pj_memcpy(channel->out_pkt, rtphdr, sizeof(pjmedia_rtp_hdr));
/* 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;
}
@@ -459,6 +496,7 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
const pjmedia_rtp_hdr *hdr;
const void *payload;
unsigned payloadlen;
+ pjmedia_rtp_status seq_st;
/* Go straight to read next packet if bytes_read == 0.
*/
@@ -476,6 +514,10 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
}
+ /* 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) {
handle_incoming_dtmf(stream, payload, payloadlen);
@@ -486,29 +528,20 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
/* Update RTP session (also checks if RTP session can accept
* 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;
-
-
/* See if source address of RTP packet is different than the
* configured address.
*/
@@ -571,9 +604,36 @@ static void on_rx_rtcp( pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
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));
+ }
+
}
@@ -658,6 +718,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
pjmedia_stream *stream;
pjmedia_codec_param codec_param;
pj_ioqueue_callback ioqueue_cb;
+ pj_uint16_t rtcp_port;
pj_status_t status;
PJ_ASSERT_RETURN(pool && info && p_stream, PJ_EINVAL);
@@ -690,13 +751,13 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
stream->user_data = user_data;
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: */
status = pj_mutex_create_simple(pool, NULL, &stream->jb_mutex);
@@ -738,7 +799,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
/* 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);
/* Create jitter buffer: */
@@ -799,6 +862,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
/* Init pending operation key. */
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);
@@ -900,12 +965,11 @@ PJ_DEF(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream)
* Get stream statistics.
*/
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;
}
diff --git a/pjsip-apps/src/samples/siprtp.c b/pjsip-apps/src/samples/siprtp.c
index d8d18f1d..108d0ca5 100644
--- a/pjsip-apps/src/samples/siprtp.c
+++ b/pjsip-apps/src/samples/siprtp.c
@@ -51,18 +51,6 @@ struct codec
};
-/* 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
{
@@ -87,11 +75,6 @@ struct media_stream
/* 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: */
pj_bool_t thread_quit_flag; /* worker thread quit flag */
@@ -1073,8 +1056,6 @@ static int media_thread(void *arg)
continue;
}
- ++strm->rx_stat.pkt;
- strm->rx_stat.payload += (size - 12);
/* Decode RTP packet. */
status = pjmedia_rtp_decode_rtp(&strm->in_sess,
@@ -1083,27 +1064,15 @@ static int media_thread(void *arg)
&payload, &payload_len);
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);
}
if (rc > 0 && PJ_FD_ISSET(strm->rtcp_sock, &set)) {
@@ -1118,37 +1087,8 @@ static int media_thread(void *arg)
status = pj_sock_recv( strm->rtcp_sock, packet, &size, 0);
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);
}
@@ -1193,10 +1133,6 @@ static int media_thread(void *arg)
/* 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;
}
@@ -1228,24 +1164,7 @@ static int media_thread(void *arg)
app_perror(THIS_FILE, "Error sending RTCP packet", status);
}
-
- /* 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);
}
}
@@ -1318,12 +1237,9 @@ static void call_on_media_update( pjsip_inv_session *inv,
pjmedia_rtp_session_init(&audio->out_sess, audio->si.tx_pt,
pj_rand());
pjmedia_rtp_session_init(&audio->in_sess, audio->si.fmt.pt, 0);
- pjmedia_rtcp_init(&audio->rtcp, audio->clock_rate, 0);
-
+ pjmedia_rtcp_init(&audio->rtcp, audio->clock_rate,
+ audio->samples_per_frame, 0);
- /* Clear media statistics */
- pj_memset(&audio->rx_stat, 0, sizeof(audio->rx_stat));
- pj_memset(&audio->tx_stat, 0, sizeof(audio->tx_stat));
/* Start media thread. */
@@ -1410,16 +1326,15 @@ static void print_call(int call_index)
pjsip_dialog *dlg = inv->dlg;
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);
sprintf(duration, " [duration: %02ld:%02ld:%02ld.%03ld]",
@@ -1488,63 +1403,97 @@ static void print_call(int call_index)
good_number(bps, audio->bytes_per_frame * audio->clock_rate / audio->samples_per_frame),
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;
+ 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 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),
+ 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,
+ ""
+ );
}
diff --git a/pjsip/src/pjsua-lib/pjsua_settings.c b/pjsip/src/pjsua-lib/pjsua_settings.c
index 0c4f081a..e24e9741 100644
--- a/pjsip/src/pjsua-lib/pjsua_settings.c
+++ b/pjsip/src/pjsua-lib/pjsua_settings.c
@@ -647,13 +647,15 @@ static void dump_media_session(pjmedia_session *session)
pjmedia_session_get_info(session, &info);
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];
+ char last_update[40];
+ char packets[16], bytes[16], ipbytes[16];
+ pj_time_val now;
- pjmedia_session_get_stream_stat(session, i, &strm_stat);
+ 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);
@@ -676,15 +678,105 @@ static void dump_media_session(pjmedia_session *session)
info.stream_info[i].fmt.sample_rate / 1000,
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,
+ ""
+ ));
}
}
@@ -712,27 +804,25 @@ void pjsua_dump(pj_bool_t detail)
/* Dump all invite sessions: */
- if (detail) {
- PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
+ PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
- if (pjsua.call_cnt == 0) {
+ if (pjsua.call_cnt == 0) {
- PJ_LOG(3,(THIS_FILE, " - no sessions -"));
+ PJ_LOG(3,(THIS_FILE, " - no sessions -"));
- } else {
- int i;
+ } else {
+ int i;
- for (i=0; i<pjsua.max_calls; ++i) {
+ for (i=0; i<pjsua.max_calls; ++i) {
- if (pjsua.calls[i].inv == NULL)
- continue;
+ if (pjsua.calls[i].inv == NULL)
+ continue;
- print_call(" ", i, buf, sizeof(buf));
- PJ_LOG(3,(THIS_FILE, "%s", buf));
+ 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);
- }
+ if (pjsua.calls[i].session)
+ dump_media_session(pjsua.calls[i].session);
}
}