| /* | |
| * 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 <cstddef> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <utility> | |
| #include "api/units/data_size.h" | |
| #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 "rtc_base/checks.h" | |
| #include "test/gtest.h" | |
| namespace webrtc { | |
| namespace { | |
| constexpr uint32_t kDefaultSsrc = 123; | |
| constexpr int kDefaultPayloadSize = 789; | |
| std::unique_ptr<RtpPacketToSend> CreatePacket(RtpPacketMediaType type, | |
| uint16_t seq, | |
| uint32_t ssrc = kDefaultSsrc, | |
| bool is_key_frame = false) { | |
| auto packet = std::make_unique<RtpPacketToSend>(/*extensions=*/nullptr); | |
| packet->set_packet_type(type); | |
| packet->SetSsrc(ssrc); | |
| packet->SetSequenceNumber(seq); | |
| packet->SetPayloadSize(kDefaultPayloadSize); | |
| packet->set_is_key_frame(is_key_frame); | |
| return packet; | |
| } | |
| std::unique_ptr<RtpPacketToSend> CreateRetransmissionPacket( | |
| RtpPacketMediaType original_type, | |
| uint16_t seq, | |
| uint32_t ssrc = kDefaultSsrc) { | |
| auto packet = std::make_unique<RtpPacketToSend>(/*extensions=*/nullptr); | |
| packet->set_packet_type(original_type); | |
| packet->set_packet_type(RtpPacketMediaType::kRetransmission); | |
| RTC_DCHECK(packet->packet_type() == RtpPacketMediaType::kRetransmission); | |
| if (original_type == RtpPacketMediaType::kVideo) { | |
| RTC_DCHECK(packet->original_packet_type() == | |
| RtpPacketToSend::OriginalType::kVideo); | |
| } else { | |
| RTC_DCHECK(packet->original_packet_type() == | |
| RtpPacketToSend::OriginalType::kAudio); | |
| } | |
| packet->SetSsrc(ssrc); | |
| packet->SetSequenceNumber(seq); | |
| 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, | |
| CreateRetransmissionPacket(RtpPacketMediaType::kVideo, /*seq=*/4)); | |
| queue.Push(now, | |
| CreateRetransmissionPacket(RtpPacketMediaType::kAudio, /*seq=*/5)); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/6)); | |
| // Packets should be returned in high to low order. | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 6); | |
| // Audio and video retransmission has same prio, but video was enqueued first. | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 4); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 5); | |
| // 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, | |
| PrioritizeAudioRetransmissionBeforeVideoRetransmissionIfConfigured) { | |
| Timestamp now = Timestamp::Zero(); | |
| PrioritizedPacketQueue queue(now, /*prioritize_audio_retransmission=*/true); | |
| // Add packets in low to high packet order. | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/3)); | |
| queue.Push(now, | |
| CreateRetransmissionPacket(RtpPacketMediaType::kVideo, /*seq=*/4)); | |
| queue.Push(now, | |
| CreateRetransmissionPacket(RtpPacketMediaType::kAudio, /*seq=*/5)); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/6)); | |
| // Packets should be returned in high to low order. | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 6); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 5); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 4); | |
| } | |
| 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<RtpPacketToSend>(/*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, ReportsLeadingPacketEnqueueTimeForRetransmission) { | |
| PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero(), | |
| /*prioritize_audio_retransmission=*/true); | |
| EXPECT_EQ(queue.LeadingPacketEnqueueTimeForRetransmission(), | |
| Timestamp::PlusInfinity()); | |
| queue.Push(Timestamp::Millis(10), | |
| CreateRetransmissionPacket(RtpPacketMediaType::kVideo, /*seq=*/1)); | |
| queue.Push(Timestamp::Millis(11), | |
| CreateRetransmissionPacket(RtpPacketMediaType::kAudio, /*seq=*/2)); | |
| EXPECT_EQ(queue.LeadingPacketEnqueueTimeForRetransmission(), | |
| Timestamp::Millis(10)); | |
| queue.Pop(); // Pop audio retransmission since it has higher prio. | |
| EXPECT_EQ(queue.LeadingPacketEnqueueTimeForRetransmission(), | |
| Timestamp::Millis(10)); | |
| queue.Pop(); // Pop video retransmission. | |
| EXPECT_EQ(queue.LeadingPacketEnqueueTimeForRetransmission(), | |
| Timestamp::PlusInfinity()); | |
| } | |
| 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<size_t>( | |
| RtpPacketMediaType::kAudio)], | |
| 1); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, 2)); | |
| EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[static_cast<size_t>( | |
| RtpPacketMediaType::kVideo)], | |
| 1); | |
| queue.Push(now, CreateRetransmissionPacket(RtpPacketMediaType::kVideo, 3)); | |
| EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[static_cast<size_t>( | |
| RtpPacketMediaType::kRetransmission)], | |
| 1); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kForwardErrorCorrection, 4)); | |
| EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[static_cast<size_t>( | |
| RtpPacketMediaType::kForwardErrorCorrection)], | |
| 1); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kPadding, 5)); | |
| EXPECT_EQ(queue.SizeInPacketsPerRtpPacketMediaType()[static_cast<size_t>( | |
| 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<size_t>( | |
| 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<RtpPacketMediaType>(i), | |
| sequence_number++, kSsrc)); | |
| queue.Push(now, CreatePacket(static_cast<RtpPacketMediaType>(i), | |
| sequence_number++, kSsrc)); | |
| } | |
| EXPECT_EQ(queue.SizeInPackets(), 2 * int{kNumMediaTypes}); | |
| // Remove all of them. | |
| queue.RemovePacketsForSsrc(kSsrc); | |
| EXPECT_TRUE(queue.Empty()); | |
| 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, CreateRetransmissionPacket(RtpPacketMediaType::kVideo, | |
| /*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, CreateRetransmissionPacket(RtpPacketMediaType::kVideo, | |
| /*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()); | |
| } | |
| TEST(PrioritizedPacketQueue, ReportsKeyframePackets) { | |
| Timestamp now = Timestamp::Zero(); | |
| PrioritizedPacketQueue queue(now); | |
| const uint32_t kVideoSsrc1 = 1234; | |
| const uint32_t kVideoSsrc2 = 2345; | |
| EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc1)); | |
| EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc2)); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/1, | |
| kVideoSsrc1, /*is_key_frame=*/true)); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/11, | |
| kVideoSsrc2, /*is_key_frame=*/false)); | |
| EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc1)); | |
| EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc2)); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2, | |
| kVideoSsrc1, /*is_key_frame=*/true)); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/12, | |
| kVideoSsrc2, /*is_key_frame=*/true)); | |
| EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc1)); | |
| EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc2)); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/3, | |
| kVideoSsrc1, /*is_key_frame=*/false)); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/13, | |
| kVideoSsrc2, /*is_key_frame=*/true)); | |
| EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc1)); | |
| EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc2)); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 1); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 11); | |
| EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc1)); | |
| EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc2)); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 2); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 12); | |
| EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc1)); | |
| EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc2)); | |
| queue.RemovePacketsForSsrc(kVideoSsrc2); | |
| EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc1)); | |
| EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc2)); | |
| } | |
| TEST(PrioritizedPacketQueue, PacketsDroppedIfNotPulledWithinTttl) { | |
| Timestamp now = Timestamp::Zero(); | |
| PacketQueueTTL ttls; | |
| ttls.audio_retransmission = TimeDelta::Millis(200); | |
| PrioritizedPacketQueue queue(now, /*prioritize_audio_retransmission=*/true, | |
| ttls); | |
| queue.Push(now, | |
| CreateRetransmissionPacket(RtpPacketMediaType::kAudio, /*seq=*/1)); | |
| now += ttls.audio_retransmission + TimeDelta::Millis(1); | |
| EXPECT_EQ(queue.SizeInPackets(), 1); | |
| queue.Push(now, | |
| CreateRetransmissionPacket(RtpPacketMediaType::kAudio, /*seq=*/2)); | |
| EXPECT_EQ(queue.SizeInPackets(), 1); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 2); | |
| } | |
| TEST(PrioritizedPacketQueue, DontSendPacketsAfterTttl) { | |
| Timestamp now = Timestamp::Zero(); | |
| PacketQueueTTL ttls; | |
| ttls.audio_retransmission = TimeDelta::Millis(200); | |
| PrioritizedPacketQueue queue(now, /*prioritize_audio_retransmission=*/true, | |
| ttls); | |
| queue.Push(now, | |
| CreateRetransmissionPacket(RtpPacketMediaType::kAudio, /*seq=*/1)); | |
| now += ttls.audio_retransmission + TimeDelta::Millis(1); | |
| EXPECT_EQ(queue.SizeInPackets(), 1); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2)); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/3)); | |
| // Expect the old packet to have been removed since it was not popped in time. | |
| EXPECT_EQ(queue.SizeInPackets(), 3); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 3); | |
| EXPECT_EQ(queue.SizeInPackets(), 1); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 2); | |
| EXPECT_EQ(queue.SizeInPackets(), 0); | |
| } | |
| TEST(PrioritizedPacketQueue, SendsNewVideoPacketAfterPurgingLastOldRtxPacket) { | |
| Timestamp now = Timestamp::Zero(); | |
| PacketQueueTTL ttls; | |
| ttls.video_retransmission = TimeDelta::Millis(400); | |
| PrioritizedPacketQueue queue(now, /*prioritize_audio_retransmission=*/true, | |
| ttls); | |
| queue.Push(now, | |
| CreateRetransmissionPacket(RtpPacketMediaType::kVideo, /*seq=*/1)); | |
| now += ttls.video_retransmission + TimeDelta::Millis(1); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/2)); | |
| EXPECT_EQ(queue.SizeInPackets(), 2); | |
| // Expect the audio packet to be send and the video retransmission packet to | |
| // be dropped since it is old. | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 2); | |
| EXPECT_EQ(queue.SizeInPackets(), 0); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/3)); | |
| EXPECT_EQ(queue.SizeInPackets(), 1); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 3); | |
| EXPECT_EQ(queue.SizeInPackets(), 0); | |
| } | |
| TEST(PrioritizedPacketQueue, | |
| SendsPacketsAfterTttlIfPrioHigherThanPushedPackets) { | |
| Timestamp now = Timestamp::Zero(); | |
| PacketQueueTTL ttls; | |
| ttls.audio_retransmission = TimeDelta::Millis(200); | |
| PrioritizedPacketQueue queue(now, /*prioritize_audio_retransmission=*/true, | |
| ttls); | |
| queue.Push(now, | |
| CreateRetransmissionPacket(RtpPacketMediaType::kAudio, /*seq=*/1)); | |
| now += ttls.audio_retransmission + TimeDelta::Millis(1); | |
| EXPECT_EQ(queue.SizeInPackets(), 1); | |
| queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2)); | |
| // This test just show that TTL is not enforced strictly. If a new audio | |
| // packet had been queued before a packet was popped, the audio retransmission | |
| // packet would have been dropped. | |
| EXPECT_EQ(queue.SizeInPackets(), 2); | |
| EXPECT_EQ(queue.Pop()->SequenceNumber(), 1); | |
| EXPECT_EQ(queue.SizeInPackets(), 1); | |
| } | |
| } // namespace webrtc |