Filter out small packets from delay-based overuse detection.

The change is behind a field trial. The intention is to use this
to (heuristically) base the bandwidth estimate only on video packets
even if both audio and video packets have transport sequence numbers.

Bug: webrtc:10932
Change-Id: I6cc5bb9ab6f1a3f25b84ee6ac78e4abb4112032e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/150787
Commit-Queue: Björn Terelius <terelius@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29031}
This commit is contained in:
Bjorn Terelius
2019-09-01 16:25:14 +02:00
committed by Commit Bot
parent f660e81a56
commit 602942f14c
5 changed files with 239 additions and 114 deletions

View File

@ -158,9 +158,10 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback,
packet_feedback.sent_packet.size.bytes(), &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.sent_packet.send_time.ms(),
packet_feedback.receive_time.ms(), calculated_deltas);
delay_detector_->Update(
t_delta, ts_delta_ms, packet_feedback.sent_packet.send_time.ms(),
packet_feedback.receive_time.ms(),
packet_feedback.sent_packet.size.bytes(), calculated_deltas);
}
DataRate DelayBasedBwe::TriggerOveruse(Timestamp at_time,

View File

@ -28,6 +28,7 @@ class DelayIncreaseDetectorInterface {
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size,
bool calculated_deltas) = 0;
virtual BandwidthUsage State() const = 0;

View File

@ -19,11 +19,29 @@
#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/experiments/struct_parameters_parser.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_minmax.h"
namespace webrtc {
constexpr char BweIgnoreSmallPacketsSettings::kKey[];
BweIgnoreSmallPacketsSettings::BweIgnoreSmallPacketsSettings(
const WebRtcKeyValueConfig* key_value_config) {
Parser()->Parse(
key_value_config->Lookup(BweIgnoreSmallPacketsSettings::kKey));
}
std::unique_ptr<StructParametersParser>
BweIgnoreSmallPacketsSettings::Parser() {
return StructParametersParser::Create(
"smoothing_factor", &smoothing_factor, //
"min_fraction_large_packets", &min_fraction_large_packets, //
"large_packet_size", &large_packet_size, //
"ignored_size", &ignored_size);
}
namespace {
// Parameters for linear least squares fit of regression line to noisy data.
@ -84,23 +102,14 @@ constexpr int kDeltaCounterMax = 1000;
TrendlineEstimator::TrendlineEstimator(
const WebRtcKeyValueConfig* key_value_config,
NetworkStatePredictor* network_state_predictor)
: TrendlineEstimator(
key_value_config->Lookup(kBweWindowSizeInPacketsExperiment)
: ignore_small_packets_(key_value_config),
fraction_large_packets_(0.5),
window_size_(key_value_config->Lookup(kBweWindowSizeInPacketsExperiment)
.find("Enabled") == 0
? ReadTrendlineFilterWindowSize(key_value_config)
: kDefaultTrendlineWindowSize,
kDefaultTrendlineSmoothingCoeff,
kDefaultTrendlineThresholdGain,
network_state_predictor) {}
TrendlineEstimator::TrendlineEstimator(
size_t window_size,
double smoothing_coef,
double threshold_gain,
NetworkStatePredictor* network_state_predictor)
: window_size_(window_size),
smoothing_coef_(smoothing_coef),
threshold_gain_(threshold_gain),
: kDefaultTrendlineWindowSize),
smoothing_coef_(kDefaultTrendlineSmoothingCoeff),
threshold_gain_(kDefaultTrendlineThresholdGain),
num_of_deltas_(0),
first_arrival_time_ms_(-1),
accumulated_delay_(0),
@ -120,17 +129,34 @@ TrendlineEstimator::TrendlineEstimator(
network_state_predictor_(network_state_predictor) {
RTC_LOG(LS_INFO)
<< "Using Trendline filter for delay change estimation with window size "
<< window_size_;
<< window_size_ << " and field trial "
<< ignore_small_packets_.Parser()->Encode();
}
TrendlineEstimator::~TrendlineEstimator() {}
void TrendlineEstimator::Update(double recv_delta_ms,
void TrendlineEstimator::UpdateTrendline(double recv_delta_ms,
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
bool calculated_deltas) {
if (calculated_deltas) {
size_t packet_size) {
if (ignore_small_packets_.ignored_size > 0) {
// Process the packet if it is "large" or if all packets in the call are
// "small". The packet size may have a significant effect on the propagation
// delay, especially at low bandwidths. Variations in packet size will then
// show up as noise in the delay measurement.
// By default, we include all packets.
fraction_large_packets_ =
(1 - ignore_small_packets_.smoothing_factor) * fraction_large_packets_ +
ignore_small_packets_.smoothing_factor *
(packet_size >= ignore_small_packets_.large_packet_size);
if (packet_size <= ignore_small_packets_.ignored_size &&
fraction_large_packets_ >=
ignore_small_packets_.min_fraction_large_packets) {
return;
}
}
const double delta_ms = recv_delta_ms - send_delta_ms;
++num_of_deltas_;
num_of_deltas_ = std::min(num_of_deltas_, kDeltaCounterMax);
@ -161,11 +187,21 @@ void TrendlineEstimator::Update(double recv_delta_ms,
// trend < 0 -> the delay decreases, queues are being emptied
trend = LinearFitSlope(delay_hist_).value_or(trend);
}
BWE_TEST_LOGGING_PLOT(1, "trendline_slope", arrival_time_ms, trend);
Detect(trend, send_delta_ms, arrival_time_ms);
}
void TrendlineEstimator::Update(double recv_delta_ms,
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size,
bool calculated_deltas) {
if (calculated_deltas) {
UpdateTrendline(recv_delta_ms, send_delta_ms, send_time_ms, arrival_time_ms,
packet_size);
}
if (network_state_predictor_) {
hypothesis_predicted_ = network_state_predictor_->Update(
send_time_ms, arrival_time_ms, hypothesis_);

View File

@ -14,6 +14,7 @@
#include <stdint.h>
#include <deque>
#include <memory>
#include <utility>
#include "api/network_state_predictor.h"
@ -21,24 +22,29 @@
#include "modules/congestion_controller/goog_cc/delay_increase_detector_interface.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
namespace webrtc {
struct BweIgnoreSmallPacketsSettings {
static constexpr char kKey[] = "WebRTC-BweIgnoreSmallPackets";
BweIgnoreSmallPacketsSettings() = default;
explicit BweIgnoreSmallPacketsSettings(
const WebRtcKeyValueConfig* key_value_config);
double smoothing_factor = 0.1;
double min_fraction_large_packets = 1.0;
unsigned large_packet_size = 0;
unsigned ignored_size = 0;
std::unique_ptr<StructParametersParser> Parser();
};
class TrendlineEstimator : public DelayIncreaseDetectorInterface {
public:
TrendlineEstimator(const WebRtcKeyValueConfig* key_value_config,
NetworkStatePredictor* network_state_predictor);
// |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.|network_state_predictor| is used to
// bettter predict network state.
TrendlineEstimator(size_t window_size,
double smoothing_coef,
double threshold_gain,
NetworkStatePredictor* network_state_predictor);
~TrendlineEstimator() override;
@ -48,13 +54,16 @@ class TrendlineEstimator : public DelayIncreaseDetectorInterface {
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size,
bool calculated_deltas) override;
BandwidthUsage State() const override;
void UpdateTrendline(double recv_delta_ms,
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size);
protected:
// Used in unit tests.
double modified_trend() const { return prev_trend_ * threshold_gain_; }
BandwidthUsage State() const override;
private:
friend class GoogCcStatePrinter;
@ -63,6 +72,11 @@ class TrendlineEstimator : public DelayIncreaseDetectorInterface {
void UpdateThreshold(double modified_offset, int64_t now_ms);
// Filtering out small packets. (Intention is to base the detection only
// on video packets even if we have TWCC sequence number for audio.)
BweIgnoreSmallPacketsSettings ignore_small_packets_;
double fraction_large_packets_;
// Parameters.
const size_t window_size_;
const double smoothing_coef_;

View File

@ -10,69 +10,142 @@
#include "modules/congestion_controller/goog_cc/trendline_estimator.h"
#include <algorithm>
#include <numeric>
#include <vector>
#include "api/transport/field_trial_based_config.h"
#include "rtc_base/random.h"
#include "test/gtest.h"
namespace webrtc {
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;
class TrendlineEstimatorForTest : public TrendlineEstimator {
class PacketTimeGenerator {
public:
using TrendlineEstimator::modified_trend;
using TrendlineEstimator::TrendlineEstimator;
PacketTimeGenerator(int64_t initial_clock, double time_between_packets)
: initial_clock_(initial_clock),
time_between_packets_(time_between_packets),
packets_(0) {}
int64_t operator()() {
return initial_clock_ + time_between_packets_ * packets_++;
}
private:
const int64_t initial_clock_;
const double time_between_packets_;
size_t packets_;
};
void TestEstimator(double slope, double jitter_stddev, double tolerance) {
TrendlineEstimatorForTest estimator(kWindowSize, kSmoothing, kGain, nullptr);
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;
class TrendlineEstimatorTest : public testing::Test {
public:
TrendlineEstimatorTest()
: send_times(kPacketCount),
recv_times(kPacketCount),
packet_sizes(kPacketCount),
config(),
estimator(&config, nullptr),
count(1) {
std::fill(packet_sizes.begin(), packet_sizes.end(), kPacketSizeBytes);
}
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, 0, recv_times[i], true);
if (i < kWindowSize)
EXPECT_NEAR(estimator.modified_trend(), 0, 0.001);
else
EXPECT_NEAR(estimator.modified_trend(), slope, tolerance);
void RunTestUntilStateChange() {
RTC_DCHECK_EQ(send_times.size(), kPacketCount);
RTC_DCHECK_EQ(recv_times.size(), kPacketCount);
RTC_DCHECK_EQ(packet_sizes.size(), kPacketCount);
RTC_DCHECK_GE(count, 1);
RTC_DCHECK_LT(count, kPacketCount);
auto initial_state = estimator.State();
for (; count < kPacketCount; count++) {
double recv_delta = recv_times[count] - recv_times[count - 1];
double send_delta = send_times[count] - send_times[count - 1];
estimator.Update(recv_delta, send_delta, send_times[count],
recv_times[count], packet_sizes[count], true);
if (estimator.State() != initial_state) {
return;
}
}
}
protected:
const size_t kPacketCount = 25;
const size_t kPacketSizeBytes = 1200;
std::vector<int64_t> send_times;
std::vector<int64_t> recv_times;
std::vector<size_t> packet_sizes;
const FieldTrialBasedConfig config;
TrendlineEstimator estimator;
size_t count;
};
} // namespace
TEST(TrendlineEstimator, PerfectLineSlopeOneHalf) {
TestEstimator(0.5, 0, 0.001);
TEST_F(TrendlineEstimatorTest, Normal) {
PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
20 /*20 ms between sent packets*/);
std::generate(send_times.begin(), send_times.end(), send_time_generator);
PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
20 /*delivered at the same pace*/);
std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
EXPECT_EQ(count, kPacketCount); // All packets processed
}
TEST(TrendlineEstimator, PerfectLineSlopeMinusOne) {
TestEstimator(-1, 0, 0.001);
TEST_F(TrendlineEstimatorTest, Overusing) {
PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
20 /*20 ms between sent packets*/);
std::generate(send_times.begin(), send_times.end(), send_time_generator);
PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
1.1 * 20 /*10% slower delivery*/);
std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
EXPECT_EQ(count, kPacketCount); // All packets processed
}
TEST(TrendlineEstimator, PerfectLineSlopeZero) {
TestEstimator(0, 0, 0.001);
TEST_F(TrendlineEstimatorTest, Underusing) {
PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
20 /*20 ms between sent packets*/);
std::generate(send_times.begin(), send_times.end(), send_time_generator);
PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
0.85 * 20 /*15% faster delivery*/);
std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwUnderusing);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwUnderusing);
EXPECT_EQ(count, kPacketCount); // All packets processed
}
TEST(TrendlineEstimator, JitteryLineSlopeOneHalf) {
TestEstimator(0.5, kAvgTimeBetweenPackets / 3.0, 0.01);
}
TEST_F(TrendlineEstimatorTest, IncludesSmallPacketsByDefault) {
PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
20 /*20 ms between sent packets*/);
std::generate(send_times.begin(), send_times.end(), send_time_generator);
TEST(TrendlineEstimator, JitteryLineSlopeMinusOne) {
TestEstimator(-1, kAvgTimeBetweenPackets / 3.0, 0.075);
}
PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
1.1 * 20 /*10% slower delivery*/);
std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
TEST(TrendlineEstimator, JitteryLineSlopeZero) {
TestEstimator(0, kAvgTimeBetweenPackets / 3.0, 0.02);
std::fill(packet_sizes.begin(), packet_sizes.end(), 100);
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
EXPECT_EQ(count, kPacketCount); // All packets processed
}
} // namespace webrtc