mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-14 14:20:45 +01:00

This CL adds functionality to remove packets matching a given SSRC from the pacer queue, and calls that with any SSRCs used by an RTP module when that module is removed. Bug: chromium:1395081 Change-Id: I13c0285ddca600e784ad04a806727a508ede6dcc Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/287124 Reviewed-by: Jakob Ivarsson <jakobi@webrtc.org> Commit-Queue: Erik Språng <sprang@webrtc.org> Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Reviewed-by: Philip Eliasson <philipel@webrtc.org> Cr-Commit-Position: refs/heads/main@{#38880}
363 lines
14 KiB
C++
363 lines
14 KiB
C++
/*
|
|
* 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 <utility>
|
|
|
|
#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<RtpPacketToSend> CreatePacket(RtpPacketMediaType type,
|
|
uint16_t sequence_number,
|
|
uint32_t ssrc = kDefaultSsrc) {
|
|
auto packet = std::make_unique<RtpPacketToSend>(/*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<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,
|
|
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, CreatePacket(RtpPacketMediaType::kRetransmission, 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());
|
|
}
|
|
|
|
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
|