/* * 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 #include #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 CreatePacketResults( const std::vector& packets_send_times, const std::vector& packets_received_times = {}, const std::vector& packets_sizes = {}) { std::vector 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 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 mock_utility_function = std::make_unique(); 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 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 mock_utility_function = std::make_unique(); 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 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 mock_utility_function = std::make_unique(); 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 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 mock_utility_function = std::make_unique(); 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 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]), absl::nullopt); } TEST(PccBitrateControllerTest, StepSizeIncrease) { std::unique_ptr mock_utility_function = std::make_unique(); 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 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