📄 rtcp.c
字号:
/* $Id: rtcp.c 974 2007-02-19 01:13:53Z bennylp $ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjmedia/rtcp.h>
#include <pjmedia/errno.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pj/sock.h>
#include <pj/string.h>
#define THIS_FILE "rtcp.c"
#define RTCP_SR 200
#define RTCP_RR 201
#if PJ_HAS_HIGH_RES_TIMER==0
# error "High resolution timer needs to be enabled"
#endif
#if 0
# define TRACE_(x) PJ_LOG(3,x)
#else
# define TRACE_(x) ;
#endif
/*
* Get NTP time.
*/
PJ_DEF(pj_status_t) pjmedia_rtcp_get_ntp_time(const pjmedia_rtcp_session *sess,
pjmedia_rtcp_ntp_rec *ntp)
{
/* Seconds between 1900-01-01 to 1970-01-01 */
#define JAN_1970 (2208988800UL)
pj_timestamp ts;
pj_status_t status;
status = pj_get_timestamp(&ts);
/* Fill up the high 32bit part */
ntp->hi = (pj_uint32_t)((ts.u64 - sess->ts_base.u64) / sess->ts_freq.u64)
+ sess->tv_base.sec + JAN_1970;
/* Calculate seconds fractions */
ts.u64 %= sess->ts_freq.u64;
pj_assert(ts.u64 < sess->ts_freq.u64);
ts.u64 = (ts.u64 << 32) / sess->ts_freq.u64;
/* Fill up the low 32bit part */
ntp->lo = ts.u32.lo;
#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
(defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0)
/* On Win32, since we use QueryPerformanceCounter() as the backend
* timestamp API, we need to protect against this bug:
* Performance counter value may unexpectedly leap forward
* http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323
*/
{
/*
* Compare elapsed time reported by timestamp with actual elapsed
* time. If the difference is too excessive, then we use system
* time instead.
*/
/* MIN_DIFF needs to be large enough so that "normal" diff caused
* by system activity or context switch doesn't trigger the time
* correction.
*/
enum { MIN_DIFF = 400 };
pj_time_val ts_time, elapsed, diff;
pj_gettimeofday(&elapsed);
ts_time.sec = ntp->hi - sess->tv_base.sec - JAN_1970;
ts_time.msec = (long)(ntp->lo * 1000.0 / 0xFFFFFFFF);
PJ_TIME_VAL_SUB(elapsed, sess->tv_base);
if (PJ_TIME_VAL_LT(ts_time, elapsed)) {
diff = elapsed;
PJ_TIME_VAL_SUB(diff, ts_time);
} else {
diff = ts_time;
PJ_TIME_VAL_SUB(diff, elapsed);
}
if (PJ_TIME_VAL_MSEC(diff) >= MIN_DIFF) {
TRACE_((sess->name, "RTCP NTP timestamp corrected by %d ms",
PJ_TIME_VAL_MSEC(diff)));
ntp->hi = elapsed.sec + sess->tv_base.sec + JAN_1970;
ntp->lo = (elapsed.msec * 65536 / 1000) << 16;
}
}
#endif
return status;
}
PJ_DEF(void) pjmedia_rtcp_init(pjmedia_rtcp_session *sess,
char *name,
unsigned clock_rate,
unsigned samples_per_frame,
pj_uint32_t ssrc)
{
pjmedia_rtcp_pkt *rtcp_pkt = &sess->rtcp_pkt;
pj_time_val now;
/* Memset everything */
pj_bzero(sess, sizeof(pjmedia_rtcp_session));
/* Last RX timestamp in RTP packet */
sess->rtp_last_ts = (unsigned)-1;
/* Name */
sess->name = name ? name : THIS_FILE,
/* Set clock rate */
sess->clock_rate = clock_rate;
sess->pkt_size = samples_per_frame;
/* Init common RTCP header */
rtcp_pkt->common.version = 2;
rtcp_pkt->common.count = 1;
rtcp_pkt->common.pt = RTCP_SR;
rtcp_pkt->common.length = pj_htons(12);
/* Init SR */
rtcp_pkt->sr.ssrc = pj_htonl(ssrc);
/* Get time and timestamp base and frequency */
pj_gettimeofday(&now);
sess->tv_base = now;
sess->stat.start = now;
pj_get_timestamp(&sess->ts_base);
pj_get_timestamp_freq(&sess->ts_freq);
/* RR will be initialized on receipt of the first RTP packet. */
}
PJ_DEF(void) pjmedia_rtcp_fini(pjmedia_rtcp_session *sess)
{
/* Nothing to do. */
PJ_UNUSED_ARG(sess);
}
static void rtcp_init_seq(pjmedia_rtcp_session *sess)
{
sess->received = 0;
sess->exp_prior = 0;
sess->rx_prior = 0;
sess->transit = 0;
sess->jitter = 0;
}
PJ_DEF(void) pjmedia_rtcp_rx_rtp(pjmedia_rtcp_session *sess,
unsigned seq,
unsigned rtp_ts,
unsigned payload)
{
pj_timestamp ts;
pj_uint32_t arrival;
pj_int32_t transit;
pjmedia_rtp_status seq_st;
unsigned last_seq;
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++;
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 */
if (sess->stat.rx.loss_period.count == 0 ||
period < sess->stat.rx.loss_period.min)
{
sess->stat.rx.loss_period.min = period;
}
if (period > sess->stat.rx.loss_period.max)
sess->stat.rx.loss_period.max = period;
sess->stat.rx.loss_period.avg =
(sess->stat.rx.loss_period.avg * sess->stat.rx.loss_period.count +
period) / (sess->stat.rx.loss_period.count + 1);
sess->stat.rx.loss_period.last = period;
++sess->stat.rx.loss_period.count;
}
/*
* 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 = 2000;
} 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;
}
/* Add to average */
sess->avg_jitter =
(jitter + sess->avg_jitter * sess->stat.rx.jitter.count) /
(sess->stat.rx.jitter.count + 1);
sess->stat.rx.jitter.avg = (unsigned)sess->avg_jitter;
++sess->stat.rx.jitter.count;
/* Update jitter stat */
if (jitter < sess->stat.rx.jitter.min)
sess->stat.rx.jitter.min = jitter;
if (jitter > sess->stat.rx.jitter.max)
sess->stat.rx.jitter.max = jitter;
sess->stat.rx.jitter.last = jitter;
}
}
/* Update timestamp of last RX RTP packet */
sess->rtp_last_ts = rtp_ts;
}
PJ_DEF(void) pjmedia_rtcp_tx_rtp(pjmedia_rtcp_session *sess,
unsigned bytes_payload_size)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -