Simplified throughput estimator
Add interface for AcknowledgedBitrateEstimator Add simplified throughput estimator, implementing the same interface. The choice of estimator implementation can be controlled by a field trial. Bug: webrtc:10274 Change-Id: I6bef090a8a6a1783f3f5750a2ee56189f562a9c8 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/158892 Commit-Queue: Björn Terelius <terelius@webrtc.org> Reviewed-by: Sebastian Jansson <srte@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29761}
This commit is contained in:
parent
62a19d0798
commit
251b0dcc4f
@ -106,6 +106,8 @@ rtc_library("estimators") {
|
|||||||
sources = [
|
sources = [
|
||||||
"acknowledged_bitrate_estimator.cc",
|
"acknowledged_bitrate_estimator.cc",
|
||||||
"acknowledged_bitrate_estimator.h",
|
"acknowledged_bitrate_estimator.h",
|
||||||
|
"acknowledged_bitrate_estimator_interface.cc",
|
||||||
|
"acknowledged_bitrate_estimator_interface.h",
|
||||||
"bitrate_estimator.cc",
|
"bitrate_estimator.cc",
|
||||||
"bitrate_estimator.h",
|
"bitrate_estimator.h",
|
||||||
"delay_increase_detector_interface.h",
|
"delay_increase_detector_interface.h",
|
||||||
@ -113,6 +115,8 @@ rtc_library("estimators") {
|
|||||||
"median_slope_estimator.h",
|
"median_slope_estimator.h",
|
||||||
"probe_bitrate_estimator.cc",
|
"probe_bitrate_estimator.cc",
|
||||||
"probe_bitrate_estimator.h",
|
"probe_bitrate_estimator.h",
|
||||||
|
"robust_throughput_estimator.cc",
|
||||||
|
"robust_throughput_estimator.h",
|
||||||
"trendline_estimator.cc",
|
"trendline_estimator.cc",
|
||||||
"trendline_estimator.h",
|
"trendline_estimator.h",
|
||||||
]
|
]
|
||||||
@ -249,6 +253,7 @@ if (rtc_include_tests) {
|
|||||||
"median_slope_estimator_unittest.cc",
|
"median_slope_estimator_unittest.cc",
|
||||||
"probe_bitrate_estimator_unittest.cc",
|
"probe_bitrate_estimator_unittest.cc",
|
||||||
"probe_controller_unittest.cc",
|
"probe_controller_unittest.cc",
|
||||||
|
"robust_throughput_estimator_unittest.cc",
|
||||||
"send_side_bandwidth_estimation_unittest.cc",
|
"send_side_bandwidth_estimation_unittest.cc",
|
||||||
"trendline_estimator_unittest.cc",
|
"trendline_estimator_unittest.cc",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -18,11 +18,13 @@
|
|||||||
#include "api/transport/network_types.h"
|
#include "api/transport/network_types.h"
|
||||||
#include "api/transport/webrtc_key_value_config.h"
|
#include "api/transport/webrtc_key_value_config.h"
|
||||||
#include "api/units/data_rate.h"
|
#include "api/units/data_rate.h"
|
||||||
|
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
|
||||||
#include "modules/congestion_controller/goog_cc/bitrate_estimator.h"
|
#include "modules/congestion_controller/goog_cc/bitrate_estimator.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
class AcknowledgedBitrateEstimator {
|
class AcknowledgedBitrateEstimator
|
||||||
|
: public AcknowledgedBitrateEstimatorInterface {
|
||||||
public:
|
public:
|
||||||
AcknowledgedBitrateEstimator(
|
AcknowledgedBitrateEstimator(
|
||||||
const WebRtcKeyValueConfig* key_value_config,
|
const WebRtcKeyValueConfig* key_value_config,
|
||||||
@ -30,14 +32,14 @@ class AcknowledgedBitrateEstimator {
|
|||||||
|
|
||||||
explicit AcknowledgedBitrateEstimator(
|
explicit AcknowledgedBitrateEstimator(
|
||||||
const WebRtcKeyValueConfig* key_value_config);
|
const WebRtcKeyValueConfig* key_value_config);
|
||||||
~AcknowledgedBitrateEstimator();
|
~AcknowledgedBitrateEstimator() override;
|
||||||
|
|
||||||
void IncomingPacketFeedbackVector(
|
void IncomingPacketFeedbackVector(
|
||||||
const std::vector<PacketResult>& packet_feedback_vector);
|
const std::vector<PacketResult>& packet_feedback_vector) override;
|
||||||
absl::optional<DataRate> bitrate() const;
|
absl::optional<DataRate> bitrate() const override;
|
||||||
absl::optional<DataRate> PeekRate() const;
|
absl::optional<DataRate> PeekRate() const override;
|
||||||
void SetAlr(bool in_alr);
|
void SetAlr(bool in_alr) override;
|
||||||
void SetAlrEndedTime(Timestamp alr_ended_time);
|
void SetAlrEndedTime(Timestamp alr_ended_time) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
absl::optional<Timestamp> alr_ended_time_;
|
absl::optional<Timestamp> alr_ended_time_;
|
||||||
|
|||||||
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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_interface.h"
|
||||||
|
|
||||||
|
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
|
||||||
|
#include "modules/congestion_controller/goog_cc/robust_throughput_estimator.h"
|
||||||
|
#include "rtc_base/logging.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
constexpr char RobustThroughputEstimatorSettings::kKey[];
|
||||||
|
|
||||||
|
RobustThroughputEstimatorSettings::RobustThroughputEstimatorSettings(
|
||||||
|
const WebRtcKeyValueConfig* key_value_config) {
|
||||||
|
Parser()->Parse(
|
||||||
|
key_value_config->Lookup(RobustThroughputEstimatorSettings::kKey));
|
||||||
|
if (min_packets < 10 || kMaxPackets < min_packets) {
|
||||||
|
RTC_LOG(LS_WARNING) << "Window size must be between 10 and " << kMaxPackets
|
||||||
|
<< " packets";
|
||||||
|
min_packets = 20;
|
||||||
|
}
|
||||||
|
if (window_duration < TimeDelta::ms(100) ||
|
||||||
|
TimeDelta::ms(2000) < window_duration) {
|
||||||
|
RTC_LOG(LS_WARNING) << "Window duration must be between 100 and 2000 ms";
|
||||||
|
window_duration = TimeDelta::ms(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<StructParametersParser>
|
||||||
|
RobustThroughputEstimatorSettings::Parser() {
|
||||||
|
return StructParametersParser::Create("enabled", &enabled, //
|
||||||
|
"reduce_bias", &reduce_bias, //
|
||||||
|
"assume_shared_link",
|
||||||
|
&assume_shared_link, //
|
||||||
|
"min_packets", &min_packets, //
|
||||||
|
"window_duration", &window_duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
AcknowledgedBitrateEstimatorInterface::
|
||||||
|
~AcknowledgedBitrateEstimatorInterface() {}
|
||||||
|
|
||||||
|
std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
|
||||||
|
AcknowledgedBitrateEstimatorInterface::Create(
|
||||||
|
const WebRtcKeyValueConfig* key_value_config) {
|
||||||
|
RobustThroughputEstimatorSettings simplified_estimator_settings(
|
||||||
|
key_value_config);
|
||||||
|
if (simplified_estimator_settings.enabled) {
|
||||||
|
return std::make_unique<RobustThroughputEstimator>(
|
||||||
|
simplified_estimator_settings);
|
||||||
|
}
|
||||||
|
return std::make_unique<AcknowledgedBitrateEstimator>(key_value_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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_INTERFACE_H_
|
||||||
|
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "absl/types/optional.h"
|
||||||
|
#include "api/transport/network_types.h"
|
||||||
|
#include "api/transport/webrtc_key_value_config.h"
|
||||||
|
#include "api/units/data_rate.h"
|
||||||
|
#include "rtc_base/experiments/struct_parameters_parser.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
struct RobustThroughputEstimatorSettings {
|
||||||
|
static constexpr char kKey[] = "WebRTC-Bwe-RobustThroughputEstimatorSettings";
|
||||||
|
static constexpr size_t kMaxPackets = 500;
|
||||||
|
|
||||||
|
RobustThroughputEstimatorSettings() = delete;
|
||||||
|
explicit RobustThroughputEstimatorSettings(
|
||||||
|
const WebRtcKeyValueConfig* key_value_config);
|
||||||
|
|
||||||
|
bool enabled = false; // Set to true to use RobustThroughputEstimator.
|
||||||
|
|
||||||
|
// The estimator handles delay spikes by removing the largest receive time
|
||||||
|
// gap, but this introduces some bias that may lead to overestimation when
|
||||||
|
// there isn't any delay spike. If |reduce_bias| is true, we instead replace
|
||||||
|
// the largest receive time gap by the second largest. This reduces the bias
|
||||||
|
// at the cost of not completely removing the genuine delay spikes.
|
||||||
|
bool reduce_bias = true;
|
||||||
|
|
||||||
|
// If |assume_shared_link| is false, we ignore the size of the first packet
|
||||||
|
// when computing the receive rate. Otherwise, we remove half of the first
|
||||||
|
// and last packet's sizes.
|
||||||
|
bool assume_shared_link = false;
|
||||||
|
|
||||||
|
// The estimator window keeps at least |min_packets| packets and up to
|
||||||
|
// kMaxPackets received during the last |window_duration|.
|
||||||
|
unsigned min_packets = 20;
|
||||||
|
TimeDelta window_duration = TimeDelta::ms(500);
|
||||||
|
|
||||||
|
std::unique_ptr<StructParametersParser> Parser();
|
||||||
|
};
|
||||||
|
|
||||||
|
class AcknowledgedBitrateEstimatorInterface {
|
||||||
|
public:
|
||||||
|
static std::unique_ptr<AcknowledgedBitrateEstimatorInterface> Create(
|
||||||
|
const WebRtcKeyValueConfig* key_value_config);
|
||||||
|
virtual ~AcknowledgedBitrateEstimatorInterface();
|
||||||
|
|
||||||
|
virtual void IncomingPacketFeedbackVector(
|
||||||
|
const std::vector<PacketResult>& packet_feedback_vector) = 0;
|
||||||
|
virtual absl::optional<DataRate> bitrate() const = 0;
|
||||||
|
virtual absl::optional<DataRate> PeekRate() const = 0;
|
||||||
|
virtual void SetAlr(bool in_alr) = 0;
|
||||||
|
virtual void SetAlrEndedTime(Timestamp alr_ended_time) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_
|
||||||
@ -149,7 +149,7 @@ DelayBasedBweTest::DelayBasedBweTest()
|
|||||||
: field_trial(),
|
: field_trial(),
|
||||||
clock_(100000000),
|
clock_(100000000),
|
||||||
acknowledged_bitrate_estimator_(
|
acknowledged_bitrate_estimator_(
|
||||||
std::make_unique<AcknowledgedBitrateEstimator>(&field_trial_config_)),
|
AcknowledgedBitrateEstimatorInterface::Create(&field_trial_config_)),
|
||||||
probe_bitrate_estimator_(new ProbeBitrateEstimator(nullptr)),
|
probe_bitrate_estimator_(new ProbeBitrateEstimator(nullptr)),
|
||||||
bitrate_estimator_(
|
bitrate_estimator_(
|
||||||
new DelayBasedBwe(&field_trial_config_, nullptr, nullptr)),
|
new DelayBasedBwe(&field_trial_config_, nullptr, nullptr)),
|
||||||
@ -163,7 +163,7 @@ DelayBasedBweTest::DelayBasedBweTest(const std::string& field_trial_string)
|
|||||||
std::make_unique<test::ScopedFieldTrials>(field_trial_string)),
|
std::make_unique<test::ScopedFieldTrials>(field_trial_string)),
|
||||||
clock_(100000000),
|
clock_(100000000),
|
||||||
acknowledged_bitrate_estimator_(
|
acknowledged_bitrate_estimator_(
|
||||||
std::make_unique<AcknowledgedBitrateEstimator>(&field_trial_config_)),
|
AcknowledgedBitrateEstimatorInterface::Create(&field_trial_config_)),
|
||||||
probe_bitrate_estimator_(new ProbeBitrateEstimator(nullptr)),
|
probe_bitrate_estimator_(new ProbeBitrateEstimator(nullptr)),
|
||||||
bitrate_estimator_(
|
bitrate_estimator_(
|
||||||
new DelayBasedBwe(&field_trial_config_, nullptr, nullptr)),
|
new DelayBasedBwe(&field_trial_config_, nullptr, nullptr)),
|
||||||
|
|||||||
@ -169,7 +169,8 @@ class DelayBasedBweTest : public ::testing::Test {
|
|||||||
field_trial; // Must be initialized first.
|
field_trial; // Must be initialized first.
|
||||||
SimulatedClock clock_; // Time at the receiver.
|
SimulatedClock clock_; // Time at the receiver.
|
||||||
test::TestBitrateObserver bitrate_observer_;
|
test::TestBitrateObserver bitrate_observer_;
|
||||||
std::unique_ptr<AcknowledgedBitrateEstimator> acknowledged_bitrate_estimator_;
|
std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
|
||||||
|
acknowledged_bitrate_estimator_;
|
||||||
const std::unique_ptr<ProbeBitrateEstimator> probe_bitrate_estimator_;
|
const std::unique_ptr<ProbeBitrateEstimator> probe_bitrate_estimator_;
|
||||||
std::unique_ptr<DelayBasedBwe> bitrate_estimator_;
|
std::unique_ptr<DelayBasedBwe> bitrate_estimator_;
|
||||||
std::unique_ptr<test::StreamGenerator> stream_generator_;
|
std::unique_ptr<test::StreamGenerator> stream_generator_;
|
||||||
|
|||||||
@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
#include "api/units/time_delta.h"
|
#include "api/units/time_delta.h"
|
||||||
#include "logging/rtc_event_log/events/rtc_event_remote_estimate.h"
|
#include "logging/rtc_event_log/events/rtc_event_remote_estimate.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/alr_detector.h"
|
||||||
#include "modules/congestion_controller/goog_cc/probe_controller.h"
|
#include "modules/congestion_controller/goog_cc/probe_controller.h"
|
||||||
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
|
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||||
@ -32,6 +31,7 @@
|
|||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// From RTCPSender video report interval.
|
// From RTCPSender video report interval.
|
||||||
constexpr TimeDelta kLossUpdateInterval = TimeDelta::Millis<1000>();
|
constexpr TimeDelta kLossUpdateInterval = TimeDelta::Millis<1000>();
|
||||||
@ -96,7 +96,7 @@ GoogCcNetworkController::GoogCcNetworkController(NetworkControllerConfig config,
|
|||||||
event_log_,
|
event_log_,
|
||||||
network_state_predictor_.get())),
|
network_state_predictor_.get())),
|
||||||
acknowledged_bitrate_estimator_(
|
acknowledged_bitrate_estimator_(
|
||||||
std::make_unique<AcknowledgedBitrateEstimator>(key_value_config_)),
|
AcknowledgedBitrateEstimatorInterface::Create(key_value_config_)),
|
||||||
initial_config_(config),
|
initial_config_(config),
|
||||||
last_loss_based_target_rate_(*config.constraints.starting_rate),
|
last_loss_based_target_rate_(*config.constraints.starting_rate),
|
||||||
last_pushback_target_rate_(last_loss_based_target_rate_),
|
last_pushback_target_rate_(last_loss_based_target_rate_),
|
||||||
@ -146,8 +146,8 @@ NetworkControlUpdate GoogCcNetworkController::OnNetworkRouteChange(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
acknowledged_bitrate_estimator_.reset(
|
acknowledged_bitrate_estimator_ =
|
||||||
new AcknowledgedBitrateEstimator(key_value_config_));
|
AcknowledgedBitrateEstimatorInterface::Create(key_value_config_);
|
||||||
probe_bitrate_estimator_.reset(new ProbeBitrateEstimator(event_log_));
|
probe_bitrate_estimator_.reset(new ProbeBitrateEstimator(event_log_));
|
||||||
if (network_estimator_)
|
if (network_estimator_)
|
||||||
network_estimator_->OnRouteChange(msg);
|
network_estimator_->OnRouteChange(msg);
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
#include "api/units/data_rate.h"
|
#include "api/units/data_rate.h"
|
||||||
#include "api/units/data_size.h"
|
#include "api/units/data_size.h"
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
|
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
|
||||||
#include "modules/congestion_controller/goog_cc/alr_detector.h"
|
#include "modules/congestion_controller/goog_cc/alr_detector.h"
|
||||||
#include "modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h"
|
#include "modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h"
|
||||||
#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
|
#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
|
||||||
@ -100,7 +100,8 @@ class GoogCcNetworkController : public NetworkControllerInterface {
|
|||||||
std::unique_ptr<NetworkStateEstimator> network_estimator_;
|
std::unique_ptr<NetworkStateEstimator> network_estimator_;
|
||||||
std::unique_ptr<NetworkStatePredictor> network_state_predictor_;
|
std::unique_ptr<NetworkStatePredictor> network_state_predictor_;
|
||||||
std::unique_ptr<DelayBasedBwe> delay_based_bwe_;
|
std::unique_ptr<DelayBasedBwe> delay_based_bwe_;
|
||||||
std::unique_ptr<AcknowledgedBitrateEstimator> acknowledged_bitrate_estimator_;
|
std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
|
||||||
|
acknowledged_bitrate_estimator_;
|
||||||
|
|
||||||
absl::optional<NetworkControllerConfig> initial_config_;
|
absl::optional<NetworkControllerConfig> initial_config_;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/robust_throughput_estimator.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "rtc_base/checks.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
RobustThroughputEstimator::RobustThroughputEstimator(
|
||||||
|
const RobustThroughputEstimatorSettings& settings)
|
||||||
|
: settings_(settings) {
|
||||||
|
RTC_DCHECK(settings.enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
RobustThroughputEstimator::~RobustThroughputEstimator() {}
|
||||||
|
|
||||||
|
void RobustThroughputEstimator::IncomingPacketFeedbackVector(
|
||||||
|
const std::vector<PacketResult>& packet_feedback_vector) {
|
||||||
|
RTC_DCHECK(std::is_sorted(packet_feedback_vector.begin(),
|
||||||
|
packet_feedback_vector.end(),
|
||||||
|
PacketResult::ReceiveTimeOrder()));
|
||||||
|
for (const auto& packet : packet_feedback_vector) {
|
||||||
|
// Insert the new packet.
|
||||||
|
window_.push_back(packet);
|
||||||
|
// In most cases, receive timestamps should already be in order, but in the
|
||||||
|
// rare case where feedback packets have been reordered, we do some swaps to
|
||||||
|
// ensure that the window is sorted.
|
||||||
|
for (size_t i = window_.size() - 1;
|
||||||
|
i > 0 && window_[i].receive_time < window_[i - 1].receive_time; i--) {
|
||||||
|
std::swap(window_[i], window_[i - 1]);
|
||||||
|
}
|
||||||
|
// Remove old packets.
|
||||||
|
while (window_.size() > settings_.kMaxPackets ||
|
||||||
|
(window_.size() > settings_.min_packets &&
|
||||||
|
packet.receive_time - window_.front().receive_time >
|
||||||
|
settings_.window_duration)) {
|
||||||
|
window_.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<DataRate> RobustThroughputEstimator::bitrate() const {
|
||||||
|
if (window_.size() < settings_.min_packets)
|
||||||
|
return absl::nullopt;
|
||||||
|
|
||||||
|
TimeDelta largest_recv_gap(TimeDelta::ms(0));
|
||||||
|
TimeDelta second_largest_recv_gap(TimeDelta::ms(0));
|
||||||
|
for (size_t i = 1; i < window_.size(); i++) {
|
||||||
|
// Find receive time gaps
|
||||||
|
TimeDelta gap = window_[i].receive_time - window_[i - 1].receive_time;
|
||||||
|
if (gap > largest_recv_gap) {
|
||||||
|
second_largest_recv_gap = largest_recv_gap;
|
||||||
|
largest_recv_gap = gap;
|
||||||
|
} else if (gap > second_largest_recv_gap) {
|
||||||
|
second_largest_recv_gap = gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timestamp min_send_time = window_[0].sent_packet.send_time;
|
||||||
|
Timestamp max_send_time = window_[0].sent_packet.send_time;
|
||||||
|
Timestamp min_recv_time = window_[0].receive_time;
|
||||||
|
Timestamp max_recv_time = window_[0].receive_time;
|
||||||
|
DataSize data_size = DataSize::bytes(0);
|
||||||
|
for (const auto& packet : window_) {
|
||||||
|
min_send_time = std::min(min_send_time, packet.sent_packet.send_time);
|
||||||
|
max_send_time = std::max(max_send_time, packet.sent_packet.send_time);
|
||||||
|
min_recv_time = std::min(min_recv_time, packet.receive_time);
|
||||||
|
max_recv_time = std::max(max_recv_time, packet.receive_time);
|
||||||
|
data_size += packet.sent_packet.size;
|
||||||
|
data_size += packet.sent_packet.prior_unacked_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suppose a packet of size S is sent every T milliseconds.
|
||||||
|
// A window of N packets would contain N*S bytes, but the time difference
|
||||||
|
// between the first and the last packet would only be (N-1)*T. Thus, we
|
||||||
|
// need to remove one packet.
|
||||||
|
DataSize recv_size = data_size;
|
||||||
|
DataSize send_size = data_size;
|
||||||
|
if (settings_.assume_shared_link) {
|
||||||
|
// Depending on how the bottleneck queue is implemented, a large packet
|
||||||
|
// may delay sending of sebsequent packets, so the delay between packets
|
||||||
|
// i and i+1 depends on the size of both packets. In this case we minimize
|
||||||
|
// the maximum error by removing half of both the first and last packet
|
||||||
|
// size.
|
||||||
|
DataSize first_last_average_size =
|
||||||
|
(window_.front().sent_packet.size +
|
||||||
|
window_.front().sent_packet.prior_unacked_data +
|
||||||
|
window_.back().sent_packet.size +
|
||||||
|
window_.back().sent_packet.prior_unacked_data) /
|
||||||
|
2;
|
||||||
|
recv_size -= first_last_average_size;
|
||||||
|
send_size -= first_last_average_size;
|
||||||
|
} else {
|
||||||
|
// In the simpler case where the delay between packets i and i+1 only
|
||||||
|
// depends on the size of packet i+1, the first packet doesn't give us
|
||||||
|
// any information. Analogously, we assume that the start send time
|
||||||
|
// for the last packet doesn't depend on the size of the packet.
|
||||||
|
recv_size -= (window_.front().sent_packet.size +
|
||||||
|
window_.front().sent_packet.prior_unacked_data);
|
||||||
|
send_size -= (window_.back().sent_packet.size +
|
||||||
|
window_.back().sent_packet.prior_unacked_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the largest gap by replacing it by the second largest gap
|
||||||
|
// or the average gap.
|
||||||
|
TimeDelta send_duration = max_send_time - min_send_time;
|
||||||
|
TimeDelta recv_duration = (max_recv_time - min_recv_time) - largest_recv_gap;
|
||||||
|
if (settings_.reduce_bias) {
|
||||||
|
recv_duration += second_largest_recv_gap;
|
||||||
|
} else {
|
||||||
|
recv_duration += recv_duration / (window_.size() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
send_duration = std::max(send_duration, TimeDelta::ms(1));
|
||||||
|
recv_duration = std::max(recv_duration, TimeDelta::ms(1));
|
||||||
|
return std::min(send_size / send_duration, recv_size / recv_duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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_ROBUST_THROUGHPUT_ESTIMATOR_H_
|
||||||
|
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ROBUST_THROUGHPUT_ESTIMATOR_H_
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "absl/types/optional.h"
|
||||||
|
#include "api/transport/network_types.h"
|
||||||
|
#include "api/transport/webrtc_key_value_config.h"
|
||||||
|
#include "api/units/data_rate.h"
|
||||||
|
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class RobustThroughputEstimator : public AcknowledgedBitrateEstimatorInterface {
|
||||||
|
public:
|
||||||
|
explicit RobustThroughputEstimator(
|
||||||
|
const RobustThroughputEstimatorSettings& settings);
|
||||||
|
~RobustThroughputEstimator() override;
|
||||||
|
|
||||||
|
void IncomingPacketFeedbackVector(
|
||||||
|
const std::vector<PacketResult>& packet_feedback_vector) override;
|
||||||
|
|
||||||
|
absl::optional<DataRate> bitrate() const override;
|
||||||
|
|
||||||
|
absl::optional<DataRate> PeekRate() const override { return bitrate(); }
|
||||||
|
void SetAlr(bool /*in_alr*/) override {}
|
||||||
|
void SetAlrEndedTime(Timestamp /*alr_ended_time*/) override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const RobustThroughputEstimatorSettings settings_;
|
||||||
|
std::deque<PacketResult> window_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ROBUST_THROUGHPUT_ESTIMATOR_H_
|
||||||
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/robust_throughput_estimator.h"
|
||||||
|
|
||||||
|
#include "api/transport/field_trial_based_config.h"
|
||||||
|
#include "test/field_trial.h"
|
||||||
|
#include "test/gtest.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace {
|
||||||
|
std::vector<PacketResult> CreateFeedbackVector(size_t number_of_packets,
|
||||||
|
DataSize packet_size,
|
||||||
|
TimeDelta send_increment,
|
||||||
|
TimeDelta recv_increment,
|
||||||
|
Timestamp* send_clock,
|
||||||
|
Timestamp* recv_clock,
|
||||||
|
uint16_t* sequence_number) {
|
||||||
|
std::vector<PacketResult> packet_feedback_vector(number_of_packets);
|
||||||
|
for (size_t i = 0; i < number_of_packets; i++) {
|
||||||
|
packet_feedback_vector[i].receive_time = *recv_clock;
|
||||||
|
packet_feedback_vector[i].sent_packet.send_time = *send_clock;
|
||||||
|
packet_feedback_vector[i].sent_packet.sequence_number = *sequence_number;
|
||||||
|
packet_feedback_vector[i].sent_packet.size = packet_size;
|
||||||
|
*send_clock += send_increment;
|
||||||
|
*recv_clock += recv_increment;
|
||||||
|
*sequence_number += 1;
|
||||||
|
}
|
||||||
|
return packet_feedback_vector;
|
||||||
|
}
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
TEST(RobustThroughputEstimatorTest, SteadyRate) {
|
||||||
|
webrtc::test::ScopedFieldTrials field_trials(
|
||||||
|
"WebRTC-Bwe-RobustThroughputEstimatorSettings/"
|
||||||
|
"enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10,"
|
||||||
|
"window_duration:100ms/");
|
||||||
|
FieldTrialBasedConfig field_trial_config;
|
||||||
|
RobustThroughputEstimatorSettings settings(&field_trial_config);
|
||||||
|
RobustThroughputEstimator throughput_estimator(settings);
|
||||||
|
DataSize packet_size(DataSize::bytes(1000));
|
||||||
|
Timestamp send_clock(Timestamp::ms(100000));
|
||||||
|
Timestamp recv_clock(Timestamp::ms(10000));
|
||||||
|
TimeDelta send_increment(TimeDelta::ms(10));
|
||||||
|
TimeDelta recv_increment(TimeDelta::ms(10));
|
||||||
|
uint16_t sequence_number = 100;
|
||||||
|
std::vector<PacketResult> packet_feedback =
|
||||||
|
CreateFeedbackVector(9, packet_size, send_increment, recv_increment,
|
||||||
|
&send_clock, &recv_clock, &sequence_number);
|
||||||
|
throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
|
||||||
|
EXPECT_FALSE(throughput_estimator.bitrate().has_value());
|
||||||
|
|
||||||
|
packet_feedback =
|
||||||
|
CreateFeedbackVector(11, packet_size, send_increment, recv_increment,
|
||||||
|
&send_clock, &recv_clock, &sequence_number);
|
||||||
|
throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
|
||||||
|
auto throughput = throughput_estimator.bitrate();
|
||||||
|
EXPECT_TRUE(throughput.has_value());
|
||||||
|
EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), 100 * 1000.0,
|
||||||
|
0.05 * 100 * 1000.0); // Allow 5% error
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RobustThroughputEstimatorTest, DelaySpike) {
|
||||||
|
webrtc::test::ScopedFieldTrials field_trials(
|
||||||
|
"WebRTC-Bwe-RobustThroughputEstimatorSettings/"
|
||||||
|
"enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10,"
|
||||||
|
"window_duration:100ms/");
|
||||||
|
FieldTrialBasedConfig field_trial_config;
|
||||||
|
RobustThroughputEstimatorSettings settings(&field_trial_config);
|
||||||
|
RobustThroughputEstimator throughput_estimator(settings);
|
||||||
|
DataSize packet_size(DataSize::bytes(1000));
|
||||||
|
Timestamp send_clock(Timestamp::ms(100000));
|
||||||
|
Timestamp recv_clock(Timestamp::ms(10000));
|
||||||
|
TimeDelta send_increment(TimeDelta::ms(10));
|
||||||
|
TimeDelta recv_increment(TimeDelta::ms(10));
|
||||||
|
uint16_t sequence_number = 100;
|
||||||
|
std::vector<PacketResult> packet_feedback =
|
||||||
|
CreateFeedbackVector(20, packet_size, send_increment, recv_increment,
|
||||||
|
&send_clock, &recv_clock, &sequence_number);
|
||||||
|
throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
|
||||||
|
auto throughput = throughput_estimator.bitrate();
|
||||||
|
EXPECT_TRUE(throughput.has_value());
|
||||||
|
EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), 100 * 1000.0,
|
||||||
|
0.05 * 100 * 1000.0); // Allow 5% error
|
||||||
|
|
||||||
|
// Delay spike
|
||||||
|
recv_clock += TimeDelta::ms(40);
|
||||||
|
|
||||||
|
// Faster delivery after the gap
|
||||||
|
recv_increment = TimeDelta::ms(2);
|
||||||
|
packet_feedback =
|
||||||
|
CreateFeedbackVector(5, packet_size, send_increment, recv_increment,
|
||||||
|
&send_clock, &recv_clock, &sequence_number);
|
||||||
|
throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
|
||||||
|
throughput = throughput_estimator.bitrate();
|
||||||
|
EXPECT_TRUE(throughput.has_value());
|
||||||
|
EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), 100 * 1000.0,
|
||||||
|
0.05 * 100 * 1000.0); // Allow 5% error
|
||||||
|
|
||||||
|
// Delivery at normal rate. This will be capped by the send rate.
|
||||||
|
recv_increment = TimeDelta::ms(10);
|
||||||
|
packet_feedback =
|
||||||
|
CreateFeedbackVector(5, packet_size, send_increment, recv_increment,
|
||||||
|
&send_clock, &recv_clock, &sequence_number);
|
||||||
|
throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
|
||||||
|
throughput = throughput_estimator.bitrate();
|
||||||
|
EXPECT_TRUE(throughput.has_value());
|
||||||
|
EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), 100 * 1000.0,
|
||||||
|
0.05 * 100 * 1000.0); // Allow 5% error
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RobustThroughputEstimatorTest, CappedByReceiveRate) {
|
||||||
|
webrtc::test::ScopedFieldTrials field_trials(
|
||||||
|
"WebRTC-Bwe-RobustThroughputEstimatorSettings/"
|
||||||
|
"enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10,"
|
||||||
|
"window_duration:100ms/");
|
||||||
|
FieldTrialBasedConfig field_trial_config;
|
||||||
|
RobustThroughputEstimatorSettings settings(&field_trial_config);
|
||||||
|
RobustThroughputEstimator throughput_estimator(settings);
|
||||||
|
DataSize packet_size(DataSize::bytes(1000));
|
||||||
|
Timestamp send_clock(Timestamp::ms(100000));
|
||||||
|
Timestamp recv_clock(Timestamp::ms(10000));
|
||||||
|
TimeDelta send_increment(TimeDelta::ms(10));
|
||||||
|
TimeDelta recv_increment(TimeDelta::ms(40));
|
||||||
|
uint16_t sequence_number = 100;
|
||||||
|
std::vector<PacketResult> packet_feedback =
|
||||||
|
CreateFeedbackVector(20, packet_size, send_increment, recv_increment,
|
||||||
|
&send_clock, &recv_clock, &sequence_number);
|
||||||
|
throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
|
||||||
|
auto throughput = throughput_estimator.bitrate();
|
||||||
|
EXPECT_TRUE(throughput.has_value());
|
||||||
|
EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), 25 * 1000.0,
|
||||||
|
0.05 * 25 * 1000.0); // Allow 5% error
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RobustThroughputEstimatorTest, CappedBySendRate) {
|
||||||
|
webrtc::test::ScopedFieldTrials field_trials(
|
||||||
|
"WebRTC-Bwe-RobustThroughputEstimatorSettings/"
|
||||||
|
"enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10,"
|
||||||
|
"window_duration:100ms/");
|
||||||
|
FieldTrialBasedConfig field_trial_config;
|
||||||
|
RobustThroughputEstimatorSettings settings(&field_trial_config);
|
||||||
|
RobustThroughputEstimator throughput_estimator(settings);
|
||||||
|
DataSize packet_size(DataSize::bytes(1000));
|
||||||
|
Timestamp send_clock(Timestamp::ms(100000));
|
||||||
|
Timestamp recv_clock(Timestamp::ms(10000));
|
||||||
|
TimeDelta send_increment(TimeDelta::ms(20));
|
||||||
|
TimeDelta recv_increment(TimeDelta::ms(10));
|
||||||
|
uint16_t sequence_number = 100;
|
||||||
|
std::vector<PacketResult> packet_feedback =
|
||||||
|
CreateFeedbackVector(20, packet_size, send_increment, recv_increment,
|
||||||
|
&send_clock, &recv_clock, &sequence_number);
|
||||||
|
throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
|
||||||
|
auto throughput = throughput_estimator.bitrate();
|
||||||
|
EXPECT_TRUE(throughput.has_value());
|
||||||
|
EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), 50 * 1000.0,
|
||||||
|
0.05 * 50 * 1000.0); // Allow 5% error
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc*/
|
||||||
@ -1269,14 +1269,14 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) {
|
|||||||
FieldTrialBasedConfig field_trial_config_;
|
FieldTrialBasedConfig field_trial_config_;
|
||||||
// The event_log_visualizer should normally not be compiled with
|
// The event_log_visualizer should normally not be compiled with
|
||||||
// BWE_TEST_LOGGING_COMPILE_TIME_ENABLE since the normal plots won't work.
|
// BWE_TEST_LOGGING_COMPILE_TIME_ENABLE since the normal plots won't work.
|
||||||
// However, compiling with BWE_TEST_LOGGING, running with --plot_sendside_bwe
|
// However, compiling with BWE_TEST_LOGGING, running with --plot=sendside_bwe
|
||||||
// and piping the output to plot_dynamics.py can be used as a hack to get the
|
// and piping the output to plot_dynamics.py can be used as a hack to get the
|
||||||
// internal state of various BWE components. In this case, it is important
|
// internal state of various BWE components. In this case, it is important
|
||||||
// we don't instantiate the AcknowledgedBitrateEstimator both here and in
|
// we don't instantiate the AcknowledgedBitrateEstimator both here and in
|
||||||
// GoogCcNetworkController since that would lead to duplicate outputs.
|
// GoogCcNetworkController since that would lead to duplicate outputs.
|
||||||
AcknowledgedBitrateEstimator acknowledged_bitrate_estimator(
|
std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
|
||||||
&field_trial_config_,
|
acknowledged_bitrate_estimator(
|
||||||
std::make_unique<BitrateEstimator>(&field_trial_config_));
|
AcknowledgedBitrateEstimatorInterface::Create(&field_trial_config_));
|
||||||
#endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
|
#endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
|
||||||
int64_t time_us =
|
int64_t time_us =
|
||||||
std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()});
|
std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()});
|
||||||
@ -1321,7 +1321,8 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) {
|
|||||||
feedback_msg->SortedByReceiveTime();
|
feedback_msg->SortedByReceiveTime();
|
||||||
if (!feedback.empty()) {
|
if (!feedback.empty()) {
|
||||||
#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
|
#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
|
||||||
acknowledged_bitrate_estimator.IncomingPacketFeedbackVector(feedback);
|
acknowledged_bitrate_estimator->IncomingPacketFeedbackVector(
|
||||||
|
feedback);
|
||||||
#endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
|
#endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
|
||||||
for (const PacketResult& packet : feedback)
|
for (const PacketResult& packet : feedback)
|
||||||
acked_bitrate.Update(packet.sent_packet.size.bytes(),
|
acked_bitrate.Update(packet.sent_packet.size.bytes(),
|
||||||
@ -1334,7 +1335,7 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) {
|
|||||||
float y = bitrate_bps.value_or(0) / 1000;
|
float y = bitrate_bps.value_or(0) / 1000;
|
||||||
acked_time_series.points.emplace_back(x, y);
|
acked_time_series.points.emplace_back(x, y);
|
||||||
#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
|
#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
|
||||||
y = acknowledged_bitrate_estimator.bitrate()
|
y = acknowledged_bitrate_estimator->bitrate()
|
||||||
.value_or(DataRate::Zero())
|
.value_or(DataRate::Zero())
|
||||||
.kbps();
|
.kbps();
|
||||||
acked_estimate_time_series.points.emplace_back(x, y);
|
acked_estimate_time_series.points.emplace_back(x, y);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user