stefan@webrtc.org 20ed36dada Break out RtpClock to system_wrappers and make it more generic.
The goal with this new clock interface is to have something which is used all
over WebRTC to make it easier to switch clock implementation depending on where
the components are used. This is a first step in that direction.

Next steps will be to, step by step, move all modules, video engine and voice
engine over to the new interface, effectively deprecating the old clock
interfaces. Long-term my vision is that we should be able to deprecate the clock
of WebRTC and rely on the user providing the implementation.

TEST=vie_auto_test, rtp_rtcp_unittests, trybots

Review URL: https://webrtc-codereview.appspot.com/1041004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3381 4adac7df-926f-26a2-2b94-8c16560cd09d
2013-01-17 14:01:20 +00:00

2117 lines
62 KiB
C++

/*
* 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 <cassert> // assert
#include <cstdlib> // rand
#include <string.h> // memcpy
#include "common_types.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_impl.h"
#include "system_wrappers/interface/critical_section_wrapper.h"
#include "system_wrappers/interface/trace.h"
namespace webrtc {
using RTCPUtility::RTCPCnameInformation;
RTCPSender::RTCPSender(const WebRtc_Word32 id,
const bool audio,
Clock* 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),
start_timestamp_(0),
last_rtp_timestamp_(0),
last_frame_capture_time_ms_(-1),
_SSRC(0),
_remoteSSRC(0),
_CNAME(),
_reportBlocks(),
_csrcCNAMEs(),
_cameraDelayMS(0),
_lastSendReport(),
_lastRTCPTime(),
_CSRCs(0),
_CSRC(),
_includeCSRCs(true),
_sequenceNumberFIR(0),
_lengthRembSSRC(0),
_sizeRembSSRC(0),
_rembSSRC(NULL),
_rembBitrate(0),
_tmmbrHelp(),
_tmmbr_Send(0),
_packetOH_Send(0),
_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;
last_rtp_timestamp_ = 0;
last_frame_capture_time_ms_ = -1;
start_timestamp_ = -1;
_SSRC = 0;
_remoteSSRC = 0;
_cameraDelayMS = 0;
_sequenceNumberFIR = 0;
_tmmbr_Send = 0;
_packetOH_Send = 0;
_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.TimeInMilliseconds() +
(RTCP_INTERVAL_AUDIO_MS/2);
} else
{
_nextTimeToSendRTCP = _clock.TimeInMilliseconds() +
(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::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::SetStartTimestamp(uint32_t start_timestamp) {
start_timestamp_ = start_timestamp;
}
void RTCPSender::SetLastRtpTime(uint32_t rtp_timestamp,
int64_t capture_time_ms) {
last_rtp_timestamp_ = rtp_timestamp;
if (capture_time_ms < 0) {
// We don't currently get a capture time from VoiceEngine.
last_frame_capture_time_ms_ = _clock.TimeInMilliseconds();
} else {
last_frame_capture_time_ms_ = capture_time_ms;
}
}
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.TimeInMilliseconds() + 100;
}
_SSRC = ssrc;
}
WebRtc_Word32
RTCPSender::SetRemoteSSRC( const WebRtc_UWord32 ssrc)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_remoteSSRC = ssrc;
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]) {
if (!cName)
return -1;
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_Word64 now = _clock.TimeInMilliseconds();
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;
}
std::map<WebRtc_UWord32, RTCPReportBlock*>::iterator it =
_reportBlocks.find(SSRC);
if (it != _reportBlocks.end()) {
delete it->second;
_reportBlocks.erase(it);
}
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 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);
_lastSendReport[0] = (NTPsec << 16) + (NTPfrac >> 16);
WebRtc_UWord32 freqHz = 90000; // For video
if(_audio) {
freqHz = _rtpRtcp.CurrentSendFrequencyHz();
}
// The timestamp of this RTCP packet should be estimated as the timestamp of
// the frame being captured at this moment. We are calculating that
// timestamp as the last frame's timestamp + the time since the last frame
// was captured.
RTPtime = start_timestamp_ + last_rtp_timestamp_ + (
_clock.TimeInMilliseconds() - last_frame_capture_time_ms_) *
(freqHz / 1000);
// 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, NTPsec);
pos += 4;
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, NTPfrac);
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,
bool repeat) {
// sanity
if(pos + 20 >= IP_PACKET_SIZE) {
return -2;
}
if (!repeat) {
_sequenceNumberFIR++; // do not increase if repetition
}
// 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;
}
void
RTCPSender::SetTargetBitrate(unsigned int target_bitrate)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_tmmbr_Send = target_bitrate / 1000;
}
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;
// store in candidateSet, allocates one extra slot
TMMBRSet* candidateSet = _tmmbrHelp.CandidateSet();
// holding _criticalSectionRTCPSender while calling RTCPreceiver which
// will accuire _criticalSectionRTCPReceiver is a potental deadlock but
// since RTCPreceiver is not doing the reverse we should be fine
WebRtc_Word32 lengthOfBoundingSet
= _rtpRtcp.BoundingSet(tmmbrOwner, candidateSet);
if(lengthOfBoundingSet > 0)
{
for (WebRtc_Word32 i = 0; i < lengthOfBoundingSet; i++)
{
if( candidateSet->Tmmbr(i) == _tmmbr_Send &&
candidateSet->PacketOH(i) == _packetOH_Send)
{
// do not send the same tuple
return 0;
}
}
if(!tmmbrOwner)
{
// use received bounding set as candidate set
// add current tuple
candidateSet->SetEntry(lengthOfBoundingSet,
_tmmbr_Send,
_packetOH_Send,
_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->Tmmbr(n) > 0)
{
WebRtc_UWord32 tmmbrSSRC = boundingSet->Ssrc(n);
ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, tmmbrSSRC);
pos += 4;
WebRtc_UWord32 bitRate = boundingSet->Tmmbr(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->PacketOH(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 bool repeat, // 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.TimeInMilliseconds() + 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, repeat);
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;
}
} // namespace webrtc