Reland "Moved congestion controller to goog_cc folder."

This is a reland of e6cefdf9c572cdce55ff0497ad6e516c76132ee8.

Original change's description:
> Moved congestion controller to goog_cc folder.
> 
> Bug: webrtc:8415
> Change-Id: I2070da0cacf1dbfc4b6a89285af3e68fd03497ab
> Reviewed-on: https://webrtc-review.googlesource.com/43841
> Commit-Queue: Sebastian Jansson <srte@webrtc.org>
> Reviewed-by: Björn Terelius <terelius@webrtc.org>
> Reviewed-by: Stefan Holmer <stefan@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#21928}

Bug: webrtc:8415
Change-Id: Ib5cf8641466655d64ac80f720561817f4cab49a9
Reviewed-on: https://webrtc-review.googlesource.com/53062
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22244}
This commit is contained in:
Sebastian Jansson
2018-02-28 16:48:00 +01:00
committed by Commit Bot
parent cdff887238
commit fc7ec8e9f8
34 changed files with 239 additions and 219 deletions

View File

@ -0,0 +1,164 @@
# 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.
import("../../../webrtc.gni")
config("bwe_test_logging") {
if (rtc_enable_bwe_test_logging) {
defines = [ "BWE_TEST_LOGGING_COMPILE_TIME_ENABLE=1" ]
} else {
defines = [ "BWE_TEST_LOGGING_COMPILE_TIME_ENABLE=0" ]
}
}
rtc_static_library("goog_cc") {
configs += [ ":bwe_test_logging" ]
sources = [
"alr_detector.cc",
"alr_detector.h",
"goog_cc_factory.cc",
"goog_cc_network_control.cc",
"goog_cc_network_control.h",
"include/goog_cc_factory.h",
"probe_controller.cc",
"probe_controller.h",
]
# TODO(jschuh): Bug 1348: fix this warning.
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
if (!build_with_chromium && is_clang) {
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
}
deps = [
":delay_based_bwe",
":estimators",
"../..:module_api",
"../../..:webrtc_common",
"../../../:typedefs",
"../../../api:optional",
"../../../logging:rtc_event_log_api",
"../../../logging:rtc_event_pacing",
"../../../rtc_base:checks",
"../../../rtc_base:rtc_base_approved",
"../../../rtc_base/experiments:alr_experiment",
"../../../system_wrappers",
"../../../system_wrappers:field_trial_api",
"../../../system_wrappers:metrics_api",
"../../bitrate_controller",
"../../pacing",
"../../remote_bitrate_estimator",
"../../rtp_rtcp:rtp_rtcp_format",
"../network_control",
]
}
rtc_source_set("estimators") {
configs += [ ":bwe_test_logging" ]
sources = [
"acknowledged_bitrate_estimator.cc",
"acknowledged_bitrate_estimator.h",
"bitrate_estimator.cc",
"bitrate_estimator.h",
"delay_increase_detector_interface.h",
"median_slope_estimator.cc",
"median_slope_estimator.h",
"probe_bitrate_estimator.cc",
"probe_bitrate_estimator.h",
"trendline_estimator.cc",
"trendline_estimator.h",
]
# TODO(jschuh): Bug 1348: fix this warning.
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
if (!build_with_chromium && is_clang) {
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
}
deps = [
"../../../api:optional",
"../../../logging:rtc_event_bwe",
"../../../logging:rtc_event_log_api",
"../../../rtc_base:checks",
"../../../rtc_base:rtc_base_approved",
"../../../rtc_base:rtc_numerics",
"../../../system_wrappers:field_trial_api",
"../../../system_wrappers:metrics_api",
"../../remote_bitrate_estimator",
"../../rtp_rtcp:rtp_rtcp_format",
]
}
rtc_source_set("delay_based_bwe") {
configs += [ ":bwe_test_logging" ]
sources = [
"delay_based_bwe.cc",
"delay_based_bwe.h",
]
deps = [
":estimators",
"../../../:typedefs",
"../../../logging:rtc_event_bwe",
"../../../logging:rtc_event_log_api",
"../../../rtc_base:checks",
"../../../rtc_base:rtc_base_approved",
"../../../system_wrappers:field_trial_api",
"../../../system_wrappers:metrics_api",
"../../pacing",
"../../remote_bitrate_estimator",
]
if (!build_with_chromium && is_clang) {
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
}
}
if (rtc_include_tests) {
rtc_source_set("goog_cc_unittests") {
testonly = true
sources = [
"acknowledged_bitrate_estimator_unittest.cc",
"alr_detector_unittest.cc",
"delay_based_bwe_unittest.cc",
"delay_based_bwe_unittest_helper.cc",
"delay_based_bwe_unittest_helper.h",
"median_slope_estimator_unittest.cc",
"probe_bitrate_estimator_unittest.cc",
"probe_controller_unittest.cc",
"trendline_estimator_unittest.cc",
]
deps = [
":delay_based_bwe",
":estimators",
":goog_cc",
"../../../rtc_base:checks",
"../../../rtc_base:rtc_base_approved",
"../../../rtc_base:rtc_base_tests_utils",
"../../../rtc_base/experiments:alr_experiment",
"../../../system_wrappers",
"../../../test:field_trial",
"../../../test:test_support",
"../../pacing",
"../../remote_bitrate_estimator",
"../../rtp_rtcp:rtp_rtcp_format",
"../network_control",
"//testing/gmock",
]
if (!build_with_chromium && is_clang) {
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
}
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2017 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/goog_cc/acknowledged_bitrate_estimator.h"
#include <utility>
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/ptr_util.h"
namespace webrtc {
namespace webrtc_cc {
namespace {
bool IsInSendTimeHistory(const PacketFeedback& packet) {
return packet.send_time_ms != PacketFeedback::kNoSendTime;
}
} // namespace
AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator()
: AcknowledgedBitrateEstimator(rtc::MakeUnique<BitrateEstimator>()) {}
AcknowledgedBitrateEstimator::~AcknowledgedBitrateEstimator() {}
AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator(
std::unique_ptr<BitrateEstimator> bitrate_estimator)
: bitrate_estimator_(std::move(bitrate_estimator)) {}
void AcknowledgedBitrateEstimator::IncomingPacketFeedbackVector(
const std::vector<PacketFeedback>& packet_feedback_vector) {
RTC_DCHECK(std::is_sorted(packet_feedback_vector.begin(),
packet_feedback_vector.end(),
PacketFeedbackComparator()));
for (const auto& packet : packet_feedback_vector) {
if (IsInSendTimeHistory(packet)) {
MaybeExpectFastRateChange(packet.send_time_ms);
bitrate_estimator_->Update(packet.arrival_time_ms,
rtc::dchecked_cast<int>(packet.payload_size));
}
}
}
rtc::Optional<uint32_t> AcknowledgedBitrateEstimator::bitrate_bps() const {
return bitrate_estimator_->bitrate_bps();
}
void AcknowledgedBitrateEstimator::SetAlrEndedTimeMs(
int64_t alr_ended_time_ms) {
alr_ended_time_ms_.emplace(alr_ended_time_ms);
}
void AcknowledgedBitrateEstimator::MaybeExpectFastRateChange(
int64_t packet_send_time_ms) {
if (alr_ended_time_ms_ && packet_send_time_ms > *alr_ended_time_ms_) {
bitrate_estimator_->ExpectFastRateChange();
alr_ended_time_ms_.reset();
}
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2017 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_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
#include <memory>
#include <vector>
#include "api/optional.h"
#include "modules/congestion_controller/goog_cc/bitrate_estimator.h"
namespace webrtc {
struct PacketFeedback;
namespace webrtc_cc {
class AcknowledgedBitrateEstimator {
public:
explicit AcknowledgedBitrateEstimator(
std::unique_ptr<BitrateEstimator> bitrate_estimator);
AcknowledgedBitrateEstimator();
~AcknowledgedBitrateEstimator();
void IncomingPacketFeedbackVector(
const std::vector<PacketFeedback>& packet_feedback_vector);
rtc::Optional<uint32_t> bitrate_bps() const;
void SetAlrEndedTimeMs(int64_t alr_ended_time_ms);
private:
void MaybeExpectFastRateChange(int64_t packet_arrival_time_ms);
rtc::Optional<int64_t> alr_ended_time_ms_;
std::unique_ptr<BitrateEstimator> bitrate_estimator_;
};
} // namespace webrtc_cc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_

View File

@ -0,0 +1,136 @@
/*
* Copyright (c) 2017 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/goog_cc/acknowledged_bitrate_estimator.h"
#include <utility>
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "rtc_base/fakeclock.h"
#include "rtc_base/ptr_util.h"
#include "test/gmock.h"
#include "test/gtest.h"
using testing::_;
using testing::NiceMock;
using testing::InSequence;
using testing::Return;
namespace webrtc {
namespace webrtc_cc {
namespace {
constexpr int64_t kFirstArrivalTimeMs = 10;
constexpr int64_t kFirstSendTimeMs = 10;
constexpr uint16_t kSequenceNumber = 1;
constexpr size_t kPayloadSize = 10;
class MockBitrateEstimator : public BitrateEstimator {
public:
MOCK_METHOD2(Update, void(int64_t now_ms, int bytes));
MOCK_CONST_METHOD0(bitrate_bps, rtc::Optional<uint32_t>());
MOCK_METHOD0(ExpectFastRateChange, void());
};
struct AcknowledgedBitrateEstimatorTestStates {
std::unique_ptr<AcknowledgedBitrateEstimator> acknowledged_bitrate_estimator;
MockBitrateEstimator* mock_bitrate_estimator;
};
AcknowledgedBitrateEstimatorTestStates CreateTestStates() {
AcknowledgedBitrateEstimatorTestStates states;
auto mock_bitrate_estimator = rtc::MakeUnique<MockBitrateEstimator>();
states.mock_bitrate_estimator = mock_bitrate_estimator.get();
states.acknowledged_bitrate_estimator =
rtc::MakeUnique<AcknowledgedBitrateEstimator>(
std::move(mock_bitrate_estimator));
return states;
}
std::vector<PacketFeedback> CreateFeedbackVector() {
std::vector<PacketFeedback> packet_feedback_vector;
const PacedPacketInfo pacing_info;
packet_feedback_vector.push_back(
PacketFeedback(kFirstArrivalTimeMs, kFirstSendTimeMs, kSequenceNumber,
kPayloadSize, pacing_info));
packet_feedback_vector.push_back(
PacketFeedback(kFirstArrivalTimeMs + 10, kFirstSendTimeMs + 10,
kSequenceNumber, kPayloadSize + 10, pacing_info));
return packet_feedback_vector;
}
} // anonymous namespace
TEST(TestAcknowledgedBitrateEstimator, DontAddPacketsWhichAreNotInSendHistory) {
auto states = CreateTestStates();
std::vector<PacketFeedback> packet_feedback_vector;
packet_feedback_vector.push_back(
PacketFeedback(kFirstArrivalTimeMs, kSequenceNumber));
EXPECT_CALL(*states.mock_bitrate_estimator, Update(_, _)).Times(0);
states.acknowledged_bitrate_estimator->IncomingPacketFeedbackVector(
packet_feedback_vector);
}
TEST(TestAcknowledgedBitrateEstimator, UpdateBandwith) {
auto states = CreateTestStates();
auto packet_feedback_vector = CreateFeedbackVector();
{
InSequence dummy;
EXPECT_CALL(
*states.mock_bitrate_estimator,
Update(packet_feedback_vector[0].arrival_time_ms,
static_cast<int>(packet_feedback_vector[0].payload_size)))
.Times(1);
EXPECT_CALL(
*states.mock_bitrate_estimator,
Update(packet_feedback_vector[1].arrival_time_ms,
static_cast<int>(packet_feedback_vector[1].payload_size)))
.Times(1);
}
states.acknowledged_bitrate_estimator->IncomingPacketFeedbackVector(
packet_feedback_vector);
}
TEST(TestAcknowledgedBitrateEstimator, ExpectFastRateChangeWhenLeftAlr) {
auto states = CreateTestStates();
auto packet_feedback_vector = CreateFeedbackVector();
{
InSequence dummy;
EXPECT_CALL(
*states.mock_bitrate_estimator,
Update(packet_feedback_vector[0].arrival_time_ms,
static_cast<int>(packet_feedback_vector[0].payload_size)))
.Times(1);
EXPECT_CALL(*states.mock_bitrate_estimator, ExpectFastRateChange())
.Times(1);
EXPECT_CALL(
*states.mock_bitrate_estimator,
Update(packet_feedback_vector[1].arrival_time_ms,
static_cast<int>(packet_feedback_vector[1].payload_size)))
.Times(1);
}
states.acknowledged_bitrate_estimator->SetAlrEndedTimeMs(kFirstArrivalTimeMs +
1);
states.acknowledged_bitrate_estimator->IncomingPacketFeedbackVector(
packet_feedback_vector);
}
TEST(TestAcknowledgedBitrateEstimator, ReturnBitrate) {
auto states = CreateTestStates();
rtc::Optional<uint32_t> return_value(42);
EXPECT_CALL(*states.mock_bitrate_estimator, bitrate_bps())
.Times(1)
.WillOnce(Return(return_value));
EXPECT_EQ(return_value, states.acknowledged_bitrate_estimator->bitrate_bps());
}
} // namespace webrtc_cc
} // namespace webrtc*/

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2016 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/goog_cc/alr_detector.h"
#include <algorithm>
#include <cstdio>
#include <string>
#include "logging/rtc_event_log/events/rtc_event_alr_state.h"
#include "logging/rtc_event_log/rtc_event_log.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/alr_experiment.h"
#include "rtc_base/format_macros.h"
#include "rtc_base/logging.h"
#include "rtc_base/ptr_util.h"
#include "rtc_base/timeutils.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace webrtc_cc {
AlrDetector::AlrDetector() : AlrDetector(nullptr) {}
AlrDetector::AlrDetector(RtcEventLog* event_log)
: bandwidth_usage_percent_(kDefaultAlrBandwidthUsagePercent),
alr_start_budget_level_percent_(kDefaultAlrStartBudgetLevelPercent),
alr_stop_budget_level_percent_(kDefaultAlrStopBudgetLevelPercent),
alr_budget_(0, true),
event_log_(event_log) {
RTC_CHECK(AlrExperimentSettings::MaxOneFieldTrialEnabled());
rtc::Optional<AlrExperimentSettings> experiment_settings =
AlrExperimentSettings::CreateFromFieldTrial(
AlrExperimentSettings::kScreenshareProbingBweExperimentName);
if (!experiment_settings) {
experiment_settings = AlrExperimentSettings::CreateFromFieldTrial(
AlrExperimentSettings::kStrictPacingAndProbingExperimentName);
}
if (experiment_settings) {
alr_stop_budget_level_percent_ =
experiment_settings->alr_stop_budget_level_percent;
alr_start_budget_level_percent_ =
experiment_settings->alr_start_budget_level_percent;
bandwidth_usage_percent_ = experiment_settings->alr_bandwidth_usage_percent;
}
}
AlrDetector::~AlrDetector() {}
void AlrDetector::OnBytesSent(size_t bytes_sent, int64_t send_time_ms) {
if (!last_send_time_ms_.has_value()) {
last_send_time_ms_ = send_time_ms;
// Since the duration for sending the bytes is unknwon, return without
// updating alr state.
return;
}
int64_t delta_time_ms = send_time_ms - *last_send_time_ms_;
last_send_time_ms_ = send_time_ms;
alr_budget_.UseBudget(bytes_sent);
alr_budget_.IncreaseBudget(delta_time_ms);
bool state_changed = false;
if (alr_budget_.budget_level_percent() > alr_start_budget_level_percent_ &&
!alr_started_time_ms_) {
alr_started_time_ms_.emplace(rtc::TimeMillis());
state_changed = true;
} else if (alr_budget_.budget_level_percent() <
alr_stop_budget_level_percent_ &&
alr_started_time_ms_) {
state_changed = true;
alr_started_time_ms_.reset();
}
if (event_log_ && state_changed) {
event_log_->Log(
rtc::MakeUnique<RtcEventAlrState>(alr_started_time_ms_.has_value()));
}
}
void AlrDetector::SetEstimatedBitrate(int bitrate_bps) {
RTC_DCHECK(bitrate_bps);
const auto target_rate_kbps = static_cast<int64_t>(bitrate_bps) *
bandwidth_usage_percent_ / (1000 * 100);
alr_budget_.set_target_rate_kbps(rtc::dchecked_cast<int>(target_rate_kbps));
}
rtc::Optional<int64_t> AlrDetector::GetApplicationLimitedRegionStartTime()
const {
return alr_started_time_ms_;
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2016 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_GOOG_CC_ALR_DETECTOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_
#include "api/optional.h"
#include "common_types.h" // NOLINT(build/include)
#include "modules/pacing/interval_budget.h"
#include "modules/pacing/paced_sender.h"
#include "rtc_base/rate_statistics.h"
#include "typedefs.h" // NOLINT(build/include)
namespace webrtc {
class RtcEventLog;
namespace webrtc_cc {
// Application limited region detector is a class that utilizes signals of
// elapsed time and bytes sent to estimate whether network traffic is
// currently limited by the application's ability to generate traffic.
//
// AlrDetector provides a signal that can be utilized to adjust
// estimate bandwidth.
// Note: This class is not thread-safe.
class AlrDetector {
public:
AlrDetector();
explicit AlrDetector(RtcEventLog* event_log);
~AlrDetector();
void OnBytesSent(size_t bytes_sent, int64_t send_time_ms);
// Set current estimated bandwidth.
void SetEstimatedBitrate(int bitrate_bps);
// Returns time in milliseconds when the current application-limited region
// started or empty result if the sender is currently not application-limited.
rtc::Optional<int64_t> GetApplicationLimitedRegionStartTime() const;
// Sent traffic percentage as a function of network capacity used to determine
// application-limited region. ALR region start when bandwidth usage drops
// below kAlrStartUsagePercent and ends when it raises above
// kAlrEndUsagePercent. NOTE: This is intentionally conservative at the moment
// until BW adjustments of application limited region is fine tuned.
static constexpr int kDefaultAlrBandwidthUsagePercent = 65;
static constexpr int kDefaultAlrStartBudgetLevelPercent = 80;
static constexpr int kDefaultAlrStopBudgetLevelPercent = 50;
void UpdateBudgetWithElapsedTime(int64_t delta_time_ms);
void UpdateBudgetWithBytesSent(size_t bytes_sent);
private:
int bandwidth_usage_percent_;
int alr_start_budget_level_percent_;
int alr_stop_budget_level_percent_;
rtc::Optional<int64_t> last_send_time_ms_;
IntervalBudget alr_budget_;
rtc::Optional<int64_t> alr_started_time_ms_;
RtcEventLog* event_log_;
};
} // namespace webrtc_cc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_

View File

@ -0,0 +1,177 @@
/*
* Copyright (c) 2016 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/goog_cc/alr_detector.h"
#include "rtc_base/experiments/alr_experiment.h"
#include "test/field_trial.h"
#include "test/gtest.h"
namespace {
constexpr int kEstimatedBitrateBps = 300000;
} // namespace
namespace webrtc {
namespace webrtc_cc {
namespace {
class SimulateOutgoingTrafficIn {
public:
explicit SimulateOutgoingTrafficIn(AlrDetector* alr_detector,
int64_t* timestamp_ms)
: alr_detector_(alr_detector), timestamp_ms_(timestamp_ms) {
RTC_CHECK(alr_detector_);
}
SimulateOutgoingTrafficIn& ForTimeMs(int time_ms) {
interval_ms_ = time_ms;
ProduceTraffic();
return *this;
}
SimulateOutgoingTrafficIn& AtPercentOfEstimatedBitrate(int usage_percentage) {
usage_percentage_.emplace(usage_percentage);
ProduceTraffic();
return *this;
}
private:
void ProduceTraffic() {
if (!interval_ms_ || !usage_percentage_)
return;
const int kTimeStepMs = 10;
for (int t = 0; t < *interval_ms_; t += kTimeStepMs) {
*timestamp_ms_ += kTimeStepMs;
alr_detector_->OnBytesSent(kEstimatedBitrateBps * *usage_percentage_ *
kTimeStepMs / (8 * 100 * 1000),
*timestamp_ms_);
}
int remainder_ms = *interval_ms_ % kTimeStepMs;
if (remainder_ms > 0) {
*timestamp_ms_ += kTimeStepMs;
alr_detector_->OnBytesSent(kEstimatedBitrateBps * *usage_percentage_ *
remainder_ms / (8 * 100 * 1000),
*timestamp_ms_);
}
}
AlrDetector* const alr_detector_;
int64_t* timestamp_ms_;
rtc::Optional<int> interval_ms_;
rtc::Optional<int> usage_percentage_;
};
} // namespace
class AlrDetectorTest : public testing::Test {
public:
void SetUp() override {
alr_detector_.SetEstimatedBitrate(kEstimatedBitrateBps);
}
protected:
AlrDetector alr_detector_;
int64_t timestamp_ms_ = 1000;
};
TEST_F(AlrDetectorTest, AlrDetection) {
// Start in non-ALR state.
EXPECT_FALSE(alr_detector_.GetApplicationLimitedRegionStartTime());
// Stay in non-ALR state when usage is close to 100%.
SimulateOutgoingTrafficIn(&alr_detector_, &timestamp_ms_)
.ForTimeMs(1000)
.AtPercentOfEstimatedBitrate(90);
EXPECT_FALSE(alr_detector_.GetApplicationLimitedRegionStartTime());
// Verify that we ALR starts when bitrate drops below 20%.
SimulateOutgoingTrafficIn(&alr_detector_, &timestamp_ms_)
.ForTimeMs(1500)
.AtPercentOfEstimatedBitrate(20);
EXPECT_TRUE(alr_detector_.GetApplicationLimitedRegionStartTime());
// Verify that ALR ends when usage is above 65%.
SimulateOutgoingTrafficIn(&alr_detector_, &timestamp_ms_)
.ForTimeMs(4000)
.AtPercentOfEstimatedBitrate(100);
EXPECT_FALSE(alr_detector_.GetApplicationLimitedRegionStartTime());
}
TEST_F(AlrDetectorTest, ShortSpike) {
// Start in non-ALR state.
EXPECT_FALSE(alr_detector_.GetApplicationLimitedRegionStartTime());
// Verify that we ALR starts when bitrate drops below 20%.
SimulateOutgoingTrafficIn(&alr_detector_, &timestamp_ms_)
.ForTimeMs(1000)
.AtPercentOfEstimatedBitrate(20);
EXPECT_TRUE(alr_detector_.GetApplicationLimitedRegionStartTime());
// Verify that we stay in ALR region even after a short bitrate spike.
SimulateOutgoingTrafficIn(&alr_detector_, &timestamp_ms_)
.ForTimeMs(100)
.AtPercentOfEstimatedBitrate(150);
EXPECT_TRUE(alr_detector_.GetApplicationLimitedRegionStartTime());
// ALR ends when usage is above 65%.
SimulateOutgoingTrafficIn(&alr_detector_, &timestamp_ms_)
.ForTimeMs(3000)
.AtPercentOfEstimatedBitrate(100);
EXPECT_FALSE(alr_detector_.GetApplicationLimitedRegionStartTime());
}
TEST_F(AlrDetectorTest, BandwidthEstimateChanges) {
// Start in non-ALR state.
EXPECT_FALSE(alr_detector_.GetApplicationLimitedRegionStartTime());
// ALR starts when bitrate drops below 20%.
SimulateOutgoingTrafficIn(&alr_detector_, &timestamp_ms_)
.ForTimeMs(1000)
.AtPercentOfEstimatedBitrate(20);
EXPECT_TRUE(alr_detector_.GetApplicationLimitedRegionStartTime());
// When bandwidth estimate drops the detector should stay in ALR mode and quit
// it shortly afterwards as the sender continues sending the same amount of
// traffic. This is necessary to ensure that ProbeController can still react
// to the BWE drop by initiating a new probe.
alr_detector_.SetEstimatedBitrate(kEstimatedBitrateBps / 5);
EXPECT_TRUE(alr_detector_.GetApplicationLimitedRegionStartTime());
SimulateOutgoingTrafficIn(&alr_detector_, &timestamp_ms_)
.ForTimeMs(1000)
.AtPercentOfEstimatedBitrate(50);
EXPECT_FALSE(alr_detector_.GetApplicationLimitedRegionStartTime());
}
TEST_F(AlrDetectorTest, ParseControlFieldTrial) {
webrtc::test::ScopedFieldTrials field_trial(
"WebRTC-ProbingScreenshareBwe/Control/");
rtc::Optional<AlrExperimentSettings> parsed_params =
AlrExperimentSettings::CreateFromFieldTrial(
"WebRTC-ProbingScreenshareBwe");
EXPECT_FALSE(static_cast<bool>(parsed_params));
}
TEST_F(AlrDetectorTest, ParseActiveFieldTrial) {
webrtc::test::ScopedFieldTrials field_trial(
"WebRTC-ProbingScreenshareBwe/1.1,2875,85,20,-20,1/");
rtc::Optional<AlrExperimentSettings> parsed_params =
AlrExperimentSettings::CreateFromFieldTrial(
"WebRTC-ProbingScreenshareBwe");
ASSERT_TRUE(static_cast<bool>(parsed_params));
EXPECT_EQ(1.1f, parsed_params->pacing_factor);
EXPECT_EQ(2875, parsed_params->max_paced_queue_time);
EXPECT_EQ(85, parsed_params->alr_bandwidth_usage_percent);
EXPECT_EQ(20, parsed_params->alr_start_budget_level_percent);
EXPECT_EQ(-20, parsed_params->alr_stop_budget_level_percent);
EXPECT_EQ(1, parsed_params->group_id);
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,109 @@
/*
* Copyright (c) 2017 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/goog_cc/bitrate_estimator.h"
#include <cmath>
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
namespace webrtc {
namespace webrtc_cc {
namespace {
constexpr int kInitialRateWindowMs = 500;
constexpr int kRateWindowMs = 150;
} // namespace
BitrateEstimator::BitrateEstimator()
: sum_(0),
current_win_ms_(0),
prev_time_ms_(-1),
bitrate_estimate_(-1.0f),
bitrate_estimate_var_(50.0f) {}
BitrateEstimator::~BitrateEstimator() = default;
void BitrateEstimator::Update(int64_t now_ms, int bytes) {
int rate_window_ms = kRateWindowMs;
// We use a larger window at the beginning to get a more stable sample that
// we can use to initialize the estimate.
if (bitrate_estimate_ < 0.f)
rate_window_ms = kInitialRateWindowMs;
float bitrate_sample = UpdateWindow(now_ms, bytes, rate_window_ms);
if (bitrate_sample < 0.0f)
return;
if (bitrate_estimate_ < 0.0f) {
// This is the very first sample we get. Use it to initialize the estimate.
bitrate_estimate_ = bitrate_sample;
return;
}
// Define the sample uncertainty as a function of how far away it is from the
// current estimate.
float sample_uncertainty =
10.0f * std::abs(bitrate_estimate_ - bitrate_sample) / bitrate_estimate_;
float sample_var = sample_uncertainty * sample_uncertainty;
// Update a bayesian estimate of the rate, weighting it lower if the sample
// uncertainty is large.
// The bitrate estimate uncertainty is increased with each update to model
// that the bitrate changes over time.
float pred_bitrate_estimate_var = bitrate_estimate_var_ + 5.f;
bitrate_estimate_ = (sample_var * bitrate_estimate_ +
pred_bitrate_estimate_var * bitrate_sample) /
(sample_var + pred_bitrate_estimate_var);
bitrate_estimate_var_ = sample_var * pred_bitrate_estimate_var /
(sample_var + pred_bitrate_estimate_var);
BWE_TEST_LOGGING_PLOT(1, "acknowledged_bitrate", now_ms,
bitrate_estimate_ * 1000);
}
float BitrateEstimator::UpdateWindow(int64_t now_ms,
int bytes,
int rate_window_ms) {
// Reset if time moves backwards.
if (now_ms < prev_time_ms_) {
prev_time_ms_ = -1;
sum_ = 0;
current_win_ms_ = 0;
}
if (prev_time_ms_ >= 0) {
current_win_ms_ += now_ms - prev_time_ms_;
// Reset if nothing has been received for more than a full window.
if (now_ms - prev_time_ms_ > rate_window_ms) {
sum_ = 0;
current_win_ms_ %= rate_window_ms;
}
}
prev_time_ms_ = now_ms;
float bitrate_sample = -1.0f;
if (current_win_ms_ >= rate_window_ms) {
bitrate_sample = 8.0f * sum_ / static_cast<float>(rate_window_ms);
current_win_ms_ -= rate_window_ms;
sum_ = 0;
}
sum_ += bytes;
return bitrate_sample;
}
rtc::Optional<uint32_t> BitrateEstimator::bitrate_bps() const {
if (bitrate_estimate_ < 0.f)
return rtc::nullopt;
return bitrate_estimate_ * 1000;
}
void BitrateEstimator::ExpectFastRateChange() {
// By setting the bitrate-estimate variance to a higher value we allow the
// bitrate to change fast for the next few samples.
bitrate_estimate_var_ += 200;
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2017 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_GOOG_CC_BITRATE_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_
#include <vector>
#include "api/optional.h"
namespace webrtc {
namespace webrtc_cc {
// Computes a bayesian estimate of the throughput given acks containing
// the arrival time and payload size. Samples which are far from the current
// estimate or are based on few packets are given a smaller weight, as they
// are considered to be more likely to have been caused by, e.g., delay spikes
// unrelated to congestion.
class BitrateEstimator {
public:
BitrateEstimator();
virtual ~BitrateEstimator();
virtual void Update(int64_t now_ms, int bytes);
virtual rtc::Optional<uint32_t> bitrate_bps() const;
virtual void ExpectFastRateChange();
private:
float UpdateWindow(int64_t now_ms, int bytes, int rate_window_ms);
int sum_;
int64_t current_win_ms_;
int64_t prev_time_ms_;
float bitrate_estimate_;
float bitrate_estimate_var_;
};
} // namespace webrtc_cc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_

View File

@ -0,0 +1,331 @@
/*
* Copyright (c) 2016 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/goog_cc/delay_based_bwe.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
#include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h"
#include "logging/rtc_event_log/rtc_event_log.h"
#include "modules/congestion_controller/goog_cc/trendline_estimator.h"
#include "modules/pacing/paced_sender.h"
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/checks.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/logging.h"
#include "rtc_base/ptr_util.h"
#include "rtc_base/thread_annotations.h"
#include "system_wrappers/include/field_trial.h"
#include "system_wrappers/include/metrics.h"
#include "typedefs.h" // NOLINT(build/include)
namespace {
constexpr int kTimestampGroupLengthMs = 5;
constexpr int kAbsSendTimeFraction = 18;
constexpr int kAbsSendTimeInterArrivalUpshift = 8;
constexpr int kInterArrivalShift =
kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift;
constexpr double kTimestampToMs =
1000.0 / static_cast<double>(1 << kInterArrivalShift);
// This ssrc is used to fulfill the current API but will be removed
// after the API has been changed.
constexpr uint32_t kFixedSsrc = 0;
// Parameters for linear least squares fit of regression line to noisy data.
constexpr size_t kDefaultTrendlineWindowSize = 20;
constexpr double kDefaultTrendlineSmoothingCoeff = 0.9;
constexpr double kDefaultTrendlineThresholdGain = 4.0;
constexpr int kMaxConsecutiveFailedLookups = 5;
const char kBweWindowSizeInPacketsExperiment[] =
"WebRTC-BweWindowSizeInPackets";
size_t ReadTrendlineFilterWindowSize() {
std::string experiment_string =
webrtc::field_trial::FindFullName(kBweWindowSizeInPacketsExperiment);
size_t window_size;
int parsed_values =
sscanf(experiment_string.c_str(), "Enabled-%zu", &window_size);
if (parsed_values == 1) {
if (window_size > 1)
return window_size;
RTC_LOG(WARNING) << "Window size must be greater than 1.";
}
RTC_LOG(LS_WARNING) << "Failed to parse parameters for BweTrendlineFilter "
"experiment from field trial string. Using default.";
return kDefaultTrendlineWindowSize;
}
} // namespace
namespace webrtc {
namespace webrtc_cc {
DelayBasedBwe::Result::Result()
: updated(false),
probe(false),
target_bitrate_bps(0),
recovered_from_overuse(false) {}
DelayBasedBwe::Result::Result(bool probe, uint32_t target_bitrate_bps)
: updated(true),
probe(probe),
target_bitrate_bps(target_bitrate_bps),
recovered_from_overuse(false) {}
DelayBasedBwe::Result::~Result() {}
DelayBasedBwe::DelayBasedBwe(RtcEventLog* event_log)
: event_log_(event_log),
inter_arrival_(),
delay_detector_(),
last_seen_packet_ms_(-1),
uma_recorded_(false),
probe_bitrate_estimator_(event_log),
trendline_window_size_(
webrtc::field_trial::IsEnabled(kBweWindowSizeInPacketsExperiment)
? ReadTrendlineFilterWindowSize()
: kDefaultTrendlineWindowSize),
trendline_smoothing_coeff_(kDefaultTrendlineSmoothingCoeff),
trendline_threshold_gain_(kDefaultTrendlineThresholdGain),
consecutive_delayed_feedbacks_(0),
prev_bitrate_(0),
prev_state_(BandwidthUsage::kBwNormal) {
RTC_LOG(LS_INFO)
<< "Using Trendline filter for delay change estimation with window size "
<< trendline_window_size_;
delay_detector_.reset(new TrendlineEstimator(trendline_window_size_,
trendline_smoothing_coeff_,
trendline_threshold_gain_));
}
DelayBasedBwe::~DelayBasedBwe() {}
DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector(
const std::vector<PacketFeedback>& packet_feedback_vector,
rtc::Optional<uint32_t> acked_bitrate_bps,
int64_t at_time_ms) {
RTC_DCHECK(std::is_sorted(packet_feedback_vector.begin(),
packet_feedback_vector.end(),
PacketFeedbackComparator()));
RTC_DCHECK_RUNS_SERIALIZED(&network_race_);
// TOOD(holmer): An empty feedback vector here likely means that
// all acks were too late and that the send time history had
// timed out. We should reduce the rate when this occurs.
if (packet_feedback_vector.empty()) {
RTC_LOG(LS_WARNING) << "Very late feedback received.";
return DelayBasedBwe::Result();
}
if (!uma_recorded_) {
RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram,
BweNames::kSendSideTransportSeqNum,
BweNames::kBweNamesMax);
uma_recorded_ = true;
}
bool delayed_feedback = true;
bool recovered_from_overuse = false;
BandwidthUsage prev_detector_state = delay_detector_->State();
for (const auto& packet_feedback : packet_feedback_vector) {
if (packet_feedback.send_time_ms < 0)
continue;
delayed_feedback = false;
IncomingPacketFeedback(packet_feedback, at_time_ms);
if (prev_detector_state == BandwidthUsage::kBwUnderusing &&
delay_detector_->State() == BandwidthUsage::kBwNormal) {
recovered_from_overuse = true;
}
prev_detector_state = delay_detector_->State();
}
if (delayed_feedback) {
++consecutive_delayed_feedbacks_;
if (consecutive_delayed_feedbacks_ >= kMaxConsecutiveFailedLookups) {
consecutive_delayed_feedbacks_ = 0;
return OnLongFeedbackDelay(packet_feedback_vector.back().arrival_time_ms);
}
} else {
consecutive_delayed_feedbacks_ = 0;
return MaybeUpdateEstimate(acked_bitrate_bps, recovered_from_overuse,
at_time_ms);
}
return Result();
}
DelayBasedBwe::Result DelayBasedBwe::OnLongFeedbackDelay(
int64_t arrival_time_ms) {
// Estimate should always be valid since a start bitrate always is set in the
// Call constructor. An alternative would be to return an empty Result here,
// or to estimate the throughput based on the feedback we received.
RTC_DCHECK(rate_control_.ValidEstimate());
rate_control_.SetEstimate(rate_control_.LatestEstimate() / 2,
arrival_time_ms);
Result result;
result.updated = true;
result.probe = false;
result.target_bitrate_bps = rate_control_.LatestEstimate();
RTC_LOG(LS_WARNING) << "Long feedback delay detected, reducing BWE to "
<< result.target_bitrate_bps;
return result;
}
void DelayBasedBwe::IncomingPacketFeedback(
const PacketFeedback& packet_feedback,
int64_t at_time_ms) {
int64_t now_ms = at_time_ms;
// Reset if the stream has timed out.
if (last_seen_packet_ms_ == -1 ||
now_ms - last_seen_packet_ms_ > kStreamTimeOutMs) {
inter_arrival_.reset(
new InterArrival((kTimestampGroupLengthMs << kInterArrivalShift) / 1000,
kTimestampToMs, true));
delay_detector_.reset(new TrendlineEstimator(trendline_window_size_,
trendline_smoothing_coeff_,
trendline_threshold_gain_));
}
last_seen_packet_ms_ = now_ms;
uint32_t send_time_24bits =
static_cast<uint32_t>(
((static_cast<uint64_t>(packet_feedback.send_time_ms)
<< kAbsSendTimeFraction) +
500) /
1000) &
0x00FFFFFF;
// Shift up send time to use the full 32 bits that inter_arrival works with,
// so wrapping works properly.
uint32_t timestamp = send_time_24bits << kAbsSendTimeInterArrivalUpshift;
uint32_t ts_delta = 0;
int64_t t_delta = 0;
int size_delta = 0;
if (inter_arrival_->ComputeDeltas(timestamp, packet_feedback.arrival_time_ms,
now_ms, packet_feedback.payload_size,
&ts_delta, &t_delta, &size_delta)) {
double ts_delta_ms = (1000.0 * ts_delta) / (1 << kInterArrivalShift);
delay_detector_->Update(t_delta, ts_delta_ms,
packet_feedback.arrival_time_ms);
}
if (packet_feedback.pacing_info.probe_cluster_id !=
PacedPacketInfo::kNotAProbe) {
probe_bitrate_estimator_.HandleProbeAndEstimateBitrate(packet_feedback);
}
}
DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
rtc::Optional<uint32_t> acked_bitrate_bps,
bool recovered_from_overuse,
int64_t at_time_ms) {
Result result;
int64_t now_ms = at_time_ms;
rtc::Optional<int> probe_bitrate_bps =
probe_bitrate_estimator_.FetchAndResetLastEstimatedBitrateBps();
// Currently overusing the bandwidth.
if (delay_detector_->State() == BandwidthUsage::kBwOverusing) {
if (acked_bitrate_bps &&
rate_control_.TimeToReduceFurther(now_ms, *acked_bitrate_bps)) {
result.updated =
UpdateEstimate(now_ms, acked_bitrate_bps, &result.target_bitrate_bps);
} else if (!acked_bitrate_bps && rate_control_.ValidEstimate() &&
rate_control_.TimeToReduceFurther(
now_ms, rate_control_.LatestEstimate() / 2 - 1)) {
// Overusing before we have a measured acknowledged bitrate. We check
// TimeToReduceFurther (with a fake acknowledged bitrate) to avoid
// reducing too often.
// TODO(tschumim): Improve this and/or the acknowledged bitrate estimator
// so that we (almost) always have a bitrate estimate.
rate_control_.SetEstimate(rate_control_.LatestEstimate() / 2, now_ms);
result.updated = true;
result.probe = false;
result.target_bitrate_bps = rate_control_.LatestEstimate();
}
} else {
if (probe_bitrate_bps) {
result.probe = true;
result.updated = true;
result.target_bitrate_bps = *probe_bitrate_bps;
rate_control_.SetEstimate(*probe_bitrate_bps, now_ms);
} else {
result.updated =
UpdateEstimate(now_ms, acked_bitrate_bps, &result.target_bitrate_bps);
result.recovered_from_overuse = recovered_from_overuse;
}
}
BandwidthUsage detector_state = delay_detector_->State();
if ((result.updated && prev_bitrate_ != result.target_bitrate_bps) ||
detector_state != prev_state_) {
uint32_t bitrate_bps =
result.updated ? result.target_bitrate_bps : prev_bitrate_;
BWE_TEST_LOGGING_PLOT(1, "target_bitrate_bps", now_ms, bitrate_bps);
if (event_log_) {
event_log_->Log(rtc::MakeUnique<RtcEventBweUpdateDelayBased>(
bitrate_bps, detector_state));
}
prev_bitrate_ = bitrate_bps;
prev_state_ = detector_state;
}
return result;
}
bool DelayBasedBwe::UpdateEstimate(int64_t now_ms,
rtc::Optional<uint32_t> acked_bitrate_bps,
uint32_t* target_bitrate_bps) {
// TODO(terelius): RateControlInput::noise_var is deprecated and will be
// removed. In the meantime, we set it to zero.
const RateControlInput input(delay_detector_->State(), acked_bitrate_bps, 0);
*target_bitrate_bps = rate_control_.Update(&input, now_ms);
return rate_control_.ValidEstimate();
}
void DelayBasedBwe::OnRttUpdate(int64_t avg_rtt_ms) {
rate_control_.SetRtt(avg_rtt_ms);
}
bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs,
uint32_t* bitrate_bps) const {
// Currently accessed from both the process thread (see
// ModuleRtpRtcpImpl::Process()) and the configuration thread (see
// Call::GetStats()). Should in the future only be accessed from a single
// thread.
RTC_DCHECK(ssrcs);
RTC_DCHECK(bitrate_bps);
if (!rate_control_.ValidEstimate())
return false;
*ssrcs = {kFixedSsrc};
*bitrate_bps = rate_control_.LatestEstimate();
return true;
}
void DelayBasedBwe::SetStartBitrate(int start_bitrate_bps) {
RTC_LOG(LS_WARNING) << "BWE Setting start bitrate to: " << start_bitrate_bps;
rate_control_.SetStartBitrate(start_bitrate_bps);
}
void DelayBasedBwe::SetMinBitrate(int min_bitrate_bps) {
// Called from both the configuration thread and the network thread. Shouldn't
// be called from the network thread in the future.
rate_control_.SetMinBitrate(min_bitrate_bps);
}
int64_t DelayBasedBwe::GetExpectedBwePeriodMs() const {
return rate_control_.GetExpectedBandwidthPeriodMs();
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2016 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_GOOG_CC_DELAY_BASED_BWE_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_
#include <memory>
#include <utility>
#include <vector>
#include "modules/congestion_controller/goog_cc/delay_increase_detector_interface.h"
#include "modules/congestion_controller/goog_cc/probe_bitrate_estimator.h"
#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "modules/remote_bitrate_estimator/inter_arrival.h"
#include "rtc_base/checks.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/race_checker.h"
namespace webrtc {
class RtcEventLog;
namespace webrtc_cc {
class DelayBasedBwe {
public:
static const int64_t kStreamTimeOutMs = 2000;
struct Result {
Result();
Result(bool probe, uint32_t target_bitrate_bps);
~Result();
bool updated;
bool probe;
uint32_t target_bitrate_bps;
bool recovered_from_overuse;
};
explicit DelayBasedBwe(RtcEventLog* event_log);
virtual ~DelayBasedBwe();
Result IncomingPacketFeedbackVector(
const std::vector<PacketFeedback>& packet_feedback_vector,
rtc::Optional<uint32_t> acked_bitrate_bps,
int64_t at_time_ms);
void OnRttUpdate(int64_t avg_rtt_ms);
bool LatestEstimate(std::vector<uint32_t>* ssrcs,
uint32_t* bitrate_bps) const;
void SetStartBitrate(int start_bitrate_bps);
void SetMinBitrate(int min_bitrate_bps);
int64_t GetExpectedBwePeriodMs() const;
private:
void IncomingPacketFeedback(const PacketFeedback& packet_feedback,
int64_t at_time_ms);
Result OnLongFeedbackDelay(int64_t arrival_time_ms);
Result MaybeUpdateEstimate(rtc::Optional<uint32_t> acked_bitrate_bps,
bool request_probe,
int64_t at_time_ms);
// Updates the current remote rate estimate and returns true if a valid
// estimate exists.
bool UpdateEstimate(int64_t now_ms,
rtc::Optional<uint32_t> acked_bitrate_bps,
uint32_t* target_bitrate_bps);
rtc::RaceChecker network_race_;
RtcEventLog* const event_log_;
std::unique_ptr<InterArrival> inter_arrival_;
std::unique_ptr<DelayIncreaseDetectorInterface> delay_detector_;
int64_t last_seen_packet_ms_;
bool uma_recorded_;
AimdRateControl rate_control_;
ProbeBitrateEstimator probe_bitrate_estimator_;
size_t trendline_window_size_;
double trendline_smoothing_coeff_;
double trendline_threshold_gain_;
int consecutive_delayed_feedbacks_;
uint32_t prev_bitrate_;
BandwidthUsage prev_state_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(DelayBasedBwe);
};
} // namespace webrtc_cc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_

View File

@ -0,0 +1,239 @@
/*
* Copyright (c) 2016 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/goog_cc/delay_based_bwe.h"
#include "modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h"
#include "modules/pacing/paced_sender.h"
#include "rtc_base/constructormagic.h"
#include "system_wrappers/include/clock.h"
#include "test/field_trial.h"
#include "test/gtest.h"
namespace webrtc {
namespace webrtc_cc {
namespace {
constexpr int kNumProbesCluster0 = 5;
constexpr int kNumProbesCluster1 = 8;
const PacedPacketInfo kPacingInfo0(0, kNumProbesCluster0, 2000);
const PacedPacketInfo kPacingInfo1(1, kNumProbesCluster1, 4000);
constexpr float kTargetUtilizationFraction = 0.95f;
constexpr int64_t kDummyTimestamp = 1000;
} // namespace
TEST_F(DelayBasedBweTest, NoCrashEmptyFeedback) {
std::vector<PacketFeedback> packet_feedback_vector;
bitrate_estimator_->IncomingPacketFeedbackVector(
packet_feedback_vector, rtc::nullopt, kDummyTimestamp);
}
TEST_F(DelayBasedBweTest, NoCrashOnlyLostFeedback) {
std::vector<PacketFeedback> packet_feedback_vector;
packet_feedback_vector.push_back(PacketFeedback(PacketFeedback::kNotReceived,
PacketFeedback::kNoSendTime,
0, 1500, PacedPacketInfo()));
packet_feedback_vector.push_back(PacketFeedback(PacketFeedback::kNotReceived,
PacketFeedback::kNoSendTime,
1, 1500, PacedPacketInfo()));
bitrate_estimator_->IncomingPacketFeedbackVector(
packet_feedback_vector, rtc::nullopt, kDummyTimestamp);
}
TEST_F(DelayBasedBweTest, ProbeDetection) {
int64_t now_ms = clock_.TimeInMilliseconds();
uint16_t seq_num = 0;
// First burst sent at 8 * 1000 / 10 = 800 kbps.
for (int i = 0; i < kNumProbesCluster0; ++i) {
clock_.AdvanceTimeMilliseconds(10);
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, now_ms, seq_num++, 1000, kPacingInfo0);
}
EXPECT_TRUE(bitrate_observer_.updated());
// Second burst sent at 8 * 1000 / 5 = 1600 kbps.
for (int i = 0; i < kNumProbesCluster1; ++i) {
clock_.AdvanceTimeMilliseconds(5);
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, now_ms, seq_num++, 1000, kPacingInfo1);
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_GT(bitrate_observer_.latest_bitrate(), 1500000u);
}
TEST_F(DelayBasedBweTest, ProbeDetectionNonPacedPackets) {
int64_t now_ms = clock_.TimeInMilliseconds();
uint16_t seq_num = 0;
// First burst sent at 8 * 1000 / 10 = 800 kbps, but with every other packet
// not being paced which could mess things up.
for (int i = 0; i < kNumProbesCluster0; ++i) {
clock_.AdvanceTimeMilliseconds(5);
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, now_ms, seq_num++, 1000, kPacingInfo0);
// Non-paced packet, arriving 5 ms after.
clock_.AdvanceTimeMilliseconds(5);
IncomingFeedback(now_ms, now_ms, seq_num++, 100, PacedPacketInfo());
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_GT(bitrate_observer_.latest_bitrate(), 800000u);
}
TEST_F(DelayBasedBweTest, ProbeDetectionFasterArrival) {
int64_t now_ms = clock_.TimeInMilliseconds();
uint16_t seq_num = 0;
// First burst sent at 8 * 1000 / 10 = 800 kbps.
// Arriving at 8 * 1000 / 5 = 1600 kbps.
int64_t send_time_ms = 0;
for (int i = 0; i < kNumProbesCluster0; ++i) {
clock_.AdvanceTimeMilliseconds(1);
send_time_ms += 10;
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, send_time_ms, seq_num++, 1000, kPacingInfo0);
}
EXPECT_FALSE(bitrate_observer_.updated());
}
TEST_F(DelayBasedBweTest, ProbeDetectionSlowerArrival) {
int64_t now_ms = clock_.TimeInMilliseconds();
uint16_t seq_num = 0;
// First burst sent at 8 * 1000 / 5 = 1600 kbps.
// Arriving at 8 * 1000 / 7 = 1142 kbps.
// Since the receive rate is significantly below the send rate, we expect to
// use 95% of the estimated capacity.
int64_t send_time_ms = 0;
for (int i = 0; i < kNumProbesCluster1; ++i) {
clock_.AdvanceTimeMilliseconds(7);
send_time_ms += 5;
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, send_time_ms, seq_num++, 1000, kPacingInfo1);
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_NEAR(bitrate_observer_.latest_bitrate(),
kTargetUtilizationFraction * 1140000u, 10000u);
}
TEST_F(DelayBasedBweTest, ProbeDetectionSlowerArrivalHighBitrate) {
int64_t now_ms = clock_.TimeInMilliseconds();
uint16_t seq_num = 0;
// Burst sent at 8 * 1000 / 1 = 8000 kbps.
// Arriving at 8 * 1000 / 2 = 4000 kbps.
// Since the receive rate is significantly below the send rate, we expect to
// use 95% of the estimated capacity.
int64_t send_time_ms = 0;
for (int i = 0; i < kNumProbesCluster1; ++i) {
clock_.AdvanceTimeMilliseconds(2);
send_time_ms += 1;
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, send_time_ms, seq_num++, 1000, kPacingInfo1);
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_NEAR(bitrate_observer_.latest_bitrate(),
kTargetUtilizationFraction * 4000000u, 10000u);
}
TEST_F(DelayBasedBweTest, GetExpectedBwePeriodMs) {
int64_t default_interval_ms = bitrate_estimator_->GetExpectedBwePeriodMs();
EXPECT_GT(default_interval_ms, 0);
CapacityDropTestHelper(1, true, 333, 0);
int64_t interval_ms = bitrate_estimator_->GetExpectedBwePeriodMs();
EXPECT_GT(interval_ms, 0);
EXPECT_NE(interval_ms, default_interval_ms);
}
TEST_F(DelayBasedBweTest, InitialBehavior) {
InitialBehaviorTestHelper(730000);
}
TEST_F(DelayBasedBweTest, RateIncreaseReordering) {
RateIncreaseReorderingTestHelper(730000);
}
TEST_F(DelayBasedBweTest, RateIncreaseRtpTimestamps) {
RateIncreaseRtpTimestampsTestHelper(627);
}
TEST_F(DelayBasedBweTest, CapacityDropOneStream) {
CapacityDropTestHelper(1, false, 300, 0);
}
TEST_F(DelayBasedBweTest, CapacityDropPosOffsetChange) {
CapacityDropTestHelper(1, false, 867, 30000);
}
TEST_F(DelayBasedBweTest, CapacityDropNegOffsetChange) {
CapacityDropTestHelper(1, false, 933, -30000);
}
TEST_F(DelayBasedBweTest, CapacityDropOneStreamWrap) {
CapacityDropTestHelper(1, true, 333, 0);
}
TEST_F(DelayBasedBweTest, TestTimestampGrouping) {
TestTimestampGroupingTestHelper();
}
TEST_F(DelayBasedBweTest, TestShortTimeoutAndWrap) {
// Simulate a client leaving and rejoining the call after 35 seconds. This
// will make abs send time wrap, so if streams aren't timed out properly
// the next 30 seconds of packets will be out of order.
TestWrappingHelper(35);
}
TEST_F(DelayBasedBweTest, TestLongTimeoutAndWrap) {
// Simulate a client leaving and rejoining the call after some multiple of
// 64 seconds later. This will cause a zero difference in abs send times due
// to the wrap, but a big difference in arrival time, if streams aren't
// properly timed out.
TestWrappingHelper(10 * 64);
}
TEST_F(DelayBasedBweTest, TestInitialOveruse) {
const uint32_t kStartBitrate = 300e3;
const uint32_t kInitialCapacityBps = 200e3;
const uint32_t kDummySsrc = 0;
// High FPS to ensure that we send a lot of packets in a short time.
const int kFps = 90;
stream_generator_->AddStream(new test::RtpStream(kFps, kStartBitrate));
stream_generator_->set_capacity_bps(kInitialCapacityBps);
// Needed to initialize the AimdRateControl.
bitrate_estimator_->SetStartBitrate(kStartBitrate);
// Produce 30 frames (in 1/3 second) and give them to the estimator.
uint32_t bitrate_bps = kStartBitrate;
bool seen_overuse = false;
for (int i = 0; i < 30; ++i) {
bool overuse = GenerateAndProcessFrame(kDummySsrc, bitrate_bps);
// The purpose of this test is to ensure that we back down even if we don't
// have any acknowledged bitrate estimate yet. Hence, if the test works
// as expected, we should not have a measured bitrate yet.
EXPECT_FALSE(acknowledged_bitrate_estimator_->bitrate_bps().has_value());
if (overuse) {
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_NEAR(bitrate_observer_.latest_bitrate(), kStartBitrate / 2, 15000);
bitrate_bps = bitrate_observer_.latest_bitrate();
seen_overuse = true;
break;
} else if (bitrate_observer_.updated()) {
bitrate_bps = bitrate_observer_.latest_bitrate();
bitrate_observer_.Reset();
}
}
EXPECT_TRUE(seen_overuse);
EXPECT_NEAR(bitrate_observer_.latest_bitrate(), kStartBitrate / 2, 15000);
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,517 @@
/*
* Copyright (c) 2016 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/goog_cc/delay_based_bwe_unittest_helper.h"
#include <algorithm>
#include <limits>
#include <utility>
#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
#include "rtc_base/checks.h"
#include "rtc_base/ptr_util.h"
namespace webrtc {
namespace webrtc_cc {
constexpr size_t kMtu = 1200;
constexpr uint32_t kAcceptedBitrateErrorBps = 50000;
// Number of packets needed before we have a valid estimate.
constexpr int kNumInitialPackets = 2;
constexpr int kInitialProbingPackets = 5;
namespace test {
void TestBitrateObserver::OnReceiveBitrateChanged(
const std::vector<uint32_t>& ssrcs,
uint32_t bitrate) {
latest_bitrate_ = bitrate;
updated_ = true;
}
RtpStream::RtpStream(int fps, int bitrate_bps)
: fps_(fps),
bitrate_bps_(bitrate_bps),
next_rtp_time_(0),
sequence_number_(0) {
RTC_CHECK_GT(fps_, 0);
}
// Generates a new frame for this stream. If called too soon after the
// previous frame, no frame will be generated. The frame is split into
// packets.
int64_t RtpStream::GenerateFrame(int64_t time_now_us,
std::vector<PacketFeedback>* packets) {
if (time_now_us < next_rtp_time_) {
return next_rtp_time_;
}
RTC_CHECK(packets != NULL);
size_t bits_per_frame = (bitrate_bps_ + fps_ / 2) / fps_;
size_t n_packets =
std::max<size_t>((bits_per_frame + 4 * kMtu) / (8 * kMtu), 1u);
size_t payload_size = (bits_per_frame + 4 * n_packets) / (8 * n_packets);
for (size_t i = 0; i < n_packets; ++i) {
PacketFeedback packet(-1, sequence_number_++);
packet.send_time_ms = (time_now_us + kSendSideOffsetUs) / 1000;
packet.payload_size = payload_size;
packets->push_back(packet);
}
next_rtp_time_ = time_now_us + (1000000 + fps_ / 2) / fps_;
return next_rtp_time_;
}
// The send-side time when the next frame can be generated.
int64_t RtpStream::next_rtp_time() const {
return next_rtp_time_;
}
void RtpStream::set_bitrate_bps(int bitrate_bps) {
ASSERT_GE(bitrate_bps, 0);
bitrate_bps_ = bitrate_bps;
}
int RtpStream::bitrate_bps() const {
return bitrate_bps_;
}
bool RtpStream::Compare(const std::unique_ptr<RtpStream>& lhs,
const std::unique_ptr<RtpStream>& rhs) {
return lhs->next_rtp_time_ < rhs->next_rtp_time_;
}
StreamGenerator::StreamGenerator(int capacity, int64_t time_now)
: capacity_(capacity), prev_arrival_time_us_(time_now) {}
// Add a new stream.
void StreamGenerator::AddStream(RtpStream* stream) {
streams_.push_back(std::unique_ptr<RtpStream>(stream));
}
// Set the link capacity.
void StreamGenerator::set_capacity_bps(int capacity_bps) {
ASSERT_GT(capacity_bps, 0);
capacity_ = capacity_bps;
}
// Divides |bitrate_bps| among all streams. The allocated bitrate per stream
// is decided by the current allocation ratios.
void StreamGenerator::SetBitrateBps(int bitrate_bps) {
ASSERT_GE(streams_.size(), 0u);
int total_bitrate_before = 0;
for (const auto& stream : streams_) {
total_bitrate_before += stream->bitrate_bps();
}
int64_t bitrate_before = 0;
int total_bitrate_after = 0;
for (const auto& stream : streams_) {
bitrate_before += stream->bitrate_bps();
int64_t bitrate_after =
(bitrate_before * bitrate_bps + total_bitrate_before / 2) /
total_bitrate_before;
stream->set_bitrate_bps(bitrate_after - total_bitrate_after);
total_bitrate_after += stream->bitrate_bps();
}
ASSERT_EQ(bitrate_before, total_bitrate_before);
EXPECT_EQ(total_bitrate_after, bitrate_bps);
}
// TODO(holmer): Break out the channel simulation part from this class to make
// it possible to simulate different types of channels.
int64_t StreamGenerator::GenerateFrame(std::vector<PacketFeedback>* packets,
int64_t time_now_us) {
RTC_CHECK(packets != NULL);
RTC_CHECK(packets->empty());
RTC_CHECK_GT(capacity_, 0);
auto it =
std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare);
(*it)->GenerateFrame(time_now_us, packets);
int i = 0;
for (PacketFeedback& packet : *packets) {
int capacity_bpus = capacity_ / 1000;
int64_t required_network_time_us =
(8 * 1000 * packet.payload_size + capacity_bpus / 2) / capacity_bpus;
prev_arrival_time_us_ =
std::max(time_now_us + required_network_time_us,
prev_arrival_time_us_ + required_network_time_us);
packet.arrival_time_ms = prev_arrival_time_us_ / 1000;
++i;
}
it = std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare);
return std::max((*it)->next_rtp_time(), time_now_us);
}
} // namespace test
DelayBasedBweTest::DelayBasedBweTest()
: clock_(100000000),
acknowledged_bitrate_estimator_(
rtc::MakeUnique<AcknowledgedBitrateEstimator>()),
bitrate_estimator_(new DelayBasedBwe(nullptr)),
stream_generator_(new test::StreamGenerator(1e6, // Capacity.
clock_.TimeInMicroseconds())),
arrival_time_offset_ms_(0),
first_update_(true) {}
DelayBasedBweTest::~DelayBasedBweTest() {}
void DelayBasedBweTest::AddDefaultStream() {
stream_generator_->AddStream(new test::RtpStream(30, 3e5));
}
const uint32_t DelayBasedBweTest::kDefaultSsrc = 0;
void DelayBasedBweTest::IncomingFeedback(int64_t arrival_time_ms,
int64_t send_time_ms,
uint16_t sequence_number,
size_t payload_size) {
IncomingFeedback(arrival_time_ms, send_time_ms, sequence_number, payload_size,
PacedPacketInfo());
}
void DelayBasedBweTest::IncomingFeedback(int64_t arrival_time_ms,
int64_t send_time_ms,
uint16_t sequence_number,
size_t payload_size,
const PacedPacketInfo& pacing_info) {
RTC_CHECK_GE(arrival_time_ms + arrival_time_offset_ms_, 0);
PacketFeedback packet(arrival_time_ms + arrival_time_offset_ms_, send_time_ms,
sequence_number, payload_size, pacing_info);
std::vector<PacketFeedback> packets;
packets.push_back(packet);
acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(packets);
DelayBasedBwe::Result result =
bitrate_estimator_->IncomingPacketFeedbackVector(
packets, acknowledged_bitrate_estimator_->bitrate_bps(),
clock_.TimeInMilliseconds());
const uint32_t kDummySsrc = 0;
if (result.updated) {
bitrate_observer_.OnReceiveBitrateChanged({kDummySsrc},
result.target_bitrate_bps);
}
}
// Generates a frame of packets belonging to a stream at a given bitrate and
// with a given ssrc. The stream is pushed through a very simple simulated
// network, and is then given to the receive-side bandwidth estimator.
// Returns true if an over-use was seen, false otherwise.
// The StreamGenerator::updated() should be used to check for any changes in
// target bitrate after the call to this function.
bool DelayBasedBweTest::GenerateAndProcessFrame(uint32_t ssrc,
uint32_t bitrate_bps) {
stream_generator_->SetBitrateBps(bitrate_bps);
std::vector<PacketFeedback> packets;
int64_t next_time_us =
stream_generator_->GenerateFrame(&packets, clock_.TimeInMicroseconds());
if (packets.empty())
return false;
bool overuse = false;
bitrate_observer_.Reset();
clock_.AdvanceTimeMicroseconds(1000 * packets.back().arrival_time_ms -
clock_.TimeInMicroseconds());
for (auto& packet : packets) {
RTC_CHECK_GE(packet.arrival_time_ms + arrival_time_offset_ms_, 0);
packet.arrival_time_ms += arrival_time_offset_ms_;
}
acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(packets);
DelayBasedBwe::Result result =
bitrate_estimator_->IncomingPacketFeedbackVector(
packets, acknowledged_bitrate_estimator_->bitrate_bps(),
clock_.TimeInMilliseconds());
const uint32_t kDummySsrc = 0;
if (result.updated) {
bitrate_observer_.OnReceiveBitrateChanged({kDummySsrc},
result.target_bitrate_bps);
if (!first_update_ && result.target_bitrate_bps < bitrate_bps)
overuse = true;
first_update_ = false;
}
clock_.AdvanceTimeMicroseconds(next_time_us - clock_.TimeInMicroseconds());
return overuse;
}
// Run the bandwidth estimator with a stream of |number_of_frames| frames, or
// until it reaches |target_bitrate|.
// Can for instance be used to run the estimator for some time to get it
// into a steady state.
uint32_t DelayBasedBweTest::SteadyStateRun(uint32_t ssrc,
int max_number_of_frames,
uint32_t start_bitrate,
uint32_t min_bitrate,
uint32_t max_bitrate,
uint32_t target_bitrate) {
uint32_t bitrate_bps = start_bitrate;
bool bitrate_update_seen = false;
// Produce |number_of_frames| frames and give them to the estimator.
for (int i = 0; i < max_number_of_frames; ++i) {
bool overuse = GenerateAndProcessFrame(ssrc, bitrate_bps);
if (overuse) {
EXPECT_LT(bitrate_observer_.latest_bitrate(), max_bitrate);
EXPECT_GT(bitrate_observer_.latest_bitrate(), min_bitrate);
bitrate_bps = bitrate_observer_.latest_bitrate();
bitrate_update_seen = true;
} else if (bitrate_observer_.updated()) {
bitrate_bps = bitrate_observer_.latest_bitrate();
bitrate_observer_.Reset();
}
if (bitrate_update_seen && bitrate_bps > target_bitrate) {
break;
}
}
EXPECT_TRUE(bitrate_update_seen);
return bitrate_bps;
}
void DelayBasedBweTest::InitialBehaviorTestHelper(
uint32_t expected_converge_bitrate) {
const int kFramerate = 50; // 50 fps to avoid rounding errors.
const int kFrameIntervalMs = 1000 / kFramerate;
const PacedPacketInfo kPacingInfo(0, 5, 5000);
uint32_t bitrate_bps = 0;
int64_t send_time_ms = 0;
uint16_t sequence_number = 0;
std::vector<uint32_t> ssrcs;
EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_bps));
EXPECT_EQ(0u, ssrcs.size());
clock_.AdvanceTimeMilliseconds(1000);
EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_bps));
EXPECT_FALSE(bitrate_observer_.updated());
bitrate_observer_.Reset();
clock_.AdvanceTimeMilliseconds(1000);
// Inserting packets for 5 seconds to get a valid estimate.
for (int i = 0; i < 5 * kFramerate + 1 + kNumInitialPackets; ++i) {
// NOTE!!! If the following line is moved under the if case then this test
// wont work on windows realease bots.
PacedPacketInfo pacing_info =
i < kInitialProbingPackets ? kPacingInfo : PacedPacketInfo();
if (i == kNumInitialPackets) {
EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_bps));
EXPECT_EQ(0u, ssrcs.size());
EXPECT_FALSE(bitrate_observer_.updated());
bitrate_observer_.Reset();
}
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms,
sequence_number++, kMtu, pacing_info);
clock_.AdvanceTimeMilliseconds(1000 / kFramerate);
send_time_ms += kFrameIntervalMs;
}
EXPECT_TRUE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_bps));
ASSERT_EQ(1u, ssrcs.size());
EXPECT_EQ(kDefaultSsrc, ssrcs.front());
EXPECT_NEAR(expected_converge_bitrate, bitrate_bps, kAcceptedBitrateErrorBps);
EXPECT_TRUE(bitrate_observer_.updated());
bitrate_observer_.Reset();
EXPECT_EQ(bitrate_observer_.latest_bitrate(), bitrate_bps);
}
void DelayBasedBweTest::RateIncreaseReorderingTestHelper(
uint32_t expected_bitrate_bps) {
const int kFramerate = 50; // 50 fps to avoid rounding errors.
const int kFrameIntervalMs = 1000 / kFramerate;
const PacedPacketInfo kPacingInfo(0, 5, 5000);
int64_t send_time_ms = 0;
uint16_t sequence_number = 0;
// Inserting packets for five seconds to get a valid estimate.
for (int i = 0; i < 5 * kFramerate + 1 + kNumInitialPackets; ++i) {
// NOTE!!! If the following line is moved under the if case then this test
// wont work on windows realease bots.
PacedPacketInfo pacing_info =
i < kInitialProbingPackets ? kPacingInfo : PacedPacketInfo();
// TODO(sprang): Remove this hack once the single stream estimator is gone,
// as it doesn't do anything in Process().
if (i == kNumInitialPackets) {
// Process after we have enough frames to get a valid input rate estimate.
EXPECT_FALSE(bitrate_observer_.updated()); // No valid estimate.
}
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms,
sequence_number++, kMtu, pacing_info);
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
send_time_ms += kFrameIntervalMs;
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_NEAR(expected_bitrate_bps, bitrate_observer_.latest_bitrate(),
kAcceptedBitrateErrorBps);
for (int i = 0; i < 10; ++i) {
clock_.AdvanceTimeMilliseconds(2 * kFrameIntervalMs);
send_time_ms += 2 * kFrameIntervalMs;
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms,
sequence_number + 2, 1000);
IncomingFeedback(clock_.TimeInMilliseconds(),
send_time_ms - kFrameIntervalMs, sequence_number + 1,
1000);
sequence_number += 2;
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_NEAR(expected_bitrate_bps, bitrate_observer_.latest_bitrate(),
kAcceptedBitrateErrorBps);
}
// Make sure we initially increase the bitrate as expected.
void DelayBasedBweTest::RateIncreaseRtpTimestampsTestHelper(
int expected_iterations) {
// This threshold corresponds approximately to increasing linearly with
// bitrate(i) = 1.04 * bitrate(i-1) + 1000
// until bitrate(i) > 500000, with bitrate(1) ~= 30000.
uint32_t bitrate_bps = 30000;
int iterations = 0;
AddDefaultStream();
// Feed the estimator with a stream of packets and verify that it reaches
// 500 kbps at the expected time.
while (bitrate_bps < 5e5) {
bool overuse = GenerateAndProcessFrame(kDefaultSsrc, bitrate_bps);
if (overuse) {
EXPECT_GT(bitrate_observer_.latest_bitrate(), bitrate_bps);
bitrate_bps = bitrate_observer_.latest_bitrate();
bitrate_observer_.Reset();
} else if (bitrate_observer_.updated()) {
bitrate_bps = bitrate_observer_.latest_bitrate();
bitrate_observer_.Reset();
}
++iterations;
}
ASSERT_EQ(expected_iterations, iterations);
}
void DelayBasedBweTest::CapacityDropTestHelper(
int number_of_streams,
bool wrap_time_stamp,
uint32_t expected_bitrate_drop_delta,
int64_t receiver_clock_offset_change_ms) {
const int kFramerate = 30;
const int kStartBitrate = 900e3;
const int kMinExpectedBitrate = 800e3;
const int kMaxExpectedBitrate = 1100e3;
const uint32_t kInitialCapacityBps = 1000e3;
const uint32_t kReducedCapacityBps = 500e3;
int steady_state_time = 0;
if (number_of_streams <= 1) {
steady_state_time = 10;
AddDefaultStream();
} else {
steady_state_time = 10 * number_of_streams;
int bitrate_sum = 0;
int kBitrateDenom = number_of_streams * (number_of_streams - 1);
for (int i = 0; i < number_of_streams; i++) {
// First stream gets half available bitrate, while the rest share the
// remaining half i.e.: 1/2 = Sum[n/(N*(N-1))] for n=1..N-1 (rounded up)
int bitrate = kStartBitrate / 2;
if (i > 0) {
bitrate = (kStartBitrate * i + kBitrateDenom / 2) / kBitrateDenom;
}
stream_generator_->AddStream(new test::RtpStream(kFramerate, bitrate));
bitrate_sum += bitrate;
}
ASSERT_EQ(bitrate_sum, kStartBitrate);
}
// Run in steady state to make the estimator converge.
stream_generator_->set_capacity_bps(kInitialCapacityBps);
uint32_t bitrate_bps = SteadyStateRun(
kDefaultSsrc, steady_state_time * kFramerate, kStartBitrate,
kMinExpectedBitrate, kMaxExpectedBitrate, kInitialCapacityBps);
EXPECT_NEAR(kInitialCapacityBps, bitrate_bps, 180000u);
bitrate_observer_.Reset();
// Add an offset to make sure the BWE can handle it.
arrival_time_offset_ms_ += receiver_clock_offset_change_ms;
// Reduce the capacity and verify the decrease time.
stream_generator_->set_capacity_bps(kReducedCapacityBps);
int64_t overuse_start_time = clock_.TimeInMilliseconds();
int64_t bitrate_drop_time = -1;
for (int i = 0; i < 100 * number_of_streams; ++i) {
GenerateAndProcessFrame(kDefaultSsrc, bitrate_bps);
if (bitrate_drop_time == -1 &&
bitrate_observer_.latest_bitrate() <= kReducedCapacityBps) {
bitrate_drop_time = clock_.TimeInMilliseconds();
}
if (bitrate_observer_.updated())
bitrate_bps = bitrate_observer_.latest_bitrate();
}
EXPECT_NEAR(expected_bitrate_drop_delta,
bitrate_drop_time - overuse_start_time, 33);
}
void DelayBasedBweTest::TestTimestampGroupingTestHelper() {
const int kFramerate = 50; // 50 fps to avoid rounding errors.
const int kFrameIntervalMs = 1000 / kFramerate;
int64_t send_time_ms = 0;
uint16_t sequence_number = 0;
// Initial set of frames to increase the bitrate. 6 seconds to have enough
// time for the first estimate to be generated and for Process() to be called.
for (int i = 0; i <= 6 * kFramerate; ++i) {
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms,
sequence_number++, 1000);
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
send_time_ms += kFrameIntervalMs;
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_GE(bitrate_observer_.latest_bitrate(), 400000u);
// Insert batches of frames which were sent very close in time. Also simulate
// capacity over-use to see that we back off correctly.
const int kTimestampGroupLength = 15;
for (int i = 0; i < 100; ++i) {
for (int j = 0; j < kTimestampGroupLength; ++j) {
// Insert |kTimestampGroupLength| frames with just 1 timestamp ticks in
// between. Should be treated as part of the same group by the estimator.
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms,
sequence_number++, 100);
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs / kTimestampGroupLength);
send_time_ms += 1;
}
// Increase time until next batch to simulate over-use.
clock_.AdvanceTimeMilliseconds(10);
send_time_ms += kFrameIntervalMs - kTimestampGroupLength;
}
EXPECT_TRUE(bitrate_observer_.updated());
// Should have reduced the estimate.
EXPECT_LT(bitrate_observer_.latest_bitrate(), 400000u);
}
void DelayBasedBweTest::TestWrappingHelper(int silence_time_s) {
const int kFramerate = 100;
const int kFrameIntervalMs = 1000 / kFramerate;
int64_t send_time_ms = 0;
uint16_t sequence_number = 0;
for (size_t i = 0; i < 3000; ++i) {
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms,
sequence_number++, 1000);
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
send_time_ms += kFrameIntervalMs;
}
uint32_t bitrate_before = 0;
std::vector<uint32_t> ssrcs;
bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_before);
clock_.AdvanceTimeMilliseconds(silence_time_s * 1000);
send_time_ms += silence_time_s * 1000;
for (size_t i = 0; i < 24; ++i) {
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms,
sequence_number++, 1000);
clock_.AdvanceTimeMilliseconds(2 * kFrameIntervalMs);
send_time_ms += kFrameIntervalMs;
}
uint32_t bitrate_after = 0;
bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_after);
EXPECT_LT(bitrate_after, bitrate_before);
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,180 @@
/*
* Copyright (c) 2016 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_GOOG_CC_DELAY_BASED_BWE_UNITTEST_HELPER_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_UNITTEST_HELPER_H_
#include <list>
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "rtc_base/constructormagic.h"
#include "system_wrappers/include/clock.h"
#include "test/gtest.h"
namespace webrtc {
namespace webrtc_cc {
namespace test {
class TestBitrateObserver : public RemoteBitrateObserver {
public:
TestBitrateObserver() : updated_(false), latest_bitrate_(0) {}
virtual ~TestBitrateObserver() {}
void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
uint32_t bitrate) override;
void Reset() { updated_ = false; }
bool updated() const { return updated_; }
uint32_t latest_bitrate() const { return latest_bitrate_; }
private:
bool updated_;
uint32_t latest_bitrate_;
};
class RtpStream {
public:
enum { kSendSideOffsetUs = 1000000 };
RtpStream(int fps, int bitrate_bps);
// Generates a new frame for this stream. If called too soon after the
// previous frame, no frame will be generated. The frame is split into
// packets.
int64_t GenerateFrame(int64_t time_now_us,
std::vector<PacketFeedback>* packets);
// The send-side time when the next frame can be generated.
int64_t next_rtp_time() const;
void set_bitrate_bps(int bitrate_bps);
int bitrate_bps() const;
static bool Compare(const std::unique_ptr<RtpStream>& lhs,
const std::unique_ptr<RtpStream>& rhs);
private:
int fps_;
int bitrate_bps_;
int64_t next_rtp_time_;
uint16_t sequence_number_;
RTC_DISALLOW_COPY_AND_ASSIGN(RtpStream);
};
class StreamGenerator {
public:
StreamGenerator(int capacity, int64_t time_now);
// Add a new stream.
void AddStream(RtpStream* stream);
// Set the link capacity.
void set_capacity_bps(int capacity_bps);
// Divides |bitrate_bps| among all streams. The allocated bitrate per stream
// is decided by the initial allocation ratios.
void SetBitrateBps(int bitrate_bps);
// Set the RTP timestamp offset for the stream identified by |ssrc|.
void set_rtp_timestamp_offset(uint32_t ssrc, uint32_t offset);
// TODO(holmer): Break out the channel simulation part from this class to make
// it possible to simulate different types of channels.
int64_t GenerateFrame(std::vector<PacketFeedback>* packets,
int64_t time_now_us);
private:
// Capacity of the simulated channel in bits per second.
int capacity_;
// The time when the last packet arrived.
int64_t prev_arrival_time_us_;
// All streams being transmitted on this simulated channel.
std::vector<std::unique_ptr<RtpStream>> streams_;
RTC_DISALLOW_COPY_AND_ASSIGN(StreamGenerator);
};
} // namespace test
class DelayBasedBweTest : public ::testing::Test {
public:
DelayBasedBweTest();
virtual ~DelayBasedBweTest();
protected:
void AddDefaultStream();
// Helpers to insert a single packet into the delay-based BWE.
void IncomingFeedback(int64_t arrival_time_ms,
int64_t send_time_ms,
uint16_t sequence_number,
size_t payload_size);
void IncomingFeedback(int64_t arrival_time_ms,
int64_t send_time_ms,
uint16_t sequence_number,
size_t payload_size,
const PacedPacketInfo& pacing_info);
// Generates a frame of packets belonging to a stream at a given bitrate and
// with a given ssrc. The stream is pushed through a very simple simulated
// network, and is then given to the receive-side bandwidth estimator.
// Returns true if an over-use was seen, false otherwise.
// The StreamGenerator::updated() should be used to check for any changes in
// target bitrate after the call to this function.
bool GenerateAndProcessFrame(uint32_t ssrc, uint32_t bitrate_bps);
// Run the bandwidth estimator with a stream of |number_of_frames| frames, or
// until it reaches |target_bitrate|.
// Can for instance be used to run the estimator for some time to get it
// into a steady state.
uint32_t SteadyStateRun(uint32_t ssrc,
int number_of_frames,
uint32_t start_bitrate,
uint32_t min_bitrate,
uint32_t max_bitrate,
uint32_t target_bitrate);
void TestTimestampGroupingTestHelper();
void TestWrappingHelper(int silence_time_s);
void InitialBehaviorTestHelper(uint32_t expected_converge_bitrate);
void RateIncreaseReorderingTestHelper(uint32_t expected_bitrate);
void RateIncreaseRtpTimestampsTestHelper(int expected_iterations);
void CapacityDropTestHelper(int number_of_streams,
bool wrap_time_stamp,
uint32_t expected_bitrate_drop_delta,
int64_t receiver_clock_offset_change_ms);
static const uint32_t kDefaultSsrc;
SimulatedClock clock_; // Time at the receiver.
test::TestBitrateObserver bitrate_observer_;
std::unique_ptr<AcknowledgedBitrateEstimator> acknowledged_bitrate_estimator_;
std::unique_ptr<DelayBasedBwe> bitrate_estimator_;
std::unique_ptr<test::StreamGenerator> stream_generator_;
int64_t arrival_time_offset_ms_;
bool first_update_;
RTC_DISALLOW_COPY_AND_ASSIGN(DelayBasedBweTest);
};
} // namespace webrtc_cc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_UNITTEST_HELPER_H_

View File

@ -0,0 +1,39 @@
/*
* 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_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_
#include <stdint.h>
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "rtc_base/constructormagic.h"
namespace webrtc {
namespace webrtc_cc {
class DelayIncreaseDetectorInterface {
public:
DelayIncreaseDetectorInterface() {}
virtual ~DelayIncreaseDetectorInterface() {}
// Update the detector with a new sample. The deltas should represent deltas
// between timestamp groups as defined by the InterArrival class.
virtual void Update(double recv_delta_ms,
double send_delta_ms,
int64_t arrival_time_ms) = 0;
virtual BandwidthUsage State() const = 0;
RTC_DISALLOW_COPY_AND_ASSIGN(DelayIncreaseDetectorInterface);
};
} // namespace webrtc_cc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_

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/goog_cc/include/goog_cc_factory.h"
#include "modules/congestion_controller/goog_cc/goog_cc_network_control.h"
#include "rtc_base/ptr_util.h"
namespace webrtc {
GoogCcNetworkControllerFactory::GoogCcNetworkControllerFactory(
RtcEventLog* event_log)
: event_log_(event_log) {}
NetworkControllerInterface::uptr GoogCcNetworkControllerFactory::Create(
NetworkControllerObserver* observer) {
return rtc::MakeUnique<webrtc_cc::GoogCcNetworkController>(event_log_,
observer);
}
TimeDelta GoogCcNetworkControllerFactory::GetProcessInterval() const {
const int64_t kUpdateIntervalMs = 25;
return TimeDelta::ms(kUpdateIntervalMs);
}
} // namespace webrtc

View File

@ -0,0 +1,413 @@
/*
* 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/goog_cc/goog_cc_network_control.h"
#include <algorithm>
#include <functional>
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
#include "modules/congestion_controller/goog_cc/alr_detector.h"
#include "modules/congestion_controller/goog_cc/include/goog_cc_factory.h"
#include "modules/congestion_controller/goog_cc/probe_controller.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/checks.h"
#include "rtc_base/format_macros.h"
#include "rtc_base/logging.h"
#include "rtc_base/ptr_util.h"
#include "rtc_base/timeutils.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace webrtc_cc {
namespace {
const char kCwndExperiment[] = "WebRTC-CwndExperiment";
const int64_t kDefaultAcceptedQueueMs = 250;
// Pacing-rate relative to our target send rate.
// Multiplicative factor that is applied to the target bitrate to calculate
// the number of bytes that can be transmitted per interval.
// Increasing this factor will result in lower delays in cases of bitrate
// overshoots from the encoder.
const float kDefaultPaceMultiplier = 2.5f;
bool CwndExperimentEnabled() {
std::string experiment_string =
webrtc::field_trial::FindFullName(kCwndExperiment);
// The experiment is enabled iff the field trial string begins with "Enabled".
return experiment_string.find("Enabled") == 0;
}
bool ReadCwndExperimentParameter(int64_t* accepted_queue_ms) {
RTC_DCHECK(accepted_queue_ms);
std::string experiment_string =
webrtc::field_trial::FindFullName(kCwndExperiment);
int parsed_values =
sscanf(experiment_string.c_str(), "Enabled-%" PRId64, accepted_queue_ms);
if (parsed_values == 1) {
RTC_CHECK_GE(*accepted_queue_ms, 0)
<< "Accepted must be greater than or equal to 0.";
return true;
}
return false;
}
// Makes sure that the bitrate and the min, max values are in valid range.
static void ClampBitrates(int64_t* bitrate_bps,
int64_t* min_bitrate_bps,
int64_t* max_bitrate_bps) {
// TODO(holmer): We should make sure the default bitrates are set to 10 kbps,
// and that we don't try to set the min bitrate to 0 from any applications.
// The congestion controller should allow a min bitrate of 0.
if (*min_bitrate_bps < congestion_controller::GetMinBitrateBps())
*min_bitrate_bps = congestion_controller::GetMinBitrateBps();
if (*max_bitrate_bps > 0)
*max_bitrate_bps = std::max(*min_bitrate_bps, *max_bitrate_bps);
if (*bitrate_bps > 0)
*bitrate_bps = std::max(*min_bitrate_bps, *bitrate_bps);
}
std::vector<PacketFeedback> ReceivedPacketsFeedbackAsRtp(
const TransportPacketsFeedback report) {
std::vector<PacketFeedback> packet_feedback_vector;
for (auto& fb : report.PacketsWithFeedback()) {
if (fb.receive_time.IsFinite()) {
PacketFeedback pf(fb.receive_time.ms(), 0);
pf.creation_time_ms = report.feedback_time.ms();
if (fb.sent_packet.has_value()) {
pf.payload_size = fb.sent_packet->size.bytes();
pf.pacing_info = fb.sent_packet->pacing_info;
pf.send_time_ms = fb.sent_packet->send_time.ms();
} else {
pf.send_time_ms = PacketFeedback::kNoSendTime;
}
packet_feedback_vector.push_back(pf);
}
}
return packet_feedback_vector;
}
} // namespace
GoogCcNetworkController::GoogCcNetworkController(
RtcEventLog* event_log,
NetworkControllerObserver* observer)
: event_log_(event_log),
observer_(observer),
probe_controller_(new ProbeController(observer_)),
bandwidth_estimation_(
rtc::MakeUnique<SendSideBandwidthEstimation>(event_log_)),
alr_detector_(rtc::MakeUnique<AlrDetector>()),
delay_based_bwe_(new DelayBasedBwe(event_log_)),
acknowledged_bitrate_estimator_(
rtc::MakeUnique<AcknowledgedBitrateEstimator>()),
pacing_factor_(kDefaultPaceMultiplier),
min_pacing_rate_(DataRate::Zero()),
max_padding_rate_(DataRate::Zero()),
in_cwnd_experiment_(CwndExperimentEnabled()),
accepted_queue_ms_(kDefaultAcceptedQueueMs) {
delay_based_bwe_->SetMinBitrate(congestion_controller::GetMinBitrateBps());
if (in_cwnd_experiment_ &&
!ReadCwndExperimentParameter(&accepted_queue_ms_)) {
RTC_LOG(LS_WARNING) << "Failed to parse parameters for CwndExperiment "
"from field trial string. Experiment disabled.";
in_cwnd_experiment_ = false;
}
}
GoogCcNetworkController::~GoogCcNetworkController() {}
void GoogCcNetworkController::OnNetworkAvailability(NetworkAvailability msg) {
probe_controller_->OnNetworkAvailability(msg);
}
void GoogCcNetworkController::OnNetworkRouteChange(NetworkRouteChange msg) {
int64_t min_bitrate_bps = msg.constraints.min_data_rate.bps();
int64_t max_bitrate_bps = -1;
int64_t start_bitrate_bps = -1;
if (msg.constraints.max_data_rate.IsFinite())
max_bitrate_bps = msg.constraints.max_data_rate.bps();
if (msg.constraints.starting_rate.IsFinite())
start_bitrate_bps = msg.constraints.starting_rate.bps();
ClampBitrates(&start_bitrate_bps, &min_bitrate_bps, &max_bitrate_bps);
bandwidth_estimation_ =
rtc::MakeUnique<SendSideBandwidthEstimation>(event_log_);
bandwidth_estimation_->SetBitrates(start_bitrate_bps, min_bitrate_bps,
max_bitrate_bps);
delay_based_bwe_.reset(new DelayBasedBwe(event_log_));
acknowledged_bitrate_estimator_.reset(new AcknowledgedBitrateEstimator());
delay_based_bwe_->SetStartBitrate(start_bitrate_bps);
delay_based_bwe_->SetMinBitrate(min_bitrate_bps);
probe_controller_->Reset(msg.at_time.ms());
probe_controller_->SetBitrates(min_bitrate_bps, start_bitrate_bps,
max_bitrate_bps, msg.at_time.ms());
MaybeTriggerOnNetworkChanged(msg.at_time);
}
void GoogCcNetworkController::OnProcessInterval(ProcessInterval msg) {
bandwidth_estimation_->UpdateEstimate(msg.at_time.ms());
rtc::Optional<int64_t> start_time_ms =
alr_detector_->GetApplicationLimitedRegionStartTime();
probe_controller_->SetAlrStartTimeMs(start_time_ms);
probe_controller_->Process(msg.at_time.ms());
MaybeTriggerOnNetworkChanged(msg.at_time);
}
void GoogCcNetworkController::OnRemoteBitrateReport(RemoteBitrateReport msg) {
bandwidth_estimation_->UpdateReceiverEstimate(msg.receive_time.ms(),
msg.bandwidth.bps());
BWE_TEST_LOGGING_PLOT(1, "REMB_kbps", msg.receive_time.ms(),
msg.bandwidth.bps() / 1000);
}
void GoogCcNetworkController::OnRoundTripTimeUpdate(RoundTripTimeUpdate msg) {
if (msg.smoothed) {
delay_based_bwe_->OnRttUpdate(msg.round_trip_time.ms());
} else {
bandwidth_estimation_->UpdateRtt(msg.round_trip_time.ms(),
msg.receive_time.ms());
}
}
void GoogCcNetworkController::OnSentPacket(SentPacket sent_packet) {
alr_detector_->OnBytesSent(sent_packet.size.bytes(),
sent_packet.send_time.ms());
}
void GoogCcNetworkController::OnStreamsConfig(StreamsConfig msg) {
probe_controller_->EnablePeriodicAlrProbing(msg.requests_alr_probing);
bool pacing_changed = false;
if (msg.pacing_factor && *msg.pacing_factor != pacing_factor_) {
pacing_factor_ = *msg.pacing_factor;
pacing_changed = true;
}
if (msg.min_pacing_rate && *msg.min_pacing_rate != min_pacing_rate_) {
min_pacing_rate_ = *msg.min_pacing_rate;
pacing_changed = true;
}
if (msg.max_padding_rate && *msg.max_padding_rate != max_padding_rate_) {
max_padding_rate_ = *msg.max_padding_rate;
pacing_changed = true;
}
if (pacing_changed)
UpdatePacingRates(msg.at_time);
}
void GoogCcNetworkController::OnTargetRateConstraints(
TargetRateConstraints constraints) {
int64_t min_bitrate_bps = constraints.min_data_rate.bps();
int64_t max_bitrate_bps = -1;
int64_t start_bitrate_bps = -1;
if (constraints.max_data_rate.IsFinite())
max_bitrate_bps = constraints.max_data_rate.bps();
if (constraints.starting_rate.IsFinite())
start_bitrate_bps = constraints.starting_rate.bps();
ClampBitrates(&start_bitrate_bps, &min_bitrate_bps, &max_bitrate_bps);
probe_controller_->SetBitrates(min_bitrate_bps, start_bitrate_bps,
max_bitrate_bps, constraints.at_time.ms());
bandwidth_estimation_->SetBitrates(start_bitrate_bps, min_bitrate_bps,
max_bitrate_bps);
if (start_bitrate_bps > 0)
delay_based_bwe_->SetStartBitrate(start_bitrate_bps);
delay_based_bwe_->SetMinBitrate(min_bitrate_bps);
MaybeTriggerOnNetworkChanged(constraints.at_time);
}
void GoogCcNetworkController::OnTransportLossReport(TransportLossReport msg) {
int64_t total_packets_delta =
msg.packets_received_delta + msg.packets_lost_delta;
bandwidth_estimation_->UpdatePacketsLost(
msg.packets_lost_delta, total_packets_delta, msg.receive_time.ms());
}
void GoogCcNetworkController::OnTransportPacketsFeedback(
TransportPacketsFeedback report) {
int64_t feedback_rtt = -1;
for (const auto& packet_feedback : report.PacketsWithFeedback()) {
if (packet_feedback.sent_packet.has_value() &&
packet_feedback.receive_time.IsFinite()) {
int64_t rtt = report.feedback_time.ms() -
packet_feedback.sent_packet->send_time.ms();
// max() is used to account for feedback being delayed by the
// receiver.
feedback_rtt = std::max(rtt, feedback_rtt);
}
}
if (feedback_rtt > -1) {
feedback_rtts_.push_back(feedback_rtt);
const size_t kFeedbackRttWindow = 32;
if (feedback_rtts_.size() > kFeedbackRttWindow)
feedback_rtts_.pop_front();
min_feedback_rtt_ms_.emplace(
*std::min_element(feedback_rtts_.begin(), feedback_rtts_.end()));
}
std::vector<PacketFeedback> received_feedback_vector =
ReceivedPacketsFeedbackAsRtp(report);
rtc::Optional<int64_t> alr_start_time =
alr_detector_->GetApplicationLimitedRegionStartTime();
if (previously_in_alr && !alr_start_time.has_value()) {
int64_t now_ms = report.feedback_time.ms();
acknowledged_bitrate_estimator_->SetAlrEndedTimeMs(now_ms);
probe_controller_->SetAlrEndedTimeMs(now_ms);
}
previously_in_alr = alr_start_time.has_value();
acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(
received_feedback_vector);
DelayBasedBwe::Result result;
result = delay_based_bwe_->IncomingPacketFeedbackVector(
received_feedback_vector, acknowledged_bitrate_estimator_->bitrate_bps(),
report.feedback_time.ms());
if (result.updated) {
if (result.probe) {
bandwidth_estimation_->SetSendBitrate(result.target_bitrate_bps);
}
// Since SetSendBitrate now resets the delay-based estimate, we have to call
// UpdateDelayBasedEstimate after SetSendBitrate.
bandwidth_estimation_->UpdateDelayBasedEstimate(report.feedback_time.ms(),
result.target_bitrate_bps);
// Update the estimate in the ProbeController, in case we want to probe.
MaybeTriggerOnNetworkChanged(report.feedback_time);
}
if (result.recovered_from_overuse) {
probe_controller_->SetAlrStartTimeMs(alr_start_time);
probe_controller_->RequestProbe(report.feedback_time.ms());
}
MaybeUpdateCongestionWindow();
}
void GoogCcNetworkController::MaybeUpdateCongestionWindow() {
if (!in_cwnd_experiment_)
return;
// No valid RTT. Could be because send-side BWE isn't used, in which case
// we don't try to limit the outstanding packets.
if (!min_feedback_rtt_ms_)
return;
if (!last_estimate_.has_value())
return;
const DataSize kMinCwnd = DataSize::bytes(2 * 1500);
TimeDelta time_window =
TimeDelta::ms(*min_feedback_rtt_ms_ + accepted_queue_ms_);
DataSize data_window = last_estimate_->bandwidth * time_window;
CongestionWindow msg;
msg.enabled = true;
msg.data_window = std::max(kMinCwnd, data_window);
observer_->OnCongestionWindow(msg);
RTC_LOG(LS_INFO) << "Feedback rtt: " << *min_feedback_rtt_ms_
<< " Bitrate: " << last_estimate_->bandwidth.bps();
}
void GoogCcNetworkController::MaybeTriggerOnNetworkChanged(Timestamp at_time) {
int32_t estimated_bitrate_bps;
uint8_t fraction_loss;
int64_t rtt_ms;
bool estimate_changed = GetNetworkParameters(
&estimated_bitrate_bps, &fraction_loss, &rtt_ms, at_time);
if (estimate_changed) {
TimeDelta bwe_period =
TimeDelta::ms(delay_based_bwe_->GetExpectedBwePeriodMs());
NetworkEstimate new_estimate;
new_estimate.at_time = at_time;
new_estimate.round_trip_time = TimeDelta::ms(rtt_ms);
new_estimate.bandwidth = DataRate::bps(estimated_bitrate_bps);
new_estimate.loss_rate_ratio = fraction_loss / 255.0f;
new_estimate.bwe_period = bwe_period;
new_estimate.changed = true;
last_estimate_ = new_estimate;
OnNetworkEstimate(new_estimate);
}
}
bool GoogCcNetworkController::GetNetworkParameters(
int32_t* estimated_bitrate_bps,
uint8_t* fraction_loss,
int64_t* rtt_ms,
Timestamp at_time) {
bandwidth_estimation_->CurrentEstimate(estimated_bitrate_bps, fraction_loss,
rtt_ms);
*estimated_bitrate_bps = std::max<int32_t>(
*estimated_bitrate_bps, bandwidth_estimation_->GetMinBitrate());
bool estimate_changed = false;
if ((*estimated_bitrate_bps != last_estimated_bitrate_bps_) ||
(*fraction_loss != last_estimated_fraction_loss_) ||
(*rtt_ms != last_estimated_rtt_ms_)) {
last_estimated_bitrate_bps_ = *estimated_bitrate_bps;
last_estimated_fraction_loss_ = *fraction_loss;
last_estimated_rtt_ms_ = *rtt_ms;
estimate_changed = true;
}
BWE_TEST_LOGGING_PLOT(1, "fraction_loss_%", at_time.ms(),
(*fraction_loss * 100) / 256);
BWE_TEST_LOGGING_PLOT(1, "rtt_ms", at_time.ms(), *rtt_ms);
BWE_TEST_LOGGING_PLOT(1, "Target_bitrate_kbps", at_time.ms(),
*estimated_bitrate_bps / 1000);
return estimate_changed;
}
void GoogCcNetworkController::OnNetworkEstimate(NetworkEstimate estimate) {
if (!estimate.changed)
return;
UpdatePacingRates(estimate.at_time);
alr_detector_->SetEstimatedBitrate(estimate.bandwidth.bps());
probe_controller_->SetEstimatedBitrate(estimate.bandwidth.bps(),
estimate.at_time.ms());
TargetTransferRate target_rate;
target_rate.at_time = estimate.at_time;
// Set the target rate to the full estimated bandwidth since the estimation
// for legacy reasons includes target rate constraints.
target_rate.target_rate = estimate.bandwidth;
target_rate.network_estimate = estimate;
observer_->OnTargetTransferRate(target_rate);
}
void GoogCcNetworkController::UpdatePacingRates(Timestamp at_time) {
if (!last_estimate_)
return;
DataRate pacing_rate =
std::max(min_pacing_rate_, last_estimate_->bandwidth) * pacing_factor_;
DataRate padding_rate =
std::min(max_padding_rate_, last_estimate_->bandwidth);
PacerConfig msg;
msg.at_time = at_time;
msg.time_window = TimeDelta::s(1);
msg.data_window = pacing_rate * msg.time_window;
msg.pad_window = padding_rate * msg.time_window;
observer_->OnPacerConfig(msg);
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,94 @@
/*
* 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_GOOG_CC_GOOG_CC_NETWORK_CONTROL_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_GOOG_CC_NETWORK_CONTROL_H_
#include <stdint.h>
#include <deque>
#include <memory>
#include <vector>
#include "api/optional.h"
#include "logging/rtc_event_log/rtc_event_log.h"
#include "modules/bitrate_controller/send_side_bandwidth_estimation.h"
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
#include "modules/congestion_controller/goog_cc/alr_detector.h"
#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
#include "modules/congestion_controller/goog_cc/probe_controller.h"
#include "modules/congestion_controller/network_control/include/network_control.h"
#include "rtc_base/constructormagic.h"
namespace webrtc {
namespace webrtc_cc {
class GoogCcNetworkController : public NetworkControllerInterface {
public:
GoogCcNetworkController(RtcEventLog* event_log,
NetworkControllerObserver* observer);
~GoogCcNetworkController() override;
// NetworkControllerInterface
void OnNetworkAvailability(NetworkAvailability msg) override;
void OnNetworkRouteChange(NetworkRouteChange msg) override;
void OnProcessInterval(ProcessInterval msg) override;
void OnRemoteBitrateReport(RemoteBitrateReport msg) override;
void OnRoundTripTimeUpdate(RoundTripTimeUpdate msg) override;
void OnSentPacket(SentPacket msg) override;
void OnStreamsConfig(StreamsConfig msg) override;
void OnTargetRateConstraints(TargetRateConstraints msg) override;
void OnTransportLossReport(TransportLossReport msg) override;
void OnTransportPacketsFeedback(TransportPacketsFeedback msg) override;
private:
void MaybeUpdateCongestionWindow();
void MaybeTriggerOnNetworkChanged(Timestamp at_time);
bool GetNetworkParameters(int32_t* estimated_bitrate_bps,
uint8_t* fraction_loss,
int64_t* rtt_ms,
Timestamp at_time);
void OnNetworkEstimate(NetworkEstimate msg);
void UpdatePacingRates(Timestamp at_time);
RtcEventLog* const event_log_;
NetworkControllerObserver* const observer_;
const std::unique_ptr<ProbeController> probe_controller_;
std::unique_ptr<SendSideBandwidthEstimation> bandwidth_estimation_;
std::unique_ptr<AlrDetector> alr_detector_;
std::unique_ptr<DelayBasedBwe> delay_based_bwe_;
std::unique_ptr<AcknowledgedBitrateEstimator> acknowledged_bitrate_estimator_;
std::deque<int64_t> feedback_rtts_;
rtc::Optional<int64_t> min_feedback_rtt_ms_;
rtc::Optional<NetworkEstimate> last_estimate_;
rtc::Optional<TargetTransferRate> last_target_rate_;
int32_t last_estimated_bitrate_bps_ = 0;
uint8_t last_estimated_fraction_loss_ = 0;
int64_t last_estimated_rtt_ms_ = 0;
double pacing_factor_;
DataRate min_pacing_rate_;
DataRate max_padding_rate_;
bool in_cwnd_experiment_;
int64_t accepted_queue_ms_;
bool previously_in_alr = false;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(GoogCcNetworkController);
};
} // namespace webrtc_cc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_GOOG_CC_NETWORK_CONTROL_H_

View File

@ -0,0 +1,32 @@
/*
* 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_GOOG_CC_INCLUDE_GOOG_CC_FACTORY_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_INCLUDE_GOOG_CC_FACTORY_H_
#include "modules/congestion_controller/network_control/include/network_control.h"
namespace webrtc {
class Clock;
class RtcEventLog;
class GoogCcNetworkControllerFactory
: public NetworkControllerFactoryInterface {
public:
explicit GoogCcNetworkControllerFactory(RtcEventLog*);
NetworkControllerInterface::uptr Create(
NetworkControllerObserver* observer) override;
TimeDelta GetProcessInterval() const override;
private:
RtcEventLog* const event_log_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_INCLUDE_GOOG_CC_FACTORY_H_

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2016 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/goog_cc/median_slope_estimator.h"
#include <algorithm>
#include <vector>
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace webrtc_cc {
constexpr unsigned int kDeltaCounterMax = 1000;
MedianSlopeEstimator::MedianSlopeEstimator(size_t window_size,
double threshold_gain)
: window_size_(window_size),
threshold_gain_(threshold_gain),
num_of_deltas_(0),
accumulated_delay_(0),
delay_hist_(),
median_filter_(0.5),
trendline_(0) {}
MedianSlopeEstimator::~MedianSlopeEstimator() {}
MedianSlopeEstimator::DelayInfo::DelayInfo(int64_t time,
double delay,
size_t slope_count)
: time(time), delay(delay) {
slopes.reserve(slope_count);
}
MedianSlopeEstimator::DelayInfo::~DelayInfo() = default;
void MedianSlopeEstimator::Update(double recv_delta_ms,
double send_delta_ms,
int64_t arrival_time_ms) {
const double delta_ms = recv_delta_ms - send_delta_ms;
++num_of_deltas_;
if (num_of_deltas_ > kDeltaCounterMax)
num_of_deltas_ = kDeltaCounterMax;
accumulated_delay_ += delta_ms;
BWE_TEST_LOGGING_PLOT(1, "accumulated_delay_ms", arrival_time_ms,
accumulated_delay_);
// If the window is full, remove the |window_size_| - 1 slopes that belong to
// the oldest point.
if (delay_hist_.size() == window_size_) {
for (double slope : delay_hist_.front().slopes) {
const bool success = median_filter_.Erase(slope);
RTC_CHECK(success);
}
delay_hist_.pop_front();
}
// Add |window_size_| - 1 new slopes.
for (auto& old_delay : delay_hist_) {
if (arrival_time_ms - old_delay.time != 0) {
// The C99 standard explicitly states that casts and assignments must
// perform the associated conversions. This means that |slope| will be
// a 64-bit double even if the division is computed using, e.g., 80-bit
// extended precision. I believe this also holds in C++ even though the
// C++11 standard isn't as explicit. Furthermore, there are good reasons
// to believe that compilers couldn't perform optimizations that break
// this assumption even if they wanted to.
double slope = (accumulated_delay_ - old_delay.delay) /
static_cast<double>(arrival_time_ms - old_delay.time);
median_filter_.Insert(slope);
// We want to avoid issues with different rounding mode / precision
// which we might get if we recomputed the slope when we remove it.
old_delay.slopes.push_back(slope);
}
}
delay_hist_.emplace_back(arrival_time_ms, accumulated_delay_,
window_size_ - 1);
// Recompute the median slope.
if (delay_hist_.size() == window_size_)
trendline_ = median_filter_.GetPercentileValue();
BWE_TEST_LOGGING_PLOT(1, "trendline_slope", arrival_time_ms, trendline_);
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2016 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_GOOG_CC_MEDIAN_SLOPE_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_MEDIAN_SLOPE_ESTIMATOR_H_
#include <stddef.h>
#include <stdint.h>
#include <deque>
#include <vector>
#include "rtc_base/constructormagic.h"
#include "rtc_base/numerics/percentile_filter.h"
namespace webrtc {
namespace webrtc_cc {
class MedianSlopeEstimator {
public:
// |window_size| is the number of points required to compute a trend line.
// |threshold_gain| is used to scale the trendline slope for comparison to
// the old threshold. Once the old estimator has been removed (or the
// thresholds been merged into the estimators), we can just set the
// threshold instead of setting a gain.
MedianSlopeEstimator(size_t window_size, double threshold_gain);
~MedianSlopeEstimator();
// Update the estimator with a new sample. The deltas should represent deltas
// between timestamp groups as defined by the InterArrival class.
void Update(double recv_delta_ms,
double send_delta_ms,
int64_t arrival_time_ms);
// Returns the estimated trend k multiplied by some gain.
// 0 < k < 1 -> the delay increases, queues are filling up
// k == 0 -> the delay does not change
// k < 0 -> the delay decreases, queues are being emptied
double trendline_slope() const { return trendline_ * threshold_gain_; }
// Returns the number of deltas which the current estimator state is based on.
unsigned int num_of_deltas() const { return num_of_deltas_; }
private:
struct DelayInfo {
DelayInfo(int64_t time, double delay, size_t slope_count);
~DelayInfo();
int64_t time;
double delay;
std::vector<double> slopes;
};
// Parameters.
const size_t window_size_;
const double threshold_gain_;
// Used by the existing threshold.
unsigned int num_of_deltas_;
// Theil-Sen robust line fitting
double accumulated_delay_;
std::deque<DelayInfo> delay_hist_;
PercentileFilter<double> median_filter_;
double trendline_;
RTC_DISALLOW_COPY_AND_ASSIGN(MedianSlopeEstimator);
};
} // namespace webrtc_cc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_MEDIAN_SLOPE_ESTIMATOR_H_

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2016 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/goog_cc/median_slope_estimator.h"
#include "rtc_base/random.h"
#include "test/gtest.h"
namespace webrtc {
namespace webrtc_cc {
namespace {
constexpr size_t kWindowSize = 20;
constexpr double kGain = 1;
constexpr int64_t kAvgTimeBetweenPackets = 10;
constexpr size_t kPacketCount = 2 * kWindowSize + 1;
void TestEstimator(double slope, double jitter_stddev, double tolerance) {
MedianSlopeEstimator estimator(kWindowSize, kGain);
Random random(0x1234567);
int64_t send_times[kPacketCount];
int64_t recv_times[kPacketCount];
int64_t send_start_time = random.Rand(1000000);
int64_t recv_start_time = random.Rand(1000000);
for (size_t i = 0; i < kPacketCount; ++i) {
send_times[i] = send_start_time + i * kAvgTimeBetweenPackets;
double latency = i * kAvgTimeBetweenPackets / (1 - slope);
double jitter = random.Gaussian(0, jitter_stddev);
recv_times[i] = recv_start_time + latency + jitter;
}
for (size_t i = 1; i < kPacketCount; ++i) {
double recv_delta = recv_times[i] - recv_times[i - 1];
double send_delta = send_times[i] - send_times[i - 1];
estimator.Update(recv_delta, send_delta, recv_times[i]);
if (i < kWindowSize)
EXPECT_NEAR(estimator.trendline_slope(), 0, 0.001);
else
EXPECT_NEAR(estimator.trendline_slope(), slope, tolerance);
}
}
} // namespace
TEST(MedianSlopeEstimator, PerfectLineSlopeOneHalf) {
TestEstimator(0.5, 0, 0.001);
}
TEST(MedianSlopeEstimator, PerfectLineSlopeMinusOne) {
TestEstimator(-1, 0, 0.001);
}
TEST(MedianSlopeEstimator, PerfectLineSlopeZero) {
TestEstimator(0, 0, 0.001);
}
TEST(MedianSlopeEstimator, JitteryLineSlopeOneHalf) {
TestEstimator(0.5, kAvgTimeBetweenPackets / 3.0, 0.01);
}
TEST(MedianSlopeEstimator, JitteryLineSlopeMinusOne) {
TestEstimator(-1, kAvgTimeBetweenPackets / 3.0, 0.05);
}
TEST(MedianSlopeEstimator, JitteryLineSlopeZero) {
TestEstimator(0, kAvgTimeBetweenPackets / 3.0, 0.02);
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,191 @@
/*
* Copyright (c) 2016 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/goog_cc/probe_bitrate_estimator.h"
#include <algorithm>
#include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h"
#include "logging/rtc_event_log/events/rtc_event_probe_result_success.h"
#include "logging/rtc_event_log/rtc_event_log.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/ptr_util.h"
namespace {
// The minumum number of probes we need to receive feedback about in percent
// in order to have a valid estimate.
constexpr int kMinReceivedProbesPercent = 80;
// The minumum number of bytes we need to receive feedback about in percent
// in order to have a valid estimate.
constexpr int kMinReceivedBytesPercent = 80;
// The maximum |receive rate| / |send rate| ratio for a valid estimate.
constexpr float kMaxValidRatio = 2.0f;
// The minimum |receive rate| / |send rate| ratio assuming that the link is
// not saturated, i.e. we assume that we will receive at least
// kMinRatioForUnsaturatedLink * |send rate| if |send rate| is less than the
// link capacity.
constexpr float kMinRatioForUnsaturatedLink = 0.9f;
// The target utilization of the link. If we know true link capacity
// we'd like to send at 95% of that rate.
constexpr float kTargetUtilizationFraction = 0.95f;
// The maximum time period over which the cluster history is retained.
// This is also the maximum time period beyond which a probing burst is not
// expected to last.
constexpr int kMaxClusterHistoryMs = 1000;
// The maximum time interval between first and the last probe on a cluster
// on the sender side as well as the receive side.
constexpr int kMaxProbeIntervalMs = 1000;
} // namespace
namespace webrtc {
namespace webrtc_cc {
ProbeBitrateEstimator::ProbeBitrateEstimator(RtcEventLog* event_log)
: event_log_(event_log) {}
ProbeBitrateEstimator::~ProbeBitrateEstimator() = default;
int ProbeBitrateEstimator::HandleProbeAndEstimateBitrate(
const PacketFeedback& packet_feedback) {
int cluster_id = packet_feedback.pacing_info.probe_cluster_id;
RTC_DCHECK_NE(cluster_id, PacedPacketInfo::kNotAProbe);
EraseOldClusters(packet_feedback.arrival_time_ms - kMaxClusterHistoryMs);
int payload_size_bits =
rtc::dchecked_cast<int>(packet_feedback.payload_size * 8);
AggregatedCluster* cluster = &clusters_[cluster_id];
if (packet_feedback.send_time_ms < cluster->first_send_ms) {
cluster->first_send_ms = packet_feedback.send_time_ms;
}
if (packet_feedback.send_time_ms > cluster->last_send_ms) {
cluster->last_send_ms = packet_feedback.send_time_ms;
cluster->size_last_send = payload_size_bits;
}
if (packet_feedback.arrival_time_ms < cluster->first_receive_ms) {
cluster->first_receive_ms = packet_feedback.arrival_time_ms;
cluster->size_first_receive = payload_size_bits;
}
if (packet_feedback.arrival_time_ms > cluster->last_receive_ms) {
cluster->last_receive_ms = packet_feedback.arrival_time_ms;
}
cluster->size_total += payload_size_bits;
cluster->num_probes += 1;
RTC_DCHECK_GT(packet_feedback.pacing_info.probe_cluster_min_probes, 0);
RTC_DCHECK_GT(packet_feedback.pacing_info.probe_cluster_min_bytes, 0);
int min_probes = packet_feedback.pacing_info.probe_cluster_min_probes *
kMinReceivedProbesPercent / 100;
int min_bytes = packet_feedback.pacing_info.probe_cluster_min_bytes *
kMinReceivedBytesPercent / 100;
if (cluster->num_probes < min_probes || cluster->size_total < min_bytes * 8)
return -1;
float send_interval_ms = cluster->last_send_ms - cluster->first_send_ms;
float receive_interval_ms =
cluster->last_receive_ms - cluster->first_receive_ms;
if (send_interval_ms <= 0 || send_interval_ms > kMaxProbeIntervalMs ||
receive_interval_ms <= 0 || receive_interval_ms > kMaxProbeIntervalMs) {
RTC_LOG(LS_INFO) << "Probing unsuccessful, invalid send/receive interval"
<< " [cluster id: " << cluster_id
<< "] [send interval: " << send_interval_ms << " ms]"
<< " [receive interval: " << receive_interval_ms << " ms]";
if (event_log_) {
event_log_->Log(rtc::MakeUnique<RtcEventProbeResultFailure>(
cluster_id, ProbeFailureReason::kInvalidSendReceiveInterval));
}
return -1;
}
// Since the |send_interval_ms| does not include the time it takes to actually
// send the last packet the size of the last sent packet should not be
// included when calculating the send bitrate.
RTC_DCHECK_GT(cluster->size_total, cluster->size_last_send);
float send_size = cluster->size_total - cluster->size_last_send;
float send_bps = send_size / send_interval_ms * 1000;
// Since the |receive_interval_ms| does not include the time it takes to
// actually receive the first packet the size of the first received packet
// should not be included when calculating the receive bitrate.
RTC_DCHECK_GT(cluster->size_total, cluster->size_first_receive);
float receive_size = cluster->size_total - cluster->size_first_receive;
float receive_bps = receive_size / receive_interval_ms * 1000;
float ratio = receive_bps / send_bps;
if (ratio > kMaxValidRatio) {
RTC_LOG(LS_INFO) << "Probing unsuccessful, receive/send ratio too high"
<< " [cluster id: " << cluster_id
<< "] [send: " << send_size << " bytes / "
<< send_interval_ms << " ms = " << send_bps / 1000
<< " kb/s]"
<< " [receive: " << receive_size << " bytes / "
<< receive_interval_ms << " ms = " << receive_bps / 1000
<< " kb/s]"
<< " [ratio: " << receive_bps / 1000 << " / "
<< send_bps / 1000 << " = " << ratio
<< " > kMaxValidRatio (" << kMaxValidRatio << ")]";
if (event_log_) {
event_log_->Log(rtc::MakeUnique<RtcEventProbeResultFailure>(
cluster_id, ProbeFailureReason::kInvalidSendReceiveRatio));
}
return -1;
}
RTC_LOG(LS_INFO) << "Probing successful"
<< " [cluster id: " << cluster_id << "] [send: " << send_size
<< " bytes / " << send_interval_ms
<< " ms = " << send_bps / 1000 << " kb/s]"
<< " [receive: " << receive_size << " bytes / "
<< receive_interval_ms << " ms = " << receive_bps / 1000
<< " kb/s]";
float res = std::min(send_bps, receive_bps);
// If we're receiving at significantly lower bitrate than we were sending at,
// it suggests that we've found the true capacity of the link. In this case,
// set the target bitrate slightly lower to not immediately overuse.
if (receive_bps < kMinRatioForUnsaturatedLink * send_bps) {
RTC_DCHECK_GT(send_bps, receive_bps);
res = kTargetUtilizationFraction * receive_bps;
}
if (event_log_) {
event_log_->Log(
rtc::MakeUnique<RtcEventProbeResultSuccess>(cluster_id, res));
}
estimated_bitrate_bps_ = res;
return *estimated_bitrate_bps_;
}
rtc::Optional<int>
ProbeBitrateEstimator::FetchAndResetLastEstimatedBitrateBps() {
rtc::Optional<int> estimated_bitrate_bps = estimated_bitrate_bps_;
estimated_bitrate_bps_.reset();
return estimated_bitrate_bps;
}
void ProbeBitrateEstimator::EraseOldClusters(int64_t timestamp_ms) {
for (auto it = clusters_.begin(); it != clusters_.end();) {
if (it->second.last_receive_ms < timestamp_ms) {
it = clusters_.erase(it);
} else {
++it;
}
}
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2016 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_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_
#include <limits>
#include <map>
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
namespace webrtc {
class RtcEventLog;
namespace webrtc_cc {
class ProbeBitrateEstimator {
public:
explicit ProbeBitrateEstimator(RtcEventLog* event_log);
~ProbeBitrateEstimator();
// Should be called for every probe packet we receive feedback about.
// Returns the estimated bitrate if the probe completes a valid cluster.
int HandleProbeAndEstimateBitrate(const PacketFeedback& packet_feedback);
rtc::Optional<int> FetchAndResetLastEstimatedBitrateBps();
private:
struct AggregatedCluster {
int num_probes = 0;
int64_t first_send_ms = std::numeric_limits<int64_t>::max();
int64_t last_send_ms = 0;
int64_t first_receive_ms = std::numeric_limits<int64_t>::max();
int64_t last_receive_ms = 0;
int size_last_send = 0;
int size_first_receive = 0;
int size_total = 0;
};
// Erases old cluster data that was seen before |timestamp_ms|.
void EraseOldClusters(int64_t timestamp_ms);
std::map<int, AggregatedCluster> clusters_;
RtcEventLog* const event_log_;
rtc::Optional<int> estimated_bitrate_bps_;
};
} // namespace webrtc_cc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_

View File

@ -0,0 +1,223 @@
/*
* Copyright (c) 2016 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/goog_cc/probe_bitrate_estimator.h"
#include <utility>
#include <vector>
#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace webrtc_cc {
namespace {
constexpr int kInvalidBitrate = -1;
constexpr int kDefaultMinProbes = 5;
constexpr int kDefaultMinBytes = 5000;
constexpr float kTargetUtilizationFraction = 0.95f;
} // anonymous namespace
class TestProbeBitrateEstimator : public ::testing::Test {
public:
TestProbeBitrateEstimator() : probe_bitrate_estimator_(nullptr) {}
// TODO(philipel): Use PacedPacketInfo when ProbeBitrateEstimator is rewritten
// to use that information.
void AddPacketFeedback(int probe_cluster_id,
size_t size_bytes,
int64_t send_time_ms,
int64_t arrival_time_ms,
int min_probes = kDefaultMinProbes,
int min_bytes = kDefaultMinBytes) {
PacedPacketInfo pacing_info(probe_cluster_id, min_probes, min_bytes);
PacketFeedback packet_feedback(arrival_time_ms, send_time_ms, 0, size_bytes,
pacing_info);
measured_bps_ =
probe_bitrate_estimator_.HandleProbeAndEstimateBitrate(packet_feedback);
}
protected:
int measured_bps_ = kInvalidBitrate;
ProbeBitrateEstimator probe_bitrate_estimator_;
};
TEST_F(TestProbeBitrateEstimator, OneCluster) {
AddPacketFeedback(0, 1000, 0, 10);
AddPacketFeedback(0, 1000, 10, 20);
AddPacketFeedback(0, 1000, 20, 30);
AddPacketFeedback(0, 1000, 30, 40);
EXPECT_NEAR(measured_bps_, 800000, 10);
}
TEST_F(TestProbeBitrateEstimator, OneClusterTooFewProbes) {
AddPacketFeedback(0, 2000, 0, 10);
AddPacketFeedback(0, 2000, 10, 20);
AddPacketFeedback(0, 2000, 20, 30);
EXPECT_EQ(kInvalidBitrate, measured_bps_);
}
TEST_F(TestProbeBitrateEstimator, OneClusterTooFewBytes) {
const int kMinBytes = 6000;
AddPacketFeedback(0, 800, 0, 10, kDefaultMinProbes, kMinBytes);
AddPacketFeedback(0, 800, 10, 20, kDefaultMinProbes, kMinBytes);
AddPacketFeedback(0, 800, 20, 30, kDefaultMinProbes, kMinBytes);
AddPacketFeedback(0, 800, 30, 40, kDefaultMinProbes, kMinBytes);
AddPacketFeedback(0, 800, 40, 50, kDefaultMinProbes, kMinBytes);
EXPECT_EQ(kInvalidBitrate, measured_bps_);
}
TEST_F(TestProbeBitrateEstimator, SmallCluster) {
const int kMinBytes = 1000;
AddPacketFeedback(0, 150, 0, 10, kDefaultMinProbes, kMinBytes);
AddPacketFeedback(0, 150, 10, 20, kDefaultMinProbes, kMinBytes);
AddPacketFeedback(0, 150, 20, 30, kDefaultMinProbes, kMinBytes);
AddPacketFeedback(0, 150, 30, 40, kDefaultMinProbes, kMinBytes);
AddPacketFeedback(0, 150, 40, 50, kDefaultMinProbes, kMinBytes);
AddPacketFeedback(0, 150, 50, 60, kDefaultMinProbes, kMinBytes);
EXPECT_NEAR(measured_bps_, 120000, 10);
}
TEST_F(TestProbeBitrateEstimator, LargeCluster) {
const int kMinProbes = 30;
const int kMinBytes = 312500;
int64_t send_time = 0;
int64_t receive_time = 5;
for (int i = 0; i < 25; ++i) {
AddPacketFeedback(0, 12500, send_time, receive_time, kMinProbes, kMinBytes);
++send_time;
++receive_time;
}
EXPECT_NEAR(measured_bps_, 100000000, 10);
}
TEST_F(TestProbeBitrateEstimator, FastReceive) {
AddPacketFeedback(0, 1000, 0, 15);
AddPacketFeedback(0, 1000, 10, 30);
AddPacketFeedback(0, 1000, 20, 35);
AddPacketFeedback(0, 1000, 30, 40);
EXPECT_NEAR(measured_bps_, 800000, 10);
}
TEST_F(TestProbeBitrateEstimator, TooFastReceive) {
AddPacketFeedback(0, 1000, 0, 19);
AddPacketFeedback(0, 1000, 10, 22);
AddPacketFeedback(0, 1000, 20, 25);
AddPacketFeedback(0, 1000, 40, 27);
EXPECT_EQ(measured_bps_, kInvalidBitrate);
}
TEST_F(TestProbeBitrateEstimator, SlowReceive) {
AddPacketFeedback(0, 1000, 0, 10);
AddPacketFeedback(0, 1000, 10, 40);
AddPacketFeedback(0, 1000, 20, 70);
AddPacketFeedback(0, 1000, 30, 85);
// Expected send rate = 800 kbps, expected receive rate = 320 kbps.
EXPECT_NEAR(measured_bps_, kTargetUtilizationFraction * 320000, 10);
}
TEST_F(TestProbeBitrateEstimator, BurstReceive) {
AddPacketFeedback(0, 1000, 0, 50);
AddPacketFeedback(0, 1000, 10, 50);
AddPacketFeedback(0, 1000, 20, 50);
AddPacketFeedback(0, 1000, 40, 50);
EXPECT_EQ(measured_bps_, kInvalidBitrate);
}
TEST_F(TestProbeBitrateEstimator, MultipleClusters) {
AddPacketFeedback(0, 1000, 0, 10);
AddPacketFeedback(0, 1000, 10, 20);
AddPacketFeedback(0, 1000, 20, 30);
AddPacketFeedback(0, 1000, 40, 60);
// Expected send rate = 600 kbps, expected receive rate = 480 kbps.
EXPECT_NEAR(measured_bps_, kTargetUtilizationFraction * 480000, 10);
AddPacketFeedback(0, 1000, 50, 60);
// Expected send rate = 640 kbps, expected receive rate = 640 kbps.
EXPECT_NEAR(measured_bps_, 640000, 10);
AddPacketFeedback(1, 1000, 60, 70);
AddPacketFeedback(1, 1000, 65, 77);
AddPacketFeedback(1, 1000, 70, 84);
AddPacketFeedback(1, 1000, 75, 90);
// Expected send rate = 1600 kbps, expected receive rate = 1200 kbps.
EXPECT_NEAR(measured_bps_, kTargetUtilizationFraction * 1200000, 10);
}
TEST_F(TestProbeBitrateEstimator, IgnoreOldClusters) {
AddPacketFeedback(0, 1000, 0, 10);
AddPacketFeedback(0, 1000, 10, 20);
AddPacketFeedback(0, 1000, 20, 30);
AddPacketFeedback(1, 1000, 60, 70);
AddPacketFeedback(1, 1000, 65, 77);
AddPacketFeedback(1, 1000, 70, 84);
AddPacketFeedback(1, 1000, 75, 90);
// Expected send rate = 1600 kbps, expected receive rate = 1200 kbps.
EXPECT_NEAR(measured_bps_, kTargetUtilizationFraction * 1200000, 10);
// Coming in 6s later
AddPacketFeedback(0, 1000, 40 + 6000, 60 + 6000);
EXPECT_EQ(measured_bps_, kInvalidBitrate);
}
TEST_F(TestProbeBitrateEstimator, IgnoreSizeLastSendPacket) {
AddPacketFeedback(0, 1000, 0, 10);
AddPacketFeedback(0, 1000, 10, 20);
AddPacketFeedback(0, 1000, 20, 30);
AddPacketFeedback(0, 1000, 30, 40);
AddPacketFeedback(0, 1500, 40, 50);
// Expected send rate = 800 kbps, expected receive rate = 900 kbps.
EXPECT_NEAR(measured_bps_, 800000, 10);
}
TEST_F(TestProbeBitrateEstimator, IgnoreSizeFirstReceivePacket) {
AddPacketFeedback(0, 1500, 0, 10);
AddPacketFeedback(0, 1000, 10, 20);
AddPacketFeedback(0, 1000, 20, 30);
AddPacketFeedback(0, 1000, 30, 40);
// Expected send rate = 933 kbps, expected receive rate = 800 kbps.
EXPECT_NEAR(measured_bps_, kTargetUtilizationFraction * 800000, 10);
}
TEST_F(TestProbeBitrateEstimator, NoLastEstimatedBitrateBps) {
EXPECT_FALSE(probe_bitrate_estimator_.FetchAndResetLastEstimatedBitrateBps());
}
TEST_F(TestProbeBitrateEstimator, FetchLastEstimatedBitrateBps) {
AddPacketFeedback(0, 1000, 0, 10);
AddPacketFeedback(0, 1000, 10, 20);
AddPacketFeedback(0, 1000, 20, 30);
AddPacketFeedback(0, 1000, 30, 40);
auto estimated_bitrate_bps =
probe_bitrate_estimator_.FetchAndResetLastEstimatedBitrateBps();
EXPECT_TRUE(estimated_bitrate_bps);
EXPECT_NEAR(*estimated_bitrate_bps, 800000, 10);
EXPECT_FALSE(probe_bitrate_estimator_.FetchAndResetLastEstimatedBitrateBps());
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,310 @@
/*
* Copyright (c) 2016 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/goog_cc/probe_controller.h"
#include <algorithm>
#include <initializer_list>
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "system_wrappers/include/field_trial.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
namespace webrtc_cc {
namespace {
// The minimum number probing packets used.
constexpr int kMinProbePacketsSent = 5;
// The minimum probing duration in ms.
constexpr int kMinProbeDurationMs = 15;
// Maximum waiting time from the time of initiating probing to getting
// the measured results back.
constexpr int64_t kMaxWaitingTimeForProbingResultMs = 1000;
// Value of |min_bitrate_to_probe_further_bps_| that indicates
// further probing is disabled.
constexpr int kExponentialProbingDisabled = 0;
// Default probing bitrate limit. Applied only when the application didn't
// specify max bitrate.
constexpr int64_t kDefaultMaxProbingBitrateBps = 5000000;
// Interval between probes when ALR periodic probing is enabled.
constexpr int64_t kAlrPeriodicProbingIntervalMs = 5000;
// Minimum probe bitrate percentage to probe further for repeated probes,
// relative to the previous probe. For example, if 1Mbps probe results in
// 80kbps, then we'll probe again at 1.6Mbps. In that case second probe won't be
// sent if we get 600kbps from the first one.
constexpr int kRepeatedProbeMinPercentage = 70;
// If the bitrate drops to a factor |kBitrateDropThreshold| or lower
// and we recover within |kBitrateDropTimeoutMs|, then we'll send
// a probe at a fraction |kProbeFractionAfterDrop| of the original bitrate.
constexpr double kBitrateDropThreshold = 0.66;
constexpr int kBitrateDropTimeoutMs = 5000;
constexpr double kProbeFractionAfterDrop = 0.85;
// Timeout for probing after leaving ALR. If the bitrate drops significantly,
// (as determined by the delay based estimator) and we leave ALR, then we will
// send a probe if we recover within |kLeftAlrTimeoutMs| ms.
constexpr int kAlrEndedTimeoutMs = 3000;
// The expected uncertainty of probe result (as a fraction of the target probe
// This is a limit on how often probing can be done when there is a BW
// drop detected in ALR.
constexpr int64_t kMinTimeBetweenAlrProbesMs = 5000;
// bitrate). Used to avoid probing if the probe bitrate is close to our current
// estimate.
constexpr double kProbeUncertainty = 0.05;
// Use probing to recover faster after large bitrate estimate drops.
constexpr char kBweRapidRecoveryExperiment[] =
"WebRTC-BweRapidRecoveryExperiment";
} // namespace
ProbeController::ProbeController(NetworkControllerObserver* observer)
: observer_(observer), enable_periodic_alr_probing_(false) {
Reset(0);
in_rapid_recovery_experiment_ = webrtc::field_trial::FindFullName(
kBweRapidRecoveryExperiment) == "Enabled";
}
ProbeController::~ProbeController() {}
void ProbeController::SetBitrates(int64_t min_bitrate_bps,
int64_t start_bitrate_bps,
int64_t max_bitrate_bps,
int64_t at_time_ms) {
if (start_bitrate_bps > 0) {
start_bitrate_bps_ = start_bitrate_bps;
estimated_bitrate_bps_ = start_bitrate_bps;
} else if (start_bitrate_bps_ == 0) {
start_bitrate_bps_ = min_bitrate_bps;
}
// The reason we use the variable |old_max_bitrate_pbs| is because we
// need to set |max_bitrate_bps_| before we call InitiateProbing.
int64_t old_max_bitrate_bps = max_bitrate_bps_;
max_bitrate_bps_ = max_bitrate_bps;
switch (state_) {
case State::kInit:
if (network_available_)
InitiateExponentialProbing(at_time_ms);
break;
case State::kWaitingForProbingResult:
break;
case State::kProbingComplete:
// If the new max bitrate is higher than the old max bitrate and the
// estimate is lower than the new max bitrate then initiate probing.
if (estimated_bitrate_bps_ != 0 &&
old_max_bitrate_bps < max_bitrate_bps_ &&
estimated_bitrate_bps_ < max_bitrate_bps_) {
// The assumption is that if we jump more than 20% in the bandwidth
// estimate or if the bandwidth estimate is within 90% of the new
// max bitrate then the probing attempt was successful.
mid_call_probing_succcess_threshold_ =
std::min(estimated_bitrate_bps_ * 1.2, max_bitrate_bps_ * 0.9);
mid_call_probing_waiting_for_result_ = true;
mid_call_probing_bitrate_bps_ = max_bitrate_bps_;
RTC_HISTOGRAM_COUNTS_10000("WebRTC.BWE.MidCallProbing.Initiated",
max_bitrate_bps_ / 1000);
InitiateProbing(at_time_ms, {max_bitrate_bps}, false);
}
break;
}
}
void ProbeController::OnNetworkAvailability(NetworkAvailability msg) {
network_available_ = msg.network_available;
if (network_available_ && state_ == State::kInit && start_bitrate_bps_ > 0)
InitiateExponentialProbing(msg.at_time.ms());
}
void ProbeController::InitiateExponentialProbing(int64_t at_time_ms) {
RTC_DCHECK(network_available_);
RTC_DCHECK(state_ == State::kInit);
RTC_DCHECK_GT(start_bitrate_bps_, 0);
// When probing at 1.8 Mbps ( 6x 300), this represents a threshold of
// 1.2 Mbps to continue probing.
InitiateProbing(at_time_ms, {3 * start_bitrate_bps_, 6 * start_bitrate_bps_},
true);
}
void ProbeController::SetEstimatedBitrate(int64_t bitrate_bps,
int64_t at_time_ms) {
int64_t now_ms = at_time_ms;
if (mid_call_probing_waiting_for_result_ &&
bitrate_bps >= mid_call_probing_succcess_threshold_) {
RTC_HISTOGRAM_COUNTS_10000("WebRTC.BWE.MidCallProbing.Success",
mid_call_probing_bitrate_bps_ / 1000);
RTC_HISTOGRAM_COUNTS_10000("WebRTC.BWE.MidCallProbing.ProbedKbps",
bitrate_bps / 1000);
mid_call_probing_waiting_for_result_ = false;
}
if (state_ == State::kWaitingForProbingResult) {
// Continue probing if probing results indicate channel has greater
// capacity.
RTC_LOG(LS_INFO) << "Measured bitrate: " << bitrate_bps
<< " Minimum to probe further: "
<< min_bitrate_to_probe_further_bps_;
if (min_bitrate_to_probe_further_bps_ != kExponentialProbingDisabled &&
bitrate_bps > min_bitrate_to_probe_further_bps_) {
// Double the probing bitrate.
InitiateProbing(now_ms, {2 * bitrate_bps}, true);
}
}
if (bitrate_bps < kBitrateDropThreshold * estimated_bitrate_bps_) {
time_of_last_large_drop_ms_ = now_ms;
bitrate_before_last_large_drop_bps_ = estimated_bitrate_bps_;
}
estimated_bitrate_bps_ = bitrate_bps;
}
void ProbeController::EnablePeriodicAlrProbing(bool enable) {
enable_periodic_alr_probing_ = enable;
}
void ProbeController::SetAlrStartTimeMs(
rtc::Optional<int64_t> alr_start_time_ms) {
alr_start_time_ms_ = alr_start_time_ms;
}
void ProbeController::SetAlrEndedTimeMs(int64_t alr_end_time_ms) {
alr_end_time_ms_.emplace(alr_end_time_ms);
}
void ProbeController::RequestProbe(int64_t at_time_ms) {
// Called once we have returned to normal state after a large drop in
// estimated bandwidth. The current response is to initiate a single probe
// session (if not already probing) at the previous bitrate.
//
// If the probe session fails, the assumption is that this drop was a
// real one from a competing flow or a network change.
bool in_alr = alr_start_time_ms_.has_value();
bool alr_ended_recently =
(alr_end_time_ms_.has_value() &&
at_time_ms - alr_end_time_ms_.value() < kAlrEndedTimeoutMs);
if (in_alr || alr_ended_recently || in_rapid_recovery_experiment_) {
if (state_ == State::kProbingComplete) {
uint32_t suggested_probe_bps =
kProbeFractionAfterDrop * bitrate_before_last_large_drop_bps_;
uint32_t min_expected_probe_result_bps =
(1 - kProbeUncertainty) * suggested_probe_bps;
int64_t time_since_drop_ms = at_time_ms - time_of_last_large_drop_ms_;
int64_t time_since_probe_ms = at_time_ms - last_bwe_drop_probing_time_ms_;
if (min_expected_probe_result_bps > estimated_bitrate_bps_ &&
time_since_drop_ms < kBitrateDropTimeoutMs &&
time_since_probe_ms > kMinTimeBetweenAlrProbesMs) {
RTC_LOG(LS_INFO) << "Detected big bandwidth drop, start probing.";
// Track how often we probe in response to bandwidth drop in ALR.
RTC_HISTOGRAM_COUNTS_10000(
"WebRTC.BWE.BweDropProbingIntervalInS",
(at_time_ms - last_bwe_drop_probing_time_ms_) / 1000);
InitiateProbing(at_time_ms, {suggested_probe_bps}, false);
last_bwe_drop_probing_time_ms_ = at_time_ms;
}
}
}
}
void ProbeController::Reset(int64_t at_time_ms) {
network_available_ = true;
state_ = State::kInit;
min_bitrate_to_probe_further_bps_ = kExponentialProbingDisabled;
time_last_probing_initiated_ms_ = 0;
estimated_bitrate_bps_ = 0;
start_bitrate_bps_ = 0;
max_bitrate_bps_ = 0;
int64_t now_ms = at_time_ms;
last_bwe_drop_probing_time_ms_ = now_ms;
alr_end_time_ms_.reset();
mid_call_probing_waiting_for_result_ = false;
time_of_last_large_drop_ms_ = now_ms;
bitrate_before_last_large_drop_bps_ = 0;
}
void ProbeController::Process(int64_t at_time_ms) {
int64_t now_ms = at_time_ms;
if (now_ms - time_last_probing_initiated_ms_ >
kMaxWaitingTimeForProbingResultMs) {
mid_call_probing_waiting_for_result_ = false;
if (state_ == State::kWaitingForProbingResult) {
RTC_LOG(LS_INFO) << "kWaitingForProbingResult: timeout";
state_ = State::kProbingComplete;
min_bitrate_to_probe_further_bps_ = kExponentialProbingDisabled;
}
}
if (state_ != State::kProbingComplete || !enable_periodic_alr_probing_)
return;
// Probe bandwidth periodically when in ALR state.
if (alr_start_time_ms_ && estimated_bitrate_bps_ > 0) {
int64_t next_probe_time_ms =
std::max(*alr_start_time_ms_, time_last_probing_initiated_ms_) +
kAlrPeriodicProbingIntervalMs;
if (now_ms >= next_probe_time_ms) {
InitiateProbing(now_ms, {estimated_bitrate_bps_ * 2}, true);
}
}
}
void ProbeController::InitiateProbing(
int64_t now_ms,
std::initializer_list<int64_t> bitrates_to_probe,
bool probe_further) {
for (int64_t bitrate : bitrates_to_probe) {
RTC_DCHECK_GT(bitrate, 0);
int64_t max_probe_bitrate_bps =
max_bitrate_bps_ > 0 ? max_bitrate_bps_ : kDefaultMaxProbingBitrateBps;
if (bitrate > max_probe_bitrate_bps) {
bitrate = max_probe_bitrate_bps;
probe_further = false;
}
ProbeClusterConfig config;
config.at_time = Timestamp::ms(now_ms);
config.target_data_rate = DataRate::bps(rtc::dchecked_cast<int>(bitrate));
config.target_duration = TimeDelta::ms(kMinProbeDurationMs);
config.target_probe_count = kMinProbePacketsSent;
observer_->OnProbeClusterConfig(config);
}
time_last_probing_initiated_ms_ = now_ms;
if (probe_further) {
state_ = State::kWaitingForProbingResult;
min_bitrate_to_probe_further_bps_ =
(*(bitrates_to_probe.end() - 1)) * kRepeatedProbeMinPercentage / 100;
} else {
state_ = State::kProbingComplete;
min_bitrate_to_probe_further_bps_ = kExponentialProbingDisabled;
}
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2016 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_GOOG_CC_PROBE_CONTROLLER_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_
#include <stdint.h>
#include <initializer_list>
#include "api/optional.h"
#include "modules/congestion_controller/network_control/include/network_control.h"
namespace webrtc {
class Clock;
namespace webrtc_cc {
// This class controls initiation of probing to estimate initial channel
// capacity. There is also support for probing during a session when max
// bitrate is adjusted by an application.
class ProbeController {
public:
explicit ProbeController(NetworkControllerObserver* observer);
~ProbeController();
void SetBitrates(int64_t min_bitrate_bps,
int64_t start_bitrate_bps,
int64_t max_bitrate_bps,
int64_t at_time_ms);
void OnNetworkAvailability(NetworkAvailability msg);
void SetEstimatedBitrate(int64_t bitrate_bps, int64_t at_time_ms);
void EnablePeriodicAlrProbing(bool enable);
void SetAlrStartTimeMs(rtc::Optional<int64_t> alr_start_time);
void SetAlrEndedTimeMs(int64_t alr_end_time);
void RequestProbe(int64_t at_time_ms);
// Resets the ProbeController to a state equivalent to as if it was just
// created EXCEPT for |enable_periodic_alr_probing_|.
void Reset(int64_t at_time_ms);
void Process(int64_t at_time_ms);
private:
enum class State {
// Initial state where no probing has been triggered yet.
kInit,
// Waiting for probing results to continue further probing.
kWaitingForProbingResult,
// Probing is complete.
kProbingComplete,
};
void InitiateExponentialProbing(int64_t at_time_ms);
void InitiateProbing(int64_t now_ms,
std::initializer_list<int64_t> bitrates_to_probe,
bool probe_further);
NetworkControllerObserver* const observer_;
bool network_available_;
State state_;
int64_t min_bitrate_to_probe_further_bps_;
int64_t time_last_probing_initiated_ms_;
int64_t estimated_bitrate_bps_;
int64_t start_bitrate_bps_;
int64_t max_bitrate_bps_;
int64_t last_bwe_drop_probing_time_ms_;
rtc::Optional<int64_t> alr_start_time_ms_;
rtc::Optional<int64_t> alr_end_time_ms_;
bool enable_periodic_alr_probing_;
int64_t time_of_last_large_drop_ms_;
int64_t bitrate_before_last_large_drop_bps_;
bool in_rapid_recovery_experiment_;
// For WebRTC.BWE.MidCallProbing.* metric.
bool mid_call_probing_waiting_for_result_;
int64_t mid_call_probing_bitrate_bps_;
int64_t mid_call_probing_succcess_threshold_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(ProbeController);
};
} // namespace webrtc_cc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_

View File

@ -0,0 +1,292 @@
/*
* Copyright (c) 2016 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 "modules/congestion_controller/goog_cc/probe_controller.h"
#include "modules/congestion_controller/network_control/include/network_types.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/clock.h"
#include "test/gmock.h"
#include "test/gtest.h"
using testing::_;
using testing::AtLeast;
using testing::Field;
using testing::Matcher;
using testing::NiceMock;
using testing::Return;
namespace webrtc {
namespace webrtc_cc {
namespace test {
namespace {
constexpr int kMinBitrateBps = 100;
constexpr int kStartBitrateBps = 300;
constexpr int kMaxBitrateBps = 10000;
constexpr int kExponentialProbingTimeoutMs = 5000;
constexpr int kAlrProbeInterval = 5000;
constexpr int kAlrEndedTimeoutMs = 3000;
constexpr int kBitrateDropTimeoutMs = 5000;
inline Matcher<ProbeClusterConfig> DataRateEqBps(int bps) {
return Field(&ProbeClusterConfig::target_data_rate, DataRate::bps(bps));
}
class MockNetworkControllerObserver : public NetworkControllerObserver {
public:
MOCK_METHOD1(OnCongestionWindow, void(CongestionWindow));
MOCK_METHOD1(OnPacerConfig, void(PacerConfig));
MOCK_METHOD1(OnProbeClusterConfig, void(ProbeClusterConfig));
MOCK_METHOD1(OnTargetTransferRate, void(TargetTransferRate));
};
} // namespace
class ProbeControllerTest : public ::testing::Test {
protected:
ProbeControllerTest() : clock_(100000000L) {
probe_controller_.reset(new ProbeController(&cluster_handler_));
}
~ProbeControllerTest() override {}
void SetNetworkAvailable(bool available) {
NetworkAvailability msg;
msg.at_time = Timestamp::ms(NowMs());
msg.network_available = available;
probe_controller_->OnNetworkAvailability(msg);
}
int64_t NowMs() { return clock_.TimeInMilliseconds(); }
SimulatedClock clock_;
NiceMock<MockNetworkControllerObserver> cluster_handler_;
std::unique_ptr<ProbeController> probe_controller_;
};
TEST_F(ProbeControllerTest, InitiatesProbingAtStart) {
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(AtLeast(2));
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
}
TEST_F(ProbeControllerTest, ProbeOnlyWhenNetworkIsUp) {
SetNetworkAvailable(false);
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(0);
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
testing::Mock::VerifyAndClearExpectations(&cluster_handler_);
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(AtLeast(2));
SetNetworkAvailable(true);
}
TEST_F(ProbeControllerTest, InitiatesProbingOnMaxBitrateIncrease) {
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(AtLeast(2));
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
// Long enough to time out exponential probing.
clock_.AdvanceTimeMilliseconds(kExponentialProbingTimeoutMs);
probe_controller_->SetEstimatedBitrate(kStartBitrateBps, NowMs());
probe_controller_->Process(NowMs());
EXPECT_CALL(cluster_handler_,
OnProbeClusterConfig(DataRateEqBps(kMaxBitrateBps + 100)));
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps + 100, NowMs());
}
TEST_F(ProbeControllerTest, InitiatesProbingOnMaxBitrateIncreaseAtMaxBitrate) {
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(AtLeast(2));
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
// Long enough to time out exponential probing.
clock_.AdvanceTimeMilliseconds(kExponentialProbingTimeoutMs);
probe_controller_->SetEstimatedBitrate(kStartBitrateBps, NowMs());
probe_controller_->Process(NowMs());
probe_controller_->SetEstimatedBitrate(kMaxBitrateBps, NowMs());
EXPECT_CALL(cluster_handler_,
OnProbeClusterConfig(DataRateEqBps(kMaxBitrateBps + 100)));
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps + 100, NowMs());
}
TEST_F(ProbeControllerTest, TestExponentialProbing) {
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
// Repeated probe should only be sent when estimated bitrate climbs above
// 0.7 * 6 * kStartBitrateBps = 1260.
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(0);
probe_controller_->SetEstimatedBitrate(1000, NowMs());
testing::Mock::VerifyAndClearExpectations(&cluster_handler_);
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(DataRateEqBps(2 * 1800)));
probe_controller_->SetEstimatedBitrate(1800, NowMs());
}
TEST_F(ProbeControllerTest, TestExponentialProbingTimeout) {
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
// Advance far enough to cause a time out in waiting for probing result.
clock_.AdvanceTimeMilliseconds(kExponentialProbingTimeoutMs);
probe_controller_->Process(NowMs());
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(0);
probe_controller_->SetEstimatedBitrate(1800, NowMs());
}
TEST_F(ProbeControllerTest, RequestProbeInAlr) {
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(2);
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
probe_controller_->SetEstimatedBitrate(500, NowMs());
testing::Mock::VerifyAndClearExpectations(&cluster_handler_);
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(DataRateEqBps(0.85 * 500)))
.Times(1);
probe_controller_->SetAlrStartTimeMs(clock_.TimeInMilliseconds());
clock_.AdvanceTimeMilliseconds(kAlrProbeInterval + 1);
probe_controller_->Process(NowMs());
probe_controller_->SetEstimatedBitrate(250, NowMs());
probe_controller_->RequestProbe(NowMs());
}
TEST_F(ProbeControllerTest, RequestProbeWhenAlrEndedRecently) {
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(2);
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
probe_controller_->SetEstimatedBitrate(500, NowMs());
testing::Mock::VerifyAndClearExpectations(&cluster_handler_);
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(DataRateEqBps(0.85 * 500)))
.Times(1);
probe_controller_->SetAlrStartTimeMs(rtc::nullopt);
clock_.AdvanceTimeMilliseconds(kAlrProbeInterval + 1);
probe_controller_->Process(NowMs());
probe_controller_->SetEstimatedBitrate(250, NowMs());
probe_controller_->SetAlrEndedTimeMs(clock_.TimeInMilliseconds());
clock_.AdvanceTimeMilliseconds(kAlrEndedTimeoutMs - 1);
probe_controller_->RequestProbe(NowMs());
}
TEST_F(ProbeControllerTest, RequestProbeWhenAlrNotEndedRecently) {
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(2);
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
probe_controller_->SetEstimatedBitrate(500, NowMs());
testing::Mock::VerifyAndClearExpectations(&cluster_handler_);
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(0);
probe_controller_->SetAlrStartTimeMs(rtc::nullopt);
clock_.AdvanceTimeMilliseconds(kAlrProbeInterval + 1);
probe_controller_->Process(NowMs());
probe_controller_->SetEstimatedBitrate(250, NowMs());
probe_controller_->SetAlrEndedTimeMs(clock_.TimeInMilliseconds());
clock_.AdvanceTimeMilliseconds(kAlrEndedTimeoutMs + 1);
probe_controller_->RequestProbe(NowMs());
}
TEST_F(ProbeControllerTest, RequestProbeWhenBweDropNotRecent) {
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(2);
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
probe_controller_->SetEstimatedBitrate(500, NowMs());
testing::Mock::VerifyAndClearExpectations(&cluster_handler_);
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(0);
probe_controller_->SetAlrStartTimeMs(clock_.TimeInMilliseconds());
clock_.AdvanceTimeMilliseconds(kAlrProbeInterval + 1);
probe_controller_->Process(NowMs());
probe_controller_->SetEstimatedBitrate(250, NowMs());
clock_.AdvanceTimeMilliseconds(kBitrateDropTimeoutMs + 1);
probe_controller_->RequestProbe(NowMs());
}
TEST_F(ProbeControllerTest, PeriodicProbing) {
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(2);
probe_controller_->EnablePeriodicAlrProbing(true);
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
probe_controller_->SetEstimatedBitrate(500, NowMs());
testing::Mock::VerifyAndClearExpectations(&cluster_handler_);
int64_t start_time = clock_.TimeInMilliseconds();
// Expect the controller to send a new probe after 5s has passed.
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(DataRateEqBps(1000)))
.Times(1);
probe_controller_->SetAlrStartTimeMs(start_time);
clock_.AdvanceTimeMilliseconds(5000);
probe_controller_->Process(NowMs());
probe_controller_->SetEstimatedBitrate(500, NowMs());
testing::Mock::VerifyAndClearExpectations(&cluster_handler_);
// The following probe should be sent at 10s into ALR.
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(0);
probe_controller_->SetAlrStartTimeMs(start_time);
clock_.AdvanceTimeMilliseconds(4000);
probe_controller_->Process(NowMs());
probe_controller_->SetEstimatedBitrate(500, NowMs());
testing::Mock::VerifyAndClearExpectations(&cluster_handler_);
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(1);
probe_controller_->SetAlrStartTimeMs(start_time);
clock_.AdvanceTimeMilliseconds(1000);
probe_controller_->Process(NowMs());
probe_controller_->SetEstimatedBitrate(500, NowMs());
testing::Mock::VerifyAndClearExpectations(&cluster_handler_);
}
TEST_F(ProbeControllerTest, PeriodicProbingAfterReset) {
NiceMock<MockNetworkControllerObserver> local_handler;
probe_controller_.reset(new ProbeController(&local_handler));
int64_t alr_start_time = clock_.TimeInMilliseconds();
probe_controller_->SetAlrStartTimeMs(alr_start_time);
EXPECT_CALL(local_handler, OnProbeClusterConfig(_)).Times(2);
probe_controller_->EnablePeriodicAlrProbing(true);
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
probe_controller_->Reset(NowMs());
clock_.AdvanceTimeMilliseconds(10000);
probe_controller_->Process(NowMs());
EXPECT_CALL(local_handler, OnProbeClusterConfig(_)).Times(2);
probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
kMaxBitrateBps, NowMs());
// Make sure we use |kStartBitrateBps| as the estimated bitrate
// until SetEstimatedBitrate is called with an updated estimate.
clock_.AdvanceTimeMilliseconds(10000);
EXPECT_CALL(local_handler,
OnProbeClusterConfig(DataRateEqBps(kStartBitrateBps * 2)));
probe_controller_->Process(NowMs());
}
TEST_F(ProbeControllerTest, TestExponentialProbingOverflow) {
const int64_t kMbpsMultiplier = 1000000;
probe_controller_->SetBitrates(kMinBitrateBps, 10 * kMbpsMultiplier,
100 * kMbpsMultiplier, NowMs());
// Verify that probe bitrate is capped at the specified max bitrate.
EXPECT_CALL(cluster_handler_,
OnProbeClusterConfig(DataRateEqBps(100 * kMbpsMultiplier)));
probe_controller_->SetEstimatedBitrate(60 * kMbpsMultiplier, NowMs());
testing::Mock::VerifyAndClearExpectations(&cluster_handler_);
// Verify that repeated probes aren't sent.
EXPECT_CALL(cluster_handler_, OnProbeClusterConfig(_)).Times(0);
probe_controller_->SetEstimatedBitrate(100 * kMbpsMultiplier, NowMs());
}
} // namespace test
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,185 @@
/*
* Copyright (c) 2016 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/goog_cc/trendline_estimator.h"
#include <math.h>
#include <algorithm>
#include "api/optional.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_minmax.h"
namespace webrtc {
namespace webrtc_cc {
namespace {
rtc::Optional<double> LinearFitSlope(
const std::deque<std::pair<double, double>>& points) {
RTC_DCHECK(points.size() >= 2);
// Compute the "center of mass".
double sum_x = 0;
double sum_y = 0;
for (const auto& point : points) {
sum_x += point.first;
sum_y += point.second;
}
double x_avg = sum_x / points.size();
double y_avg = sum_y / points.size();
// Compute the slope k = \sum (x_i-x_avg)(y_i-y_avg) / \sum (x_i-x_avg)^2
double numerator = 0;
double denominator = 0;
for (const auto& point : points) {
numerator += (point.first - x_avg) * (point.second - y_avg);
denominator += (point.first - x_avg) * (point.first - x_avg);
}
if (denominator == 0)
return rtc::nullopt;
return numerator / denominator;
}
constexpr double kMaxAdaptOffsetMs = 15.0;
constexpr double kOverUsingTimeThreshold = 10;
constexpr int kMinNumDeltas = 60;
} // namespace
enum { kDeltaCounterMax = 1000 };
TrendlineEstimator::TrendlineEstimator(size_t window_size,
double smoothing_coef,
double threshold_gain)
: window_size_(window_size),
smoothing_coef_(smoothing_coef),
threshold_gain_(threshold_gain),
num_of_deltas_(0),
first_arrival_time_ms_(-1),
accumulated_delay_(0),
smoothed_delay_(0),
delay_hist_(),
trendline_(0),
k_up_(0.0087),
k_down_(0.039),
overusing_time_threshold_(kOverUsingTimeThreshold),
threshold_(12.5),
last_update_ms_(-1),
prev_offset_(0.0),
time_over_using_(-1),
overuse_counter_(0),
hypothesis_(BandwidthUsage::kBwNormal) {}
TrendlineEstimator::~TrendlineEstimator() {}
void TrendlineEstimator::Update(double recv_delta_ms,
double send_delta_ms,
int64_t arrival_time_ms) {
const double delta_ms = recv_delta_ms - send_delta_ms;
++num_of_deltas_;
if (num_of_deltas_ > kDeltaCounterMax)
num_of_deltas_ = kDeltaCounterMax;
if (first_arrival_time_ms_ == -1)
first_arrival_time_ms_ = arrival_time_ms;
// Exponential backoff filter.
accumulated_delay_ += delta_ms;
BWE_TEST_LOGGING_PLOT(1, "accumulated_delay_ms", arrival_time_ms,
accumulated_delay_);
smoothed_delay_ = smoothing_coef_ * smoothed_delay_ +
(1 - smoothing_coef_) * accumulated_delay_;
BWE_TEST_LOGGING_PLOT(1, "smoothed_delay_ms", arrival_time_ms,
smoothed_delay_);
// Simple linear regression.
delay_hist_.push_back(std::make_pair(
static_cast<double>(arrival_time_ms - first_arrival_time_ms_),
smoothed_delay_));
if (delay_hist_.size() > window_size_)
delay_hist_.pop_front();
if (delay_hist_.size() == window_size_) {
// Only update trendline_ if it is possible to fit a line to the data.
trendline_ = LinearFitSlope(delay_hist_).value_or(trendline_);
}
BWE_TEST_LOGGING_PLOT(1, "trendline_slope", arrival_time_ms, trendline_);
Detect(trendline_slope(), send_delta_ms, num_of_deltas(), arrival_time_ms);
}
BandwidthUsage TrendlineEstimator::State() const {
return hypothesis_;
}
void TrendlineEstimator::Detect(double offset,
double ts_delta,
int num_of_deltas,
int64_t now_ms) {
if (num_of_deltas < 2) {
hypothesis_ = BandwidthUsage::kBwNormal;
return;
}
const double T = std::min(num_of_deltas, kMinNumDeltas) * offset;
BWE_TEST_LOGGING_PLOT(1, "T", now_ms, T);
BWE_TEST_LOGGING_PLOT(1, "threshold", now_ms, threshold_);
if (T > threshold_) {
if (time_over_using_ == -1) {
// Initialize the timer. Assume that we've been
// over-using half of the time since the previous
// sample.
time_over_using_ = ts_delta / 2;
} else {
// Increment timer
time_over_using_ += ts_delta;
}
overuse_counter_++;
if (time_over_using_ > overusing_time_threshold_ && overuse_counter_ > 1) {
if (offset >= prev_offset_) {
time_over_using_ = 0;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwOverusing;
}
}
} else if (T < -threshold_) {
time_over_using_ = -1;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwUnderusing;
} else {
time_over_using_ = -1;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwNormal;
}
prev_offset_ = offset;
UpdateThreshold(T, now_ms);
}
void TrendlineEstimator::UpdateThreshold(double modified_offset,
int64_t now_ms) {
if (last_update_ms_ == -1)
last_update_ms_ = now_ms;
if (fabs(modified_offset) > threshold_ + kMaxAdaptOffsetMs) {
// Avoid adapting the threshold to big latency spikes, caused e.g.,
// by a sudden capacity drop.
last_update_ms_ = now_ms;
return;
}
const double k = fabs(modified_offset) < threshold_ ? k_down_ : k_up_;
const int64_t kMaxTimeDeltaMs = 100;
int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs);
threshold_ += k * (fabs(modified_offset) - threshold_) * time_delta_ms;
threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f);
last_update_ms_ = now_ms;
}
} // namespace webrtc_cc
} // namespace webrtc

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2016 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_GOOG_CC_TRENDLINE_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_
#include <stddef.h>
#include <stdint.h>
#include <deque>
#include <utility>
#include "modules/congestion_controller/goog_cc/delay_increase_detector_interface.h"
#include "rtc_base/constructormagic.h"
namespace webrtc {
namespace webrtc_cc {
class TrendlineEstimator : public DelayIncreaseDetectorInterface {
public:
// |window_size| is the number of points required to compute a trend line.
// |smoothing_coef| controls how much we smooth out the delay before fitting
// the trend line. |threshold_gain| is used to scale the trendline slope for
// comparison to the old threshold. Once the old estimator has been removed
// (or the thresholds been merged into the estimators), we can just set the
// threshold instead of setting a gain.
TrendlineEstimator(size_t window_size,
double smoothing_coef,
double threshold_gain);
~TrendlineEstimator() override;
// Update the estimator with a new sample. The deltas should represent deltas
// between timestamp groups as defined by the InterArrival class.
void Update(double recv_delta_ms,
double send_delta_ms,
int64_t arrival_time_ms) override;
BandwidthUsage State() const override;
// Returns the estimated trend k multiplied by some gain.
// 0 < k < 1 -> the delay increases, queues are filling up
// k == 0 -> the delay does not change
// k < 0 -> the delay decreases, queues are being emptied
double trendline_slope() const { return trendline_ * threshold_gain_; }
// Returns the number of deltas which the current estimator state is based on.
unsigned int num_of_deltas() const { return num_of_deltas_; }
private:
void Detect(double offset,
double ts_delta,
int num_of_deltas,
int64_t now_ms);
void UpdateThreshold(double modified_offset, int64_t now_ms);
// Parameters.
const size_t window_size_;
const double smoothing_coef_;
const double threshold_gain_;
// Used by the existing threshold.
unsigned int num_of_deltas_;
// Keep the arrival times small by using the change from the first packet.
int64_t first_arrival_time_ms_;
// Exponential backoff filtering.
double accumulated_delay_;
double smoothed_delay_;
// Linear least squares regression.
std::deque<std::pair<double, double>> delay_hist_;
double trendline_;
const double k_up_;
const double k_down_;
double overusing_time_threshold_;
double threshold_;
int64_t last_update_ms_;
double prev_offset_;
double time_over_using_;
int overuse_counter_;
BandwidthUsage hypothesis_;
RTC_DISALLOW_COPY_AND_ASSIGN(TrendlineEstimator);
};
} // namespace webrtc_cc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2016 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/goog_cc/trendline_estimator.h"
#include "rtc_base/random.h"
#include "test/gtest.h"
namespace webrtc {
namespace webrtc_cc {
namespace {
constexpr size_t kWindowSize = 20;
constexpr double kSmoothing = 0.0;
constexpr double kGain = 1;
constexpr int64_t kAvgTimeBetweenPackets = 10;
constexpr size_t kPacketCount = 2 * kWindowSize + 1;
void TestEstimator(double slope, double jitter_stddev, double tolerance) {
TrendlineEstimator estimator(kWindowSize, kSmoothing, kGain);
Random random(0x1234567);
int64_t send_times[kPacketCount];
int64_t recv_times[kPacketCount];
int64_t send_start_time = random.Rand(1000000);
int64_t recv_start_time = random.Rand(1000000);
for (size_t i = 0; i < kPacketCount; ++i) {
send_times[i] = send_start_time + i * kAvgTimeBetweenPackets;
double latency = i * kAvgTimeBetweenPackets / (1 - slope);
double jitter = random.Gaussian(0, jitter_stddev);
recv_times[i] = recv_start_time + latency + jitter;
}
for (size_t i = 1; i < kPacketCount; ++i) {
double recv_delta = recv_times[i] - recv_times[i - 1];
double send_delta = send_times[i] - send_times[i - 1];
estimator.Update(recv_delta, send_delta, recv_times[i]);
if (i < kWindowSize)
EXPECT_NEAR(estimator.trendline_slope(), 0, 0.001);
else
EXPECT_NEAR(estimator.trendline_slope(), slope, tolerance);
}
}
} // namespace
TEST(TrendlineEstimator, PerfectLineSlopeOneHalf) {
TestEstimator(0.5, 0, 0.001);
}
TEST(TrendlineEstimator, PerfectLineSlopeMinusOne) {
TestEstimator(-1, 0, 0.001);
}
TEST(TrendlineEstimator, PerfectLineSlopeZero) {
TestEstimator(0, 0, 0.001);
}
TEST(TrendlineEstimator, JitteryLineSlopeOneHalf) {
TestEstimator(0.5, kAvgTimeBetweenPackets / 3.0, 0.01);
}
TEST(TrendlineEstimator, JitteryLineSlopeMinusOne) {
TestEstimator(-1, kAvgTimeBetweenPackets / 3.0, 0.075);
}
TEST(TrendlineEstimator, JitteryLineSlopeZero) {
TestEstimator(0, kAvgTimeBetweenPackets / 3.0, 0.02);
}
} // namespace webrtc_cc
} // namespace webrtc