/* * Copyright (c) 2022 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/pacing/prioritized_packet_queue.h" #include #include "api/units/time_delta.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "rtc_base/checks.h" #include "test/gmock.h" #include "test/gtest.h" namespace webrtc { namespace { constexpr uint32_t kDefaultSsrc = 123; constexpr int kDefaultPayloadSize = 789; std::unique_ptr CreatePacket(RtpPacketMediaType type, uint16_t sequence_number, uint32_t ssrc = kDefaultSsrc) { auto packet = std::make_unique(/*extensions=*/nullptr); packet->set_packet_type(type); packet->SetSsrc(ssrc); packet->SetSequenceNumber(sequence_number); packet->SetPayloadSize(kDefaultPayloadSize); return packet; } } // namespace TEST(PrioritizedPacketQueue, ReturnsPacketsInPrioritizedOrder) { Timestamp now = Timestamp::Zero(); PrioritizedPacketQueue queue(now); // Add packets in low to high packet order. queue.Push(now, CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/1)); queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2)); queue.Push(now, CreatePacket(RtpPacketMediaType::kForwardErrorCorrection, /*seq=*/3)); queue.Push(now, CreatePacket(RtpPacketMediaType::kRetransmission, /*seq=*/4)); queue.Push(now, CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/5)); // Packets should be returned in high to low order. EXPECT_EQ(queue.Pop()->SequenceNumber(), 5); EXPECT_EQ(queue.Pop()->SequenceNumber(), 4); // Video and FEC prioritized equally - but video was enqueued first. EXPECT_EQ(queue.Pop()->SequenceNumber(), 2); EXPECT_EQ(queue.Pop()->SequenceNumber(), 3); EXPECT_EQ(queue.Pop()->SequenceNumber(), 1); } TEST(PrioritizedPacketQueue, ReturnsEqualPrioPacketsInRoundRobinOrder) { Timestamp now = Timestamp::Zero(); PrioritizedPacketQueue queue(now); // Insert video packets (prioritized equally), simulating a simulcast-type use // case. queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/1, /*ssrc=*/100)); queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2, /*ssrc=*/101)); queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/3, /*ssrc=*/101)); queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/4, /*ssrc=*/102)); queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/5, /*ssrc=*/102)); queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/6, /*ssrc=*/102)); queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/7, /*ssrc=*/102)); // First packet from each SSRC. EXPECT_EQ(queue.Pop()->SequenceNumber(), 1); EXPECT_EQ(queue.Pop()->SequenceNumber(), 2); EXPECT_EQ(queue.Pop()->SequenceNumber(), 4); // Second packets from streams that have packets left. EXPECT_EQ(queue.Pop()->SequenceNumber(), 3); EXPECT_EQ(queue.Pop()->SequenceNumber(), 5); // Only packets from last stream remaining. EXPECT_EQ(queue.Pop()->SequenceNumber(), 6); EXPECT_EQ(queue.Pop()->SequenceNumber(), 7); } TEST(PrioritizedPacketQueue, ReportsSizeInPackets) { PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); EXPECT_EQ(queue.SizeInPackets(), 0); queue.Push(/*enqueue_time=*/Timestamp::Zero(), CreatePacket(RtpPacketMediaType::kVideo, /*seq_no=*/1)); EXPECT_EQ(queue.SizeInPackets(), 1); queue.Pop(); EXPECT_EQ(queue.SizeInPackets(), 0); } TEST(PrioritizedPacketQueue, ReportsPayloadSize) { PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); EXPECT_EQ(queue.SizeInPayloadBytes(), DataSize::Zero()); queue.Push(/*enqueue_time=*/Timestamp::Zero(), CreatePacket(RtpPacketMediaType::kVideo, /*seq_no=*/1)); EXPECT_EQ(queue.SizeInPayloadBytes(), DataSize::Bytes(kDefaultPayloadSize)); queue.Pop(); EXPECT_EQ(queue.SizeInPayloadBytes(), DataSize::Zero()); } TEST(PrioritizedPacketQueue, ReportsPaddingSize) { PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); EXPECT_EQ(queue.SizeInPayloadBytes(), DataSize::Zero()); static constexpr DataSize kPaddingSize = DataSize::Bytes(190); auto packet = std::make_unique(/*extensions=*/nullptr); packet->set_packet_type(RtpPacketMediaType::kPadding); packet->SetSsrc(kDefaultSsrc); packet->SetSequenceNumber(/*seq=*/1); packet->SetPadding(kPaddingSize.bytes()); queue.Push(/*enqueue_time=*/Timestamp::Zero(), std::move(packet)); EXPECT_EQ(queue.SizeInPayloadBytes(), kPaddingSize); queue.Pop(); EXPECT_EQ(queue.SizeInPayloadBytes(), DataSize::Zero()); } TEST(PrioritizedPacketQueue, ReportsOldestEnqueueTime) { PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); EXPECT_EQ(queue.OldestEnqueueTime(), Timestamp::MinusInfinity()); // Add three packets, with the middle packet having higher prio. queue.Push(Timestamp::Millis(10), CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/1)); queue.Push(Timestamp::Millis(20), CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2)); queue.Push(Timestamp::Millis(30), CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/3)); EXPECT_EQ(queue.OldestEnqueueTime(), Timestamp::Millis(10)); queue.Pop(); // Pop packet with enqueue time 20. EXPECT_EQ(queue.OldestEnqueueTime(), Timestamp::Millis(10)); queue.Pop(); // Pop packet with enqueue time 10. EXPECT_EQ(queue.OldestEnqueueTime(), Timestamp::Millis(30)); queue.Pop(); // Pop packet with enqueue time 30, queue empty again. EXPECT_EQ(queue.OldestEnqueueTime(), Timestamp::MinusInfinity()); } TEST(PrioritizedPacketQueue, ReportsAverageQueueTime) { PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Zero()); // Add three packets, with the middle packet having higher prio. queue.Push(Timestamp::Millis(10), CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/1)); queue.Push(Timestamp::Millis(20), CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2)); queue.Push(Timestamp::Millis(30), CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/3)); queue.UpdateAverageQueueTime(Timestamp::Millis(40)); // Packets have waited 30, 20, 10 ms -> average = 20ms. EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(20)); queue.Pop(); // Pop packet with enqueue time 20. EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(20)); queue.Pop(); // Pop packet with enqueue time 10. EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(10)); queue.Pop(); // Pop packet with enqueue time 30, queue empty again. EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Zero()); } TEST(PrioritizedPacketQueue, SubtractsPusedTimeFromAverageQueueTime) { PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Zero()); // Add a packet and then enable paused state. queue.Push(Timestamp::Millis(100), CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/1)); queue.SetPauseState(true, Timestamp::Millis(600)); EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(500)); // Enqueue a packet 500ms into the paused state. Queue time of // original packet is still seen as 500ms and new one has 0ms giving // an average of 250ms. queue.Push(Timestamp::Millis(1100), CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2)); EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(250)); // Unpause some time later, queue time still unchanged. queue.SetPauseState(false, Timestamp::Millis(1600)); EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(250)); // Update queue time 500ms after pause state ended. queue.UpdateAverageQueueTime(Timestamp::Millis(2100)); EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(750)); } TEST(PrioritizedPacketQueue, ReportsLeadingPacketEnqueueTime) { PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kAudio), Timestamp::MinusInfinity()); EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kVideo), Timestamp::MinusInfinity()); queue.Push(Timestamp::Millis(10), CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/1)); EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kAudio), Timestamp::MinusInfinity()); EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kVideo), Timestamp::Millis(10)); queue.Push(Timestamp::Millis(20), CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/2)); EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kAudio), Timestamp::Millis(20)); EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kVideo), Timestamp::Millis(10)); queue.Pop(); // Pop audio packet. EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kAudio), Timestamp::MinusInfinity()); EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kVideo), Timestamp::Millis(10)); queue.Pop(); // Pop video packet. EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kAudio), Timestamp::MinusInfinity()); EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kVideo), Timestamp::MinusInfinity()); } TEST(PrioritizedPacketQueue, PushAndPopUpdatesSizeInPacketsPerRtpPacketMediaType) { Timestamp now = Timestamp::Zero(); PrioritizedPacketQueue queue(now); // Initially all sizes are zero. for (size_t i = 0; i < kNumMediaTypes; ++i) { EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[i], 0); } // Push packets. queue.Push(now, CreatePacket(RtpPacketMediaType::kAudio, 1)); EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[static_cast( RtpPacketMediaType::kAudio)], 1); queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, 2)); EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[static_cast( RtpPacketMediaType::kVideo)], 1); queue.Push(now, CreatePacket(RtpPacketMediaType::kRetransmission, 3)); EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[static_cast( RtpPacketMediaType::kRetransmission)], 1); queue.Push(now, CreatePacket(RtpPacketMediaType::kForwardErrorCorrection, 4)); EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[static_cast( RtpPacketMediaType::kForwardErrorCorrection)], 1); queue.Push(now, CreatePacket(RtpPacketMediaType::kPadding, 5)); EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[static_cast( RtpPacketMediaType::kPadding)], 1); // Now all sizes are 1. for (size_t i = 0; i < kNumMediaTypes; ++i) { EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[i], 1); } // Popping happens in a priority order based on media type. This test does not // assert what this order is, only that the counter for the popped packet's // media type is decremented. for (size_t i = 0; i < kNumMediaTypes; ++i) { auto popped_packet = queue.Pop(); EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[static_cast( popped_packet->packet_type().value())], 0); } // We've popped all packets, so all sizes are zero. for (size_t i = 0; i < kNumMediaTypes; ++i) { EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[i], 0); } } TEST(PrioritizedPacketQueue, ClearsPackets) { Timestamp now = Timestamp::Zero(); PrioritizedPacketQueue queue(now); const uint32_t kSsrc = 1; // Add two packets of each type, all using the same SSRC. int sequence_number = 0; for (size_t i = 0; i < kNumMediaTypes; ++i) { queue.Push(now, CreatePacket(static_cast(i), sequence_number++, kSsrc)); queue.Push(now, CreatePacket(static_cast(i), sequence_number++, kSsrc)); } EXPECT_EQ(queue.SizeInPackets(), 2 * int{kNumMediaTypes}); // Remove all of them. queue.RemovePacketsForSsrc(kSsrc); EXPECT_TRUE(queue.Empty()); } TEST(PrioritizedPacketQueue, ClearPacketsAffectsOnlySpecifiedSsrc) { Timestamp now = Timestamp::Zero(); PrioritizedPacketQueue queue(now); const uint32_t kRemovingSsrc = 1; const uint32_t kStayingSsrc = 2; // Add an audio packet and a retransmission for the SSRC we will remove, // ensuring they are first in line. queue.Push( now, CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/1, kRemovingSsrc)); queue.Push(now, CreatePacket(RtpPacketMediaType::kRetransmission, /*seq=*/2, kRemovingSsrc)); // Add a video packet and a retransmission for the SSRC that will remain. // The retransmission packets now both have pointers to their respective qeues // from the same prio level. queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/3, kStayingSsrc)); queue.Push(now, CreatePacket(RtpPacketMediaType::kRetransmission, /*seq=*/4, kStayingSsrc)); EXPECT_EQ(queue.SizeInPackets(), 4); // Clear the first two packets. queue.RemovePacketsForSsrc(kRemovingSsrc); EXPECT_EQ(queue.SizeInPackets(), 2); // We should get the single remaining retransmission first, then the video // packet. EXPECT_EQ(queue.Pop()->SequenceNumber(), 4); EXPECT_EQ(queue.Pop()->SequenceNumber(), 3); EXPECT_TRUE(queue.Empty()); } } // namespace webrtc