diff --git a/webrtc/modules/rtp_rtcp/include/rtp_rtcp.h b/webrtc/modules/rtp_rtcp/include/rtp_rtcp.h index bb9e1f2959..b0f50755dd 100644 --- a/webrtc/modules/rtp_rtcp/include/rtp_rtcp.h +++ b/webrtc/modules/rtp_rtcp/include/rtp_rtcp.h @@ -424,6 +424,8 @@ class RtpRtcp : public Module { // BWE feedback packets. virtual bool SendFeedbackPacket(const rtcp::TransportFeedback& packet) = 0; + virtual void SetVideoBitrateAllocation(const BitrateAllocation& bitrate) = 0; + // ************************************************************************** // Audio // ************************************************************************** diff --git a/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h b/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h index a8128ea497..567fa0bc25 100644 --- a/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h +++ b/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h @@ -102,6 +102,7 @@ enum RTCPPacketType : uint32_t { kRtcpXrReceiverReferenceTime = 0x40000, kRtcpXrDlrrReportBlock = 0x80000, kRtcpTransportFeedback = 0x100000, + kRtcpXrTargetBitrate = 0x200000 }; enum KeyFrameRequestMethod { kKeyFrameReqPliRtcp, kKeyFrameReqFirRtcp }; diff --git a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index 37ead5ba72..0e81e4dc6e 100644 --- a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -204,6 +204,7 @@ class MockRtpRtcp : public RtpRtcp { void(StreamDataCountersCallback*)); MOCK_CONST_METHOD0(GetSendChannelRtpStatisticsCallback, StreamDataCountersCallback*(void)); + MOCK_METHOD1(SetVideoBitrateAllocation, void(const BitrateAllocation&)); // Members. unsigned int remote_ssrc_; }; diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc index 2f4d393b83..c47e060454 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc @@ -42,6 +42,12 @@ namespace webrtc { +namespace { +const uint32_t kRtcpAnyExtendedReports = + kRtcpXrVoipMetric | kRtcpXrReceiverReferenceTime | kRtcpXrDlrrReportBlock | + kRtcpXrTargetBitrate; +} // namespace + NACKStringBuilder::NACKStringBuilder() : stream_(""), count_(0), prevNack_(0), consecutive_(false) {} @@ -197,10 +203,7 @@ RTCPSender::RTCPSender( builders_[kRtcpTmmbr] = &RTCPSender::BuildTMMBR; builders_[kRtcpTmmbn] = &RTCPSender::BuildTMMBN; builders_[kRtcpNack] = &RTCPSender::BuildNACK; - builders_[kRtcpXrVoipMetric] = &RTCPSender::BuildVoIPMetric; - builders_[kRtcpXrReceiverReferenceTime] = - &RTCPSender::BuildReceiverReferenceTime; - builders_[kRtcpXrDlrrReportBlock] = &RTCPSender::BuildDlrr; + builders_[kRtcpAnyExtendedReports] = &RTCPSender::BuildExtendedReports; } RTCPSender::~RTCPSender() {} @@ -692,44 +695,47 @@ std::unique_ptr RTCPSender::BuildBYE(const RtcpContext& ctx) { return std::unique_ptr(bye); } -std::unique_ptr RTCPSender::BuildReceiverReferenceTime( +std::unique_ptr RTCPSender::BuildExtendedReports( const RtcpContext& ctx) { - - rtcp::ExtendedReports* xr = new rtcp::ExtendedReports(); + std::unique_ptr xr(new rtcp::ExtendedReports()); xr->SetSenderSsrc(ssrc_); - rtcp::Rrtr rrtr; - rrtr.SetNtp(NtpTime(ctx.ntp_sec_, ctx.ntp_frac_)); + if (!sending_ && xr_send_receiver_reference_time_enabled_) { + rtcp::Rrtr rrtr; + rrtr.SetNtp(NtpTime(ctx.ntp_sec_, ctx.ntp_frac_)); + xr->SetRrtr(rrtr); + } - xr->SetRrtr(rrtr); + if (ctx.feedback_state_.has_last_xr_rr) { + xr->AddDlrrItem(ctx.feedback_state_.last_xr_rr); + } - // TODO(sprang): Merge XR report sending to contain all of RRTR, DLRR, VOIP? + if (video_bitrate_allocation_) { + rtcp::TargetBitrate target_bitrate; - return std::unique_ptr(xr); -} + for (int sl = 0; sl < kMaxSpatialLayers; ++sl) { + for (int tl = 0; tl < kMaxTemporalStreams; ++tl) { + uint32_t layer_bitrate_bps = + video_bitrate_allocation_->GetBitrate(sl, tl); + if (layer_bitrate_bps > 0) + target_bitrate.AddTargetBitrate(sl, tl, layer_bitrate_bps / 1000); + } + } -std::unique_ptr RTCPSender::BuildDlrr( - const RtcpContext& ctx) { - rtcp::ExtendedReports* xr = new rtcp::ExtendedReports(); - xr->SetSenderSsrc(ssrc_); - RTC_DCHECK(ctx.feedback_state_.has_last_xr_rr); - xr->AddDlrrItem(ctx.feedback_state_.last_xr_rr); + xr->SetTargetBitrate(target_bitrate); + video_bitrate_allocation_.reset(); + } - return std::unique_ptr(xr); -} + if (xr_voip_metric_) { + rtcp::VoipMetric voip; + voip.SetMediaSsrc(remote_ssrc_); + voip.SetVoipMetric(*xr_voip_metric_); + xr_voip_metric_.reset(); -std::unique_ptr RTCPSender::BuildVoIPMetric( - const RtcpContext& context) { - rtcp::ExtendedReports* xr = new rtcp::ExtendedReports(); - xr->SetSenderSsrc(ssrc_); + xr->SetVoipMetric(voip); + } - rtcp::VoipMetric voip; - voip.SetMediaSsrc(remote_ssrc_); - voip.SetVoipMetric(xr_voip_metric_); - - xr->SetVoipMetric(voip); - - return std::unique_ptr(xr); + return std::move(xr); } int32_t RTCPSender::SendRTCP(const FeedbackState& feedback_state, @@ -794,7 +800,8 @@ int32_t RTCPSender::SendCompoundRTCP( auto it = report_flags_.begin(); while (it != report_flags_.end()) { auto builder_it = builders_.find(it->type); - RTC_DCHECK(builder_it != builders_.end()); + RTC_DCHECK(builder_it != builders_.end()) + << "Could not find builder for packet type " << it->type; if (it->is_volatile) { report_flags_.erase(it++); } else { @@ -849,10 +856,10 @@ void RTCPSender::PrepareReport(const FeedbackState& feedback_state) { 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); + if ((!sending_ && xr_send_receiver_reference_time_enabled_) || + feedback_state.has_last_xr_rr || video_bitrate_allocation_) { + SetFlag(kRtcpAnyExtendedReports, true); + } // generate next time to send an RTCP report uint32_t minIntervalMs = RTCP_INTERVAL_AUDIO_MS; @@ -959,9 +966,9 @@ int32_t RTCPSender::SetApplicationSpecificData(uint8_t subType, int32_t RTCPSender::SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric) { rtc::CritScope lock(&critical_section_rtcp_sender_); - memcpy(&xr_voip_metric_, VoIPMetric, sizeof(RTCPVoIPMetric)); + xr_voip_metric_.emplace(*VoIPMetric); - SetFlag(kRtcpXrVoipMetric, true); + SetFlag(kRtcpAnyExtendedReports, true); return 0; } @@ -981,8 +988,12 @@ void RTCPSender::SetTmmbn(std::vector bounding_set) { SetFlag(kRtcpTmmbn, true); } -void RTCPSender::SetFlag(RTCPPacketType type, bool is_volatile) { - report_flags_.insert(ReportFlag(type, is_volatile)); +void RTCPSender::SetFlag(uint32_t type, bool is_volatile) { + if (type & kRtcpAnyExtendedReports) { + report_flags_.insert(ReportFlag(kRtcpAnyExtendedReports, is_volatile)); + } else { + report_flags_.insert(ReportFlag(type, is_volatile)); + } } void RTCPSender::SetFlags(const std::set& types, @@ -991,11 +1002,11 @@ void RTCPSender::SetFlags(const std::set& types, SetFlag(type, is_volatile); } -bool RTCPSender::IsFlagPresent(RTCPPacketType type) const { +bool RTCPSender::IsFlagPresent(uint32_t type) const { return report_flags_.find(ReportFlag(type, false)) != report_flags_.end(); } -bool RTCPSender::ConsumeFlag(RTCPPacketType type, bool forced) { +bool RTCPSender::ConsumeFlag(uint32_t type, bool forced) { auto it = report_flags_.find(ReportFlag(type, false)); if (it == report_flags_.end()) return false; @@ -1012,6 +1023,12 @@ bool RTCPSender::AllVolatileFlagsConsumed() const { return true; } +void RTCPSender::SetVideoBitrateAllocation(const BitrateAllocation& bitrate) { + rtc::CritScope lock(&critical_section_rtcp_sender_); + video_bitrate_allocation_.emplace(bitrate); + SetFlag(kRtcpAnyExtendedReports, true); +} + bool RTCPSender::SendFeedbackPacket(const rtcp::TransportFeedback& packet) { class Sender : public rtcp::RtcpPacket::PacketReadyCallback { public: diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_sender.h b/webrtc/modules/rtp_rtcp/source/rtcp_sender.h index f59676685d..a8bd275eea 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_sender.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_sender.h @@ -21,6 +21,7 @@ #include "webrtc/api/call/transport.h" #include "webrtc/base/constructormagic.h" #include "webrtc/base/criticalsection.h" +#include "webrtc/base/optional.h" #include "webrtc/base/random.h" #include "webrtc/base/thread_annotations.h" #include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h" @@ -150,6 +151,7 @@ class RTCPSender { void SetCsrcs(const std::vector& csrcs); void SetTargetBitrate(unsigned int target_bitrate); + void SetVideoBitrateAllocation(const BitrateAllocation& bitrate); bool SendFeedbackPacket(const rtcp::TransportFeedback& packet); private: @@ -180,7 +182,8 @@ class RTCPSender { EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); std::unique_ptr BuildAPP(const RtcpContext& context) EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); - std::unique_ptr BuildVoIPMetric(const RtcpContext& context) + std::unique_ptr BuildExtendedReports( + const RtcpContext& context) EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); std::unique_ptr BuildBYE(const RtcpContext& context) EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); @@ -192,11 +195,6 @@ class RTCPSender { EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); std::unique_ptr BuildNACK(const RtcpContext& context) EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); - std::unique_ptr BuildReceiverReferenceTime( - const RtcpContext& context) - EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); - std::unique_ptr BuildDlrr(const RtcpContext& context) - EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); private: const bool audio_; @@ -257,7 +255,8 @@ class RTCPSender { GUARDED_BY(critical_section_rtcp_sender_); // XR VoIP metric - RTCPVoIPMetric xr_voip_metric_ GUARDED_BY(critical_section_rtcp_sender_); + rtc::Optional xr_voip_metric_ + GUARDED_BY(critical_section_rtcp_sender_); RtcpPacketTypeCounterObserver* const packet_type_counter_observer_; RtcpPacketTypeCounter packet_type_counter_ @@ -265,22 +264,25 @@ class RTCPSender { RTCPUtility::NackStats nack_stats_ GUARDED_BY(critical_section_rtcp_sender_); - void SetFlag(RTCPPacketType type, bool is_volatile) + rtc::Optional video_bitrate_allocation_ + GUARDED_BY(critical_section_rtcp_sender_); + + void SetFlag(uint32_t type, bool is_volatile) EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); void SetFlags(const std::set& types, bool is_volatile) EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); - bool IsFlagPresent(RTCPPacketType type) const + bool IsFlagPresent(uint32_t type) const EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); - bool ConsumeFlag(RTCPPacketType type, bool forced = false) + bool ConsumeFlag(uint32_t type, bool forced = false) EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); bool AllVolatileFlagsConsumed() const EXCLUSIVE_LOCKS_REQUIRED(critical_section_rtcp_sender_); struct ReportFlag { - ReportFlag(RTCPPacketType type, bool is_volatile) + ReportFlag(uint32_t type, bool is_volatile) : type(type), is_volatile(is_volatile) {} bool operator<(const ReportFlag& flag) const { return type < flag.type; } bool operator==(const ReportFlag& flag) const { return type == flag.type; } - const RTCPPacketType type; + const uint32_t type; const bool is_volatile; }; @@ -288,7 +290,8 @@ class RTCPSender { typedef std::unique_ptr (RTCPSender::*BuilderFunc)( const RtcpContext&); - std::map builders_; + // Map from RTCPPacketType to builder. + std::map builders_; RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RTCPSender); }; diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc index a09d67ad48..8346ad4592 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc @@ -821,4 +821,39 @@ TEST_F(RtcpSenderTest, ByeMustBeLast) { EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state(), kRtcpBye)); } +TEST_F(RtcpSenderTest, SendXrWithTargetBitrate) { + rtcp_sender_->SetRTCPStatus(RtcpMode::kCompound); + const size_t kNumSpatialLayers = 2; + const size_t kNumTemporalLayers = 2; + BitrateAllocation allocation; + for (size_t sl = 0; sl < kNumSpatialLayers; ++sl) { + uint32_t start_bitrate_bps = (sl + 1) * 100000; + for (size_t tl = 0; tl < kNumTemporalLayers; ++tl) + allocation.SetBitrate(sl, tl, start_bitrate_bps + (tl * 20000)); + } + rtcp_sender_->SetVideoBitrateAllocation(allocation); + + EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state(), kRtcpReport)); + EXPECT_EQ(1, parser()->xr()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser()->xr()->sender_ssrc()); + const rtc::Optional& target_bitrate = + parser()->xr()->target_bitrate(); + ASSERT_TRUE(target_bitrate); + const std::vector& bitrates = + target_bitrate->GetTargetBitrates(); + EXPECT_EQ(kNumSpatialLayers * kNumTemporalLayers, bitrates.size()); + + for (size_t sl = 0; sl < kNumSpatialLayers; ++sl) { + uint32_t start_bitrate_bps = (sl + 1) * 100000; + for (size_t tl = 0; tl < kNumTemporalLayers; ++tl) { + size_t index = (sl * kNumSpatialLayers) + tl; + const rtcp::TargetBitrate::BitrateItem& item = bitrates[index]; + EXPECT_EQ(sl, item.spatial_layer); + EXPECT_EQ(tl, item.temporal_layer); + EXPECT_EQ(start_bitrate_bps + (tl * 20000), + item.target_bitrate_kbps * 1000); + } + } +} + } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 339e4f0d56..b80c8daf8b 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -953,4 +953,9 @@ StreamDataCountersCallback* ModuleRtpRtcpImpl::GetSendChannelRtpStatisticsCallback() const { return rtp_sender_.GetRtpStatisticsCallback(); } + +void ModuleRtpRtcpImpl::SetVideoBitrateAllocation( + const BitrateAllocation& bitrate) { + rtcp_sender_.SetVideoBitrateAllocation(bitrate); +} } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h index 4808a4e7ea..bd978d05b5 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -313,6 +313,8 @@ class ModuleRtpRtcpImpl : public RtpRtcp, public RTCPReceiver::ModuleRtpRtcp { const ReportBlockList& report_blocks) override; void OnRequestSendReport() override; + void SetVideoBitrateAllocation(const BitrateAllocation& bitrate) override; + protected: bool UpdateRTCPReceiveInformationTimers();