From f2047874785a18bc2653ea8ab119945142f49ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Bostr=C3=B6m?= Date: Thu, 16 May 2019 13:32:20 +0200 Subject: [PATCH] ReportBlockData and observer added, for stats collection in future CLs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ReportBlockData contains information about a ReportBlock and additional data such as RTT. This will be used for the calculation of RTCRemoteInboundRtpStreamStats, see full picture here: https://webrtc-review.googlesource.com/c/src/+/134107 ReportBlockData is a class version of the previously internal struct RTCPReceiver::ReportBlockWithRtt. - The new name makes sense even if we add more info to it, which will be needed for future metrics. - The new location is modules/rtp_rtcp/include/report_block_data.h. The RTCPReceiver allows obtaining the ReportBlockData in two ways: 1. Using a ReportBlockDataObserver that is notified on receiving a report block. 2. Using the GetLatestReportBlockData(). Both codepaths will be needed; video stats uses observers and audio stats uses polling. Further plumbing will be done in follow-up CLs. Bug: webrtc:10455, webrtc:10456 Change-Id: Ic9e5b4f451b5f4b203efcd6fa3bbf9736487e1f4 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/136584 Commit-Queue: Henrik Boström Reviewed-by: Danil Chapovalov Cr-Commit-Position: refs/heads/master@{#27961} --- modules/rtp_rtcp/BUILD.gn | 3 + modules/rtp_rtcp/include/report_block_data.cc | 44 ++++ modules/rtp_rtcp/include/report_block_data.h | 59 +++++ modules/rtp_rtcp/source/rtcp_receiver.cc | 93 ++++---- modules/rtp_rtcp/source/rtcp_receiver.h | 17 +- .../rtp_rtcp/source/rtcp_receiver_unittest.cc | 206 +++++++++++++++++- 6 files changed, 377 insertions(+), 45 deletions(-) create mode 100644 modules/rtp_rtcp/include/report_block_data.cc create mode 100644 modules/rtp_rtcp/include/report_block_data.h diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn index cf5ef59b53..02c7207279 100644 --- a/modules/rtp_rtcp/BUILD.gn +++ b/modules/rtp_rtcp/BUILD.gn @@ -11,6 +11,7 @@ import("../../webrtc.gni") rtc_source_set("rtp_rtcp_format") { visibility = [ "*" ] public = [ + "include/report_block_data.h", "include/rtcp_statistics.h", "include/rtp_cvo.h", "include/rtp_header_extension_map.h", @@ -50,6 +51,7 @@ rtc_source_set("rtp_rtcp_format") { "source/rtp_packet_to_send.h", ] sources = [ + "include/report_block_data.cc", "include/rtp_rtcp_defines.cc", "source/rtcp_packet.cc", "source/rtcp_packet/app.cc", @@ -446,6 +448,7 @@ if (rtc_include_tests) { "../../api:scoped_refptr", "../../api:transport_api", "../../api/transport:field_trial_based_config", + "../../api/units:timestamp", "../../api/video:video_bitrate_allocation", "../../api/video:video_bitrate_allocator", "../../api/video:video_codec_constants", diff --git a/modules/rtp_rtcp/include/report_block_data.cc b/modules/rtp_rtcp/include/report_block_data.cc new file mode 100644 index 0000000000..ec4d9d82e0 --- /dev/null +++ b/modules/rtp_rtcp/include/report_block_data.cc @@ -0,0 +1,44 @@ +/* + * Copyright 2019 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 "modules/rtp_rtcp/include/report_block_data.h" + +namespace webrtc { + +ReportBlockData::ReportBlockData() + : report_block_(), + report_block_timestamp_utc_us_(0), + last_rtt_ms_(0), + min_rtt_ms_(0), + max_rtt_ms_(0), + sum_rtt_ms_(0), + num_rtts_(0) {} + +double ReportBlockData::AvgRttMs() const { + return num_rtts_ ? static_cast(sum_rtt_ms_) / num_rtts_ : 0.0; +} + +void ReportBlockData::SetReportBlock(RTCPReportBlock report_block, + int64_t report_block_timestamp_utc_us) { + report_block_ = report_block; + report_block_timestamp_utc_us_ = report_block_timestamp_utc_us; +} + +void ReportBlockData::AddRoundTripTimeSample(int64_t rtt_ms) { + if (rtt_ms > max_rtt_ms_) + max_rtt_ms_ = rtt_ms; + if (num_rtts_ == 0 || rtt_ms < min_rtt_ms_) + min_rtt_ms_ = rtt_ms; + last_rtt_ms_ = rtt_ms; + sum_rtt_ms_ += rtt_ms; + ++num_rtts_; +} + +} // namespace webrtc diff --git a/modules/rtp_rtcp/include/report_block_data.h b/modules/rtp_rtcp/include/report_block_data.h new file mode 100644 index 0000000000..2c4533ada8 --- /dev/null +++ b/modules/rtp_rtcp/include/report_block_data.h @@ -0,0 +1,59 @@ +/* + * Copyright 2019 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. + */ + +#ifndef MODULES_RTP_RTCP_INCLUDE_REPORT_BLOCK_DATA_H_ +#define MODULES_RTP_RTCP_INCLUDE_REPORT_BLOCK_DATA_H_ + +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" + +namespace webrtc { + +class ReportBlockData { + public: + ReportBlockData(); + + const RTCPReportBlock& report_block() const { return report_block_; } + int64_t report_block_timestamp_utc_us() const { + return report_block_timestamp_utc_us_; + } + int64_t last_rtt_ms() const { return last_rtt_ms_; } + int64_t min_rtt_ms() const { return min_rtt_ms_; } + int64_t max_rtt_ms() const { return max_rtt_ms_; } + int64_t sum_rtt_ms() const { return sum_rtt_ms_; } + size_t num_rtts() const { return num_rtts_; } + bool has_rtt() const { return num_rtts_ != 0; } + + double AvgRttMs() const; + + void SetReportBlock(RTCPReportBlock report_block, + int64_t report_block_timestamp_utc_us); + void AddRoundTripTimeSample(int64_t rtt_ms); + + private: + RTCPReportBlock report_block_; + int64_t report_block_timestamp_utc_us_; + + int64_t last_rtt_ms_; + int64_t min_rtt_ms_; + int64_t max_rtt_ms_; + int64_t sum_rtt_ms_; + size_t num_rtts_; +}; + +class ReportBlockDataObserver { + public: + virtual ~ReportBlockDataObserver() = default; + + virtual void OnReportBlockDataUpdated(ReportBlockData report_block_data) = 0; +}; + +} // namespace webrtc + +#endif // MODULES_RTP_RTCP_INCLUDE_REPORT_BLOCK_DATA_H_ diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc index 0c1c326e4f..c79143421a 100644 --- a/modules/rtp_rtcp/source/rtcp_receiver.cc +++ b/modules/rtp_rtcp/source/rtcp_receiver.cc @@ -42,6 +42,7 @@ #include "modules/rtp_rtcp/source/tmmbr_help.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" +#include "rtc_base/time_utils.h" #include "rtc_base/trace_event.h" #include "system_wrappers/include/ntp_time.h" @@ -69,7 +70,9 @@ struct RTCPReceiver::PacketInformation { uint32_t remote_ssrc = 0; std::vector nack_sequence_numbers; + // TODO(hbos): Remove |report_blocks| in favor of |report_block_datas|. ReportBlockList report_blocks; + std::vector report_block_datas; int64_t rtt_ms = 0; uint32_t receiver_estimated_max_bitrate_bps = 0; std::unique_ptr transport_feedback; @@ -108,16 +111,6 @@ struct RTCPReceiver::RrtrInformation { uint32_t local_receive_mid_ntp_time; }; -struct RTCPReceiver::ReportBlockWithRtt { - RTCPReportBlock report_block; - - int64_t last_rtt_ms = 0; - int64_t min_rtt_ms = 0; - int64_t max_rtt_ms = 0; - int64_t sum_rtt_ms = 0; - size_t num_rtts = 0; -}; - struct RTCPReceiver::LastFirStatus { LastFirStatus(int64_t now_ms, uint8_t sequence_number) : request_ms(now_ms), sequence_number(sequence_number) {} @@ -154,6 +147,7 @@ RTCPReceiver::RTCPReceiver( last_received_rb_ms_(0), last_increased_sequence_number_ms_(0), stats_callback_(nullptr), + report_block_data_observer_(nullptr), packet_type_counter_observer_(packet_type_counter_observer), num_skipped_packets_(0), last_skipped_packets_warning_ms_(clock->TimeInMilliseconds()) { @@ -213,22 +207,24 @@ int32_t RTCPReceiver::RTT(uint32_t remote_ssrc, if (it_info == it->second.end()) return -1; - const ReportBlockWithRtt* report_block = &it_info->second; + const ReportBlockData* report_block_data = &it_info->second; - if (report_block->num_rtts == 0) + if (report_block_data->num_rtts() == 0) return -1; if (last_rtt_ms) - *last_rtt_ms = report_block->last_rtt_ms; + *last_rtt_ms = report_block_data->last_rtt_ms(); - if (avg_rtt_ms) - *avg_rtt_ms = report_block->sum_rtt_ms / report_block->num_rtts; + if (avg_rtt_ms) { + *avg_rtt_ms = + report_block_data->sum_rtt_ms() / report_block_data->num_rtts(); + } if (min_rtt_ms) - *min_rtt_ms = report_block->min_rtt_ms; + *min_rtt_ms = report_block_data->min_rtt_ms(); if (max_rtt_ms) - *max_rtt_ms = report_block->max_rtt_ms; + *max_rtt_ms = report_block_data->max_rtt_ms(); return 0; } @@ -307,10 +303,19 @@ int32_t RTCPReceiver::StatisticsReceived( rtc::CritScope lock(&rtcp_receiver_lock_); for (const auto& reports_per_receiver : received_report_blocks_) for (const auto& report : reports_per_receiver.second) - receive_blocks->push_back(report.second.report_block); + receive_blocks->push_back(report.second.report_block()); return 0; } +std::vector RTCPReceiver::GetLatestReportBlockData() const { + std::vector result; + rtc::CritScope lock(&rtcp_receiver_lock_); + for (const auto& reports_per_receiver : received_report_blocks_) + for (const auto& report : reports_per_receiver.second) + result.push_back(report.second); + return result; +} + bool RTCPReceiver::ParseCompoundPacket(const uint8_t* packet_begin, const uint8_t* packet_end, PacketInformation* packet_information) { @@ -483,26 +488,26 @@ void RTCPReceiver::HandleReportBlock(const ReportBlock& report_block, last_received_rb_ms_ = clock_->TimeInMilliseconds(); - ReportBlockWithRtt* report_block_info = + ReportBlockData* report_block_data = &received_report_blocks_[report_block.source_ssrc()][remote_ssrc]; - report_block_info->report_block.sender_ssrc = remote_ssrc; - report_block_info->report_block.source_ssrc = report_block.source_ssrc(); - report_block_info->report_block.fraction_lost = report_block.fraction_lost(); - report_block_info->report_block.packets_lost = - report_block.cumulative_lost_signed(); + RTCPReportBlock rtcp_report_block; + rtcp_report_block.sender_ssrc = remote_ssrc; + rtcp_report_block.source_ssrc = report_block.source_ssrc(); + rtcp_report_block.fraction_lost = report_block.fraction_lost(); + rtcp_report_block.packets_lost = report_block.cumulative_lost_signed(); if (report_block.extended_high_seq_num() > - report_block_info->report_block.extended_highest_sequence_number) { + report_block_data->report_block().extended_highest_sequence_number) { // We have successfully delivered new RTP packets to the remote side after // the last RR was sent from the remote side. last_increased_sequence_number_ms_ = clock_->TimeInMilliseconds(); } - report_block_info->report_block.extended_highest_sequence_number = + rtcp_report_block.extended_highest_sequence_number = report_block.extended_high_seq_num(); - report_block_info->report_block.jitter = report_block.jitter(); - report_block_info->report_block.delay_since_last_sender_report = + rtcp_report_block.jitter = report_block.jitter(); + rtcp_report_block.delay_since_last_sender_report = report_block.delay_since_last_sr(); - report_block_info->report_block.last_sender_report_timestamp = - report_block.last_sr(); + rtcp_report_block.last_sender_report_timestamp = report_block.last_sr(); + report_block_data->SetReportBlock(rtcp_report_block, rtc::TimeUTCMicros()); int64_t rtt_ms = 0; uint32_t send_time_ntp = report_block.last_sr(); @@ -527,21 +532,14 @@ void RTCPReceiver::HandleReportBlock(const ReportBlock& report_block, uint32_t rtt_ntp = receive_time_ntp - delay_ntp - send_time_ntp; // Convert to 1/1000 seconds (milliseconds). rtt_ms = CompactNtpRttToMs(rtt_ntp); - if (rtt_ms > report_block_info->max_rtt_ms) - report_block_info->max_rtt_ms = rtt_ms; - - if (report_block_info->num_rtts == 0 || - rtt_ms < report_block_info->min_rtt_ms) - report_block_info->min_rtt_ms = rtt_ms; - - report_block_info->last_rtt_ms = rtt_ms; - report_block_info->sum_rtt_ms += rtt_ms; - ++report_block_info->num_rtts; + report_block_data->AddRoundTripTimeSample(rtt_ms); packet_information->rtt_ms = rtt_ms; } - packet_information->report_blocks.push_back(report_block_info->report_block); + packet_information->report_blocks.push_back( + report_block_data->report_block()); + packet_information->report_block_datas.push_back(*report_block_data); } RTCPReceiver::TmmbrInformation* RTCPReceiver::FindOrCreateTmmbrInfo( @@ -975,6 +973,12 @@ RtcpStatisticsCallback* RTCPReceiver::GetRtcpStatisticsCallback() { return stats_callback_; } +void RTCPReceiver::SetReportBlockDataObserver( + ReportBlockDataObserver* observer) { + rtc::CritScope cs(&feedbacks_lock_); + report_block_data_observer_ = observer; +} + // Holding no Critical section. void RTCPReceiver::TriggerCallbacksFromRtcpPacket( const PacketInformation& packet_information) { @@ -1085,6 +1089,13 @@ void RTCPReceiver::TriggerCallbacksFromRtcpPacket( stats_callback_->StatisticsUpdated(stats, report_block.source_ssrc); } } + if (report_block_data_observer_) { + for (const auto& report_block_data : + packet_information.report_block_datas) { + report_block_data_observer_->OnReportBlockDataUpdated( + report_block_data); + } + } } } diff --git a/modules/rtp_rtcp/source/rtcp_receiver.h b/modules/rtp_rtcp/source/rtcp_receiver.h index afc3f96773..e971c15765 100644 --- a/modules/rtp_rtcp/source/rtcp_receiver.h +++ b/modules/rtp_rtcp/source/rtcp_receiver.h @@ -17,6 +17,7 @@ #include #include +#include "modules/rtp_rtcp/include/report_block_data.h" #include "modules/rtp_rtcp/include/rtcp_statistics.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtcp_nack_stats.h" @@ -94,6 +95,11 @@ class RTCPReceiver { // Get statistics. int32_t StatisticsReceived(std::vector* receiveBlocks) const; + // A snapshot of Report Blocks with additional data of interest to statistics. + // Within this list, the sender-source SSRC pair is unique and per-pair the + // ReportBlockData represents the latest Report Block that was received for + // that pair. + std::vector GetLatestReportBlockData() const; // Returns true if we haven't received an RTCP RR for several RTCP // intervals, but only triggers true once. @@ -114,17 +120,17 @@ class RTCPReceiver { void RegisterRtcpStatisticsCallback(RtcpStatisticsCallback* callback); RtcpStatisticsCallback* GetRtcpStatisticsCallback(); + void SetReportBlockDataObserver(ReportBlockDataObserver* observer); private: struct PacketInformation; struct TmmbrInformation; struct RrtrInformation; - struct ReportBlockWithRtt; struct LastFirStatus; // RTCP report blocks mapped by remote SSRC. - using ReportBlockInfoMap = std::map; + using ReportBlockDataMap = std::map; // RTCP report blocks map mapped by source SSRC. - using ReportBlockMap = std::map; + using ReportBlockMap = std::map; bool ParseCompoundPacket(const uint8_t* packet_begin, const uint8_t* packet_end, @@ -262,6 +268,11 @@ class RTCPReceiver { int64_t last_increased_sequence_number_ms_; RtcpStatisticsCallback* stats_callback_ RTC_GUARDED_BY(feedbacks_lock_); + // TODO(hbos): Remove RtcpStatisticsCallback in favor of + // ReportBlockDataObserver; the ReportBlockData contains a superset of the + // RtcpStatistics data. + ReportBlockDataObserver* report_block_data_observer_ + RTC_GUARDED_BY(feedbacks_lock_); RtcpPacketTypeCounterObserver* const packet_type_counter_observer_; RtcpPacketTypeCounter packet_type_counter_; diff --git a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc index 332495d677..c9664703b0 100644 --- a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc @@ -11,8 +11,10 @@ #include #include "api/array_view.h" +#include "api/units/timestamp.h" #include "api/video/video_bitrate_allocation.h" #include "api/video/video_bitrate_allocator.h" +#include "modules/rtp_rtcp/include/report_block_data.h" #include "modules/rtp_rtcp/mocks/mock_rtcp_bandwidth_observer.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/rtcp_packet.h" @@ -34,6 +36,7 @@ #include "modules/rtp_rtcp/source/rtcp_receiver.h" #include "modules/rtp_rtcp/source/time_util.h" #include "rtc_base/arraysize.h" +#include "rtc_base/fake_clock.h" #include "rtc_base/random.h" #include "system_wrappers/include/ntp_time.h" #include "test/gmock.h" @@ -42,10 +45,12 @@ namespace webrtc { namespace { +using rtcp::ReceiveTimeInfo; using ::testing::_; using ::testing::AllOf; using ::testing::ElementsAreArray; using ::testing::Field; +using ::testing::InSequence; using ::testing::IsEmpty; using ::testing::NiceMock; using ::testing::Property; @@ -53,7 +58,6 @@ using ::testing::SizeIs; using ::testing::StrEq; using ::testing::StrictMock; using ::testing::UnorderedElementsAre; -using rtcp::ReceiveTimeInfo; class MockRtcpPacketTypeCounterObserver : public RtcpPacketTypeCounterObserver { public: @@ -82,6 +86,11 @@ class MockRtcpCallbackImpl : public RtcpStatisticsCallback { MOCK_METHOD2(CNameChanged, void(const char*, uint32_t)); }; +class MockReportBlockDataObserverImpl : public ReportBlockDataObserver { + public: + MOCK_METHOD1(OnReportBlockDataUpdated, void(ReportBlockData)); +}; + class MockTransportFeedbackObserver : public TransportFeedbackObserver { public: MOCK_METHOD1(OnAddPacket, void(const RtpPacketSendInfo&)); @@ -1176,6 +1185,201 @@ TEST_F(RtcpReceiverTest, Callbacks) { InjectRtcpPacket(rr2); } +TEST_F(RtcpReceiverTest, + VerifyBlockAndTimestampObtainedFromReportBlockDataObserver) { + MockReportBlockDataObserverImpl observer; + rtcp_receiver_.SetReportBlockDataObserver(&observer); + + const uint8_t kFractionLoss = 3; + const uint32_t kCumulativeLoss = 7; + const uint32_t kJitter = 9; + const uint16_t kSequenceNumber = 1234; + const int64_t kUtcNowUs = 42; + + // The "report_block_timestamp_utc_us" is obtained from the global UTC clock + // (not the simulcated |system_clock_|) and requires a scoped fake clock. + rtc::ScopedFakeClock fake_clock; + fake_clock.SetTime(Timestamp::us(kUtcNowUs)); + + rtcp::ReportBlock rtcp_block; + rtcp_block.SetMediaSsrc(kReceiverMainSsrc); + rtcp_block.SetExtHighestSeqNum(kSequenceNumber); + rtcp_block.SetFractionLost(kFractionLoss); + rtcp_block.SetCumulativeLost(kCumulativeLoss); + rtcp_block.SetJitter(kJitter); + + rtcp::ReceiverReport rtcp_report; + rtcp_report.SetSenderSsrc(kSenderSsrc); + rtcp_report.AddReportBlock(rtcp_block); + EXPECT_CALL(observer, OnReportBlockDataUpdated) + .WillOnce([&](ReportBlockData report_block_data) { + const auto& report_block = report_block_data.report_block(); + EXPECT_EQ(rtcp_block.source_ssrc(), report_block.source_ssrc); + EXPECT_EQ(kSenderSsrc, report_block.sender_ssrc); + EXPECT_EQ(rtcp_block.fraction_lost(), report_block.fraction_lost); + EXPECT_EQ(rtcp_block.cumulative_lost_signed(), + report_block.packets_lost); + EXPECT_EQ(rtcp_block.extended_high_seq_num(), + report_block.extended_highest_sequence_number); + EXPECT_EQ(rtcp_block.jitter(), report_block.jitter); + EXPECT_EQ(kUtcNowUs, report_block_data.report_block_timestamp_utc_us()); + // No RTT is calculated in this test. + EXPECT_EQ(0u, report_block_data.num_rtts()); + }); + EXPECT_CALL(rtp_rtcp_impl_, OnReceivedRtcpReportBlocks); + EXPECT_CALL(bandwidth_observer_, OnReceivedRtcpReceiverReport); + InjectRtcpPacket(rtcp_report); +} + +TEST_F(RtcpReceiverTest, VerifyRttObtainedFromReportBlockDataObserver) { + MockReportBlockDataObserverImpl observer; + rtcp_receiver_.SetReportBlockDataObserver(&observer); + + const int64_t kRttMs = 120; + const uint32_t kDelayNtp = 123000; + const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + + uint32_t sent_ntp = + CompactNtp(TimeMicrosToNtp(system_clock_.TimeInMicroseconds())); + system_clock_.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + + rtcp::SenderReport sr; + sr.SetSenderSsrc(kSenderSsrc); + rtcp::ReportBlock block; + block.SetMediaSsrc(kReceiverMainSsrc); + block.SetLastSr(sent_ntp); + block.SetDelayLastSr(kDelayNtp); + sr.AddReportBlock(block); + block.SetMediaSsrc(kReceiverExtraSsrc); + block.SetLastSr(0); + sr.AddReportBlock(block); + + EXPECT_CALL(rtp_rtcp_impl_, OnReceivedRtcpReportBlocks); + EXPECT_CALL(bandwidth_observer_, OnReceivedRtcpReceiverReport); + InSequence sequence; + EXPECT_CALL(observer, OnReportBlockDataUpdated) + .WillOnce([&](ReportBlockData report_block_data) { + EXPECT_EQ(kReceiverMainSsrc, + report_block_data.report_block().source_ssrc); + EXPECT_EQ(1u, report_block_data.num_rtts()); + EXPECT_EQ(kRttMs, report_block_data.min_rtt_ms()); + EXPECT_EQ(kRttMs, report_block_data.max_rtt_ms()); + EXPECT_EQ(kRttMs, report_block_data.sum_rtt_ms()); + EXPECT_EQ(kRttMs, report_block_data.last_rtt_ms()); + }); + EXPECT_CALL(observer, OnReportBlockDataUpdated) + .WillOnce([](ReportBlockData report_block_data) { + EXPECT_EQ(kReceiverExtraSsrc, + report_block_data.report_block().source_ssrc); + EXPECT_EQ(0u, report_block_data.num_rtts()); + }); + InjectRtcpPacket(sr); +} + +TEST_F(RtcpReceiverTest, GetReportBlockDataAfterOneReportBlock) { + const uint16_t kSequenceNumber = 1234; + + rtcp::ReportBlock rtcp_block; + rtcp_block.SetMediaSsrc(kReceiverMainSsrc); + rtcp_block.SetExtHighestSeqNum(kSequenceNumber); + + rtcp::ReceiverReport rtcp_report; + rtcp_report.SetSenderSsrc(kSenderSsrc); + rtcp_report.AddReportBlock(rtcp_block); + EXPECT_CALL(rtp_rtcp_impl_, OnReceivedRtcpReportBlocks); + EXPECT_CALL(bandwidth_observer_, OnReceivedRtcpReceiverReport); + InjectRtcpPacket(rtcp_report); + + auto report_block_datas = rtcp_receiver_.GetLatestReportBlockData(); + ASSERT_THAT(report_block_datas, SizeIs(1)); + EXPECT_EQ(kReceiverMainSsrc, + report_block_datas[0].report_block().source_ssrc); + EXPECT_EQ( + kSequenceNumber, + report_block_datas[0].report_block().extended_highest_sequence_number); +} + +TEST_F(RtcpReceiverTest, GetReportBlockDataAfterTwoReportBlocksOfSameSsrc) { + const uint16_t kSequenceNumber1 = 1234; + const uint16_t kSequenceNumber2 = 1235; + + rtcp::ReportBlock rtcp_block1; + rtcp_block1.SetMediaSsrc(kReceiverMainSsrc); + rtcp_block1.SetExtHighestSeqNum(kSequenceNumber1); + + rtcp::ReceiverReport rtcp_report1; + rtcp_report1.SetSenderSsrc(kSenderSsrc); + rtcp_report1.AddReportBlock(rtcp_block1); + EXPECT_CALL(rtp_rtcp_impl_, OnReceivedRtcpReportBlocks); + EXPECT_CALL(bandwidth_observer_, OnReceivedRtcpReceiverReport); + InjectRtcpPacket(rtcp_report1); + + // Inject a report block with an increased the sequence number for the same + // source SSRC. + rtcp::ReportBlock rtcp_block2; + rtcp_block2.SetMediaSsrc(kReceiverMainSsrc); + rtcp_block2.SetExtHighestSeqNum(kSequenceNumber2); + + rtcp::ReceiverReport rtcp_report2; + rtcp_report2.SetSenderSsrc(kSenderSsrc); + rtcp_report2.AddReportBlock(rtcp_block2); + EXPECT_CALL(rtp_rtcp_impl_, OnReceivedRtcpReportBlocks); + EXPECT_CALL(bandwidth_observer_, OnReceivedRtcpReceiverReport); + InjectRtcpPacket(rtcp_report2); + + // Only the latest block should be returned. + auto report_block_datas = rtcp_receiver_.GetLatestReportBlockData(); + ASSERT_THAT(report_block_datas, SizeIs(1)); + EXPECT_EQ(kReceiverMainSsrc, + report_block_datas[0].report_block().source_ssrc); + EXPECT_EQ( + kSequenceNumber2, + report_block_datas[0].report_block().extended_highest_sequence_number); +} + +TEST_F(RtcpReceiverTest, + GetReportBlockDataAfterTwoReportBlocksOfDifferentSsrcs) { + const uint16_t kSequenceNumber1 = 1234; + const uint16_t kSequenceNumber2 = 42; + + rtcp::ReportBlock rtcp_block1; + rtcp_block1.SetMediaSsrc(kReceiverMainSsrc); + rtcp_block1.SetExtHighestSeqNum(kSequenceNumber1); + + rtcp::ReceiverReport rtcp_report1; + rtcp_report1.SetSenderSsrc(kSenderSsrc); + rtcp_report1.AddReportBlock(rtcp_block1); + EXPECT_CALL(rtp_rtcp_impl_, OnReceivedRtcpReportBlocks); + EXPECT_CALL(bandwidth_observer_, OnReceivedRtcpReceiverReport); + InjectRtcpPacket(rtcp_report1); + + // Inject a report block for a different source SSRC. + rtcp::ReportBlock rtcp_block2; + rtcp_block2.SetMediaSsrc(kReceiverExtraSsrc); + rtcp_block2.SetExtHighestSeqNum(kSequenceNumber2); + + rtcp::ReceiverReport rtcp_report2; + rtcp_report2.SetSenderSsrc(kSenderSsrc); + rtcp_report2.AddReportBlock(rtcp_block2); + EXPECT_CALL(rtp_rtcp_impl_, OnReceivedRtcpReportBlocks); + EXPECT_CALL(bandwidth_observer_, OnReceivedRtcpReceiverReport); + InjectRtcpPacket(rtcp_report2); + + // Both report blocks should be returned. + auto report_block_datas = rtcp_receiver_.GetLatestReportBlockData(); + ASSERT_THAT(report_block_datas, SizeIs(2)); + EXPECT_EQ(kReceiverMainSsrc, + report_block_datas[0].report_block().source_ssrc); + EXPECT_EQ( + kSequenceNumber1, + report_block_datas[0].report_block().extended_highest_sequence_number); + EXPECT_EQ(kReceiverExtraSsrc, + report_block_datas[1].report_block().source_ssrc); + EXPECT_EQ( + kSequenceNumber2, + report_block_datas[1].report_block().extended_highest_sequence_number); +} + TEST_F(RtcpReceiverTest, ReceivesTransportFeedback) { rtcp::TransportFeedback packet; packet.SetMediaSsrc(kReceiverMainSsrc);