static pj_status_t play_cb(void *user_data, pjmedia_frame *frame) { struct test_data *test_data = (struct test_data *)user_data; struct stream_data *strm_data = &test_data->playback_data; pj_mutex_lock(test_data->mutex); /* Skip frames when test is not started or test has finished */ if (!test_data->running) { pj_bzero(frame->buf, frame->size); pj_mutex_unlock(test_data->mutex); return PJ_SUCCESS; } /* Save last timestamp seen (to calculate drift) */ strm_data->last_timestamp = frame->timestamp.u32.lo; if (strm_data->last_called.u64 == 0) { /* Init vars. */ pj_get_timestamp(&strm_data->last_called); pj_math_stat_init(&strm_data->delay); strm_data->first_timestamp = frame->timestamp.u32.lo; } else { pj_timestamp now; unsigned delay; /* Calculate frame interval */ pj_get_timestamp(&now); delay = pj_elapsed_usec(&strm_data->last_called, &now); strm_data->last_called = now; /* Update frame interval statistic */ pj_math_stat_update(&strm_data->delay, delay); } pj_bzero(frame->buf, frame->size); pj_mutex_unlock(test_data->mutex); return PJ_SUCCESS; }
PJ_DEF(void) pjmedia_rtcp_rx_rtp2(pjmedia_rtcp_session *sess, unsigned seq, unsigned rtp_ts, unsigned payload, pj_bool_t discarded) { pj_timestamp ts; pj_uint32_t arrival; pj_int32_t transit; pjmedia_rtp_status seq_st; unsigned last_seq; #if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0) PJ_UNUSED_ARG(discarded); #endif if (sess->stat.rx.pkt == 0) { /* Init sequence for the first time. */ pjmedia_rtp_seq_init(&sess->seq_ctrl, (pj_uint16_t)seq); } sess->stat.rx.pkt++; sess->stat.rx.bytes += payload; /* Process the RTP packet. */ last_seq = sess->seq_ctrl.max_seq; pjmedia_rtp_seq_update(&sess->seq_ctrl, (pj_uint16_t)seq, &seq_st); if (seq_st.status.flag.restart) { rtcp_init_seq(sess); } if (seq_st.status.flag.dup) { sess->stat.rx.dup++; TRACE_((sess->name, "Duplicate packet detected")); } if (seq_st.status.flag.outorder && !seq_st.status.flag.probation) { sess->stat.rx.reorder++; TRACE_((sess->name, "Out-of-order packet detected")); } if (seq_st.status.flag.bad) { sess->stat.rx.discard++; #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq, -1, /* lost */ (seq_st.status.flag.dup? 1:0), /* dup */ (!seq_st.status.flag.dup? 1:-1), /* discard */ -1, /* jitter */ -1, 0); /* toh */ #endif TRACE_((sess->name, "Bad packet discarded")); return; } /* Only mark "good" packets */ ++sess->received; /* Calculate loss periods. */ if (seq_st.diff > 1) { unsigned count = seq_st.diff - 1; unsigned period; period = count * sess->pkt_size * 1000 / sess->clock_rate; period *= 1000; /* Update packet lost. * The packet lost number will also be updated when we're sending * outbound RTCP RR. */ sess->stat.rx.loss += (seq_st.diff - 1); TRACE_((sess->name, "%d packet(s) lost", seq_st.diff - 1)); /* Update loss period stat */ pj_math_stat_update(&sess->stat.rx.loss_period, period); } /* * Calculate jitter only when sequence is good (see RFC 3550 section A.8), * AND only when the timestamp is different than the last packet * (see RTP FAQ). */ if (seq_st.diff == 1 && rtp_ts != sess->rtp_last_ts) { /* Get arrival time and convert timestamp to samples */ pj_get_timestamp(&ts); ts.u64 = ts.u64 * sess->clock_rate / sess->ts_freq.u64; arrival = ts.u32.lo; transit = arrival - rtp_ts; /* Ignore the first N packets as they normally have bad jitter * due to other threads working to establish the call */ if (sess->transit == 0 || sess->received < PJMEDIA_RTCP_IGNORE_FIRST_PACKETS) { sess->transit = transit; sess->stat.rx.jitter.min = (unsigned)-1; } else { pj_int32_t d; pj_uint32_t jitter; d = transit - sess->transit; sess->transit = transit; if (d < 0) d = -d; sess->jitter += d - ((sess->jitter + 8) >> 4); /* Get jitter in usec */ if (d < 4294) jitter = d * 1000000 / sess->clock_rate; else { jitter = d * 1000 / sess->clock_rate; jitter *= 1000; } /* Update jitter stat */ pj_math_stat_update(&sess->stat.rx.jitter, jitter); #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq, 0, /* lost */ 0, /* dup */ discarded, /* discard */ (sess->jitter >> 4), /* jitter */ -1, 0); /* toh */ #endif } #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) } else if (seq_st.diff > 1) {
static void jbuf_calculate_jitter(pjmedia_jbuf *jb) { int diff, cur_size; cur_size = jb_framelist_eff_size(&jb->jb_framelist); pj_math_stat_update(&jb->jb_burst, jb->jb_level); jb->jb_max_hist_level = PJ_MAX(jb->jb_max_hist_level, jb->jb_level); /* Burst level is decreasing */ if (jb->jb_level < jb->jb_eff_level) { enum { STABLE_HISTORY_LIMIT = 20 }; jb->jb_stable_hist++; /* Only update the effective level (and prefetch) if 'stable' * condition is reached (not just short time impulse) */ if (jb->jb_stable_hist > STABLE_HISTORY_LIMIT) { diff = (jb->jb_eff_level - jb->jb_max_hist_level) / 3; if (diff < 1) diff = 1; /* Update effective burst level */ jb->jb_eff_level -= diff; /* Update prefetch based on level */ if (jb->jb_init_prefetch) { jb->jb_prefetch = jb->jb_eff_level; if (jb->jb_prefetch < jb->jb_min_prefetch) jb->jb_prefetch = jb->jb_min_prefetch; } /* Reset history */ jb->jb_max_hist_level = 0; jb->jb_stable_hist = 0; TRACE__((jb->jb_name.ptr,"jb updated(1), lvl=%d pre=%d, size=%d", jb->jb_eff_level, jb->jb_prefetch, cur_size)); } } /* Burst level is increasing */ else if (jb->jb_level > jb->jb_eff_level) { /* Instaneous set effective burst level to recent maximum level */ jb->jb_eff_level = PJ_MIN(jb->jb_max_hist_level, (int)(jb->jb_max_count*4/5)); /* Update prefetch based on level */ if (jb->jb_init_prefetch) { jb->jb_prefetch = jb->jb_eff_level; if (jb->jb_prefetch > jb->jb_max_prefetch) jb->jb_prefetch = jb->jb_max_prefetch; } jb->jb_stable_hist = 0; /* Do not reset max_hist_level. */ //jb->jb_max_hist_level = 0; TRACE__((jb->jb_name.ptr,"jb updated(2), lvl=%d pre=%d, size=%d", jb->jb_eff_level, jb->jb_prefetch, cur_size)); } /* Level is unchanged */ else { jb->jb_stable_hist = 0; } }
/* * Get frame from jitter buffer. */ PJ_DEF(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb, void *frame, pj_size_t *size, char *p_frame_type, pj_uint32_t *bit_info, pj_uint32_t *ts, int *seq) { if (jb->jb_prefetching) { /* Can't return frame because jitter buffer is filling up * minimum prefetch. */ //pj_bzero(frame, jb->jb_frame_size); *p_frame_type = PJMEDIA_JB_ZERO_PREFETCH_FRAME; if (size) *size = 0; TRACE__((jb->jb_name.ptr, "GET prefetch_cnt=%d/%d", jb_framelist_eff_size(&jb->jb_framelist), jb->jb_prefetch)); jb->jb_empty++; } else { pjmedia_jb_frame_type ftype = PJMEDIA_JB_NORMAL_FRAME; pj_bool_t res; /* Try to retrieve a frame from frame list */ res = jb_framelist_get(&jb->jb_framelist, frame, size, &ftype, bit_info, ts, seq); if (res) { /* We've successfully retrieved a frame from the frame list, but * the frame could be a blank frame! */ if (ftype == PJMEDIA_JB_NORMAL_FRAME) { *p_frame_type = PJMEDIA_JB_NORMAL_FRAME; } else { *p_frame_type = PJMEDIA_JB_MISSING_FRAME; jb->jb_lost++; } /* Store delay history at the first GET */ if (jb->jb_last_op == JB_OP_PUT) { unsigned cur_size; /* We've just retrieved one frame, so add one to cur_size */ cur_size = jb_framelist_eff_size(&jb->jb_framelist) + 1; pj_math_stat_update(&jb->jb_delay, cur_size*jb->jb_frame_ptime); } } else { /* Jitter buffer is empty */ if (jb->jb_prefetch) jb->jb_prefetching = PJ_TRUE; //pj_bzero(frame, jb->jb_frame_size); *p_frame_type = PJMEDIA_JB_ZERO_EMPTY_FRAME; if (size) *size = 0; jb->jb_empty++; } } jb->jb_level++; jbuf_update(jb, JB_OP_GET); }