2003 lines
58 KiB
C++
2003 lines
58 KiB
C++
/*
|
|
* Copyright (c) 2011 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 "rtcp_utility.h"
|
|
|
|
#include <string.h> // memcpy
|
|
#include <cassert> // assert
|
|
#include <cstdlib> // rand
|
|
|
|
#include "trace.h"
|
|
#include "tick_util.h"
|
|
#include "common_types.h"
|
|
#include "critical_section_wrapper.h"
|
|
|
|
namespace webrtc {
|
|
RTCPSender::RTCPSender(const WebRtc_Word32 id,
|
|
const bool audio,
|
|
ModuleRtpRtcpPrivate& callback) :
|
|
_id(id),
|
|
_audio(audio),
|
|
_method(kRtcpOff),
|
|
_cbRtcpPrivate(callback),
|
|
_criticalSectionTransport(*CriticalSectionWrapper::CreateCriticalSection()),
|
|
_cbTransport(NULL),
|
|
|
|
_criticalSectionRTCPSender(*CriticalSectionWrapper::CreateCriticalSection()),
|
|
_usingNack(false),
|
|
_sending(false),
|
|
_sendTMMBN(false),
|
|
_TMMBR(false),
|
|
_nextTimeToSendRTCP(0),
|
|
_SSRC(0),
|
|
_remoteSSRC(0),
|
|
_CNAME(),
|
|
_reportBlocks(),
|
|
_csrcCNAMEs(),
|
|
|
|
_cameraDelayMS(0),
|
|
|
|
_lastSendReport(),
|
|
_lastRTCPTime(),
|
|
|
|
_CSRCs(0),
|
|
_CSRC(),
|
|
_includeCSRCs(true),
|
|
|
|
_sequenceNumberFIR(0),
|
|
_lastTimeFIR(0),
|
|
|
|
_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()
|
|
{
|
|
if(_appData)
|
|
{
|
|
delete [] _appData;
|
|
}
|
|
|
|
MapItem* item = _reportBlocks.First();
|
|
while(item)
|
|
{
|
|
RTCPReportBlock* ptr = (RTCPReportBlock*)(item->GetItem());
|
|
if(ptr)
|
|
{
|
|
delete ptr;
|
|
}
|
|
_reportBlocks.Erase(item);
|
|
item = _reportBlocks.First();
|
|
}
|
|
item = _csrcCNAMEs.First();
|
|
while(item)
|
|
{
|
|
RTCPUtility::RTCPCnameInformation* ptr = (RTCPUtility::RTCPCnameInformation*)(item->GetItem());
|
|
if(ptr)
|
|
{
|
|
delete ptr;
|
|
}
|
|
_csrcCNAMEs.Erase(item);
|
|
item = _csrcCNAMEs.First();
|
|
}
|
|
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;
|
|
_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 = ModuleRTPUtility::GetTimeInMS() + (RTCP_INTERVAL_AUDIO_MS/2);
|
|
} else
|
|
{
|
|
_nextTimeToSendRTCP = ModuleRTPUtility::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::TMMBR() const
|
|
{
|
|
CriticalSectionScoped lock(_criticalSectionRTCPSender);
|
|
return _TMMBR;
|
|
}
|
|
|
|
WebRtc_Word32
|
|
RTCPSender::SetTMMBRStatus(const bool enable)
|
|
{
|
|
CriticalSectionScoped lock(_criticalSectionRTCPSender);
|
|
_TMMBR = 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
|
|
_nextTimeToSendRTCP = ModuleRTPUtility::GetTimeInMS() + 100; // make sure that we send a RTP packet
|
|
}
|
|
_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(WebRtc_Word8 cName[RTCP_CNAME_SIZE])
|
|
{
|
|
if(cName == NULL)
|
|
{
|
|
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
CriticalSectionScoped lock(_criticalSectionRTCPSender);
|
|
memcpy(cName, _CNAME, RTCP_CNAME_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
WebRtc_Word32
|
|
RTCPSender::SetCNAME(const WebRtc_Word8 cName[RTCP_CNAME_SIZE])
|
|
{
|
|
if(cName == NULL)
|
|
{
|
|
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
WebRtc_Word32 length = (WebRtc_Word32)strlen(cName);
|
|
if(length > RTCP_CNAME_SIZE)
|
|
{
|
|
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument, too long cName", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
CriticalSectionScoped lock(_criticalSectionRTCPSender);
|
|
|
|
memcpy(_CNAME, cName, length+1);
|
|
return 0;
|
|
}
|
|
|
|
WebRtc_Word32
|
|
RTCPSender::AddMixedCNAME(const WebRtc_UWord32 SSRC,
|
|
const WebRtc_Word8 cName[RTCP_CNAME_SIZE])
|
|
{
|
|
if(cName == NULL)
|
|
{
|
|
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
WebRtc_Word32 length = (WebRtc_Word32)strlen(cName);
|
|
if(length > RTCP_CNAME_SIZE)
|
|
{
|
|
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument, too long cName", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
CriticalSectionScoped lock(_criticalSectionRTCPSender);
|
|
if(_csrcCNAMEs.Size() == kRtpCsrcSize)
|
|
{
|
|
return -1;
|
|
}
|
|
RTCPUtility::RTCPCnameInformation* ptr= new RTCPUtility::RTCPCnameInformation();
|
|
|
|
memcpy(ptr->name, cName, length+1);
|
|
ptr->length = (WebRtc_UWord8)length;
|
|
_csrcCNAMEs.Insert(SSRC, ptr);
|
|
return 0;
|
|
}
|
|
|
|
WebRtc_Word32
|
|
RTCPSender::RemoveMixedCNAME(const WebRtc_UWord32 SSRC)
|
|
{
|
|
CriticalSectionScoped lock(_criticalSectionRTCPSender);
|
|
MapItem* item= _csrcCNAMEs.Find(SSRC);
|
|
if(item)
|
|
{
|
|
RTCPUtility::RTCPCnameInformation* ptr= (RTCPUtility::RTCPCnameInformation*)(item->GetItem());
|
|
if(ptr)
|
|
{
|
|
delete ptr;
|
|
}
|
|
_csrcCNAMEs.Erase(item);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
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
|
|
*/
|
|
|
|
if(_method == kRtcpOff)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS();
|
|
|
|
CriticalSectionScoped lock(_criticalSectionRTCPSender);
|
|
|
|
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.Insert(SSRC, copyReportBlock);
|
|
return 0;
|
|
}
|
|
|
|
WebRtc_Word32
|
|
RTCPSender::RemoveReportBlock(const WebRtc_UWord32 SSRC)
|
|
{
|
|
CriticalSectionScoped lock(_criticalSectionRTCPSender);
|
|
|
|
MapItem* item= _reportBlocks.Find(SSRC);
|
|
if(item)
|
|
{
|
|
RTCPReportBlock* ptr= (RTCPReportBlock*)(item->GetItem());
|
|
if(ptr)
|
|
{
|
|
delete ptr;
|
|
}
|
|
_reportBlocks.Erase(item);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
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 = _cbRtcpPrivate.CurrentSendFrequencyHz();
|
|
RTPtime = ModuleRTPUtility::CurrentRTP(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, _cbRtcpPrivate.PacketCountSent());
|
|
pos += 4;
|
|
|
|
//sender's octet count
|
|
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _cbRtcpPrivate.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)
|
|
{
|
|
WebRtc_UWord32 lengthCname =(WebRtc_UWord32)strlen((char*)_CNAME);
|
|
|
|
// sanity max is 255
|
|
if(lengthCname > RTCP_CNAME_SIZE)
|
|
{
|
|
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++]=(WebRtc_UWord8)0x80 + 1 + _csrcCNAMEs.Size(); // source counts
|
|
rtcpbuffer[pos++]=(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++]=(WebRtc_UWord8)1;
|
|
|
|
//
|
|
rtcpbuffer[pos++]=(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;
|
|
|
|
MapItem* item = _csrcCNAMEs.First();
|
|
|
|
for(int i = 0; item && i < _csrcCNAMEs.Size(); i++)
|
|
{
|
|
RTCPUtility::RTCPCnameInformation* cname = (RTCPUtility::RTCPCnameInformation*)(item->GetItem());
|
|
WebRtc_UWord32 SSRC = item->GetUnsignedId();
|
|
|
|
// Add SSRC
|
|
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, SSRC);
|
|
pos += 4;
|
|
|
|
// CNAME = 1
|
|
rtcpbuffer[pos++]=(WebRtc_UWord8)1;
|
|
|
|
rtcpbuffer[pos++]= cname->length;
|
|
|
|
SDESLength += 6;
|
|
|
|
memcpy(&rtcpbuffer[pos],cname->name, cname->length);
|
|
pos += cname->length;
|
|
SDESLength += cname->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;
|
|
|
|
item = _csrcCNAMEs.Next(item);
|
|
}
|
|
WebRtc_UWord16 length = SDESLength;
|
|
length= (length/4) - 1; // in 32-bit words minus one and we dont count the header
|
|
|
|
ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+SDESLengthPos, 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;
|
|
}
|
|
|
|
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 = ModuleRTPUtility::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 = ModuleRTPUtility::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::BuildTMMBR(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos, WebRtc_UWord32 RTT)
|
|
{
|
|
// 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
|
|
|
|
// About to send TMMBR, first run remote rate control
|
|
// to get a target bit rate.
|
|
_tmmbr_Send = _remoteRateControl.TargetBitRate(RTT) / 1000;
|
|
|
|
// 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 = _cbRtcpPrivate.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];
|
|
|
|
if(_method == kRtcpOff)
|
|
{
|
|
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s invalid state", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
|
|
if( _method == kRtcpCompound ||
|
|
rtcpPacketTypeFlags & kRtcpReport ||
|
|
rtcpPacketTypeFlags & kRtcpSr ||
|
|
rtcpPacketTypeFlags & kRtcpRr)
|
|
{
|
|
// get statistics from our RTPreceiver outside critsect
|
|
if(_cbRtcpPrivate.ReportBlockStatistics(&received.fractionLost,
|
|
&received.cumulativeLost,
|
|
&received.extendedHighSeqNum,
|
|
&received.jitter) == 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
|
|
_cbRtcpPrivate.LastReceivedNTP(lastReceivedRRNTPsecs, lastReceivedRRNTPfrac, remoteSR);
|
|
|
|
// get our NTP as late as possible to avoid a race
|
|
ModuleRTPUtility::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
|
|
ModuleRTPUtility::CurrentNTP(NTPsec, NTPfrac);
|
|
}
|
|
}
|
|
|
|
CriticalSectionScoped lock(_criticalSectionRTCPSender);
|
|
|
|
if(_TMMBR ) // attach TMMBR to send and receive reports
|
|
{
|
|
rtcpPacketTypeFlags |= kRtcpTmmbr;
|
|
}
|
|
if(_appSend)
|
|
{
|
|
rtcpPacketTypeFlags |= kRtcpApp;
|
|
_appSend = false;
|
|
}
|
|
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;
|
|
}
|
|
} 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_Word32 sendBitrateKbit = _cbRtcpPrivate.BitrateSent()/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 = ModuleRTPUtility::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 & 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 = _cbRtcpPrivate.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 & 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, RTT);
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
MapItem* item = _reportBlocks.First();
|
|
for(int i = 0; i < _reportBlocks.Size() && item; i++)
|
|
{
|
|
// we can have multiple report block in a conference
|
|
WebRtc_UWord32 remoteSSRC = item->GetId();
|
|
RTCPReportBlock* reportBlock = (RTCPReportBlock*)item->GetItem();
|
|
if(reportBlock)
|
|
{
|
|
// Remote SSRC
|
|
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, remoteSSRC);
|
|
pos += 4;
|
|
|
|
// fraction lost
|
|
rtcpbuffer[pos++]=(WebRtc_UWord8)(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;
|
|
}
|
|
item = _reportBlocks.Next(item);
|
|
}
|
|
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);
|
|
}
|
|
} // namespace webrtc
|