Network & bitrate controllers are added for PCC.

Network controller is an implementation of the
NetworkControllerInterface which is a part of congestion controller API.
Bitrate controller computes rate update each iteration (see
https://www.usenix.org/system/files/conference/nsdi18/nsdi18-dong.pdf).

Bug: webrtc:9434
Change-Id: I48d3d9e1c713985ef9ebe28dc1f1285757588c69
Reviewed-on: https://webrtc-review.googlesource.com/87222
Commit-Queue: Anastasia Koloskova <koloskova@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24376}
This commit is contained in:
Anastasia Koloskova
2018-08-21 16:12:55 +02:00
committed by Commit Bot
parent 801500cf99
commit ea9249ed15
9 changed files with 1227 additions and 0 deletions

View File

@ -8,6 +8,34 @@
import("../../../webrtc.gni")
rtc_static_library("pcc") {
sources = [
"pcc_factory.cc",
"pcc_factory.h",
]
deps = [
":pcc_controller",
"../../../api/transport:network_control",
"../../../rtc_base:rtc_base_approved",
"//third_party/abseil-cpp/absl/memory:memory",
]
}
rtc_static_library("pcc_controller") {
sources = [
"pcc_network_controller.cc",
"pcc_network_controller.h",
]
deps = [
":bitrate_controller",
":monitor_interval",
":rtt_tracker",
"../../../api/transport:network_control",
"../../../rtc_base:rtc_base_approved",
"//third_party/abseil-cpp/absl/memory:memory",
]
}
rtc_static_library("monitor_interval") {
sources = [
"monitor_interval.cc",
@ -42,23 +70,45 @@ rtc_static_library("utility_function") {
]
}
rtc_static_library("bitrate_controller") {
sources = [
"bitrate_controller.cc",
"bitrate_controller.h",
]
deps = [
":monitor_interval",
":utility_function",
"../../../api/transport:network_control",
"../../../rtc_base:rtc_base_approved",
"//third_party/abseil-cpp/absl/memory:memory",
"//third_party/abseil-cpp/absl/types:optional",
]
}
if (rtc_include_tests) {
rtc_source_set("pcc_unittests") {
testonly = true
sources = [
"bitrate_controller_unittest.cc",
"monitor_interval_unittest.cc",
"pcc_network_controller_unittest.cc",
"rtt_tracker_unittest.cc",
"utility_function_unittest.cc",
]
deps = [
":bitrate_controller",
":monitor_interval",
":pcc",
":pcc_controller",
":rtt_tracker",
":utility_function",
"../../../api/transport:network_control_test",
"../../../api/units:data_rate",
"../../../api/units:time_delta",
"../../../api/units:timestamp",
"../../../rtc_base:rtc_base_approved",
"../../../test:test_support",
"//third_party/abseil-cpp/absl/memory:memory",
]
}
}

View File

@ -0,0 +1,140 @@
/*
* 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 <algorithm>
#include <array>
#include <cmath>
#include <cstdlib>
#include <string>
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "modules/congestion_controller/pcc/bitrate_controller.h"
namespace webrtc {
namespace pcc {
PccBitrateController::PccBitrateController(double initial_conversion_factor,
double initial_dynamic_boundary,
double dynamic_boundary_increment,
double rtt_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power,
double rtt_gradient_threshold,
double delay_gradient_negative_bound)
: PccBitrateController(initial_conversion_factor,
initial_dynamic_boundary,
dynamic_boundary_increment,
absl::make_unique<ModifiedVivaceUtilityFunction>(
rtt_gradient_coefficient,
loss_coefficient,
throughput_coefficient,
throughput_power,
rtt_gradient_threshold,
delay_gradient_negative_bound)) {}
PccBitrateController::PccBitrateController(
double initial_conversion_factor,
double initial_dynamic_boundary,
double dynamic_boundary_increment,
std::unique_ptr<PccUtilityFunctionInterface> utility_function)
: consecutive_boundary_adjustments_number_(0),
initial_dynamic_boundary_(initial_dynamic_boundary),
dynamic_boundary_increment_(dynamic_boundary_increment),
utility_function_(std::move(utility_function)),
step_size_adjustments_number_(0),
initial_conversion_factor_(initial_conversion_factor) {}
PccBitrateController::~PccBitrateController() = default;
double PccBitrateController::ComputeStepSize(double utility_gradient) {
// Computes number of consecutive step size adjustments.
if (utility_gradient > 0) {
step_size_adjustments_number_ =
std::max<int64_t>(step_size_adjustments_number_ + 1, 1);
} else if (utility_gradient < 0) {
step_size_adjustments_number_ =
std::min<int64_t>(step_size_adjustments_number_ - 1, -1);
} else {
step_size_adjustments_number_ = 0;
}
// Computes step size amplifier.
int64_t step_size_amplifier = 1;
if (std::abs(step_size_adjustments_number_) <= 3) {
step_size_amplifier =
std::max<int64_t>(std::abs(step_size_adjustments_number_), 1);
} else {
step_size_amplifier = 2 * std::abs(step_size_adjustments_number_) - 3;
}
return step_size_amplifier * initial_conversion_factor_;
}
double PccBitrateController::ApplyDynamicBoundary(double rate_change,
double bitrate) {
double rate_change_abs = std::abs(rate_change);
int64_t rate_change_sign = (rate_change > 0) ? 1 : -1;
if (consecutive_boundary_adjustments_number_ * rate_change_sign < 0) {
consecutive_boundary_adjustments_number_ = 0;
}
double dynamic_change_boundary =
initial_dynamic_boundary_ +
std::abs(consecutive_boundary_adjustments_number_) *
dynamic_boundary_increment_;
double boundary = bitrate * dynamic_change_boundary;
if (rate_change_abs > boundary) {
consecutive_boundary_adjustments_number_ += rate_change_sign;
return boundary * rate_change_sign;
}
// Rate change smaller than boundary. Reset boundary to the smallest possible
// that would allow the change.
while (rate_change_abs <= boundary &&
consecutive_boundary_adjustments_number_ * rate_change_sign > 0) {
consecutive_boundary_adjustments_number_ -= rate_change_sign;
dynamic_change_boundary =
initial_dynamic_boundary_ +
std::abs(consecutive_boundary_adjustments_number_) *
dynamic_boundary_increment_;
boundary = bitrate * dynamic_change_boundary;
}
consecutive_boundary_adjustments_number_ += rate_change_sign;
return rate_change;
}
absl::optional<DataRate>
PccBitrateController::ComputeRateUpdateForSlowStartMode(
const PccMonitorInterval& monitor_interval) {
double utility_value = utility_function_->Compute(monitor_interval);
if (previous_utility_.has_value() && utility_value <= previous_utility_) {
return absl::nullopt;
}
previous_utility_ = utility_value;
return monitor_interval.GetTargetSendingRate();
}
DataRate PccBitrateController::ComputeRateUpdateForOnlineLearningMode(
const std::vector<PccMonitorInterval>& intervals,
DataRate bandwith_estimate) {
double first_utility = utility_function_->Compute(intervals[0]);
double second_utility = utility_function_->Compute(intervals[1]);
double first_bitrate_bps = intervals[0].GetTargetSendingRate().bps();
double second_bitrate_bps = intervals[1].GetTargetSendingRate().bps();
double gradient = (first_utility - second_utility) /
(first_bitrate_bps - second_bitrate_bps);
double rate_change_bps = gradient * ComputeStepSize(gradient); // delta_r
rate_change_bps =
ApplyDynamicBoundary(rate_change_bps, bandwith_estimate.bps());
return DataRate::bps(
std::max(0.0, bandwith_estimate.bps() + rate_change_bps));
}
} // namespace pcc
} // namespace webrtc

View File

@ -0,0 +1,72 @@
/*
* 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.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_PCC_BITRATE_CONTROLLER_H_
#define MODULES_CONGESTION_CONTROLLER_PCC_BITRATE_CONTROLLER_H_
#include <memory>
#include <vector>
#include "api/transport/network_control.h"
#include "api/transport/network_types.h"
#include "modules/congestion_controller/pcc/monitor_interval.h"
#include "modules/congestion_controller/pcc/utility_function.h"
namespace webrtc {
namespace pcc {
class PccBitrateController {
public:
PccBitrateController(double initial_conversion_factor,
double initial_dynamic_boundary,
double dynamic_boundary_increment,
double rtt_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power,
double rtt_gradient_threshold,
double delay_gradient_negative_bound);
PccBitrateController(
double initial_conversion_factor,
double initial_dynamic_boundary,
double dynamic_boundary_increment,
std::unique_ptr<PccUtilityFunctionInterface> utility_function);
absl::optional<DataRate> ComputeRateUpdateForSlowStartMode(
const PccMonitorInterval& monitor_interval);
DataRate ComputeRateUpdateForOnlineLearningMode(
const std::vector<PccMonitorInterval>& block,
DataRate bandwidth_estimate);
~PccBitrateController();
private:
double ApplyDynamicBoundary(double rate_change, double bitrate);
double ComputeStepSize(double utility_gradient);
// Dynamic boundary variables:
int64_t consecutive_boundary_adjustments_number_;
const double initial_dynamic_boundary_;
const double dynamic_boundary_increment_;
const std::unique_ptr<PccUtilityFunctionInterface> utility_function_;
// Step Size variables:
int64_t step_size_adjustments_number_;
const double initial_conversion_factor_;
absl::optional<double> previous_utility_;
};
} // namespace pcc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_PCC_BITRATE_CONTROLLER_H_

View File

@ -0,0 +1,300 @@
/*
* 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 <utility>
#include "absl/memory/memory.h"
#include "modules/congestion_controller/pcc/bitrate_controller.h"
#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::kbps(300);
const double kEpsilon = 0.05;
const Timestamp kStartTime = Timestamp::us(0);
const TimeDelta kPacketsDelta = TimeDelta::ms(1);
const TimeDelta kIntervalDuration = TimeDelta::ms(1000);
const TimeDelta kDefaultRtt = TimeDelta::ms(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_CONST_METHOD1(Compute,
double(const PccMonitorInterval& monitor_interval));
};
} // 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 =
absl::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 =
absl::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 =
absl::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 =
absl::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]),
absl::nullopt);
}
TEST(PccBitrateControllerTest, StepSizeIncrease) {
std::unique_ptr<MockUtilityFunction> mock_utility_function =
absl::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

View File

@ -0,0 +1,30 @@
/*
* 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/pcc_factory.h"
#include <memory>
#include "absl/memory/memory.h"
#include "modules/congestion_controller/pcc/pcc_network_controller.h"
namespace webrtc {
PccNetworkControllerFactory::PccNetworkControllerFactory() {}
std::unique_ptr<NetworkControllerInterface> PccNetworkControllerFactory::Create(
NetworkControllerConfig config) {
return absl::make_unique<pcc::PccNetworkController>(config);
}
TimeDelta PccNetworkControllerFactory::GetProcessInterval() const {
return TimeDelta::PlusInfinity();
}
} // namespace webrtc

View File

@ -0,0 +1,29 @@
/*
* 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.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_PCC_PCC_FACTORY_H_
#define MODULES_CONGESTION_CONTROLLER_PCC_PCC_FACTORY_H_
#include <memory>
#include "api/transport/network_control.h"
namespace webrtc {
class PccNetworkControllerFactory : public NetworkControllerFactoryInterface {
public:
PccNetworkControllerFactory();
std::unique_ptr<NetworkControllerInterface> Create(
NetworkControllerConfig config) override;
TimeDelta GetProcessInterval() const override;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_PCC_PCC_FACTORY_H_

View File

@ -0,0 +1,375 @@
/*
* 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 <algorithm>
#include "absl/memory/memory.h"
#include "modules/congestion_controller/pcc/pcc_network_controller.h"
namespace webrtc {
namespace pcc {
namespace {
constexpr int64_t kInitialRttMs = 200;
constexpr int64_t kInitialBandwidthKbps = 300;
constexpr double kMonitorIntervalDurationRatio = 1;
constexpr double kDefaultSamplingStep = 0.05;
constexpr double kTimeoutRatio = 2;
constexpr double kAlphaForRtt = 0.9;
constexpr double kSlowStartModeIncrease = 1.5;
constexpr double kAlphaForPacketInterval = 0.9;
constexpr int64_t kMinPacketsNumberPerInterval = 20;
const TimeDelta kMinDurationOfMonitorInterval = TimeDelta::ms(50);
const TimeDelta kStartupDuration = TimeDelta::ms(500);
constexpr double kMinRateChangeBps = 4000;
const DataRate kMinRateHaveMultiplicativeRateChange =
DataRate::bps(kMinRateChangeBps / kDefaultSamplingStep);
// Bitrate controller constants.
constexpr double kInitialConversionFactor = 5;
constexpr double kInitialDynamicBoundary = 0.1;
constexpr double kDynamicBoundaryIncrement = 0.1;
// Utility function parameters.
constexpr double kRttGradientCoefficientBps = 0.005;
constexpr double kLossCoefficientBps = 10;
constexpr double kThroughputCoefficient = 0.001;
constexpr double kThroughputPower = 0.9;
constexpr double kRttGradientThreshold = 0.01;
constexpr double kDelayGradientNegativeBound = 0.1;
constexpr int64_t kNumberOfPacketsToKeep = 20;
const uint64_t kRandomSeed = 100;
} // namespace
PccNetworkController::PccNetworkController(NetworkControllerConfig config)
: start_time_(Timestamp::Infinity()),
last_sent_packet_time_(Timestamp::Infinity()),
smoothed_packets_sending_interval_(TimeDelta::Zero()),
mode_(Mode::kStartup),
default_bandwidth_(DataRate::kbps(kInitialBandwidthKbps)),
bandwidth_estimate_(default_bandwidth_),
rtt_tracker_(TimeDelta::ms(kInitialRttMs), kAlphaForRtt),
monitor_interval_timeout_(TimeDelta::ms(kInitialRttMs) * kTimeoutRatio),
monitor_interval_length_strategy_(MonitorIntervalLengthStrategy::kFixed),
monitor_interval_duration_ratio_(kMonitorIntervalDurationRatio),
sampling_step_(kDefaultSamplingStep),
monitor_interval_timeout_ratio_(kTimeoutRatio),
min_packets_number_per_interval_(kMinPacketsNumberPerInterval),
bitrate_controller_(kInitialConversionFactor,
kInitialDynamicBoundary,
kDynamicBoundaryIncrement,
kRttGradientCoefficientBps,
kLossCoefficientBps,
kThroughputCoefficient,
kThroughputPower,
kRttGradientThreshold,
kDelayGradientNegativeBound),
monitor_intervals_duration_(TimeDelta::Zero()),
complete_feedback_monitor_interval_number_(0),
random_generator_(kRandomSeed) {
if (config.starting_bandwidth.IsFinite()) {
default_bandwidth_ = config.starting_bandwidth;
bandwidth_estimate_ = default_bandwidth_;
}
}
PccNetworkController::~PccNetworkController() {}
NetworkControlUpdate PccNetworkController::CreateRateUpdate(
Timestamp at_time) const {
DataRate sending_rate = DataRate::Zero();
if (monitor_intervals_.empty() ||
(monitor_intervals_.size() >= monitor_intervals_bitrates_.size() &&
at_time >= monitor_intervals_.back().GetEndTime())) {
sending_rate = bandwidth_estimate_;
} else {
sending_rate = monitor_intervals_.back().GetTargetSendingRate();
}
// Set up config when sending rate is computed.
NetworkControlUpdate update;
// Set up target rate to encoder.
TargetTransferRate target_rate_msg;
target_rate_msg.network_estimate.at_time = at_time;
target_rate_msg.network_estimate.round_trip_time = rtt_tracker_.GetRtt();
target_rate_msg.network_estimate.bandwidth = bandwidth_estimate_;
// TODO(koloskova): Add correct estimate.
target_rate_msg.network_estimate.loss_rate_ratio = 0;
target_rate_msg.network_estimate.bwe_period =
monitor_interval_duration_ratio_ * rtt_tracker_.GetRtt();
target_rate_msg.target_rate = sending_rate;
update.target_rate = target_rate_msg;
// Set up pacing/padding target rate.
PacerConfig pacer_config;
pacer_config.at_time = at_time;
pacer_config.time_window = TimeDelta::ms(1);
pacer_config.data_window = sending_rate * pacer_config.time_window;
pacer_config.pad_window = sending_rate * pacer_config.time_window;
update.pacer_config = pacer_config;
return update;
}
NetworkControlUpdate PccNetworkController::OnSentPacket(SentPacket msg) {
// Start new monitor interval if previous has finished.
// Monitor interval is initialized in OnProcessInterval function.
if (start_time_.IsInfinite()) {
start_time_ = msg.send_time;
monitor_intervals_duration_ = kStartupDuration;
monitor_intervals_bitrates_ = {bandwidth_estimate_};
monitor_intervals_.emplace_back(bandwidth_estimate_, msg.send_time,
monitor_intervals_duration_);
complete_feedback_monitor_interval_number_ = 0;
}
if (last_sent_packet_time_.IsFinite()) {
smoothed_packets_sending_interval_ =
(msg.send_time - last_sent_packet_time_) * kAlphaForPacketInterval +
(1 - kAlphaForPacketInterval) * smoothed_packets_sending_interval_;
}
last_sent_packet_time_ = msg.send_time;
if (!monitor_intervals_.empty() &&
msg.send_time >= monitor_intervals_.back().GetEndTime() &&
monitor_intervals_bitrates_.size() > monitor_intervals_.size()) {
// Start new monitor interval.
monitor_intervals_.emplace_back(
monitor_intervals_bitrates_[monitor_intervals_.size()], msg.send_time,
monitor_intervals_duration_);
}
if (IsTimeoutExpired(msg.send_time)) {
DataSize received_size = DataSize::Zero();
for (size_t i = 1; i < last_received_packets_.size(); ++i) {
received_size += last_received_packets_[i].sent_packet->size;
}
TimeDelta sending_time = TimeDelta::Zero();
if (last_received_packets_.size() > 0)
sending_time = last_received_packets_.back().receive_time -
last_received_packets_.front().receive_time;
DataRate receiving_rate = bandwidth_estimate_;
if (sending_time > TimeDelta::Zero())
receiving_rate = received_size / sending_time;
bandwidth_estimate_ =
std::min<DataRate>(bandwidth_estimate_ * 0.5, receiving_rate);
if (mode_ == Mode::kSlowStart)
mode_ = Mode::kOnlineLearning;
}
if (mode_ == Mode::kStartup &&
msg.send_time - start_time_ >= kStartupDuration) {
DataSize received_size = DataSize::Zero();
for (size_t i = 1; i < last_received_packets_.size(); ++i) {
received_size += last_received_packets_[i].sent_packet->size;
}
TimeDelta sending_time = TimeDelta::Zero();
if (last_received_packets_.size() > 0)
sending_time = last_received_packets_.back().receive_time -
last_received_packets_.front().receive_time;
DataRate receiving_rate = bandwidth_estimate_;
if (sending_time > TimeDelta::Zero())
receiving_rate = received_size / sending_time;
bandwidth_estimate_ = receiving_rate;
monitor_intervals_.clear();
mode_ = Mode::kSlowStart;
monitor_intervals_duration_ = ComputeMonitorIntervalsDuration();
monitor_intervals_bitrates_ = {bandwidth_estimate_};
monitor_intervals_.emplace_back(bandwidth_estimate_, msg.send_time,
monitor_intervals_duration_);
bandwidth_estimate_ = bandwidth_estimate_ * (1 / kSlowStartModeIncrease);
complete_feedback_monitor_interval_number_ = 0;
return CreateRateUpdate(msg.send_time);
}
if (IsFeedbackCollectionDone() || IsTimeoutExpired(msg.send_time)) {
// Creating new monitor intervals.
monitor_intervals_.clear();
monitor_interval_timeout_ =
rtt_tracker_.GetRtt() * monitor_interval_timeout_ratio_;
monitor_intervals_duration_ = ComputeMonitorIntervalsDuration();
complete_feedback_monitor_interval_number_ = 0;
// Compute bitrates and start first monitor interval.
if (mode_ == Mode::kSlowStart) {
monitor_intervals_bitrates_ = {kSlowStartModeIncrease *
bandwidth_estimate_};
monitor_intervals_.emplace_back(
kSlowStartModeIncrease * bandwidth_estimate_, msg.send_time,
monitor_intervals_duration_);
} else {
RTC_DCHECK(mode_ == Mode::kOnlineLearning || mode_ == Mode::kDoubleCheck);
monitor_intervals_.clear();
int64_t sign = 2 * (random_generator_.Rand(0, 1) % 2) - 1;
RTC_DCHECK_GE(sign, -1);
RTC_DCHECK_LE(sign, 1);
if (bandwidth_estimate_ >= kMinRateHaveMultiplicativeRateChange) {
monitor_intervals_bitrates_ = {
bandwidth_estimate_ * (1 + sign * sampling_step_),
bandwidth_estimate_ * (1 - sign * sampling_step_)};
} else {
monitor_intervals_bitrates_ = {
DataRate::bps(std::max<double>(
bandwidth_estimate_.bps() + sign * kMinRateChangeBps, 0)),
DataRate::bps(std::max<double>(
bandwidth_estimate_.bps() - sign * kMinRateChangeBps, 0))};
}
monitor_intervals_.emplace_back(monitor_intervals_bitrates_[0],
msg.send_time,
monitor_intervals_duration_);
}
}
return CreateRateUpdate(msg.send_time);
}
TimeDelta PccNetworkController::ComputeMonitorIntervalsDuration() const {
TimeDelta monitor_intervals_duration = TimeDelta::Zero();
if (monitor_interval_length_strategy_ ==
MonitorIntervalLengthStrategy::kAdaptive) {
monitor_intervals_duration = std::max(
rtt_tracker_.GetRtt() * monitor_interval_duration_ratio_,
smoothed_packets_sending_interval_ * min_packets_number_per_interval_);
} else {
RTC_DCHECK(monitor_interval_length_strategy_ ==
MonitorIntervalLengthStrategy::kFixed);
monitor_intervals_duration =
smoothed_packets_sending_interval_ * min_packets_number_per_interval_;
}
monitor_intervals_duration =
std::max(kMinDurationOfMonitorInterval, monitor_intervals_duration);
return monitor_intervals_duration;
}
bool PccNetworkController::IsTimeoutExpired(Timestamp current_time) const {
if (complete_feedback_monitor_interval_number_ >= monitor_intervals_.size()) {
return false;
}
return current_time -
monitor_intervals_[complete_feedback_monitor_interval_number_]
.GetEndTime() >=
monitor_interval_timeout_;
}
bool PccNetworkController::IsFeedbackCollectionDone() const {
return complete_feedback_monitor_interval_number_ >=
monitor_intervals_bitrates_.size();
}
NetworkControlUpdate PccNetworkController::OnTransportPacketsFeedback(
TransportPacketsFeedback msg) {
// Save packets to last_received_packets_ array.
for (const PacketResult& packet_result : msg.ReceivedWithSendInfo()) {
last_received_packets_.push_back(packet_result);
}
while (last_received_packets_.size() > kNumberOfPacketsToKeep) {
last_received_packets_.pop_front();
}
rtt_tracker_.OnPacketsFeedback(msg.PacketsWithFeedback(), msg.feedback_time);
// Skip rate update in case when online learning mode just started, but
// corresponding monitor intervals were not started yet.
if (mode_ == Mode::kOnlineLearning &&
monitor_intervals_bitrates_.size() < 2) {
return NetworkControlUpdate();
}
if (!IsFeedbackCollectionDone() && !monitor_intervals_.empty()) {
while (complete_feedback_monitor_interval_number_ <
monitor_intervals_.size()) {
monitor_intervals_[complete_feedback_monitor_interval_number_]
.OnPacketsFeedback(msg.PacketsWithFeedback());
if (!monitor_intervals_[complete_feedback_monitor_interval_number_]
.IsFeedbackCollectionDone())
break;
++complete_feedback_monitor_interval_number_;
}
}
if (IsFeedbackCollectionDone()) {
if (mode_ == Mode::kDoubleCheck) {
mode_ = Mode::kOnlineLearning;
} else if (NeedDoubleCheckMeasurments()) {
mode_ = Mode::kDoubleCheck;
}
if (mode_ != Mode::kDoubleCheck)
UpdateSendingRateAndMode();
}
return NetworkControlUpdate();
}
bool PccNetworkController::NeedDoubleCheckMeasurments() const {
if (mode_ == Mode::kSlowStart) {
return false;
}
double first_loss_rate = monitor_intervals_[0].GetLossRate();
double second_loss_rate = monitor_intervals_[1].GetLossRate();
DataRate first_bitrate = monitor_intervals_[0].GetTargetSendingRate();
DataRate second_bitrate = monitor_intervals_[1].GetTargetSendingRate();
if ((first_bitrate.bps() - second_bitrate.bps()) *
(first_loss_rate - second_loss_rate) <
0) {
return true;
}
return false;
}
void PccNetworkController::UpdateSendingRateAndMode() {
if (monitor_intervals_.empty() || !IsFeedbackCollectionDone()) {
return;
}
if (mode_ == Mode::kSlowStart) {
DataRate old_bandwidth_estimate = bandwidth_estimate_;
bandwidth_estimate_ =
bitrate_controller_
.ComputeRateUpdateForSlowStartMode(monitor_intervals_[0])
.value_or(bandwidth_estimate_);
if (bandwidth_estimate_ <= old_bandwidth_estimate)
mode_ = Mode::kOnlineLearning;
} else {
RTC_DCHECK(mode_ == Mode::kOnlineLearning);
bandwidth_estimate_ =
bitrate_controller_.ComputeRateUpdateForOnlineLearningMode(
monitor_intervals_, bandwidth_estimate_);
}
}
NetworkControlUpdate PccNetworkController::OnNetworkAvailability(
NetworkAvailability msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnNetworkRouteChange(
NetworkRouteChange msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnProcessInterval(
ProcessInterval msg) {
return CreateRateUpdate(msg.at_time);
}
NetworkControlUpdate PccNetworkController::OnTargetRateConstraints(
TargetRateConstraints msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnRemoteBitrateReport(
RemoteBitrateReport) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnRoundTripTimeUpdate(
RoundTripTimeUpdate) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnTransportLossReport(
TransportLossReport) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnStreamsConfig(StreamsConfig) {
return NetworkControlUpdate();
}
} // namespace pcc
} // namespace webrtc

View File

@ -0,0 +1,116 @@
/*
* 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.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_PCC_PCC_NETWORK_CONTROLLER_H_
#define MODULES_CONGESTION_CONTROLLER_PCC_PCC_NETWORK_CONTROLLER_H_
#include <deque>
#include <vector>
#include "api/transport/network_control.h"
#include "api/transport/network_types.h"
#include "modules/congestion_controller/pcc/bitrate_controller.h"
#include "modules/congestion_controller/pcc/monitor_interval.h"
#include "modules/congestion_controller/pcc/rtt_tracker.h"
#include "rtc_base/random.h"
namespace webrtc {
namespace pcc {
// PCC (Performance-oriented Congestion Control) Vivace is a congestion
// control algorithm based on online (convex) optimization in machine learning.
// It divides time into consecutive Monitor Intervals (MI) to test sending
// rates r(1 + eps), r(1 - eps) for the current sending rate r.
// At the end of each MI it computes utility function to transform the
// performance statistics into a numerical value. Then it updates current
// sending rate using gradient ascent to maximize utility function.
class PccNetworkController : public NetworkControllerInterface {
public:
enum class Mode {
kStartup,
// Slow start phase of PCC doubles sending rate each monitor interval.
kSlowStart,
// After getting the first decrease in utility function PCC exits slow start
// and enters the online learning phase.
kOnlineLearning,
// If we got that sending with the lower rate resulted in higher packet
// loss, then the measurements are unreliable and we need to double check
// them.
kDoubleCheck
};
enum class MonitorIntervalLengthStrategy {
// Monitor interval length adaptive when it is proportional to packets RTT.
kAdaptive,
// Monitor interval length is fixed when it is equal to the time of sending
// predefined amount of packets (kMinPacketsNumberPerInterval).
kFixed
};
explicit PccNetworkController(NetworkControllerConfig config);
~PccNetworkController() override;
// NetworkControllerInterface
NetworkControlUpdate OnNetworkAvailability(NetworkAvailability msg) override;
NetworkControlUpdate OnNetworkRouteChange(NetworkRouteChange msg) override;
NetworkControlUpdate OnProcessInterval(ProcessInterval msg) override;
NetworkControlUpdate OnSentPacket(SentPacket msg) override;
NetworkControlUpdate OnTargetRateConstraints(
TargetRateConstraints msg) override;
NetworkControlUpdate OnTransportPacketsFeedback(
TransportPacketsFeedback msg) override;
// Part of remote bitrate estimation api, not implemented for PCC
NetworkControlUpdate OnStreamsConfig(StreamsConfig msg) override;
NetworkControlUpdate OnRemoteBitrateReport(RemoteBitrateReport msg) override;
NetworkControlUpdate OnRoundTripTimeUpdate(RoundTripTimeUpdate msg) override;
NetworkControlUpdate OnTransportLossReport(TransportLossReport msg) override;
private:
void UpdateSendingRateAndMode();
NetworkControlUpdate CreateRateUpdate(Timestamp at_time) const;
TimeDelta ComputeMonitorIntervalsDuration() const;
bool NeedDoubleCheckMeasurments() const;
bool IsTimeoutExpired(Timestamp current_time) const;
bool IsFeedbackCollectionDone() const;
Timestamp start_time_;
Timestamp last_sent_packet_time_;
TimeDelta smoothed_packets_sending_interval_;
Mode mode_;
// Default value used for initializing bandwidth.
DataRate default_bandwidth_;
// Current estimate r.
DataRate bandwidth_estimate_;
RttTracker rtt_tracker_;
TimeDelta monitor_interval_timeout_;
const MonitorIntervalLengthStrategy monitor_interval_length_strategy_;
const double monitor_interval_duration_ratio_;
const double sampling_step_; // Epsilon.
const double monitor_interval_timeout_ratio_;
const int64_t min_packets_number_per_interval_;
PccBitrateController bitrate_controller_;
std::vector<PccMonitorInterval> monitor_intervals_;
std::vector<DataRate> monitor_intervals_bitrates_;
TimeDelta monitor_intervals_duration_;
size_t complete_feedback_monitor_interval_number_;
webrtc::Random random_generator_;
std::deque<PacketResult> last_received_packets_;
};
} // namespace pcc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_PCC_PCC_NETWORK_CONTROLLER_H_

View File

@ -0,0 +1,115 @@
/*
* 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 <memory>
#include "api/transport/test/network_control_tester.h"
#include "modules/congestion_controller/pcc/pcc_factory.h"
#include "modules/congestion_controller/pcc/pcc_network_controller.h"
#include "test/gmock.h"
#include "test/gtest.h"
using testing::Field;
using testing::Matcher;
using testing::AllOf;
using testing::Ge;
using testing::Le;
using testing::Property;
namespace webrtc {
namespace pcc {
namespace test {
namespace {
const DataRate kInitialBitrate = DataRate::kbps(60);
const Timestamp kDefaultStartTime = Timestamp::ms(10000000);
constexpr double kDataRateMargin = 0.20;
constexpr double kMinDataRateFactor = 1 - kDataRateMargin;
constexpr double kMaxDataRateFactor = 1 + kDataRateMargin;
inline Matcher<TargetTransferRate> TargetRateCloseTo(DataRate rate) {
DataRate min_data_rate = rate * kMinDataRateFactor;
DataRate max_data_rate = rate * kMaxDataRateFactor;
return Field(&TargetTransferRate::target_rate,
AllOf(Ge(min_data_rate), Le(max_data_rate)));
}
NetworkControllerConfig InitialConfig(
int starting_bandwidth_kbps = kInitialBitrate.kbps(),
int min_data_rate_kbps = 0,
int max_data_rate_kbps = 5 * kInitialBitrate.kbps()) {
NetworkControllerConfig config;
config.constraints.at_time = kDefaultStartTime;
config.constraints.min_data_rate = DataRate::kbps(min_data_rate_kbps);
config.constraints.max_data_rate = DataRate::kbps(max_data_rate_kbps);
config.starting_bandwidth = DataRate::kbps(starting_bandwidth_kbps);
return config;
}
ProcessInterval InitialProcessInterval() {
ProcessInterval process_interval;
process_interval.at_time = kDefaultStartTime;
return process_interval;
}
} // namespace
TEST(PccNetworkControllerTest, SendsConfigurationOnFirstProcess) {
std::unique_ptr<NetworkControllerInterface> controller_;
controller_.reset(new PccNetworkController(InitialConfig()));
NetworkControlUpdate update =
controller_->OnProcessInterval(InitialProcessInterval());
EXPECT_THAT(*update.target_rate, TargetRateCloseTo(kInitialBitrate));
EXPECT_THAT(*update.pacer_config,
Property(&PacerConfig::data_rate, Ge(kInitialBitrate)));
}
TEST(PccNetworkControllerTest, UpdatesTargetSendRate) {
PccNetworkControllerFactory factory;
webrtc::test::NetworkControllerTester tester(&factory,
InitialConfig(60, 0, 600));
auto packet_producer = &webrtc::test::SimpleTargetRateProducer::ProduceNext;
tester.RunSimulation(TimeDelta::seconds(10), TimeDelta::ms(10),
DataRate::kbps(300), TimeDelta::ms(100),
packet_producer);
EXPECT_GE(tester.GetState().target_rate->target_rate.kbps(),
300 * kMinDataRateFactor);
EXPECT_LE(tester.GetState().target_rate->target_rate.kbps(),
300 * kMaxDataRateFactor);
tester.RunSimulation(TimeDelta::seconds(30), TimeDelta::ms(10),
DataRate::kbps(500), TimeDelta::ms(100),
packet_producer);
EXPECT_GE(tester.GetState().target_rate->target_rate.kbps(),
500 * kMinDataRateFactor);
EXPECT_LE(tester.GetState().target_rate->target_rate.kbps(),
500 * kMaxDataRateFactor);
tester.RunSimulation(TimeDelta::seconds(2), TimeDelta::ms(10),
DataRate::kbps(200), TimeDelta::ms(200),
packet_producer);
EXPECT_LE(tester.GetState().target_rate->target_rate.kbps(),
200 * kMaxDataRateFactor);
tester.RunSimulation(TimeDelta::seconds(18), TimeDelta::ms(10),
DataRate::kbps(200), TimeDelta::ms(200),
packet_producer);
EXPECT_GE(tester.GetState().target_rate->target_rate.kbps(),
200 * kMinDataRateFactor);
EXPECT_LE(tester.GetState().target_rate->target_rate.kbps(),
200 * kMaxDataRateFactor);
}
} // namespace test
} // namespace pcc
} // namespace webrtc