/* * 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 #include #include #include #include #include #include "api/units/data_rate.h" #include "modules/pacing/packet_router.h" #include "system_wrappers/include/clock.h" #include "test/explicit_key_value_config.h" #include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" using ::testing::_; using ::testing::Field; using ::testing::Pointee; using ::testing::Property; using ::testing::Return; namespace webrtc { namespace test { 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 uint32_t kVideoRtxSsrc = 34567; constexpr uint32_t kFlexFecSsrc = 45678; constexpr DataRate kTargetRate = DataRate::KilobitsPerSec(800); std::unique_ptr BuildPacket(RtpPacketMediaType type, uint32_t ssrc, uint16_t sequence_number, int64_t capture_time_ms, size_t size) { auto packet = std::make_unique(nullptr); packet->set_packet_type(type); packet->SetSsrc(ssrc); packet->SetSequenceNumber(sequence_number); packet->set_capture_time_ms(capture_time_ms); packet->SetPayloadSize(size); return packet; } } // namespace // 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 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> GeneratePadding( DataSize target_size) override { std::vector> ret; size_t padding_size = SendPadding(target_size.bytes()); if (padding_size > 0) { auto packet = std::make_unique(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>, 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 packet, const PacedPacketInfo& cluster_info), (override)); MOCK_METHOD(std::vector>, FetchFec, (), (override)); MOCK_METHOD(std::vector>, 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 packet, const PacedPacketInfo& pacing_info) override { total_bytes_sent_ += packet->payload_size(); } std::vector> FetchFec() override { return {}; } std::vector> GeneratePadding( DataSize target_size) override { size_t num_packets = (target_size.bytes() + kPaddingPacketSize - 1) / kPaddingPacketSize; std::vector> packets; for (size_t i = 0; i < num_packets; ++i) { packets.emplace_back(std::make_unique(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 packet, const PacedPacketInfo& pacing_info) override { if (packet->packet_type() != RtpPacketMediaType::kPadding) { ++packets_sent_; } last_pacing_info_ = pacing_info; } std::vector> FetchFec() override { return {}; } std::vector> 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> packets; while (target_size > DataSize::Zero()) { DataSize padding_size = std::min(kMaxPadding, target_size); packets.emplace_back(std::make_unique(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::TestWithParam { protected: PacingControllerTest() : clock_(123456) {} void SetUp() override { srand(0); // Need to initialize PacingController after we initialize clock. pacer_ = std::make_unique(&clock_, &callback_, nullptr, nullptr, GetParam()); Init(); } bool PeriodicProcess() const { return GetParam() == PacingController::ProcessMode::kPeriodic; } void Init() { pacer_->CreateProbeCluster(kFirstClusterRate, /*cluster_id=*/0); pacer_->CreateProbeCluster(kSecondClusterRate, /*cluster_id=*/1); // Default to bitrate probing disabled for testing purposes. Probing tests // have to enable probing, either by creating a new PacingController // instance or by calling SetProbingEnabled(true). pacer_->SetProbingEnabled(false); pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero()); clock_.AdvanceTime(TimeUntilNextProcess()); } void Send(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)); } void SendAndExpectPacket(RtpPacketMediaType type, uint32_t ssrc, uint16_t sequence_number, int64_t capture_time_ms, size_t size) { Send(type, ssrc, sequence_number, capture_time_ms, size); EXPECT_CALL(callback_, SendPacket(ssrc, sequence_number, capture_time_ms, type == RtpPacketMediaType::kRetransmission, false)) .Times(1); } std::unique_ptr BuildRtpPacket(RtpPacketMediaType type) { auto packet = std::make_unique(nullptr); packet->set_packet_type(type); switch (type) { case RtpPacketMediaType::kAudio: packet->SetSsrc(kAudioSsrc); break; case RtpPacketMediaType::kVideo: packet->SetSsrc(kVideoSsrc); break; case RtpPacketMediaType::kRetransmission: case RtpPacketMediaType::kPadding: packet->SetSsrc(kVideoRtxSsrc); break; case RtpPacketMediaType::kForwardErrorCorrection: packet->SetSsrc(kFlexFecSsrc); break; } packet->SetPayloadSize(234); return packet; } TimeDelta TimeUntilNextProcess() { Timestamp now = clock_.CurrentTime(); return std::max(pacer_->NextSendTime() - now, TimeDelta::Zero()); } void AdvanceTimeAndProcess() { Timestamp now = clock_.CurrentTime(); Timestamp next_send_time = pacer_->NextSendTime(); clock_.AdvanceTime(std::max(TimeDelta::Zero(), next_send_time - now)); pacer_->ProcessPackets(); } void ConsumeInitialBudget() { const uint32_t kSsrc = 54321; uint16_t sequence_number = 1234; int64_t capture_time_ms = clock_.TimeInMilliseconds(); const size_t kPacketSize = 250; EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); // 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(RtpPacketMediaType::kVideo, kSsrc, sequence_number++, capture_time_ms, kPacketSize); } while (pacer_->QueueSizePackets() > 0) { if (PeriodicProcess()) { clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); } else { AdvanceTimeAndProcess(); } } } SimulatedClock clock_; ::testing::NiceMock callback_; std::unique_ptr pacer_; }; class PacingControllerFieldTrialTest : public ::testing::TestWithParam { protected: struct MediaStream { const RtpPacketMediaType type; const uint32_t ssrc; const size_t packet_size; uint16_t seq_num; }; const int kProcessIntervalsPerSecond = 1000 / 5; PacingControllerFieldTrialTest() : clock_(123456) {} void InsertPacket(PacingController* pacer, MediaStream* stream) { pacer->EnqueuePacket( BuildPacket(stream->type, stream->ssrc, stream->seq_num++, clock_.TimeInMilliseconds(), stream->packet_size)); } void ProcessNext(PacingController* pacer) { if (GetParam() == PacingController::ProcessMode::kPeriodic) { TimeDelta process_interval = TimeDelta::Millis(5); clock_.AdvanceTime(process_interval); pacer->ProcessPackets(); return; } Timestamp now = clock_.CurrentTime(); Timestamp next_send_time = pacer->NextSendTime(); TimeDelta wait_time = std::max(TimeDelta::Zero(), next_send_time - now); clock_.AdvanceTime(wait_time); pacer->ProcessPackets(); } MediaStream audio{/*type*/ RtpPacketMediaType::kAudio, /*ssrc*/ 3333, /*packet_size*/ 100, /*seq_num*/ 1000}; MediaStream video{/*type*/ RtpPacketMediaType::kVideo, /*ssrc*/ 4444, /*packet_size*/ 1000, /*seq_num*/ 1000}; SimulatedClock clock_; MockPacingControllerCallback callback_; }; TEST_P(PacingControllerFieldTrialTest, DefaultNoPaddingInSilence) { PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); pacer.SetPacingRates(kTargetRate, DataRate::Zero()); // Video packet to reset last send time and provide padding data. InsertPacket(&pacer, &video); 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_P(PacingControllerFieldTrialTest, PaddingInSilenceWithTrial) { ScopedFieldTrials trial("WebRTC-Pacer-PadInSilence/Enabled/"); PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); pacer.SetPacingRates(kTargetRate, DataRate::Zero()); // Video packet to reset last send time and provide padding data. InsertPacket(&pacer, &video); 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_P(PacingControllerFieldTrialTest, CongestionWindowAffectsAudioInTrial) { ScopedFieldTrials trial("WebRTC-Pacer-BlockAudio/Enabled/"); EXPECT_CALL(callback_, SendPadding).Times(0); PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); pacer.SetPacingRates(DataRate::KilobitsPerSec(10000), DataRate::Zero()); pacer.SetCongestionWindow(DataSize::Bytes(video.packet_size - 100)); pacer.UpdateOutstandingData(DataSize::Zero()); // Video packet fills congestion window. InsertPacket(&pacer, &video); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); // Audio packet blocked due to congestion. InsertPacket(&pacer, &audio); EXPECT_CALL(callback_, SendPacket).Times(0); if (GetParam() == PacingController::ProcessMode::kDynamic) { // Without interval budget we'll forward time to where we send keep-alive. EXPECT_CALL(callback_, SendPadding(1)).Times(2); } ProcessNext(&pacer); ProcessNext(&pacer); // Audio packet unblocked when congestion window clear. ::testing::Mock::VerifyAndClearExpectations(&callback_); pacer.UpdateOutstandingData(DataSize::Zero()); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); } TEST_P(PacingControllerFieldTrialTest, DefaultCongestionWindowDoesNotAffectAudio) { EXPECT_CALL(callback_, SendPadding).Times(0); PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); pacer.SetPacingRates(DataRate::BitsPerSec(10000000), DataRate::Zero()); pacer.SetCongestionWindow(DataSize::Bytes(800)); pacer.UpdateOutstandingData(DataSize::Zero()); // Video packet fills congestion window. InsertPacket(&pacer, &video); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); // Audio not blocked due to congestion. InsertPacket(&pacer, &audio); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); } TEST_P(PacingControllerFieldTrialTest, BudgetAffectsAudioInTrial) { ScopedFieldTrials trial("WebRTC-Pacer-BlockAudio/Enabled/"); PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); DataRate pacing_rate = DataRate::BitsPerSec(video.packet_size / 3 * 8 * kProcessIntervalsPerSecond); pacer.SetPacingRates(pacing_rate, DataRate::Zero()); // Video fills budget for following process periods. InsertPacket(&pacer, &video); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); // Audio packet blocked due to budget limit. InsertPacket(&pacer, &audio); Timestamp wait_start_time = clock_.CurrentTime(); Timestamp wait_end_time = Timestamp::MinusInfinity(); EXPECT_CALL(callback_, SendPacket) .WillOnce([&](uint32_t ssrc, uint16_t sequence_number, int64_t capture_timestamp, bool retransmission, bool padding) { wait_end_time = clock_.CurrentTime(); }); while (!wait_end_time.IsFinite()) { ProcessNext(&pacer); } const TimeDelta expected_wait_time = DataSize::Bytes(video.packet_size) / pacing_rate; // Verify delay is near expectation, within timing margin. EXPECT_LT(((wait_end_time - wait_start_time) - expected_wait_time).Abs(), GetParam() == PacingController::ProcessMode::kPeriodic ? TimeDelta::Millis(5) : PacingController::kMinSleepTime); } TEST_P(PacingControllerFieldTrialTest, DefaultBudgetDoesNotAffectAudio) { EXPECT_CALL(callback_, SendPadding).Times(0); PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); pacer.SetPacingRates(DataRate::BitsPerSec(video.packet_size / 3 * 8 * kProcessIntervalsPerSecond), DataRate::Zero()); // Video fills budget for following process periods. InsertPacket(&pacer, &video); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); // Audio packet not blocked due to budget limit. EXPECT_CALL(callback_, SendPacket).Times(1); InsertPacket(&pacer, &audio); ProcessNext(&pacer); } INSTANTIATE_TEST_SUITE_P(WithAndWithoutIntervalBudget, PacingControllerFieldTrialTest, ::testing::Values(false, true)); TEST_P(PacingControllerTest, FirstSentPacketTimeIsSet) { uint16_t sequence_number = 1234; const uint32_t kSsrc = 12345; const size_t kSizeBytes = 250; const size_t kPacketToSend = 3; const Timestamp kStartTime = clock_.CurrentTime(); // No packet sent. EXPECT_FALSE(pacer_->FirstSentPacketTime().has_value()); for (size_t i = 0; i < kPacketToSend; ++i) { SendAndExpectPacket(RtpPacketMediaType::kVideo, kSsrc, sequence_number++, clock_.TimeInMilliseconds(), kSizeBytes); clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); } EXPECT_EQ(kStartTime, pacer_->FirstSentPacketTime()); } TEST_P(PacingControllerTest, QueuePacket) { if (!PeriodicProcess()) { // This test checks behavior applicable only when using interval budget. return; } uint32_t ssrc = 12345; uint16_t sequence_number = 1234; // Due to the multiplicative factor we can send 5 packets during a 5ms send // interval. (network capacity * multiplier / (8 bits per byte * // (packet size * #send intervals per second) const size_t kPacketsToSend = kTargetRate.bps() * kPaceMultiplier / (8 * 250 * 200); for (size_t i = 0; i < kPacketsToSend; ++i) { SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), 250); } EXPECT_CALL(callback_, SendPadding).Times(0); // Enqueue one extra packet. int64_t queued_packet_timestamp = clock_.TimeInMilliseconds(); Send(RtpPacketMediaType::kVideo, ssrc, sequence_number, queued_packet_timestamp, 250); EXPECT_EQ(kPacketsToSend + 1, pacer_->QueueSizePackets()); // The first kPacketsToSend packets will be sent with budget from the // initial 5ms interval. pacer_->ProcessPackets(); EXPECT_EQ(1u, pacer_->QueueSizePackets()); // Advance time to next interval, make sure the last packet is sent. clock_.AdvanceTimeMilliseconds(5); EXPECT_CALL(callback_, SendPacket(ssrc, sequence_number++, queued_packet_timestamp, false, false)) .Times(1); pacer_->ProcessPackets(); sequence_number++; EXPECT_EQ(0u, pacer_->QueueSizePackets()); // We can send packets_to_send -1 packets of size 250 during the current // interval since one packet has already been sent. for (size_t i = 0; i < kPacketsToSend - 1; ++i) { SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), 250); } Send(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), 250); EXPECT_EQ(kPacketsToSend, pacer_->QueueSizePackets()); pacer_->ProcessPackets(); EXPECT_EQ(1u, pacer_->QueueSizePackets()); } TEST_P(PacingControllerTest, QueueAndPacePackets) { if (PeriodicProcess()) { // This test checks behavior when not using interval budget. return; } 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(); for (size_t i = 0; i < kPacketsToSend; ++i) { SendAndExpectPacket(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(); Send(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) { AdvanceTimeAndProcess(); } 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); AdvanceTimeAndProcess(); EXPECT_GE(clock_.CurrentTime() - start_time, kSendInterval); EXPECT_EQ(pacer_->QueueSizePackets(), 0u); } TEST_P(PacingControllerTest, PaceQueuedPackets) { uint32_t ssrc = 12345; uint16_t sequence_number = 1234; const size_t kPacketSize = 250; // 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(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); } for (size_t j = 0; j < packets_to_send_per_interval * 10; ++j) { Send(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); } EXPECT_EQ(packets_to_send_per_interval + packets_to_send_per_interval * 10, pacer_->QueueSizePackets()); if (PeriodicProcess()) { pacer_->ProcessPackets(); } else { while (pacer_->QueueSizePackets() > packets_to_send_per_interval * 10) { AdvanceTimeAndProcess(); } } 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) { if (PeriodicProcess()) { clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); } else { AdvanceTimeAndProcess(); } } const TimeDelta actual_pace_time = clock_.CurrentTime() - start_time; EXPECT_LT((actual_pace_time - expected_pace_time).Abs(), PeriodicProcess() ? TimeDelta::Millis(5) : PacingController::kMinSleepTime); EXPECT_EQ(0u, pacer_->QueueSizePackets()); clock_.AdvanceTime(TimeUntilNextProcess()); 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(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), 250); } EXPECT_EQ(packets_to_send_per_interval, pacer_->QueueSizePackets()); if (PeriodicProcess()) { pacer_->ProcessPackets(); } else { for (size_t i = 0; i < packets_to_send_per_interval; ++i) { AdvanceTimeAndProcess(); } } EXPECT_EQ(0u, pacer_->QueueSizePackets()); } TEST_P(PacingControllerTest, RepeatedRetransmissionsAllowed) { // 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(is_retransmission ? RtpPacketMediaType::kRetransmission : RtpPacketMediaType::kVideo, ssrc, sequence_number, clock_.TimeInMilliseconds(), bytes); clock_.AdvanceTimeMilliseconds(5); } if (PeriodicProcess()) { pacer_->ProcessPackets(); } else { while (pacer_->QueueSizePackets() > 0) { AdvanceTimeAndProcess(); } } } TEST_P(PacingControllerTest, CanQueuePacketsWithSameSequenceNumberOnDifferentSsrcs) { uint32_t ssrc = 12345; uint16_t sequence_number = 1234; SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number, clock_.TimeInMilliseconds(), 250); // Expect packet on second ssrc to be queued and sent as well. SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc + 1, sequence_number, clock_.TimeInMilliseconds(), 250); clock_.AdvanceTimeMilliseconds(1000); if (PeriodicProcess()) { pacer_->ProcessPackets(); } else { while (pacer_->QueueSizePackets() > 0) { AdvanceTimeAndProcess(); } } } TEST_P(PacingControllerTest, Padding) { uint32_t ssrc = 12345; uint16_t sequence_number = 1234; const size_t kPacketSize = 250; pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate); if (PeriodicProcess()) { ConsumeInitialBudget(); // 5 milliseconds later should not send padding since we filled the buffers // initially. EXPECT_CALL(callback_, SendPadding(kPacketSize)).Times(0); clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); // 5 milliseconds later we have enough budget to send some padding. EXPECT_CALL(callback_, SendPadding(250)).WillOnce(Return(kPacketSize)); EXPECT_CALL(callback_, SendPacket(_, _, _, _, true)).Times(1); clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); } else { const size_t kPacketsToSend = 20; for (size_t i = 0; i < kPacketsToSend; ++i) { SendAndExpectPacket(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) { AdvanceTimeAndProcess(); } 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) { AdvanceTimeAndProcess(); } // 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_P(PacingControllerTest, NoPaddingBeforeNormalPacket) { pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate); EXPECT_CALL(callback_, SendPadding).Times(0); pacer_->ProcessPackets(); clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); clock_.AdvanceTime(TimeUntilNextProcess()); uint32_t ssrc = 12345; uint16_t sequence_number = 1234; int64_t capture_time_ms = 56789; SendAndExpectPacket(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); if (PeriodicProcess()) { pacer_->ProcessPackets(); } else { while (!padding_sent) { AdvanceTimeAndProcess(); } } } TEST_P(PacingControllerTest, VerifyPaddingUpToBitrate) { if (!PeriodicProcess()) { // Already tested in PacingControllerTest.Padding. return; } uint32_t ssrc = 12345; uint16_t sequence_number = 1234; int64_t capture_time_ms = 56789; const int kTimeStep = 5; const int64_t kBitrateWindow = 100; pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate); int64_t start_time = clock_.TimeInMilliseconds(); while (clock_.TimeInMilliseconds() - start_time < kBitrateWindow) { SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, capture_time_ms, 250); EXPECT_CALL(callback_, SendPadding(250)).WillOnce(Return(250)); EXPECT_CALL(callback_, SendPacket(_, _, _, _, true)).Times(1); pacer_->ProcessPackets(); clock_.AdvanceTimeMilliseconds(kTimeStep); } } TEST_P(PacingControllerTest, VerifyAverageBitrateVaryingMediaPayload) { uint32_t ssrc = 12345; uint16_t sequence_number = 1234; int64_t capture_time_ms = 56789; const int kTimeStep = 5; const TimeDelta kAveragingWindowLength = TimeDelta::Seconds(10); PacingControllerPadding callback; pacer_ = std::make_unique(&clock_, &callback, nullptr, nullptr, GetParam()); 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 media_payload = rand_value % 400 + 800; // [400, 1200] bytes. Send(RtpPacketMediaType::kVideo, ssrc, sequence_number++, capture_time_ms, media_payload); media_bytes += media_payload; } if (PeriodicProcess()) { clock_.AdvanceTimeMilliseconds(kTimeStep); pacer_->ProcessPackets(); } else { AdvanceTimeAndProcess(); } } EXPECT_NEAR( kTargetRate.bps(), (DataSize::Bytes(callback.total_bytes_sent()) / kAveragingWindowLength) .bps(), (kTargetRate * 0.01 /* 1% error marging */).bps()); } TEST_P(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; ConsumeInitialBudget(); // Expect normal and low priority to be queued and high to pass through. Send(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) { Send(RtpPacketMediaType::kRetransmission, ssrc, sequence_number++, capture_time_ms, 250); } Send(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); if (PeriodicProcess()) { clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); } else { while (pacer_->QueueSizePackets() > 1) { AdvanceTimeAndProcess(); } } EXPECT_EQ(1u, pacer_->QueueSizePackets()); EXPECT_CALL(callback_, SendPacket(ssrc_low_priority, _, capture_time_ms_low_priority, _, _)) .Times(1); if (PeriodicProcess()) { clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); } else { AdvanceTimeAndProcess(); } } TEST_P(PacingControllerTest, RetransmissionPriority) { uint32_t ssrc = 12345; uint16_t sequence_number = 1234; int64_t capture_time_ms = 45678; int64_t capture_time_ms_retransmission = 56789; // 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) { Send(RtpPacketMediaType::kVideo, ssrc, sequence_number++, capture_time_ms, 250); Send(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); if (PeriodicProcess()) { clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); } else { while (pacer_->QueueSizePackets() > packets_to_send_per_interval) { AdvanceTimeAndProcess(); } } 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); if (PeriodicProcess()) { clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); } else { while (pacer_->QueueSizePackets() > 0) { AdvanceTimeAndProcess(); } } EXPECT_EQ(0u, pacer_->QueueSizePackets()); } TEST_P(PacingControllerTest, HighPrioDoesntAffectBudget) { const size_t kPacketSize = 250; uint32_t ssrc = 12346; uint16_t sequence_number = 1234; int64_t capture_time_ms = 56789; // 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(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(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); } // Send all packets and measure pace time. Timestamp start_time = clock_.CurrentTime(); while (pacer_->QueueSizePackets() > 0) { if (PeriodicProcess()) { clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); } else { AdvanceTimeAndProcess(); } } // 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(), expected_pacing_time.us(), PeriodicProcess() ? 5000.0 : PacingController::kMinSleepTime.us()); } TEST_P(PacingControllerTest, SendsOnlyPaddingWhenCongested) { uint32_t ssrc = 202020; uint16_t sequence_number = 1000; int kPacketSize = 250; int kCongestionWindow = kPacketSize * 10; pacer_->UpdateOutstandingData(DataSize::Zero()); pacer_->SetCongestionWindow(DataSize::Bytes(kCongestionWindow)); int sent_data = 0; while (sent_data < kCongestionWindow) { sent_data += kPacketSize; SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); AdvanceTimeAndProcess(); } ::testing::Mock::VerifyAndClearExpectations(&callback_); EXPECT_CALL(callback_, SendPacket).Times(0); EXPECT_CALL(callback_, SendPadding).Times(0); size_t blocked_packets = 0; int64_t expected_time_until_padding = 500; while (expected_time_until_padding > 5) { Send(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_P(PacingControllerTest, DoesNotAllowOveruseAfterCongestion) { uint32_t ssrc = 202020; uint16_t seq_num = 1000; int size = 1000; auto now_ms = [this] { return clock_.TimeInMilliseconds(); }; 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()); // The congestion window is small enough to only let one packet through. pacer_->SetCongestionWindow(DataSize::Bytes(800)); pacer_->UpdateOutstandingData(DataSize::Zero()); // Not yet budget limited or congested, packet is sent. Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); EXPECT_CALL(callback_, SendPacket).Times(1); clock_.AdvanceTimeMilliseconds(5); pacer_->ProcessPackets(); // Packet blocked due to congestion. Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); EXPECT_CALL(callback_, SendPacket).Times(0); clock_.AdvanceTimeMilliseconds(5); pacer_->ProcessPackets(); // Packet blocked due to congestion. Send(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. Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); EXPECT_CALL(callback_, SendPacket).Times(1); clock_.AdvanceTimeMilliseconds(5); pacer_->UpdateOutstandingData(DataSize::Zero()); pacer_->ProcessPackets(); // Should be blocked due to budget limitation as congestion has be removed. Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); EXPECT_CALL(callback_, SendPacket).Times(0); clock_.AdvanceTimeMilliseconds(5); pacer_->ProcessPackets(); } TEST_P(PacingControllerTest, ResumesSendingWhenCongestionEnds) { uint32_t ssrc = 202020; uint16_t sequence_number = 1000; int64_t kPacketSize = 250; int64_t kCongestionCount = 10; int64_t kCongestionWindow = kPacketSize * kCongestionCount; int64_t kCongestionTimeMs = 1000; pacer_->UpdateOutstandingData(DataSize::Zero()); pacer_->SetCongestionWindow(DataSize::Bytes(kCongestionWindow)); int sent_data = 0; while (sent_data < kCongestionWindow) { sent_data += kPacketSize; SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); clock_.AdvanceTimeMilliseconds(5); pacer_->ProcessPackets(); } ::testing::Mock::VerifyAndClearExpectations(&callback_); EXPECT_CALL(callback_, SendPacket).Times(0); int unacked_packets = 0; for (int duration = 0; duration < kCongestionTimeMs; duration += 5) { Send(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); unacked_packets++; clock_.AdvanceTimeMilliseconds(5); pacer_->ProcessPackets(); } ::testing::Mock::VerifyAndClearExpectations(&callback_); // First mark half of the congested packets as cleared and make sure that just // as many are sent int ack_count = kCongestionCount / 2; EXPECT_CALL(callback_, SendPacket(ssrc, _, _, false, _)).Times(ack_count); pacer_->UpdateOutstandingData( DataSize::Bytes(kCongestionWindow - kPacketSize * ack_count)); for (int duration = 0; duration < kCongestionTimeMs; duration += 5) { clock_.AdvanceTimeMilliseconds(5); pacer_->ProcessPackets(); } unacked_packets -= ack_count; ::testing::Mock::VerifyAndClearExpectations(&callback_); // Second make sure all packets are sent if sent packets are continuously // marked as acked. EXPECT_CALL(callback_, SendPacket(ssrc, _, _, false, _)) .Times(unacked_packets); for (int duration = 0; duration < kCongestionTimeMs; duration += 5) { pacer_->UpdateOutstandingData(DataSize::Zero()); clock_.AdvanceTimeMilliseconds(5); pacer_->ProcessPackets(); } } TEST_P(PacingControllerTest, Pause) { uint32_t ssrc_low_priority = 12345; uint32_t ssrc = 12346; uint32_t ssrc_high_priority = 12347; uint16_t sequence_number = 1234; EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); ConsumeInitialBudget(); 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) { Send(RtpPacketMediaType::kVideo, ssrc_low_priority, sequence_number++, capture_time_ms, 250); Send(RtpPacketMediaType::kRetransmission, ssrc, sequence_number++, capture_time_ms, 250); Send(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) { Send(RtpPacketMediaType::kVideo, ssrc_low_priority, sequence_number++, second_capture_time_ms, 250); Send(RtpPacketMediaType::kRetransmission, ssrc, sequence_number++, second_capture_time_ms, 250); Send(RtpPacketMediaType::kAudio, ssrc_high_priority, sequence_number++, second_capture_time_ms, 250); } // Expect everything to be queued. EXPECT_EQ(TimeDelta::Millis(second_capture_time_ms - capture_time_ms), pacer_->OldestPacketWaitTime()); // 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(); if (PeriodicProcess()) { // The pacer was resumed directly after the previous process call finished. // It will therefore wait 5 ms until next process. clock_.AdvanceTime(TimeUntilNextProcess()); for (size_t i = 0; i < 4; i++) { pacer_->ProcessPackets(); clock_.AdvanceTime(TimeUntilNextProcess()); } } else { while (pacer_->QueueSizePackets() > 0) { AdvanceTimeAndProcess(); } } EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); } TEST_P(PacingControllerTest, InactiveFromStart) { // Recreate the pacer without the inital time forwarding. pacer_ = std::make_unique(&clock_, &callback_, nullptr, nullptr, GetParam()); pacer_->SetProbingEnabled(false); pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate); if (PeriodicProcess()) { // In period mode, pause the pacer to check the same idle behavior as // dynamic. pacer_->Pause(); } // 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 = (GetParam() == PacingController::ProcessMode::kDynamic ? PacingController::kMinSleepTime : TimeDelta::Zero()) + 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_P(PacingControllerTest, ExpectedQueueTimeMs) { uint32_t ssrc = 12346; uint16_t sequence_number = 1234; const size_t kNumPackets = 60; const size_t kPacketSize = 1200; const int32_t kMaxBitrate = kPaceMultiplier * 30000; EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); pacer_->SetPacingRates(DataRate::BitsPerSec(30000 * kPaceMultiplier), DataRate::Zero()); for (size_t i = 0; i < kNumPackets; ++i) { SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); } // Queue in ms = 1000 * (bytes in queue) *8 / (bits per second) TimeDelta queue_time = TimeDelta::Millis(1000 * kNumPackets * kPacketSize * 8 / kMaxBitrate); EXPECT_EQ(queue_time, pacer_->ExpectedQueueTime()); const Timestamp time_start = clock_.CurrentTime(); while (pacer_->QueueSizePackets() > 0) { clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); } TimeDelta duration = clock_.CurrentTime() - time_start; EXPECT_EQ(TimeDelta::Zero(), pacer_->ExpectedQueueTime()); // Allow for aliasing, duration should be within one pack of max time limit. const TimeDelta deviation = duration - PacingController::kMaxExpectedQueueLength; EXPECT_LT(deviation.Abs(), TimeDelta::Millis(1000 * kPacketSize * 8 / kMaxBitrate)); } TEST_P(PacingControllerTest, QueueTimeGrowsOverTime) { uint32_t ssrc = 12346; uint16_t sequence_number = 1234; EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); pacer_->SetPacingRates(DataRate::BitsPerSec(30000 * kPaceMultiplier), DataRate::Zero()); SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number, clock_.TimeInMilliseconds(), 1200); clock_.AdvanceTimeMilliseconds(500); EXPECT_EQ(TimeDelta::Millis(500), pacer_->OldestPacketWaitTime()); pacer_->ProcessPackets(); EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); } TEST_P(PacingControllerTest, ProbingWithInsertedPackets) { const size_t kPacketSize = 1200; const int kInitialBitrateBps = 300000; uint32_t ssrc = 12346; uint16_t sequence_number = 1234; PacingControllerProbing packet_sender; pacer_ = std::make_unique(&clock_, &packet_sender, nullptr, nullptr, GetParam()); pacer_->CreateProbeCluster(kFirstClusterRate, /*cluster_id=*/0); pacer_->CreateProbeCluster(kSecondClusterRate, /*cluster_id=*/1); pacer_->SetPacingRates( DataRate::BitsPerSec(kInitialBitrateBps * kPaceMultiplier), DataRate::Zero()); for (int i = 0; i < 10; ++i) { Send(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); } int64_t start = clock_.TimeInMilliseconds(); while (packet_sender.packets_sent() < 5) { clock_.AdvanceTime(TimeUntilNextProcess()); 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()); EXPECT_EQ(0, packet_sender.padding_sent()); clock_.AdvanceTime(TimeUntilNextProcess()); start = clock_.TimeInMilliseconds(); while (packet_sender.packets_sent() < 10) { clock_.AdvanceTime(TimeUntilNextProcess()); 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_P(PacingControllerTest, SkipsProbesWhenProcessIntervalTooLarge) { const size_t kPacketSize = 1200; const int kInitialBitrateBps = 300000; const uint32_t ssrc = 12346; const int kProbeClusterId = 3; // Test with both legacy and new probe discard modes. // TODO(bugs.webrtc.org/11780): Clean up when legacy is gone. for (bool abort_delayed_probes : {false, true}) { uint16_t sequence_number = 1234; PacingControllerProbing packet_sender; const test::ExplicitKeyValueConfig trials( abort_delayed_probes ? "WebRTC-Bwe-ProbingBehavior/" "abort_delayed_probes:1,max_probe_delay:2ms/" : "WebRTC-Bwe-ProbingBehavior/" "abort_delayed_probes:0,max_probe_delay:2ms/"); pacer_ = std::make_unique(&clock_, &packet_sender, nullptr, &trials, GetParam()); pacer_->SetPacingRates( DataRate::BitsPerSec(kInitialBitrateBps * kPaceMultiplier), DataRate::BitsPerSec(kInitialBitrateBps)); for (int i = 0; i < 10; ++i) { Send(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); } while (pacer_->QueueSizePackets() > 0) { clock_.AdvanceTime(TimeUntilNextProcess()); pacer_->ProcessPackets(); } // Probe at a very high rate. pacer_->CreateProbeCluster(DataRate::KilobitsPerSec(10000), // 10 Mbps. /*cluster_id=*/kProbeClusterId); // We need one packet to start the probe. Send(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); const int packets_sent_before_probe = packet_sender.packets_sent(); clock_.AdvanceTime(TimeUntilNextProcess()); 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(); clock_.AdvanceTime(TimeUntilNextProcess()); TimeDelta time_between_probes = clock_.CurrentTime() - start_time; // Advance that distance again + 1ms. clock_.AdvanceTime(time_between_probes); // Send second probe packet. Send(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(); if (abort_delayed_probes) { // 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. if (PeriodicProcess()) { do { AdvanceTimeAndProcess(); } while (packet_sender.total_packets_sent() == packets_sent_before_timeout); } else { AdvanceTimeAndProcess(); } const int expected_probe_id = PacedPacketInfo::kNotAProbe; EXPECT_EQ(packet_sender.last_pacing_info().probe_cluster_id, expected_probe_id); } else { // Legacy behaviour, probe "aborted" so send time moved back. Next call to // ProcessPackets() still results in packets being marked as part of probe // cluster. EXPECT_GT(pacer_->NextSendTime(), probe_time); AdvanceTimeAndProcess(); EXPECT_GT(packet_sender.total_packets_sent(), packets_sent_before_timeout); const int expected_probe_id = last_pacing_info.probe_cluster_id; EXPECT_EQ(packet_sender.last_pacing_info().probe_cluster_id, expected_probe_id); // Time between sent packets keeps being too large, but we still mark the // packets as being part of the cluster. Timestamp a = clock_.CurrentTime(); AdvanceTimeAndProcess(); EXPECT_GT(packet_sender.total_packets_sent(), packets_sent_before_timeout); EXPECT_EQ(packet_sender.last_pacing_info().probe_cluster_id, expected_probe_id); EXPECT_GT(clock_.CurrentTime() - a, time_between_probes); } } } TEST_P(PacingControllerTest, ProbingWithPaddingSupport) { const size_t kPacketSize = 1200; const int kInitialBitrateBps = 300000; uint32_t ssrc = 12346; uint16_t sequence_number = 1234; PacingControllerProbing packet_sender; pacer_ = std::make_unique(&clock_, &packet_sender, nullptr, nullptr, GetParam()); pacer_->CreateProbeCluster(kFirstClusterRate, /*cluster_id=*/0); pacer_->SetPacingRates( DataRate::BitsPerSec(kInitialBitrateBps * kPaceMultiplier), DataRate::Zero()); for (int i = 0; i < 3; ++i) { Send(RtpPacketMediaType::kVideo, ssrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); } int64_t start = clock_.TimeInMilliseconds(); int process_count = 0; while (process_count < 5) { clock_.AdvanceTime(TimeUntilNextProcess()); 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_P(PacingControllerTest, PaddingOveruse) { uint32_t ssrc = 12346; uint16_t sequence_number = 1234; const size_t kPacketSize = 1200; // Initially no padding rate. pacer_->ProcessPackets(); pacer_->SetPacingRates(DataRate::BitsPerSec(60000 * kPaceMultiplier), DataRate::Zero()); SendAndExpectPacket(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(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); if (PeriodicProcess()) { pacer_->ProcessPackets(); } else { AdvanceTimeAndProcess(); } } TEST_P(PacingControllerTest, ProbeClusterId) { MockPacketSender callback; pacer_ = std::make_unique(&clock_, &callback, nullptr, nullptr, GetParam()); Init(); uint32_t ssrc = 12346; uint16_t sequence_number = 1234; const size_t kPacketSize = 1200; pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate); pacer_->SetProbingEnabled(true); for (int i = 0; i < 10; ++i) { Send(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) { AdvanceTimeAndProcess(); } // Second probing cluster. EXPECT_CALL(callback, SendPacket(_, Field(&PacedPacketInfo::probe_cluster_id, 1))) .Times(5); for (int i = 0; i < 5; ++i) { AdvanceTimeAndProcess(); } // 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> 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 packet, const PacedPacketInfo& cluster_info) { EXPECT_EQ(cluster_info.probe_cluster_id, kNotAProbe); non_probe_packet_seen = true; }); while (!non_probe_packet_seen) { AdvanceTimeAndProcess(); } } TEST_P(PacingControllerTest, OwnedPacketPrioritizedOnType) { MockPacketSender callback; pacer_ = std::make_unique(&clock_, &callback, nullptr, nullptr, GetParam()); Init(); // 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(BuildRtpPacket(type)); } ::testing::InSequence seq; EXPECT_CALL( callback, SendPacket(Pointee(Property(&RtpPacketToSend::Ssrc, kAudioSsrc)), _)); EXPECT_CALL( callback, SendPacket(Pointee(Property(&RtpPacketToSend::Ssrc, kVideoRtxSsrc)), _)); // FEC and video actually have the same priority, so will come out in // insertion order. EXPECT_CALL( callback, SendPacket(Pointee(Property(&RtpPacketToSend::Ssrc, kFlexFecSsrc)), _)); EXPECT_CALL( callback, SendPacket(Pointee(Property(&RtpPacketToSend::Ssrc, kVideoSsrc)), _)); EXPECT_CALL( callback, SendPacket(Pointee(Property(&RtpPacketToSend::Ssrc, kVideoRtxSsrc)), _)); while (pacer_->QueueSizePackets() > 0) { if (PeriodicProcess()) { clock_.AdvanceTimeMilliseconds(5); pacer_->ProcessPackets(); } else { AdvanceTimeAndProcess(); } } } TEST_P(PacingControllerTest, SmallFirstProbePacket) { ScopedFieldTrials trial("WebRTC-Pacer-SmallFirstProbePacket/Enabled/"); MockPacketSender callback; pacer_ = std::make_unique(&clock_, &callback, nullptr, nullptr, GetParam()); pacer_->CreateProbeCluster(kFirstClusterRate, /*cluster_id=*/0); pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero()); // Add high prio media. pacer_->EnqueuePacket(BuildRtpPacket(RtpPacketMediaType::kAudio)); // Expect small padding packet to be requested. EXPECT_CALL(callback, GeneratePadding(DataSize::Bytes(1))) .WillOnce([&](DataSize padding_size) { std::vector> 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 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_P(PacingControllerTest, TaskLate) { if (PeriodicProcess()) { // This test applies only when NOT using interval budget. return; } // Set a low send rate to more easily test timing issues. DataRate kSendRate = DataRate::KilobitsPerSec(30); pacer_->SetPacingRates(kSendRate, DataRate::Zero()); // Add four packets of equal size and priority. pacer_->EnqueuePacket(BuildRtpPacket(RtpPacketMediaType::kVideo)); pacer_->EnqueuePacket(BuildRtpPacket(RtpPacketMediaType::kVideo)); pacer_->EnqueuePacket(BuildRtpPacket(RtpPacketMediaType::kVideo)); pacer_->EnqueuePacket(BuildRtpPacket(RtpPacketMediaType::kVideo)); // 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_P(PacingControllerTest, NoProbingWhilePaused) { uint32_t ssrc = 12345; uint16_t sequence_number = 1234; pacer_->SetProbingEnabled(true); // Send at least one packet so probing can initate. SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number, clock_.TimeInMilliseconds(), 250); while (pacer_->QueueSizePackets() > 0) { AdvanceTimeAndProcess(); } // Trigger probing. pacer_->CreateProbeCluster(DataRate::KilobitsPerSec(10000), // 10 Mbps. /*cluster_id=*/3); // 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_P(PacingControllerTest, AudioNotPacedEvenWhenAccountedFor) { const uint32_t kSsrc = 12345; uint16_t sequence_number = 1234; const size_t kPacketSize = 123; // 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(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(RtpPacketMediaType::kAudio, kSsrc, sequence_number++, clock_.TimeInMilliseconds(), kPacketSize); pacer_->ProcessPackets(); } TEST_P(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()); pacer_ = std::make_unique(&clock_, &callback, nullptr, nullptr, GetParam()); 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) { AdvanceTimeAndProcess(); } // 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> 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_P(PacingControllerTest, AccountsForAudioEnqueuTime) { if (PeriodicProcess()) { // This test applies only when NOT using interval budget. return; } 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; // 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(RtpPacketMediaType::kAudio, kSsrc, sequnce_number++, clock_.TimeInMilliseconds(), kPacketSize.bytes()); SendAndExpectPacket(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. Send(RtpPacketMediaType::kVideo, kSsrc + 1, sequnce_number++, clock_.TimeInMilliseconds(), kPacketSize.bytes()); EXPECT_EQ(pacer_->NextSendTime() - clock_.CurrentTime(), kPacketPacingTime); } TEST_P(PacingControllerTest, NextSendTimeAccountsForPadding) { if (PeriodicProcess()) { // This test applies only when NOT using interval budget. return; } 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; // Start with no padding. pacer_->SetPacingRates(kPacingDataRate, DataRate::Zero()); // Send a single packet. SendAndExpectPacket(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(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. Send(RtpPacketMediaType::kVideo, kSsrc, sequnce_number++, clock_.TimeInMilliseconds(), kPacketSize.bytes()); EXPECT_EQ(pacer_->NextSendTime() - clock_.CurrentTime(), kPacketPacingTime); } TEST_P(PacingControllerTest, PaddingTargetAccountsForPaddingRate) { if (PeriodicProcess()) { // This test applies only when NOT using interval budget. return; } // Re-init pacer with an explicitly set padding target of 10ms; const TimeDelta kPaddingTarget = TimeDelta::Millis(10); ScopedFieldTrials field_trials( "WebRTC-Pacer-DynamicPaddingTarget/timedelta:10ms/"); SetUp(); 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(RtpPacketMediaType::kVideo, kSsrc, sequnce_number++, clock_.TimeInMilliseconds(), kPacketSize.bytes()); AdvanceTimeAndProcess(); ::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)); AdvanceTimeAndProcess(); // 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)); AdvanceTimeAndProcess(); } TEST_P(PacingControllerTest, SendsDeferredFecPackets) { ScopedFieldTrials trial("WebRTC-DeferredFecGeneration/Enabled/"); SetUp(); 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; // 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(); Send(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> fec_packets; fec_packets.push_back( BuildPacket(RtpPacketMediaType::kForwardErrorCorrection, kFlexSsrc, flexfec_sequence_number, now, kPacketSize)); return fec_packets; }); AdvanceTimeAndProcess(); AdvanceTimeAndProcess(); } INSTANTIATE_TEST_SUITE_P( WithAndWithoutIntervalBudget, PacingControllerTest, ::testing::Values(PacingController::ProcessMode::kPeriodic, PacingController::ProcessMode::kDynamic)); } // namespace test } // namespace webrtc