Added support for sending periodic RTCP reports and the final RTCP bye.

git-svn-id: http://voip.null.ro/svn/yate@3248 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2010-04-27 22:20:09 +00:00
parent bf6b1d43c2
commit da2cd240d0
4 changed files with 142 additions and 6 deletions

View File

@ -30,6 +30,9 @@
; rtcp: bool: Allocate socket for the RTCP protocol by default
;rtcp=enabled
; rtcp_interval: int: RTCP report interval in ms (500-60000), zero disables
;rtcp_interval=4500
; drillhole: bool: Attempt to drill a hole through a firewall or NAT
;drillhole=disable in server mode, enable in client mode

View File

@ -379,7 +379,7 @@ bool RTPReceiver::rtpCheckIntegrity(const unsigned char* data, int len, const vo
RTPSender::RTPSender(RTPSession* session, bool randomTs)
: RTPBaseIO(session), m_evTime(0), m_tsLast(0), m_padding(0)
: RTPBaseIO(session), m_evTime(0), m_padding(0)
{
if (randomTs) {
m_ts = ::random() & ~1;
@ -635,7 +635,8 @@ void UDPSession::setTimeout(int interval)
RTPSession::RTPSession()
: m_direction(FullStop),
m_send(0), m_recv(0), m_secure(0)
m_send(0), m_recv(0), m_secure(0),
m_reportTime(0), m_reportInterval(0)
{
DDebug(DebugInfo,"RTPSession::RTPSession() [%p]",this);
}
@ -668,6 +669,12 @@ void RTPSession::timerTick(const Time& when)
else
m_timeoutTime = when + m_timeoutInterval;
}
if (m_reportInterval) {
if (when >= m_reportTime) {
m_reportTime = when + m_reportInterval;
sendRtcpReport(when);
}
}
}
void RTPSession::rtpData(const void* data, int len)
@ -738,6 +745,8 @@ bool RTPSession::checkCipher(const String& name)
void RTPSession::transport(RTPTransport* trans)
{
if (!trans)
sendRtcpBye();
UDPSession::transport(trans);
if (!m_transport)
m_direction = FullStop;
@ -748,6 +757,7 @@ void RTPSession::sender(RTPSender* send)
DDebug(DebugInfo,"RTPSession::sender(%p) old=%p [%p]",send,m_send,this);
if (send == m_send)
return;
sendRtcpBye();
RTPSender* tmp = m_send;
m_send = send;
if (tmp)
@ -848,6 +858,93 @@ void RTPSession::getStats(String& stats) const
}
}
void RTPSession::setReports(int interval)
{
if (interval > 0 && m_transport && m_transport->rtcpSock()->valid()) {
if (interval < 500)
interval = 500;
else if (interval > 60000)
interval = 60000;
m_reportInterval = interval * (u_int64_t)1000 + (::random() % 20000);
}
else
m_reportInterval = 0;
m_reportTime = 0;
}
static void store32(unsigned char* buf, unsigned int& len, u_int32_t val)
{
buf[len++] = (unsigned char)(val >> 24);
buf[len++] = (unsigned char)(val >> 16);
buf[len++] = (unsigned char)(val >> 8);
buf[len++] = (unsigned char)(val & 0xff);
}
void RTPSession::sendRtcpReport(const Time& when)
{
if (!((m_send || m_recv) && m_transport && m_transport->rtcpSock()->valid()))
return;
unsigned char buf[52];
buf[0] = 0x80; // RC=0
buf[1] = 0xc9; // RR
buf[2] = 0;
unsigned int len = 8;
if (m_send && m_send->ioPackets()) {
// Include a sender report
buf[1] = 0xc8; // SR
// NTP timestamp
store32(buf,len,2208988800 + (when.usec() / 1000000));
store32(buf,len,((when.usec() % 1000000) << 32) / 1000000);
// RTP timestamp
store32(buf,len,m_send->tsLast());
// Packet and octet counters
store32(buf,len,m_send->ioPackets());
store32(buf,len,m_send->ioOctets());
}
if (m_recv && m_recv->ioPackets()) {
// Add a single receiver report
buf[0] |= 0x01; // RC=1
store32(buf,len,m_recv->ssrc());
u_int32_t lost = m_recv->ioPacketsLost();
u_int32_t lostf = 0xff & (lost * 255 / (lost + m_recv->ioPackets()));
store32(buf,len,(lost & 0xffffff) | (lostf << 24));
store32(buf,len,m_recv->fullSeq());
// TODO: Compute and store Jitter, LSR and DLSR
store32(buf,len,0);
store32(buf,len,0);
store32(buf,len,0);
}
// Don't send a RR with no receiver report blocks...
if (len <= 8)
return;
DDebug(DebugInfo,"RTPSession sending RTCP Report [%p]",this);
unsigned int lptr = 4;
store32(buf,lptr,(m_send ? m_send->ssrcInit() : 0));
buf[3] = (len - 1) / 4; // same as ((len + 3) / 4) - 1
static_cast<RTPProcessor*>(m_transport)->rtcpData(buf,len);
}
void RTPSession::sendRtcpBye()
{
if (!(m_send && m_transport && m_transport->rtcpSock()->valid()))
return;
u_int32_t ssrc = m_send->ssrc();
if (!ssrc)
return;
DDebug(DebugInfo,"RTPSession sending RTCP Bye [%p]",this);
// SSRC was initialized if we sent at least one RTP or RTCP packet
unsigned char buf[8];
buf[0] = 0x81;
buf[1] = 0xcb;
buf[2] = 0;
buf[3] = 1; // len = 2 x 32bit
buf[4] = (unsigned char)(ssrc >> 24);
buf[5] = (unsigned char)(ssrc >> 16);
buf[6] = (unsigned char)(ssrc >> 8);
buf[7] = (unsigned char)(0xff & ssrc);
static_cast<RTPProcessor*>(m_transport)->rtcpData(buf,8);
}
UDPTLSession::UDPTLSession(u_int16_t maxLen, u_int8_t maxSec)
: m_rxSeq(0xffff), m_txSeq(0xffff),

View File

@ -271,6 +271,13 @@ public:
inline Socket* rtpSock()
{ return &m_rtpSock; }
/**
* Get the RTCP socket used by this transport
* @return Pointer to the RTCP socket
*/
inline Socket* rtcpSock()
{ return &m_rtcpSock; }
/**
* Drill a hole in a firewall or NAT for the RTP and RTCP sockets
* @return True if at least a packet was sent for the RTP socket
@ -378,7 +385,7 @@ public:
m_ssrcInit(true), m_ssrc(0), m_ts(0),
m_seq(0), m_rollover(0), m_secLen(0), m_mkiLen(0),
m_evTs(0), m_evNum(-1), m_evVol(-1),
m_ioPackets(), m_ioOctets(0),
m_ioPackets(), m_ioOctets(0), m_tsLast(0),
m_dataType(-1), m_eventType(-1), m_silenceType(-1)
{ }
@ -490,6 +497,13 @@ public:
inline u_int32_t ioOctets() const
{ return m_ioOctets; }
/**
* Get the timestamp of the last packet as transmitted over the wire
* @return Timestamp of last packet sent or received
*/
inline unsigned int tsLast() const
{ return m_ts + m_tsLast; }
/**
* Get the session this object belongs to
* @return Pointer to RTP session or NULL
@ -539,6 +553,7 @@ protected:
int m_evVol;
u_int32_t m_ioPackets;
u_int32_t m_ioOctets;
unsigned int m_tsLast;
private:
int m_dataType;
@ -559,7 +574,7 @@ public:
*/
inline RTPReceiver(RTPSession* session = 0)
: RTPBaseIO(session),
m_ioLostPkt(0), m_dejitter(0), m_tsLast(0),
m_ioLostPkt(0), m_dejitter(0),
m_seqSync(0), m_seqCount(0), m_warn(true)
{ }
@ -685,7 +700,6 @@ private:
void finishEvent(unsigned int timestamp);
bool pushEvent(int event, int duration, int volume, unsigned int timestamp);
RTPDejitter* m_dejitter;
unsigned int m_tsLast;
u_int16_t m_seqSync;
u_int16_t m_seqCount;
bool m_warn;
@ -796,7 +810,6 @@ protected:
private:
int m_evTime;
unsigned int m_tsLast;
unsigned char m_padding;
bool sendEventData(unsigned int timestamp);
};
@ -1196,6 +1209,12 @@ public:
*/
void security(RTPSecure* secure);
/**
* Set the RTCP report interval
* @param interval Average interval between reports in msec, zero to disable
*/
void setReports(int interval);
protected:
/**
* Method called periodically to push any asynchronous data or statistics
@ -1203,11 +1222,24 @@ protected:
*/
virtual void timerTick(const Time& when);
/**
* Send a RTCP report
* @param when Time to use as base for timestamps
*/
void sendRtcpReport(const Time& when);
/**
* Send a RTCP BYE when the sender is stopped or replaced
*/
void sendRtcpBye();
private:
Direction m_direction;
RTPSender* m_send;
RTPReceiver* m_recv;
RTPSecure* m_secure;
u_int64_t m_reportTime;
u_int64_t m_reportInterval;
};
/**

View File

@ -99,7 +99,9 @@ static bool s_drill = false;
static Thread::Priority s_priority = Thread::Normal;
static int s_tos = 0;
static int s_sleep = 5;
static int s_interval= 0;
static int s_timeout = 0;
static int s_minjitter = 0;
static int s_maxjitter = 0;
@ -700,6 +702,7 @@ bool YRTPWrapper::startRTP(const char* raddr, unsigned int rport, Message& msg)
(ok ? "opened" : "failed to open"),this);
}
setTimeout(msg,s_timeout);
m_rtp->setReports(msg.getIntValue("rtcp_interval",s_interval));
// if (maxJitter > 0)
// m_rtp->setDejitter(minJitter*1000,maxJitter*1000);
m_bufsize = s_bufsize;
@ -1737,6 +1740,7 @@ void YRTPPlugin::initialize()
s_anyssrc = cfg.getBoolValue("general","anyssrc",false);
s_padding = cfg.getIntValue("general","padding",0);
s_rtcp = cfg.getBoolValue("general","rtcp",true);
s_interval = cfg.getIntValue("general","rtcp_interval",4500);
s_drill = cfg.getBoolValue("general","drillhole",Engine::clientMode());
s_sleep = cfg.getIntValue("general","defsleep",5);
RTPGroup::setMinSleep(cfg.getIntValue("general","minsleep"));