summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pjmedia/include/pjmedia/rtcp_xr.h12
-rw-r--r--pjmedia/include/pjmedia/stream.h17
-rw-r--r--pjmedia/src/pjmedia/rtcp_xr.c361
-rw-r--r--pjmedia/src/pjmedia/session.c5
-rw-r--r--pjmedia/src/pjmedia/stream.c23
-rw-r--r--pjsip-apps/src/samples/streamutil.c353
6 files changed, 596 insertions, 175 deletions
diff --git a/pjmedia/include/pjmedia/rtcp_xr.h b/pjmedia/include/pjmedia/rtcp_xr.h
index d483366d..cdedc1dd 100644
--- a/pjmedia/include/pjmedia/rtcp_xr.h
+++ b/pjmedia/include/pjmedia/rtcp_xr.h
@@ -229,8 +229,10 @@ typedef struct pjmedia_rtcp_xr_pkt
typedef struct pjmedia_rtcp_xr_stream_stat
{
struct {
- pj_uint32_t begin_seq;
- pj_uint32_t end_seq;
+ pj_time_val update; /**< Time of last update. */
+
+ pj_uint32_t begin_seq; /**< Begin # seq of this interval. */
+ pj_uint32_t end_seq; /**< End # seq of this interval. */
unsigned count; /**< Number of packets. */
/**
@@ -263,6 +265,8 @@ typedef struct pjmedia_rtcp_xr_stream_stat
} stat_sum;
struct {
+ pj_time_val update; /**< Time of last update. */
+
pj_uint8_t loss_rate; /**< Packet loss rate */
pj_uint8_t discard_rate; /**< Packet discarded rate */
pj_uint8_t burst_den; /**< Burst density */
@@ -271,8 +275,8 @@ typedef struct pjmedia_rtcp_xr_stream_stat
pj_uint16_t gap_dur; /**< Gap duration */
pj_uint16_t rnd_trip_delay; /**< Round trip delay */
pj_uint16_t end_sys_delay; /**< End system delay */
- pj_uint8_t signal_lvl; /**< Signal level */
- pj_uint8_t noise_lvl; /**< Noise level */
+ pj_int8_t signal_lvl; /**< Signal level */
+ pj_int8_t noise_lvl; /**< Noise level */
pj_uint8_t rerl; /**< Residual Echo Return Loss */
pj_uint8_t gmin; /**< The gap threshold */
pj_uint8_t r_factor; /**< Voice quality metric carried
diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h
index 64082a8b..159d03a1 100644
--- a/pjmedia/include/pjmedia/stream.h
+++ b/pjmedia/include/pjmedia/stream.h
@@ -95,6 +95,10 @@ struct pjmedia_stream_info
pj_sockaddr rem_rtcp; /**< Optional remote RTCP address. If
sin_family is zero, the RTP address
will be calculated from RTP. */
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+ pj_bool_t rtcp_xr_enabled;
+ /**< Specify whether RTCP XR is enabled.*/
+#endif
pjmedia_codec_info fmt; /**< Incoming codec format info. */
pjmedia_codec_param *param; /**< Optional codec param. */
unsigned tx_pt; /**< Outgoing codec paylaod type. */
@@ -206,6 +210,19 @@ PJ_DECL(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream);
PJ_DECL(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream,
pjmedia_rtcp_stat *stat);
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+/**
+ * Get the stream extended report statistics (RTCP XR).
+ *
+ * @param stream The media stream.
+ * @param stat Media stream extended report statistics.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_stream_get_stat_xr( const pjmedia_stream *stream,
+ pjmedia_rtcp_xr_stat *stat);
+#endif
+
/**
* Pause the individual channel in the stream.
*
diff --git a/pjmedia/src/pjmedia/rtcp_xr.c b/pjmedia/src/pjmedia/rtcp_xr.c
index f8d53896..27a726aa 100644
--- a/pjmedia/src/pjmedia/rtcp_xr.c
+++ b/pjmedia/src/pjmedia/rtcp_xr.c
@@ -86,22 +86,31 @@ void pjmedia_rtcp_xr_init( pjmedia_rtcp_xr_session *session,
{
pj_bzero(session, sizeof(pjmedia_rtcp_xr_session));
+ session->name = parent_session->name;
session->rtcp_session = parent_session;
pj_memcpy(&session->pkt.common, &session->rtcp_session->rtcp_sr_pkt.common,
sizeof(pjmedia_rtcp_common));
session->pkt.common.pt = RTCP_XR;
/* Init config */
- session->stat.tx.voip_mtc.gmin = (pj_uint8_t)(gmin? gmin : DEFAULT_GMIN);
+ session->stat.rx.voip_mtc.gmin = (pj_uint8_t)(gmin? gmin : DEFAULT_GMIN);
session->ptime = session->rtcp_session->pkt_size * 1000 /
session->rtcp_session->clock_rate;
session->frames_per_packet = frames_per_packet;
/* Init Statistics Summary fields which have non-zero default */
- session->stat.tx.stat_sum.jitter.min = (unsigned) -1;
- session->stat.tx.stat_sum.toh.min = (unsigned) -1;
+ session->stat.rx.stat_sum.jitter.min = (unsigned) -1;
+ session->stat.rx.stat_sum.toh.min = (unsigned) -1;
/* Init VoIP Metrics fields which have non-zero default */
+ session->stat.rx.voip_mtc.signal_lvl = 127;
+ session->stat.rx.voip_mtc.noise_lvl = 127;
+ session->stat.rx.voip_mtc.rerl = 127;
+ session->stat.rx.voip_mtc.r_factor = 127;
+ session->stat.rx.voip_mtc.ext_r_factor = 127;
+ session->stat.rx.voip_mtc.mos_lq = 127;
+ session->stat.rx.voip_mtc.mos_cq = 127;
+
session->stat.tx.voip_mtc.signal_lvl = 127;
session->stat.tx.voip_mtc.noise_lvl = 127;
session->stat.tx.voip_mtc.rerl = 127;
@@ -189,7 +198,7 @@ PJ_DEF(void) pjmedia_rtcp_build_rtcp_xr( pjmedia_rtcp_xr_session *sess,
/* Statistics Summary Block */
/* Build this block if we have received packets since last build */
if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_STATS)) &&
- sess->stat.tx.stat_sum.count > 0)
+ sess->stat.rx.stat_sum.count > 0)
{
pjmedia_rtcp_xr_rb_stats *r;
pj_uint8_t specific = 0;
@@ -198,10 +207,10 @@ PJ_DEF(void) pjmedia_rtcp_build_rtcp_xr( pjmedia_rtcp_xr_session *sess,
pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_stats));
/* Init block header */
- specific |= sess->stat.tx.stat_sum.l ? (1 << 7) : 0;
- specific |= sess->stat.tx.stat_sum.d ? (1 << 6) : 0;
- specific |= sess->stat.tx.stat_sum.j ? (1 << 5) : 0;
- specific |= (sess->stat.tx.stat_sum.t & 3) << 3;
+ specific |= sess->stat.rx.stat_sum.l ? (1 << 7) : 0;
+ specific |= sess->stat.rx.stat_sum.d ? (1 << 6) : 0;
+ specific |= sess->stat.rx.stat_sum.j ? (1 << 5) : 0;
+ specific |= (sess->stat.rx.stat_sum.t & 3) << 3;
r->header.bt = BT_STATS;
r->header.specific = specific;
r->header.length = pj_htons(9);
@@ -209,39 +218,40 @@ PJ_DEF(void) pjmedia_rtcp_build_rtcp_xr( pjmedia_rtcp_xr_session *sess,
/* Generate block contents */
r->ssrc = pj_htonl(sess->rtcp_session->peer_ssrc);
r->begin_seq = pj_htons((pj_uint16_t)
- (sess->stat.tx.stat_sum.begin_seq & 0xFFFF));
+ (sess->stat.rx.stat_sum.begin_seq & 0xFFFF));
r->end_seq = pj_htons((pj_uint16_t)
- (sess->stat.tx.stat_sum.end_seq & 0xFFFF));
- if (sess->stat.tx.stat_sum.l) {
- r->lost = pj_htonl(sess->stat.tx.stat_sum.lost);
+ (sess->stat.rx.stat_sum.end_seq & 0xFFFF));
+ if (sess->stat.rx.stat_sum.l) {
+ r->lost = pj_htonl(sess->stat.rx.stat_sum.lost);
}
- if (sess->stat.tx.stat_sum.d) {
- r->dup = pj_htonl(sess->stat.tx.stat_sum.dup);
+ if (sess->stat.rx.stat_sum.d) {
+ r->dup = pj_htonl(sess->stat.rx.stat_sum.dup);
}
- if (sess->stat.tx.stat_sum.j) {
- r->jitter_min = pj_htonl(sess->stat.tx.stat_sum.jitter.min);
- r->jitter_max = pj_htonl(sess->stat.tx.stat_sum.jitter.max);
- r->jitter_mean = pj_htonl(sess->stat.tx.stat_sum.jitter.mean);
- sess->stat.tx.stat_sum.jitter.dev =
- my_isqrt(sess->stat.tx.stat_sum.jitter.dev);
- r->jitter_dev = pj_htonl(sess->stat.tx.stat_sum.jitter.dev);
+ if (sess->stat.rx.stat_sum.j) {
+ r->jitter_min = pj_htonl(sess->stat.rx.stat_sum.jitter.min);
+ r->jitter_max = pj_htonl(sess->stat.rx.stat_sum.jitter.max);
+ r->jitter_mean = pj_htonl(sess->stat.rx.stat_sum.jitter.mean);
+ sess->stat.rx.stat_sum.jitter.dev =
+ my_isqrt(sess->stat.rx.stat_sum.jitter.dev);
+ r->jitter_dev = pj_htonl(sess->stat.rx.stat_sum.jitter.dev);
}
- if (sess->stat.tx.stat_sum.t) {
- r->toh_min = sess->stat.tx.stat_sum.toh.min;
- r->toh_max = sess->stat.tx.stat_sum.toh.max;
- r->toh_mean = sess->stat.tx.stat_sum.toh.mean;
- sess->stat.tx.stat_sum.toh.dev =
- my_isqrt(sess->stat.tx.stat_sum.toh.dev);
- r->toh_dev = sess->stat.tx.stat_sum.toh.dev;
+ if (sess->stat.rx.stat_sum.t) {
+ r->toh_min = sess->stat.rx.stat_sum.toh.min;
+ r->toh_max = sess->stat.rx.stat_sum.toh.max;
+ r->toh_mean = sess->stat.rx.stat_sum.toh.mean;
+ sess->stat.rx.stat_sum.toh.dev =
+ my_isqrt(sess->stat.rx.stat_sum.toh.dev);
+ r->toh_dev = sess->stat.rx.stat_sum.toh.dev;
}
/* Reset TX statistics summary each time built */
- pj_bzero(&sess->stat.tx.stat_sum, sizeof(sess->stat.tx.stat_sum));
- sess->stat.tx.stat_sum.jitter.min = (unsigned) -1;
- sess->stat.tx.stat_sum.toh.min = (unsigned) -1;
+ pj_bzero(&sess->stat.rx.stat_sum, sizeof(sess->stat.rx.stat_sum));
+ sess->stat.rx.stat_sum.jitter.min = (unsigned) -1;
+ sess->stat.rx.stat_sum.toh.min = (unsigned) -1;
/* Finally */
size += sizeof(pjmedia_rtcp_xr_rb_stats);
+ pj_gettimeofday(&sess->stat.rx.stat_sum.update);
}
/* Voip Metrics Block */
@@ -288,45 +298,47 @@ PJ_DEF(void) pjmedia_rtcp_build_rtcp_xr( pjmedia_rtcp_xr_session *sess,
} else {
p23 = 1 - c22/(c22 + c23);
}
- sess->stat.tx.voip_mtc.burst_den = (pj_uint8_t)(256*p23/(p23 + p32));
- sess->stat.tx.voip_mtc.gap_den = (pj_uint8_t)(256*c14/(c11 + c14));
+ sess->stat.rx.voip_mtc.burst_den = (pj_uint8_t)(256*p23/(p23 + p32));
+ sess->stat.rx.voip_mtc.gap_den = (pj_uint8_t)(256*c14/(c11 + c14));
/* Calculate burst and gap durations in ms */
- sess->stat.tx.voip_mtc.gap_dur = (pj_uint16_t)((c11+c14+c13)*m/c13);
- sess->stat.tx.voip_mtc.burst_dur = (pj_uint16_t)(ctotal*m/c13 -
- sess->stat.tx.voip_mtc.gap_dur);
+ sess->stat.rx.voip_mtc.gap_dur = (pj_uint16_t)((c11+c14+c13)*m/c13);
+ sess->stat.rx.voip_mtc.burst_dur = (pj_uint16_t)(ctotal*m/c13 -
+ sess->stat.rx.voip_mtc.gap_dur);
} else {
/* No burst occurred yet until this time?
* Just report full gap.
*/
- ctotal = sess->rtcp_session->stat.rx.pkt;
+ ctotal = sess->rtcp_session->stat.rx.pkt +
+ sess->voip_mtc_stat.loss_count +
+ sess->voip_mtc_stat.discard_count;
- sess->stat.tx.voip_mtc.burst_den = 0;
- sess->stat.tx.voip_mtc.gap_den = (pj_uint8_t)(256 *
+ sess->stat.rx.voip_mtc.burst_den = 0;
+ sess->stat.rx.voip_mtc.gap_den = (pj_uint8_t)(256 *
(sess->voip_mtc_stat.loss_count +
sess->voip_mtc_stat.discard_count) /
ctotal);
/* Calculate burst and gap durations in ms */
- sess->stat.tx.voip_mtc.gap_dur = (pj_uint16_t)((m*ctotal) < 0xFFFF?
+ sess->stat.rx.voip_mtc.gap_dur = (pj_uint16_t)((m*ctotal) < 0xFFFF?
(m*ctotal) : 0xFFFF);
- sess->stat.tx.voip_mtc.burst_dur = 0;
+ sess->stat.rx.voip_mtc.burst_dur = 0;
}
/* Calculate loss and discard rates */
- sess->stat.tx.voip_mtc.loss_rate = (pj_uint8_t)
+ sess->stat.rx.voip_mtc.loss_rate = (pj_uint8_t)
(256 * sess->voip_mtc_stat.loss_count / ctotal);
- sess->stat.tx.voip_mtc.discard_rate = (pj_uint8_t)
+ sess->stat.rx.voip_mtc.discard_rate = (pj_uint8_t)
(256 * sess->voip_mtc_stat.discard_count / ctotal);
/* Set round trip delay (in ms) to RTT calculated after receiving
* DLRR or DLSR.
*/
if (sess->stat.rtt.last)
- sess->stat.tx.voip_mtc.rnd_trip_delay = (pj_uint16_t)
+ sess->stat.rx.voip_mtc.rnd_trip_delay = (pj_uint16_t)
(sess->stat.rtt.last / 1000);
else if (sess->rtcp_session->stat.rtt.last)
- sess->stat.tx.voip_mtc.rnd_trip_delay = (pj_uint16_t)
+ sess->stat.rx.voip_mtc.rnd_trip_delay = (pj_uint16_t)
(sess->rtcp_session->stat.rtt.last / 1000);
/* End system delay estimation = RTT/2 + current jitter buffer size +
@@ -336,35 +348,41 @@ PJ_DEF(void) pjmedia_rtcp_build_rtcp_xr( pjmedia_rtcp_xr_session *sess,
* Since it is difficult to get the exact value of EXTRA, estimation
* is taken to be totally around 50 ms.
*/
- sess->stat.tx.voip_mtc.end_sys_delay = (pj_uint16_t)
- (sess->stat.tx.voip_mtc.rnd_trip_delay / 2 +
- sess->stat.tx.voip_mtc.jb_nom + 50);
+ sess->stat.rx.voip_mtc.end_sys_delay = (pj_uint16_t)
+ (sess->stat.rx.voip_mtc.rnd_trip_delay / 2 +
+ sess->stat.rx.voip_mtc.jb_nom + 50);
/* Generate block contents */
r->ssrc = pj_htonl(sess->rtcp_session->peer_ssrc);
- r->loss_rate = sess->stat.tx.voip_mtc.loss_rate;
- r->discard_rate = sess->stat.tx.voip_mtc.discard_rate;
- r->burst_den = sess->stat.tx.voip_mtc.burst_den;
- r->gap_den = sess->stat.tx.voip_mtc.gap_den;
- r->burst_dur = pj_htons(sess->stat.tx.voip_mtc.burst_dur);
- r->gap_dur = pj_htons(sess->stat.tx.voip_mtc.gap_dur);
- r->rnd_trip_delay = pj_htons(sess->stat.tx.voip_mtc.rnd_trip_delay);
- r->end_sys_delay = pj_htons(sess->stat.tx.voip_mtc.end_sys_delay);
- r->signal_lvl = sess->stat.tx.voip_mtc.signal_lvl;
- r->noise_lvl = sess->stat.tx.voip_mtc.noise_lvl;
- r->rerl = sess->stat.tx.voip_mtc.rerl;
- r->gmin = sess->stat.tx.voip_mtc.gmin;
- r->r_factor = sess->stat.tx.voip_mtc.r_factor;
- r->ext_r_factor = sess->stat.tx.voip_mtc.ext_r_factor;
- r->mos_lq = sess->stat.tx.voip_mtc.mos_lq;
- r->mos_cq = sess->stat.tx.voip_mtc.mos_cq;
- r->rx_config = sess->stat.tx.voip_mtc.rx_config;
- r->jb_nom = pj_htons(sess->stat.tx.voip_mtc.jb_nom);
- r->jb_max = pj_htons(sess->stat.tx.voip_mtc.jb_max);
- r->jb_abs_max = pj_htons(sess->stat.tx.voip_mtc.jb_abs_max);
+ r->loss_rate = sess->stat.rx.voip_mtc.loss_rate;
+ r->discard_rate = sess->stat.rx.voip_mtc.discard_rate;
+ r->burst_den = sess->stat.rx.voip_mtc.burst_den;
+ r->gap_den = sess->stat.rx.voip_mtc.gap_den;
+ r->burst_dur = pj_htons(sess->stat.rx.voip_mtc.burst_dur);
+ r->gap_dur = pj_htons(sess->stat.rx.voip_mtc.gap_dur);
+ r->rnd_trip_delay = pj_htons(sess->stat.rx.voip_mtc.rnd_trip_delay);
+ r->end_sys_delay = pj_htons(sess->stat.rx.voip_mtc.end_sys_delay);
+ /* signal & noise level encoded in two's complement form */
+ r->signal_lvl = (sess->stat.rx.voip_mtc.signal_lvl >= 0)?
+ sess->stat.rx.voip_mtc.signal_lvl :
+ (sess->stat.rx.voip_mtc.signal_lvl + 256);
+ r->noise_lvl = (sess->stat.rx.voip_mtc.noise_lvl >= 0)?
+ sess->stat.rx.voip_mtc.noise_lvl :
+ (sess->stat.rx.voip_mtc.noise_lvl + 256);
+ r->rerl = sess->stat.rx.voip_mtc.rerl;
+ r->gmin = sess->stat.rx.voip_mtc.gmin;
+ r->r_factor = sess->stat.rx.voip_mtc.r_factor;
+ r->ext_r_factor = sess->stat.rx.voip_mtc.ext_r_factor;
+ r->mos_lq = sess->stat.rx.voip_mtc.mos_lq;
+ r->mos_cq = sess->stat.rx.voip_mtc.mos_cq;
+ r->rx_config = sess->stat.rx.voip_mtc.rx_config;
+ r->jb_nom = pj_htons(sess->stat.rx.voip_mtc.jb_nom);
+ r->jb_max = pj_htons(sess->stat.rx.voip_mtc.jb_max);
+ r->jb_abs_max = pj_htons(sess->stat.rx.voip_mtc.jb_abs_max);
/* Finally */
size += sizeof(pjmedia_rtcp_xr_rb_voip_mtc);
+ pj_gettimeofday(&sess->stat.rx.voip_mtc.update);
}
/* Add RTCP XR header size */
@@ -532,66 +550,73 @@ void pjmedia_rtcp_xr_rx_rtcp_xr( pjmedia_rtcp_xr_session *sess,
if (rb_stats) {
pj_uint8_t flags = rb_stats->header.specific;
- pj_bzero(&sess->stat.rx.stat_sum, sizeof(sess->stat.rx.stat_sum));
+ pj_bzero(&sess->stat.tx.stat_sum, sizeof(sess->stat.tx.stat_sum));
/* Range of packets sequence reported in this blocks */
- sess->stat.rx.stat_sum.begin_seq = pj_ntohs(rb_stats->begin_seq);
- sess->stat.rx.stat_sum.end_seq = pj_ntohs(rb_stats->end_seq);
+ sess->stat.tx.stat_sum.begin_seq = pj_ntohs(rb_stats->begin_seq);
+ sess->stat.tx.stat_sum.end_seq = pj_ntohs(rb_stats->end_seq);
/* Get flags of valid fields */
- sess->stat.rx.stat_sum.l = (flags & (1 << 7)) != 0;
- sess->stat.rx.stat_sum.d = (flags & (1 << 6)) != 0;
- sess->stat.rx.stat_sum.j = (flags & (1 << 5)) != 0;
- sess->stat.rx.stat_sum.t = (flags & (3 << 3)) != 0;
+ sess->stat.tx.stat_sum.l = (flags & (1 << 7)) != 0;
+ sess->stat.tx.stat_sum.d = (flags & (1 << 6)) != 0;
+ sess->stat.tx.stat_sum.j = (flags & (1 << 5)) != 0;
+ sess->stat.tx.stat_sum.t = (flags & (3 << 3)) != 0;
/* Fetch the reports info */
- if (sess->stat.rx.stat_sum.l) {
- sess->stat.rx.stat_sum.lost = pj_ntohl(rb_stats->lost);
+ if (sess->stat.tx.stat_sum.l) {
+ sess->stat.tx.stat_sum.lost = pj_ntohl(rb_stats->lost);
}
- if (sess->stat.rx.stat_sum.d) {
- sess->stat.rx.stat_sum.dup = pj_ntohl(rb_stats->dup);
+ if (sess->stat.tx.stat_sum.d) {
+ sess->stat.tx.stat_sum.dup = pj_ntohl(rb_stats->dup);
}
- if (sess->stat.rx.stat_sum.j) {
- sess->stat.rx.stat_sum.jitter.min = pj_ntohl(rb_stats->jitter_min);
- sess->stat.rx.stat_sum.jitter.max = pj_ntohl(rb_stats->jitter_max);
- sess->stat.rx.stat_sum.jitter.mean = pj_ntohl(rb_stats->jitter_mean);
- sess->stat.rx.stat_sum.jitter.dev = pj_ntohl(rb_stats->jitter_dev);
+ if (sess->stat.tx.stat_sum.j) {
+ sess->stat.tx.stat_sum.jitter.min = pj_ntohl(rb_stats->jitter_min);
+ sess->stat.tx.stat_sum.jitter.max = pj_ntohl(rb_stats->jitter_max);
+ sess->stat.tx.stat_sum.jitter.mean = pj_ntohl(rb_stats->jitter_mean);
+ sess->stat.tx.stat_sum.jitter.dev = pj_ntohl(rb_stats->jitter_dev);
}
- if (sess->stat.rx.stat_sum.t) {
- sess->stat.rx.stat_sum.toh.min = rb_stats->toh_min;
- sess->stat.rx.stat_sum.toh.max = rb_stats->toh_max;
- sess->stat.rx.stat_sum.toh.mean = rb_stats->toh_mean;
- sess->stat.rx.stat_sum.toh.dev = rb_stats->toh_dev;
+ if (sess->stat.tx.stat_sum.t) {
+ sess->stat.tx.stat_sum.toh.min = rb_stats->toh_min;
+ sess->stat.tx.stat_sum.toh.max = rb_stats->toh_max;
+ sess->stat.tx.stat_sum.toh.mean = rb_stats->toh_mean;
+ sess->stat.tx.stat_sum.toh.dev = rb_stats->toh_dev;
}
+
+ pj_gettimeofday(&sess->stat.tx.stat_sum.update);
}
/* Receiving VoIP Metrics */
if (rb_voip_mtc) {
- sess->stat.rx.voip_mtc.loss_rate = rb_voip_mtc->loss_rate;
- sess->stat.rx.voip_mtc.discard_rate = rb_voip_mtc->discard_rate;
- sess->stat.rx.voip_mtc.burst_den = rb_voip_mtc->burst_den;
- sess->stat.rx.voip_mtc.gap_den = rb_voip_mtc->gap_den;
- sess->stat.rx.voip_mtc.burst_dur = pj_ntohs(rb_voip_mtc->burst_dur);
- sess->stat.rx.voip_mtc.gap_dur = pj_ntohs(rb_voip_mtc->gap_dur);
- sess->stat.rx.voip_mtc.rnd_trip_delay =
+ sess->stat.tx.voip_mtc.loss_rate = rb_voip_mtc->loss_rate;
+ sess->stat.tx.voip_mtc.discard_rate = rb_voip_mtc->discard_rate;
+ sess->stat.tx.voip_mtc.burst_den = rb_voip_mtc->burst_den;
+ sess->stat.tx.voip_mtc.gap_den = rb_voip_mtc->gap_den;
+ sess->stat.tx.voip_mtc.burst_dur = pj_ntohs(rb_voip_mtc->burst_dur);
+ sess->stat.tx.voip_mtc.gap_dur = pj_ntohs(rb_voip_mtc->gap_dur);
+ sess->stat.tx.voip_mtc.rnd_trip_delay =
pj_ntohs(rb_voip_mtc->rnd_trip_delay);
- sess->stat.rx.voip_mtc.end_sys_delay =
+ sess->stat.tx.voip_mtc.end_sys_delay =
pj_ntohs(rb_voip_mtc->end_sys_delay);
- sess->stat.rx.voip_mtc.signal_lvl = rb_voip_mtc->signal_lvl;
- sess->stat.rx.voip_mtc.noise_lvl = rb_voip_mtc->noise_lvl;
- sess->stat.rx.voip_mtc.rerl = rb_voip_mtc->rerl;
- sess->stat.rx.voip_mtc.gmin = rb_voip_mtc->gmin;
- sess->stat.rx.voip_mtc.r_factor = rb_voip_mtc->r_factor;
- sess->stat.rx.voip_mtc.ext_r_factor = rb_voip_mtc->ext_r_factor;
- sess->stat.rx.voip_mtc.mos_lq = rb_voip_mtc->mos_lq;
- sess->stat.rx.voip_mtc.mos_cq = rb_voip_mtc->mos_cq;
- sess->stat.rx.voip_mtc.rx_config = rb_voip_mtc->rx_config;
- sess->stat.rx.voip_mtc.jb_nom = pj_ntohs(rb_voip_mtc->jb_nom);
- sess->stat.rx.voip_mtc.jb_max = pj_ntohs(rb_voip_mtc->jb_max);
- sess->stat.rx.voip_mtc.jb_abs_max = pj_ntohs(rb_voip_mtc->jb_abs_max);
+ /* signal & noise level encoded in two's complement form */
+ sess->stat.tx.voip_mtc.signal_lvl = (rb_voip_mtc->signal_lvl > 127)?
+ (rb_voip_mtc->signal_lvl - 256) : rb_voip_mtc->signal_lvl;
+ sess->stat.tx.voip_mtc.noise_lvl = (rb_voip_mtc->noise_lvl > 127)?
+ (rb_voip_mtc->noise_lvl - 256) : rb_voip_mtc->noise_lvl;
+ sess->stat.tx.voip_mtc.rerl = rb_voip_mtc->rerl;
+ sess->stat.tx.voip_mtc.gmin = rb_voip_mtc->gmin;
+ sess->stat.tx.voip_mtc.r_factor = rb_voip_mtc->r_factor;
+ sess->stat.tx.voip_mtc.ext_r_factor = rb_voip_mtc->ext_r_factor;
+ sess->stat.tx.voip_mtc.mos_lq = rb_voip_mtc->mos_lq;
+ sess->stat.tx.voip_mtc.mos_cq = rb_voip_mtc->mos_cq;
+ sess->stat.tx.voip_mtc.rx_config = rb_voip_mtc->rx_config;
+ sess->stat.tx.voip_mtc.jb_nom = pj_ntohs(rb_voip_mtc->jb_nom);
+ sess->stat.tx.voip_mtc.jb_max = pj_ntohs(rb_voip_mtc->jb_max);
+ sess->stat.tx.voip_mtc.jb_abs_max = pj_ntohs(rb_voip_mtc->jb_abs_max);
+
+ pj_gettimeofday(&sess->stat.tx.voip_mtc.update);
}
}
@@ -664,75 +689,75 @@ void pjmedia_rtcp_xr_rx_rtp( pjmedia_rtcp_xr_session *sess,
ext_seq = extend_seq(sess, (pj_uint16_t)seq);
/* Update statistics summary */
- sess->stat.tx.stat_sum.count++;
+ sess->stat.rx.stat_sum.count++;
- if (sess->stat.tx.stat_sum.begin_seq == 0 ||
- sess->stat.tx.stat_sum.begin_seq > ext_seq)
+ if (sess->stat.rx.stat_sum.begin_seq == 0 ||
+ sess->stat.rx.stat_sum.begin_seq > ext_seq)
{
- sess->stat.tx.stat_sum.begin_seq = ext_seq;
+ sess->stat.rx.stat_sum.begin_seq = ext_seq;
}
- if (sess->stat.tx.stat_sum.end_seq == 0 ||
- sess->stat.tx.stat_sum.end_seq < ext_seq)
+ if (sess->stat.rx.stat_sum.end_seq == 0 ||
+ sess->stat.rx.stat_sum.end_seq < ext_seq)
{
- sess->stat.tx.stat_sum.end_seq = ext_seq;
+ sess->stat.rx.stat_sum.end_seq = ext_seq;
}
if (lost >= 0) {
- sess->stat.tx.stat_sum.l = PJ_TRUE;
+ sess->stat.rx.stat_sum.l = PJ_TRUE;
if (lost > 0)
- sess->stat.tx.stat_sum.lost++;
+ sess->stat.rx.stat_sum.lost++;
}
if (dup >= 0) {
- sess->stat.tx.stat_sum.d = PJ_TRUE;
+ sess->stat.rx.stat_sum.d = PJ_TRUE;
if (dup > 0)
- sess->stat.tx.stat_sum.dup++;
+ sess->stat.rx.stat_sum.dup++;
}
if (jitter >= 0) {
pj_int32_t diff;
- sess->stat.tx.stat_sum.j = PJ_TRUE;
- if (sess->stat.tx.stat_sum.jitter.min > (pj_uint32_t)jitter)
- sess->stat.tx.stat_sum.jitter.min = jitter;
- if (sess->stat.tx.stat_sum.jitter.max < (pj_uint32_t)jitter)
- sess->stat.tx.stat_sum.jitter.max = jitter;
- sess->stat.tx.stat_sum.jitter.mean =
- (jitter + sess->stat.tx.stat_sum.jitter.mean *
- sess->stat.tx.stat_sum.jitter.count) /
- (sess->stat.tx.stat_sum.jitter.count + 1);
-
- diff = sess->stat.tx.stat_sum.jitter.mean - jitter;
- sess->stat.tx.stat_sum.jitter.dev =
- (diff * diff + sess->stat.tx.stat_sum.jitter.dev *
- sess->stat.tx.stat_sum.jitter.count) /
- (sess->stat.tx.stat_sum.jitter.count + 1);
-
- ++sess->stat.tx.stat_sum.jitter.count;
+ sess->stat.rx.stat_sum.j = PJ_TRUE;
+ if (sess->stat.rx.stat_sum.jitter.min > (pj_uint32_t)jitter)
+ sess->stat.rx.stat_sum.jitter.min = jitter;
+ if (sess->stat.rx.stat_sum.jitter.max < (pj_uint32_t)jitter)
+ sess->stat.rx.stat_sum.jitter.max = jitter;
+ sess->stat.rx.stat_sum.jitter.mean =
+ (jitter + sess->stat.rx.stat_sum.jitter.mean *
+ sess->stat.rx.stat_sum.jitter.count) /
+ (sess->stat.rx.stat_sum.jitter.count + 1);
+
+ diff = sess->stat.rx.stat_sum.jitter.mean - jitter;
+ sess->stat.rx.stat_sum.jitter.dev =
+ (diff * diff + sess->stat.rx.stat_sum.jitter.dev *
+ sess->stat.rx.stat_sum.jitter.count) /
+ (sess->stat.rx.stat_sum.jitter.count + 1);
+
+ ++sess->stat.rx.stat_sum.jitter.count;
}
if (toh >= 0) {
pj_int32_t diff;
- sess->stat.tx.stat_sum.t = toh_ipv4? 1 : 2;
+ sess->stat.rx.stat_sum.t = toh_ipv4? 1 : 2;
- if (sess->stat.tx.stat_sum.toh.min > (pj_uint32_t)toh)
- sess->stat.tx.stat_sum.toh.min = toh;
- if (sess->stat.tx.stat_sum.toh.max < (pj_uint32_t)toh)
- sess->stat.tx.stat_sum.toh.max = toh;
- sess->stat.tx.stat_sum.toh.mean =
- (toh + sess->stat.tx.stat_sum.toh.mean *
- sess->stat.tx.stat_sum.toh.count) /
- (sess->stat.tx.stat_sum.toh.count + 1);
+ if (sess->stat.rx.stat_sum.toh.min > (pj_uint32_t)toh)
+ sess->stat.rx.stat_sum.toh.min = toh;
+ if (sess->stat.rx.stat_sum.toh.max < (pj_uint32_t)toh)
+ sess->stat.rx.stat_sum.toh.max = toh;
+ sess->stat.rx.stat_sum.toh.mean =
+ (toh + sess->stat.rx.stat_sum.toh.mean *
+ sess->stat.rx.stat_sum.toh.count) /
+ (sess->stat.rx.stat_sum.toh.count + 1);
- diff = sess->stat.tx.stat_sum.toh.mean - toh;
- sess->stat.tx.stat_sum.toh.dev =
- (diff * diff + sess->stat.tx.stat_sum.toh.dev *
- sess->stat.tx.stat_sum.toh.count) /
- (sess->stat.tx.stat_sum.toh.count + 1);
+ diff = sess->stat.rx.stat_sum.toh.mean - toh;
+ sess->stat.rx.stat_sum.toh.dev =
+ (diff * diff + sess->stat.rx.stat_sum.toh.dev *
+ sess->stat.rx.stat_sum.toh.count) /
+ (sess->stat.rx.stat_sum.toh.count + 1);
- ++sess->stat.tx.stat_sum.toh.count;
+ ++sess->stat.rx.stat_sum.toh.count;
}
/* Update burst metrics.
@@ -752,7 +777,7 @@ void pjmedia_rtcp_xr_rx_rtp( pjmedia_rtcp_xr_session *sess,
sess->voip_mtc_stat.pkt++;
}
else {
- if(sess->voip_mtc_stat.pkt >= sess->stat.tx.voip_mtc.gmin) {
+ if(sess->voip_mtc_stat.pkt >= sess->stat.rx.voip_mtc.gmin) {
/* Gap condition */
if(sess->voip_mtc_stat.lost == 1) {
/* Gap -> Gap */
@@ -800,60 +825,60 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_xr_update_info(
switch(info) {
case PJMEDIA_RTCP_XR_INFO_SIGNAL_LVL:
- sess->stat.tx.voip_mtc.signal_lvl = (pj_uint8_t) v;
+ sess->stat.rx.voip_mtc.signal_lvl = (pj_int8_t) v;
break;
case PJMEDIA_RTCP_XR_INFO_NOISE_LVL:
- sess->stat.tx.voip_mtc.noise_lvl = (pj_uint8_t) v;
+ sess->stat.rx.voip_mtc.noise_lvl = (pj_int8_t) v;
break;
case PJMEDIA_RTCP_XR_INFO_RERL:
- sess->stat.tx.voip_mtc.rerl = (pj_uint8_t) v;
+ sess->stat.rx.voip_mtc.rerl = (pj_uint8_t) v;
break;
case PJMEDIA_RTCP_XR_INFO_R_FACTOR:
- sess->stat.tx.voip_mtc.ext_r_factor = (pj_uint8_t) v;
+ sess->stat.rx.voip_mtc.ext_r_factor = (pj_uint8_t) v;
break;
case PJMEDIA_RTCP_XR_INFO_MOS_LQ:
- sess->stat.tx.voip_mtc.mos_lq = (pj_uint8_t) v;
+ sess->stat.rx.voip_mtc.mos_lq = (pj_uint8_t) v;
break;
case PJMEDIA_RTCP_XR_INFO_MOS_CQ:
- sess->stat.tx.voip_mtc.mos_cq = (pj_uint8_t) v;
+ sess->stat.rx.voip_mtc.mos_cq = (pj_uint8_t) v;
break;
case PJMEDIA_RTCP_XR_INFO_CONF_PLC:
if (v >= 0 && v <= 3) {
- sess->stat.tx.voip_mtc.rx_config &= 0x3F;
- sess->stat.tx.voip_mtc.rx_config |= (pj_uint8_t) (v << 6);
+ sess->stat.rx.voip_mtc.rx_config &= 0x3F;
+ sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) (v << 6);
}
break;
case PJMEDIA_RTCP_XR_INFO_CONF_JBA:
if (v >= 0 && v <= 3) {
- sess->stat.tx.voip_mtc.rx_config &= 0xCF;
- sess->stat.tx.voip_mtc.rx_config |= (pj_uint8_t) (v << 4);
+ sess->stat.rx.voip_mtc.rx_config &= 0xCF;
+ sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) (v << 4);
}
break;
case PJMEDIA_RTCP_XR_INFO_CONF_JBR:
if (v >= 0 && v <= 15) {
- sess->stat.tx.voip_mtc.rx_config &= 0xF0;
- sess->stat.tx.voip_mtc.rx_config |= (pj_uint8_t) v;
+ sess->stat.rx.voip_mtc.rx_config &= 0xF0;
+ sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) v;
}
break;
case PJMEDIA_RTCP_XR_INFO_JB_NOM:
- sess->stat.tx.voip_mtc.jb_nom = (pj_uint16_t) v;
+ sess->stat.rx.voip_mtc.jb_nom = (pj_uint16_t) v;
break;
case PJMEDIA_RTCP_XR_INFO_JB_MAX:
- sess->stat.tx.voip_mtc.jb_max = (pj_uint16_t) v;
+ sess->stat.rx.voip_mtc.jb_max = (pj_uint16_t) v;
break;
case PJMEDIA_RTCP_XR_INFO_JB_ABS_MAX:
- sess->stat.tx.voip_mtc.jb_abs_max = (pj_uint16_t) v;
+ sess->stat.rx.voip_mtc.jb_abs_max = (pj_uint16_t) v;
break;
default:
diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c
index fdbbb0fc..e8e65951 100644
--- a/pjmedia/src/pjmedia/session.c
+++ b/pjmedia/src/pjmedia/session.c
@@ -166,6 +166,11 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
pj_bzero(si, sizeof(*si));
+#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
+ /* Set default RTCP XR enabled/disabled */
+ si->rtcp_xr_enabled = PJ_TRUE;
+#endif
+
/* Media type: */
if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0) {
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 6673b833..d826ab70 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -1557,10 +1557,11 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
stream->transport = tp;
-#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
- /* Enable RTCP XR and update some settings */
- {
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+ /* Enable RTCP XR and update stream info/config to RTCP XR */
+ if (info->rtcp_xr_enabled) {
int i;
+
pjmedia_rtcp_enable_xr(&stream->rtcp, PJ_TRUE);
/* jitter buffer adaptive info */
@@ -1713,6 +1714,22 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream,
return PJ_SUCCESS;
}
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+/*
+ * Get stream extended statistics.
+ */
+PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr( const pjmedia_stream *stream,
+ pjmedia_rtcp_xr_stat *stat)
+{
+ PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL);
+
+ if (stream->rtcp.xr_enabled) {
+ pj_memcpy(stat, &stream->rtcp.xr_session.stat, sizeof(pjmedia_rtcp_xr_stat));
+ return PJ_SUCCESS;
+ }
+ return PJ_ENOTFOUND;
+}
+#endif
/*
* Pause stream.
diff --git a/pjsip-apps/src/samples/streamutil.c b/pjsip-apps/src/samples/streamutil.c
index 0f71a76b..dfd2ee54 100644
--- a/pjsip-apps/src/samples/streamutil.c
+++ b/pjsip-apps/src/samples/streamutil.c
@@ -168,6 +168,10 @@ static pj_status_t create_stream( pj_pool_t *pool,
info.tx_pt = codec_info->pt;
info.ssrc = pj_rand();
+#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
+ /* Set default RTCP XR enabled/disabled */
+ info.rtcp_xr_enabled = PJ_TRUE;
+#endif
/* Copy remote address */
pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
@@ -695,6 +699,22 @@ static const char *good_number(char *buf, pj_int32_t val)
}
+#define SAMPLES_TO_USEC(usec, samples, clock_rate) \
+ do { \
+ if (samples <= 4294) \
+ usec = samples * 1000000 / clock_rate; \
+ else { \
+ usec = samples * 1000 / clock_rate; \
+ usec *= 1000; \
+ } \
+ } while(0)
+
+#define PRINT_VOIP_MTC_VAL(s, v) \
+ if (v == 127) \
+ sprintf(s, "(na)"); \
+ else \
+ sprintf(s, "%d", v)
+
/*
* Print stream statistics
@@ -827,5 +847,338 @@ static void print_stream_stat(pjmedia_stream *stream)
""
);
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+ /* RTCP XR Reports */
+ do {
+ char loss[16], dup[16];
+ char jitter[80];
+ char toh[80];
+ char plc[16], jba[16], jbr[16];
+ char signal_lvl[16], noise_lvl[16], rerl[16];
+ char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
+ pjmedia_rtcp_xr_stat xr_stat;
+
+ if (pjmedia_stream_get_stat_xr(stream, &xr_stat) != PJ_SUCCESS)
+ break;
+
+ puts("\nExtended reports:");
+
+ /* Statistics Summary */
+ puts(" Statistics Summary");
+
+ if (xr_stat.rx.stat_sum.l)
+ sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
+ else
+ sprintf(loss, "(na)");
+
+ if (xr_stat.rx.stat_sum.d)
+ sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
+ else
+ sprintf(dup, "(na)");
+
+ if (xr_stat.rx.stat_sum.j) {
+ unsigned jmin, jmax, jmean, jdev;
+
+ SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
+ port->info.clock_rate);
+ SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
+ port->info.clock_rate);
+ SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
+ port->info.clock_rate);
+ SAMPLES_TO_USEC(jdev, xr_stat.rx.stat_sum.jitter.dev,
+ port->info.clock_rate);
+ sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
+ jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
+ } else
+ sprintf(jitter, "(report not available)");
+
+ if (xr_stat.rx.stat_sum.t) {
+ sprintf(toh, "%11d %11d %11d %11d",
+ xr_stat.rx.stat_sum.toh.min,
+ xr_stat.rx.stat_sum.toh.mean,
+ xr_stat.rx.stat_sum.toh.max,
+ xr_stat.rx.stat_sum.toh.dev);
+ } else
+ sprintf(toh, "(report not available)");
+
+ if (xr_stat.rx.stat_sum.update.sec == 0)
+ strcpy(last_update, "never");
+ else {
+ pj_gettimeofday(&now);
+ PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
+ sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
+ now.sec / 3600,
+ (now.sec % 3600) / 60,
+ now.sec % 60,
+ now.msec);
+ }
+
+ printf(" RX last update: %s\n"
+ " begin seq=%d, end seq=%d%s\n"
+ " pkt loss=%s, dup=%s%s\n"
+ " (msec) min avg max dev\n"
+ " jitter : %s\n"
+ " toh : %s\n",
+ last_update,
+ xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
+ "",
+ loss, dup,
+ "",
+ jitter,
+ toh
+ );
+
+ if (xr_stat.tx.stat_sum.l)
+ sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
+ else
+ sprintf(loss, "(na)");
+
+ if (xr_stat.tx.stat_sum.d)
+ sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
+ else
+ sprintf(dup, "(na)");
+
+ if (xr_stat.tx.stat_sum.j) {
+ unsigned jmin, jmax, jmean, jdev;
+
+ SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
+ port->info.clock_rate);
+ SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
+ port->info.clock_rate);
+ SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
+ port->info.clock_rate);
+ SAMPLES_TO_USEC(jdev, xr_stat.tx.stat_sum.jitter.dev,
+ port->info.clock_rate);
+ sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
+ jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
+ } else
+ sprintf(jitter, "(report not available)");
+
+ if (xr_stat.tx.stat_sum.t) {
+ sprintf(toh, "%11d %11d %11d %11d",
+ xr_stat.tx.stat_sum.toh.min,
+ xr_stat.tx.stat_sum.toh.mean,
+ xr_stat.tx.stat_sum.toh.max,
+ xr_stat.tx.stat_sum.toh.dev);
+ } else
+ sprintf(toh, "(report not available)");
+
+ if (xr_stat.tx.stat_sum.update.sec == 0)
+ strcpy(last_update, "never");
+ else {
+ pj_gettimeofday(&now);
+ PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
+ sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
+ now.sec / 3600,
+ (now.sec % 3600) / 60,
+ now.sec % 60,
+ now.msec);
+ }
+
+ printf(" TX last update: %s\n"
+ " begin seq=%d, end seq=%d%s\n"
+ " pkt loss=%s, dup=%s%s\n"
+ " (msec) min avg max dev\n"
+ " jitter : %s\n"
+ " toh : %s\n",
+ last_update,
+ xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
+ "",
+ loss, dup,
+ "",
+ jitter,
+ toh
+ );
+
+ /* VoIP Metrics */
+ puts(" VoIP Metrics");
+
+ PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
+ PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
+ PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
+ PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
+ PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
+ PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
+ PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
+
+ switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
+ case PJMEDIA_RTCP_XR_PLC_DIS:
+ sprintf(plc, "DISABLED");
+ break;
+ case PJMEDIA_RTCP_XR_PLC_ENH:
+ sprintf(plc, "ENHANCED");
+ break;
+ case PJMEDIA_RTCP_XR_PLC_STD:
+ sprintf(plc, "STANDARD");
+ break;
+ case PJMEDIA_RTCP_XR_PLC_UNK:
+ default:
+ sprintf(plc, "UNKNOWN");
+ break;
+ }
+
+ switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
+ case PJMEDIA_RTCP_XR_JB_FIXED:
+ sprintf(jba, "FIXED");
+ break;
+ case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
+ sprintf(jba, "ADAPTIVE");
+ break;
+ default:
+ sprintf(jba, "UNKNOWN");
+ break;
+ }
+
+ sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
+
+ if (xr_stat.rx.voip_mtc.update.sec == 0)
+ strcpy(last_update, "never");
+ else {
+ pj_gettimeofday(&now);
+ PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
+ sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
+ now.sec / 3600,
+ (now.sec % 3600) / 60,
+ now.sec % 60,
+ now.msec);
+ }
+
+ printf(" RX last update: %s\n"
+ " packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
+ " burst : density=%d (%.2f%%), duration=%d%s\n"
+ " gap : density=%d (%.2f%%), duration=%d%s\n"
+ " delay : round trip=%d%s, end system=%d%s\n"
+ " level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
+ " quality : R factor=%s, ext R factor=%s\n"
+ " MOS LQ=%s, MOS CQ=%s\n"
+ " config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
+ " JB delay : cur=%d%s, max=%d%s, abs max=%d%s\n",
+ last_update,
+ /* pakcets */
+ xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
+ xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
+ /* burst */
+ xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
+ xr_stat.rx.voip_mtc.burst_dur, "ms",
+ /* gap */
+ xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
+ xr_stat.rx.voip_mtc.gap_dur, "ms",
+ /* delay */
+ xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
+ xr_stat.rx.voip_mtc.end_sys_delay, "ms",
+ /* level */
+ signal_lvl, "dB",
+ noise_lvl, "dB",
+ rerl, "",
+ /* quality */
+ r_factor, ext_r_factor, mos_lq, mos_cq,
+ /* config */
+ plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
+ /* JB delay */
+ xr_stat.rx.voip_mtc.jb_nom, "ms",
+ xr_stat.rx.voip_mtc.jb_max, "ms",
+ xr_stat.rx.voip_mtc.jb_abs_max, "ms"
+ );
+
+ PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
+ PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
+ PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
+ PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
+ PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
+ PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
+ PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
+
+ switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
+ case PJMEDIA_RTCP_XR_PLC_DIS:
+ sprintf(plc, "DISABLED");
+ break;
+ case PJMEDIA_RTCP_XR_PLC_ENH:
+ sprintf(plc, "ENHANCED");
+ break;
+ case PJMEDIA_RTCP_XR_PLC_STD:
+ sprintf(plc, "STANDARD");
+ break;
+ case PJMEDIA_RTCP_XR_PLC_UNK:
+ default:
+ sprintf(plc, "unknown");
+ break;
+ }
+
+ switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
+ case PJMEDIA_RTCP_XR_JB_FIXED:
+ sprintf(jba, "FIXED");
+ break;
+ case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
+ sprintf(jba, "ADAPTIVE");
+ break;
+ default:
+ sprintf(jba, "unknown");
+ break;
+ }
+
+ sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
+
+ if (xr_stat.tx.voip_mtc.update.sec == 0)
+ strcpy(last_update, "never");
+ else {
+ pj_gettimeofday(&now);
+ PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
+ sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
+ now.sec / 3600,
+ (now.sec % 3600) / 60,
+ now.sec % 60,
+ now.msec);
+ }
+
+ printf(" TX last update: %s\n"
+ " packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
+ " burst : density=%d (%.2f%%), duration=%d%s\n"
+ " gap : density=%d (%.2f%%), duration=%d%s\n"
+ " delay : round trip=%d%s, end system=%d%s\n"
+ " level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
+ " quality : R factor=%s, ext R factor=%s\n"
+ " MOS LQ=%s, MOS CQ=%s\n"
+ " config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
+ " JB delay : cur=%d%s, max=%d%s, abs max=%d%s\n",
+ last_update,
+ /* pakcets */
+ xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
+ xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
+ /* burst */
+ xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
+ xr_stat.tx.voip_mtc.burst_dur, "ms",
+ /* gap */
+ xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
+ xr_stat.tx.voip_mtc.gap_dur, "ms",
+ /* delay */
+ xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
+ xr_stat.tx.voip_mtc.end_sys_delay, "ms",
+ /* level */
+ signal_lvl, "dB",
+ noise_lvl, "dB",
+ rerl, "",
+ /* quality */
+ r_factor, ext_r_factor, mos_lq, mos_cq,
+ /* config */
+ plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
+ /* JB delay */
+ xr_stat.tx.voip_mtc.jb_nom, "ms",
+ xr_stat.tx.voip_mtc.jb_max, "ms",
+ xr_stat.tx.voip_mtc.jb_abs_max, "ms"
+ );
+
+
+ /* RTT delay, need this? */
+ printf(" (msec) min avg max last\n");
+ printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f%s\n",
+ xr_stat.rtt.min / 1000.0,
+ xr_stat.rtt.avg / 1000.0,
+ xr_stat.rtt.max / 1000.0,
+ xr_stat.rtt.last / 1000.0,
+ ""
+ );
+ } while (0);
+#endif /* PJMEDIA_HAS_RTCP_XR */
+
}