/* * 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 #include // rand #include // memcpy #include // min #include // max #include "webrtc/base/checks.h" #include "webrtc/common_types.h" #include "webrtc/modules/rtp_rtcp/source/byte_io.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), prevNack_(0), consecutive_(false) { } 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), module(nullptr) { } struct RTCPSender::RtcpContext { RtcpContext(const FeedbackState& feedback_state, int32_t nack_size, const uint16_t* nack_list, bool repeat, uint64_t picture_id, uint8_t* buffer, uint32_t buffer_size) : feedback_state(feedback_state), nack_size(nack_size), nack_list(nack_list), repeat(repeat), picture_id(picture_id), buffer(buffer), buffer_size(buffer_size), ntp_sec(0), ntp_frac(0), position(0) {} uint8_t* AllocateData(uint32_t bytes) { DCHECK_LE(position + bytes, buffer_size); uint8_t* ptr = &buffer[position]; position += bytes; return ptr; } const FeedbackState& feedback_state; int32_t nack_size; const uint16_t* nack_list; bool repeat; uint64_t picture_id; uint8_t* buffer; uint32_t buffer_size; uint32_t ntp_sec; uint32_t ntp_frac; uint32_t position; }; // TODO(sprang): Once all builders use RtcpPacket, call SendToNetwork() here. class RTCPSender::PacketBuiltCallback : public rtcp::RtcpPacket::PacketReadyCallback { public: PacketBuiltCallback(RtcpContext* context) : context_(context) {} virtual ~PacketBuiltCallback() {} void OnPacketReady(uint8_t* data, size_t length) override { context_->position += length; } private: RtcpContext* const context_; }; RTCPSender::RTCPSender( int32_t id, bool audio, Clock* clock, ReceiveStatistics* receive_statistics, RtcpPacketTypeCounterObserver* packet_type_counter_observer) : id_(id), audio_(audio), clock_(clock), method_(kRtcpOff), critical_section_transport_( CriticalSectionWrapper::CreateCriticalSection()), cbTransport_(nullptr), critical_section_rtcp_sender_( CriticalSectionWrapper::CreateCriticalSection()), using_nack_(false), sending_(false), remb_enabled_(false), next_time_to_send_rtcp_(0), start_timestamp_(0), last_rtp_timestamp_(0), last_frame_capture_time_ms_(-1), ssrc_(0), remote_ssrc_(0), receive_statistics_(receive_statistics), sequence_number_fir_(0), remb_bitrate_(0), tmmbr_help_(), tmmbr_send_(0), packet_oh_send_(0), app_sub_type_(0), app_name_(0), app_data_(nullptr), app_length_(0), xr_send_receiver_reference_time_enabled_(false), packet_type_counter_observer_(packet_type_counter_observer) { memset(last_send_report_, 0, sizeof(last_send_report_)); memset(last_rtcp_time_, 0, sizeof(last_rtcp_time_)); builders_[kRtcpSr] = &RTCPSender::BuildSR; builders_[kRtcpRr] = &RTCPSender::BuildRR; builders_[kRtcpSdes] = &RTCPSender::BuildSDES; builders_[kRtcpPli] = &RTCPSender::BuildPLI; builders_[kRtcpFir] = &RTCPSender::BuildFIR; builders_[kRtcpSli] = &RTCPSender::BuildSLI; builders_[kRtcpRpsi] = &RTCPSender::BuildRPSI; builders_[kRtcpRemb] = &RTCPSender::BuildREMB; builders_[kRtcpBye] = &RTCPSender::BuildBYE; builders_[kRtcpApp] = &RTCPSender::BuildAPP; builders_[kRtcpTmmbr] = &RTCPSender::BuildTMMBR; builders_[kRtcpTmmbn] = &RTCPSender::BuildTMMBN; builders_[kRtcpNack] = &RTCPSender::BuildNACK; builders_[kRtcpXrVoipMetric] = &RTCPSender::BuildVoIPMetric; builders_[kRtcpXrReceiverReferenceTime] = &RTCPSender::BuildReceiverReferenceTime; builders_[kRtcpXrDlrrReportBlock] = &RTCPSender::BuildDlrr; } RTCPSender::~RTCPSender() { } int32_t RTCPSender::RegisterSendTransport(Transport* outgoingTransport) { CriticalSectionScoped lock(critical_section_transport_.get()); cbTransport_ = outgoingTransport; return 0; } RTCPMethod RTCPSender::Status() const { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); return method_; } void RTCPSender::SetRTCPStatus(RTCPMethod method) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); method_ = method; if (method == kRtcpOff) return; next_time_to_send_rtcp_ = clock_->TimeInMilliseconds() + (audio_ ? RTCP_INTERVAL_AUDIO_MS / 2 : RTCP_INTERVAL_VIDEO_MS / 2); } bool RTCPSender::Sending() const { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); return sending_; } int32_t RTCPSender::SetSendingStatus(const FeedbackState& feedback_state, bool sending) { bool sendRTCPBye = false; { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); 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(critical_section_rtcp_sender_.get()); return remb_enabled_; } void RTCPSender::SetREMBStatus(bool enable) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); remb_enabled_ = enable; } void RTCPSender::SetREMBData(uint32_t bitrate, const std::vector& ssrcs) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); remb_bitrate_ = bitrate; remb_ssrcs_ = ssrcs; if (remb_enabled_) SetFlag(kRtcpRemb, false); // Send a REMB immediately if we have a new REMB. The frequency of REMBs is // throttled by the caller. next_time_to_send_rtcp_ = clock_->TimeInMilliseconds(); } bool RTCPSender::TMMBR() const { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); return IsFlagPresent(RTCPPacketType::kRtcpTmmbr); } void RTCPSender::SetTMMBRStatus(bool enable) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); if (enable) { SetFlag(RTCPPacketType::kRtcpTmmbr, false); } else { ConsumeFlag(RTCPPacketType::kRtcpTmmbr, true); } } void RTCPSender::SetStartTimestamp(uint32_t start_timestamp) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); start_timestamp_ = start_timestamp; } void RTCPSender::SetLastRtpTime(uint32_t rtp_timestamp, int64_t capture_time_ms) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); 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(uint32_t ssrc) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); if (ssrc_ != 0) { // not first SetSSRC, probably due to a collision // schedule a new RTCP report // make sure that we send a RTP packet next_time_to_send_rtcp_ = clock_->TimeInMilliseconds() + 100; } ssrc_ = ssrc; } void RTCPSender::SetRemoteSSRC(uint32_t ssrc) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); remote_ssrc_ = ssrc; } int32_t RTCPSender::SetCNAME(const char* c_name) { if (!c_name) return -1; DCHECK_LT(strlen(c_name), static_cast(RTCP_CNAME_SIZE)); CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); cname_ = c_name; return 0; } int32_t RTCPSender::AddMixedCNAME(uint32_t SSRC, const char* c_name) { assert(c_name); DCHECK_LT(strlen(c_name), static_cast(RTCP_CNAME_SIZE)); CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); if (csrc_cnames_.size() >= kRtpCsrcSize) return -1; csrc_cnames_[SSRC] = c_name; return 0; } int32_t RTCPSender::RemoveMixedCNAME(uint32_t SSRC) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); auto it = csrc_cnames_.find(SSRC); if (it == csrc_cnames_.end()) return -1; csrc_cnames_.erase(it); return 0; } bool RTCPSender::TimeToSendRTCPReport(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(critical_section_rtcp_sender_.get()); 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 >= next_time_to_send_rtcp_) { return true; } else if (now < 0x0000ffff && next_time_to_send_rtcp_ > 0xffff0000) { // 65 sec margin // wrap return true; } return false; } int64_t RTCPSender::SendTimeOfSendReport(uint32_t sendReport) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); // This is only saved when we are the sender if ((last_send_report_[0] == 0) || (sendReport == 0)) { return 0; // will be ignored } else { for (int i = 0; i < RTCP_NUMBER_OF_SR; ++i) { if (last_send_report_[i] == sendReport) return last_rtcp_time_[i]; } } return 0; } bool RTCPSender::SendTimeOfXrRrReport(uint32_t mid_ntp, int64_t* time_ms) const { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); if (last_xr_rr_.empty()) { return false; } std::map::const_iterator it = last_xr_rr_.find(mid_ntp); if (it == last_xr_rr_.end()) { return false; } *time_ms = it->second; return true; } int32_t RTCPSender::AddReportBlock(const RTCPReportBlock& report_block) { if (report_blocks_.size() >= RTCP_MAX_REPORT_BLOCKS) { LOG(LS_WARNING) << "Too many report blocks."; return -1; } rtcp::ReportBlock* block = &report_blocks_[report_block.remoteSSRC]; block->To(report_block.remoteSSRC); block->WithFractionLost(report_block.fractionLost); block->WithCumulativeLost(report_block.cumulativeLost); block->WithExtHighestSeqNum(report_block.extendedHighSeqNum); block->WithJitter(report_block.jitter); block->WithLastSr(report_block.lastSR); block->WithDelayLastSr(report_block.delaySinceLastSR); return 0; } RTCPSender::BuildResult RTCPSender::BuildSR(RtcpContext* ctx) { for (int i = (RTCP_NUMBER_OF_SR - 2); i >= 0; i--) { // shift old last_send_report_[i + 1] = last_send_report_[i]; last_rtcp_time_[i + 1] = last_rtcp_time_[i]; } last_rtcp_time_[0] = Clock::NtpToMs(ctx->ntp_sec, ctx->ntp_frac); last_send_report_[0] = (ctx->ntp_sec << 16) + (ctx->ntp_frac >> 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. uint32_t rtp_timestamp = start_timestamp_ + last_rtp_timestamp_ + (clock_->TimeInMilliseconds() - last_frame_capture_time_ms_) * (ctx->feedback_state.frequency_hz / 1000); rtcp::SenderReport report; report.From(ssrc_); report.WithNtpSec(ctx->ntp_sec); report.WithNtpFrac(ctx->ntp_frac); report.WithRtpTimestamp(rtp_timestamp); report.WithPacketCount(ctx->feedback_state.packets_sent); report.WithOctetCount(ctx->feedback_state.media_bytes_sent); for (auto it : report_blocks_) report.WithReportBlock(it.second); PacketBuiltCallback callback(ctx); if (!report.BuildExternalBuffer(&ctx->buffer[ctx->position], ctx->buffer_size - ctx->position, &callback)) { return BuildResult::kTruncated; } report_blocks_.clear(); return BuildResult::kSuccess; } RTCPSender::BuildResult RTCPSender::BuildSDES(RtcpContext* ctx) { size_t length_cname = cname_.length(); CHECK_LT(length_cname, static_cast(RTCP_CNAME_SIZE)); rtcp::Sdes sdes; sdes.WithCName(ssrc_, cname_); for (const auto it : csrc_cnames_) sdes.WithCName(it.first, it.second); PacketBuiltCallback callback(ctx); if (!sdes.BuildExternalBuffer(&ctx->buffer[ctx->position], ctx->buffer_size - ctx->position, &callback)) { return BuildResult::kTruncated; } return BuildResult::kSuccess; } RTCPSender::BuildResult RTCPSender::BuildRR(RtcpContext* ctx) { rtcp::ReceiverReport report; report.From(ssrc_); for (auto it : report_blocks_) report.WithReportBlock(it.second); PacketBuiltCallback callback(ctx); if (!report.BuildExternalBuffer(&ctx->buffer[ctx->position], ctx->buffer_size - ctx->position, &callback)) { return BuildResult::kTruncated; } report_blocks_.clear(); return BuildResult::kSuccess; } RTCPSender::BuildResult RTCPSender::BuildPLI(RtcpContext* ctx) { // sanity if (ctx->position + 12 >= IP_PACKET_SIZE) return BuildResult::kTruncated; // add picture loss indicator uint8_t FMT = 1; *ctx->AllocateData(1) = 0x80 + FMT; *ctx->AllocateData(1) = 206; // Used fixed length of 2 *ctx->AllocateData(1) = 0; *ctx->AllocateData(1) = 2; // Add our own SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // Add the remote SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), remote_ssrc_); TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "RTCPSender::PLI"); ++packet_type_counter_.pli_packets; TRACE_COUNTER_ID1(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "RTCP_PLICount", ssrc_, packet_type_counter_.pli_packets); return BuildResult::kSuccess; } RTCPSender::BuildResult RTCPSender::BuildFIR(RtcpContext* ctx) { // sanity if (ctx->position + 20 >= IP_PACKET_SIZE) return BuildResult::kTruncated; if (!ctx->repeat) sequence_number_fir_++; // do not increase if repetition // add full intra request indicator uint8_t FMT = 4; *ctx->AllocateData(1) = 0x80 + FMT; *ctx->AllocateData(1) = 206; //Length of 4 *ctx->AllocateData(1) = 0; *ctx->AllocateData(1) = 4; // Add our own SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // RFC 5104 4.3.1.2. Semantics // SSRC of media source ByteWriter::WriteBigEndian(ctx->AllocateData(4), 0); // Additional Feedback Control Information (FCI) ByteWriter::WriteBigEndian(ctx->AllocateData(4), remote_ssrc_); *ctx->AllocateData(1) = sequence_number_fir_; *ctx->AllocateData(1) = 0; *ctx->AllocateData(1) = 0; *ctx->AllocateData(1) = 0; TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "RTCPSender::FIR"); ++packet_type_counter_.fir_packets; TRACE_COUNTER_ID1(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "RTCP_FIRCount", ssrc_, packet_type_counter_.fir_packets); return BuildResult::kSuccess; } /* 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 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ RTCPSender::BuildResult RTCPSender::BuildSLI(RtcpContext* ctx) { // sanity if (ctx->position + 16 >= IP_PACKET_SIZE) return BuildResult::kTruncated; // add slice loss indicator uint8_t FMT = 2; *ctx->AllocateData(1) = 0x80 + FMT; *ctx->AllocateData(1) = 206; // Used fixed length of 3 *ctx->AllocateData(1) = 0; *ctx->AllocateData(1) = 3; // Add our own SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // Add the remote SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), remote_ssrc_); // 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 & ctx->picture_id); ByteWriter::WriteBigEndian(ctx->AllocateData(4), sliField); return BuildResult::kSuccess; } /* 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 */ RTCPSender::BuildResult RTCPSender::BuildRPSI(RtcpContext* ctx) { if (ctx->feedback_state.send_payload_type == 0xFF) return BuildResult::kError; // sanity if (ctx->position + 24 >= IP_PACKET_SIZE) return BuildResult::kTruncated; // add Reference Picture Selection Indication uint8_t FMT = 3; *ctx->AllocateData(1) = 0x80 + FMT; *ctx->AllocateData(1) = 206; // calc length uint32_t bitsRequired = 7; uint8_t bytesRequired = 1; while ((ctx->picture_id >> bitsRequired) > 0) { bitsRequired += 7; bytesRequired++; } uint8_t size = 3; if (bytesRequired > 6) { size = 5; } else if (bytesRequired > 2) { size = 4; } *ctx->AllocateData(1) = 0; *ctx->AllocateData(1) = size; // Add our own SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // Add the remote SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), remote_ssrc_); // calc padding length uint8_t paddingBytes = 4 - ((2 + bytesRequired) % 4); if (paddingBytes == 4) paddingBytes = 0; // add padding length in bits *ctx->AllocateData(1) = paddingBytes * 8; // padding can be 0, 8, 16 or 24 // add payload type *ctx->AllocateData(1) = ctx->feedback_state.send_payload_type; // add picture ID for (int i = bytesRequired - 1; i > 0; --i) { *ctx->AllocateData(1) = 0x80 | static_cast(ctx->picture_id >> (i * 7)); } // add last byte of picture ID *ctx->AllocateData(1) = static_cast(ctx->picture_id & 0x7f); // add padding for (int j = 0; j < paddingBytes; j++) { *ctx->AllocateData(1) = 0; } return BuildResult::kSuccess; } RTCPSender::BuildResult RTCPSender::BuildREMB(RtcpContext* ctx) { // sanity if (ctx->position + 20 + 4 * remb_ssrcs_.size() >= IP_PACKET_SIZE) return BuildResult::kTruncated; // add application layer feedback uint8_t FMT = 15; *ctx->AllocateData(1) = 0x80 + FMT; *ctx->AllocateData(1) = 206; *ctx->AllocateData(1) = 0; *ctx->AllocateData(1) = static_cast(remb_ssrcs_.size() + 4); // Add our own SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // Remote SSRC must be 0 ByteWriter::WriteBigEndian(ctx->AllocateData(4), 0); *ctx->AllocateData(1) = 'R'; *ctx->AllocateData(1) = 'E'; *ctx->AllocateData(1) = 'M'; *ctx->AllocateData(1) = 'B'; *ctx->AllocateData(1) = remb_ssrcs_.size(); // 6 bit Exp // 18 bit mantissa uint8_t brExp = 0; for (uint32_t i = 0; i < 64; i++) { if (remb_bitrate_ <= (0x3FFFFu << i)) { brExp = i; break; } } const uint32_t brMantissa = (remb_bitrate_ >> brExp); *ctx->AllocateData(1) = static_cast((brExp << 2) + ((brMantissa >> 16) & 0x03)); *ctx->AllocateData(1) = static_cast(brMantissa >> 8); *ctx->AllocateData(1) = static_cast(brMantissa); for (size_t i = 0; i < remb_ssrcs_.size(); i++) ByteWriter::WriteBigEndian(ctx->AllocateData(4), remb_ssrcs_[i]); TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "RTCPSender::REMB"); return BuildResult::kSuccess; } void RTCPSender::SetTargetBitrate(unsigned int target_bitrate) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); tmmbr_send_ = target_bitrate / 1000; } RTCPSender::BuildResult RTCPSender::BuildTMMBR(RtcpContext* ctx) { if (ctx->feedback_state.module == NULL) return BuildResult::kError; // 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 = tmmbr_help_.CandidateSet(); // holding critical_section_rtcp_sender_ 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 = ctx->feedback_state.module->BoundingSet(tmmbrOwner, candidateSet); if (lengthOfBoundingSet > 0) { for (int32_t i = 0; i < lengthOfBoundingSet; i++) { if (candidateSet->Tmmbr(i) == tmmbr_send_ && candidateSet->PacketOH(i) == packet_oh_send_) { // do not send the same tuple return BuildResult::kAborted; } } if (!tmmbrOwner) { // use received bounding set as candidate set // add current tuple candidateSet->SetEntry(lengthOfBoundingSet, tmmbr_send_, packet_oh_send_, ssrc_); int numCandidates = lengthOfBoundingSet + 1; // find bounding set TMMBRSet* boundingSet = NULL; int numBoundingSet = tmmbr_help_.FindTMMBRBoundingSet(boundingSet); if (numBoundingSet > 0 || numBoundingSet <= numCandidates) tmmbrOwner = tmmbr_help_.IsOwner(ssrc_, numBoundingSet); if (!tmmbrOwner) { // did not enter bounding set, no meaning to send this request return BuildResult::kAborted; } } } if (tmmbr_send_) { // sanity if (ctx->position + 20 >= IP_PACKET_SIZE) return BuildResult::kTruncated; // add TMMBR indicator uint8_t FMT = 3; *ctx->AllocateData(1) = 0x80 + FMT; *ctx->AllocateData(1) = 205; // Length of 4 *ctx->AllocateData(1) = 0; *ctx->AllocateData(1) = 4; // Add our own SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // RFC 5104 4.2.1.2. Semantics // SSRC of media source ByteWriter::WriteBigEndian(ctx->AllocateData(4), 0); // Additional Feedback Control Information (FCI) ByteWriter::WriteBigEndian(ctx->AllocateData(4), remote_ssrc_); uint32_t bitRate = tmmbr_send_ * 1000; uint32_t mmbrExp = 0; for (uint32_t i = 0; i < 64; i++) { if (bitRate <= (0x1FFFFu << i)) { mmbrExp = i; break; } } uint32_t mmbrMantissa = (bitRate >> mmbrExp); *ctx->AllocateData(1) = static_cast((mmbrExp << 2) + ((mmbrMantissa >> 15) & 0x03)); *ctx->AllocateData(1) = static_cast(mmbrMantissa >> 7); *ctx->AllocateData(1) = static_cast( (mmbrMantissa << 1) + ((packet_oh_send_ >> 8) & 0x01)); *ctx->AllocateData(1) = static_cast(packet_oh_send_); } return BuildResult::kSuccess; } RTCPSender::BuildResult RTCPSender::BuildTMMBN(RtcpContext* ctx) { TMMBRSet* boundingSet = tmmbr_help_.BoundingSetToSend(); if (boundingSet == NULL) return BuildResult::kError; // sanity if (ctx->position + 12 + boundingSet->lengthOfSet() * 8 >= IP_PACKET_SIZE) { LOG(LS_WARNING) << "Failed to build TMMBN."; return BuildResult::kTruncated; } uint8_t FMT = 4; // add TMMBN indicator *ctx->AllocateData(1) = 0x80 + FMT; *ctx->AllocateData(1) = 205; // Add length later int posLength = ctx->position; ctx->AllocateData(2); // Add our own SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // RFC 5104 4.2.2.2. Semantics // SSRC of media source ByteWriter::WriteBigEndian(ctx->AllocateData(4), 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); ByteWriter::WriteBigEndian(ctx->AllocateData(4), tmmbrSSRC); uint32_t bitRate = boundingSet->Tmmbr(n) * 1000; uint32_t mmbrExp = 0; for (int i = 0; i < 64; i++) { if (bitRate <= (0x1FFFFu << i)) { mmbrExp = i; break; } } uint32_t mmbrMantissa = (bitRate >> mmbrExp); uint32_t measuredOH = boundingSet->PacketOH(n); *ctx->AllocateData(1) = static_cast((mmbrExp << 2) + ((mmbrMantissa >> 15) & 0x03)); *ctx->AllocateData(1) = static_cast(mmbrMantissa >> 7); *ctx->AllocateData(1) = static_cast((mmbrMantissa << 1) + ((measuredOH >> 8) & 0x01)); *ctx->AllocateData(1) = static_cast(measuredOH); numBoundingSet++; } } uint16_t length = static_cast(2 + 2 * numBoundingSet); ctx->buffer[posLength++] = static_cast(length >> 8); ctx->buffer[posLength] = static_cast(length); return BuildResult::kSuccess; } RTCPSender::BuildResult RTCPSender::BuildAPP(RtcpContext* ctx) { // sanity if (app_data_ == NULL) { LOG(LS_WARNING) << "Failed to build app specific."; return BuildResult::kError; } if (ctx->position + 12 + app_length_ >= IP_PACKET_SIZE) { LOG(LS_WARNING) << "Failed to build app specific."; return BuildResult::kTruncated; } *ctx->AllocateData(1) = 0x80 + app_sub_type_; // Add APP ID *ctx->AllocateData(1) = 204; uint16_t length = (app_length_ >> 2) + 2; // include SSRC and name *ctx->AllocateData(1) = static_cast(length >> 8); *ctx->AllocateData(1) = static_cast(length); // Add our own SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // Add our application name ByteWriter::WriteBigEndian(ctx->AllocateData(4), app_name_); // Add the data memcpy(ctx->AllocateData(app_length_), app_data_.get(), app_length_); return BuildResult::kSuccess; } RTCPSender::BuildResult RTCPSender::BuildNACK(RtcpContext* ctx) { // sanity if (ctx->position + 16 >= IP_PACKET_SIZE) { LOG(LS_WARNING) << "Failed to build NACK."; return BuildResult::kTruncated; } // int size, uint16_t* nack_list // add nack list uint8_t FMT = 1; *ctx->AllocateData(1) = 0x80 + FMT; *ctx->AllocateData(1) = 205; *ctx->AllocateData(1) = 0; int nack_size_pos_ = ctx->position; *ctx->AllocateData(1) = 3; // setting it to one kNACK signal as default // Add our own SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // Add the remote SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), remote_ssrc_); // 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(kRtcpMaxNackFields, (IP_PACKET_SIZE - ctx->position) / 4); int i = 0; while (i < ctx->nack_size && numOfNackFields < maxNackFields) { uint16_t nack = ctx->nack_list[i++]; uint16_t bitmask = 0; while (i < ctx->nack_size) { int shift = static_cast(ctx->nack_list[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(ctx->position + 4 < IP_PACKET_SIZE); ByteWriter::WriteBigEndian(ctx->AllocateData(2), nack); ByteWriter::WriteBigEndian(ctx->AllocateData(2), bitmask); numOfNackFields++; } ctx->buffer[nack_size_pos_] = static_cast(2 + numOfNackFields); if (i != ctx->nack_size) LOG(LS_WARNING) << "Nack list too large for one packet."; // Report stats. NACKStringBuilder stringBuilder; for (int idx = 0; idx < i; ++idx) { stringBuilder.PushNACK(ctx->nack_list[idx]); nack_stats_.ReportRequest(ctx->nack_list[idx]); } packet_type_counter_.nack_requests = nack_stats_.requests(); packet_type_counter_.unique_nack_requests = nack_stats_.unique_requests(); TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "RTCPSender::NACK", "nacks", TRACE_STR_COPY(stringBuilder.GetResult().c_str())); ++packet_type_counter_.nack_packets; TRACE_COUNTER_ID1(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "RTCP_NACKCount", ssrc_, packet_type_counter_.nack_packets); return BuildResult::kSuccess; } RTCPSender::BuildResult RTCPSender::BuildBYE(RtcpContext* ctx) { // sanity if (ctx->position + 8 >= IP_PACKET_SIZE) return BuildResult::kTruncated; // Add a bye packet // Number of SSRC + CSRCs. *ctx->AllocateData(1) = static_cast(0x80 + 1 + csrcs_.size()); *ctx->AllocateData(1) = 203; // length *ctx->AllocateData(1) = 0; *ctx->AllocateData(1) = static_cast(1 + csrcs_.size()); // Add our own SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // add CSRCs for (size_t i = 0; i < csrcs_.size(); i++) ByteWriter::WriteBigEndian(ctx->AllocateData(4), csrcs_[i]); return BuildResult::kSuccess; } RTCPSender::BuildResult RTCPSender::BuildReceiverReferenceTime( RtcpContext* ctx) { const int kRrTimeBlockLength = 20; if (ctx->position + kRrTimeBlockLength >= IP_PACKET_SIZE) return BuildResult::kTruncated; if (last_xr_rr_.size() >= RTCP_NUMBER_OF_SR) last_xr_rr_.erase(last_xr_rr_.begin()); last_xr_rr_.insert(std::pair( RTCPUtility::MidNtp(ctx->ntp_sec, ctx->ntp_frac), Clock::NtpToMs(ctx->ntp_sec, ctx->ntp_frac))); // Add XR header. *ctx->AllocateData(1) = 0x80; *ctx->AllocateData(1) = 207; ByteWriter::WriteBigEndian(ctx->AllocateData(2), 4); // XR packet length. // Add our own SSRC. ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // 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. *ctx->AllocateData(1) = 4; // BT. *ctx->AllocateData(1) = 0; // Reserved. ByteWriter::WriteBigEndian(ctx->AllocateData(2), 2); // Block length. // NTP timestamp. ByteWriter::WriteBigEndian(ctx->AllocateData(4), ctx->ntp_sec); ByteWriter::WriteBigEndian(ctx->AllocateData(4), ctx->ntp_frac); return BuildResult::kSuccess; } RTCPSender::BuildResult RTCPSender::BuildDlrr(RtcpContext* ctx) { const int kDlrrBlockLength = 24; if (ctx->position + kDlrrBlockLength >= IP_PACKET_SIZE) return BuildResult::kTruncated; // Add XR header. *ctx->AllocateData(1) = 0x80; *ctx->AllocateData(1) = 207; ByteWriter::WriteBigEndian(ctx->AllocateData(2), 5); // XR packet length. // Add our own SSRC. ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // 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. *ctx->AllocateData(1) = 5; // BT. *ctx->AllocateData(1) = 0; // Reserved. ByteWriter::WriteBigEndian(ctx->AllocateData(2), 3); // Block length. // NTP timestamp. const RtcpReceiveTimeInfo& info = ctx->feedback_state.last_xr_rr; ByteWriter::WriteBigEndian(ctx->AllocateData(4), info.sourceSSRC); ByteWriter::WriteBigEndian(ctx->AllocateData(4), info.lastRR); ByteWriter::WriteBigEndian(ctx->AllocateData(4), info.delaySinceLastRR); return BuildResult::kSuccess; } // TODO(sprang): Add a unit test for this, or remove if the code isn't used. RTCPSender::BuildResult RTCPSender::BuildVoIPMetric(RtcpContext* ctx) { // sanity if (ctx->position + 44 >= IP_PACKET_SIZE) return BuildResult::kTruncated; // Add XR header *ctx->AllocateData(1) = 0x80; *ctx->AllocateData(1) = 207; uint32_t XRLengthPos = ctx->position; // handle length later on ctx->AllocateData(2); // Add our own SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), ssrc_); // Add a VoIP metrics block *ctx->AllocateData(1) = 7; *ctx->AllocateData(1) = 0; ByteWriter::WriteBigEndian(ctx->AllocateData(2), 8); // Add the remote SSRC ByteWriter::WriteBigEndian(ctx->AllocateData(4), remote_ssrc_); *ctx->AllocateData(1) = xr_voip_metric_.lossRate; *ctx->AllocateData(1) = xr_voip_metric_.discardRate; *ctx->AllocateData(1) = xr_voip_metric_.burstDensity; *ctx->AllocateData(1) = xr_voip_metric_.gapDensity; ByteWriter::WriteBigEndian(ctx->AllocateData(2), xr_voip_metric_.burstDuration); ByteWriter::WriteBigEndian(ctx->AllocateData(2), xr_voip_metric_.gapDuration); ByteWriter::WriteBigEndian(ctx->AllocateData(2), xr_voip_metric_.roundTripDelay); ByteWriter::WriteBigEndian(ctx->AllocateData(2), xr_voip_metric_.endSystemDelay); *ctx->AllocateData(1) = xr_voip_metric_.signalLevel; *ctx->AllocateData(1) = xr_voip_metric_.noiseLevel; *ctx->AllocateData(1) = xr_voip_metric_.RERL; *ctx->AllocateData(1) = xr_voip_metric_.Gmin; *ctx->AllocateData(1) = xr_voip_metric_.Rfactor; *ctx->AllocateData(1) = xr_voip_metric_.extRfactor; *ctx->AllocateData(1) = xr_voip_metric_.MOSLQ; *ctx->AllocateData(1) = xr_voip_metric_.MOSCQ; *ctx->AllocateData(1) = xr_voip_metric_.RXconfig; *ctx->AllocateData(1) = 0; // reserved ByteWriter::WriteBigEndian(ctx->AllocateData(2), xr_voip_metric_.JBnominal); ByteWriter::WriteBigEndian(ctx->AllocateData(2), xr_voip_metric_.JBmax); ByteWriter::WriteBigEndian(ctx->AllocateData(2), xr_voip_metric_.JBabsMax); ByteWriter::WriteBigEndian(&ctx->buffer[XRLengthPos], 10); return BuildResult::kSuccess; } int32_t RTCPSender::SendRTCP(const FeedbackState& feedback_state, RTCPPacketType packetType, int32_t nack_size, const uint16_t* nack_list, bool repeat, uint64_t pictureID) { return SendCompoundRTCP( feedback_state, std::set(&packetType, &packetType + 1), nack_size, nack_list, repeat, pictureID); } int32_t RTCPSender::SendCompoundRTCP( const FeedbackState& feedback_state, const std::set& packetTypes, int32_t nack_size, const uint16_t* nack_list, bool repeat, uint64_t pictureID) { { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); 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, packetTypes, nack_size, nack_list, repeat, pictureID, rtcp_buffer, IP_PACKET_SIZE); // Sanity don't send empty packets. if (rtcp_length <= 0) return -1; return SendToNetwork(rtcp_buffer, static_cast(rtcp_length)); } int RTCPSender::PrepareRTCP(const FeedbackState& feedback_state, const std::set& packetTypes, int32_t nack_size, const uint16_t* nack_list, bool repeat, uint64_t pictureID, uint8_t* rtcp_buffer, int buffer_size) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); RtcpContext context(feedback_state, nack_size, nack_list, repeat, pictureID, rtcp_buffer, buffer_size); // Add all flags as volatile. Non volatile entries will not be overwritten // and all new volatile flags added will be consumed by the end of this call. SetFlags(packetTypes, true); if (packet_type_counter_.first_packet_time_ms == -1) packet_type_counter_.first_packet_time_ms = clock_->TimeInMilliseconds(); bool generate_report; if (IsFlagPresent(kRtcpSr) || IsFlagPresent(kRtcpRr)) { // Report type already explicitly set, don't automatically populate. generate_report = true; DCHECK(ConsumeFlag(kRtcpReport) == false); } else { generate_report = (ConsumeFlag(kRtcpReport) && method_ == kRtcpNonCompound) || method_ == kRtcpCompound; if (generate_report) SetFlag(sending_ ? kRtcpSr : kRtcpRr, true); } if (IsFlagPresent(kRtcpSr) || (IsFlagPresent(kRtcpRr) && !cname_.empty())) SetFlag(kRtcpSdes, true); // We need to send our NTP even if we haven't received any reports. clock_->CurrentNtp(context.ntp_sec, context.ntp_frac); if (generate_report) { if (!sending_ && xr_send_receiver_reference_time_enabled_) SetFlag(kRtcpXrReceiverReferenceTime, true); if (feedback_state.has_last_xr_rr) SetFlag(kRtcpXrDlrrReportBlock, true); // generate next time to send an 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); } next_time_to_send_rtcp_ = clock_->TimeInMilliseconds() + timeToNext; StatisticianMap statisticians = receive_statistics_->GetActiveStatisticians(); if (!statisticians.empty()) { for (auto it = statisticians.begin(); it != statisticians.end(); ++it) { RTCPReportBlock report_block; if (PrepareReport(feedback_state, it->first, it->second, &report_block)) { AddReportBlock(report_block); } } } } auto it = report_flags_.begin(); while (it != report_flags_.end()) { auto builder = builders_.find(it->type); DCHECK(builder != builders_.end()); if (it->is_volatile) { report_flags_.erase(it++); } else { ++it; } uint32_t start_position = context.position; BuildResult result = (this->*(builder->second))(&context); switch (result) { case BuildResult::kError: return -1; case BuildResult::kTruncated: return context.position; case BuildResult::kAborted: context.position = start_position; FALLTHROUGH(); case BuildResult::kSuccess: continue; default: abort(); } } if (packet_type_counter_observer_ != NULL) { packet_type_counter_observer_->RtcpPacketTypesCounterUpdated( remote_ssrc_, packet_type_counter_); } DCHECK(AllVolatileFlagsConsumed()); return context.position; } bool RTCPSender::PrepareReport(const FeedbackState& feedback_state, uint32_t ssrc, StreamStatistician* statistician, RTCPReportBlock* report_block) { // 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; report_block->remoteSSRC = ssrc; // TODO(sprang): Do we really need separate time stamps for each report? // Get our NTP as late as possible to avoid a race. uint32_t ntp_secs; uint32_t ntp_frac; 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 highest 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, size_t length) { CriticalSectionScoped lock(critical_section_transport_.get()); if (cbTransport_) { if (cbTransport_->SendRTCPPacket(id_, dataBuffer, length) > 0) return 0; } return -1; } void RTCPSender::SetCsrcs(const std::vector& csrcs) { assert(csrcs.size() <= kRtpCsrcSize); CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); csrcs_ = csrcs; } int32_t RTCPSender::SetApplicationSpecificData(uint8_t subType, uint32_t name, const uint8_t* data, uint16_t length) { if (length % 4 != 0) { LOG(LS_ERROR) << "Failed to SetApplicationSpecificData."; return -1; } CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); SetFlag(kRtcpApp, true); app_sub_type_ = subType; app_name_ = name; app_data_.reset(new uint8_t[length]); app_length_ = length; memcpy(app_data_.get(), data, length); return 0; } int32_t RTCPSender::SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); memcpy(&xr_voip_metric_, VoIPMetric, sizeof(RTCPVoIPMetric)); SetFlag(kRtcpXrVoipMetric, true); return 0; } void RTCPSender::SendRtcpXrReceiverReferenceTime(bool enable) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); xr_send_receiver_reference_time_enabled_ = enable; } bool RTCPSender::RtcpXrReceiverReferenceTime() const { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); return xr_send_receiver_reference_time_enabled_; } // no callbacks allowed inside this function int32_t RTCPSender::SetTMMBN(const TMMBRSet* boundingSet, uint32_t maxBitrateKbit) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); if (0 == tmmbr_help_.SetTMMBRBoundingSetToSend(boundingSet, maxBitrateKbit)) { SetFlag(kRtcpTmmbn, true); return 0; } return -1; } void RTCPSender::SetFlag(RTCPPacketType type, bool is_volatile) { report_flags_.insert(ReportFlag(type, is_volatile)); } void RTCPSender::SetFlags(const std::set& types, bool is_volatile) { for (RTCPPacketType type : types) SetFlag(type, is_volatile); } bool RTCPSender::IsFlagPresent(RTCPPacketType type) const { return report_flags_.find(ReportFlag(type, false)) != report_flags_.end(); } bool RTCPSender::ConsumeFlag(RTCPPacketType type, bool forced) { auto it = report_flags_.find(ReportFlag(type, false)); if (it == report_flags_.end()) return false; if (it->is_volatile || forced) report_flags_.erase((it)); return true; } bool RTCPSender::AllVolatileFlagsConsumed() const { for (const ReportFlag& flag : report_flags_) { if (flag.is_volatile) return false; } return true; } } // namespace webrtc