| /* |
| * Copyright (c) 2018 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/congestion_controller/pcc/bitrate_controller.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "modules/congestion_controller/pcc/monitor_interval.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| |
| namespace webrtc { |
| namespace pcc { |
| namespace test { |
| namespace { |
| constexpr double kInitialConversionFactor = 1; |
| constexpr double kInitialDynamicBoundary = 0.05; |
| constexpr double kDynamicBoundaryIncrement = 0.1; |
| |
| constexpr double kDelayGradientCoefficient = 900; |
| constexpr double kLossCoefficient = 11.35; |
| constexpr double kThroughputCoefficient = 500 * 1000; |
| constexpr double kThroughputPower = 0.99; |
| constexpr double kDelayGradientThreshold = 0.01; |
| constexpr double kDelayGradientNegativeBound = 10; |
| |
| const DataRate kTargetSendingRate = DataRate::KilobitsPerSec(300); |
| const double kEpsilon = 0.05; |
| const Timestamp kStartTime = Timestamp::Micros(0); |
| const TimeDelta kPacketsDelta = TimeDelta::Millis(1); |
| const TimeDelta kIntervalDuration = TimeDelta::Millis(1000); |
| const TimeDelta kDefaultRtt = TimeDelta::Millis(1000); |
| const DataSize kDefaultDataSize = DataSize::Bytes(100); |
| |
| std::vector<PacketResult> CreatePacketResults( |
| const std::vector<Timestamp>& packets_send_times, |
| const std::vector<Timestamp>& packets_received_times = {}, |
| const std::vector<DataSize>& packets_sizes = {}) { |
| std::vector<PacketResult> packet_results; |
| PacketResult packet_result; |
| SentPacket sent_packet; |
| for (size_t i = 0; i < packets_send_times.size(); ++i) { |
| sent_packet.send_time = packets_send_times[i]; |
| if (packets_sizes.empty()) { |
| sent_packet.size = kDefaultDataSize; |
| } else { |
| sent_packet.size = packets_sizes[i]; |
| } |
| packet_result.sent_packet = sent_packet; |
| if (packets_received_times.empty()) { |
| packet_result.receive_time = packets_send_times[i] + kDefaultRtt; |
| } else { |
| packet_result.receive_time = packets_received_times[i]; |
| } |
| packet_results.push_back(packet_result); |
| } |
| return packet_results; |
| } |
| |
| class MockUtilityFunction : public PccUtilityFunctionInterface { |
| public: |
| MOCK_METHOD(double, |
| Compute, |
| (const PccMonitorInterval& monitor_interval), |
| (const, override)); |
| }; |
| |
| } // namespace |
| |
| TEST(PccBitrateControllerTest, IncreaseRateWhenNoChangesForTestBitrates) { |
| PccBitrateController bitrate_controller( |
| kInitialConversionFactor, kInitialDynamicBoundary, |
| kDynamicBoundaryIncrement, kDelayGradientCoefficient, kLossCoefficient, |
| kThroughputCoefficient, kThroughputPower, kDelayGradientThreshold, |
| kDelayGradientNegativeBound); |
| VivaceUtilityFunction utility_function( |
| kDelayGradientCoefficient, kLossCoefficient, kThroughputCoefficient, |
| kThroughputPower, kDelayGradientThreshold, kDelayGradientNegativeBound); |
| std::vector<PccMonitorInterval> monitor_block{ |
| PccMonitorInterval(kTargetSendingRate * (1 + kEpsilon), kStartTime, |
| kIntervalDuration), |
| PccMonitorInterval(kTargetSendingRate * (1 - kEpsilon), |
| kStartTime + kIntervalDuration, kIntervalDuration)}; |
| monitor_block[0].OnPacketsFeedback( |
| CreatePacketResults({kStartTime + kPacketsDelta, |
| kStartTime + kIntervalDuration + kPacketsDelta, |
| kStartTime + 3 * kIntervalDuration}, |
| {}, {})); |
| monitor_block[1].OnPacketsFeedback( |
| CreatePacketResults({kStartTime + kPacketsDelta, |
| kStartTime + kIntervalDuration + kPacketsDelta, |
| kStartTime + 3 * kIntervalDuration}, |
| {}, {})); |
| // For both of the monitor intervals there were no change in rtt gradient |
| // and in packet loss. Since the only difference is in the sending rate, |
| // the higher sending rate should be chosen by congestion controller. |
| EXPECT_GT(bitrate_controller |
| .ComputeRateUpdateForOnlineLearningMode(monitor_block, |
| kTargetSendingRate) |
| .bps(), |
| kTargetSendingRate.bps()); |
| } |
| |
| TEST(PccBitrateControllerTest, NoChangesWhenUtilityFunctionDoesntChange) { |
| std::unique_ptr<MockUtilityFunction> mock_utility_function = |
| std::make_unique<MockUtilityFunction>(); |
| EXPECT_CALL(*mock_utility_function, Compute(::testing::_)) |
| .Times(2) |
| .WillOnce(::testing::Return(100)) |
| .WillOnce(::testing::Return(100)); |
| |
| PccBitrateController bitrate_controller( |
| kInitialConversionFactor, kInitialDynamicBoundary, |
| kDynamicBoundaryIncrement, std::move(mock_utility_function)); |
| std::vector<PccMonitorInterval> monitor_block{ |
| PccMonitorInterval(kTargetSendingRate * (1 + kEpsilon), kStartTime, |
| kIntervalDuration), |
| PccMonitorInterval(kTargetSendingRate * (1 - kEpsilon), |
| kStartTime + kIntervalDuration, kIntervalDuration)}; |
| // To complete collecting feedback within monitor intervals. |
| monitor_block[0].OnPacketsFeedback( |
| CreatePacketResults({kStartTime + 3 * kIntervalDuration}, {}, {})); |
| monitor_block[1].OnPacketsFeedback( |
| CreatePacketResults({kStartTime + 3 * kIntervalDuration}, {}, {})); |
| // Because we don't have any packets inside of monitor intervals, utility |
| // function should be zero for both of them and the sending rate should not |
| // change. |
| EXPECT_EQ(bitrate_controller |
| .ComputeRateUpdateForOnlineLearningMode(monitor_block, |
| kTargetSendingRate) |
| .bps(), |
| kTargetSendingRate.bps()); |
| } |
| |
| TEST(PccBitrateControllerTest, NoBoundaryWhenSmallGradient) { |
| std::unique_ptr<MockUtilityFunction> mock_utility_function = |
| std::make_unique<MockUtilityFunction>(); |
| constexpr double kFirstMonitorIntervalUtility = 0; |
| const double kSecondMonitorIntervalUtility = |
| 2 * kTargetSendingRate.bps() * kEpsilon; |
| |
| EXPECT_CALL(*mock_utility_function, Compute(::testing::_)) |
| .Times(2) |
| .WillOnce(::testing::Return(kFirstMonitorIntervalUtility)) |
| .WillOnce(::testing::Return(kSecondMonitorIntervalUtility)); |
| |
| PccBitrateController bitrate_controller( |
| kInitialConversionFactor, kInitialDynamicBoundary, |
| kDynamicBoundaryIncrement, std::move(mock_utility_function)); |
| std::vector<PccMonitorInterval> monitor_block{ |
| PccMonitorInterval(kTargetSendingRate * (1 + kEpsilon), kStartTime, |
| kIntervalDuration), |
| PccMonitorInterval(kTargetSendingRate * (1 - kEpsilon), |
| kStartTime + kIntervalDuration, kIntervalDuration)}; |
| // To complete collecting feedback within monitor intervals. |
| monitor_block[0].OnPacketsFeedback( |
| CreatePacketResults({kStartTime + 3 * kIntervalDuration}, {}, {})); |
| monitor_block[1].OnPacketsFeedback( |
| CreatePacketResults({kStartTime + 3 * kIntervalDuration}, {}, {})); |
| |
| double gradient = |
| (kFirstMonitorIntervalUtility - kSecondMonitorIntervalUtility) / |
| (kTargetSendingRate.bps() * 2 * kEpsilon); |
| // When the gradient is small we don't hit the dynamic boundary. |
| EXPECT_EQ(bitrate_controller |
| .ComputeRateUpdateForOnlineLearningMode(monitor_block, |
| kTargetSendingRate) |
| .bps(), |
| kTargetSendingRate.bps() + kInitialConversionFactor * gradient); |
| } |
| |
| TEST(PccBitrateControllerTest, FaceBoundaryWhenLargeGradient) { |
| std::unique_ptr<MockUtilityFunction> mock_utility_function = |
| std::make_unique<MockUtilityFunction>(); |
| constexpr double kFirstMonitorIntervalUtility = 0; |
| const double kSecondMonitorIntervalUtility = |
| 10 * kInitialDynamicBoundary * kTargetSendingRate.bps() * 2 * |
| kTargetSendingRate.bps() * kEpsilon; |
| |
| EXPECT_CALL(*mock_utility_function, Compute(::testing::_)) |
| .Times(4) |
| .WillOnce(::testing::Return(kFirstMonitorIntervalUtility)) |
| .WillOnce(::testing::Return(kSecondMonitorIntervalUtility)) |
| .WillOnce(::testing::Return(kFirstMonitorIntervalUtility)) |
| .WillOnce(::testing::Return(kSecondMonitorIntervalUtility)); |
| |
| PccBitrateController bitrate_controller( |
| kInitialConversionFactor, kInitialDynamicBoundary, |
| kDynamicBoundaryIncrement, std::move(mock_utility_function)); |
| std::vector<PccMonitorInterval> monitor_block{ |
| PccMonitorInterval(kTargetSendingRate * (1 + kEpsilon), kStartTime, |
| kIntervalDuration), |
| PccMonitorInterval(kTargetSendingRate * (1 - kEpsilon), |
| kStartTime + kIntervalDuration, kIntervalDuration)}; |
| // To complete collecting feedback within monitor intervals. |
| monitor_block[0].OnPacketsFeedback( |
| CreatePacketResults({kStartTime + 3 * kIntervalDuration}, {}, {})); |
| monitor_block[1].OnPacketsFeedback( |
| CreatePacketResults({kStartTime + 3 * kIntervalDuration}, {}, {})); |
| // The utility function gradient is too big and we hit the dynamic boundary. |
| EXPECT_EQ(bitrate_controller.ComputeRateUpdateForOnlineLearningMode( |
| monitor_block, kTargetSendingRate), |
| kTargetSendingRate * (1 - kInitialDynamicBoundary)); |
| // For the second time we hit the dynamic boundary in the same direction, the |
| // boundary should increase. |
| EXPECT_EQ(bitrate_controller |
| .ComputeRateUpdateForOnlineLearningMode(monitor_block, |
| kTargetSendingRate) |
| .bps(), |
| kTargetSendingRate.bps() * |
| (1 - kInitialDynamicBoundary - kDynamicBoundaryIncrement)); |
| } |
| |
| TEST(PccBitrateControllerTest, SlowStartMode) { |
| std::unique_ptr<MockUtilityFunction> mock_utility_function = |
| std::make_unique<MockUtilityFunction>(); |
| constexpr double kFirstUtilityFunction = 1000; |
| EXPECT_CALL(*mock_utility_function, Compute(::testing::_)) |
| .Times(4) |
| // For first 3 calls we expect to stay in the SLOW_START mode and double |
| // the sending rate since the utility function increases its value. For |
| // the last call utility function decreases its value, this means that |
| // we should not double the sending rate and exit SLOW_START mode. |
| .WillOnce(::testing::Return(kFirstUtilityFunction)) |
| .WillOnce(::testing::Return(kFirstUtilityFunction + 1)) |
| .WillOnce(::testing::Return(kFirstUtilityFunction + 2)) |
| .WillOnce(::testing::Return(kFirstUtilityFunction + 1)); |
| |
| PccBitrateController bitrate_controller( |
| kInitialConversionFactor, kInitialDynamicBoundary, |
| kDynamicBoundaryIncrement, std::move(mock_utility_function)); |
| std::vector<PccMonitorInterval> monitor_block{PccMonitorInterval( |
| 2 * kTargetSendingRate, kStartTime, kIntervalDuration)}; |
| // To complete collecting feedback within monitor intervals. |
| monitor_block[0].OnPacketsFeedback( |
| CreatePacketResults({kStartTime + 3 * kIntervalDuration}, {}, {})); |
| EXPECT_EQ( |
| bitrate_controller.ComputeRateUpdateForSlowStartMode(monitor_block[0]), |
| kTargetSendingRate * 2); |
| EXPECT_EQ( |
| bitrate_controller.ComputeRateUpdateForSlowStartMode(monitor_block[0]), |
| kTargetSendingRate * 2); |
| EXPECT_EQ( |
| bitrate_controller.ComputeRateUpdateForSlowStartMode(monitor_block[0]), |
| kTargetSendingRate * 2); |
| EXPECT_EQ( |
| bitrate_controller.ComputeRateUpdateForSlowStartMode(monitor_block[0]), |
| std::nullopt); |
| } |
| |
| TEST(PccBitrateControllerTest, StepSizeIncrease) { |
| std::unique_ptr<MockUtilityFunction> mock_utility_function = |
| std::make_unique<MockUtilityFunction>(); |
| constexpr double kFirstMiUtilityFunction = 0; |
| const double kSecondMiUtilityFunction = |
| 2 * kTargetSendingRate.bps() * kEpsilon; |
| |
| EXPECT_CALL(*mock_utility_function, Compute(::testing::_)) |
| .Times(4) |
| .WillOnce(::testing::Return(kFirstMiUtilityFunction)) |
| .WillOnce(::testing::Return(kSecondMiUtilityFunction)) |
| .WillOnce(::testing::Return(kFirstMiUtilityFunction)) |
| .WillOnce(::testing::Return(kSecondMiUtilityFunction)); |
| std::vector<PccMonitorInterval> monitor_block{ |
| PccMonitorInterval(kTargetSendingRate * (1 + kEpsilon), kStartTime, |
| kIntervalDuration), |
| PccMonitorInterval(kTargetSendingRate * (1 - kEpsilon), |
| kStartTime + kIntervalDuration, kIntervalDuration)}; |
| // To complete collecting feedback within monitor intervals. |
| monitor_block[0].OnPacketsFeedback( |
| CreatePacketResults({kStartTime + 3 * kIntervalDuration}, {}, {})); |
| monitor_block[1].OnPacketsFeedback( |
| CreatePacketResults({kStartTime + 3 * kIntervalDuration}, {}, {})); |
| |
| double gradient = (kFirstMiUtilityFunction - kSecondMiUtilityFunction) / |
| (kTargetSendingRate.bps() * 2 * kEpsilon); |
| PccBitrateController bitrate_controller( |
| kInitialConversionFactor, kInitialDynamicBoundary, |
| kDynamicBoundaryIncrement, std::move(mock_utility_function)); |
| // If we are moving in the same direction - the step size should increase. |
| EXPECT_EQ(bitrate_controller |
| .ComputeRateUpdateForOnlineLearningMode(monitor_block, |
| kTargetSendingRate) |
| .bps(), |
| kTargetSendingRate.bps() + kInitialConversionFactor * gradient); |
| EXPECT_EQ(bitrate_controller |
| .ComputeRateUpdateForOnlineLearningMode(monitor_block, |
| kTargetSendingRate) |
| .bps(), |
| kTargetSendingRate.bps() + 2 * kInitialConversionFactor * gradient); |
| } |
| |
| } // namespace test |
| } // namespace pcc |
| } // namespace webrtc |