|  | /* | 
|  | *  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_(), | 
|  |  | 
|  | _sequenceNumberFIR(0), | 
|  |  | 
|  | _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 [] _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 std::vector<uint32_t>& ssrcs) | 
|  | { | 
|  | CriticalSectionScoped lock(_criticalSectionRTCPSender); | 
|  | _rembBitrate = bitrate; | 
|  | remb_ssrcs_ = ssrcs; | 
|  |  | 
|  | _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 * remb_ssrcs_.size() >= 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++]=remb_ssrcs_.size() + 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++] = remb_ssrcs_.size(); | 
|  | // 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 (size_t i = 0; i < remb_ssrcs_.size(); i++) | 
|  | { | 
|  | RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, remb_ssrcs_[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; | 
|  |  | 
|  | // 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) { | 
|  | 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) { | 
|  | 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++; | 
|  | } | 
|  | rtcpbuffer[nackSizePos] = static_cast<uint8_t>(2 + numOfNackFields); | 
|  |  | 
|  | if (i != nackSize) { | 
|  | LOG(LS_WARNING) << "Nack list too large for one packet."; | 
|  | } | 
|  |  | 
|  | // Report stats. | 
|  | NACKStringBuilder stringBuilder; | 
|  | for (int idx = 0; idx < i; ++idx) { | 
|  | stringBuilder.PushNACK(nackList[idx]); | 
|  | nack_stats_.ReportRequest(nackList[idx]); | 
|  | } | 
|  | *nackString = stringBuilder.GetResult(); | 
|  | packet_type_counter_.nack_requests = nack_stats_.requests(); | 
|  | packet_type_counter_.unique_nack_requests = nack_stats_.unique_requests(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t RTCPSender::BuildBYE(uint8_t* rtcpbuffer, int& pos) { | 
|  | // sanity | 
|  | if (pos + 8 >= IP_PACKET_SIZE) { | 
|  | return -2; | 
|  | } | 
|  |  | 
|  | // Add a bye packet | 
|  | // Number of SSRC + CSRCs. | 
|  | rtcpbuffer[pos++] = (uint8_t)0x80 + 1 + csrcs_.size(); | 
|  | rtcpbuffer[pos++] = (uint8_t)203; | 
|  |  | 
|  | // length | 
|  | rtcpbuffer[pos++] = (uint8_t)0; | 
|  | rtcpbuffer[pos++] = (uint8_t)(1 + csrcs_.size()); | 
|  |  | 
|  | // Add our own SSRC | 
|  | RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); | 
|  | pos += 4; | 
|  |  | 
|  | // add CSRCs | 
|  | for (size_t i = 0; i < csrcs_.size(); i++) { | 
|  | RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, csrcs_[i]); | 
|  | 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<size_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 size_t length) | 
|  | { | 
|  | CriticalSectionScoped lock(_criticalSectionTransport); | 
|  | if(_cbTransport) | 
|  | { | 
|  | if(_cbTransport->SendRTCPPacket(_id, dataBuffer, length) > 0) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | void RTCPSender::SetCsrcs(const std::vector<uint32_t>& csrcs) { | 
|  | assert(csrcs.size() <= kRtpCsrcSize); | 
|  | CriticalSectionScoped lock(_criticalSectionRTCPSender); | 
|  | csrcs_ = csrcs; | 
|  | } | 
|  |  | 
|  | 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 |