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
2176 lines
62 KiB
C++
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
|