|  | /* | 
|  | *  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 |