blob: c8a9cddcf751eafd1262f2cde7c6bb2b609fd06d [file] [log] [blame]
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "rtcp_sender.h"
#include <string.h> // memcpy
#include <cassert> // assert
#include <cstdlib> // rand
#include "trace.h"
#include "common_types.h"
#include "critical_section_wrapper.h"
#include "rtp_rtcp_impl.h"
namespace webrtc {
using RTCPUtility::RTCPCnameInformation;
RTCPSender::RTCPSender(const WebRtc_Word32 id,
const bool audio,
RtpRtcpClock* clock,
ModuleRtpRtcpImpl* owner) :
_id(id),
_audio(audio),
_clock(*clock),
_method(kRtcpOff),
_rtpRtcp(*owner),
_criticalSectionTransport(CriticalSectionWrapper::CreateCriticalSection()),
_cbTransport(NULL),
_criticalSectionRTCPSender(CriticalSectionWrapper::CreateCriticalSection()),
_usingNack(false),
_sending(false),
_sendTMMBN(false),
_REMB(false),
_sendREMB(false),
_TMMBR(false),
_IJ(false),
_nextTimeToSendRTCP(0),
_SSRC(0),
_remoteSSRC(0),
_CNAME(),
_reportBlocks(),
_csrcCNAMEs(),
_cameraDelayMS(0),
_lastSendReport(),
_lastRTCPTime(),
_CSRCs(0),
_CSRC(),
_includeCSRCs(true),
_sequenceNumberFIR(0),
_lastTimeFIR(0),
_lengthRembSSRC(0),
_sizeRembSSRC(0),
_rembSSRC(NULL),
_rembBitrate(0),
_bitrate_observer(NULL),
_tmmbrHelp(audio),
_tmmbr_Send(0),
_packetOH_Send(0),
_remoteRateControl(),
_appSend(false),
_appSubType(0),
_appName(),
_appData(NULL),
_appLength(0),
_xrSendVoIPMetric(false),
_xrVoIPMetric()
{
memset(_CNAME, 0, sizeof(_CNAME));
memset(_lastSendReport, 0, sizeof(_lastSendReport));
memset(_lastRTCPTime, 0, sizeof(_lastRTCPTime));
WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__);
}
RTCPSender::~RTCPSender() {
delete [] _rembSSRC;
delete [] _appData;
while (!_reportBlocks.empty()) {
std::map<WebRtc_UWord32, RTCPReportBlock*>::iterator it =
_reportBlocks.begin();
delete it->second;
_reportBlocks.erase(it);
}
while (!_csrcCNAMEs.empty()) {
std::map<WebRtc_UWord32, RTCPCnameInformation*>::iterator it =
_csrcCNAMEs.begin();
delete it->second;
_csrcCNAMEs.erase(it);
}
delete _criticalSectionTransport;
delete _criticalSectionRTCPSender;
WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__);
}
WebRtc_Word32
RTCPSender::Init()
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_method = kRtcpOff;
_cbTransport = NULL;
_usingNack = false;
_sending = false;
_sendTMMBN = false;
_TMMBR = false;
_IJ = false;
_REMB = false;
_sendREMB = false;
_SSRC = 0;
_remoteSSRC = 0;
_cameraDelayMS = 0;
_sequenceNumberFIR = 0;
_tmmbr_Send = 0;
_packetOH_Send = 0;
_remoteRateControl.Reset();
_nextTimeToSendRTCP = 0;
_CSRCs = 0;
_appSend = false;
_appSubType = 0;
if(_appData)
{
delete [] _appData;
_appData = NULL;
}
_appLength = 0;
_xrSendVoIPMetric = false;
memset(&_xrVoIPMetric, 0, sizeof(_xrVoIPMetric));
memset(_CNAME, 0, sizeof(_CNAME));
memset(_lastSendReport, 0, sizeof(_lastSendReport));
memset(_lastRTCPTime, 0, sizeof(_lastRTCPTime));
return 0;
}
void
RTCPSender::ChangeUniqueId(const WebRtc_Word32 id)
{
_id = id;
}
WebRtc_Word32
RTCPSender::RegisterSendTransport(Transport* outgoingTransport)
{
CriticalSectionScoped lock(_criticalSectionTransport);
_cbTransport = outgoingTransport;
return 0;
}
RTCPMethod
RTCPSender::Status() const
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return _method;
}
WebRtc_Word32
RTCPSender::SetRTCPStatus(const RTCPMethod method)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(method != kRtcpOff)
{
if(_audio)
{
_nextTimeToSendRTCP = _clock.GetTimeInMS() + (RTCP_INTERVAL_AUDIO_MS/2);
} else
{
_nextTimeToSendRTCP = _clock.GetTimeInMS() + (RTCP_INTERVAL_VIDEO_MS/2);
}
}
_method = method;
return 0;
}
bool
RTCPSender::Sending() const
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return _sending;
}
WebRtc_Word32
RTCPSender::SetSendingStatus(const bool sending)
{
bool sendRTCPBye = false;
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(_method != kRtcpOff)
{
if(sending == false && _sending == true)
{
// Trigger RTCP bye
sendRTCPBye = true;
}
}
_sending = sending;
}
if(sendRTCPBye)
{
return SendRTCP(kRtcpBye);
}
return 0;
}
bool
RTCPSender::REMB() const
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return _REMB;
}
WebRtc_Word32
RTCPSender::SetREMBStatus(const bool enable)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_REMB = enable;
return 0;
}
WebRtc_Word32
RTCPSender::SetREMBData(const WebRtc_UWord32 bitrate,
const WebRtc_UWord8 numberOfSSRC,
const WebRtc_UWord32* SSRC)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_rembBitrate = bitrate;
if(_sizeRembSSRC < numberOfSSRC)
{
delete [] _rembSSRC;
_rembSSRC = new WebRtc_UWord32[numberOfSSRC];
_sizeRembSSRC = numberOfSSRC;
}
_lengthRembSSRC = numberOfSSRC;
for (int i = 0; i < numberOfSSRC; i++)
{
_rembSSRC[i] = SSRC[i];
}
_sendREMB = true;
return 0;
}
bool RTCPSender::SetRemoteBitrateObserver(RtpRemoteBitrateObserver* observer) {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if (observer && _bitrate_observer) {
return false;
}
_bitrate_observer = observer;
return true;
}
void RTCPSender::UpdateRemoteBitrateEstimate(unsigned int target_bitrate) {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if (_bitrate_observer) {
_bitrate_observer->OnReceiveBitrateChanged(_remoteSSRC, target_bitrate);
}
}
bool
RTCPSender::TMMBR() const
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return _TMMBR;
}
WebRtc_Word32
RTCPSender::SetTMMBRStatus(const bool enable)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_TMMBR = enable;
return 0;
}
bool
RTCPSender::IJ() const
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return _IJ;
}
WebRtc_Word32
RTCPSender::SetIJStatus(const bool enable)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_IJ = enable;
return 0;
}
void
RTCPSender::SetSSRC( const WebRtc_UWord32 ssrc)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(_SSRC != 0)
{
// not first SetSSRC, probably due to a collision
// schedule a new RTCP report
// make sure that we send a RTP packet
_nextTimeToSendRTCP = _clock.GetTimeInMS() + 100;
}
_SSRC = ssrc;
}
WebRtc_Word32
RTCPSender::SetRemoteSSRC( const WebRtc_UWord32 ssrc)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_remoteSSRC = ssrc;
_remoteRateControl.Reset();
return 0;
}
WebRtc_Word32
RTCPSender::SetCameraDelay(const WebRtc_Word32 delayMS)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(delayMS > 1000 || delayMS < -1000)
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument, delay can't be larger than 1 sec", __FUNCTION__);
return -1;
}
_cameraDelayMS = delayMS;
return 0;
}
WebRtc_Word32 RTCPSender::CNAME(char cName[RTCP_CNAME_SIZE]) {
assert(cName);
CriticalSectionScoped lock(_criticalSectionRTCPSender);
cName[RTCP_CNAME_SIZE - 1] = 0;
strncpy(cName, _CNAME, RTCP_CNAME_SIZE - 1);
return 0;
}
WebRtc_Word32 RTCPSender::SetCNAME(const char cName[RTCP_CNAME_SIZE]) {
assert(cName);
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_CNAME[RTCP_CNAME_SIZE - 1] = 0;
strncpy(_CNAME, cName, RTCP_CNAME_SIZE - 1);
return 0;
}
WebRtc_Word32 RTCPSender::AddMixedCNAME(const WebRtc_UWord32 SSRC,
const char cName[RTCP_CNAME_SIZE]) {
assert(cName);
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if (_csrcCNAMEs.size() >= kRtpCsrcSize) {
return -1;
}
RTCPCnameInformation* ptr = new RTCPCnameInformation();
ptr->name[RTCP_CNAME_SIZE - 1] = 0;
strncpy(ptr->name, cName, RTCP_CNAME_SIZE - 1);
_csrcCNAMEs[SSRC] = ptr;
return 0;
}
WebRtc_Word32 RTCPSender::RemoveMixedCNAME(const WebRtc_UWord32 SSRC) {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
std::map<WebRtc_UWord32, RTCPCnameInformation*>::iterator it =
_csrcCNAMEs.find(SSRC);
if (it == _csrcCNAMEs.end()) {
return -1;
}
delete it->second;
_csrcCNAMEs.erase(it);
return 0;
}
bool
RTCPSender::TimeToSendRTCPReport(const bool sendKeyframeBeforeRTP) const
{
/*
For audio we use a fix 5 sec interval
For video we use 1 sec interval fo a BW smaller than 360 kbit/s,
technicaly we break the max 5% RTCP BW for video below 10 kbit/s but that should be extreamly rare
From RFC 3550
MAX RTCP BW is 5% if the session BW
A send report is approximately 65 bytes inc CNAME
A report report is approximately 28 bytes
The RECOMMENDED value for the reduced minimum in seconds is 360
divided by the session bandwidth in kilobits/second. This minimum
is smaller than 5 seconds for bandwidths greater than 72 kb/s.
If the participant has not yet sent an RTCP packet (the variable
initial is true), the constant Tmin is set to 2.5 seconds, else it
is set to 5 seconds.
The interval between RTCP packets is varied randomly over the
range [0.5,1.5] times the calculated interval to avoid unintended
synchronization of all participants
if we send
If the participant is a sender (we_sent true), the constant C is
set to the average RTCP packet size (avg_rtcp_size) divided by 25%
of the RTCP bandwidth (rtcp_bw), and the constant n is set to the
number of senders.
if we receive only
If we_sent is not true, the constant C is set
to the average RTCP packet size divided by 75% of the RTCP
bandwidth. The constant n is set to the number of receivers
(members - senders). If the number of senders is greater than
25%, senders and receivers are treated together.
reconsideration NOT required for peer-to-peer
"timer reconsideration" is
employed. This algorithm implements a simple back-off mechanism
which causes users to hold back RTCP packet transmission if the
group sizes are increasing.
n = number of members
C = avg_size/(rtcpBW/4)
3. The deterministic calculated interval Td is set to max(Tmin, n*C).
4. The calculated interval T is set to a number uniformly distributed
between 0.5 and 1.5 times the deterministic calculated interval.
5. The resulting value of T is divided by e-3/2=1.21828 to compensate
for the fact that the timer reconsideration algorithm converges to
a value of the RTCP bandwidth below the intended average
*/
WebRtc_UWord32 now = _clock.GetTimeInMS();
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(_method == kRtcpOff)
{
return false;
}
if(!_audio && sendKeyframeBeforeRTP)
{
// for video key-frames we want to send the RTCP before the large key-frame
// if we have a 100 ms margin
now += RTCP_SEND_BEFORE_KEY_FRAME_MS;
}
if(now > _nextTimeToSendRTCP)
{
return true;
} else if(now < 0x0000ffff && _nextTimeToSendRTCP > 0xffff0000) // 65 sec margin
{
// wrap
return true;
}
return false;
}
WebRtc_UWord32
RTCPSender::LastSendReport( WebRtc_UWord32& lastRTCPTime)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
lastRTCPTime = _lastRTCPTime[0];
return _lastSendReport[0];
}
WebRtc_UWord32
RTCPSender::SendTimeOfSendReport(const WebRtc_UWord32 sendReport)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
// This is only saved when we are the sender
if((_lastSendReport[0] == 0) || (sendReport == 0))
{
return 0; // will be ignored
} else
{
for(int i = 0; i < RTCP_NUMBER_OF_SR; ++i)
{
if( _lastSendReport[i] == sendReport)
{
return _lastRTCPTime[i];
}
}
}
return 0;
}
WebRtc_Word32 RTCPSender::AddReportBlock(const WebRtc_UWord32 SSRC,
const RTCPReportBlock* reportBlock) {
if (reportBlock == NULL) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s invalid argument", __FUNCTION__);
return -1;
}
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if (_reportBlocks.size() >= RTCP_MAX_REPORT_BLOCKS) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s invalid argument", __FUNCTION__);
return -1;
}
RTCPReportBlock* copyReportBlock = new RTCPReportBlock();
memcpy(copyReportBlock, reportBlock, sizeof(RTCPReportBlock));
_reportBlocks[SSRC] = copyReportBlock;
return 0;
}
WebRtc_Word32 RTCPSender::RemoveReportBlock(const WebRtc_UWord32 SSRC) {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
std::map<WebRtc_UWord32, RTCPReportBlock*>::iterator it =
_reportBlocks.find(SSRC);
if (it == _reportBlocks.end()) {
return -1;
}
delete it->second;
_reportBlocks.erase(it);
return 0;
}
WebRtc_Word32
RTCPSender::BuildSR(WebRtc_UWord8* rtcpbuffer,
WebRtc_UWord32& pos,
const WebRtc_UWord32 NTPsec,
const WebRtc_UWord32 NTPfrac,
const RTCPReportBlock* received)
{
// sanity
if(pos + 52 >= IP_PACKET_SIZE)
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__);
return -2;
}
WebRtc_UWord32 RTPtime;
WebRtc_UWord32 BackTimedNTPsec;
WebRtc_UWord32 BackTimedNTPfrac;
WebRtc_UWord32 posNumberOfReportBlocks = pos;
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80;
// Sender report
rtcpbuffer[pos++]=(WebRtc_UWord8)200;
for(int i = (RTCP_NUMBER_OF_SR-2); i >= 0; i--)
{
// shift old
_lastSendReport[i+1] = _lastSendReport[i];
_lastRTCPTime[i+1] =_lastRTCPTime[i];
}
_lastRTCPTime[0] = ModuleRTPUtility::ConvertNTPTimeToMS(NTPsec, NTPfrac); // before video cam compensation
if(_cameraDelayMS >= 0)
{
// fraction of a second as an unsigned word32 4.294 967 296E9
WebRtc_UWord32 cameraDelayFixFrac = (WebRtc_UWord32)_cameraDelayMS* 4294967; // note camera delay can't be larger than +/-1000ms
if(NTPfrac > cameraDelayFixFrac)
{
// no problem just reduce the fraction part
BackTimedNTPfrac = NTPfrac - cameraDelayFixFrac;
BackTimedNTPsec = NTPsec;
} else
{
// we need to reduce the sec and add that sec to the frac
BackTimedNTPsec = NTPsec - 1;
BackTimedNTPfrac = 0xffffffff - (cameraDelayFixFrac - NTPfrac);
}
} else
{
// fraction of a second as an unsigned word32 4.294 967 296E9
WebRtc_UWord32 cameraDelayFixFrac = (WebRtc_UWord32)(-_cameraDelayMS)* 4294967; // note camera delay can't be larger than +/-1000ms
if(NTPfrac > 0xffffffff - cameraDelayFixFrac)
{
// we need to add the sec and add that sec to the frac
BackTimedNTPsec = NTPsec + 1;
BackTimedNTPfrac = cameraDelayFixFrac + NTPfrac; // this will wrap but that is ok
} else
{
// no problem just add the fraction part
BackTimedNTPsec = NTPsec;
BackTimedNTPfrac = NTPfrac + cameraDelayFixFrac;
}
}
_lastSendReport[0] = (BackTimedNTPsec <<16) + (BackTimedNTPfrac >> 16);
// RTP timestamp
// This should have a ramdom start value added
// RTP is counted from NTP not the acctual RTP
// This reflects the perfect RTP time
// we solve this by initiating RTP to our NTP :)
WebRtc_UWord32 freqHz = 90000; // For video
if(_audio)
{
freqHz = _rtpRtcp.CurrentSendFrequencyHz();
RTPtime = ModuleRTPUtility::GetCurrentRTP(&_clock, freqHz);
}
else // video
{
// used to be (WebRtc_UWord32)(((float)BackTimedNTPfrac/(float)FRAC)* 90000)
WebRtc_UWord32 tmp = 9*(BackTimedNTPfrac/429496);
RTPtime = BackTimedNTPsec*freqHz + tmp;
}
// Add sender data
// Save for our length field
pos++;
pos++;
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// NTP
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, BackTimedNTPsec);
pos += 4;
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, BackTimedNTPfrac);
pos += 4;
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, RTPtime);
pos += 4;
//sender's packet count
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _rtpRtcp.PacketCountSent());
pos += 4;
//sender's octet count
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _rtpRtcp.ByteCountSent());
pos += 4;
WebRtc_UWord8 numberOfReportBlocks = 0;
WebRtc_Word32 retVal = AddReportBlocks(rtcpbuffer, pos, numberOfReportBlocks, received, NTPsec, NTPfrac);
if(retVal < 0)
{
//
return retVal ;
}
rtcpbuffer[posNumberOfReportBlocks] += numberOfReportBlocks;
WebRtc_UWord16 len = WebRtc_UWord16((pos/4) -1);
ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+2, len);
return 0;
}
WebRtc_Word32 RTCPSender::BuildSDEC(WebRtc_UWord8* rtcpbuffer,
WebRtc_UWord32& pos) {
size_t lengthCname = strlen(_CNAME);
assert(lengthCname < RTCP_CNAME_SIZE);
// sanity
if(pos + 12 + lengthCname >= IP_PACKET_SIZE) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s invalid argument", __FUNCTION__);
return -2;
}
// SDEC Source Description
// We always need to add SDES CNAME
rtcpbuffer[pos++] = static_cast<WebRtc_UWord8>(0x80 + 1 + _csrcCNAMEs.size());
rtcpbuffer[pos++] = static_cast<WebRtc_UWord8>(202);
// handle SDES length later on
WebRtc_UWord32 SDESLengthPos = pos;
pos++;
pos++;
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// CNAME = 1
rtcpbuffer[pos++] = static_cast<WebRtc_UWord8>(1);
//
rtcpbuffer[pos++] = static_cast<WebRtc_UWord8>(lengthCname);
WebRtc_UWord16 SDESLength = 10;
memcpy(&rtcpbuffer[pos], _CNAME, lengthCname);
pos += lengthCname;
SDESLength += (WebRtc_UWord16)lengthCname;
WebRtc_UWord16 padding = 0;
// We must have a zero field even if we have an even multiple of 4 bytes
if ((pos % 4) == 0) {
padding++;
rtcpbuffer[pos++]=0;
}
while ((pos % 4) != 0) {
padding++;
rtcpbuffer[pos++]=0;
}
SDESLength += padding;
std::map<WebRtc_UWord32, RTCPUtility::RTCPCnameInformation*>::iterator it =
_csrcCNAMEs.begin();
for(; it != _csrcCNAMEs.end(); it++) {
RTCPCnameInformation* cname = it->second;
WebRtc_UWord32 SSRC = it->first;
// Add SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, SSRC);
pos += 4;
// CNAME = 1
rtcpbuffer[pos++] = static_cast<WebRtc_UWord8>(1);
size_t length = strlen(cname->name);
assert(length < RTCP_CNAME_SIZE);
rtcpbuffer[pos++]= static_cast<WebRtc_UWord8>(length);
SDESLength += 6;
memcpy(&rtcpbuffer[pos],cname->name, length);
pos += length;
SDESLength += length;
WebRtc_UWord16 padding = 0;
// We must have a zero field even if we have an even multiple of 4 bytes
if((pos % 4) == 0){
padding++;
rtcpbuffer[pos++]=0;
}
while((pos % 4) != 0){
padding++;
rtcpbuffer[pos++] = 0;
}
SDESLength += padding;
}
// in 32-bit words minus one and we don't count the header
WebRtc_UWord16 buffer_length = (SDESLength / 4) - 1;
ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer + SDESLengthPos,
buffer_length);
return 0;
}
WebRtc_Word32
RTCPSender::BuildRR(WebRtc_UWord8* rtcpbuffer,
WebRtc_UWord32& pos,
const WebRtc_UWord32 NTPsec,
const WebRtc_UWord32 NTPfrac,
const RTCPReportBlock* received)
{
// sanity one block
if(pos + 32 >= IP_PACKET_SIZE)
{
return -2;
}
WebRtc_UWord32 posNumberOfReportBlocks = pos;
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80;
rtcpbuffer[pos++]=(WebRtc_UWord8)201;
// Save for our length field
pos++;
pos++;
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
WebRtc_UWord8 numberOfReportBlocks = 0;
WebRtc_Word32 retVal = AddReportBlocks(rtcpbuffer, pos, numberOfReportBlocks, received, NTPsec, NTPfrac);
if(retVal < 0)
{
return retVal;
}
rtcpbuffer[posNumberOfReportBlocks] += numberOfReportBlocks;
WebRtc_UWord16 len = WebRtc_UWord16((pos)/4 -1);
ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+2, len);
return 0;
}
// From RFC 5450: Transmission Time Offsets in RTP Streams.
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// hdr |V=2|P| RC | PT=IJ=195 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | inter-arrival jitter |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . .
// . .
// | inter-arrival jitter |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// If present, this RTCP packet must be placed after a receiver report
// (inside a compound RTCP packet), and MUST have the same value for RC
// (reception report count) as the receiver report.
WebRtc_Word32
RTCPSender::BuildExtendedJitterReport(
WebRtc_UWord8* rtcpbuffer,
WebRtc_UWord32& pos,
const WebRtc_UWord32 jitterTransmissionTimeOffset)
{
if (_reportBlocks.size() > 0)
{
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "Not implemented.");
return 0;
}
// sanity
if(pos + 8 >= IP_PACKET_SIZE)
{
return -2;
}
// add picture loss indicator
WebRtc_UWord8 RC = 1;
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + RC;
rtcpbuffer[pos++]=(WebRtc_UWord8)195;
// Used fixed length of 2
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)(1);
// Add inter-arrival jitter
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer + pos,
jitterTransmissionTimeOffset);
pos += 4;
return 0;
}
WebRtc_Word32
RTCPSender::BuildPLI(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos)
{
// sanity
if(pos + 12 >= IP_PACKET_SIZE)
{
return -2;
}
// add picture loss indicator
WebRtc_UWord8 FMT = 1;
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT;
rtcpbuffer[pos++]=(WebRtc_UWord8)206;
//Used fixed length of 2
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)(2);
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// Add the remote SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC);
pos += 4;
return 0;
}
WebRtc_Word32
RTCPSender::BuildFIR(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos, const WebRtc_UWord32 RTT)
{
bool firRepeat = false;
WebRtc_UWord32 diff = _clock.GetTimeInMS() - _lastTimeFIR;
if(diff < RTT + 3) // 3 is processing jitter
{
// we have recently sent a FIR
// don't send another
return 0;
} else
{
if(diff < (RTT*2 + RTCP_MIN_FRAME_LENGTH_MS))
{
// send a FIR_REPEAT instead of a FIR
firRepeat = true;
}
}
_lastTimeFIR = _clock.GetTimeInMS();
if(!firRepeat)
{
_sequenceNumberFIR++; // do not increase if repetition
}
// sanity
if(pos + 20 >= IP_PACKET_SIZE)
{
return -2;
}
// add full intra request indicator
WebRtc_UWord8 FMT = 4;
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT;
rtcpbuffer[pos++]=(WebRtc_UWord8)206;
//Length of 4
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)(4);
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// RFC 5104 4.3.1.2. Semantics
// SSRC of media source
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
// Additional Feedback Control Information (FCI)
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC);
pos += 4;
rtcpbuffer[pos++]=(WebRtc_UWord8)(_sequenceNumberFIR);
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
return 0;
}
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| First | Number | PictureID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
WebRtc_Word32
RTCPSender::BuildSLI(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos, const WebRtc_UWord8 pictureID)
{
// sanity
if(pos + 16 >= IP_PACKET_SIZE)
{
return -2;
}
// add slice loss indicator
WebRtc_UWord8 FMT = 2;
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT;
rtcpbuffer[pos++]=(WebRtc_UWord8)206;
//Used fixed length of 3
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)(3);
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// Add the remote SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC);
pos += 4;
// Add first, number & picture ID 6 bits
// first = 0, 13 - bits
// number = 0x1fff, 13 - bits only ones for now
WebRtc_UWord32 sliField = (0x1fff << 6)+ (0x3f & pictureID);
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, sliField);
pos += 4;
return 0;
}
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PB |0| Payload Type| Native RPSI bit string |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| defined per codec ... | Padding (0) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/*
* Note: not generic made for VP8
*/
WebRtc_Word32
RTCPSender::BuildRPSI(WebRtc_UWord8* rtcpbuffer,
WebRtc_UWord32& pos,
const WebRtc_UWord64 pictureID,
const WebRtc_UWord8 payloadType)
{
// sanity
if(pos + 24 >= IP_PACKET_SIZE)
{
return -2;
}
// add Reference Picture Selection Indication
WebRtc_UWord8 FMT = 3;
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT;
rtcpbuffer[pos++]=(WebRtc_UWord8)206;
// calc length
WebRtc_UWord32 bitsRequired = 7;
WebRtc_UWord8 bytesRequired = 1;
while((pictureID>>bitsRequired) > 0)
{
bitsRequired += 7;
bytesRequired++;
}
WebRtc_UWord8 size = 3;
if(bytesRequired > 6)
{
size = 5;
} else if(bytesRequired > 2)
{
size = 4;
}
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=size;
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// Add the remote SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC);
pos += 4;
// calc padding length
WebRtc_UWord8 paddingBytes = 4-((2+bytesRequired)%4);
if(paddingBytes == 4)
{
paddingBytes = 0;
}
// add padding length in bits
rtcpbuffer[pos] = paddingBytes*8; // padding can be 0, 8, 16 or 24
pos++;
// add payload type
rtcpbuffer[pos] = payloadType;
pos++;
// add picture ID
for(int i = bytesRequired-1; i > 0; i--)
{
rtcpbuffer[pos] = 0x80 | WebRtc_UWord8(pictureID >> (i*7));
pos++;
}
// add last byte of picture ID
rtcpbuffer[pos] = WebRtc_UWord8(pictureID & 0x7f);
pos++;
// add padding
for(int j = 0; j <paddingBytes; j++)
{
rtcpbuffer[pos] = 0;
pos++;
}
return 0;
}
WebRtc_Word32
RTCPSender::BuildREMB(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos)
{
// sanity
if(pos + 20 + 4 * _lengthRembSSRC >= IP_PACKET_SIZE)
{
return -2;
}
// add application layer feedback
WebRtc_UWord8 FMT = 15;
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT;
rtcpbuffer[pos++]=(WebRtc_UWord8)206;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=_lengthRembSSRC + 4;
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// Remote SSRC must be 0
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, 0);
pos += 4;
rtcpbuffer[pos++]='R';
rtcpbuffer[pos++]='E';
rtcpbuffer[pos++]='M';
rtcpbuffer[pos++]='B';
rtcpbuffer[pos++] = _lengthRembSSRC;
// 6 bit Exp
// 18 bit mantissa
WebRtc_UWord8 brExp = 0;
for(WebRtc_UWord32 i=0; i<64; i++)
{
if(_rembBitrate <= ((WebRtc_UWord32)262143 << i))
{
brExp = i;
break;
}
}
const WebRtc_UWord32 brMantissa = (_rembBitrate >> brExp);
rtcpbuffer[pos++]=(WebRtc_UWord8)((brExp << 2) + ((brMantissa >> 16) & 0x03));
rtcpbuffer[pos++]=(WebRtc_UWord8)(brMantissa >> 8);
rtcpbuffer[pos++]=(WebRtc_UWord8)(brMantissa);
for (int i = 0; i < _lengthRembSSRC; i++)
{
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _rembSSRC[i]);
pos += 4;
}
return 0;
}
WebRtc_UWord32
RTCPSender::CalculateNewTargetBitrate(WebRtc_UWord32 RTT)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
WebRtc_UWord32 target_bitrate =
_remoteRateControl.TargetBitRate(RTT, _clock.GetTimeInMS());
_tmmbr_Send = target_bitrate / 1000;
return target_bitrate;
}
bool
RTCPSender::ValidBitrateEstimate() {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return _remoteRateControl.ValidEstimate();
}
WebRtc_Word32
RTCPSender::BuildTMMBR(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos)
{
// Before sending the TMMBR check the received TMMBN, only an owner is allowed to raise the bitrate
// If the sender is an owner of the TMMBN -> send TMMBR
// If not an owner but the TMMBR would enter the TMMBN -> send TMMBR
// get current bounding set from RTCP receiver
bool tmmbrOwner = false;
TMMBRSet* candidateSet = _tmmbrHelp.CandidateSet(); // store in candidateSet, allocates one extra slot
// holding _criticalSectionRTCPSender while calling RTCPreceiver which will accuire _criticalSectionRTCPReceiver
// is a potental deadlock but since RTCPreceiver is not doing the revese we should be fine
WebRtc_Word32 lengthOfBoundingSet = _rtpRtcp.BoundingSet(tmmbrOwner, candidateSet);
if(lengthOfBoundingSet > 0)
{
for (WebRtc_Word32 i = 0; i < lengthOfBoundingSet; i++)
{
if( candidateSet->ptrTmmbrSet[i] == _tmmbr_Send &&
candidateSet->ptrPacketOHSet[i] == _packetOH_Send)
{
// do not send the same tuple
return 0;
}
}
if(!tmmbrOwner)
{
// use received bounding set as candidate set
// add current tuple
candidateSet->ptrTmmbrSet[lengthOfBoundingSet] = _tmmbr_Send;
candidateSet->ptrPacketOHSet[lengthOfBoundingSet] = _packetOH_Send;
candidateSet->ptrSsrcSet[lengthOfBoundingSet] = _SSRC;
int numCandidates = lengthOfBoundingSet+ 1;
// find bounding set
TMMBRSet* boundingSet = NULL;
int numBoundingSet = _tmmbrHelp.FindTMMBRBoundingSet(boundingSet);
if(numBoundingSet > 0 || numBoundingSet <= numCandidates)
{
tmmbrOwner = _tmmbrHelp.IsOwner(_SSRC, numBoundingSet);
}
if(!tmmbrOwner)
{
// did not enter bounding set, no meaning to send this request
return 0;
}
}
}
if(_tmmbr_Send)
{
// sanity
if(pos + 20 >= IP_PACKET_SIZE)
{
return -2;
}
// add TMMBR indicator
WebRtc_UWord8 FMT = 3;
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT;
rtcpbuffer[pos++]=(WebRtc_UWord8)205;
//Length of 4
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)(4);
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// RFC 5104 4.2.1.2. Semantics
// SSRC of media source
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
// Additional Feedback Control Information (FCI)
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC);
pos += 4;
WebRtc_UWord32 bitRate = _tmmbr_Send*1000;
WebRtc_UWord32 mmbrExp = 0;
for(WebRtc_UWord32 i=0;i<64;i++)
{
if(bitRate <= ((WebRtc_UWord32)131071 << i))
{
mmbrExp = i;
break;
}
}
WebRtc_UWord32 mmbrMantissa = (bitRate >> mmbrExp);
rtcpbuffer[pos++]=(WebRtc_UWord8)((mmbrExp << 2) + ((mmbrMantissa >> 15) & 0x03));
rtcpbuffer[pos++]=(WebRtc_UWord8)(mmbrMantissa >> 7);
rtcpbuffer[pos++]=(WebRtc_UWord8)((mmbrMantissa << 1) + ((_packetOH_Send >> 8)& 0x01));
rtcpbuffer[pos++]=(WebRtc_UWord8)(_packetOH_Send);
}
return 0;
}
WebRtc_Word32
RTCPSender::BuildTMMBN(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos)
{
TMMBRSet* boundingSet = _tmmbrHelp.BoundingSetToSend();
if(boundingSet == NULL)
{
return -1;
}
// sanity
if(pos + 12 + boundingSet->lengthOfSet*8 >= IP_PACKET_SIZE)
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__);
return -2;
}
WebRtc_UWord8 FMT = 4;
// add TMMBN indicator
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT;
rtcpbuffer[pos++]=(WebRtc_UWord8)205;
//Add length later
int posLength = pos;
pos++;
pos++;
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// RFC 5104 4.2.2.2. Semantics
// SSRC of media source
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
// Additional Feedback Control Information (FCI)
int numBoundingSet = 0;
for(WebRtc_UWord32 n=0; n< boundingSet->lengthOfSet; n++)
{
if (boundingSet->ptrTmmbrSet[n] > 0)
{
WebRtc_UWord32 tmmbrSSRC = boundingSet->ptrSsrcSet[n];
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, tmmbrSSRC);
pos += 4;
WebRtc_UWord32 bitRate = boundingSet->ptrTmmbrSet[n] * 1000;
WebRtc_UWord32 mmbrExp = 0;
for(int i=0; i<64; i++)
{
if(bitRate <= ((WebRtc_UWord32)131071 << i))
{
mmbrExp = i;
break;
}
}
WebRtc_UWord32 mmbrMantissa = (bitRate >> mmbrExp);
WebRtc_UWord32 measuredOH = boundingSet->ptrPacketOHSet[n];
rtcpbuffer[pos++]=(WebRtc_UWord8)((mmbrExp << 2) + ((mmbrMantissa >> 15) & 0x03));
rtcpbuffer[pos++]=(WebRtc_UWord8)(mmbrMantissa >> 7);
rtcpbuffer[pos++]=(WebRtc_UWord8)((mmbrMantissa << 1) + ((measuredOH >> 8)& 0x01));
rtcpbuffer[pos++]=(WebRtc_UWord8)(measuredOH);
numBoundingSet++;
}
}
WebRtc_UWord16 length= (WebRtc_UWord16)(2+2*numBoundingSet);
rtcpbuffer[posLength++]=(WebRtc_UWord8)(length>>8);
rtcpbuffer[posLength]=(WebRtc_UWord8)(length);
return 0;
}
WebRtc_Word32
RTCPSender::BuildAPP(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos)
{
// sanity
if(_appData == NULL)
{
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s invalid state", __FUNCTION__);
return -1;
}
if(pos + 12 + _appLength >= IP_PACKET_SIZE)
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__);
return -2;
}
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + _appSubType;
// Add APP ID
rtcpbuffer[pos++]=(WebRtc_UWord8)204;
WebRtc_UWord16 length = (_appLength>>2) + 2; // include SSRC and name
rtcpbuffer[pos++]=(WebRtc_UWord8)(length>>8);
rtcpbuffer[pos++]=(WebRtc_UWord8)(length);
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// Add our application name
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _appName);
pos += 4;
// Add the data
memcpy(rtcpbuffer +pos, _appData,_appLength);
pos += _appLength;
return 0;
}
WebRtc_Word32
RTCPSender::BuildNACK(WebRtc_UWord8* rtcpbuffer,
WebRtc_UWord32& pos,
const WebRtc_Word32 nackSize,
const WebRtc_UWord16* nackList)
{
// sanity
if(pos + 16 >= IP_PACKET_SIZE)
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__);
return -2;
}
// int size, WebRtc_UWord16* nackList
// add nack list
WebRtc_UWord8 FMT = 1;
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT;
rtcpbuffer[pos++]=(WebRtc_UWord8)205;
rtcpbuffer[pos++]=(WebRtc_UWord8) 0;
int nackSizePos = pos;
rtcpbuffer[pos++]=(WebRtc_UWord8)(3); //setting it to one kNACK signal as default
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// Add the remote SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC);
pos += 4;
// add the list
int i = 0;
int numOfNackFields = 0;
while(nackSize > i && numOfNackFields < 253)
{
WebRtc_UWord16 nack = nackList[i];
// put dow our sequence number
ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+pos, nack);
pos += 2;
i++;
numOfNackFields++;
if(nackSize > i)
{
bool moreThan16Away = (WebRtc_UWord16(nack+16) < nackList[i])?true: false;
if(!moreThan16Away)
{
// check for a wrap
if(WebRtc_UWord16(nack+16) > 0xff00 && nackList[i] < 0x0fff)
{
// wrap
moreThan16Away = true;
}
}
if(moreThan16Away)
{
// next is more than 16 away
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
} else
{
// build our bitmask
WebRtc_UWord16 bitmask = 0;
bool within16Away = (WebRtc_UWord16(nack+16) > nackList[i])?true: false;
if(within16Away)
{
// check for a wrap
if(WebRtc_UWord16(nack+16) > 0xff00 && nackList[i] < 0x0fff)
{
// wrap
within16Away = false;
}
}
while( nackSize > i && within16Away)
{
WebRtc_Word16 shift = (nackList[i]-nack)-1;
assert(!(shift > 15) && !(shift < 0));
bitmask += (1<< shift);
i++;
if(nackSize > i)
{
within16Away = (WebRtc_UWord16(nack+16) > nackList[i])?true: false;
if(within16Away)
{
// check for a wrap
if(WebRtc_UWord16(nack+16) > 0xff00 && nackList[i] < 0x0fff)
{
// wrap
within16Away = false;
}
}
}
}
ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+pos, bitmask);
pos += 2;
}
// sanity do we have room from one more 4 byte block?
if(pos + 4 >= IP_PACKET_SIZE)
{
return -2;
}
} else
{
// no more in the list
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
}
}
rtcpbuffer[nackSizePos]=(WebRtc_UWord8)(2+numOfNackFields);
return 0;
}
WebRtc_Word32
RTCPSender::BuildBYE(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos)
{
// sanity
if(pos + 8 >= IP_PACKET_SIZE)
{
return -2;
}
if(_includeCSRCs)
{
// Add a bye packet
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + 1 + _CSRCs; // number of SSRC+CSRCs
rtcpbuffer[pos++]=(WebRtc_UWord8)203;
// length
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)(1 + _CSRCs);
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// add CSRCs
for(int i = 0; i < _CSRCs; i++)
{
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _CSRC[i]);
pos += 4;
}
} else
{
// Add a bye packet
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + 1; // number of SSRC+CSRCs
rtcpbuffer[pos++]=(WebRtc_UWord8)203;
// length
rtcpbuffer[pos++]=(WebRtc_UWord8)0;
rtcpbuffer[pos++]=(WebRtc_UWord8)1;
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
}
return 0;
}
WebRtc_Word32
RTCPSender::BuildVoIPMetric(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos)
{
// sanity
if(pos + 44 >= IP_PACKET_SIZE)
{
return -2;
}
// Add XR header
rtcpbuffer[pos++]=(WebRtc_UWord8)0x80;
rtcpbuffer[pos++]=(WebRtc_UWord8)207;
WebRtc_UWord32 XRLengthPos = pos;
// handle length later on
pos++;
pos++;
// Add our own SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC);
pos += 4;
// Add a VoIP metrics block
rtcpbuffer[pos++]=7;
rtcpbuffer[pos++]=0;
rtcpbuffer[pos++]=0;
rtcpbuffer[pos++]=8;
// Add the remote SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC);
pos += 4;
rtcpbuffer[pos++] = _xrVoIPMetric.lossRate;
rtcpbuffer[pos++] = _xrVoIPMetric.discardRate;
rtcpbuffer[pos++] = _xrVoIPMetric.burstDensity;
rtcpbuffer[pos++] = _xrVoIPMetric.gapDensity;
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.burstDuration >> 8);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.burstDuration);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.gapDuration >> 8);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.gapDuration);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.roundTripDelay >> 8);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.roundTripDelay);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.endSystemDelay >> 8);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.endSystemDelay);
rtcpbuffer[pos++] = _xrVoIPMetric.signalLevel;
rtcpbuffer[pos++] = _xrVoIPMetric.noiseLevel;
rtcpbuffer[pos++] = _xrVoIPMetric.RERL;
rtcpbuffer[pos++] = _xrVoIPMetric.Gmin;
rtcpbuffer[pos++] = _xrVoIPMetric.Rfactor;
rtcpbuffer[pos++] = _xrVoIPMetric.extRfactor;
rtcpbuffer[pos++] = _xrVoIPMetric.MOSLQ;
rtcpbuffer[pos++] = _xrVoIPMetric.MOSCQ;
rtcpbuffer[pos++] = _xrVoIPMetric.RXconfig;
rtcpbuffer[pos++] = 0; // reserved
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBnominal >> 8);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBnominal);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBmax >> 8);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBmax);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBabsMax >> 8);
rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBabsMax);
rtcpbuffer[XRLengthPos]=(WebRtc_UWord8)(0);
rtcpbuffer[XRLengthPos+1]=(WebRtc_UWord8)(10);
return 0;
}
WebRtc_Word32
RTCPSender::SendRTCP(const WebRtc_UWord32 packetTypeFlags,
const WebRtc_Word32 nackSize, // NACK
const WebRtc_UWord16* nackList, // NACK
const WebRtc_UWord32 RTT, // FIR
const WebRtc_UWord64 pictureID) // SLI & RPSI
{
WebRtc_UWord32 rtcpPacketTypeFlags = packetTypeFlags;
WebRtc_UWord32 pos = 0;
WebRtc_UWord8 rtcpbuffer[IP_PACKET_SIZE];
do // only to be able to use break :) (and the critsect must be inside its own scope)
{
// collect the received information
RTCPReportBlock received;
bool hasReceived = false;
WebRtc_UWord32 NTPsec = 0;
WebRtc_UWord32 NTPfrac = 0;
bool rtcpCompound = false;
WebRtc_UWord32 jitterTransmissionOffset = 0;
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(_method == kRtcpOff)
{
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
"%s invalid state", __FUNCTION__);
return -1;
}
rtcpCompound = (_method == kRtcpCompound) ? true : false;
}
if (rtcpCompound ||
rtcpPacketTypeFlags & kRtcpReport ||
rtcpPacketTypeFlags & kRtcpSr ||
rtcpPacketTypeFlags & kRtcpRr)
{
// get statistics from our RTPreceiver outside critsect
if(_rtpRtcp.ReportBlockStatistics(&received.fractionLost,
&received.cumulativeLost,
&received.extendedHighSeqNum,
&received.jitter,
&jitterTransmissionOffset) == 0)
{
hasReceived = true;
WebRtc_UWord32 lastReceivedRRNTPsecs = 0;
WebRtc_UWord32 lastReceivedRRNTPfrac = 0;
WebRtc_UWord32 remoteSR = 0;
// ok even if we have not received a SR, we will send 0 in that case
_rtpRtcp.LastReceivedNTP(lastReceivedRRNTPsecs,
lastReceivedRRNTPfrac,
remoteSR);
// get our NTP as late as possible to avoid a race
_clock.CurrentNTP(NTPsec, NTPfrac);
// Delay since last received report
WebRtc_UWord32 delaySinceLastReceivedSR = 0;
if((lastReceivedRRNTPsecs !=0) || (lastReceivedRRNTPfrac !=0))
{
// get the 16 lowest bits of seconds and the 16 higest bits of fractions
WebRtc_UWord32 now=NTPsec&0x0000FFFF;
now <<=16;
now += (NTPfrac&0xffff0000)>>16;
WebRtc_UWord32 receiveTime = lastReceivedRRNTPsecs&0x0000FFFF;
receiveTime <<=16;
receiveTime += (lastReceivedRRNTPfrac&0xffff0000)>>16;
delaySinceLastReceivedSR = now-receiveTime;
}
received.delaySinceLastSR = delaySinceLastReceivedSR;
received.lastSR = remoteSR;
} else
{
// we need to send our NTP even if we dont have received any reports
_clock.CurrentNTP(NTPsec, NTPfrac);
}
}
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(_TMMBR ) // attach TMMBR to send and receive reports
{
rtcpPacketTypeFlags |= kRtcpTmmbr;
}
if(_appSend)
{
rtcpPacketTypeFlags |= kRtcpApp;
_appSend = false;
}
if(_REMB && _sendREMB)
{
// Always attach REMB to SR if that is configured. Note that REMB is
// only sent on one of the RTP modules in the REMB group.
rtcpPacketTypeFlags |= kRtcpRemb;
}
if(_xrSendVoIPMetric)
{
rtcpPacketTypeFlags |= kRtcpXrVoipMetric;
_xrSendVoIPMetric = false;
}
if(_sendTMMBN) // set when having received a TMMBR
{
rtcpPacketTypeFlags |= kRtcpTmmbn;
_sendTMMBN = false;
}
if(_method == kRtcpCompound)
{
if(_sending)
{
rtcpPacketTypeFlags |= kRtcpSr;
} else
{
rtcpPacketTypeFlags |= kRtcpRr;
}
if (_IJ && hasReceived)
{
rtcpPacketTypeFlags |= kRtcpTransmissionTimeOffset;
}
} else if(_method == kRtcpNonCompound)
{
if(rtcpPacketTypeFlags & kRtcpReport)
{
if(_sending)
{
rtcpPacketTypeFlags |= kRtcpSr;
} else
{
rtcpPacketTypeFlags |= kRtcpRr;
}
}
}
if( rtcpPacketTypeFlags & kRtcpRr ||
rtcpPacketTypeFlags & kRtcpSr)
{
// generate next time to send a RTCP report
// seeded from RTP constructor
WebRtc_Word32 random = rand() % 1000;
WebRtc_Word32 timeToNext = RTCP_INTERVAL_AUDIO_MS;
if(_audio)
{
timeToNext = (RTCP_INTERVAL_AUDIO_MS/2) + (RTCP_INTERVAL_AUDIO_MS*random/1000);
}else
{
WebRtc_UWord32 minIntervalMs = RTCP_INTERVAL_AUDIO_MS;
if(_sending)
{
// calc bw for video 360/sendBW in kbit/s
WebRtc_UWord32 sendBitrateKbit = 0;
WebRtc_UWord32 videoRate = 0;
WebRtc_UWord32 fecRate = 0;
WebRtc_UWord32 nackRate = 0;
_rtpRtcp.BitrateSent(&sendBitrateKbit,
&videoRate,
&fecRate,
&nackRate);
sendBitrateKbit /= 1000;
if(sendBitrateKbit != 0)
{
minIntervalMs = 360000/sendBitrateKbit;
}
}
if(minIntervalMs > RTCP_INTERVAL_VIDEO_MS)
{
minIntervalMs = RTCP_INTERVAL_VIDEO_MS;
}
timeToNext = (minIntervalMs/2) + (minIntervalMs*random/1000);
}
_nextTimeToSendRTCP = _clock.GetTimeInMS() + timeToNext;
}
// if the data does not fitt in the packet we fill it as much as possible
WebRtc_Word32 buildVal = 0;
if(rtcpPacketTypeFlags & kRtcpSr)
{
if(hasReceived)
{
buildVal = BuildSR(rtcpbuffer, pos, NTPsec, NTPfrac, &received);
} else
{
buildVal = BuildSR(rtcpbuffer, pos, NTPsec, NTPfrac);
}
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
buildVal = BuildSDEC(rtcpbuffer, pos);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}else if(rtcpPacketTypeFlags & kRtcpRr)
{
if(hasReceived)
{
buildVal = BuildRR(rtcpbuffer, pos, NTPsec, NTPfrac,&received);
}else
{
buildVal = BuildRR(rtcpbuffer, pos, NTPsec, NTPfrac);
}
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
// only of set
if(_CNAME[0] != 0)
{
buildVal = BuildSDEC(rtcpbuffer, pos);
if(buildVal == -1)
{
return -1; // error
}
}
}
if(rtcpPacketTypeFlags & kRtcpTransmissionTimeOffset)
{
// If present, this RTCP packet must be placed after a
// receiver report.
buildVal = BuildExtendedJitterReport(rtcpbuffer,
pos,
jitterTransmissionOffset);
if(buildVal == -1)
{
return -1; // error
}
else if(buildVal == -2)
{
break; // out of buffer
}
}
if(rtcpPacketTypeFlags & kRtcpPli)
{
buildVal = BuildPLI(rtcpbuffer, pos);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}
if(rtcpPacketTypeFlags & kRtcpFir)
{
buildVal = BuildFIR(rtcpbuffer, pos, RTT);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}
if(rtcpPacketTypeFlags & kRtcpSli)
{
buildVal = BuildSLI(rtcpbuffer, pos, (WebRtc_UWord8)pictureID);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}
if(rtcpPacketTypeFlags & kRtcpRpsi)
{
const WebRtc_Word8 payloadType = _rtpRtcp.SendPayloadType();
if(payloadType == -1)
{
return -1;
}
buildVal = BuildRPSI(rtcpbuffer, pos, pictureID, (WebRtc_UWord8)payloadType);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}
if(rtcpPacketTypeFlags & kRtcpRemb)
{
buildVal = BuildREMB(rtcpbuffer, pos);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}
if(rtcpPacketTypeFlags & kRtcpBye)
{
buildVal = BuildBYE(rtcpbuffer, pos);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}
if(rtcpPacketTypeFlags & kRtcpApp)
{
buildVal = BuildAPP(rtcpbuffer, pos);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}
if(rtcpPacketTypeFlags & kRtcpTmmbr)
{
buildVal = BuildTMMBR(rtcpbuffer, pos);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}
if(rtcpPacketTypeFlags & kRtcpTmmbn)
{
buildVal = BuildTMMBN(rtcpbuffer, pos);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}
if(rtcpPacketTypeFlags & kRtcpNack)
{
buildVal = BuildNACK(rtcpbuffer, pos, nackSize, nackList);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}
if(rtcpPacketTypeFlags & kRtcpXrVoipMetric)
{
buildVal = BuildVoIPMetric(rtcpbuffer, pos);
if(buildVal == -1)
{
return -1; // error
}else if(buildVal == -2)
{
break; // out of buffer
}
}
}while (false);
// Sanity don't send empty packets.
if (pos == 0)
{
return -1;
}
return SendToNetwork(rtcpbuffer, (WebRtc_UWord16)pos);
}
WebRtc_Word32
RTCPSender::SendToNetwork(const WebRtc_UWord8* dataBuffer,
const WebRtc_UWord16 length)
{
CriticalSectionScoped lock(_criticalSectionTransport);
if(_cbTransport)
{
if(_cbTransport->SendRTCPPacket(_id, dataBuffer, length) > 0)
{
return 0;
}
}
return -1;
}
WebRtc_Word32
RTCPSender::SetCSRCStatus(const bool include)
{
_includeCSRCs = include;
return 0;
}
WebRtc_Word32
RTCPSender::SetCSRCs(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize],
const WebRtc_UWord8 arrLength)
{
if(arrLength > kRtpCsrcSize)
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__);
assert(false);
return -1;
}
CriticalSectionScoped lock(_criticalSectionRTCPSender);
for(int i = 0; i < arrLength;i++)
{
_CSRC[i] = arrOfCSRC[i];
}
_CSRCs = arrLength;
return 0;
}
WebRtc_Word32
RTCPSender::SetApplicationSpecificData(const WebRtc_UWord8 subType,
const WebRtc_UWord32 name,
const WebRtc_UWord8* data,
const WebRtc_UWord16 length)
{
if(length %4 != 0)
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__);
return -1;
}
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(_appData)
{
delete [] _appData;
}
_appSend = true;
_appSubType = subType;
_appName = name;
_appData = new WebRtc_UWord8[length];
_appLength = length;
memcpy(_appData, data, length);
return 0;
}
WebRtc_Word32
RTCPSender::SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
memcpy(&_xrVoIPMetric, VoIPMetric, sizeof(RTCPVoIPMetric));
_xrSendVoIPMetric = true;
return 0;
}
// called under critsect _criticalSectionRTCPSender
WebRtc_Word32 RTCPSender::AddReportBlocks(WebRtc_UWord8* rtcpbuffer,
WebRtc_UWord32& pos,
WebRtc_UWord8& numberOfReportBlocks,
const RTCPReportBlock* received,
const WebRtc_UWord32 NTPsec,
const WebRtc_UWord32 NTPfrac) {
// sanity one block
if(pos + 24 >= IP_PACKET_SIZE) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s invalid argument", __FUNCTION__);
return -1;
}
numberOfReportBlocks = _reportBlocks.size();
if (received) {
// add our multiple RR to numberOfReportBlocks
numberOfReportBlocks++;
}
if (received) {
// answer to the one that sends to me
_lastRTCPTime[0] = ModuleRTPUtility::ConvertNTPTimeToMS(NTPsec, NTPfrac);
// Remote SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC);
pos += 4;
// fraction lost
rtcpbuffer[pos++]=received->fractionLost;
// cumulative loss
ModuleRTPUtility::AssignUWord24ToBuffer(rtcpbuffer+pos,
received->cumulativeLost);
pos += 3;
// extended highest seq_no, contain the highest sequence number received
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos,
received->extendedHighSeqNum);
pos += 4;
//Jitter
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, received->jitter);
pos += 4;
// Last SR timestamp, our NTP time when we received the last report
// This is the value that we read from the send report packet not when we
// received it...
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, received->lastSR);
pos += 4;
// Delay since last received report,time since we received the report
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos,
received->delaySinceLastSR);
pos += 4;
}
if ((pos + _reportBlocks.size() * 24) >= IP_PACKET_SIZE) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s invalid argument", __FUNCTION__);
return -1;
}
std::map<WebRtc_UWord32, RTCPReportBlock*>::iterator it =
_reportBlocks.begin();
for (; it != _reportBlocks.end(); it++) {
// we can have multiple report block in a conference
WebRtc_UWord32 remoteSSRC = it->first;
RTCPReportBlock* reportBlock = it->second;
if (reportBlock) {
// Remote SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, remoteSSRC);
pos += 4;
// fraction lost
rtcpbuffer[pos++] = reportBlock->fractionLost;
// cumulative loss
ModuleRTPUtility::AssignUWord24ToBuffer(rtcpbuffer+pos,
reportBlock->cumulativeLost);
pos += 3;
// extended highest seq_no, contain the highest sequence number received
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos,
reportBlock->extendedHighSeqNum);
pos += 4;
//Jitter
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos,
reportBlock->jitter);
pos += 4;
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos,
reportBlock->lastSR);
pos += 4;
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos,
reportBlock->delaySinceLastSR);
pos += 4;
}
}
return pos;
}
// no callbacks allowed inside this function
WebRtc_Word32
RTCPSender::SetTMMBN(const TMMBRSet* boundingSet,
const WebRtc_UWord32 maxBitrateKbit)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if (0 == _tmmbrHelp.SetTMMBRBoundingSetToSend(boundingSet, maxBitrateKbit))
{
_sendTMMBN = true;
return 0;
}
return -1;
}
WebRtc_Word32
RTCPSender::RequestTMMBR(WebRtc_UWord32 estimatedBW, WebRtc_UWord32 packetOH)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(_TMMBR)
{
_tmmbr_Send = estimatedBW;
_packetOH_Send = packetOH;
return 0;
}
return -1;
}
RateControlRegion
RTCPSender::UpdateOverUseState(const RateControlInput& rateControlInput, bool& firstOverUse)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return _remoteRateControl.Update(rateControlInput, firstOverUse,
_clock.GetTimeInMS());
}
} // namespace webrtc