webrtc/modules/pacing/pacing_controller_unittest.cc
Erik Språng df9e51a190 Fix issue with pacing rate after long queue times.
A recent cleanup cl (r36900) had an unintended side-effect.

If the queue-time limit is expected to be hit, we adjust the pacing
bitrate up to make sure all packets are sent within the nominal time
frame.
However after that change we stopped adjusting the pacing rate back to
normal levels when queue clears - at least not until the next BWE
update (which is fairly often - but not immediate).

This CL fixes that, and also makes sure whe properly update the
adjusted media rate on enqueu, dequeue and set rate calls.

Bug: webrtc:10809
Change-Id: If00dc35169f1a1347fea6eb44fdb2868282ed3b7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/265387
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Commit-Queue: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37178}
2022-06-10 11:43:14 +00:00

2068 lines
82 KiB
C++

/*
* Copyright (c) 2019 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/pacing_controller.h"
#include <algorithm>
#include <list>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "modules/pacing/packet_router.h"
#include "system_wrappers/include/clock.h"
#include "test/explicit_key_value_config.h"
#include "test/gmock.h"
#include "test/gtest.h"
using ::testing::_;
using ::testing::Field;
using ::testing::Pointee;
using ::testing::Property;
using ::testing::Return;
using ::testing::WithoutArgs;
using ::webrtc::test::ExplicitKeyValueConfig;
namespace webrtc {
namespace {
constexpr DataRate kFirstClusterRate = DataRate::KilobitsPerSec(900);
constexpr DataRate kSecondClusterRate = DataRate::KilobitsPerSec(1800);
// The error stems from truncating the time interval of probe packets to integer
// values. This results in probing slightly higher than the target bitrate.
// For 1.8 Mbps, this comes to be about 120 kbps with 1200 probe packets.
constexpr DataRate kProbingErrorMargin = DataRate::KilobitsPerSec(150);
const float kPaceMultiplier = 2.5f;
constexpr uint32_t kAudioSsrc = 12345;
constexpr uint32_t kVideoSsrc = 234565;
constexpr DataRate kTargetRate = DataRate::KilobitsPerSec(800);
std::unique_ptr<RtpPacketToSend> BuildPacket(RtpPacketMediaType type,
uint32_t ssrc,
uint16_t sequence_number,
int64_t capture_time_ms,
size_t size) {
auto packet = std::make_unique<RtpPacketToSend>(nullptr);
packet->set_packet_type(type);
packet->SetSsrc(ssrc);
packet->SetSequenceNumber(sequence_number);
packet->set_capture_time(Timestamp::Millis(capture_time_ms));
packet->SetPayloadSize(size);
return packet;
}
class MediaStream {
public:
MediaStream(SimulatedClock& clock,
RtpPacketMediaType type,
uint32_t ssrc,
size_t packet_size)
: clock_(clock), type_(type), ssrc_(ssrc), packet_size_(packet_size) {}
std::unique_ptr<RtpPacketToSend> BuildNextPacket() {
return BuildPacket(type_, ssrc_, seq_num_++, clock_.TimeInMilliseconds(),
packet_size_);
}
std::unique_ptr<RtpPacketToSend> BuildNextPacket(size_t size) {
return BuildPacket(type_, ssrc_, seq_num_++, clock_.TimeInMilliseconds(),
size);
}
private:
SimulatedClock& clock_;
const RtpPacketMediaType type_;
const uint32_t ssrc_;
const size_t packet_size_;
uint16_t seq_num_ = 1000;
};
// Mock callback proxy, where both new and old api redirects to common mock
// methods that focus on core aspects.
class MockPacingControllerCallback : public PacingController::PacketSender {
public:
void SendPacket(std::unique_ptr<RtpPacketToSend> packet,
const PacedPacketInfo& cluster_info) override {
SendPacket(packet->Ssrc(), packet->SequenceNumber(),
packet->capture_time().ms(),
packet->packet_type() == RtpPacketMediaType::kRetransmission,
packet->packet_type() == RtpPacketMediaType::kPadding);
}
std::vector<std::unique_ptr<RtpPacketToSend>> GeneratePadding(
DataSize target_size) override {
std::vector<std::unique_ptr<RtpPacketToSend>> ret;
size_t padding_size = SendPadding(target_size.bytes());
if (padding_size > 0) {
auto packet = std::make_unique<RtpPacketToSend>(nullptr);
packet->SetPayloadSize(padding_size);
packet->set_packet_type(RtpPacketMediaType::kPadding);
ret.emplace_back(std::move(packet));
}
return ret;
}
MOCK_METHOD(void,
SendPacket,
(uint32_t ssrc,
uint16_t sequence_number,
int64_t capture_timestamp,
bool retransmission,
bool padding));
MOCK_METHOD(std::vector<std::unique_ptr<RtpPacketToSend>>,
FetchFec,
(),
(override));
MOCK_METHOD(size_t, SendPadding, (size_t target_size));
};
// Mock callback implementing the raw api.
class MockPacketSender : public PacingController::PacketSender {
public:
MOCK_METHOD(void,
SendPacket,
(std::unique_ptr<RtpPacketToSend> packet,
const PacedPacketInfo& cluster_info),
(override));
MOCK_METHOD(std::vector<std::unique_ptr<RtpPacketToSend>>,
FetchFec,
(),
(override));
MOCK_METHOD(std::vector<std::unique_ptr<RtpPacketToSend>>,
GeneratePadding,
(DataSize target_size),
(override));
};
class PacingControllerPadding : public PacingController::PacketSender {
public:
static const size_t kPaddingPacketSize = 224;
PacingControllerPadding() : padding_sent_(0), total_bytes_sent_(0) {}
void SendPacket(std::unique_ptr<RtpPacketToSend> packet,
const PacedPacketInfo& pacing_info) override {
total_bytes_sent_ += packet->payload_size();
}
std::vector<std::unique_ptr<RtpPacketToSend>> FetchFec() override {
return {};
}
std::vector<std::unique_ptr<RtpPacketToSend>> GeneratePadding(
DataSize target_size) override {
size_t num_packets =
(target_size.bytes() + kPaddingPacketSize - 1) / kPaddingPacketSize;
std::vector<std::unique_ptr<RtpPacketToSend>> packets;
for (size_t i = 0; i < num_packets; ++i) {
packets.emplace_back(std::make_unique<RtpPacketToSend>(nullptr));
packets.back()->SetPadding(kPaddingPacketSize);
packets.back()->set_packet_type(RtpPacketMediaType::kPadding);
padding_sent_ += kPaddingPacketSize;
}
return packets;
}
size_t padding_sent() { return padding_sent_; }
size_t total_bytes_sent() { return total_bytes_sent_; }
private:
size_t padding_sent_;
size_t total_bytes_sent_;
};
class PacingControllerProbing : public PacingController::PacketSender {
public:
PacingControllerProbing() : packets_sent_(0), padding_sent_(0) {}
void SendPacket(std::unique_ptr<RtpPacketToSend> packet,
const PacedPacketInfo& pacing_info) override {
if (packet->packet_type() != RtpPacketMediaType::kPadding) {
++packets_sent_;
}
last_pacing_info_ = pacing_info;
}
std::vector<std::unique_ptr<RtpPacketToSend>> FetchFec() override {
return {};
}
std::vector<std::unique_ptr<RtpPacketToSend>> GeneratePadding(
DataSize target_size) override {
// From RTPSender:
// Max in the RFC 3550 is 255 bytes, we limit it to be modulus 32 for SRTP.
const DataSize kMaxPadding = DataSize::Bytes(224);
std::vector<std::unique_ptr<RtpPacketToSend>> packets;
while (target_size > DataSize::Zero()) {
DataSize padding_size = std::min(kMaxPadding, target_size);
packets.emplace_back(std::make_unique<RtpPacketToSend>(nullptr));
packets.back()->SetPadding(padding_size.bytes());
packets.back()->set_packet_type(RtpPacketMediaType::kPadding);
padding_sent_ += padding_size.bytes();
target_size -= padding_size;
}
return packets;
}
int packets_sent() const { return packets_sent_; }
int padding_sent() const { return padding_sent_; }
int total_packets_sent() const { return packets_sent_ + padding_sent_; }
PacedPacketInfo last_pacing_info() const { return last_pacing_info_; }
private:
int packets_sent_;
int padding_sent_;
PacedPacketInfo last_pacing_info_;
};
class PacingControllerTest : public ::testing::Test {
protected:
PacingControllerTest() : clock_(123456), trials_("") {}
void SendAndExpectPacket(PacingController* pacer,
RtpPacketMediaType type,
uint32_t ssrc,
uint16_t sequence_number,
int64_t capture_time_ms,
size_t size) {
pacer->EnqueuePacket(
BuildPacket(type, ssrc, sequence_number, capture_time_ms, size));
EXPECT_CALL(callback_,
SendPacket(ssrc, sequence_number, capture_time_ms,
type == RtpPacketMediaType::kRetransmission, false));
}
void AdvanceTimeUntil(webrtc::Timestamp time) {
Timestamp now = clock_.CurrentTime();
clock_.AdvanceTime(std::max(TimeDelta::Zero(), time - now));
}
void ConsumeInitialBudget(PacingController* pacer) {
const uint32_t kSsrc = 54321;
uint16_t sequence_number = 1234;
int64_t capture_time_ms = clock_.TimeInMilliseconds();
const size_t kPacketSize = 250;
EXPECT_TRUE(pacer->OldestPacketEnqueueTime().IsInfinite());
// Due to the multiplicative factor we can send 5 packets during a send
// interval. (network capacity * multiplier / (8 bits per byte *
// (packet size * #send intervals per second)
const size_t packets_to_send_per_interval =
kTargetRate.bps() * kPaceMultiplier / (8 * kPacketSize * 200);
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
SendAndExpectPacket(pacer, RtpPacketMediaType::kVideo, kSsrc,
sequence_number++, capture_time_ms, kPacketSize);
}
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
}
SimulatedClock clock_;
MediaStream audio_ = MediaStream(clock_,
/*type*/ RtpPacketMediaType::kAudio,
/*ssrc*/ kAudioSsrc,
/*packet_size*/ 100);
MediaStream video_ = MediaStream(clock_,
/*type*/ RtpPacketMediaType::kVideo,
/*ssrc*/ kVideoSsrc,
/*packet_size*/ 1000);
::testing::NiceMock<MockPacingControllerCallback> callback_;
ExplicitKeyValueConfig trials_;
};
TEST_F(PacingControllerTest, DefaultNoPaddingInSilence) {
const test::ExplicitKeyValueConfig trials("");
PacingController pacer(&clock_, &callback_, trials);
pacer.SetPacingRates(kTargetRate, DataRate::Zero());
// Video packet to reset last send time and provide padding data.
pacer.EnqueuePacket(video_.BuildNextPacket());
EXPECT_CALL(callback_, SendPacket).Times(1);
clock_.AdvanceTimeMilliseconds(5);
pacer.ProcessPackets();
EXPECT_CALL(callback_, SendPadding).Times(0);
// Waiting 500 ms should not trigger sending of padding.
clock_.AdvanceTimeMilliseconds(500);
pacer.ProcessPackets();
}
TEST_F(PacingControllerTest, PaddingInSilenceWithTrial) {
const test::ExplicitKeyValueConfig trials(
"WebRTC-Pacer-PadInSilence/Enabled/");
PacingController pacer(&clock_, &callback_, trials);
pacer.SetPacingRates(kTargetRate, DataRate::Zero());
// Video packet to reset last send time and provide padding data.
pacer.EnqueuePacket(video_.BuildNextPacket());
EXPECT_CALL(callback_, SendPacket).Times(2);
clock_.AdvanceTimeMilliseconds(5);
pacer.ProcessPackets();
EXPECT_CALL(callback_, SendPadding).WillOnce(Return(1000));
// Waiting 500 ms should trigger sending of padding.
clock_.AdvanceTimeMilliseconds(500);
pacer.ProcessPackets();
}
TEST_F(PacingControllerTest, CongestionWindowAffectsAudioInTrial) {
const test::ExplicitKeyValueConfig trials("WebRTC-Pacer-BlockAudio/Enabled/");
EXPECT_CALL(callback_, SendPadding).Times(0);
PacingController pacer(&clock_, &callback_, trials);
pacer.SetPacingRates(DataRate::KilobitsPerSec(10000), DataRate::Zero());
// Video packet fills congestion window.
pacer.EnqueuePacket(video_.BuildNextPacket());
EXPECT_CALL(callback_, SendPacket).Times(1);
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
pacer.SetCongested(true);
// Audio packet blocked due to congestion.
pacer.EnqueuePacket(audio_.BuildNextPacket());
EXPECT_CALL(callback_, SendPacket).Times(0);
// Forward time to where we send keep-alive.
EXPECT_CALL(callback_, SendPadding(1)).Times(2);
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
// Audio packet unblocked when congestion window clear.
::testing::Mock::VerifyAndClearExpectations(&callback_);
pacer.SetCongested(false);
EXPECT_CALL(callback_, SendPacket).Times(1);
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
}
TEST_F(PacingControllerTest, DefaultCongestionWindowDoesNotAffectAudio) {
EXPECT_CALL(callback_, SendPadding).Times(0);
const test::ExplicitKeyValueConfig trials("");
PacingController pacer(&clock_, &callback_, trials);
pacer.SetPacingRates(DataRate::BitsPerSec(10000000), DataRate::Zero());
// Video packet fills congestion window.
pacer.EnqueuePacket(video_.BuildNextPacket());
EXPECT_CALL(callback_, SendPacket).Times(1);
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
pacer.SetCongested(true);
// Audio not blocked due to congestion.
pacer.EnqueuePacket(audio_.BuildNextPacket());
EXPECT_CALL(callback_, SendPacket).Times(1);
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
}
TEST_F(PacingControllerTest, BudgetAffectsAudioInTrial) {
ExplicitKeyValueConfig trials("WebRTC-Pacer-BlockAudio/Enabled/");
PacingController pacer(&clock_, &callback_, trials);
const size_t kPacketSize = 1000;
const int kProcessIntervalsPerSecond = 1000 / 5;
DataRate pacing_rate =
DataRate::BitsPerSec(kPacketSize / 3 * 8 * kProcessIntervalsPerSecond);
pacer.SetPacingRates(pacing_rate, DataRate::Zero());
// Video fills budget for following process periods.
pacer.EnqueuePacket(video_.BuildNextPacket(kPacketSize));
EXPECT_CALL(callback_, SendPacket).Times(1);
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
// Audio packet blocked due to budget limit.
pacer.EnqueuePacket(audio_.BuildNextPacket());
Timestamp wait_start_time = clock_.CurrentTime();
Timestamp wait_end_time = Timestamp::MinusInfinity();
EXPECT_CALL(callback_, SendPacket).WillOnce(WithoutArgs([&]() {
wait_end_time = clock_.CurrentTime();
}));
while (!wait_end_time.IsFinite()) {
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
}
const TimeDelta expected_wait_time =
DataSize::Bytes(kPacketSize) / pacing_rate;
// Verify delay is near expectation, within timing margin.
EXPECT_LT(((wait_end_time - wait_start_time) - expected_wait_time).Abs(),
PacingController::kMinSleepTime);
}
TEST_F(PacingControllerTest, DefaultBudgetDoesNotAffectAudio) {
const size_t kPacketSize = 1000;
EXPECT_CALL(callback_, SendPadding).Times(0);
const test::ExplicitKeyValueConfig trials("");
PacingController pacer(&clock_, &callback_, trials);
const int kProcessIntervalsPerSecond = 1000 / 5;
pacer.SetPacingRates(
DataRate::BitsPerSec(kPacketSize / 3 * 8 * kProcessIntervalsPerSecond),
DataRate::Zero());
// Video fills budget for following process periods.
pacer.EnqueuePacket(video_.BuildNextPacket(kPacketSize));
EXPECT_CALL(callback_, SendPacket).Times(1);
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
// Audio packet not blocked due to budget limit.
EXPECT_CALL(callback_, SendPacket).Times(1);
pacer.EnqueuePacket(audio_.BuildNextPacket());
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
}
TEST_F(PacingControllerTest, FirstSentPacketTimeIsSet) {
const Timestamp kStartTime = clock_.CurrentTime();
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
// No packet sent.
EXPECT_FALSE(pacer->FirstSentPacketTime().has_value());
pacer->EnqueuePacket(video_.BuildNextPacket());
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
EXPECT_EQ(kStartTime, pacer->FirstSentPacketTime());
}
TEST_F(PacingControllerTest, QueueAndPacePackets) {
const uint32_t kSsrc = 12345;
uint16_t sequence_number = 1234;
const DataSize kPackeSize = DataSize::Bytes(250);
const TimeDelta kSendInterval = TimeDelta::Millis(5);
// Due to the multiplicative factor we can send 5 packets during a 5ms send
// interval. (send interval * network capacity * multiplier / packet size)
const size_t kPacketsToSend = (kSendInterval * kTargetRate).bytes() *
kPaceMultiplier / kPackeSize.bytes();
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
for (size_t i = 0; i < kPacketsToSend; ++i) {
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, kSsrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPackeSize.bytes());
}
EXPECT_CALL(callback_, SendPadding).Times(0);
// Enqueue one extra packet.
int64_t queued_packet_timestamp = clock_.TimeInMilliseconds();
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, kSsrc,
sequence_number, queued_packet_timestamp,
kPackeSize.bytes()));
EXPECT_EQ(kPacketsToSend + 1, pacer->QueueSizePackets());
// Send packets until the initial kPacketsToSend packets are done.
Timestamp start_time = clock_.CurrentTime();
while (pacer->QueueSizePackets() > 1) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
EXPECT_LT(clock_.CurrentTime() - start_time, kSendInterval);
// Proceed till last packet can be sent.
EXPECT_CALL(callback_, SendPacket(kSsrc, sequence_number,
queued_packet_timestamp, false, false))
.Times(1);
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
EXPECT_GE(clock_.CurrentTime() - start_time, kSendInterval);
EXPECT_EQ(pacer->QueueSizePackets(), 0u);
}
TEST_F(PacingControllerTest, PaceQueuedPackets) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
const size_t kPacketSize = 250;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
// Due to the multiplicative factor we can send 5 packets during a send
// interval. (network capacity * multiplier / (8 bits per byte *
// (packet size * #send intervals per second)
const size_t packets_to_send_per_interval =
kTargetRate.bps() * kPaceMultiplier / (8 * kPacketSize * 200);
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPacketSize);
}
for (size_t j = 0; j < packets_to_send_per_interval * 10; ++j) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++,
clock_.TimeInMilliseconds(), kPacketSize));
}
EXPECT_EQ(packets_to_send_per_interval + packets_to_send_per_interval * 10,
pacer->QueueSizePackets());
while (pacer->QueueSizePackets() > packets_to_send_per_interval * 10) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
EXPECT_EQ(pacer->QueueSizePackets(), packets_to_send_per_interval * 10);
EXPECT_CALL(callback_, SendPadding).Times(0);
EXPECT_CALL(callback_, SendPacket(ssrc, _, _, false, false))
.Times(pacer->QueueSizePackets());
const TimeDelta expected_pace_time =
DataSize::Bytes(pacer->QueueSizePackets() * kPacketSize) /
(kPaceMultiplier * kTargetRate);
Timestamp start_time = clock_.CurrentTime();
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
const TimeDelta actual_pace_time = clock_.CurrentTime() - start_time;
EXPECT_LT((actual_pace_time - expected_pace_time).Abs(),
PacingController::kMinSleepTime);
EXPECT_EQ(0u, pacer->QueueSizePackets());
AdvanceTimeUntil(pacer->NextSendTime());
EXPECT_EQ(0u, pacer->QueueSizePackets());
pacer->ProcessPackets();
// Send some more packet, just show that we can..?
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, clock_.TimeInMilliseconds(), 250);
}
EXPECT_EQ(packets_to_send_per_interval, pacer->QueueSizePackets());
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
EXPECT_EQ(0u, pacer->QueueSizePackets());
}
TEST_F(PacingControllerTest, RepeatedRetransmissionsAllowed) {
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
// Send one packet, then two retransmissions of that packet.
for (size_t i = 0; i < 3; i++) {
constexpr uint32_t ssrc = 333;
constexpr uint16_t sequence_number = 444;
constexpr size_t bytes = 250;
bool is_retransmission = (i != 0); // Original followed by retransmissions.
SendAndExpectPacket(pacer.get(),
is_retransmission ? RtpPacketMediaType::kRetransmission
: RtpPacketMediaType::kVideo,
ssrc, sequence_number, clock_.TimeInMilliseconds(),
bytes);
clock_.AdvanceTimeMilliseconds(5);
}
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
}
TEST_F(PacingControllerTest,
CanQueuePacketsWithSameSequenceNumberOnDifferentSsrcs) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number, clock_.TimeInMilliseconds(), 250);
// Expect packet on second ssrc to be queued and sent as well.
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc + 1,
sequence_number, clock_.TimeInMilliseconds(), 250);
clock_.AdvanceTimeMilliseconds(1000);
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
}
TEST_F(PacingControllerTest, Padding) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
const size_t kPacketSize = 250;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate);
const size_t kPacketsToSend = 20;
for (size_t i = 0; i < kPacketsToSend; ++i) {
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPacketSize);
}
const TimeDelta expected_pace_time =
DataSize::Bytes(pacer->QueueSizePackets() * kPacketSize) /
(kPaceMultiplier * kTargetRate);
EXPECT_CALL(callback_, SendPadding).Times(0);
// Only the media packets should be sent.
Timestamp start_time = clock_.CurrentTime();
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
const TimeDelta actual_pace_time = clock_.CurrentTime() - start_time;
EXPECT_LE((actual_pace_time - expected_pace_time).Abs(),
PacingController::kMinSleepTime);
// Pacing media happens at 2.5x, but padding was configured with 1.0x
// factor. We have to wait until the padding debt is gone before we start
// sending padding.
const TimeDelta time_to_padding_debt_free =
(expected_pace_time * kPaceMultiplier) - actual_pace_time;
clock_.AdvanceTime(time_to_padding_debt_free -
PacingController::kMinSleepTime);
pacer->ProcessPackets();
// Send 10 padding packets.
const size_t kPaddingPacketsToSend = 10;
DataSize padding_sent = DataSize::Zero();
size_t packets_sent = 0;
Timestamp first_send_time = Timestamp::MinusInfinity();
Timestamp last_send_time = Timestamp::MinusInfinity();
EXPECT_CALL(callback_, SendPadding)
.Times(kPaddingPacketsToSend)
.WillRepeatedly([&](size_t target_size) {
++packets_sent;
if (packets_sent < kPaddingPacketsToSend) {
// Don't count bytes of last packet, instead just
// use this as the time the last packet finished
// sending.
padding_sent += DataSize::Bytes(target_size);
}
if (first_send_time.IsInfinite()) {
first_send_time = clock_.CurrentTime();
} else {
last_send_time = clock_.CurrentTime();
}
return target_size;
});
EXPECT_CALL(callback_, SendPacket(_, _, _, false, true))
.Times(kPaddingPacketsToSend);
while (packets_sent < kPaddingPacketsToSend) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
// Verify rate of sent padding.
TimeDelta padding_duration = last_send_time - first_send_time;
DataRate padding_rate = padding_sent / padding_duration;
EXPECT_EQ(padding_rate, kTargetRate);
}
TEST_F(PacingControllerTest, NoPaddingBeforeNormalPacket) {
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate);
EXPECT_CALL(callback_, SendPadding).Times(0);
pacer->ProcessPackets();
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
AdvanceTimeUntil(pacer->NextSendTime());
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
int64_t capture_time_ms = 56789;
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, capture_time_ms, 250);
bool padding_sent = false;
EXPECT_CALL(callback_, SendPadding).WillOnce([&](size_t padding) {
padding_sent = true;
return padding;
});
EXPECT_CALL(callback_, SendPacket(_, _, _, _, true)).Times(1);
while (!padding_sent) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
}
TEST_F(PacingControllerTest, VerifyAverageBitrateVaryingMediaPayload) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
int64_t capture_time_ms = 56789;
const TimeDelta kAveragingWindowLength = TimeDelta::Seconds(10);
PacingControllerPadding callback;
auto pacer = std::make_unique<PacingController>(&clock_, &callback, trials_);
pacer->SetProbingEnabled(false);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate);
Timestamp start_time = clock_.CurrentTime();
size_t media_bytes = 0;
while (clock_.CurrentTime() - start_time < kAveragingWindowLength) {
// Maybe add some new media packets corresponding to expected send rate.
int rand_value = rand(); // NOLINT (rand_r instead of rand)
while (
media_bytes <
(kTargetRate * (clock_.CurrentTime() - start_time)).bytes<size_t>()) {
size_t media_payload = rand_value % 400 + 800; // [400, 1200] bytes.
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++, capture_time_ms,
media_payload));
media_bytes += media_payload;
}
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
EXPECT_NEAR(
kTargetRate.bps(),
(DataSize::Bytes(callback.total_bytes_sent()) / kAveragingWindowLength)
.bps(),
(kTargetRate * 0.01 /* 1% error marging */).bps());
}
TEST_F(PacingControllerTest, Priority) {
uint32_t ssrc_low_priority = 12345;
uint32_t ssrc = 12346;
uint16_t sequence_number = 1234;
int64_t capture_time_ms = 56789;
int64_t capture_time_ms_low_priority = 1234567;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
ConsumeInitialBudget(pacer.get());
// Expect normal and low priority to be queued and high to pass through.
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo,
ssrc_low_priority, sequence_number++,
capture_time_ms_low_priority, 250));
const size_t packets_to_send_per_interval =
kTargetRate.bps() * kPaceMultiplier / (8 * 250 * 200);
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kRetransmission, ssrc,
sequence_number++, capture_time_ms, 250));
}
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kAudio, ssrc,
sequence_number++, capture_time_ms, 250));
// Expect all high and normal priority to be sent out first.
EXPECT_CALL(callback_, SendPadding).Times(0);
EXPECT_CALL(callback_, SendPacket(ssrc, _, capture_time_ms, _, _))
.Times(packets_to_send_per_interval + 1);
while (pacer->QueueSizePackets() > 1) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
EXPECT_EQ(1u, pacer->QueueSizePackets());
EXPECT_CALL(callback_, SendPacket(ssrc_low_priority, _,
capture_time_ms_low_priority, _, _));
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
TEST_F(PacingControllerTest, RetransmissionPriority) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
int64_t capture_time_ms = 45678;
int64_t capture_time_ms_retransmission = 56789;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
// Due to the multiplicative factor we can send 5 packets during a send
// interval. (network capacity * multiplier / (8 bits per byte *
// (packet size * #send intervals per second)
const size_t packets_to_send_per_interval =
kTargetRate.bps() * kPaceMultiplier / (8 * 250 * 200);
pacer->ProcessPackets();
EXPECT_EQ(0u, pacer->QueueSizePackets());
// Alternate retransmissions and normal packets.
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++, capture_time_ms, 250));
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kRetransmission, ssrc,
sequence_number++,
capture_time_ms_retransmission, 250));
}
EXPECT_EQ(2 * packets_to_send_per_interval, pacer->QueueSizePackets());
// Expect all retransmissions to be sent out first despite having a later
// capture time.
EXPECT_CALL(callback_, SendPadding).Times(0);
EXPECT_CALL(callback_, SendPacket(_, _, _, false, _)).Times(0);
EXPECT_CALL(callback_,
SendPacket(ssrc, _, capture_time_ms_retransmission, true, _))
.Times(packets_to_send_per_interval);
while (pacer->QueueSizePackets() > packets_to_send_per_interval) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
EXPECT_EQ(packets_to_send_per_interval, pacer->QueueSizePackets());
// Expect the remaining (non-retransmission) packets to be sent.
EXPECT_CALL(callback_, SendPadding).Times(0);
EXPECT_CALL(callback_, SendPacket(_, _, _, true, _)).Times(0);
EXPECT_CALL(callback_, SendPacket(ssrc, _, capture_time_ms, false, _))
.Times(packets_to_send_per_interval);
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
EXPECT_EQ(0u, pacer->QueueSizePackets());
}
TEST_F(PacingControllerTest, HighPrioDoesntAffectBudget) {
const size_t kPacketSize = 250;
uint32_t ssrc = 12346;
uint16_t sequence_number = 1234;
int64_t capture_time_ms = 56789;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
// As high prio packets doesn't affect the budget, we should be able to send
// a high number of them at once.
const size_t kNumAudioPackets = 25;
for (size_t i = 0; i < kNumAudioPackets; ++i) {
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kAudio, ssrc,
sequence_number++, capture_time_ms, kPacketSize);
}
pacer->ProcessPackets();
// Low prio packets does affect the budget.
// Due to the multiplicative factor we can send 5 packets during a send
// interval. (network capacity * multiplier / (8 bits per byte *
// (packet size * #send intervals per second)
const size_t kPacketsToSendPerInterval =
kTargetRate.bps() * kPaceMultiplier / (8 * kPacketSize * 200);
for (size_t i = 0; i < kPacketsToSendPerInterval; ++i) {
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPacketSize);
}
// Send all packets and measure pace time.
Timestamp start_time = clock_.CurrentTime();
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
// Measure pacing time. Expect only low-prio packets to affect this.
TimeDelta pacing_time = clock_.CurrentTime() - start_time;
TimeDelta expected_pacing_time =
DataSize::Bytes(kPacketsToSendPerInterval * kPacketSize) /
(kTargetRate * kPaceMultiplier);
EXPECT_NEAR(pacing_time.us<double>(), expected_pacing_time.us<double>(),
PacingController::kMinSleepTime.us<double>());
}
TEST_F(PacingControllerTest, SendsOnlyPaddingWhenCongested) {
uint32_t ssrc = 202020;
uint16_t sequence_number = 1000;
int kPacketSize = 250;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
// Send an initial packet so we have a last send time.
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPacketSize);
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
::testing::Mock::VerifyAndClearExpectations(&callback_);
// Set congested state, we should not send anything until the 500ms since
// last send time limit for keep-alives is triggered.
EXPECT_CALL(callback_, SendPacket).Times(0);
EXPECT_CALL(callback_, SendPadding).Times(0);
pacer->SetCongested(true);
size_t blocked_packets = 0;
int64_t expected_time_until_padding = 500;
while (expected_time_until_padding > 5) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++,
clock_.TimeInMilliseconds(), kPacketSize));
blocked_packets++;
clock_.AdvanceTimeMilliseconds(5);
pacer->ProcessPackets();
expected_time_until_padding -= 5;
}
::testing::Mock::VerifyAndClearExpectations(&callback_);
EXPECT_CALL(callback_, SendPadding(1)).WillOnce(Return(1));
EXPECT_CALL(callback_, SendPacket(_, _, _, _, true)).Times(1);
clock_.AdvanceTimeMilliseconds(5);
pacer->ProcessPackets();
EXPECT_EQ(blocked_packets, pacer->QueueSizePackets());
}
TEST_F(PacingControllerTest, DoesNotAllowOveruseAfterCongestion) {
uint32_t ssrc = 202020;
uint16_t seq_num = 1000;
int size = 1000;
auto now_ms = [this] { return clock_.TimeInMilliseconds(); };
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
EXPECT_CALL(callback_, SendPadding).Times(0);
// The pacing rate is low enough that the budget should not allow two packets
// to be sent in a row.
pacer->SetPacingRates(DataRate::BitsPerSec(400 * 8 * 1000 / 5),
DataRate::Zero());
// Not yet budget limited or congested, packet is sent.
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size));
EXPECT_CALL(callback_, SendPacket).Times(1);
clock_.AdvanceTimeMilliseconds(5);
pacer->ProcessPackets();
// Packet blocked due to congestion.
pacer->SetCongested(true);
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size));
EXPECT_CALL(callback_, SendPacket).Times(0);
clock_.AdvanceTimeMilliseconds(5);
pacer->ProcessPackets();
// Packet blocked due to congestion.
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size));
EXPECT_CALL(callback_, SendPacket).Times(0);
clock_.AdvanceTimeMilliseconds(5);
pacer->ProcessPackets();
// Congestion removed and budget has recovered, packet is sent.
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size));
EXPECT_CALL(callback_, SendPacket).Times(1);
clock_.AdvanceTimeMilliseconds(5);
pacer->SetCongested(false);
pacer->ProcessPackets();
// Should be blocked due to budget limitation as congestion has be removed.
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size));
EXPECT_CALL(callback_, SendPacket).Times(0);
clock_.AdvanceTimeMilliseconds(5);
pacer->ProcessPackets();
}
TEST_F(PacingControllerTest, Pause) {
uint32_t ssrc_low_priority = 12345;
uint32_t ssrc = 12346;
uint32_t ssrc_high_priority = 12347;
uint16_t sequence_number = 1234;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
EXPECT_TRUE(pacer->OldestPacketEnqueueTime().IsInfinite());
ConsumeInitialBudget(pacer.get());
pacer->Pause();
int64_t capture_time_ms = clock_.TimeInMilliseconds();
const size_t packets_to_send_per_interval =
kTargetRate.bps() * kPaceMultiplier / (8 * 250 * 200);
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo,
ssrc_low_priority, sequence_number++,
capture_time_ms, 250));
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kRetransmission, ssrc,
sequence_number++, capture_time_ms, 250));
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kAudio,
ssrc_high_priority, sequence_number++,
capture_time_ms, 250));
}
clock_.AdvanceTimeMilliseconds(10000);
int64_t second_capture_time_ms = clock_.TimeInMilliseconds();
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo,
ssrc_low_priority, sequence_number++,
second_capture_time_ms, 250));
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kRetransmission, ssrc,
sequence_number++, second_capture_time_ms,
250));
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kAudio,
ssrc_high_priority, sequence_number++,
second_capture_time_ms, 250));
}
// Expect everything to be queued.
EXPECT_EQ(capture_time_ms, pacer->OldestPacketEnqueueTime().ms());
// Process triggers keep-alive packet.
EXPECT_CALL(callback_, SendPadding).WillOnce([](size_t padding) {
return padding;
});
EXPECT_CALL(callback_, SendPacket(_, _, _, _, true)).Times(1);
pacer->ProcessPackets();
// Verify no packets sent for the rest of the paused process interval.
const TimeDelta kProcessInterval = TimeDelta::Millis(5);
TimeDelta expected_time_until_send = PacingController::kPausedProcessInterval;
EXPECT_CALL(callback_, SendPadding).Times(0);
while (expected_time_until_send >= kProcessInterval) {
pacer->ProcessPackets();
clock_.AdvanceTime(kProcessInterval);
expected_time_until_send -= kProcessInterval;
}
// New keep-alive packet.
::testing::Mock::VerifyAndClearExpectations(&callback_);
EXPECT_CALL(callback_, SendPadding).WillOnce([](size_t padding) {
return padding;
});
EXPECT_CALL(callback_, SendPacket(_, _, _, _, true)).Times(1);
clock_.AdvanceTime(kProcessInterval);
pacer->ProcessPackets();
::testing::Mock::VerifyAndClearExpectations(&callback_);
// Expect high prio packets to come out first followed by normal
// prio packets and low prio packets (all in capture order).
{
::testing::InSequence sequence;
EXPECT_CALL(callback_,
SendPacket(ssrc_high_priority, _, capture_time_ms, _, _))
.Times(packets_to_send_per_interval);
EXPECT_CALL(callback_,
SendPacket(ssrc_high_priority, _, second_capture_time_ms, _, _))
.Times(packets_to_send_per_interval);
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
EXPECT_CALL(callback_, SendPacket(ssrc, _, capture_time_ms, _, _))
.Times(1);
}
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
EXPECT_CALL(callback_, SendPacket(ssrc, _, second_capture_time_ms, _, _))
.Times(1);
}
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
EXPECT_CALL(callback_,
SendPacket(ssrc_low_priority, _, capture_time_ms, _, _))
.Times(1);
}
for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
EXPECT_CALL(callback_, SendPacket(ssrc_low_priority, _,
second_capture_time_ms, _, _))
.Times(1);
}
}
pacer->Resume();
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
EXPECT_TRUE(pacer->OldestPacketEnqueueTime().IsInfinite());
}
TEST_F(PacingControllerTest, InactiveFromStart) {
// Recreate the pacer without the inital time forwarding.
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetProbingEnabled(false);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate);
// No packets sent, there should be no keep-alives sent either.
EXPECT_CALL(callback_, SendPadding).Times(0);
EXPECT_CALL(callback_, SendPacket).Times(0);
pacer->ProcessPackets();
const Timestamp start_time = clock_.CurrentTime();
// Determine the margin need so we can advance to the last possible moment
// that will not cause a process event.
const TimeDelta time_margin =
PacingController::kMinSleepTime + TimeDelta::Micros(1);
EXPECT_EQ(pacer->NextSendTime() - start_time,
PacingController::kPausedProcessInterval);
clock_.AdvanceTime(PacingController::kPausedProcessInterval - time_margin);
pacer->ProcessPackets();
EXPECT_EQ(pacer->NextSendTime() - start_time,
PacingController::kPausedProcessInterval);
clock_.AdvanceTime(time_margin);
pacer->ProcessPackets();
EXPECT_EQ(pacer->NextSendTime() - start_time,
2 * PacingController::kPausedProcessInterval);
}
TEST_F(PacingControllerTest, QueueTimeGrowsOverTime) {
uint32_t ssrc = 12346;
uint16_t sequence_number = 1234;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
EXPECT_TRUE(pacer->OldestPacketEnqueueTime().IsInfinite());
pacer->SetPacingRates(DataRate::BitsPerSec(30000 * kPaceMultiplier),
DataRate::Zero());
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number, clock_.TimeInMilliseconds(), 1200);
clock_.AdvanceTimeMilliseconds(500);
EXPECT_EQ(clock_.TimeInMilliseconds() - 500,
pacer->OldestPacketEnqueueTime().ms());
pacer->ProcessPackets();
EXPECT_TRUE(pacer->OldestPacketEnqueueTime().IsInfinite());
}
TEST_F(PacingControllerTest, ProbingWithInsertedPackets) {
const size_t kPacketSize = 1200;
const int kInitialBitrateBps = 300000;
uint32_t ssrc = 12346;
uint16_t sequence_number = 1234;
PacingControllerProbing packet_sender;
auto pacer =
std::make_unique<PacingController>(&clock_, &packet_sender, trials_);
std::vector<ProbeClusterConfig> probe_clusters = {
{.at_time = clock_.CurrentTime(),
.target_data_rate = kFirstClusterRate,
.target_duration = TimeDelta::Millis(15),
.target_probe_count = 5,
.id = 0},
{.at_time = clock_.CurrentTime(),
.target_data_rate = kSecondClusterRate,
.target_duration = TimeDelta::Millis(15),
.target_probe_count = 5,
.id = 1}};
pacer->CreateProbeClusters(probe_clusters);
pacer->SetPacingRates(
DataRate::BitsPerSec(kInitialBitrateBps * kPaceMultiplier),
DataRate::Zero());
for (int i = 0; i < 10; ++i) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++,
clock_.TimeInMilliseconds(), kPacketSize));
}
int64_t start = clock_.TimeInMilliseconds();
while (packet_sender.packets_sent() < 5) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
int packets_sent = packet_sender.packets_sent();
// Validate first cluster bitrate. Note that we have to account for number
// of intervals and hence (packets_sent - 1) on the first cluster.
EXPECT_NEAR((packets_sent - 1) * kPacketSize * 8000 /
(clock_.TimeInMilliseconds() - start),
kFirstClusterRate.bps(), kProbingErrorMargin.bps());
// Probing always starts with a small padding packet.
EXPECT_EQ(1, packet_sender.padding_sent());
AdvanceTimeUntil(pacer->NextSendTime());
start = clock_.TimeInMilliseconds();
while (packet_sender.packets_sent() < 10) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
packets_sent = packet_sender.packets_sent() - packets_sent;
// Validate second cluster bitrate.
EXPECT_NEAR((packets_sent - 1) * kPacketSize * 8000 /
(clock_.TimeInMilliseconds() - start),
kSecondClusterRate.bps(), kProbingErrorMargin.bps());
}
TEST_F(PacingControllerTest, SkipsProbesWhenProcessIntervalTooLarge) {
const size_t kPacketSize = 1200;
const int kInitialBitrateBps = 300000;
const uint32_t ssrc = 12346;
const int kProbeClusterId = 3;
uint16_t sequence_number = 1234;
PacingControllerProbing packet_sender;
const test::ExplicitKeyValueConfig trials(
"WebRTC-Bwe-ProbingBehavior/max_probe_delay:2ms/");
auto pacer =
std::make_unique<PacingController>(&clock_, &packet_sender, trials);
pacer->SetPacingRates(
DataRate::BitsPerSec(kInitialBitrateBps * kPaceMultiplier),
DataRate::BitsPerSec(kInitialBitrateBps));
for (int i = 0; i < 10; ++i) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++,
clock_.TimeInMilliseconds(), kPacketSize));
}
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
// Probe at a very high rate.
std::vector<ProbeClusterConfig> probe_clusters = {
{.at_time = clock_.CurrentTime(),
.target_data_rate = DataRate::KilobitsPerSec(10000), // 10 Mbps,
.target_duration = TimeDelta::Millis(15),
.target_probe_count = 5,
.id = kProbeClusterId}};
pacer->CreateProbeClusters(probe_clusters);
// We need one packet to start the probe.
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++,
clock_.TimeInMilliseconds(), kPacketSize));
const int packets_sent_before_probe = packet_sender.packets_sent();
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
EXPECT_EQ(packet_sender.packets_sent(), packets_sent_before_probe + 1);
// Figure out how long between probe packets.
Timestamp start_time = clock_.CurrentTime();
AdvanceTimeUntil(pacer->NextSendTime());
TimeDelta time_between_probes = clock_.CurrentTime() - start_time;
// Advance that distance again + 1ms.
clock_.AdvanceTime(time_between_probes);
// Send second probe packet.
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++,
clock_.TimeInMilliseconds(), kPacketSize));
pacer->ProcessPackets();
EXPECT_EQ(packet_sender.packets_sent(), packets_sent_before_probe + 2);
PacedPacketInfo last_pacing_info = packet_sender.last_pacing_info();
EXPECT_EQ(last_pacing_info.probe_cluster_id, kProbeClusterId);
// We're exactly where we should be for the next probe.
const Timestamp probe_time = clock_.CurrentTime();
EXPECT_EQ(pacer->NextSendTime(), clock_.CurrentTime());
BitrateProberConfig probing_config(&trials);
EXPECT_GT(probing_config.max_probe_delay.Get(), TimeDelta::Zero());
// Advance to within max probe delay, should still return same target.
clock_.AdvanceTime(probing_config.max_probe_delay.Get());
EXPECT_EQ(pacer->NextSendTime(), probe_time);
// Too high probe delay, drop it!
clock_.AdvanceTime(TimeDelta::Micros(1));
int packets_sent_before_timeout = packet_sender.total_packets_sent();
// Expected next process time is unchanged, but calling should not
// generate new packets.
EXPECT_EQ(pacer->NextSendTime(), probe_time);
pacer->ProcessPackets();
EXPECT_EQ(packet_sender.total_packets_sent(), packets_sent_before_timeout);
// Next packet sent is not part of probe.
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
const int expected_probe_id = PacedPacketInfo::kNotAProbe;
EXPECT_EQ(packet_sender.last_pacing_info().probe_cluster_id,
expected_probe_id);
}
TEST_F(PacingControllerTest, ProbingWithPaddingSupport) {
const size_t kPacketSize = 1200;
const int kInitialBitrateBps = 300000;
uint32_t ssrc = 12346;
uint16_t sequence_number = 1234;
PacingControllerProbing packet_sender;
auto pacer =
std::make_unique<PacingController>(&clock_, &packet_sender, trials_);
std::vector<ProbeClusterConfig> probe_clusters = {
{.at_time = clock_.CurrentTime(),
.target_data_rate = kFirstClusterRate,
.target_duration = TimeDelta::Millis(15),
.target_probe_count = 5,
.id = 0}};
pacer->CreateProbeClusters(probe_clusters);
pacer->SetPacingRates(
DataRate::BitsPerSec(kInitialBitrateBps * kPaceMultiplier),
DataRate::Zero());
for (int i = 0; i < 3; ++i) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++,
clock_.TimeInMilliseconds(), kPacketSize));
}
int64_t start = clock_.TimeInMilliseconds();
int process_count = 0;
while (process_count < 5) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
++process_count;
}
int packets_sent = packet_sender.packets_sent();
int padding_sent = packet_sender.padding_sent();
EXPECT_GT(packets_sent, 0);
EXPECT_GT(padding_sent, 0);
// Note that the number of intervals here for kPacketSize is
// packets_sent due to padding in the same cluster.
EXPECT_NEAR((packets_sent * kPacketSize * 8000 + padding_sent) /
(clock_.TimeInMilliseconds() - start),
kFirstClusterRate.bps(), kProbingErrorMargin.bps());
}
TEST_F(PacingControllerTest, PaddingOveruse) {
uint32_t ssrc = 12346;
uint16_t sequence_number = 1234;
const size_t kPacketSize = 1200;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
// Initially no padding rate.
pacer->ProcessPackets();
pacer->SetPacingRates(DataRate::BitsPerSec(60000 * kPaceMultiplier),
DataRate::Zero());
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPacketSize);
pacer->ProcessPackets();
// Add 30kbit padding. When increasing budget, media budget will increase from
// negative (overuse) while padding budget will increase from 0.
clock_.AdvanceTimeMilliseconds(5);
pacer->SetPacingRates(DataRate::BitsPerSec(60000 * kPaceMultiplier),
DataRate::BitsPerSec(30000));
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPacketSize);
EXPECT_LT(TimeDelta::Millis(5), pacer->ExpectedQueueTime());
// Don't send padding if queue is non-empty, even if padding budget > 0.
EXPECT_CALL(callback_, SendPadding).Times(0);
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
TEST_F(PacingControllerTest, ProbeClusterId) {
MockPacketSender callback;
uint32_t ssrc = 12346;
uint16_t sequence_number = 1234;
const size_t kPacketSize = 1200;
auto pacer = std::make_unique<PacingController>(&clock_, &callback, trials_);
pacer->CreateProbeClusters(std::vector<ProbeClusterConfig>(
{{.at_time = clock_.CurrentTime(),
.target_data_rate = kFirstClusterRate,
.target_duration = TimeDelta::Millis(15),
.target_probe_count = 5,
.id = 0},
{.at_time = clock_.CurrentTime(),
.target_data_rate = kSecondClusterRate,
.target_duration = TimeDelta::Millis(15),
.target_probe_count = 5,
.id = 1}}));
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate);
pacer->SetProbingEnabled(true);
for (int i = 0; i < 10; ++i) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++,
clock_.TimeInMilliseconds(), kPacketSize));
}
// First probing cluster.
EXPECT_CALL(callback,
SendPacket(_, Field(&PacedPacketInfo::probe_cluster_id, 0)))
.Times(5);
for (int i = 0; i < 5; ++i) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
// Second probing cluster.
EXPECT_CALL(callback,
SendPacket(_, Field(&PacedPacketInfo::probe_cluster_id, 1)))
.Times(5);
for (int i = 0; i < 5; ++i) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
// Needed for the Field comparer below.
const int kNotAProbe = PacedPacketInfo::kNotAProbe;
// No more probing packets.
EXPECT_CALL(callback, GeneratePadding).WillOnce([&](DataSize padding_size) {
std::vector<std::unique_ptr<RtpPacketToSend>> padding_packets;
padding_packets.emplace_back(
BuildPacket(RtpPacketMediaType::kPadding, ssrc, sequence_number++,
clock_.TimeInMilliseconds(), padding_size.bytes()));
return padding_packets;
});
bool non_probe_packet_seen = false;
EXPECT_CALL(callback, SendPacket)
.WillOnce([&](std::unique_ptr<RtpPacketToSend> packet,
const PacedPacketInfo& cluster_info) {
EXPECT_EQ(cluster_info.probe_cluster_id, kNotAProbe);
non_probe_packet_seen = true;
});
while (!non_probe_packet_seen) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
}
TEST_F(PacingControllerTest, OwnedPacketPrioritizedOnType) {
MockPacketSender callback;
uint32_t ssrc = 123;
auto pacer = std::make_unique<PacingController>(&clock_, &callback, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
// Insert a packet of each type, from low to high priority. Since priority
// is weighted higher than insert order, these should come out of the pacer
// in backwards order with the exception of FEC and Video.
for (RtpPacketMediaType type :
{RtpPacketMediaType::kPadding,
RtpPacketMediaType::kForwardErrorCorrection, RtpPacketMediaType::kVideo,
RtpPacketMediaType::kRetransmission, RtpPacketMediaType::kAudio}) {
pacer->EnqueuePacket(BuildPacket(type, ++ssrc, /*sequence_number=*/123,
clock_.TimeInMilliseconds(),
/*size=*/150));
}
::testing::InSequence seq;
EXPECT_CALL(callback,
SendPacket(Pointee(Property(&RtpPacketToSend::packet_type,
RtpPacketMediaType::kAudio)),
_));
EXPECT_CALL(callback,
SendPacket(Pointee(Property(&RtpPacketToSend::packet_type,
RtpPacketMediaType::kRetransmission)),
_));
// FEC and video actually have the same priority, so will come out in
// insertion order.
EXPECT_CALL(
callback,
SendPacket(Pointee(Property(&RtpPacketToSend::packet_type,
RtpPacketMediaType::kForwardErrorCorrection)),
_));
EXPECT_CALL(callback,
SendPacket(Pointee(Property(&RtpPacketToSend::packet_type,
RtpPacketMediaType::kVideo)),
_));
EXPECT_CALL(callback,
SendPacket(Pointee(Property(&RtpPacketToSend::packet_type,
RtpPacketMediaType::kPadding)),
_));
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
}
TEST_F(PacingControllerTest, SmallFirstProbePacket) {
MockPacketSender callback;
auto pacer = std::make_unique<PacingController>(&clock_, &callback, trials_);
std::vector<ProbeClusterConfig> probe_clusters = {
{.at_time = clock_.CurrentTime(),
.target_data_rate = kFirstClusterRate,
.target_duration = TimeDelta::Millis(15),
.target_probe_count = 5,
.id = 0}};
pacer->CreateProbeClusters(probe_clusters);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
// Add high prio media.
pacer->EnqueuePacket(audio_.BuildNextPacket(234));
// Expect small padding packet to be requested.
EXPECT_CALL(callback, GeneratePadding(DataSize::Bytes(1)))
.WillOnce([&](DataSize padding_size) {
std::vector<std::unique_ptr<RtpPacketToSend>> padding_packets;
padding_packets.emplace_back(
BuildPacket(RtpPacketMediaType::kPadding, kAudioSsrc, 1,
clock_.TimeInMilliseconds(), 1));
return padding_packets;
});
size_t packets_sent = 0;
bool media_seen = false;
EXPECT_CALL(callback, SendPacket)
.Times(::testing::AnyNumber())
.WillRepeatedly([&](std::unique_ptr<RtpPacketToSend> packet,
const PacedPacketInfo& cluster_info) {
if (packets_sent == 0) {
EXPECT_EQ(packet->packet_type(), RtpPacketMediaType::kPadding);
} else {
if (packet->packet_type() == RtpPacketMediaType::kAudio) {
media_seen = true;
}
}
packets_sent++;
});
while (!media_seen) {
pacer->ProcessPackets();
clock_.AdvanceTimeMilliseconds(5);
}
}
TEST_F(PacingControllerTest, TaskLate) {
// Set a low send rate to more easily test timing issues.
DataRate kSendRate = DataRate::KilobitsPerSec(30);
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kSendRate, DataRate::Zero());
// Add four packets of equal size and priority.
pacer->EnqueuePacket(video_.BuildNextPacket(1000));
pacer->EnqueuePacket(video_.BuildNextPacket(1000));
pacer->EnqueuePacket(video_.BuildNextPacket(1000));
pacer->EnqueuePacket(video_.BuildNextPacket(1000));
// Process packets, only first should be sent.
EXPECT_CALL(callback_, SendPacket).Times(1);
pacer->ProcessPackets();
Timestamp next_send_time = pacer->NextSendTime();
// Determine time between packets (ca 62ms)
const TimeDelta time_between_packets = next_send_time - clock_.CurrentTime();
// Simulate a late process call, executed just before we allow sending the
// fourth packet.
const TimeDelta kOffset = TimeDelta::Millis(1);
clock_.AdvanceTime((time_between_packets * 3) - kOffset);
EXPECT_CALL(callback_, SendPacket).Times(2);
pacer->ProcessPackets();
// Check that next scheduled send time is in ca 1ms.
next_send_time = pacer->NextSendTime();
const TimeDelta time_left = next_send_time - clock_.CurrentTime();
EXPECT_EQ(time_left.RoundTo(TimeDelta::Millis(1)), kOffset);
clock_.AdvanceTime(time_left);
EXPECT_CALL(callback_, SendPacket);
pacer->ProcessPackets();
}
TEST_F(PacingControllerTest, NoProbingWhilePaused) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetProbingEnabled(true);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
pacer->CreateProbeClusters(std::vector<ProbeClusterConfig>(
{{.at_time = clock_.CurrentTime(),
.target_data_rate = kFirstClusterRate,
.target_duration = TimeDelta::Millis(15),
.target_probe_count = 5,
.id = 0},
{.at_time = clock_.CurrentTime(),
.target_data_rate = kSecondClusterRate,
.target_duration = TimeDelta::Millis(15),
.target_probe_count = 5,
.id = 1}}));
// Send at least one packet so probing can initate.
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number, clock_.TimeInMilliseconds(), 250);
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
// Trigger probing.
std::vector<ProbeClusterConfig> probe_clusters = {
{.at_time = clock_.CurrentTime(),
.target_data_rate = DataRate::KilobitsPerSec(10000), // 10 Mbps.
.target_duration = TimeDelta::Millis(15),
.target_probe_count = 5,
.id = 3}};
pacer->CreateProbeClusters(probe_clusters);
// Time to next send time should be small.
EXPECT_LT(pacer->NextSendTime() - clock_.CurrentTime(),
PacingController::kPausedProcessInterval);
// Pause pacer, time to next send time should now be the pause process
// interval.
pacer->Pause();
EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(),
PacingController::kPausedProcessInterval);
}
TEST_F(PacingControllerTest, AudioNotPacedEvenWhenAccountedFor) {
const uint32_t kSsrc = 12345;
uint16_t sequence_number = 1234;
const size_t kPacketSize = 123;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
// Account for audio - so that audio packets can cause pushback on other
// types such as video. Audio packet should still be immediated passed
// through though ("WebRTC-Pacer-BlockAudio" needs to be enabled in order
// to pace audio packets).
pacer->SetAccountForAudioPackets(true);
// Set pacing rate to 1 packet/s, no padding.
pacer->SetPacingRates(DataSize::Bytes(kPacketSize) / TimeDelta::Seconds(1),
DataRate::Zero());
// Add and send an audio packet.
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kAudio, kSsrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPacketSize);
pacer->ProcessPackets();
// Advance time, add another audio packet and process. It should be sent
// immediately.
clock_.AdvanceTimeMilliseconds(5);
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kAudio, kSsrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPacketSize);
pacer->ProcessPackets();
}
TEST_F(PacingControllerTest,
PaddingResumesAfterSaturationEvenWithConcurrentAudio) {
const uint32_t kSsrc = 12345;
const DataRate kPacingDataRate = DataRate::KilobitsPerSec(125);
const DataRate kPaddingDataRate = DataRate::KilobitsPerSec(100);
const TimeDelta kMaxBufferInTime = TimeDelta::Millis(500);
const DataSize kPacketSize = DataSize::Bytes(130);
const TimeDelta kAudioPacketInterval = TimeDelta::Millis(20);
// In this test, we fist send a burst of video in order to saturate the
// padding debt level.
// We then proceed to send audio at a bitrate that is slightly lower than
// the padding rate, meaning there will be a period with audio but no
// padding sent while the debt is draining, then audio and padding will
// be interlieved.
// Verify both with and without accounting for audio.
for (bool account_for_audio : {false, true}) {
uint16_t sequence_number = 1234;
MockPacketSender callback;
EXPECT_CALL(callback, SendPacket).Times(::testing::AnyNumber());
auto pacer =
std::make_unique<PacingController>(&clock_, &callback, trials_);
pacer->SetAccountForAudioPackets(account_for_audio);
// First, saturate the padding budget.
pacer->SetPacingRates(kPacingDataRate, kPaddingDataRate);
const TimeDelta kPaddingSaturationTime =
kMaxBufferInTime * kPaddingDataRate /
(kPacingDataRate - kPaddingDataRate);
const DataSize kVideoToSend = kPaddingSaturationTime * kPacingDataRate;
const DataSize kVideoPacketSize = DataSize::Bytes(1200);
DataSize video_sent = DataSize::Zero();
while (video_sent < kVideoToSend) {
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, kSsrc, sequence_number++,
clock_.TimeInMilliseconds(), kVideoPacketSize.bytes()));
video_sent += kVideoPacketSize;
}
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
// Add a stream of audio packets at a rate slightly lower than the padding
// rate, once the padding debt is paid off we expect padding to be
// generated.
pacer->SetPacingRates(kPacingDataRate, kPaddingDataRate);
bool padding_seen = false;
EXPECT_CALL(callback, GeneratePadding).WillOnce([&](DataSize padding_size) {
padding_seen = true;
std::vector<std::unique_ptr<RtpPacketToSend>> padding_packets;
padding_packets.emplace_back(
BuildPacket(RtpPacketMediaType::kPadding, kSsrc, sequence_number++,
clock_.TimeInMilliseconds(), padding_size.bytes()));
return padding_packets;
});
Timestamp start_time = clock_.CurrentTime();
Timestamp last_audio_time = start_time;
while (!padding_seen) {
Timestamp now = clock_.CurrentTime();
Timestamp next_send_time = pacer->NextSendTime();
TimeDelta sleep_time =
std::min(next_send_time, last_audio_time + kAudioPacketInterval) -
now;
clock_.AdvanceTime(sleep_time);
while (clock_.CurrentTime() >= last_audio_time + kAudioPacketInterval) {
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kAudio, kSsrc, sequence_number++,
clock_.TimeInMilliseconds(), kPacketSize.bytes()));
last_audio_time += kAudioPacketInterval;
}
pacer->ProcessPackets();
}
// Verify how long it took to drain the padding debt. Allow 2% error margin.
const DataRate kAudioDataRate = kPacketSize / kAudioPacketInterval;
const TimeDelta expected_drain_time =
account_for_audio ? (kMaxBufferInTime * kPaddingDataRate /
(kPaddingDataRate - kAudioDataRate))
: kMaxBufferInTime;
const TimeDelta actual_drain_time = clock_.CurrentTime() - start_time;
EXPECT_NEAR(actual_drain_time.ms(), expected_drain_time.ms(),
expected_drain_time.ms() * 0.02)
<< " where account_for_audio = "
<< (account_for_audio ? "true" : "false");
}
}
TEST_F(PacingControllerTest, AccountsForAudioEnqueueTime) {
const uint32_t kSsrc = 12345;
const DataRate kPacingDataRate = DataRate::KilobitsPerSec(125);
const DataRate kPaddingDataRate = DataRate::Zero();
const DataSize kPacketSize = DataSize::Bytes(130);
const TimeDelta kPacketPacingTime = kPacketSize / kPacingDataRate;
uint32_t sequnce_number = 1;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
// Audio not paced, but still accounted for in budget.
pacer->SetAccountForAudioPackets(true);
pacer->SetPacingRates(kPacingDataRate, kPaddingDataRate);
// Enqueue two audio packets, advance clock to where one packet
// should have drained the buffer already, has they been sent
// immediately.
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kAudio, kSsrc,
sequnce_number++, clock_.TimeInMilliseconds(),
kPacketSize.bytes());
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kAudio, kSsrc,
sequnce_number++, clock_.TimeInMilliseconds(),
kPacketSize.bytes());
clock_.AdvanceTime(kPacketPacingTime);
// Now process and make sure both packets were sent.
pacer->ProcessPackets();
::testing::Mock::VerifyAndClearExpectations(&callback_);
// Add a video packet. I can't be sent until debt from audio
// packets have been drained.
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, kSsrc + 1, sequnce_number++,
clock_.TimeInMilliseconds(), kPacketSize.bytes()));
EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(), kPacketPacingTime);
}
TEST_F(PacingControllerTest, NextSendTimeAccountsForPadding) {
const uint32_t kSsrc = 12345;
const DataRate kPacingDataRate = DataRate::KilobitsPerSec(125);
const DataSize kPacketSize = DataSize::Bytes(130);
const TimeDelta kPacketPacingTime = kPacketSize / kPacingDataRate;
uint32_t sequnce_number = 1;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
// Start with no padding.
pacer->SetPacingRates(kPacingDataRate, DataRate::Zero());
// Send a single packet.
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, kSsrc,
sequnce_number++, clock_.TimeInMilliseconds(),
kPacketSize.bytes());
pacer->ProcessPackets();
::testing::Mock::VerifyAndClearExpectations(&callback_);
// With current conditions, no need to wake until next keep-alive.
EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(),
PacingController::kPausedProcessInterval);
// Enqueue a new packet, that can't be sent until previous buffer has
// drained.
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, kSsrc,
sequnce_number++, clock_.TimeInMilliseconds(),
kPacketSize.bytes());
EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(), kPacketPacingTime);
clock_.AdvanceTime(kPacketPacingTime);
pacer->ProcessPackets();
::testing::Mock::VerifyAndClearExpectations(&callback_);
// With current conditions, again no need to wake until next keep-alive.
EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(),
PacingController::kPausedProcessInterval);
// Set a non-zero padding rate. Padding also can't be sent until
// previous debt has cleared. Since padding was disabled before, there
// currently is no padding debt.
pacer->SetPacingRates(kPacingDataRate, kPacingDataRate / 2);
EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(), kPacketPacingTime);
// Advance time, expect padding.
EXPECT_CALL(callback_, SendPadding).WillOnce(Return(kPacketSize.bytes()));
clock_.AdvanceTime(kPacketPacingTime);
pacer->ProcessPackets();
::testing::Mock::VerifyAndClearExpectations(&callback_);
// Since padding rate is half of pacing rate, next time we can send
// padding is double the packet pacing time.
EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(),
kPacketPacingTime * 2);
// Insert a packet to be sent, this take precedence again.
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, kSsrc, sequnce_number++,
clock_.TimeInMilliseconds(), kPacketSize.bytes()));
EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(), kPacketPacingTime);
}
TEST_F(PacingControllerTest, PaddingTargetAccountsForPaddingRate) {
// Re-init pacer with an explicitly set padding target of 10ms;
const TimeDelta kPaddingTarget = TimeDelta::Millis(10);
ExplicitKeyValueConfig field_trials(
"WebRTC-Pacer-DynamicPaddingTarget/timedelta:10ms/");
srand(0);
// Need to initialize PacingController after we initialize clock.
auto pacer =
std::make_unique<PacingController>(&clock_, &callback_, field_trials);
const uint32_t kSsrc = 12345;
const DataRate kPacingDataRate = DataRate::KilobitsPerSec(125);
const DataSize kPacketSize = DataSize::Bytes(130);
uint32_t sequnce_number = 1;
// Start with pacing and padding rate equal.
pacer->SetPacingRates(kPacingDataRate, kPacingDataRate);
// Send a single packet.
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, kSsrc,
sequnce_number++, clock_.TimeInMilliseconds(),
kPacketSize.bytes());
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
::testing::Mock::VerifyAndClearExpectations(&callback_);
size_t expected_padding_target_bytes =
(kPaddingTarget * kPacingDataRate).bytes();
EXPECT_CALL(callback_, SendPadding(expected_padding_target_bytes))
.WillOnce(Return(expected_padding_target_bytes));
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
// Half the padding rate - expect half the padding target.
pacer->SetPacingRates(kPacingDataRate, kPacingDataRate / 2);
EXPECT_CALL(callback_, SendPadding(expected_padding_target_bytes / 2))
.WillOnce(Return(expected_padding_target_bytes / 2));
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
TEST_F(PacingControllerTest, SendsFecPackets) {
const uint32_t kSsrc = 12345;
const uint32_t kFlexSsrc = 54321;
uint16_t sequence_number = 1234;
uint16_t flexfec_sequence_number = 4321;
const size_t kPacketSize = 123;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
// Set pacing rate to 1000 packet/s, no padding.
pacer->SetPacingRates(
DataSize::Bytes(1000 * kPacketSize) / TimeDelta::Seconds(1),
DataRate::Zero());
int64_t now = clock_.TimeInMilliseconds();
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, kSsrc,
sequence_number, now, kPacketSize));
EXPECT_CALL(callback_, SendPacket(kSsrc, sequence_number, now, false, false));
EXPECT_CALL(callback_, FetchFec).WillOnce([&]() {
EXPECT_CALL(callback_, SendPacket(kFlexSsrc, flexfec_sequence_number, now,
false, false));
EXPECT_CALL(callback_, FetchFec);
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets;
fec_packets.push_back(
BuildPacket(RtpPacketMediaType::kForwardErrorCorrection, kFlexSsrc,
flexfec_sequence_number, now, kPacketSize));
return fec_packets;
});
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
TEST_F(PacingControllerTest, GapInPacingDoesntAccumulateBudget) {
const uint32_t kSsrc = 12345;
uint16_t sequence_number = 1234;
const DataSize kPackeSize = DataSize::Bytes(250);
const TimeDelta kPacketSendTime = TimeDelta::Millis(15);
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kPackeSize / kPacketSendTime,
/*padding_rate=*/DataRate::Zero());
// Send an initial packet.
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, kSsrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPackeSize.bytes());
pacer->ProcessPackets();
::testing::Mock::VerifyAndClearExpectations(&callback_);
// Advance time kPacketSendTime past where the media debt should be 0.
clock_.AdvanceTime(2 * kPacketSendTime);
// Enqueue two new packets. Expect only one to be sent one ProcessPackets().
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, kSsrc, sequence_number + 1,
clock_.TimeInMilliseconds(), kPackeSize.bytes()));
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, kSsrc, sequence_number + 2,
clock_.TimeInMilliseconds(), kPackeSize.bytes()));
EXPECT_CALL(callback_, SendPacket(kSsrc, sequence_number + 1,
clock_.TimeInMilliseconds(), false, false));
pacer->ProcessPackets();
}
TEST_F(PacingControllerTest, HandlesSubMicrosecondSendIntervals) {
static constexpr DataSize kPacketSize = DataSize::Bytes(1);
static constexpr TimeDelta kPacketSendTime = TimeDelta::Micros(1);
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
// Set pacing rate such that a packet is sent in 0.5us.
pacer->SetPacingRates(/*pacing_rate=*/2 * kPacketSize / kPacketSendTime,
/*padding_rate=*/DataRate::Zero());
// Enqueue three packets, the first two should be sent immediately - the third
// should cause a non-zero delta to the next process time.
EXPECT_CALL(callback_, SendPacket).Times(2);
for (int i = 0; i < 3; ++i) {
pacer->EnqueuePacket(BuildPacket(
RtpPacketMediaType::kVideo, /*ssrc=*/12345, /*sequence_number=*/i,
clock_.TimeInMilliseconds(), kPacketSize.bytes()));
}
pacer->ProcessPackets();
EXPECT_GT(pacer->NextSendTime(), clock_.CurrentTime());
}
TEST_F(PacingControllerTest, HandlesSubMicrosecondPaddingInterval) {
static constexpr DataSize kPacketSize = DataSize::Bytes(1);
static constexpr TimeDelta kPacketSendTime = TimeDelta::Micros(1);
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
// Set both pacing and padding rates to 1 byte per 0.5us.
pacer->SetPacingRates(/*pacing_rate=*/2 * kPacketSize / kPacketSendTime,
/*padding_rate=*/2 * kPacketSize / kPacketSendTime);
// Enqueue and send one packet.
EXPECT_CALL(callback_, SendPacket);
pacer->EnqueuePacket(BuildPacket(
RtpPacketMediaType::kVideo, /*ssrc=*/12345, /*sequence_number=*/1234,
clock_.TimeInMilliseconds(), kPacketSize.bytes()));
pacer->ProcessPackets();
// The padding debt is now 1 byte, and the pacing time for that is lower than
// the precision of a TimeStamp tick. Make sure the pacer still indicates a
// non-zero sleep time is needed until the next process.
EXPECT_GT(pacer->NextSendTime(), clock_.CurrentTime());
}
TEST_F(PacingControllerTest, SendsPacketsInBurstImmediately) {
constexpr TimeDelta kMaxDelay = TimeDelta::Millis(20);
PacingController pacer(&clock_, &callback_, trials_);
pacer.SetSendBurstInterval(kMaxDelay);
pacer.SetPacingRates(DataRate::BytesPerSec(10000), DataRate::Zero());
// Max allowed send burst size is 100000*20/1000) = 200byte
pacer.EnqueuePacket(video_.BuildNextPacket(100));
pacer.EnqueuePacket(video_.BuildNextPacket(100));
pacer.EnqueuePacket(video_.BuildNextPacket(100));
pacer.ProcessPackets();
EXPECT_EQ(pacer.QueueSizePackets(), 1u);
EXPECT_EQ(pacer.NextSendTime(), clock_.CurrentTime() + kMaxDelay);
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
EXPECT_EQ(pacer.QueueSizePackets(), 0u);
}
TEST_F(PacingControllerTest, SendsPacketsInBurstEvenIfNotEnqueedAtSameTime) {
constexpr TimeDelta kMaxDelay = TimeDelta::Millis(20);
PacingController pacer(&clock_, &callback_, trials_);
pacer.SetSendBurstInterval(kMaxDelay);
pacer.SetPacingRates(DataRate::BytesPerSec(10000), DataRate::Zero());
pacer.EnqueuePacket(video_.BuildNextPacket(200));
EXPECT_EQ(pacer.NextSendTime(), clock_.CurrentTime());
pacer.ProcessPackets();
clock_.AdvanceTime(TimeDelta::Millis(1));
pacer.EnqueuePacket(video_.BuildNextPacket(200));
EXPECT_EQ(pacer.NextSendTime(), clock_.CurrentTime());
pacer.ProcessPackets();
EXPECT_EQ(pacer.QueueSizePackets(), 0u);
}
TEST_F(PacingControllerTest, RespectsTargetRateWhenSendingPacketsInBursts) {
PacingController pacer(&clock_, &callback_, trials_);
pacer.SetSendBurstInterval(TimeDelta::Millis(20));
pacer.SetAccountForAudioPackets(true);
pacer.SetPacingRates(DataRate::KilobitsPerSec(1000), DataRate::Zero());
Timestamp start_time = clock_.CurrentTime();
// Inject 100 packets, with size 1000bytes over 100ms.
// Expect only 1Mbps / (8*1000) / 10 = 12 packets to be sent.
// Packets are sent in burst. Each burst is then 3 packets * 1000bytes at
// 1Mbits = 24ms long. Thus, expect 4 bursts.
EXPECT_CALL(callback_, SendPacket).Times(12);
int number_of_bursts = 0;
while (clock_.CurrentTime() < start_time + TimeDelta::Millis(100)) {
pacer.EnqueuePacket(video_.BuildNextPacket(1000));
pacer.EnqueuePacket(video_.BuildNextPacket(1000));
pacer.EnqueuePacket(video_.BuildNextPacket(1000));
pacer.EnqueuePacket(video_.BuildNextPacket(1000));
pacer.EnqueuePacket(video_.BuildNextPacket(1000));
if (pacer.NextSendTime() <= clock_.CurrentTime()) {
pacer.ProcessPackets();
++number_of_bursts;
}
clock_.AdvanceTime(TimeDelta::Millis(5));
}
EXPECT_EQ(pacer.QueueSizePackets(), 88u);
EXPECT_EQ(number_of_bursts, 4);
}
TEST_F(PacingControllerTest, RespectsQueueTimeLimit) {
static constexpr DataSize kPacketSize = DataSize::Bytes(100);
static constexpr DataRate kNominalPacingRate = DataRate::KilobitsPerSec(200);
static constexpr TimeDelta kPacketPacingTime =
kPacketSize / kNominalPacingRate;
static constexpr TimeDelta kQueueTimeLimit = TimeDelta::Millis(1000);
PacingController pacer(&clock_, &callback_, trials_);
pacer.SetPacingRates(kNominalPacingRate, /*padding_rate=*/DataRate::Zero());
pacer.SetQueueTimeLimit(kQueueTimeLimit);
// Fill pacer up to queue time limit.
static constexpr int kNumPackets = kQueueTimeLimit / kPacketPacingTime;
for (int i = 0; i < kNumPackets; ++i) {
pacer.EnqueuePacket(video_.BuildNextPacket(kPacketSize.bytes()));
}
EXPECT_EQ(pacer.ExpectedQueueTime(), kQueueTimeLimit);
EXPECT_EQ(pacer.pacing_rate(), kNominalPacingRate);
// Double the amount of packets in the queue, the queue time limit should
// effectively double the pacing rate in response.
for (int i = 0; i < kNumPackets; ++i) {
pacer.EnqueuePacket(video_.BuildNextPacket(kPacketSize.bytes()));
}
EXPECT_EQ(pacer.ExpectedQueueTime(), kQueueTimeLimit);
EXPECT_EQ(pacer.pacing_rate(), 2 * kNominalPacingRate);
// Send all the packets, should take as long as the queue time limit.
Timestamp start_time = clock_.CurrentTime();
while (pacer.QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer.NextSendTime());
pacer.ProcessPackets();
}
EXPECT_EQ(clock_.CurrentTime() - start_time, kQueueTimeLimit);
// We're back in a normal state - pacing rate should be back to previous
// levels.
EXPECT_EQ(pacer.pacing_rate(), kNominalPacingRate);
}
} // namespace
} // namespace webrtc