/* * 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 // memcpy #include // min #include // max #include #include "webrtc/base/checks.h" #include "webrtc/base/logging.h" #include "webrtc/base/trace_event.h" #include "webrtc/common_types.h" #include "webrtc/modules/rtp_rtcp/source/byte_io.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/app.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/pli.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" #include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h" #include "webrtc/system_wrappers/include/critical_section_wrapper.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) {} class PacketContainer : public rtcp::Empty, public rtcp::RtcpPacket::PacketReadyCallback { public: explicit PacketContainer(Transport* transport) : transport_(transport), bytes_sent_(0) {} virtual ~PacketContainer() { for (RtcpPacket* packet : appended_packets_) delete packet; } void OnPacketReady(uint8_t* data, size_t length) override { if (transport_->SendRtcp(data, length)) bytes_sent_ += length; } size_t SendPackets() { rtcp::Empty::Build(this); return bytes_sent_; } private: Transport* transport_; size_t bytes_sent_; }; class RTCPSender::RtcpContext { public: RtcpContext(const FeedbackState& feedback_state, int32_t nack_size, const uint16_t* nack_list, bool repeat, uint64_t picture_id, uint32_t ntp_sec, uint32_t ntp_frac, PacketContainer* container) : feedback_state_(feedback_state), nack_size_(nack_size), nack_list_(nack_list), repeat_(repeat), picture_id_(picture_id), ntp_sec_(ntp_sec), ntp_frac_(ntp_frac), container_(container) {} virtual ~RtcpContext() {} const FeedbackState& feedback_state_; const int32_t nack_size_; const uint16_t* nack_list_; const bool repeat_; const uint64_t picture_id_; const uint32_t ntp_sec_; const uint32_t ntp_frac_; PacketContainer* const container_; }; RTCPSender::RTCPSender( bool audio, Clock* clock, ReceiveStatistics* receive_statistics, RtcpPacketTypeCounterObserver* packet_type_counter_observer, Transport* outgoing_transport) : audio_(audio), clock_(clock), random_(clock_->TimeInMicroseconds()), method_(RtcpMode::kOff), transport_(outgoing_transport), 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_)); RTC_DCHECK(transport_ != nullptr); 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() {} RtcpMode RTCPSender::Status() const { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); return method_; } void RTCPSender::SetRTCPStatus(RtcpMode method) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); method_ = method; if (method == RtcpMode::kOff) 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_ != RtcpMode::kOff) { 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; RTC_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); RTC_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_ == RtcpMode::kOff) 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); if (!block->WithCumulativeLost(report_block.cumulativeLost)) { LOG(LS_WARNING) << "Cumulative lost is oversized."; return -1; } block->WithExtHighestSeqNum(report_block.extendedHighSeqNum); block->WithJitter(report_block.jitter); block->WithLastSr(report_block.lastSR); block->WithDelayLastSr(report_block.delaySinceLastSR); return 0; } rtc::scoped_ptr RTCPSender::BuildSR(const 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 = new rtcp::SenderReport(); 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); report_blocks_.clear(); return rtc::scoped_ptr(report); } rtc::scoped_ptr RTCPSender::BuildSDES( const RtcpContext& ctx) { size_t length_cname = cname_.length(); RTC_CHECK_LT(length_cname, static_cast(RTCP_CNAME_SIZE)); rtcp::Sdes* sdes = new rtcp::Sdes(); sdes->WithCName(ssrc_, cname_); for (const auto it : csrc_cnames_) sdes->WithCName(it.first, it.second); return rtc::scoped_ptr(sdes); } rtc::scoped_ptr RTCPSender::BuildRR(const RtcpContext& ctx) { rtcp::ReceiverReport* report = new rtcp::ReceiverReport(); report->From(ssrc_); for (auto it : report_blocks_) report->WithReportBlock(it.second); report_blocks_.clear(); return rtc::scoped_ptr(report); } rtc::scoped_ptr RTCPSender::BuildPLI(const RtcpContext& ctx) { rtcp::Pli* pli = new rtcp::Pli(); pli->From(ssrc_); pli->To(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 rtc::scoped_ptr(pli); } rtc::scoped_ptr RTCPSender::BuildFIR(const RtcpContext& ctx) { if (!ctx.repeat_) ++sequence_number_fir_; // Do not increase if repetition. rtcp::Fir* fir = new rtcp::Fir(); fir->From(ssrc_); fir->To(remote_ssrc_); fir->WithCommandSeqNum(sequence_number_fir_); 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 rtc::scoped_ptr(fir); } /* 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 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ rtc::scoped_ptr RTCPSender::BuildSLI(const RtcpContext& ctx) { rtcp::Sli* sli = new rtcp::Sli(); sli->From(ssrc_); sli->To(remote_ssrc_); // Crop picture id to 6 least significant bits. sli->WithPictureId(ctx.picture_id_ & 0x3F); sli->WithFirstMb(0); sli->WithNumberOfMb(0x1FFF); // 13 bits, only ones for now. return rtc::scoped_ptr(sli); } /* 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 */ rtc::scoped_ptr RTCPSender::BuildRPSI( const RtcpContext& ctx) { if (ctx.feedback_state_.send_payload_type == 0xFF) return nullptr; rtcp::Rpsi* rpsi = new rtcp::Rpsi(); rpsi->From(ssrc_); rpsi->To(remote_ssrc_); rpsi->WithPayloadType(ctx.feedback_state_.send_payload_type); rpsi->WithPictureId(ctx.picture_id_); return rtc::scoped_ptr(rpsi); } rtc::scoped_ptr RTCPSender::BuildREMB( const RtcpContext& ctx) { rtcp::Remb* remb = new rtcp::Remb(); remb->From(ssrc_); for (uint32_t ssrc : remb_ssrcs_) remb->AppliesTo(ssrc); remb->WithBitrateBps(remb_bitrate_); TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "RTCPSender::REMB"); return rtc::scoped_ptr(remb); } void RTCPSender::SetTargetBitrate(unsigned int target_bitrate) { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); tmmbr_send_ = target_bitrate / 1000; } rtc::scoped_ptr RTCPSender::BuildTMMBR( const RtcpContext& ctx) { if (ctx.feedback_state_.module == nullptr) return nullptr; // 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 nullptr; } } 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 = nullptr; 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 nullptr; } } } if (!tmmbr_send_) return nullptr; rtcp::Tmmbr* tmmbr = new rtcp::Tmmbr(); tmmbr->From(ssrc_); tmmbr->To(remote_ssrc_); tmmbr->WithBitrateKbps(tmmbr_send_); tmmbr->WithOverhead(packet_oh_send_); return rtc::scoped_ptr(tmmbr); } rtc::scoped_ptr RTCPSender::BuildTMMBN( const RtcpContext& ctx) { TMMBRSet* boundingSet = tmmbr_help_.BoundingSetToSend(); if (boundingSet == nullptr) return nullptr; rtcp::Tmmbn* tmmbn = new rtcp::Tmmbn(); tmmbn->From(ssrc_); for (uint32_t i = 0; i < boundingSet->lengthOfSet(); i++) { if (boundingSet->Tmmbr(i) > 0) { tmmbn->WithTmmbr(boundingSet->Ssrc(i), boundingSet->Tmmbr(i), boundingSet->PacketOH(i)); } } return rtc::scoped_ptr(tmmbn); } rtc::scoped_ptr RTCPSender::BuildAPP(const RtcpContext& ctx) { rtcp::App* app = new rtcp::App(); app->From(ssrc_); app->WithSubType(app_sub_type_); app->WithName(app_name_); app->WithData(app_data_.get(), app_length_); return rtc::scoped_ptr(app); } rtc::scoped_ptr RTCPSender::BuildNACK( const RtcpContext& ctx) { rtcp::Nack* nack = new rtcp::Nack(); nack->From(ssrc_); nack->To(remote_ssrc_); nack->WithList(ctx.nack_list_, ctx.nack_size_); // Report stats. NACKStringBuilder stringBuilder; for (int idx = 0; idx < ctx.nack_size_; ++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 rtc::scoped_ptr(nack); } rtc::scoped_ptr RTCPSender::BuildBYE(const RtcpContext& ctx) { rtcp::Bye* bye = new rtcp::Bye(); bye->From(ssrc_); for (uint32_t csrc : csrcs_) bye->WithCsrc(csrc); return rtc::scoped_ptr(bye); } rtc::scoped_ptr RTCPSender::BuildReceiverReferenceTime( const RtcpContext& ctx) { 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_))); rtcp::Xr* xr = new rtcp::Xr(); xr->From(ssrc_); rtcp::Rrtr rrtr; rrtr.WithNtp(NtpTime(ctx.ntp_sec_, ctx.ntp_frac_)); xr->WithRrtr(&rrtr); // TODO(sprang): Merge XR report sending to contain all of RRTR, DLRR, VOIP? return rtc::scoped_ptr(xr); } rtc::scoped_ptr RTCPSender::BuildDlrr( const RtcpContext& ctx) { rtcp::Xr* xr = new rtcp::Xr(); xr->From(ssrc_); rtcp::Dlrr dlrr; const RtcpReceiveTimeInfo& info = ctx.feedback_state_.last_xr_rr; dlrr.WithDlrrItem(info.sourceSSRC, info.lastRR, info.delaySinceLastRR); xr->WithDlrr(&dlrr); return rtc::scoped_ptr(xr); } // TODO(sprang): Add a unit test for this, or remove if the code isn't used. rtc::scoped_ptr RTCPSender::BuildVoIPMetric( const RtcpContext& context) { rtcp::Xr* xr = new rtcp::Xr(); xr->From(ssrc_); rtcp::VoipMetric voip; voip.To(remote_ssrc_); voip.WithVoipMetric(xr_voip_metric_); xr->WithVoipMetric(&voip); return rtc::scoped_ptr(xr); } 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& packet_types, int32_t nack_size, const uint16_t* nack_list, bool repeat, uint64_t pictureID) { PacketContainer container(transport_); { CriticalSectionScoped lock(critical_section_rtcp_sender_.get()); if (method_ == RtcpMode::kOff) { LOG(LS_WARNING) << "Can't send rtcp if it is disabled."; return -1; } // We need to send our NTP even if we haven't received any reports. uint32_t ntp_sec; uint32_t ntp_frac; clock_->CurrentNtp(ntp_sec, ntp_frac); RtcpContext context(feedback_state, nack_size, nack_list, repeat, pictureID, ntp_sec, ntp_frac, &container); PrepareReport(packet_types, feedback_state); auto it = report_flags_.begin(); while (it != report_flags_.end()) { auto builder_it = builders_.find(it->type); RTC_DCHECK(builder_it != builders_.end()); if (it->is_volatile) { report_flags_.erase(it++); } else { ++it; } BuilderFunc func = builder_it->second; rtc::scoped_ptr packet = (this->*func)(context); if (packet.get() == nullptr) return -1; container.Append(packet.release()); } if (packet_type_counter_observer_ != nullptr) { packet_type_counter_observer_->RtcpPacketTypesCounterUpdated( remote_ssrc_, packet_type_counter_); } RTC_DCHECK(AllVolatileFlagsConsumed()); } size_t bytes_sent = container.SendPackets(); return bytes_sent == 0 ? -1 : 0; } void RTCPSender::PrepareReport(const std::set& packetTypes, const FeedbackState& feedback_state) { // 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; RTC_DCHECK(ConsumeFlag(kRtcpReport) == false); } else { generate_report = (ConsumeFlag(kRtcpReport) && method_ == RtcpMode::kReducedSize) || method_ == RtcpMode::kCompound; if (generate_report) SetFlag(sending_ ? kRtcpSr : kRtcpRr, true); } if (IsFlagPresent(kRtcpSr) || (IsFlagPresent(kRtcpRr) && !cname_.empty())) SetFlag(kRtcpSdes, true); 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 uint32_t minIntervalMs = RTCP_INTERVAL_AUDIO_MS; if (!audio_) { 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; } // The interval between RTCP packets is varied randomly over the // range [1/2,3/2] times the calculated interval. uint32_t timeToNext = random_.Rand(minIntervalMs * 1 / 2, minIntervalMs * 3 / 2); 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 (PrepareReportBlock(feedback_state, it->first, it->second, &report_block)) { // TODO(danilchap) AddReportBlock may fail (for 2 different reasons). // Probably it shouldn't be ignored. AddReportBlock(report_block); } } } } } bool RTCPSender::PrepareReportBlock(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; } 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; } bool RTCPSender::SendFeedbackPacket(const rtcp::TransportFeedback& packet) { class Sender : public rtcp::RtcpPacket::PacketReadyCallback { public: explicit Sender(Transport* transport) : transport_(transport), send_failure_(false) {} void OnPacketReady(uint8_t* data, size_t length) override { if (!transport_->SendRtcp(data, length)) send_failure_ = true; } Transport* const transport_; bool send_failure_; } sender(transport_); uint8_t buffer[IP_PACKET_SIZE]; return packet.BuildExternalBuffer(buffer, IP_PACKET_SIZE, &sender) && !sender.send_failure_; } } // namespace webrtc