/* * Get jitter buffer state. */ PJ_DEF(pj_status_t) pjmedia_jbuf_get_state( const pjmedia_jbuf *jb, pjmedia_jb_state *state ) { PJ_ASSERT_RETURN(jb && state, PJ_EINVAL); state->frame_size = jb->jb_frame_size; state->min_prefetch = jb->jb_min_prefetch; state->max_prefetch = jb->jb_max_prefetch; state->burst = jb->jb_eff_level; state->prefetch = jb->jb_prefetch; state->size = jb_framelist_eff_size(&jb->jb_framelist); state->avg_delay = jb->jb_delay.mean; state->min_delay = jb->jb_delay.min; state->max_delay = jb->jb_delay.max; state->dev_delay = pj_math_stat_get_stddev(&jb->jb_delay); state->avg_burst = jb->jb_burst.mean; state->empty = jb->jb_empty; state->discard = jb->jb_discard; state->lost = jb->jb_lost; return PJ_SUCCESS; }
PJ_DEF(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb) { PJ_LOG(5, (jb->jb_name.ptr, "" "JB summary:\n" " size=%d/eff=%d prefetch=%d level=%d\n" " delay (min/max/avg/dev)=%d/%d/%d/%d ms\n" " burst (min/max/avg/dev)=%d/%d/%d/%d frames\n" " lost=%d discard=%d empty=%d", jb_framelist_size(&jb->jb_framelist), jb_framelist_eff_size(&jb->jb_framelist), jb->jb_prefetch, jb->jb_eff_level, jb->jb_delay.min, jb->jb_delay.max, jb->jb_delay.mean, pj_math_stat_get_stddev(&jb->jb_delay), jb->jb_burst.min, jb->jb_burst.max, jb->jb_burst.mean, pj_math_stat_get_stddev(&jb->jb_burst), jb->jb_lost, jb->jb_discard, jb->jb_empty)); return jb_framelist_destroy(&jb->jb_framelist); }
PJ_DEF(pj_status_t) pjmedia_aud_test( const pjmedia_aud_param *param, pjmedia_aud_test_results *result) { pj_status_t status = PJ_SUCCESS; pjmedia_aud_stream *strm; struct test_data test_data; unsigned ptime, tmp; /* * Init test parameters */ pj_bzero(&test_data, sizeof(test_data)); test_data.param = param; test_data.result = result; test_data.pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "audtest", 1000, 1000, NULL); pj_mutex_create_simple(test_data.pool, "sndtest", &test_data.mutex); /* * Open device. */ status = pjmedia_aud_stream_create(test_data.param, &rec_cb, &play_cb, &test_data, &strm); if (status != PJ_SUCCESS) { app_perror("Unable to open device", status); pj_pool_release(test_data.pool); return status; } /* Sleep for a while to let sound device "settles" */ pj_thread_sleep(200); /* * Start the stream. */ status = pjmedia_aud_stream_start(strm); if (status != PJ_SUCCESS) { app_perror("Unable to start capture stream", status); pjmedia_aud_stream_destroy(strm); pj_pool_release(test_data.pool); return status; } PJ_LOG(3,(THIS_FILE, " Please wait while test is in progress (~%d secs)..", (DURATION+SKIP_DURATION)/1000)); /* Let the stream runs for few msec/sec to get stable result. * (capture normally begins with frames available simultaneously). */ pj_thread_sleep(SKIP_DURATION); /* Begin gather data */ test_data.running = 1; /* * Let the test runs for a while. */ pj_thread_sleep(DURATION); /* * Close stream. */ test_data.running = 0; pjmedia_aud_stream_destroy(strm); pj_pool_release(test_data.pool); /* * Gather results */ ptime = param->samples_per_frame * 1000 / param->clock_rate; tmp = pj_math_stat_get_stddev(&test_data.capture_data.delay); result->rec.frame_cnt = test_data.capture_data.delay.n; result->rec.min_interval = DIV_ROUND(test_data.capture_data.delay.min, 1000); result->rec.max_interval = DIV_ROUND(test_data.capture_data.delay.max, 1000); result->rec.avg_interval = DIV_ROUND(test_data.capture_data.delay.mean, 1000); result->rec.dev_interval = DIV_ROUND(tmp, 1000); result->rec.max_burst = DIV_ROUND_UP(result->rec.max_interval, ptime); tmp = pj_math_stat_get_stddev(&test_data.playback_data.delay); result->play.frame_cnt = test_data.playback_data.delay.n; result->play.min_interval = DIV_ROUND(test_data.playback_data.delay.min, 1000); result->play.max_interval = DIV_ROUND(test_data.playback_data.delay.max, 1000); result->play.avg_interval = DIV_ROUND(test_data.capture_data.delay.mean, 1000); result->play.dev_interval = DIV_ROUND(tmp, 1000); result->play.max_burst = DIV_ROUND_UP(result->play.max_interval, ptime); /* Check drifting */ if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) { int end_diff, start_diff, drift; end_diff = test_data.capture_data.last_timestamp - test_data.playback_data.last_timestamp; start_diff = test_data.capture_data.first_timestamp- test_data.playback_data.first_timestamp; drift = end_diff > start_diff? end_diff - start_diff : start_diff - end_diff; /* Allow one frame tolerance for clock drift detection */ if (drift < (int)param->samples_per_frame) { result->rec_drift_per_sec = 0; } else { unsigned msec_dur; msec_dur = (test_data.capture_data.last_timestamp - test_data.capture_data.first_timestamp) * 1000 / test_data.param->clock_rate; result->rec_drift_per_sec = drift * 1000 / msec_dur; } } return test_data.has_error? PJ_EUNKNOWN : PJ_SUCCESS; }
static unsigned dump_media_stat(const char *indent, char *buf, unsigned maxlen, const pjmedia_rtcp_stat *stat, const char *rx_info, const char *tx_info) { char last_update[64]; char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32]; pj_time_val media_duration, now; char *p = buf, *end = buf+maxlen; int len; 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, "%02ldh:%02ldm:%02ld.%03lds ago", now.sec / 3600, (now.sec % 3600) / 60, now.sec % 60, now.msec); } pj_gettimeofday(&media_duration); PJ_TIME_VAL_SUB(media_duration, stat->start); if (PJ_TIME_VAL_MSEC(media_duration) == 0) media_duration.msec = 1; len = pj_ansi_snprintf(p, end-p, "%s RX %s last update:%s\n" "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n" "%s pkt loss=%d (%3.1f%%), discrd=%d (%3.1f%%), dup=%d (%2.1f%%), reord=%d (%3.1f%%)\n" "%s (msec) min avg max last dev\n" "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n" "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f\n" #if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0 "%s raw jitter : %7.3f %7.3f %7.3f %7.3f %7.3f\n" #endif #if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0 "%s IPDV : %7.3f %7.3f %7.3f %7.3f %7.3f\n" #endif "%s", indent, rx_info? rx_info : "", last_update, indent, good_number(packets, stat->rx.pkt), good_number(bytes, stat->rx.bytes), good_number(ipbytes, stat->rx.bytes + stat->rx.pkt * 40), good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat->rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))), good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat->rx.bytes + stat->rx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))), indent, stat->rx.loss, (stat->rx.loss? stat->rx.loss * 100.0 / (stat->rx.pkt + stat->rx.loss) : 0), stat->rx.discard, (stat->rx.discard? stat->rx.discard * 100.0 / (stat->rx.pkt + stat->rx.loss) : 0), stat->rx.dup, (stat->rx.dup? stat->rx.dup * 100.0 / (stat->rx.pkt + stat->rx.loss) : 0), stat->rx.reorder, (stat->rx.reorder? stat->rx.reorder * 100.0 / (stat->rx.pkt + stat->rx.loss) : 0), indent, indent, stat->rx.loss_period.min / 1000.0, stat->rx.loss_period.mean / 1000.0, stat->rx.loss_period.max / 1000.0, stat->rx.loss_period.last / 1000.0, pj_math_stat_get_stddev(&stat->rx.loss_period) / 1000.0, indent, stat->rx.jitter.min / 1000.0, stat->rx.jitter.mean / 1000.0, stat->rx.jitter.max / 1000.0, stat->rx.jitter.last / 1000.0, pj_math_stat_get_stddev(&stat->rx.jitter) / 1000.0, #if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0 indent, stat->rx_raw_jitter.min / 1000.0, stat->rx_raw_jitter.mean / 1000.0, stat->rx_raw_jitter.max / 1000.0, stat->rx_raw_jitter.last / 1000.0, pj_math_stat_get_stddev(&stat->rx_raw_jitter) / 1000.0, #endif #if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0 indent, stat->rx_ipdv.min / 1000.0, stat->rx_ipdv.mean / 1000.0, stat->rx_ipdv.max / 1000.0, stat->rx_ipdv.last / 1000.0, pj_math_stat_get_stddev(&stat->rx_ipdv) / 1000.0, #endif "" ); if (len < 1 || len > end-p) { *p = '\0'; return (p-buf); } p += len; 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, "%02ldh:%02ldm:%02ld.%03lds ago", now.sec / 3600, (now.sec % 3600) / 60, now.sec % 60, now.msec); } len = pj_ansi_snprintf(p, end-p, "%s TX %s last update:%s\n" "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n" "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n" "%s (msec) min avg max last dev \n" "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n" "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f\n", indent, tx_info, last_update, indent, good_number(packets, stat->tx.pkt), good_number(bytes, stat->tx.bytes), good_number(ipbytes, stat->tx.bytes + stat->tx.pkt * 40), good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat->tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))), good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat->tx.bytes + stat->tx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))), indent, stat->tx.loss, (stat->tx.loss? stat->tx.loss * 100.0 / (stat->tx.pkt + stat->tx.loss) : 0), stat->tx.dup, (stat->tx.dup? stat->tx.dup * 100.0 / (stat->tx.pkt + stat->tx.loss) : 0), stat->tx.reorder, (stat->tx.reorder? stat->tx.reorder * 100.0 / (stat->tx.pkt + stat->tx.loss) : 0), indent, indent, stat->tx.loss_period.min / 1000.0, stat->tx.loss_period.mean / 1000.0, stat->tx.loss_period.max / 1000.0, stat->tx.loss_period.last / 1000.0, pj_math_stat_get_stddev(&stat->tx.loss_period) / 1000.0, indent, stat->tx.jitter.min / 1000.0, stat->tx.jitter.mean / 1000.0, stat->tx.jitter.max / 1000.0, stat->tx.jitter.last / 1000.0, pj_math_stat_get_stddev(&stat->tx.jitter) / 1000.0 ); if (len < 1 || len > end-p) { *p = '\0'; return (p-buf); } p += len; len = pj_ansi_snprintf(p, end-p, "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f\n", indent, stat->rtt.min / 1000.0, stat->rtt.mean / 1000.0, stat->rtt.max / 1000.0, stat->rtt.last / 1000.0, pj_math_stat_get_stddev(&stat->rtt) / 1000.0 ); if (len < 1 || len > end-p) { *p = '\0'; return (p-buf); } p += len; return (p-buf); }
/* Dump media session */ static void dump_media_session(const char *indent, char *buf, unsigned maxlen, pjsua_call *call) { unsigned i; char *p = buf, *end = buf+maxlen; int len; for (i=0; i<call->med_cnt; ++i) { pjsua_call_media *call_med = &call->media[i]; pjmedia_rtcp_stat stat; pj_bool_t has_stat; pjmedia_transport_info tp_info; char rem_addr_buf[80]; char codec_info[32] = {'0'}; char rx_info[80] = {'\0'}; char tx_info[80] = {'\0'}; const char *rem_addr; const char *dir_str; const char *media_type_str; switch (call_med->type) { case PJMEDIA_TYPE_AUDIO: media_type_str = "audio"; break; case PJMEDIA_TYPE_VIDEO: media_type_str = "video"; break; case PJMEDIA_TYPE_APPLICATION: media_type_str = "application"; break; default: media_type_str = "unknown"; break; } /* Check if the stream is deactivated */ if (call_med->tp == NULL || (!call_med->strm.a.stream && !call_med->strm.v.stream)) { len = pj_ansi_snprintf(p, end-p, "%s #%d %s deactivated\n", indent, i, media_type_str); if (len < 1 || len > end-p) { *p = '\0'; return; } p += len; continue; } pjmedia_transport_info_init(&tp_info); pjmedia_transport_get_info(call_med->tp, &tp_info); // rem_addr will contain actual address of RTP originator, instead of // remote RTP address specified by stream which is fetched from the SDP. // Please note that we are assuming only one stream per call. //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr, // rem_addr_buf, sizeof(rem_addr_buf), 3); if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) { rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf, sizeof(rem_addr_buf), 3); } else { pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-"); rem_addr = rem_addr_buf; } if (call_med->dir == PJMEDIA_DIR_NONE) { /* To handle when the stream that is currently being paused * (http://trac.pjsip.org/repos/ticket/1079) */ dir_str = "inactive"; } else if (call_med->dir == PJMEDIA_DIR_ENCODING) dir_str = "sendonly"; else if (call_med->dir == PJMEDIA_DIR_DECODING) dir_str = "recvonly"; else if (call_med->dir == PJMEDIA_DIR_ENCODING_DECODING) dir_str = "sendrecv"; else dir_str = "inactive"; if (call_med->type == PJMEDIA_TYPE_AUDIO) { pjmedia_stream *stream = call_med->strm.a.stream; pjmedia_stream_info info; pjmedia_stream_get_stat(stream, &stat); has_stat = PJ_TRUE; pjmedia_stream_get_info(stream, &info); pj_ansi_snprintf(codec_info, sizeof(codec_info), " %.*s @%dkHz", (int)info.fmt.encoding_name.slen, info.fmt.encoding_name.ptr, info.fmt.clock_rate / 1000); pj_ansi_snprintf(rx_info, sizeof(rx_info), "pt=%d,", info.rx_pt); pj_ansi_snprintf(tx_info, sizeof(tx_info), "pt=%d, ptime=%d,", info.tx_pt, info.param->setting.frm_per_pkt* info.param->info.frm_ptime); #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) } else if (call_med->type == PJMEDIA_TYPE_VIDEO) { pjmedia_vid_stream *stream = call_med->strm.v.stream; pjmedia_vid_stream_info info; pjmedia_vid_stream_get_stat(stream, &stat); has_stat = PJ_TRUE; pjmedia_vid_stream_get_info(stream, &info); pj_ansi_snprintf(codec_info, sizeof(codec_info), " %.*s", (int)info.codec_info.encoding_name.slen, info.codec_info.encoding_name.ptr); if (call_med->dir & PJMEDIA_DIR_DECODING) { pjmedia_video_format_detail *vfd; vfd = pjmedia_format_get_video_format_detail( &info.codec_param->dec_fmt, PJ_TRUE); pj_ansi_snprintf(rx_info, sizeof(rx_info), "pt=%d, size=%dx%d, fps=%.2f,", info.rx_pt, vfd->size.w, vfd->size.h, vfd->fps.num*1.0/vfd->fps.denum); } if (call_med->dir & PJMEDIA_DIR_ENCODING) { pjmedia_video_format_detail *vfd; vfd = pjmedia_format_get_video_format_detail( &info.codec_param->enc_fmt, PJ_TRUE); pj_ansi_snprintf(tx_info, sizeof(tx_info), "pt=%d, size=%dx%d, fps=%.2f,", info.tx_pt, vfd->size.w, vfd->size.h, vfd->fps.num*1.0/vfd->fps.denum); } #endif /* PJMEDIA_HAS_VIDEO */ } else { has_stat = PJ_FALSE; } len = pj_ansi_snprintf(p, end-p, "%s #%d %s%s, %s, peer=%s\n", indent, call_med->idx, media_type_str, codec_info, dir_str, rem_addr); if (len < 1 || len > end-p) { *p = '\0'; return; } p += len; /* Get and ICE SRTP status */ if (call_med->tp) { pjmedia_transport_info tp_info; pjmedia_transport_info_init(&tp_info); pjmedia_transport_get_info(call_med->tp, &tp_info); if (tp_info.specific_info_cnt > 0) { unsigned j; for (j = 0; j < tp_info.specific_info_cnt; ++j) { if (tp_info.spc_info[j].type == PJMEDIA_TRANSPORT_TYPE_SRTP) { pjmedia_srtp_info *srtp_info = (pjmedia_srtp_info*) tp_info.spc_info[j].buffer; len = pj_ansi_snprintf(p, end-p, " %s SRTP status: %s Crypto-suite: %s", indent, (srtp_info->active?"Active":"Not active"), srtp_info->tx_policy.name.ptr); if (len > 0 && len < end-p) { p += len; *p++ = '\n'; *p = '\0'; } } else if (tp_info.spc_info[j].type==PJMEDIA_TRANSPORT_TYPE_ICE) { const pjmedia_ice_transport_info *ii; unsigned jj; ii = (const pjmedia_ice_transport_info*) tp_info.spc_info[j].buffer; len = pj_ansi_snprintf(p, end-p, " %s ICE role: %s, state: %s, comp_cnt: %u", indent, pj_ice_sess_role_name(ii->role), pj_ice_strans_state_name(ii->sess_state), ii->comp_cnt); if (len > 0 && len < end-p) { p += len; *p++ = '\n'; *p = '\0'; } for (jj=0; ii->sess_state==PJ_ICE_STRANS_STATE_RUNNING && jj<2; ++jj) { const char *type1 = pj_ice_get_cand_type_name(ii->comp[jj].lcand_type); const char *type2 = pj_ice_get_cand_type_name(ii->comp[jj].rcand_type); char addr1[PJ_INET6_ADDRSTRLEN+10]; char addr2[PJ_INET6_ADDRSTRLEN+10]; if (pj_sockaddr_has_addr(&ii->comp[jj].lcand_addr)) pj_sockaddr_print(&ii->comp[jj].lcand_addr, addr1, sizeof(addr1), 3); else strcpy(addr1, "0.0.0.0:0"); if (pj_sockaddr_has_addr(&ii->comp[jj].rcand_addr)) pj_sockaddr_print(&ii->comp[jj].rcand_addr, addr2, sizeof(addr2), 3); else strcpy(addr2, "0.0.0.0:0"); len = pj_ansi_snprintf(p, end-p, " %s [%d]: L:%s (%c) --> R:%s (%c)\n", indent, jj, addr1, type1[0], addr2, type2[0]); if (len > 0 && len < end-p) { p += len; *p = '\0'; } } } } } } if (has_stat) { len = dump_media_stat(indent, p, end-p, &stat, rx_info, tx_info); p += len; } #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) # 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) # define VALIDATE_PRINT_BUF() \ if (len < 1 || len > end-p) { *p = '\0'; return; } \ p += len; *p++ = '\n'; *p = '\0' if (call_med->type == PJMEDIA_TYPE_AUDIO) { pjmedia_stream_info info; char last_update[64]; 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; unsigned clock_rate; pj_time_val now; if (pjmedia_stream_get_stat_xr(call_med->strm.a.stream, &xr_stat) != PJ_SUCCESS) { continue; } if (pjmedia_stream_get_info(call_med->strm.a.stream, &info) != PJ_SUCCESS) { continue; } clock_rate = info.fmt.clock_rate; pj_gettimeofday(&now); len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent); VALIDATE_PRINT_BUF(); /* Statistics Summary */ len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent); VALIDATE_PRINT_BUF(); 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, clock_rate); SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max, clock_rate); SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean, clock_rate); SAMPLES_TO_USEC(jdev, pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter), 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, pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh)); } 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); } len = pj_ansi_snprintf(p, end-p, "%s RX last update: %s\n" "%s begin seq=%d, end seq=%d\n" "%s pkt loss=%s, dup=%s\n" "%s (msec) min avg max dev\n" "%s jitter : %s\n" "%s toh : %s", indent, last_update, indent, xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq, indent, loss, dup, indent, indent, jitter, indent, toh ); VALIDATE_PRINT_BUF(); 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, clock_rate); SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max, clock_rate); SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean, clock_rate); SAMPLES_TO_USEC(jdev, pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter), 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, pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh)); } 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); } len = pj_ansi_snprintf(p, end-p, "%s TX last update: %s\n" "%s begin seq=%d, end seq=%d\n" "%s pkt loss=%s, dup=%s\n" "%s (msec) min avg max dev\n" "%s jitter : %s\n" "%s toh : %s", indent, last_update, indent, xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq, indent, loss, dup, indent, indent, jitter, indent, toh ); VALIDATE_PRINT_BUF(); /* VoIP Metrics */ len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent); VALIDATE_PRINT_BUF(); 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); } len = pj_ansi_snprintf(p, end-p, "%s RX last update: %s\n" "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n" "%s burst : density=%d (%.2f%%), duration=%d%s\n" "%s gap : density=%d (%.2f%%), duration=%d%s\n" "%s delay : round trip=%d%s, end system=%d%s\n" "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n" "%s quality : R factor=%s, ext R factor=%s\n" "%s MOS LQ=%s, MOS CQ=%s\n" "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n" "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s", indent, last_update, /* packets */ indent, 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 */ indent, 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 */ indent, 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 */ indent, xr_stat.rx.voip_mtc.rnd_trip_delay, "ms", xr_stat.rx.voip_mtc.end_sys_delay, "ms", /* level */ indent, signal_lvl, "dB", noise_lvl, "dB", rerl, "", /* quality */ indent, r_factor, ext_r_factor, indent, mos_lq, mos_cq, /* config */ indent, plc, jba, jbr, xr_stat.rx.voip_mtc.gmin, /* JB delay */ indent, 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" ); VALIDATE_PRINT_BUF(); 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); } len = pj_ansi_snprintf(p, end-p, "%s TX last update: %s\n" "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n" "%s burst : density=%d (%.2f%%), duration=%d%s\n" "%s gap : density=%d (%.2f%%), duration=%d%s\n" "%s delay : round trip=%d%s, end system=%d%s\n" "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n" "%s quality : R factor=%s, ext R factor=%s\n" "%s MOS LQ=%s, MOS CQ=%s\n" "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n" "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s", indent, last_update, /* pakcets */ indent, 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 */ indent, 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 */ indent, 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 */ indent, xr_stat.tx.voip_mtc.rnd_trip_delay, "ms", xr_stat.tx.voip_mtc.end_sys_delay, "ms", /* level */ indent, signal_lvl, "dB", noise_lvl, "dB", rerl, "", /* quality */ indent, r_factor, ext_r_factor, indent, mos_lq, mos_cq, /* config */ indent, plc, jba, jbr, xr_stat.tx.voip_mtc.gmin, /* JB delay */ indent, 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" ); VALIDATE_PRINT_BUF(); /* RTT delay (by receiver side) */ len = pj_ansi_snprintf(p, end-p, "%s RTT (from recv) min avg max last dev", indent); VALIDATE_PRINT_BUF(); len = pj_ansi_snprintf(p, end-p, "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f", indent, xr_stat.rtt.min / 1000.0, xr_stat.rtt.mean / 1000.0, xr_stat.rtt.max / 1000.0, xr_stat.rtt.last / 1000.0, pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0 ); VALIDATE_PRINT_BUF(); } /* if audio */; #endif }
/* * Print stream statistics */ static void print_stream_stat(pjmedia_stream *stream, const pjmedia_codec_param *codec_param) { char duration[80], last_update[80]; char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16]; pjmedia_port *port; pjmedia_rtcp_stat stat; pj_time_val now; pj_gettimeofday(&now); pjmedia_stream_get_stat(stream, &stat); pjmedia_stream_get_port(stream, &port); puts("Stream statistics:"); /* Print duration */ PJ_TIME_VAL_SUB(now, stat.start); sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld", now.sec / 3600, (now.sec % 3600) / 60, (now.sec % 60), now.msec); printf(" Info: audio %dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n", PJMEDIA_PIA_SRATE(&port->info), PJMEDIA_PIA_PTIME(&port->info), good_number(bps, (codec_param->info.avg_bps+7)/8), good_number(ipbps, ((codec_param->info.avg_bps+7)/8) + (40 * 1000 / codec_param->setting.frm_per_pkt / codec_param->info.frm_ptime))); 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, "%02ldh:%02ldm:%02ld.%03lds 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" " (msec) min avg max last dev\n" " loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n" " jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n", 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.loss), stat.rx.dup, stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss), stat.rx.reorder, stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss), "", stat.rx.loss_period.min / 1000.0, stat.rx.loss_period.mean / 1000.0, stat.rx.loss_period.max / 1000.0, stat.rx.loss_period.last / 1000.0, pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0, "", stat.rx.jitter.min / 1000.0, stat.rx.jitter.mean / 1000.0, stat.rx.jitter.max / 1000.0, stat.rx.jitter.last / 1000.0, pj_math_stat_get_stddev(&stat.rx.jitter) / 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, "%02ldh:%02ldm:%02ld.%03lds ago", now.sec / 3600, (now.sec % 3600) / 60, now.sec % 60, now.msec); } printf(" TX stat last update: %s\n" " total %s packets %sB sent (%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 dev\n" " loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n" " jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n", 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.loss), stat.tx.dup, stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss), stat.tx.reorder, stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss), "", stat.tx.loss_period.min / 1000.0, stat.tx.loss_period.mean / 1000.0, stat.tx.loss_period.max / 1000.0, stat.tx.loss_period.last / 1000.0, pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0, "", stat.tx.jitter.min / 1000.0, stat.tx.jitter.mean / 1000.0, stat.tx.jitter.max / 1000.0, stat.tx.jitter.last / 1000.0, pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0, "" ); printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n", stat.rtt.min / 1000.0, stat.rtt.mean / 1000.0, stat.rtt.max / 1000.0, stat.rtt.last / 1000.0, pj_math_stat_get_stddev(&stat.rtt) / 1000.0, "" ); #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.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max, port->info.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean, port->info.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jdev, pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter), port->info.fmt.det.aud.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, pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh)); } 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.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max, port->info.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean, port->info.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jdev, pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter), port->info.fmt.det.aud.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, pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh)); } 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 (by receiver side) */ printf(" (msec) min avg max last dev\n"); printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n", xr_stat.rtt.min / 1000.0, xr_stat.rtt.mean / 1000.0, xr_stat.rtt.max / 1000.0, xr_stat.rtt.last / 1000.0, pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0, "" ); } while (0); #endif /* PJMEDIA_HAS_RTCP_XR */ }