From dc883a480f6dcf1e7c7a389675e3fc312b3f8e24 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 29 Apr 2008 17:15:41 +0000 Subject: More on ticket #513: - Added RTCP XR print reports to streamutil.c - Added new API pjmedia_stream_get_stat_xr() - Added field rtcp_xr_enabled to stream info structure - Swapped the wrong RTCP XR statistic storage (encoding direction should be stored in TX, decoding direction in RX, it was the opposite) git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1943 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/include/pjmedia/rtcp_xr.h | 12 +- pjmedia/include/pjmedia/stream.h | 17 ++ pjmedia/src/pjmedia/rtcp_xr.c | 361 +++++++++++++++++++----------------- pjmedia/src/pjmedia/session.c | 5 + pjmedia/src/pjmedia/stream.c | 23 ++- pjsip-apps/src/samples/streamutil.c | 353 +++++++++++++++++++++++++++++++++++ 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 */ + } -- cgit v1.2.3