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:
committed by
Commit Bot
parent
801500cf99
commit
ea9249ed15
@ -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",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
140
modules/congestion_controller/pcc/bitrate_controller.cc
Normal file
140
modules/congestion_controller/pcc/bitrate_controller.cc
Normal 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
|
||||
72
modules/congestion_controller/pcc/bitrate_controller.h
Normal file
72
modules/congestion_controller/pcc/bitrate_controller.h
Normal 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_
|
||||
300
modules/congestion_controller/pcc/bitrate_controller_unittest.cc
Normal file
300
modules/congestion_controller/pcc/bitrate_controller_unittest.cc
Normal 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
|
||||
30
modules/congestion_controller/pcc/pcc_factory.cc
Normal file
30
modules/congestion_controller/pcc/pcc_factory.cc
Normal 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
|
||||
29
modules/congestion_controller/pcc/pcc_factory.h
Normal file
29
modules/congestion_controller/pcc/pcc_factory.h
Normal 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_
|
||||
375
modules/congestion_controller/pcc/pcc_network_controller.cc
Normal file
375
modules/congestion_controller/pcc/pcc_network_controller.cc
Normal 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
|
||||
116
modules/congestion_controller/pcc/pcc_network_controller.h
Normal file
116
modules/congestion_controller/pcc/pcc_network_controller.h
Normal 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_
|
||||
@ -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
|
||||
Reference in New Issue
Block a user