pbos@webrtc.org 2f4b14e3f3 Make RTCP sender report send media bytes.
r6654 changed RtpSender::Bytes() to return the number of bytes sent
instead of number of media bytes. This is used by VideoEngine for stats.
This change broke RTCP which sends this same count as the number of
payload bytes sent (excluding headers and padding).

BUG=
R=stefan@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6691 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-07-15 15:25:39 +00:00

2176 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 "webrtc/modules/rtp_rtcp/source/rtcp_sender.h"
#include <assert.h> // assert
#include <stdlib.h> // rand
#include <string.h> // memcpy
#include <algorithm> // min
#include "webrtc/common_types.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/trace_event.h"
namespace webrtc {
using RTCPUtility::RTCPCnameInformation;
NACKStringBuilder::NACKStringBuilder() :
_stream(""), _count(0), _consecutive(false)
{
// Empty.
}
NACKStringBuilder::~NACKStringBuilder() {}
void NACKStringBuilder::PushNACK(uint16_t nack)
{
if (_count == 0)
{
_stream << nack;
} else if (nack == _prevNack + 1)
{
_consecutive = true;
} else
{
if (_consecutive)
{
_stream << "-" << _prevNack;
_consecutive = false;
}
_stream << "," << nack;
}
_count++;
_prevNack = nack;
}
std::string NACKStringBuilder::GetResult()
{
if (_consecutive)
{
_stream << "-" << _prevNack;
_consecutive = false;
}
return _stream.str();
}
RTCPSender::FeedbackState::FeedbackState()
: send_payload_type(0),
frequency_hz(0),
packets_sent(0),
media_bytes_sent(0),
send_bitrate(0),
last_rr_ntp_secs(0),
last_rr_ntp_frac(0),
remote_sr(0),
has_last_xr_rr(false) {}
RTCPSender::RTCPSender(const int32_t id,
const bool audio,
Clock* clock,
ReceiveStatistics* receive_statistics) :
_id(id),
_audio(audio),
_clock(clock),
_method(kRtcpOff),
_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(),
receive_statistics_(receive_statistics),
internal_report_blocks_(),
external_report_blocks_(),
_csrcCNAMEs(),
_cameraDelayMS(0),
_lastSendReport(),
_lastRTCPTime(),
last_xr_rr_(),
_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),
xrSendReceiverReferenceTimeEnabled_(false),
_xrSendVoIPMetric(false),
_xrVoIPMetric()
{
memset(_CNAME, 0, sizeof(_CNAME));
memset(_lastSendReport, 0, sizeof(_lastSendReport));
memset(_lastRTCPTime, 0, sizeof(_lastRTCPTime));
}
RTCPSender::~RTCPSender() {
delete [] _rembSSRC;
delete [] _appData;
while (!internal_report_blocks_.empty()) {
delete internal_report_blocks_.begin()->second;
internal_report_blocks_.erase(internal_report_blocks_.begin());
}
while (!external_report_blocks_.empty()) {
std::map<uint32_t, RTCPReportBlock*>::iterator it =
external_report_blocks_.begin();
delete it->second;
external_report_blocks_.erase(it);
}
while (!_csrcCNAMEs.empty()) {
std::map<uint32_t, RTCPCnameInformation*>::iterator it =
_csrcCNAMEs.begin();
delete it->second;
_csrcCNAMEs.erase(it);
}
delete _criticalSectionTransport;
delete _criticalSectionRTCPSender;
}
int32_t
RTCPSender::RegisterSendTransport(Transport* outgoingTransport)
{
CriticalSectionScoped lock(_criticalSectionTransport);
_cbTransport = outgoingTransport;
return 0;
}
RTCPMethod
RTCPSender::Status() const
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return _method;
}
int32_t
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;
}
int32_t
RTCPSender::SetSendingStatus(const FeedbackState& feedback_state, 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(feedback_state, kRtcpBye);
}
return 0;
}
bool
RTCPSender::REMB() const
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return _REMB;
}
int32_t
RTCPSender::SetREMBStatus(const bool enable)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_REMB = enable;
return 0;
}
int32_t
RTCPSender::SetREMBData(const uint32_t bitrate,
const uint8_t numberOfSSRC,
const uint32_t* SSRC)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_rembBitrate = bitrate;
if(_sizeRembSSRC < numberOfSSRC)
{
delete [] _rembSSRC;
_rembSSRC = new uint32_t[numberOfSSRC];
_sizeRembSSRC = numberOfSSRC;
}
_lengthRembSSRC = numberOfSSRC;
for (int i = 0; i < numberOfSSRC; i++)
{
_rembSSRC[i] = SSRC[i];
}
_sendREMB = true;
// Send a REMB immediately if we have a new REMB. The frequency of REMBs is
// throttled by the caller.
_nextTimeToSendRTCP = _clock->TimeInMilliseconds();
return 0;
}
bool
RTCPSender::TMMBR() const
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return _TMMBR;
}
int32_t
RTCPSender::SetTMMBRStatus(const bool enable)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_TMMBR = enable;
return 0;
}
bool
RTCPSender::IJ() const
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return _IJ;
}
int32_t
RTCPSender::SetIJStatus(const bool enable)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_IJ = enable;
return 0;
}
void RTCPSender::SetStartTimestamp(uint32_t start_timestamp) {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
start_timestamp_ = start_timestamp;
}
void RTCPSender::SetLastRtpTime(uint32_t rtp_timestamp,
int64_t capture_time_ms) {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
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 uint32_t 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;
}
void RTCPSender::SetRemoteSSRC(uint32_t ssrc)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_remoteSSRC = ssrc;
}
int32_t
RTCPSender::SetCameraDelay(const int32_t delayMS)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(delayMS > 1000 || delayMS < -1000)
{
LOG(LS_WARNING) << "Delay can't be larger than 1 second: "
<< delayMS << " ms";
return -1;
}
_cameraDelayMS = delayMS;
return 0;
}
int32_t 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;
}
int32_t RTCPSender::AddMixedCNAME(const uint32_t 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;
}
int32_t RTCPSender::RemoveMixedCNAME(const uint32_t SSRC) {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
std::map<uint32_t, 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 extremely rare
From RFC 3550
MAX RTCP BW is 5% if the session BW
A send report is approximately 65 bytes inc CNAME
A receiver 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
*/
int64_t 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;
}
uint32_t
RTCPSender::LastSendReport( uint32_t& lastRTCPTime)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
lastRTCPTime = _lastRTCPTime[0];
return _lastSendReport[0];
}
uint32_t
RTCPSender::SendTimeOfSendReport(const uint32_t 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;
}
bool RTCPSender::SendTimeOfXrRrReport(uint32_t mid_ntp,
int64_t* time_ms) const {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if (last_xr_rr_.empty()) {
return false;
}
std::map<uint32_t, int64_t>::const_iterator it = last_xr_rr_.find(mid_ntp);
if (it == last_xr_rr_.end()) {
return false;
}
*time_ms = it->second;
return true;
}
void RTCPSender::GetPacketTypeCounter(
RtcpPacketTypeCounter* packet_counter) const {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
*packet_counter = packet_type_counter_;
}
int32_t RTCPSender::AddExternalReportBlock(
uint32_t SSRC,
const RTCPReportBlock* reportBlock) {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return AddReportBlock(SSRC, &external_report_blocks_, reportBlock);
}
int32_t RTCPSender::AddReportBlock(
uint32_t SSRC,
std::map<uint32_t, RTCPReportBlock*>* report_blocks,
const RTCPReportBlock* reportBlock) {
assert(reportBlock);
if (report_blocks->size() >= RTCP_MAX_REPORT_BLOCKS) {
LOG(LS_WARNING) << "Too many report blocks.";
return -1;
}
std::map<uint32_t, RTCPReportBlock*>::iterator it =
report_blocks->find(SSRC);
if (it != report_blocks->end()) {
delete it->second;
report_blocks->erase(it);
}
RTCPReportBlock* copyReportBlock = new RTCPReportBlock();
memcpy(copyReportBlock, reportBlock, sizeof(RTCPReportBlock));
(*report_blocks)[SSRC] = copyReportBlock;
return 0;
}
int32_t RTCPSender::RemoveExternalReportBlock(uint32_t SSRC) {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
std::map<uint32_t, RTCPReportBlock*>::iterator it =
external_report_blocks_.find(SSRC);
if (it == external_report_blocks_.end()) {
return -1;
}
delete it->second;
external_report_blocks_.erase(it);
return 0;
}
int32_t RTCPSender::BuildSR(const FeedbackState& feedback_state,
uint8_t* rtcpbuffer,
int& pos,
uint32_t NTPsec,
uint32_t NTPfrac)
{
// sanity
if(pos + 52 >= IP_PACKET_SIZE)
{
LOG(LS_WARNING) << "Failed to build Sender Report.";
return -2;
}
uint32_t RTPtime;
uint32_t posNumberOfReportBlocks = pos;
rtcpbuffer[pos++]=(uint8_t)0x80;
// Sender report
rtcpbuffer[pos++]=(uint8_t)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] = Clock::NtpToMs(NTPsec, NTPfrac);
_lastSendReport[0] = (NTPsec << 16) + (NTPfrac >> 16);
// 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_) *
(feedback_state.frequency_hz / 1000);
// Add sender data
// Save for our length field
pos++;
pos++;
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// NTP
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, NTPsec);
pos += 4;
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, NTPfrac);
pos += 4;
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, RTPtime);
pos += 4;
//sender's packet count
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos,
feedback_state.packets_sent);
pos += 4;
//sender's octet count
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos,
feedback_state.media_bytes_sent);
pos += 4;
uint8_t numberOfReportBlocks = 0;
int32_t retVal = WriteAllReportBlocksToBuffer(rtcpbuffer, pos,
numberOfReportBlocks,
NTPsec, NTPfrac);
if(retVal < 0)
{
//
return retVal ;
}
pos = retVal;
rtcpbuffer[posNumberOfReportBlocks] += numberOfReportBlocks;
uint16_t len = uint16_t((pos/4) -1);
RtpUtility::AssignUWord16ToBuffer(rtcpbuffer + 2, len);
return 0;
}
int32_t RTCPSender::BuildSDEC(uint8_t* rtcpbuffer, int& pos) {
size_t lengthCname = strlen(_CNAME);
assert(lengthCname < RTCP_CNAME_SIZE);
// sanity
if(pos + 12 + lengthCname >= IP_PACKET_SIZE) {
LOG(LS_WARNING) << "Failed to build SDEC.";
return -2;
}
// SDEC Source Description
// We always need to add SDES CNAME
rtcpbuffer[pos++] = static_cast<uint8_t>(0x80 + 1 + _csrcCNAMEs.size());
rtcpbuffer[pos++] = static_cast<uint8_t>(202);
// handle SDES length later on
uint32_t SDESLengthPos = pos;
pos++;
pos++;
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// CNAME = 1
rtcpbuffer[pos++] = static_cast<uint8_t>(1);
//
rtcpbuffer[pos++] = static_cast<uint8_t>(lengthCname);
uint16_t SDESLength = 10;
memcpy(&rtcpbuffer[pos], _CNAME, lengthCname);
pos += lengthCname;
SDESLength += (uint16_t)lengthCname;
uint16_t 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<uint32_t, RTCPUtility::RTCPCnameInformation*>::iterator it =
_csrcCNAMEs.begin();
for(; it != _csrcCNAMEs.end(); it++) {
RTCPCnameInformation* cname = it->second;
uint32_t SSRC = it->first;
// Add SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, SSRC);
pos += 4;
// CNAME = 1
rtcpbuffer[pos++] = static_cast<uint8_t>(1);
size_t length = strlen(cname->name);
assert(length < RTCP_CNAME_SIZE);
rtcpbuffer[pos++]= static_cast<uint8_t>(length);
SDESLength += 6;
memcpy(&rtcpbuffer[pos],cname->name, length);
pos += length;
SDESLength += length;
uint16_t 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
uint16_t buffer_length = (SDESLength / 4) - 1;
RtpUtility::AssignUWord16ToBuffer(rtcpbuffer + SDESLengthPos, buffer_length);
return 0;
}
int32_t
RTCPSender::BuildRR(uint8_t* rtcpbuffer,
int& pos,
const uint32_t NTPsec,
const uint32_t NTPfrac)
{
// sanity one block
if(pos + 32 >= IP_PACKET_SIZE)
{
return -2;
}
uint32_t posNumberOfReportBlocks = pos;
rtcpbuffer[pos++]=(uint8_t)0x80;
rtcpbuffer[pos++]=(uint8_t)201;
// Save for our length field
pos++;
pos++;
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
uint8_t numberOfReportBlocks = 0;
int retVal = WriteAllReportBlocksToBuffer(rtcpbuffer, pos,
numberOfReportBlocks,
NTPsec, NTPfrac);
if(retVal < 0)
{
return pos;
}
pos = retVal;
rtcpbuffer[posNumberOfReportBlocks] += numberOfReportBlocks;
uint16_t len = uint16_t((pos)/4 -1);
RtpUtility::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.
int32_t
RTCPSender::BuildExtendedJitterReport(
uint8_t* rtcpbuffer,
int& pos,
const uint32_t jitterTransmissionTimeOffset)
{
if (external_report_blocks_.size() > 0)
{
// TODO(andresp): Remove external report blocks since they are not
// supported.
LOG(LS_ERROR) << "Handling of external report blocks not implemented.";
return 0;
}
// sanity
if(pos + 8 >= IP_PACKET_SIZE)
{
return -2;
}
// add picture loss indicator
uint8_t RC = 1;
rtcpbuffer[pos++]=(uint8_t)0x80 + RC;
rtcpbuffer[pos++]=(uint8_t)195;
// Used fixed length of 2
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)(1);
// Add inter-arrival jitter
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos,
jitterTransmissionTimeOffset);
pos += 4;
return 0;
}
int32_t
RTCPSender::BuildPLI(uint8_t* rtcpbuffer, int& pos)
{
// sanity
if(pos + 12 >= IP_PACKET_SIZE)
{
return -2;
}
// add picture loss indicator
uint8_t FMT = 1;
rtcpbuffer[pos++]=(uint8_t)0x80 + FMT;
rtcpbuffer[pos++]=(uint8_t)206;
//Used fixed length of 2
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)(2);
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// Add the remote SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC);
pos += 4;
return 0;
}
int32_t RTCPSender::BuildFIR(uint8_t* rtcpbuffer,
int& 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
uint8_t FMT = 4;
rtcpbuffer[pos++] = (uint8_t)0x80 + FMT;
rtcpbuffer[pos++] = (uint8_t)206;
//Length of 4
rtcpbuffer[pos++] = (uint8_t)0;
rtcpbuffer[pos++] = (uint8_t)(4);
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// RFC 5104 4.3.1.2. Semantics
// SSRC of media source
rtcpbuffer[pos++] = (uint8_t)0;
rtcpbuffer[pos++] = (uint8_t)0;
rtcpbuffer[pos++] = (uint8_t)0;
rtcpbuffer[pos++] = (uint8_t)0;
// Additional Feedback Control Information (FCI)
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC);
pos += 4;
rtcpbuffer[pos++] = (uint8_t)(_sequenceNumberFIR);
rtcpbuffer[pos++] = (uint8_t)0;
rtcpbuffer[pos++] = (uint8_t)0;
rtcpbuffer[pos++] = (uint8_t)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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
int32_t
RTCPSender::BuildSLI(uint8_t* rtcpbuffer, int& pos, const uint8_t pictureID)
{
// sanity
if(pos + 16 >= IP_PACKET_SIZE)
{
return -2;
}
// add slice loss indicator
uint8_t FMT = 2;
rtcpbuffer[pos++]=(uint8_t)0x80 + FMT;
rtcpbuffer[pos++]=(uint8_t)206;
//Used fixed length of 3
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)(3);
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// Add the remote SSRC
RtpUtility::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
uint32_t sliField = (0x1fff << 6)+ (0x3f & pictureID);
RtpUtility::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
*/
int32_t
RTCPSender::BuildRPSI(uint8_t* rtcpbuffer,
int& pos,
const uint64_t pictureID,
const uint8_t payloadType)
{
// sanity
if(pos + 24 >= IP_PACKET_SIZE)
{
return -2;
}
// add Reference Picture Selection Indication
uint8_t FMT = 3;
rtcpbuffer[pos++]=(uint8_t)0x80 + FMT;
rtcpbuffer[pos++]=(uint8_t)206;
// calc length
uint32_t bitsRequired = 7;
uint8_t bytesRequired = 1;
while((pictureID>>bitsRequired) > 0)
{
bitsRequired += 7;
bytesRequired++;
}
uint8_t size = 3;
if(bytesRequired > 6)
{
size = 5;
} else if(bytesRequired > 2)
{
size = 4;
}
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=size;
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// Add the remote SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC);
pos += 4;
// calc padding length
uint8_t 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 | uint8_t(pictureID >> (i*7));
pos++;
}
// add last byte of picture ID
rtcpbuffer[pos] = uint8_t(pictureID & 0x7f);
pos++;
// add padding
for(int j = 0; j <paddingBytes; j++)
{
rtcpbuffer[pos] = 0;
pos++;
}
return 0;
}
int32_t
RTCPSender::BuildREMB(uint8_t* rtcpbuffer, int& pos)
{
// sanity
if(pos + 20 + 4 * _lengthRembSSRC >= IP_PACKET_SIZE)
{
return -2;
}
// add application layer feedback
uint8_t FMT = 15;
rtcpbuffer[pos++]=(uint8_t)0x80 + FMT;
rtcpbuffer[pos++]=(uint8_t)206;
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=_lengthRembSSRC + 4;
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// Remote SSRC must be 0
RtpUtility::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
uint8_t brExp = 0;
for(uint32_t i=0; i<64; i++)
{
if(_rembBitrate <= ((uint32_t)262143 << i))
{
brExp = i;
break;
}
}
const uint32_t brMantissa = (_rembBitrate >> brExp);
rtcpbuffer[pos++]=(uint8_t)((brExp << 2) + ((brMantissa >> 16) & 0x03));
rtcpbuffer[pos++]=(uint8_t)(brMantissa >> 8);
rtcpbuffer[pos++]=(uint8_t)(brMantissa);
for (int i = 0; i < _lengthRembSSRC; i++)
{
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _rembSSRC[i]);
pos += 4;
}
return 0;
}
void
RTCPSender::SetTargetBitrate(unsigned int target_bitrate)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_tmmbr_Send = target_bitrate / 1000;
}
int32_t RTCPSender::BuildTMMBR(ModuleRtpRtcpImpl* rtp_rtcp_module,
uint8_t* rtcpbuffer,
int& pos) {
if (rtp_rtcp_module == NULL)
return -1;
// 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
int32_t lengthOfBoundingSet =
rtp_rtcp_module->BoundingSet(tmmbrOwner, candidateSet);
if(lengthOfBoundingSet > 0)
{
for (int32_t 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
uint8_t FMT = 3;
rtcpbuffer[pos++]=(uint8_t)0x80 + FMT;
rtcpbuffer[pos++]=(uint8_t)205;
//Length of 4
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)(4);
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// RFC 5104 4.2.1.2. Semantics
// SSRC of media source
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)0;
// Additional Feedback Control Information (FCI)
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC);
pos += 4;
uint32_t bitRate = _tmmbr_Send*1000;
uint32_t mmbrExp = 0;
for(uint32_t i=0;i<64;i++)
{
if(bitRate <= ((uint32_t)131071 << i))
{
mmbrExp = i;
break;
}
}
uint32_t mmbrMantissa = (bitRate >> mmbrExp);
rtcpbuffer[pos++]=(uint8_t)((mmbrExp << 2) + ((mmbrMantissa >> 15) & 0x03));
rtcpbuffer[pos++]=(uint8_t)(mmbrMantissa >> 7);
rtcpbuffer[pos++]=(uint8_t)((mmbrMantissa << 1) + ((_packetOH_Send >> 8)& 0x01));
rtcpbuffer[pos++]=(uint8_t)(_packetOH_Send);
}
return 0;
}
int32_t
RTCPSender::BuildTMMBN(uint8_t* rtcpbuffer, int& pos)
{
TMMBRSet* boundingSet = _tmmbrHelp.BoundingSetToSend();
if(boundingSet == NULL)
{
return -1;
}
// sanity
if(pos + 12 + boundingSet->lengthOfSet()*8 >= IP_PACKET_SIZE)
{
LOG(LS_WARNING) << "Failed to build TMMBN.";
return -2;
}
uint8_t FMT = 4;
// add TMMBN indicator
rtcpbuffer[pos++]=(uint8_t)0x80 + FMT;
rtcpbuffer[pos++]=(uint8_t)205;
//Add length later
int posLength = pos;
pos++;
pos++;
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// RFC 5104 4.2.2.2. Semantics
// SSRC of media source
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)0;
// Additional Feedback Control Information (FCI)
int numBoundingSet = 0;
for(uint32_t n=0; n< boundingSet->lengthOfSet(); n++)
{
if (boundingSet->Tmmbr(n) > 0)
{
uint32_t tmmbrSSRC = boundingSet->Ssrc(n);
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, tmmbrSSRC);
pos += 4;
uint32_t bitRate = boundingSet->Tmmbr(n) * 1000;
uint32_t mmbrExp = 0;
for(int i=0; i<64; i++)
{
if(bitRate <= ((uint32_t)131071 << i))
{
mmbrExp = i;
break;
}
}
uint32_t mmbrMantissa = (bitRate >> mmbrExp);
uint32_t measuredOH = boundingSet->PacketOH(n);
rtcpbuffer[pos++]=(uint8_t)((mmbrExp << 2) + ((mmbrMantissa >> 15) & 0x03));
rtcpbuffer[pos++]=(uint8_t)(mmbrMantissa >> 7);
rtcpbuffer[pos++]=(uint8_t)((mmbrMantissa << 1) + ((measuredOH >> 8)& 0x01));
rtcpbuffer[pos++]=(uint8_t)(measuredOH);
numBoundingSet++;
}
}
uint16_t length= (uint16_t)(2+2*numBoundingSet);
rtcpbuffer[posLength++]=(uint8_t)(length>>8);
rtcpbuffer[posLength]=(uint8_t)(length);
return 0;
}
int32_t
RTCPSender::BuildAPP(uint8_t* rtcpbuffer, int& pos)
{
// sanity
if(_appData == NULL)
{
LOG(LS_WARNING) << "Failed to build app specific.";
return -1;
}
if(pos + 12 + _appLength >= IP_PACKET_SIZE)
{
LOG(LS_WARNING) << "Failed to build app specific.";
return -2;
}
rtcpbuffer[pos++]=(uint8_t)0x80 + _appSubType;
// Add APP ID
rtcpbuffer[pos++]=(uint8_t)204;
uint16_t length = (_appLength>>2) + 2; // include SSRC and name
rtcpbuffer[pos++]=(uint8_t)(length>>8);
rtcpbuffer[pos++]=(uint8_t)(length);
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// Add our application name
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _appName);
pos += 4;
// Add the data
memcpy(rtcpbuffer +pos, _appData,_appLength);
pos += _appLength;
return 0;
}
int32_t
RTCPSender::BuildNACK(uint8_t* rtcpbuffer,
int& pos,
const int32_t nackSize,
const uint16_t* nackList,
std::string* nackString)
{
// sanity
if(pos + 16 >= IP_PACKET_SIZE)
{
LOG(LS_WARNING) << "Failed to build NACK.";
return -2;
}
// int size, uint16_t* nackList
// add nack list
uint8_t FMT = 1;
rtcpbuffer[pos++]=(uint8_t)0x80 + FMT;
rtcpbuffer[pos++]=(uint8_t)205;
rtcpbuffer[pos++]=(uint8_t) 0;
int nackSizePos = pos;
rtcpbuffer[pos++]=(uint8_t)(3); //setting it to one kNACK signal as default
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// Add the remote SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC);
pos += 4;
NACKStringBuilder stringBuilder;
// Build NACK bitmasks and write them to the RTCP message.
// The nack list should be sorted and not contain duplicates if one
// wants to build the smallest rtcp nack packet.
int numOfNackFields = 0;
int maxNackFields = std::min<int>(kRtcpMaxNackFields,
(IP_PACKET_SIZE - pos) / 4);
int i = 0;
while (i < nackSize && numOfNackFields < maxNackFields) {
stringBuilder.PushNACK(nackList[i]);
uint16_t nack = nackList[i++];
uint16_t bitmask = 0;
while (i < nackSize) {
int shift = static_cast<uint16_t>(nackList[i] - nack) - 1;
if (shift >= 0 && shift <= 15) {
stringBuilder.PushNACK(nackList[i]);
bitmask |= (1 << shift);
++i;
} else {
break;
}
}
// Write the sequence number and the bitmask to the packet.
assert(pos + 4 < IP_PACKET_SIZE);
RtpUtility::AssignUWord16ToBuffer(rtcpbuffer + pos, nack);
pos += 2;
RtpUtility::AssignUWord16ToBuffer(rtcpbuffer + pos, bitmask);
pos += 2;
numOfNackFields++;
}
if (i != nackSize) {
LOG(LS_WARNING) << "Nack list to large for one packet.";
}
rtcpbuffer[nackSizePos] = static_cast<uint8_t>(2 + numOfNackFields);
*nackString = stringBuilder.GetResult();
return 0;
}
int32_t
RTCPSender::BuildBYE(uint8_t* rtcpbuffer, int& pos)
{
// sanity
if(pos + 8 >= IP_PACKET_SIZE)
{
return -2;
}
if(_includeCSRCs)
{
// Add a bye packet
rtcpbuffer[pos++]=(uint8_t)0x80 + 1 + _CSRCs; // number of SSRC+CSRCs
rtcpbuffer[pos++]=(uint8_t)203;
// length
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)(1 + _CSRCs);
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
// add CSRCs
for(int i = 0; i < _CSRCs; i++)
{
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _CSRC[i]);
pos += 4;
}
} else
{
// Add a bye packet
rtcpbuffer[pos++]=(uint8_t)0x80 + 1; // number of SSRC+CSRCs
rtcpbuffer[pos++]=(uint8_t)203;
// length
rtcpbuffer[pos++]=(uint8_t)0;
rtcpbuffer[pos++]=(uint8_t)1;
// Add our own SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC);
pos += 4;
}
return 0;
}
int32_t RTCPSender::BuildReceiverReferenceTime(uint8_t* buffer,
int& pos,
uint32_t ntp_sec,
uint32_t ntp_frac) {
const int kRrTimeBlockLength = 20;
if (pos + kRrTimeBlockLength >= IP_PACKET_SIZE) {
return -2;
}
if (last_xr_rr_.size() >= RTCP_NUMBER_OF_SR) {
last_xr_rr_.erase(last_xr_rr_.begin());
}
last_xr_rr_.insert(std::pair<uint32_t, int64_t>(
RTCPUtility::MidNtp(ntp_sec, ntp_frac),
Clock::NtpToMs(ntp_sec, ntp_frac)));
// Add XR header.
buffer[pos++] = 0x80;
buffer[pos++] = 207;
buffer[pos++] = 0; // XR packet length.
buffer[pos++] = 4; // XR packet length.
// Add our own SSRC.
RtpUtility::AssignUWord32ToBuffer(buffer + pos, _SSRC);
pos += 4;
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=4 | reserved | block length = 2 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NTP timestamp, most significant word |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NTP timestamp, least significant word |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Add Receiver Reference Time Report block.
buffer[pos++] = 4; // BT.
buffer[pos++] = 0; // Reserved.
buffer[pos++] = 0; // Block length.
buffer[pos++] = 2; // Block length.
// NTP timestamp.
RtpUtility::AssignUWord32ToBuffer(buffer + pos, ntp_sec);
pos += 4;
RtpUtility::AssignUWord32ToBuffer(buffer + pos, ntp_frac);
pos += 4;
return 0;
}
int32_t RTCPSender::BuildDlrr(uint8_t* buffer,
int& pos,
const RtcpReceiveTimeInfo& info) {
const int kDlrrBlockLength = 24;
if (pos + kDlrrBlockLength >= IP_PACKET_SIZE) {
return -2;
}
// Add XR header.
buffer[pos++] = 0x80;
buffer[pos++] = 207;
buffer[pos++] = 0; // XR packet length.
buffer[pos++] = 5; // XR packet length.
// Add our own SSRC.
RtpUtility::AssignUWord32ToBuffer(buffer + pos, _SSRC);
pos += 4;
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=5 | reserved | block length |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | SSRC_1 (SSRC of first receiver) | sub-
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
// | last RR (LRR) | 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | delay since last RR (DLRR) |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | SSRC_2 (SSRC of second receiver) | sub-
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
// : ... : 2
// Add DLRR sub block.
buffer[pos++] = 5; // BT.
buffer[pos++] = 0; // Reserved.
buffer[pos++] = 0; // Block length.
buffer[pos++] = 3; // Block length.
// NTP timestamp.
RtpUtility::AssignUWord32ToBuffer(buffer + pos, info.sourceSSRC);
pos += 4;
RtpUtility::AssignUWord32ToBuffer(buffer + pos, info.lastRR);
pos += 4;
RtpUtility::AssignUWord32ToBuffer(buffer + pos, info.delaySinceLastRR);
pos += 4;
return 0;
}
int32_t
RTCPSender::BuildVoIPMetric(uint8_t* rtcpbuffer, int& pos)
{
// sanity
if(pos + 44 >= IP_PACKET_SIZE)
{
return -2;
}
// Add XR header
rtcpbuffer[pos++]=(uint8_t)0x80;
rtcpbuffer[pos++]=(uint8_t)207;
uint32_t XRLengthPos = pos;
// handle length later on
pos++;
pos++;
// Add our own SSRC
RtpUtility::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
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC);
pos += 4;
rtcpbuffer[pos++] = _xrVoIPMetric.lossRate;
rtcpbuffer[pos++] = _xrVoIPMetric.discardRate;
rtcpbuffer[pos++] = _xrVoIPMetric.burstDensity;
rtcpbuffer[pos++] = _xrVoIPMetric.gapDensity;
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.burstDuration >> 8);
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.burstDuration);
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.gapDuration >> 8);
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.gapDuration);
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.roundTripDelay >> 8);
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.roundTripDelay);
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.endSystemDelay >> 8);
rtcpbuffer[pos++] = (uint8_t)(_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++] = (uint8_t)(_xrVoIPMetric.JBnominal >> 8);
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.JBnominal);
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.JBmax >> 8);
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.JBmax);
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.JBabsMax >> 8);
rtcpbuffer[pos++] = (uint8_t)(_xrVoIPMetric.JBabsMax);
rtcpbuffer[XRLengthPos]=(uint8_t)(0);
rtcpbuffer[XRLengthPos+1]=(uint8_t)(10);
return 0;
}
int32_t RTCPSender::SendRTCP(const FeedbackState& feedback_state,
uint32_t packetTypeFlags,
int32_t nackSize,
const uint16_t* nackList,
bool repeat,
uint64_t pictureID) {
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(_method == kRtcpOff)
{
LOG(LS_WARNING) << "Can't send rtcp if it is disabled.";
return -1;
}
}
uint8_t rtcp_buffer[IP_PACKET_SIZE];
int rtcp_length = PrepareRTCP(feedback_state,
packetTypeFlags,
nackSize,
nackList,
repeat,
pictureID,
rtcp_buffer,
IP_PACKET_SIZE);
if (rtcp_length < 0) {
return -1;
}
// Sanity don't send empty packets.
if (rtcp_length == 0)
{
return -1;
}
return SendToNetwork(rtcp_buffer, static_cast<uint16_t>(rtcp_length));
}
int RTCPSender::PrepareRTCP(const FeedbackState& feedback_state,
uint32_t packetTypeFlags,
int32_t nackSize,
const uint16_t* nackList,
bool repeat,
uint64_t pictureID,
uint8_t* rtcp_buffer,
int buffer_size) {
uint32_t rtcpPacketTypeFlags = packetTypeFlags;
// Collect the received information.
uint32_t NTPsec = 0;
uint32_t NTPfrac = 0;
uint32_t jitterTransmissionOffset = 0;
int position = 0;
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 (rtcpPacketTypeFlags & kRtcpReport)
{
if (xrSendReceiverReferenceTimeEnabled_ && !_sending)
{
rtcpPacketTypeFlags |= kRtcpXrReceiverReferenceTime;
}
if (feedback_state.has_last_xr_rr)
{
rtcpPacketTypeFlags |= kRtcpXrDlrrReportBlock;
}
}
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
int32_t random = rand() % 1000;
int32_t timeToNext = RTCP_INTERVAL_AUDIO_MS;
if(_audio)
{
timeToNext = (RTCP_INTERVAL_AUDIO_MS/2) +
(RTCP_INTERVAL_AUDIO_MS*random/1000);
}else
{
uint32_t minIntervalMs = RTCP_INTERVAL_AUDIO_MS;
if(_sending)
{
// Calculate bandwidth for video; 360 / send bandwidth in kbit/s.
uint32_t send_bitrate_kbit = feedback_state.send_bitrate / 1000;
if (send_bitrate_kbit != 0)
minIntervalMs = 360000 / send_bitrate_kbit;
}
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 fit in the packet we fill it as much as possible.
int32_t buildVal = 0;
// We need to send our NTP even if we haven't received any reports.
_clock->CurrentNtp(NTPsec, NTPfrac);
if (ShouldSendReportBlocks(rtcpPacketTypeFlags)) {
StatisticianMap statisticians =
receive_statistics_->GetActiveStatisticians();
if (!statisticians.empty()) {
StatisticianMap::const_iterator it;
int i;
for (it = statisticians.begin(), i = 0; it != statisticians.end();
++it, ++i) {
RTCPReportBlock report_block;
if (PrepareReport(
feedback_state, it->second, &report_block, &NTPsec, &NTPfrac))
AddReportBlock(it->first, &internal_report_blocks_, &report_block);
}
if (_IJ && !statisticians.empty()) {
rtcpPacketTypeFlags |= kRtcpTransmissionTimeOffset;
}
}
}
if(rtcpPacketTypeFlags & kRtcpSr)
{
buildVal = BuildSR(feedback_state, rtcp_buffer, position, NTPsec, NTPfrac);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
buildVal = BuildSDEC(rtcp_buffer, position);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
}else if(rtcpPacketTypeFlags & kRtcpRr)
{
buildVal = BuildRR(rtcp_buffer, position, NTPsec, NTPfrac);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
// only of set
if(_CNAME[0] != 0)
{
buildVal = BuildSDEC(rtcp_buffer, position);
if (buildVal == -1) {
return -1;
}
}
}
if(rtcpPacketTypeFlags & kRtcpTransmissionTimeOffset)
{
// If present, this RTCP packet must be placed after a
// receiver report.
buildVal = BuildExtendedJitterReport(rtcp_buffer,
position,
jitterTransmissionOffset);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
}
if(rtcpPacketTypeFlags & kRtcpPli)
{
buildVal = BuildPLI(rtcp_buffer, position);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
TRACE_EVENT_INSTANT0("webrtc_rtp", "RTCPSender::PLI");
++packet_type_counter_.pli_packets;
TRACE_COUNTER_ID1("webrtc_rtp", "RTCP_PLICount", _SSRC,
packet_type_counter_.pli_packets);
}
if(rtcpPacketTypeFlags & kRtcpFir)
{
buildVal = BuildFIR(rtcp_buffer, position, repeat);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
TRACE_EVENT_INSTANT0("webrtc_rtp", "RTCPSender::FIR");
++packet_type_counter_.fir_packets;
TRACE_COUNTER_ID1("webrtc_rtp", "RTCP_FIRCount", _SSRC,
packet_type_counter_.fir_packets);
}
if(rtcpPacketTypeFlags & kRtcpSli)
{
buildVal = BuildSLI(rtcp_buffer, position, (uint8_t)pictureID);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
}
if(rtcpPacketTypeFlags & kRtcpRpsi)
{
const int8_t payloadType = feedback_state.send_payload_type;
if (payloadType == -1) {
return -1;
}
buildVal = BuildRPSI(rtcp_buffer, position, pictureID,
(uint8_t)payloadType);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
}
if(rtcpPacketTypeFlags & kRtcpRemb)
{
buildVal = BuildREMB(rtcp_buffer, position);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
TRACE_EVENT_INSTANT0("webrtc_rtp", "RTCPSender::REMB");
}
if(rtcpPacketTypeFlags & kRtcpBye)
{
buildVal = BuildBYE(rtcp_buffer, position);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
}
if(rtcpPacketTypeFlags & kRtcpApp)
{
buildVal = BuildAPP(rtcp_buffer, position);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
}
if(rtcpPacketTypeFlags & kRtcpTmmbr)
{
buildVal = BuildTMMBR(feedback_state.module, rtcp_buffer, position);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
}
if(rtcpPacketTypeFlags & kRtcpTmmbn)
{
buildVal = BuildTMMBN(rtcp_buffer, position);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
}
if(rtcpPacketTypeFlags & kRtcpNack)
{
std::string nackString;
buildVal = BuildNACK(rtcp_buffer, position, nackSize, nackList,
&nackString);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
TRACE_EVENT_INSTANT1("webrtc_rtp", "RTCPSender::NACK",
"nacks", TRACE_STR_COPY(nackString.c_str()));
++packet_type_counter_.nack_packets;
TRACE_COUNTER_ID1("webrtc_rtp", "RTCP_NACKCount", _SSRC,
packet_type_counter_.nack_packets);
}
if(rtcpPacketTypeFlags & kRtcpXrVoipMetric)
{
buildVal = BuildVoIPMetric(rtcp_buffer, position);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
}
if (rtcpPacketTypeFlags & kRtcpXrReceiverReferenceTime)
{
buildVal = BuildReceiverReferenceTime(rtcp_buffer,
position,
NTPsec,
NTPfrac);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
}
if (rtcpPacketTypeFlags & kRtcpXrDlrrReportBlock)
{
buildVal = BuildDlrr(rtcp_buffer, position, feedback_state.last_xr_rr);
if (buildVal == -1) {
return -1;
} else if (buildVal == -2) {
return position;
}
}
return position;
}
bool RTCPSender::ShouldSendReportBlocks(uint32_t rtcp_packet_type) const {
return Status() == kRtcpCompound ||
(rtcp_packet_type & kRtcpReport) ||
(rtcp_packet_type & kRtcpSr) ||
(rtcp_packet_type & kRtcpRr);
}
bool RTCPSender::PrepareReport(const FeedbackState& feedback_state,
StreamStatistician* statistician,
RTCPReportBlock* report_block,
uint32_t* ntp_secs, uint32_t* ntp_frac) {
// Do we have receive statistics to send?
RtcpStatistics stats;
if (!statistician->GetStatistics(&stats, true))
return false;
report_block->fractionLost = stats.fraction_lost;
report_block->cumulativeLost = stats.cumulative_lost;
report_block->extendedHighSeqNum =
stats.extended_max_sequence_number;
report_block->jitter = stats.jitter;
// get our NTP as late as possible to avoid a race
_clock->CurrentNtp(*ntp_secs, *ntp_frac);
// Delay since last received report
uint32_t delaySinceLastReceivedSR = 0;
if ((feedback_state.last_rr_ntp_secs != 0) ||
(feedback_state.last_rr_ntp_frac != 0)) {
// get the 16 lowest bits of seconds and the 16 higest bits of fractions
uint32_t now=*ntp_secs&0x0000FFFF;
now <<=16;
now += (*ntp_frac&0xffff0000)>>16;
uint32_t receiveTime = feedback_state.last_rr_ntp_secs&0x0000FFFF;
receiveTime <<=16;
receiveTime += (feedback_state.last_rr_ntp_frac&0xffff0000)>>16;
delaySinceLastReceivedSR = now-receiveTime;
}
report_block->delaySinceLastSR = delaySinceLastReceivedSR;
report_block->lastSR = feedback_state.remote_sr;
return true;
}
int32_t
RTCPSender::SendToNetwork(const uint8_t* dataBuffer,
const uint16_t length)
{
CriticalSectionScoped lock(_criticalSectionTransport);
if(_cbTransport)
{
if(_cbTransport->SendRTCPPacket(_id, dataBuffer, length) > 0)
{
return 0;
}
}
return -1;
}
int32_t
RTCPSender::SetCSRCStatus(const bool include)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
_includeCSRCs = include;
return 0;
}
int32_t
RTCPSender::SetCSRCs(const uint32_t arrOfCSRC[kRtpCsrcSize],
const uint8_t arrLength)
{
assert(arrLength <= kRtpCsrcSize);
CriticalSectionScoped lock(_criticalSectionRTCPSender);
for(int i = 0; i < arrLength;i++)
{
_CSRC[i] = arrOfCSRC[i];
}
_CSRCs = arrLength;
return 0;
}
int32_t
RTCPSender::SetApplicationSpecificData(const uint8_t subType,
const uint32_t name,
const uint8_t* data,
const uint16_t length)
{
if(length %4 != 0)
{
LOG(LS_ERROR) << "Failed to SetApplicationSpecificData.";
return -1;
}
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if(_appData)
{
delete [] _appData;
}
_appSend = true;
_appSubType = subType;
_appName = name;
_appData = new uint8_t[length];
_appLength = length;
memcpy(_appData, data, length);
return 0;
}
int32_t
RTCPSender::SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
memcpy(&_xrVoIPMetric, VoIPMetric, sizeof(RTCPVoIPMetric));
_xrSendVoIPMetric = true;
return 0;
}
void RTCPSender::SendRtcpXrReceiverReferenceTime(bool enable) {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
xrSendReceiverReferenceTimeEnabled_ = enable;
}
bool RTCPSender::RtcpXrReceiverReferenceTime() const {
CriticalSectionScoped lock(_criticalSectionRTCPSender);
return xrSendReceiverReferenceTimeEnabled_;
}
// called under critsect _criticalSectionRTCPSender
int32_t RTCPSender::WriteAllReportBlocksToBuffer(
uint8_t* rtcpbuffer,
int pos,
uint8_t& numberOfReportBlocks,
const uint32_t NTPsec,
const uint32_t NTPfrac) {
numberOfReportBlocks = external_report_blocks_.size();
numberOfReportBlocks += internal_report_blocks_.size();
if ((pos + numberOfReportBlocks * 24) >= IP_PACKET_SIZE) {
LOG(LS_WARNING) << "Can't fit all report blocks.";
return -1;
}
pos = WriteReportBlocksToBuffer(rtcpbuffer, pos, internal_report_blocks_);
while (!internal_report_blocks_.empty()) {
delete internal_report_blocks_.begin()->second;
internal_report_blocks_.erase(internal_report_blocks_.begin());
}
pos = WriteReportBlocksToBuffer(rtcpbuffer, pos, external_report_blocks_);
return pos;
}
int32_t RTCPSender::WriteReportBlocksToBuffer(
uint8_t* rtcpbuffer,
int32_t position,
const std::map<uint32_t, RTCPReportBlock*>& report_blocks) {
std::map<uint32_t, RTCPReportBlock*>::const_iterator it =
report_blocks.begin();
for (; it != report_blocks.end(); it++) {
uint32_t remoteSSRC = it->first;
RTCPReportBlock* reportBlock = it->second;
if (reportBlock) {
// Remote SSRC
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + position, remoteSSRC);
position += 4;
// fraction lost
rtcpbuffer[position++] = reportBlock->fractionLost;
// cumulative loss
RtpUtility::AssignUWord24ToBuffer(rtcpbuffer + position,
reportBlock->cumulativeLost);
position += 3;
// extended highest seq_no, contain the highest sequence number received
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + position,
reportBlock->extendedHighSeqNum);
position += 4;
// Jitter
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + position,
reportBlock->jitter);
position += 4;
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + position,
reportBlock->lastSR);
position += 4;
RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + position,
reportBlock->delaySinceLastSR);
position += 4;
}
}
return position;
}
// no callbacks allowed inside this function
int32_t
RTCPSender::SetTMMBN(const TMMBRSet* boundingSet,
const uint32_t maxBitrateKbit)
{
CriticalSectionScoped lock(_criticalSectionRTCPSender);
if (0 == _tmmbrHelp.SetTMMBRBoundingSetToSend(boundingSet, maxBitrateKbit))
{
_sendTMMBN = true;
return 0;
}
return -1;
}
} // namespace webrtc