blob: 5f45775ae58583637793440cad6b0ede0f231816 [file] [log] [blame]
/* $Id$ */
/*
* Copyright (C) 2003-2006 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 <pj/os.h> /* pj_gettimeofday */
#include <pj/sock.h> /* pj_htonx, pj_ntohx */
#include <pj/string.h> /* pj_memset */
#define RTCP_SR 200
#define RTCP_RR 201
/*
* Get NTP time.
*/
static void rtcp_get_ntp_time(struct pj_rtcp_ntp_rec *ntp)
{
pj_time_val tv;
pj_gettimeofday(&tv);
ntp->hi = tv.sec;
tv.msec = tv.msec % 1000;
ntp->lo = tv.msec * 0xFFFF / 1000;
ntp->lo <<= 16;
}
PJ_DEF(void) pj_rtcp_init(pj_rtcp_session *s, pj_uint32_t ssrc)
{
pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
pj_memset(rtcp_pkt, 0, sizeof(pj_rtcp_pkt));
/* Init time */
s->rtcp_lsr.hi = s->rtcp_lsr.lo = 0;
s->rtcp_lsr_time = 0;
/* 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);
/* RR will be initialized on receipt of the first RTP packet. */
}
PJ_DEF(void) pj_rtcp_fini(pj_rtcp_session *session)
{
/* Nothing to do. */
PJ_UNUSED_ARG(session);
}
static void rtcp_init_seq(pj_rtcp_session *s, pj_uint16_t seq)
{
s->received = 0;
s->expected_prior = 0;
s->received_prior = 0;
s->transit = 0;
s->jitter = 0;
pj_rtp_seq_restart(&s->seq_ctrl, seq);
}
PJ_DEF(void) pj_rtcp_rx_rtp(pj_rtcp_session *s, pj_uint16_t seq, pj_uint32_t rtp_ts)
{
pj_uint32_t arrival;
pj_int32_t transit;
unsigned long timer_tick;
pj_time_val tv;
int status;
/* Update sequence numbers (received, lost, etc). */
status = pj_rtp_seq_update(&s->seq_ctrl, seq);
if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
rtcp_init_seq(s, seq);
status = 0;
}
if (status != 0)
return;
++s->received;
pj_gettimeofday(&tv);
timer_tick = tv.sec * 1000 + tv.msec;
/*
* Calculate jitter (s->jitter is in timer tick unit)
*/
PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
arrival = timer_tick << 3; // 8 samples per ms.
transit = arrival - rtp_ts;
if (s->transit == 0) {
s->transit = transit;
} else {
pj_int32_t d, jitter = s->jitter;
d = transit - s->transit;
s->transit = transit;
if (d < 0)
d = -d;
jitter += d - ((jitter + 8) >> 4);
s->jitter = jitter;
}
}
PJ_DEF(void) pj_rtcp_tx_rtp(pj_rtcp_session *s, pj_uint16_t bytes_payload_size)
{
pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
rtcp_pkt->sr.sender_pcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_pcount) + 1);
rtcp_pkt->sr.sender_bcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_bcount) + bytes_payload_size );
}
static void rtcp_build_rtcp(pj_rtcp_session *s, pj_uint32_t receiver_ssrc)
{
pj_uint32_t expected;
pj_uint32_t u32;
pj_uint32_t expected_interval, received_interval, lost_interval;
pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
/* SSRC and last_seq */
rtcp_pkt->rr.ssrc = pj_htonl(receiver_ssrc);
rtcp_pkt->rr.last_seq = (s->seq_ctrl.cycles & 0xFFFF0000L);
rtcp_pkt->rr.last_seq += s->seq_ctrl.max_seq;
rtcp_pkt->rr.last_seq = pj_htonl(rtcp_pkt->rr.last_seq);
/* Jitter */
rtcp_pkt->rr.jitter = pj_htonl(s->jitter >> 4);
/* Total lost. */
expected = pj_ntohl(rtcp_pkt->rr.last_seq) - s->seq_ctrl.base_seq + 1;
u32 = expected - s->received;
rtcp_pkt->rr.total_lost_2 = (u32 >> 16) & 0x00FF;
rtcp_pkt->rr.total_lost_1 = (u32 >> 8) & 0x00FF;
rtcp_pkt->rr.total_lost_0 = u32 & 0x00FF;
/* Fraction lost calculation */
expected_interval = expected - s->expected_prior;
s->expected_prior = expected;
received_interval = s->received - s->received_prior;
s->received_prior = s->received;
lost_interval = expected_interval - received_interval;
if (expected_interval==0 || lost_interval == 0) {
rtcp_pkt->rr.fract_lost = 0;
} else {
rtcp_pkt->rr.fract_lost = (lost_interval << 8) / expected_interval;
}
}
PJ_DEF(void) pj_rtcp_build_rtcp(pj_rtcp_session *session, pj_rtcp_pkt **ret_p_pkt, int *len)
{
pj_rtcp_pkt *rtcp_pkt = &session->rtcp_pkt;
pj_rtcp_ntp_rec ntp;
pj_time_val now;
rtcp_build_rtcp(session, session->peer_ssrc);
/* Get current NTP time. */
rtcp_get_ntp_time(&ntp);
/* Fill in NTP timestamp in SR. */
rtcp_pkt->sr.ntp_sec = pj_htonl(ntp.hi);
rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo);
if (session->rtcp_lsr_time == 0 || session->rtcp_lsr.lo == 0) {
rtcp_pkt->rr.lsr = 0;
rtcp_pkt->rr.dlsr = 0;
} else {
unsigned msec_elapsed;
/* Fill in LSR.
LSR is the middle 32bit of the last SR NTP time received.
*/
rtcp_pkt->rr.lsr = ((session->rtcp_lsr.hi & 0x0000FFFF) << 16) |
((session->rtcp_lsr.lo >> 16) & 0xFFFF);
rtcp_pkt->rr.lsr = pj_htonl(rtcp_pkt->rr.lsr);
/* Fill in DLSR.
DLSR is Delay since Last SR, in 1/65536 seconds.
*/
pj_gettimeofday(&now);
msec_elapsed = (now.msec - session->rtcp_lsr_time);
rtcp_pkt->rr.dlsr = pj_htonl((msec_elapsed * 65536) / 1000);
}
/* Return pointer. */
*ret_p_pkt = rtcp_pkt;
*len = sizeof(pj_rtcp_pkt);
}