| /* |
| * 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 "modules/rtp_rtcp/source/rtp_packet_history.h" |
| |
| #include <cstdint> |
| #include <limits> |
| #include <memory> |
| #include <utility> |
| |
| #include "api/units/time_delta.h" |
| #include "api/units/timestamp.h" |
| #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" |
| #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" |
| #include "system_wrappers/include/clock.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| |
| namespace webrtc { |
| namespace { |
| // Set a high sequence number so we'll suffer a wrap-around. |
| constexpr uint16_t kStartSeqNum = 65534u; |
| |
| // Utility method for truncating sequence numbers to uint16. |
| uint16_t To16u(size_t sequence_number) { |
| return static_cast<uint16_t>(sequence_number & 0xFFFF); |
| } |
| |
| using StorageMode = RtpPacketHistory::StorageMode; |
| |
| using ::testing::AllOf; |
| using ::testing::Pointee; |
| using ::testing::Property; |
| |
| std::unique_ptr<RtpPacketToSend> CreatePacket( |
| uint16_t seq_num, |
| Timestamp capture_time = Timestamp::Zero()) { |
| // Payload, ssrc, timestamp and extensions are irrelevant for this tests. |
| std::unique_ptr<RtpPacketToSend> packet(new RtpPacketToSend(nullptr)); |
| packet->SetSequenceNumber(seq_num); |
| packet->set_capture_time(capture_time); |
| packet->set_allow_retransmission(true); |
| return packet; |
| } |
| |
| } // namespace |
| |
| class RtpPacketHistoryTest |
| : public ::testing::TestWithParam<RtpPacketHistory::PaddingMode> { |
| protected: |
| RtpPacketHistoryTest() |
| : fake_clock_(123456), |
| hist_(&fake_clock_, /*enable_padding_prio=*/GetParam()) {} |
| |
| SimulatedClock fake_clock_; |
| RtpPacketHistory hist_; |
| |
| std::unique_ptr<RtpPacketToSend> CreateRtpPacket(uint16_t seq_num) { |
| return CreatePacket(seq_num, fake_clock_.CurrentTime()); |
| } |
| }; |
| |
| TEST_P(RtpPacketHistoryTest, SetStoreStatus) { |
| EXPECT_EQ(StorageMode::kDisabled, hist_.GetStorageMode()); |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| EXPECT_EQ(StorageMode::kStoreAndCull, hist_.GetStorageMode()); |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| EXPECT_EQ(StorageMode::kStoreAndCull, hist_.GetStorageMode()); |
| hist_.SetStorePacketsStatus(StorageMode::kDisabled, 0); |
| EXPECT_EQ(StorageMode::kDisabled, hist_.GetStorageMode()); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, ClearsHistoryAfterSetStoreStatus) { |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), |
| /*send_time=*/fake_clock_.CurrentTime()); |
| EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); |
| |
| // Changing store status, even to the current one, will clear the history. |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, StartSeqResetAfterReset) { |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), |
| /*send_time=*/fake_clock_.CurrentTime()); |
| // Mark packet as pending so it won't be removed. |
| EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); |
| |
| // Changing store status, to clear the history. |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| |
| // Add a new packet. |
| hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), |
| /*send_time=*/fake_clock_.CurrentTime()); |
| EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(To16u(kStartSeqNum + 1))); |
| |
| // Advance time past where packet expires. |
| fake_clock_.AdvanceTime(RtpPacketHistory::kPacketCullingDelayFactor * |
| RtpPacketHistory::kMinPacketDuration); |
| |
| // Add one more packet and verify no state left from packet before reset. |
| hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)), |
| /*send_time=*/fake_clock_.CurrentTime()); |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); |
| EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, NoStoreStatus) { |
| EXPECT_EQ(StorageMode::kDisabled, hist_.GetStorageMode()); |
| std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum); |
| hist_.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock_.CurrentTime()); |
| // Packet should not be stored. |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, GetRtpPacket_NotStored) { |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| EXPECT_FALSE(hist_.GetPacketState(0)); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, PutRtpPacket) { |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum); |
| |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| hist_.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock_.CurrentTime()); |
| EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, GetRtpPacket) { |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| Timestamp capture_time = Timestamp::Millis(1); |
| std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum); |
| packet->set_capture_time(capture_time); |
| rtc::CopyOnWriteBuffer buffer = packet->Buffer(); |
| hist_.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock_.CurrentTime()); |
| |
| std::unique_ptr<RtpPacketToSend> packet_out = |
| hist_.GetPacketAndMarkAsPending(kStartSeqNum); |
| ASSERT_TRUE(packet_out); |
| EXPECT_EQ(buffer, packet_out->Buffer()); |
| EXPECT_EQ(capture_time, packet_out->capture_time()); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, MinResendTime) { |
| static const TimeDelta kMinRetransmitInterval = TimeDelta::Millis(100); |
| |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| hist_.SetRtt(kMinRetransmitInterval); |
| Timestamp capture_time = fake_clock_.CurrentTime(); |
| std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum); |
| size_t len = packet->size(); |
| hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); |
| |
| // First retransmission - allow early retransmission. |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| packet = hist_.GetPacketAndMarkAsPending(kStartSeqNum); |
| ASSERT_TRUE(packet); |
| EXPECT_EQ(len, packet->size()); |
| EXPECT_EQ(packet->capture_time(), capture_time); |
| hist_.MarkPacketAsSent(kStartSeqNum); |
| |
| // Second retransmission - advance time to just before retransmission OK. |
| fake_clock_.AdvanceTime(kMinRetransmitInterval - TimeDelta::Millis(1)); |
| EXPECT_FALSE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); |
| |
| // Advance time to just after retransmission OK. |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, RemovesOldestSentPacketWhenAtMaxSize) { |
| const size_t kMaxNumPackets = 10; |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets); |
| |
| // History does not allow removing packets within kMinPacketDuration, |
| // so in order to test capacity, make sure insertion spans this time. |
| const TimeDelta kPacketInterval = |
| RtpPacketHistory::kMinPacketDuration / kMaxNumPackets; |
| |
| // Add packets until the buffer is full. |
| for (size_t i = 0; i < kMaxNumPackets; ++i) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| CreateRtpPacket(To16u(kStartSeqNum + i)); |
| // Immediate mark packet as sent. |
| hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); |
| fake_clock_.AdvanceTime(kPacketInterval); |
| } |
| |
| // First packet should still be there. |
| EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); |
| |
| // History is full, oldest one should be overwritten. |
| std::unique_ptr<RtpPacketToSend> packet = |
| CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets)); |
| hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); |
| |
| // Oldest packet should be gone, but packet after than one still present. |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, RemovesOldestPacketWhenAtMaxCapacity) { |
| // Tests the absolute upper bound on number of stored packets. Don't allow |
| // storing more than this, even if packets have not yet been sent. |
| const size_t kMaxNumPackets = RtpPacketHistory::kMaxCapacity; |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, |
| RtpPacketHistory::kMaxCapacity); |
| |
| // Add packets until the buffer is full. |
| for (size_t i = 0; i < kMaxNumPackets; ++i) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| CreateRtpPacket(To16u(kStartSeqNum + i)); |
| hist_.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock_.CurrentTime()); |
| // Mark packets as pending, preventing it from being removed. |
| hist_.GetPacketAndMarkAsPending(To16u(kStartSeqNum + i)); |
| } |
| |
| // First packet should still be there. |
| EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); |
| |
| // History is full, oldest one should be overwritten. |
| std::unique_ptr<RtpPacketToSend> packet = |
| CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets)); |
| hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); |
| |
| // Oldest packet should be gone, but packet after than one still present. |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, RemovesLowestPrioPaddingWhenAtMaxCapacity) { |
| if (GetParam() != RtpPacketHistory::PaddingMode::kPriority) { |
| GTEST_SKIP() << "Padding prioritization required for this test"; |
| } |
| |
| // Tests the absolute upper bound on number of packets in the prioritized |
| // set of potential padding packets. |
| const size_t kMaxNumPackets = RtpPacketHistory::kMaxPaddingHistory; |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets * 2); |
| hist_.SetRtt(TimeDelta::Millis(1)); |
| |
| // Add packets until the max is reached, and then yet another one. |
| for (size_t i = 0; i < kMaxNumPackets + 1; ++i) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| CreateRtpPacket(To16u(kStartSeqNum + i)); |
| // Don't mark packets as sent, preventing them from being removed. |
| hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); |
| } |
| |
| // Advance time to allow retransmission/padding. |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| |
| // The oldest packet will be least prioritized and has fallen out of the |
| // priority set. |
| for (size_t i = kMaxNumPackets - 1; i > 0; --i) { |
| auto packet = hist_.GetPayloadPaddingPacket(); |
| ASSERT_TRUE(packet); |
| EXPECT_EQ(packet->SequenceNumber(), To16u(kStartSeqNum + i + 1)); |
| } |
| |
| // Wrap around to newest padding packet again. |
| auto packet = hist_.GetPayloadPaddingPacket(); |
| ASSERT_TRUE(packet); |
| EXPECT_EQ(packet->SequenceNumber(), To16u(kStartSeqNum + kMaxNumPackets)); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, DontRemoveTooRecentlyTransmittedPackets) { |
| // Set size to remove old packets as soon as possible. |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); |
| |
| // Add a packet, marked as send, and advance time to just before removal time. |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); |
| fake_clock_.AdvanceTime(RtpPacketHistory::kMinPacketDuration - |
| TimeDelta::Millis(1)); |
| |
| // Add a new packet to trigger culling. |
| hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), |
| fake_clock_.CurrentTime()); |
| // First packet should still be there. |
| EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); |
| |
| // Advance time to where packet will be eligible for removal and try again. |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)), |
| fake_clock_.CurrentTime()); |
| // First packet should no be gone, but next one still there. |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, DontRemoveTooRecentlyTransmittedPacketsHighRtt) { |
| const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; |
| const TimeDelta kPacketTimeout = |
| kRtt * RtpPacketHistory::kMinPacketDurationRtt; |
| |
| // Set size to remove old packets as soon as possible. |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); |
| hist_.SetRtt(kRtt); |
| |
| // Add a packet, marked as send, and advance time to just before removal time. |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); |
| fake_clock_.AdvanceTime(kPacketTimeout - TimeDelta::Millis(1)); |
| |
| // Add a new packet to trigger culling. |
| hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), |
| fake_clock_.CurrentTime()); |
| // First packet should still be there. |
| EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); |
| |
| // Advance time to where packet will be eligible for removal and try again. |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)), |
| fake_clock_.CurrentTime()); |
| // First packet should no be gone, but next one still there. |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, RemovesOldWithCulling) { |
| const size_t kMaxNumPackets = 10; |
| // Enable culling. Even without feedback, this can trigger early removal. |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets); |
| |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); |
| |
| TimeDelta kMaxPacketDuration = RtpPacketHistory::kMinPacketDuration * |
| RtpPacketHistory::kPacketCullingDelayFactor; |
| fake_clock_.AdvanceTime(kMaxPacketDuration - TimeDelta::Millis(1)); |
| |
| // First packet should still be there. |
| EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); |
| |
| // Advance to where packet can be culled, even if buffer is not full. |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), |
| fake_clock_.CurrentTime()); |
| |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, RemovesOldWithCullingHighRtt) { |
| const size_t kMaxNumPackets = 10; |
| const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; |
| // Enable culling. Even without feedback, this can trigger early removal. |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets); |
| hist_.SetRtt(kRtt); |
| |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); |
| |
| TimeDelta kMaxPacketDuration = kRtt * |
| RtpPacketHistory::kMinPacketDurationRtt * |
| RtpPacketHistory::kPacketCullingDelayFactor; |
| fake_clock_.AdvanceTime(kMaxPacketDuration - TimeDelta::Millis(1)); |
| |
| // First packet should still be there. |
| EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); |
| |
| // Advance to where packet can be culled, even if buffer is not full. |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), |
| fake_clock_.CurrentTime()); |
| |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, CullWithAcks) { |
| const TimeDelta kPacketLifetime = RtpPacketHistory::kMinPacketDuration * |
| RtpPacketHistory::kPacketCullingDelayFactor; |
| |
| const Timestamp start_time = fake_clock_.CurrentTime(); |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| |
| // Insert three packets 33ms apart, immediately mark them as sent. |
| std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum); |
| packet->SetPayloadSize(50); |
| hist_.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock_.CurrentTime()); |
| fake_clock_.AdvanceTimeMilliseconds(33); |
| packet = CreateRtpPacket(To16u(kStartSeqNum + 1)); |
| packet->SetPayloadSize(50); |
| hist_.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock_.CurrentTime()); |
| fake_clock_.AdvanceTimeMilliseconds(33); |
| packet = CreateRtpPacket(To16u(kStartSeqNum + 2)); |
| packet->SetPayloadSize(50); |
| hist_.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock_.CurrentTime()); |
| |
| EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); |
| EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); |
| EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); |
| |
| // Remove middle one using ack, check that only that one is gone. |
| std::vector<uint16_t> acked_sequence_numbers = {To16u(kStartSeqNum + 1)}; |
| hist_.CullAcknowledgedPackets(acked_sequence_numbers); |
| |
| EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); |
| EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); |
| EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); |
| |
| // Advance time to where second packet would have expired, verify first packet |
| // is removed. |
| Timestamp second_packet_expiry_time = |
| start_time + kPacketLifetime + TimeDelta::Millis(33 + 1); |
| fake_clock_.AdvanceTime(second_packet_expiry_time - |
| fake_clock_.CurrentTime()); |
| hist_.SetRtt(TimeDelta::Millis(1)); // Trigger culling of old packets. |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); |
| EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); |
| |
| // Advance to where last packet expires, verify all gone. |
| fake_clock_.AdvanceTimeMilliseconds(33); |
| hist_.SetRtt(TimeDelta::Millis(1)); // Trigger culling of old packets. |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); |
| EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, GetPacketAndSetSent) { |
| const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; |
| hist_.SetRtt(kRtt); |
| |
| // Set size to remove old packets as soon as possible. |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); |
| |
| // Add a sent packet to the history. |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); |
| |
| // Retransmission request, first retransmission is allowed immediately. |
| EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); |
| |
| // Packet not yet sent, new retransmission not allowed. |
| fake_clock_.AdvanceTime(kRtt); |
| EXPECT_FALSE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); |
| |
| // Mark as sent, but too early for retransmission. |
| hist_.MarkPacketAsSent(kStartSeqNum); |
| EXPECT_FALSE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); |
| |
| // Enough time has passed, retransmission is allowed again. |
| fake_clock_.AdvanceTime(kRtt); |
| EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulation) { |
| const uint32_t kSsrc = 92384762; |
| const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; |
| hist_.SetRtt(kRtt); |
| |
| // Set size to remove old packets as soon as possible. |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); |
| |
| // Add a sent packet to the history, with a set SSRC. |
| std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum); |
| packet->SetSsrc(kSsrc); |
| hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); |
| |
| // Retransmission request, simulate an RTX-like encapsulation, were the packet |
| // is sent on a different SSRC. |
| std::unique_ptr<RtpPacketToSend> retransmit_packet = |
| hist_.GetPacketAndMarkAsPending( |
| kStartSeqNum, [](const RtpPacketToSend& packet) { |
| auto encapsulated_packet = |
| std::make_unique<RtpPacketToSend>(packet); |
| encapsulated_packet->SetSsrc(packet.Ssrc() + 1); |
| return encapsulated_packet; |
| }); |
| ASSERT_TRUE(retransmit_packet); |
| EXPECT_EQ(retransmit_packet->Ssrc(), kSsrc + 1); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulationAbortOnNullptr) { |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); |
| |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); |
| |
| // Retransmission request, but the encapsulator determines that this packet is |
| // not suitable for retransmission (bandwidth exhausted?) so the retransmit is |
| // aborted and the packet is not marked as pending. |
| EXPECT_FALSE(hist_.GetPacketAndMarkAsPending( |
| kStartSeqNum, [](const RtpPacketToSend&) { return nullptr; })); |
| |
| // New try, this time getting the packet should work, and it should not be |
| // blocked due to any pending status. |
| EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, DontRemovePendingTransmissions) { |
| const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; |
| const TimeDelta kPacketTimeout = |
| kRtt * RtpPacketHistory::kMinPacketDurationRtt; |
| |
| // Set size to remove old packets as soon as possible. |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); |
| hist_.SetRtt(kRtt); |
| |
| // Add a sent packet. |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); |
| |
| // Advance clock to just before packet timeout. |
| fake_clock_.AdvanceTime(kPacketTimeout - TimeDelta::Millis(1)); |
| // Mark as enqueued in pacer. |
| EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); |
| |
| // Advance clock to where packet would have timed out. It should still |
| // be there and pending. |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); |
| |
| // Packet sent. Now it can be removed. |
| hist_.MarkPacketAsSent(kStartSeqNum); |
| hist_.SetRtt(kRtt); // Force culling of old packets. |
| EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, PrioritizedPayloadPadding) { |
| if (GetParam() != RtpPacketHistory::PaddingMode::kPriority) { |
| GTEST_SKIP() << "Padding prioritization required for this test"; |
| } |
| |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); |
| |
| // Add two sent packets, one millisecond apart. |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum + 1), |
| fake_clock_.CurrentTime()); |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| |
| // Latest packet given equal retransmission count. |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), |
| kStartSeqNum + 1); |
| |
| // Older packet has lower retransmission count. |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), kStartSeqNum); |
| |
| // Equal retransmission count again, use newest packet. |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), |
| kStartSeqNum + 1); |
| |
| // Older packet has lower retransmission count. |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), kStartSeqNum); |
| |
| // Remove newest packet. |
| hist_.CullAcknowledgedPackets(std::vector<uint16_t>{kStartSeqNum + 1}); |
| |
| // Only older packet left. |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), kStartSeqNum); |
| |
| hist_.CullAcknowledgedPackets(std::vector<uint16_t>{kStartSeqNum}); |
| |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket(), nullptr); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, NoPendingPacketAsPadding) { |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); |
| |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), kStartSeqNum); |
| |
| // If packet is pending retransmission, don't try to use it as padding. |
| hist_.GetPacketAndMarkAsPending(kStartSeqNum); |
| if (GetParam() != RtpPacketHistory::PaddingMode::kRecentLargePacket) { |
| EXPECT_EQ(nullptr, hist_.GetPayloadPaddingPacket()); |
| } else { |
| // We do allow sending the same packet multiple times in this mode. |
| EXPECT_NE(nullptr, hist_.GetPayloadPaddingPacket()); |
| } |
| |
| // Market it as no longer pending, should be usable as padding again. |
| hist_.MarkPacketAsSent(kStartSeqNum); |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), kStartSeqNum); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, PayloadPaddingWithEncapsulation) { |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); |
| |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| |
| // Aborted padding. |
| EXPECT_EQ(nullptr, hist_.GetPayloadPaddingPacket( |
| [](const RtpPacketToSend&) { return nullptr; })); |
| |
| // Get copy of packet, but with sequence number modified. |
| auto padding_packet = |
| hist_.GetPayloadPaddingPacket([&](const RtpPacketToSend& packet) { |
| auto encapsulated_packet = std::make_unique<RtpPacketToSend>(packet); |
| encapsulated_packet->SetSequenceNumber(kStartSeqNum + 1); |
| return encapsulated_packet; |
| }); |
| ASSERT_TRUE(padding_packet); |
| EXPECT_EQ(padding_packet->SequenceNumber(), kStartSeqNum + 1); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, NackAfterAckIsNoop) { |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 2); |
| // Add two sent packets. |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); |
| hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum + 1), |
| fake_clock_.CurrentTime()); |
| // Remove newest one. |
| hist_.CullAcknowledgedPackets(std::vector<uint16_t>{kStartSeqNum + 1}); |
| // Retransmission request for already acked packet, should be noop. |
| auto packet = hist_.GetPacketAndMarkAsPending(kStartSeqNum + 1); |
| EXPECT_EQ(packet.get(), nullptr); |
| } |
| |
| TEST_P(RtpPacketHistoryTest, OutOfOrderInsertRemoval) { |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| |
| // Insert packets, out of order, including both forwards and backwards |
| // sequence number wraps. |
| const int seq_offsets[] = {0, 1, -1, 2, -2, 3, -3}; |
| |
| for (int offset : seq_offsets) { |
| uint16_t seq_no = To16u(kStartSeqNum + offset); |
| std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(seq_no); |
| packet->SetPayloadSize(50); |
| hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); |
| fake_clock_.AdvanceTimeMilliseconds(33); |
| } |
| |
| // Check packet are there and remove them in the same out-of-order fashion. |
| for (int offset : seq_offsets) { |
| uint16_t seq_no = To16u(kStartSeqNum + offset); |
| EXPECT_TRUE(hist_.GetPacketState(seq_no)); |
| std::vector<uint16_t> acked_sequence_numbers = {seq_no}; |
| hist_.CullAcknowledgedPackets(acked_sequence_numbers); |
| EXPECT_FALSE(hist_.GetPacketState(seq_no)); |
| } |
| } |
| |
| TEST_P(RtpPacketHistoryTest, UsesLastPacketAsPaddingWithPrioOff) { |
| if (GetParam() != RtpPacketHistory::PaddingMode::kDefault) { |
| GTEST_SKIP() << "Default padding prioritization required for this test"; |
| } |
| |
| const size_t kHistorySize = 10; |
| hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kHistorySize); |
| |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket(), nullptr); |
| |
| for (size_t i = 0; i < kHistorySize; ++i) { |
| hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + i)), |
| fake_clock_.CurrentTime()); |
| hist_.MarkPacketAsSent(To16u(kStartSeqNum + i)); |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| |
| // Last packet always returned. |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), |
| To16u(kStartSeqNum + i)); |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), |
| To16u(kStartSeqNum + i)); |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), |
| To16u(kStartSeqNum + i)); |
| } |
| |
| // Remove packets from the end, last in the list should be returned. |
| for (size_t i = kHistorySize - 1; i > 0; --i) { |
| hist_.CullAcknowledgedPackets( |
| std::vector<uint16_t>{To16u(kStartSeqNum + i)}); |
| |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), |
| To16u(kStartSeqNum + i - 1)); |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), |
| To16u(kStartSeqNum + i - 1)); |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), |
| To16u(kStartSeqNum + i - 1)); |
| } |
| |
| hist_.CullAcknowledgedPackets(std::vector<uint16_t>{kStartSeqNum}); |
| EXPECT_EQ(hist_.GetPayloadPaddingPacket(), nullptr); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| WithAndWithoutPaddingPrio, |
| RtpPacketHistoryTest, |
| ::testing::Values(RtpPacketHistory::PaddingMode::kDefault, |
| RtpPacketHistory::PaddingMode::kPriority, |
| RtpPacketHistory::PaddingMode::kRecentLargePacket)); |
| |
| TEST(RtpPacketHistoryRecentLargePacketMode, |
| GetPayloadPaddingPacketAfterCullWithAcksReturnOldPacket) { |
| SimulatedClock fake_clock(1234); |
| RtpPacketHistory history(&fake_clock, |
| RtpPacketHistory::PaddingMode::kRecentLargePacket); |
| |
| history.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| std::unique_ptr<RtpPacketToSend> packet = CreatePacket(kStartSeqNum); |
| packet->SetPayloadSize(1000); |
| history.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock.CurrentTime()); |
| fake_clock.AdvanceTimeMilliseconds(33); |
| history.CullAcknowledgedPackets(std::vector<uint16_t>{kStartSeqNum}); |
| |
| EXPECT_THAT( |
| history.GetPayloadPaddingPacket(), |
| Pointee(AllOf(Property(&RtpPacketToSend::SequenceNumber, kStartSeqNum), |
| (Property(&RtpPacketToSend::payload_size, 1000))))); |
| } |
| |
| TEST(RtpPacketHistoryRecentLargePacketMode, |
| GetPayloadPaddingPacketIgnoreSmallRecentPackets) { |
| SimulatedClock fake_clock(1234); |
| RtpPacketHistory history(&fake_clock, |
| RtpPacketHistory::PaddingMode::kRecentLargePacket); |
| history.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| std::unique_ptr<RtpPacketToSend> packet = CreatePacket(kStartSeqNum); |
| packet->SetPayloadSize(1000); |
| history.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock.CurrentTime()); |
| packet = CreatePacket(kStartSeqNum + 1); |
| packet->SetPayloadSize(100); |
| history.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock.CurrentTime()); |
| |
| EXPECT_THAT( |
| history.GetPayloadPaddingPacket(), |
| Pointee(AllOf(Property(&RtpPacketToSend::SequenceNumber, kStartSeqNum), |
| Property(&RtpPacketToSend::payload_size, 1000)))); |
| } |
| |
| TEST(RtpPacketHistoryRecentLargePacketMode, |
| GetPayloadPaddingPacketReturnsRecentPacketIfSizeNearMax) { |
| SimulatedClock fake_clock(1234); |
| RtpPacketHistory history(&fake_clock, |
| RtpPacketHistory::PaddingMode::kRecentLargePacket); |
| history.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| std::unique_ptr<RtpPacketToSend> packet = CreatePacket(kStartSeqNum); |
| packet->SetPayloadSize(1000); |
| history.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock.CurrentTime()); |
| packet = CreatePacket(kStartSeqNum + 1); |
| packet->SetPayloadSize(950); |
| history.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock.CurrentTime()); |
| |
| EXPECT_THAT(history.GetPayloadPaddingPacket(), |
| (Pointee(AllOf( |
| Property(&RtpPacketToSend::SequenceNumber, kStartSeqNum + 1), |
| Property(&RtpPacketToSend::payload_size, 950))))); |
| } |
| |
| TEST(RtpPacketHistoryRecentLargePacketMode, |
| GetPayloadPaddingPacketReturnsLastPacketAfterLargeSequenceNumberGap) { |
| SimulatedClock fake_clock(1234); |
| RtpPacketHistory history(&fake_clock, |
| RtpPacketHistory::PaddingMode::kRecentLargePacket); |
| history.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); |
| uint16_t sequence_number = std::numeric_limits<uint16_t>::max() - 50; |
| std::unique_ptr<RtpPacketToSend> packet = CreatePacket(sequence_number); |
| packet->SetPayloadSize(1000); |
| history.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock.CurrentTime()); |
| ASSERT_THAT( |
| history.GetPayloadPaddingPacket(), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, sequence_number))); |
| |
| // A long time pass... and potentially many small packets are injected, or |
| // timestamp jumps. |
| sequence_number = 1 << 13; |
| packet = CreatePacket(sequence_number); |
| packet->SetPayloadSize(100); |
| history.PutRtpPacket(std::move(packet), |
| /*send_time=*/fake_clock.CurrentTime()); |
| EXPECT_THAT( |
| history.GetPayloadPaddingPacket(), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, sequence_number))); |
| } |
| |
| } // namespace webrtc |