/*-----------------------------------------------------------------------------
File: sphone_rtcp_msgs.c
Description: Functions for receiving rtcp messages.
Author: Olof Hagsand
CVS Version: $Id: sphone_rtcp_rcv.c,v 1.7 2004/06/05 10:11:00 olofh Exp $
This software is a part of SICSOPHONE, a real-time, IP-based system for
point-to-point delivery of audio between computer end-systems.
Sicsophone 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.
Sicsophone 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 Sicsophone; see the file COPYING.
*---------------------------------------------------------------------------*/
#include <stdio.h>
#include "sphone.h"
int
rtcp_rcv_stats_exit(struct rtcp_rcv_stats *rrs)
{
sfree(rrs);
return 0;
}
struct rtcp_rcv_stats *
rtcp_rcv_stats_new()
{
struct rtcp_rcv_stats *rrs;
rrs = (void*)smalloc(sizeof(struct rtcp_rcv_stats));
if (rrs == NULL){
sphone_error("rtcp_rcv_stats_new: malloc: %s", strerror(errno));
return NULL;
}
memset(rrs, 0, sizeof(struct rtcp_rcv_stats));
return rrs;
}
/*
* Receive Receiver Report
*/
static int
rtcp_receive_RR(char *p, struct rtp_session *rtp, struct rtcp_send_stats* rss)
{
rtcp_rr_t RR_block;
struct timeval T1;
/* log the arrival time */
T1 = gettimestamp();
rss->rss_RR_pkts++;
p = decode_report_block(&RR_block, p);
rss->rss_peer_fraction_lost = ((double)rtcp_get_lost_frac(RR_block.lost))/256.0;
rss->rss_peer_jitter = rtpTimestamp2ms(RR_block.jitter);
rss->rss_RTT_delay = calculateRTT(RR_block.lsr, RR_block.dlsr, T1);
rss->rss_peer_culm_lost = RR_block.lost >> 24;
rss->rss_peer_last_seq = RR_block.last_seq;
return 0;
}
/*
* Receive Sender Report
* Return 0 if OK or illegal src. -1 on exit.
* ssrc is "expected" ssrc.
*/
static int
rtcp_receive_SR(char *p, struct rtp_session *rtp, struct rtcp_rcv_stats* rrs)
{
rtcp_si_t SR_block;
uint32_t rtp_ts;
/*
* Record arrival time for later computation of RTT.
*/
rrs->rrs_timestamp_SR = gettimestamp();
rrs->rrs_SR_pkts++;
p = decode_sender_info(&SR_block, p);
/*
* print time
*/
rrs->rrs_timestamp_SR_ntp.sec = (SR_block.ntp_sec & 0xffff);
rrs->rrs_timestamp_SR_ntp.frac = (SR_block.ntp_frac >> 16);
/*
* RTP timestamp
*/
rtp_ts = SR_block.rtp_ts;
/*
* Packets and bytes sent -
*/
rrs->rrs_packets_sent2us = SR_block.psent;
rrs->rrs_bytes_sent2us = SR_block.osent;
return 0; /* OK */
}
/*
* Receive BYE
*/
static int
rtcp_receive_bye(char *p, struct rtp_session *rtp)
{
sphone_warning("Got bye from sender\n");
return 0;
}
static int
rtcp_receive(struct rtp_session *rtp, void *stats, int expected)
{
rtcp_common_t rtcp_header;
struct sockaddr_in from_addr;
uint16_t flags = 0x0;
int len;
char *inbuf, *p;
int type;
int retval = 0;
len = INBUF_LEN;
if ((inbuf = smalloc(len)) == NULL){
sphone_error("rtcp_receive: %s\n", strerror(errno));
return -1;
}
if (inet_input(rtp->rtcp_s, inbuf, &len, &from_addr) < 0)
return -1;
p = inbuf;
/* Decode, get flags, type and print debug */
p = decode_rtcp_header(&rtcp_header, p);
flags = rtcp_header.flags;
type = rtcp_get_t(flags);
dbg_print(DBG_RTCP, "Receiving: RTCP %s\n", rtcp_print_type(type));
/*
* Accept any new sender
* but if we have locked on a specific sender, silently drop if not for us.
*/
if (rtp->rtp_ssrc == 0){
rtp->rtp_ssrc = rtcp_header.ssrc;
rtp->rtcp_dst_addr.sin_addr.s_addr = from_addr.sin_addr.s_addr;
}
else
if (rtp->rtp_ssrc != rtcp_header.ssrc) {
sphone_warning("Unexpected source: %d\n", rtcp_header.ssrc);
sfree(inbuf);
return 0;
}
switch(type){
case RTCP_SR:
if (type != expected)
break;
retval = rtcp_receive_SR(p, rtp, stats);
break;
case RTCP_RR:
if (type != expected)
break;
retval = rtcp_receive_RR(p, rtp, stats);
break;
case RTCP_BYE:
retval = rtcp_receive_bye(p, rtp);
break;
default:
sphone_warning("rtcp_receive: unexpected type: %d\n", type);
break;
}
sfree(inbuf);
return retval;
}
int
rtcp_send_rcv_dispatch(int s, void *arg)
{
send_session_t *ss = (send_session_t *)arg;
struct rtp_session *rtp = ss->ss_rtp;
assert(s == rtp->rtcp_s);
if (rtcp_receive(rtp, (void*)ss->ss_rtcp, RTCP_RR) < 0)
return -1;
return 0;
}
int
rtcp_rcv_rcv_dispatch(int s, void *arg)
{
rcv_session_t *rs = (rcv_session_t *)arg;
struct rtp_session *rtp = rs->rs_rtp;
assert(s == rtp->rtcp_s);
if (rtcp_receive(rtp, (void*)rs->rs_rtcp, RTCP_SR) < 0)
return -1;
return 0;
}
/*
* A receiver sends a RR regularly and registers a callback for RRs.
*/
int
rtcp_rcv_init(int s, rcv_session_t *rs)
{
struct rtp_session *rtp = rs->rs_rtp;
struct timeval t, dt;
t = gettimestamp();
dt.tv_sec = RTCP_REPORT_INTERVAL_S;
dt.tv_usec = 0;
timeradd(&t, &dt, &rtp->rtcp_timer);
if (eventloop_reg_timeout(rtp->rtcp_timer, rtcp_rcv_send_dispatch,
rs, "RTCP send RR") < 0)
return -1;
if (eventloop_reg_fd(s, rtcp_rcv_rcv_dispatch, rs,
"RTCP rcv receiver") < 0)
return -1;
return 0;
}
/*-----------------------------------------------------------------------------
File: sphone_rtcp_msgs.c
Description: Functions for sending rtcp messages.
Author: Olof Hagsand
CVS Version: $Id: sphone_rtcp_send.c,v 1.8 2004/06/05 10:11:00 olofh Exp $
This software is a part of SICSOPHONE, a real-time, IP-based system for
point-to-point delivery of audio between computer end-systems.
Sicsophone 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.
Sicsophone 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 Sicsophone; see the file COPYING.
*---------------------------------------------------------------------------*/
#include <stdio.h>
#include "sphone.h"
int
rtcp_send_stats_exit(struct rtcp_send_stats *rss)
{
sfree(rss);
return 0;
}
struct rtcp_send_stats *
rtcp_send_stats_new()
{
struct rtcp_send_stats *rss;
rss = (void*)smalloc(sizeof(struct rtcp_send_stats));
if (rss == NULL){
sphone_error("rtcp_send_stats_new: malloc: %s", strerror(errno));
return NULL;
}
memset(rss, 0, sizeof(struct rtcp_send_stats));
return rss;
}
/*
* Send Sender Report
*/
static int
rtcp_send_SR(struct rtp_session *rtp,
struct rtcp_send_stats* rss,
uint32_t samp_sec,
uint32_t packets_sent,
uint32_t bytes_sent)
{
rtcp_common_t rtcp_header;
rtcp_si_t SR_block;
struct timeval now;
struct NTP_timestamp_64 ntp;
int len = 0;
char *p, *databuf;
int rtcp_type = RTCP_SR;
/*
* Get the time and convert to NTP format
*/
now = gettimestamp();
TV2NTP_64(&now, &ntp);
rtcp_set_hdr(&rtcp_header, rtcp_type, rtp->rtp_ssrc);
/*
* OK, build a sender report.
*/
SR_block.ntp_sec = ntp.sec;
SR_block.ntp_frac = ntp.frac;
#ifdef IAN
/*
The RTP timestamp is calculated from the corresponding NTP
timestamp using the relationship between the RTP timestamp
counter and real time as maintained by periodically checking the
wallclock time at a sampling instant.
*/
ntp2rtp(&ntp, start_rtp, &rtp_now);
SR_block.rtp_ts = rtp_now;
#else /* KJELL/OH */
/* (from the spec): The timestamp
corresponds to the same time as the NTP timestamp (above), but
in the same units and with the same random offset as the RTP
timestamps in data packets.
*/
SR_block.rtp_ts = samp_sec*now.tv_sec + (samp_sec*now.tv_usec)/1000000;
#endif
SR_block.psent = packets_sent;
SR_block.osent = bytes_sent;
#if 0
/*
* Don't worry about what other receivers tell us for now. Must
* have at least one reception report block, but it may be zero (see
* draft). The following sets the receiver report to 0.
*/
SR_block.->rr[0].ssrc = 0;
SR_block.->rr[0].lost = 0;
SR_block.->rr[0].last_seq = 0;
SR_block.->rr[0].jitter = 0;
SR_block.->rr[0].lsr = 0;
SR_block.->rr[0].dlsr = 0;
#endif
/* memset(SR_block.rr[0], 0, sizeof(SR_block.rr[0]));*/
if ((databuf = (char *)smalloc(rtcp_header.length+1)) == NULL){
sphone_error("rtcp_send_SR: malloc: %s", strerror(errno));
return -1;
}
p = databuf;
/*
* Make an RTCP header and convert byte order. XXX this works for now
* because we know the length, if we have a variable number of
* report blocks then we have to do this last.
*/
encode_rtcp_header(&rtcp_header, p);
len += sizeof(rtcp_common_t);
p += sizeof(rtcp_common_t);
encode_sender_info(&SR_block, p);
len += sizeof(rtcp_si_t);
p += sizeof(rtcp_si_t);
dbg_print(DBG_RTCP, "Sending: RTCP Sender report\n");
/*
* OK send off the RTCP message to the receiver.
*/
if (sendto(rtp->rtcp_s, databuf, len, 0x0,
(struct sockaddr *)&rtp->rtcp_dst_addr,
sizeof(rtp->rtcp_dst_addr)) < 0){
sphone_error("rtcp_send_SR: sendto: %s\n",
strerror(errno));
sfree(databuf);
return -1;
}
sfree(databuf);
/*
* Fill in rtcp stats struct.
*/
rss->rss_SR_pkts++;
return 0;
}
/*
* This function sends a RTPC RECEIVER REPORT.
*/
static int
rtcp_send_RR(struct rtp_session *rtp,
struct rtcp_rcv_stats* rrs,
double ia_jitter,
int curr_pktLost,
int curr_pktSeq)
{
rtcp_common_t rtcp_header;
rtcp_rr_t RR_block;
struct timeval now, Tdiff;
char *p, *databuf;
int len=0;
int expected_interval;
double fraction = 0.0;
unsigned int lost;
/*
* We shouldn't send anything if no sender has registered with us.
*/
if (rtp->rtp_ssrc == 0){
dbg_print(DBG_RTCP, "RTCP send RR: No sender registered yet.\n");
return 0;
}
/*
* Calculate the DLSR time from last received sender report SR
* to point when we send back receiver report RR
*/
now = gettimestamp();
Tdiff = timevalsub(now, rrs->rrs_timestamp_SR);
/*
* We need to send back information on the data we have received in a
* receiver report. First make a rtcp_header with the right type RTCP_RR
*/
rtcp_set_hdr(&rtcp_header, RTCP_RR, rtp->rtp_ssrc);
/*
* OK, build a report block. For format see rtcp.h, build it from
* the sender struct, we update each time a sender report comes in.
*/
lost = curr_pktLost - rrs->rrs_prev_pktLost;
expected_interval = curr_pktSeq - rrs->rrs_prev_pktSeq;
if (expected_interval != 0)
fraction = (double)lost / (double)expected_interval;
RR_block.ssrc = rtp->rtp_ssrc;
RR_block.last_seq = curr_pktSeq;
RR_block.jitter = ms2rtpTimestamp(ia_jitter);
/*
* Calculate the LSR time from a NTP times stamp
*/
RR_block.lsr = ntp2lsr(&rrs->rrs_timestamp_SR_ntp);
RR_block.dlsr = timeval2dlsr(Tdiff);
if ((databuf = (char *)smalloc(rtcp_header.length + 1)) == NULL){
sphone_error("rtcp_send_RR: malloc: %s", strerror(errno));
return -1;
}
p = databuf;
/*
* Make an RTCP header and convert byte order. XXX this works for now
* because we know the length, if we have a variable number of
* report blocks then we have to do this last.
*/
encode_rtcp_header(&rtcp_header, p);
len += sizeof(rtcp_common_t);
p += sizeof(rtcp_common_t);
encode_report_block(&RR_block, p);
len += sizeof(rtcp_rr_t);
p += sizeof(rtcp_rr_t);
dbg_print(DBG_RTCP, "Sending: RTCP Receiver report\n");
/*
* OK send off the RTCP message to the receiver.
*/
if (sendto(rtp->rtcp_s, databuf, len, 0x0,
(struct sockaddr *)&rtp->rtcp_dst_addr,
sizeof(rtp->rtcp_dst_addr)) < 0){
sphone_error("rtcp_send_RR: sendto: %s\n",
strerror(errno));
sfree(databuf);
return -1;
}
sfree(databuf);
/*
* Fill in rtcp stats struct.
*/
rrs->rrs_prev_pktLost = curr_pktLost; /*save 'til next rtcp to send*/
rrs->rrs_prev_pktSeq = curr_pktSeq;
rrs->rrs_RR_pkts++;
return 0;
}
/*
* Send Bye
*/
int
rtcp_send_bye(struct rtp_session *rtp)
{
rtcp_common_t rtcp_header;
int rtcp_type = RTCP_BYE;
char *p, *databuf;
int len=0;
rtcp_set_hdr(&rtcp_header, rtcp_type, rtp->rtp_ssrc);
if ((databuf = (char *)smalloc(sizeof(rtcp_header.length + 1))) == NULL){
sphone_error("rtcp_send_bye: malloc: %s", strerror(errno));
return -1;
}
p = databuf;
/*
* Make an RTCP header and convert byte order. XXX this works for now
* because we know the length, if we have a variable number of
* report blocks then we have to do this last.
*/
encode_rtcp_header(&rtcp_header, p);
len += sizeof(rtcp_common_t);
/*
* OK send off the RTCP BYE the receiver.
*/
dbg_print(DBG_RTCP, "Sending: RTCP Bye\n");
if (sendto(rtp->rtcp_s, databuf, len, 0x0,
(struct sockaddr *)&rtp->rtcp_dst_addr,
sizeof(rtp->rtcp_dst_addr)) < 0){
sphone_error("rtcp_send_bye: sendto: %s\n",
strerror(errno));
sfree(databuf);
return -1;
}
sfree(databuf);
return 0;
}
/*
* Function called at sender when rtcp message arrives
*/
int
rtcp_send_send_dispatch(int s, void *arg)
{
send_session_t *ss = (send_session_t *)arg;
struct rtp_session *rtp = ss->ss_rtp;
struct timeval dt;
if (rtcp_send_SR(rtp,
ss->ss_rtcp,
ss->ss_cp_audio->cp_samp_sec,
ss->ss_stats.sst_spkt,
ss->ss_stats.sst_sbytes) < 0)
return -1;
dt.tv_sec = RTCP_REPORT_INTERVAL_S;
dt.tv_usec = 0;
timeradd(&rtp->rtcp_timer, &dt, &rtp->rtcp_timer);
if (eventloop_reg_timeout(rtp->rtcp_timer, rtcp_send_send_dispatch,
ss, "RTCP send SR") < 0)
return -1;
return 0;
}
/*
* Function called at receiver when rtcp message arrives
*/
int
rtcp_rcv_send_dispatch(int s, void *arg)
{
rcv_session_t *rs = (rcv_session_t *)arg;
struct rtp_session *rtp = rs->rs_rtp;
struct timeval dt;
if (rtcp_send_RR(rtp,
rs->rs_rtcp,
0, /* XXX: ps->w_s This is in playout_shbuf */
rs->rs_stats.rst_lost_pkts,
rs->seq_cur) < 0)
return -1;
dt.tv_sec = RTCP_REPORT_INTERVAL_S;
dt.tv_usec = 0;
timeradd(&rtp->rtcp_timer, &dt, &rtp->rtcp_timer);
if (eventloop_reg_timeout(rtp->rtcp_timer, rtcp_rcv_send_dispatch,
rs, "RTCP send RR") < 0)
return -1;
return 0;
}
/*
* A sender sends a SR regularly and registers a callback for RRs.
*/
int
rtcp_send_init(int s, send_session_t *ss)
{
struct rtp_session *rtp = ss->ss_rtp;
struct timeval t, dt;
t = gettimestamp();
dt.tv_sec = RTCP_REPORT_INTERVAL_S;
dt.tv_usec = 0;
timeradd(&t, &dt, &rtp->rtcp_timer);
if (eventloop_reg_timeout(rtp->rtcp_timer, rtcp_send_send_dispatch,
ss, "RTCP send SR") < 0)
return -1;
if (eventloop_reg_fd(s, rtcp_send_rcv_dispatch, ss, "RTCP send receiver") < 0)
return -1;
return 0;
}