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:
committed by
Commit Bot
parent
cdff887238
commit
fc7ec8e9f8
164
modules/congestion_controller/goog_cc/BUILD.gn
Normal file
164
modules/congestion_controller/goog_cc/BUILD.gn
Normal 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" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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*/
|
||||
97
modules/congestion_controller/goog_cc/alr_detector.cc
Normal file
97
modules/congestion_controller/goog_cc/alr_detector.cc
Normal 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
|
||||
77
modules/congestion_controller/goog_cc/alr_detector.h
Normal file
77
modules/congestion_controller/goog_cc/alr_detector.h
Normal 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_
|
||||
177
modules/congestion_controller/goog_cc/alr_detector_unittest.cc
Normal file
177
modules/congestion_controller/goog_cc/alr_detector_unittest.cc
Normal 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_, ×tamp_ms_)
|
||||
.ForTimeMs(1000)
|
||||
.AtPercentOfEstimatedBitrate(90);
|
||||
EXPECT_FALSE(alr_detector_.GetApplicationLimitedRegionStartTime());
|
||||
|
||||
// Verify that we ALR starts when bitrate drops below 20%.
|
||||
SimulateOutgoingTrafficIn(&alr_detector_, ×tamp_ms_)
|
||||
.ForTimeMs(1500)
|
||||
.AtPercentOfEstimatedBitrate(20);
|
||||
EXPECT_TRUE(alr_detector_.GetApplicationLimitedRegionStartTime());
|
||||
|
||||
// Verify that ALR ends when usage is above 65%.
|
||||
SimulateOutgoingTrafficIn(&alr_detector_, ×tamp_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_, ×tamp_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_, ×tamp_ms_)
|
||||
.ForTimeMs(100)
|
||||
.AtPercentOfEstimatedBitrate(150);
|
||||
EXPECT_TRUE(alr_detector_.GetApplicationLimitedRegionStartTime());
|
||||
|
||||
// ALR ends when usage is above 65%.
|
||||
SimulateOutgoingTrafficIn(&alr_detector_, ×tamp_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_, ×tamp_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_, ×tamp_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
|
||||
109
modules/congestion_controller/goog_cc/bitrate_estimator.cc
Normal file
109
modules/congestion_controller/goog_cc/bitrate_estimator.cc
Normal 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
|
||||
48
modules/congestion_controller/goog_cc/bitrate_estimator.h
Normal file
48
modules/congestion_controller/goog_cc/bitrate_estimator.h
Normal 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_
|
||||
331
modules/congestion_controller/goog_cc/delay_based_bwe.cc
Normal file
331
modules/congestion_controller/goog_cc/delay_based_bwe.cc
Normal 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
|
||||
94
modules/congestion_controller/goog_cc/delay_based_bwe.h
Normal file
94
modules/congestion_controller/goog_cc/delay_based_bwe.h
Normal 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_
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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_
|
||||
30
modules/congestion_controller/goog_cc/goog_cc_factory.cc
Normal file
30
modules/congestion_controller/goog_cc/goog_cc_factory.cc
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/congestion_controller/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
|
||||
413
modules/congestion_controller/goog_cc/goog_cc_network_control.cc
Normal file
413
modules/congestion_controller/goog_cc/goog_cc_network_control.cc
Normal 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
|
||||
@ -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_
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
191
modules/congestion_controller/goog_cc/probe_bitrate_estimator.cc
Normal file
191
modules/congestion_controller/goog_cc/probe_bitrate_estimator.cc
Normal 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
|
||||
@ -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_
|
||||
@ -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
|
||||
310
modules/congestion_controller/goog_cc/probe_controller.cc
Normal file
310
modules/congestion_controller/goog_cc/probe_controller.cc
Normal 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
|
||||
100
modules/congestion_controller/goog_cc/probe_controller.h
Normal file
100
modules/congestion_controller/goog_cc/probe_controller.h
Normal 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_
|
||||
@ -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
|
||||
185
modules/congestion_controller/goog_cc/trendline_estimator.cc
Normal file
185
modules/congestion_controller/goog_cc/trendline_estimator.cc
Normal 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
|
||||
94
modules/congestion_controller/goog_cc/trendline_estimator.h
Normal file
94
modules/congestion_controller/goog_cc/trendline_estimator.h
Normal 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_
|
||||
@ -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
|
||||
Reference in New Issue
Block a user