summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc')
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc294
1 files changed, 294 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc
new file mode 100644
index 0000000000..e578be86c4
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2013 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 <iterator>
+#include <list>
+#include <memory>
+#include <set>
+
+#include "absl/algorithm/container.h"
+#include "api/call/transport.h"
+#include "call/rtp_stream_receiver_controller.h"
+#include "call/rtx_receive_stream.h"
+#include "modules/rtp_rtcp/include/receive_statistics.h"
+#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "modules/rtp_rtcp/source/rtp_packet_received.h"
+#include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h"
+#include "modules/rtp_rtcp/source/rtp_sender_video.h"
+#include "rtc_base/rate_limiter.h"
+#include "rtc_base/thread.h"
+#include "test/explicit_key_value_config.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+const int kVideoNackListSize = 30;
+const uint32_t kTestSsrc = 3456;
+const uint32_t kTestRtxSsrc = kTestSsrc + 1;
+const uint16_t kTestSequenceNumber = 2345;
+const uint32_t kTestNumberOfPackets = 1350;
+const int kTestNumberOfRtxPackets = 149;
+const int kNumFrames = 30;
+const int kPayloadType = 123;
+const int kRtxPayloadType = 98;
+const int64_t kMaxRttMs = 1000;
+
+class VerifyingMediaStream : public RtpPacketSinkInterface {
+ public:
+ VerifyingMediaStream() {}
+
+ void OnRtpPacket(const RtpPacketReceived& packet) override {
+ if (!sequence_numbers_.empty())
+ EXPECT_EQ(kTestSsrc, packet.Ssrc());
+
+ sequence_numbers_.push_back(packet.SequenceNumber());
+ }
+ std::list<uint16_t> sequence_numbers_;
+};
+
+class RtxLoopBackTransport : public webrtc::Transport {
+ public:
+ explicit RtxLoopBackTransport(uint32_t rtx_ssrc)
+ : count_(0),
+ packet_loss_(0),
+ consecutive_drop_start_(0),
+ consecutive_drop_end_(0),
+ rtx_ssrc_(rtx_ssrc),
+ count_rtx_ssrc_(0),
+ module_(NULL) {}
+
+ void SetSendModule(RtpRtcpInterface* rtpRtcpModule) {
+ module_ = rtpRtcpModule;
+ }
+
+ void DropEveryNthPacket(int n) { packet_loss_ = n; }
+
+ void DropConsecutivePackets(int start, int total) {
+ consecutive_drop_start_ = start;
+ consecutive_drop_end_ = start + total;
+ packet_loss_ = 0;
+ }
+
+ bool SendRtp(rtc::ArrayView<const uint8_t> data,
+ const PacketOptions& options) override {
+ count_++;
+ RtpPacketReceived packet;
+ if (!packet.Parse(data))
+ return false;
+ if (packet.Ssrc() == rtx_ssrc_) {
+ count_rtx_ssrc_++;
+ } else {
+ // For non-RTX packets only.
+ expected_sequence_numbers_.insert(expected_sequence_numbers_.end(),
+ packet.SequenceNumber());
+ }
+ if (packet_loss_ > 0) {
+ if ((count_ % packet_loss_) == 0) {
+ return true;
+ }
+ } else if (count_ >= consecutive_drop_start_ &&
+ count_ < consecutive_drop_end_) {
+ return true;
+ }
+ EXPECT_TRUE(stream_receiver_controller_.OnRtpPacket(packet));
+ return true;
+ }
+
+ bool SendRtcp(rtc::ArrayView<const uint8_t> data) override {
+ module_->IncomingRtcpPacket(data);
+ return true;
+ }
+ int count_;
+ int packet_loss_;
+ int consecutive_drop_start_;
+ int consecutive_drop_end_;
+ uint32_t rtx_ssrc_;
+ int count_rtx_ssrc_;
+ RtpRtcpInterface* module_;
+ RtpStreamReceiverController stream_receiver_controller_;
+ std::set<uint16_t> expected_sequence_numbers_;
+};
+
+class RtpRtcpRtxNackTest : public ::testing::Test {
+ protected:
+ RtpRtcpRtxNackTest()
+ : rtp_rtcp_module_(nullptr),
+ transport_(kTestRtxSsrc),
+ rtx_stream_(&media_stream_, rtx_associated_payload_types_, kTestSsrc),
+ fake_clock(123456),
+ retransmission_rate_limiter_(&fake_clock, kMaxRttMs) {}
+ ~RtpRtcpRtxNackTest() override {}
+
+ void SetUp() override {
+ RtpRtcpInterface::Configuration configuration;
+ configuration.audio = false;
+ configuration.clock = &fake_clock;
+ receive_statistics_ = ReceiveStatistics::Create(&fake_clock);
+ configuration.receive_statistics = receive_statistics_.get();
+ configuration.outgoing_transport = &transport_;
+ configuration.retransmission_rate_limiter = &retransmission_rate_limiter_;
+ configuration.local_media_ssrc = kTestSsrc;
+ configuration.rtx_send_ssrc = kTestRtxSsrc;
+ rtp_rtcp_module_ = ModuleRtpRtcpImpl2::Create(configuration);
+ test::ExplicitKeyValueConfig field_trials("");
+ RTPSenderVideo::Config video_config;
+ video_config.clock = &fake_clock;
+ video_config.rtp_sender = rtp_rtcp_module_->RtpSender();
+ video_config.field_trials = &field_trials;
+ rtp_sender_video_ = std::make_unique<RTPSenderVideo>(video_config);
+ rtp_rtcp_module_->SetRTCPStatus(RtcpMode::kCompound);
+ rtp_rtcp_module_->SetStorePacketsStatus(true, 600);
+ EXPECT_EQ(0, rtp_rtcp_module_->SetSendingStatus(true));
+ rtp_rtcp_module_->SetSequenceNumber(kTestSequenceNumber);
+ rtp_rtcp_module_->SetStartTimestamp(111111);
+
+ // Used for NACK processing.
+ rtp_rtcp_module_->SetRemoteSSRC(kTestSsrc);
+
+ rtp_rtcp_module_->SetRtxSendPayloadType(kRtxPayloadType, kPayloadType);
+ transport_.SetSendModule(rtp_rtcp_module_.get());
+ media_receiver_ = transport_.stream_receiver_controller_.CreateReceiver(
+ kTestSsrc, &media_stream_);
+
+ for (size_t n = 0; n < sizeof(payload_data); n++) {
+ payload_data[n] = n % 10;
+ }
+ }
+
+ int BuildNackList(uint16_t* nack_list) {
+ media_stream_.sequence_numbers_.sort();
+ std::list<uint16_t> missing_sequence_numbers;
+ std::list<uint16_t>::iterator it = media_stream_.sequence_numbers_.begin();
+
+ while (it != media_stream_.sequence_numbers_.end()) {
+ uint16_t sequence_number_1 = *it;
+ ++it;
+ if (it != media_stream_.sequence_numbers_.end()) {
+ uint16_t sequence_number_2 = *it;
+ // Add all missing sequence numbers to list
+ for (uint16_t i = sequence_number_1 + 1; i < sequence_number_2; ++i) {
+ missing_sequence_numbers.push_back(i);
+ }
+ }
+ }
+ int n = 0;
+ for (it = missing_sequence_numbers.begin();
+ it != missing_sequence_numbers.end(); ++it) {
+ nack_list[n++] = (*it);
+ }
+ return n;
+ }
+
+ bool ExpectedPacketsReceived() {
+ std::list<uint16_t> received_sorted;
+ absl::c_copy(media_stream_.sequence_numbers_,
+ std::back_inserter(received_sorted));
+ received_sorted.sort();
+ return absl::c_equal(received_sorted,
+ transport_.expected_sequence_numbers_);
+ }
+
+ void RunRtxTest(RtxMode rtx_method, int loss) {
+ rtx_receiver_ = transport_.stream_receiver_controller_.CreateReceiver(
+ kTestRtxSsrc, &rtx_stream_);
+ rtp_rtcp_module_->SetRtxSendStatus(rtx_method);
+ transport_.DropEveryNthPacket(loss);
+ uint32_t timestamp = 3000;
+ uint16_t nack_list[kVideoNackListSize];
+ for (int frame = 0; frame < kNumFrames; ++frame) {
+ RTPVideoHeader video_header;
+ EXPECT_TRUE(rtp_rtcp_module_->OnSendingRtpFrame(timestamp, timestamp / 90,
+ kPayloadType, false));
+ video_header.frame_type = VideoFrameType::kVideoFrameDelta;
+ EXPECT_TRUE(rtp_sender_video_->SendVideo(
+ kPayloadType, VideoCodecType::kVideoCodecGeneric, timestamp,
+ /*capture_time=*/Timestamp::Millis(timestamp / 90), payload_data,
+ sizeof(payload_data), video_header, TimeDelta::Zero(), {}));
+ // Min required delay until retransmit = 5 + RTT ms (RTT = 0).
+ fake_clock.AdvanceTimeMilliseconds(5);
+ int length = BuildNackList(nack_list);
+ if (length > 0)
+ rtp_rtcp_module_->SendNACK(nack_list, length);
+ fake_clock.AdvanceTimeMilliseconds(28); // 33ms - 5ms delay.
+ // Prepare next frame.
+ timestamp += 3000;
+ }
+ media_stream_.sequence_numbers_.sort();
+ }
+
+ rtc::AutoThread main_thread_;
+ std::unique_ptr<ReceiveStatistics> receive_statistics_;
+ std::unique_ptr<ModuleRtpRtcpImpl2> rtp_rtcp_module_;
+ std::unique_ptr<RTPSenderVideo> rtp_sender_video_;
+ RtxLoopBackTransport transport_;
+ const std::map<int, int> rtx_associated_payload_types_ = {
+ {kRtxPayloadType, kPayloadType}};
+ VerifyingMediaStream media_stream_;
+ RtxReceiveStream rtx_stream_;
+ uint8_t payload_data[65000];
+ SimulatedClock fake_clock;
+ RateLimiter retransmission_rate_limiter_;
+ std::unique_ptr<RtpStreamReceiverInterface> media_receiver_;
+ std::unique_ptr<RtpStreamReceiverInterface> rtx_receiver_;
+};
+
+TEST_F(RtpRtcpRtxNackTest, LongNackList) {
+ const int kNumPacketsToDrop = 900;
+ const int kNumRequiredRtcp = 4;
+ uint32_t timestamp = 3000;
+ uint16_t nack_list[kNumPacketsToDrop];
+ // Disable StorePackets to be able to set a larger packet history.
+ rtp_rtcp_module_->SetStorePacketsStatus(false, 0);
+ // Enable StorePackets with a packet history of 2000 packets.
+ rtp_rtcp_module_->SetStorePacketsStatus(true, 2000);
+ // Drop 900 packets from the second one so that we get a NACK list which is
+ // big enough to require 4 RTCP packets to be fully transmitted to the sender.
+ transport_.DropConsecutivePackets(2, kNumPacketsToDrop);
+ // Send 30 frames which at the default size is roughly what we need to get
+ // enough packets.
+ for (int frame = 0; frame < kNumFrames; ++frame) {
+ RTPVideoHeader video_header;
+ EXPECT_TRUE(rtp_rtcp_module_->OnSendingRtpFrame(timestamp, timestamp / 90,
+ kPayloadType, false));
+ video_header.frame_type = VideoFrameType::kVideoFrameDelta;
+ EXPECT_TRUE(rtp_sender_video_->SendVideo(
+ kPayloadType, VideoCodecType::kVideoCodecGeneric, timestamp,
+ Timestamp::Millis(timestamp / 90), payload_data, sizeof(payload_data),
+ video_header, TimeDelta::Zero(), {}));
+ // Prepare next frame.
+ timestamp += 3000;
+ fake_clock.AdvanceTimeMilliseconds(33);
+ }
+ EXPECT_FALSE(transport_.expected_sequence_numbers_.empty());
+ EXPECT_FALSE(media_stream_.sequence_numbers_.empty());
+ size_t last_receive_count = media_stream_.sequence_numbers_.size();
+ int length = BuildNackList(nack_list);
+ for (int i = 0; i < kNumRequiredRtcp - 1; ++i) {
+ rtp_rtcp_module_->SendNACK(nack_list, length);
+ EXPECT_GT(media_stream_.sequence_numbers_.size(), last_receive_count);
+ last_receive_count = media_stream_.sequence_numbers_.size();
+ EXPECT_FALSE(ExpectedPacketsReceived());
+ }
+ rtp_rtcp_module_->SendNACK(nack_list, length);
+ EXPECT_GT(media_stream_.sequence_numbers_.size(), last_receive_count);
+ EXPECT_TRUE(ExpectedPacketsReceived());
+}
+
+TEST_F(RtpRtcpRtxNackTest, RtxNack) {
+ RunRtxTest(kRtxRetransmitted, 10);
+ EXPECT_EQ(kTestSequenceNumber, *(media_stream_.sequence_numbers_.begin()));
+ EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
+ *(media_stream_.sequence_numbers_.rbegin()));
+ EXPECT_EQ(kTestNumberOfPackets, media_stream_.sequence_numbers_.size());
+ EXPECT_EQ(kTestNumberOfRtxPackets, transport_.count_rtx_ssrc_);
+ EXPECT_TRUE(ExpectedPacketsReceived());
+}
+
+} // namespace webrtc