Source code
Revision control
Copy as Markdown
Other Tools
/*
* 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