Merge remote bitrate estimator changes.
R=stefan@webrtc.org BUG= Review URL: https://webrtc-codereview.appspot.com/33489004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7811 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
33ccdfa1f5
commit
9f79fe684a
@ -40,7 +40,6 @@
|
||||
'modules_java_gyp_path%': '<(modules_java_gyp_path)',
|
||||
'webrtc_vp8_dir%': '<(webrtc_root)/modules/video_coding/codecs/vp8',
|
||||
'webrtc_vp9_dir%': '<(webrtc_root)/modules/video_coding/codecs/vp9',
|
||||
'rbe_components_path%': '<(webrtc_root)/modules/remote_bitrate_estimator',
|
||||
'include_opus%': 1,
|
||||
},
|
||||
'build_with_chromium%': '<(build_with_chromium)',
|
||||
@ -52,7 +51,6 @@
|
||||
'webrtc_vp9_dir%': '<(webrtc_vp9_dir)',
|
||||
'include_opus%': '<(include_opus)',
|
||||
'rtc_relative_path%': 1,
|
||||
'rbe_components_path%': '<(rbe_components_path)',
|
||||
'external_libraries%': '0',
|
||||
'json_root%': '<(DEPTH)/third_party/jsoncpp/source/include/',
|
||||
# openssl needs to be defined or gyp will complain. Is is only used when
|
||||
|
@ -89,12 +89,12 @@
|
||||
'webrtc_utility',
|
||||
'webrtc_video_coding',
|
||||
'<@(neteq_dependencies)',
|
||||
'<(rbe_components_path)/remote_bitrate_estimator_components.gyp:rbe_components',
|
||||
'<(DEPTH)/testing/gmock.gyp:gmock',
|
||||
'<(DEPTH)/testing/gtest.gyp:gtest',
|
||||
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
|
||||
'<(webrtc_root)/common_audio/common_audio.gyp:common_audio',
|
||||
'<(webrtc_root)/modules/modules.gyp:video_capture_module_impl',
|
||||
'<(webrtc_root)/modules/remote_bitrate_estimator/remote_bitrate_estimator_components.gyp:rbe_components',
|
||||
'<(webrtc_root)/modules/video_coding/codecs/vp8/vp8.gyp:webrtc_vp8',
|
||||
'<(webrtc_root)/modules/video_coding/codecs/vp9/vp9.gyp:webrtc_vp9',
|
||||
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
||||
@ -186,11 +186,16 @@
|
||||
'pacing/paced_sender_unittest.cc',
|
||||
'remote_bitrate_estimator/bwe_simulations.cc',
|
||||
'remote_bitrate_estimator/include/mock/mock_remote_bitrate_observer.h',
|
||||
'remote_bitrate_estimator/inter_arrival_unittest.cc',
|
||||
'remote_bitrate_estimator/overuse_detector_unittest.cc',
|
||||
'remote_bitrate_estimator/rate_statistics_unittest.cc',
|
||||
'remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc',
|
||||
'remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc',
|
||||
'remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc',
|
||||
'remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h',
|
||||
'remote_bitrate_estimator/remote_bitrate_estimators_test.cc',
|
||||
'remote_bitrate_estimator/test/bwe_test.cc',
|
||||
'remote_bitrate_estimator/test/bwe_test.h',
|
||||
'remote_bitrate_estimator/test/bwe_test_baselinefile.cc',
|
||||
'remote_bitrate_estimator/test/bwe_test_baselinefile.h',
|
||||
'remote_bitrate_estimator/test/bwe_test_fileutils.cc',
|
||||
@ -200,8 +205,6 @@
|
||||
'remote_bitrate_estimator/test/bwe_test_framework_unittest.cc',
|
||||
'remote_bitrate_estimator/test/bwe_test_logging.cc',
|
||||
'remote_bitrate_estimator/test/bwe_test_logging.h',
|
||||
'remote_bitrate_estimator/test/bwe_test.cc',
|
||||
'remote_bitrate_estimator/test/bwe_test.h',
|
||||
'rtp_rtcp/source/mock/mock_rtp_payload_strategy.h',
|
||||
'rtp_rtcp/source/byte_io_unittest.cc',
|
||||
'rtp_rtcp/source/fec_receiver_unittest.cc',
|
||||
|
@ -24,8 +24,17 @@ source_set("remote_bitrate_estimator") {
|
||||
|
||||
source_set("rbe_components") {
|
||||
sources = [
|
||||
"aimd_rate_control.cc",
|
||||
"aimd_rate_control.h",
|
||||
"inter_arrival.cc",
|
||||
"inter_arrival.h",
|
||||
"mimd_rate_control.cc",
|
||||
"mimd_rate_control.h",
|
||||
"overuse_detector.cc",
|
||||
"overuse_detector.h",
|
||||
"overuse_estimator.cc",
|
||||
"overuse_estimator.h",
|
||||
"remote_bitrate_estimator_abs_send_time.cc",
|
||||
"remote_bitrate_estimator_single_stream.cc",
|
||||
"remote_rate_control.cc",
|
||||
"remote_rate_control.h",
|
||||
|
339
webrtc/modules/remote_bitrate_estimator/aimd_rate_control.cc
Normal file
339
webrtc/modules/remote_bitrate_estimator/aimd_rate_control.cc
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright (c) 2014 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 "webrtc/modules/remote_bitrate_estimator/aimd_rate_control.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
static const uint32_t kDefaultRttMs = 200;
|
||||
static const int64_t kLogIntervalMs = 1000;
|
||||
static const int kMinFeedbackIntervalMs = 200;
|
||||
static const double kWithinIncomingBitrateHysteresis = 1.05;
|
||||
|
||||
AimdRateControl::AimdRateControl(uint32_t min_bitrate_bps)
|
||||
: min_configured_bitrate_bps_(min_bitrate_bps),
|
||||
max_configured_bitrate_bps_(30000000),
|
||||
current_bitrate_bps_(max_configured_bitrate_bps_),
|
||||
max_hold_rate_bps_(0),
|
||||
avg_max_bitrate_kbps_(-1.0f),
|
||||
var_max_bitrate_kbps_(0.4f),
|
||||
rate_control_state_(kRcHold),
|
||||
came_from_state_(kRcDecrease),
|
||||
rate_control_region_(kRcMaxUnknown),
|
||||
time_last_bitrate_change_(-1),
|
||||
current_input_(kBwNormal, 0, 1.0),
|
||||
updated_(false),
|
||||
time_first_incoming_estimate_(-1),
|
||||
bitrate_is_initialized_(false),
|
||||
beta_(0.9f),
|
||||
rtt_(kDefaultRttMs),
|
||||
time_of_last_log_(-1) {}
|
||||
|
||||
RateControlType AimdRateControl::GetControlType() const {
|
||||
return kAimdControl;
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::GetMinBitrate() const {
|
||||
return min_configured_bitrate_bps_;
|
||||
}
|
||||
|
||||
bool AimdRateControl::ValidEstimate() const {
|
||||
return bitrate_is_initialized_;
|
||||
}
|
||||
|
||||
int AimdRateControl::GetFeedbackInterval() const {
|
||||
// Estimate how often we can send RTCP if we allocate up to 5% of bandwidth
|
||||
// to feedback.
|
||||
static const int kRtcpSize = 80.0;
|
||||
int interval = kRtcpSize * 8.0 * 1000.0 / (0.05 * current_bitrate_bps_) + 0.5;
|
||||
if (interval > kMaxFeedbackIntervalMs)
|
||||
interval = kMaxFeedbackIntervalMs;
|
||||
else if (interval < kMinFeedbackIntervalMs) {
|
||||
interval = kMinFeedbackIntervalMs;
|
||||
}
|
||||
return interval;
|
||||
}
|
||||
|
||||
bool AimdRateControl::TimeToReduceFurther(int64_t time_now,
|
||||
uint32_t incoming_bitrate_bps) const {
|
||||
const int bitrate_reduction_interval = std::max(std::min(rtt_, 200u), 10u);
|
||||
if (time_now - time_last_bitrate_change_ >= bitrate_reduction_interval) {
|
||||
return true;
|
||||
}
|
||||
if (ValidEstimate()) {
|
||||
const int threshold = static_cast<int>(kWithinIncomingBitrateHysteresis *
|
||||
incoming_bitrate_bps);
|
||||
const int bitrate_difference = LatestEstimate() - incoming_bitrate_bps;
|
||||
return bitrate_difference > threshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::LatestEstimate() const {
|
||||
return current_bitrate_bps_;
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::UpdateBandwidthEstimate(int64_t now_ms) {
|
||||
current_bitrate_bps_ = ChangeBitrate(current_bitrate_bps_,
|
||||
current_input_._incomingBitRate,
|
||||
now_ms);
|
||||
if (now_ms - time_of_last_log_ > kLogIntervalMs) {
|
||||
time_of_last_log_ = now_ms;
|
||||
}
|
||||
return current_bitrate_bps_;
|
||||
}
|
||||
|
||||
void AimdRateControl::SetRtt(uint32_t rtt) {
|
||||
rtt_ = rtt;
|
||||
}
|
||||
|
||||
RateControlRegion AimdRateControl::Update(const RateControlInput* input,
|
||||
int64_t now_ms) {
|
||||
assert(input);
|
||||
|
||||
// Set the initial bit rate value to what we're receiving the first half
|
||||
// second.
|
||||
if (!bitrate_is_initialized_) {
|
||||
if (time_first_incoming_estimate_ < 0) {
|
||||
if (input->_incomingBitRate > 0) {
|
||||
time_first_incoming_estimate_ = now_ms;
|
||||
}
|
||||
} else if (now_ms - time_first_incoming_estimate_ > 500 &&
|
||||
input->_incomingBitRate > 0) {
|
||||
current_bitrate_bps_ = input->_incomingBitRate;
|
||||
bitrate_is_initialized_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updated_ && current_input_._bwState == kBwOverusing) {
|
||||
// Only update delay factor and incoming bit rate. We always want to react
|
||||
// on an over-use.
|
||||
current_input_._noiseVar = input->_noiseVar;
|
||||
current_input_._incomingBitRate = input->_incomingBitRate;
|
||||
} else {
|
||||
updated_ = true;
|
||||
current_input_ = *input;
|
||||
}
|
||||
return rate_control_region_;
|
||||
}
|
||||
|
||||
void AimdRateControl::SetEstimate(int bitrate_bps, int64_t now_ms) {
|
||||
updated_ = true;
|
||||
bitrate_is_initialized_ = true;
|
||||
current_bitrate_bps_ = ChangeBitrate(bitrate_bps, bitrate_bps, now_ms);
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::ChangeBitrate(uint32_t current_bitrate_bps,
|
||||
uint32_t incoming_bitrate_bps,
|
||||
int64_t now_ms) {
|
||||
BWE_TEST_LOGGING_PLOT("estimated_incoming#1", -1,
|
||||
incoming_bitrate_bps / 1000);
|
||||
if (!updated_) {
|
||||
return current_bitrate_bps_;
|
||||
}
|
||||
updated_ = false;
|
||||
ChangeState(current_input_, now_ms);
|
||||
// Calculated here because it's used in multiple places.
|
||||
const float incoming_bitrate_kbps = incoming_bitrate_bps / 1000.0f;
|
||||
// Calculate the max bit rate std dev given the normalized
|
||||
// variance and the current incoming bit rate.
|
||||
const float std_max_bit_rate = sqrt(var_max_bitrate_kbps_ *
|
||||
avg_max_bitrate_kbps_);
|
||||
bool fast_recovery_after_hold = false;
|
||||
switch (rate_control_state_) {
|
||||
case kRcHold: {
|
||||
max_hold_rate_bps_ = std::max(max_hold_rate_bps_, incoming_bitrate_bps);
|
||||
break;
|
||||
}
|
||||
case kRcIncrease: {
|
||||
if (avg_max_bitrate_kbps_ >= 0) {
|
||||
if (incoming_bitrate_kbps > avg_max_bitrate_kbps_ +
|
||||
3 * std_max_bit_rate) {
|
||||
ChangeRegion(kRcMaxUnknown);
|
||||
avg_max_bitrate_kbps_ = -1.0;
|
||||
} else if (incoming_bitrate_kbps > avg_max_bitrate_kbps_ +
|
||||
2.5 * std_max_bit_rate) {
|
||||
ChangeRegion(kRcAboveMax);
|
||||
}
|
||||
}
|
||||
if (rate_control_region_ == kRcNearMax) {
|
||||
// Approximate the over-use estimator delay to 100 ms.
|
||||
const uint32_t response_time = rtt_ + 100;
|
||||
uint32_t additive_increase_bps = AdditiveRateIncrease(
|
||||
now_ms, time_last_bitrate_change_, response_time);
|
||||
BWE_TEST_LOGGING_PLOT("add_increase#1", -1,
|
||||
additive_increase_bps / 1000);
|
||||
current_bitrate_bps += additive_increase_bps;
|
||||
|
||||
} else {
|
||||
uint32_t multiplicative_increase_bps = MultiplicativeRateIncrease(
|
||||
now_ms, time_last_bitrate_change_, current_bitrate_bps);
|
||||
BWE_TEST_LOGGING_PLOT("mult_increase#1", -1,
|
||||
multiplicative_increase_bps / 1000);
|
||||
current_bitrate_bps += multiplicative_increase_bps;
|
||||
}
|
||||
|
||||
if (max_hold_rate_bps_ > 0 &&
|
||||
beta_ * max_hold_rate_bps_ > current_bitrate_bps) {
|
||||
current_bitrate_bps = static_cast<uint32_t>(beta_ * max_hold_rate_bps_);
|
||||
avg_max_bitrate_kbps_ = beta_ * max_hold_rate_bps_ / 1000.0f;
|
||||
ChangeRegion(kRcNearMax);
|
||||
fast_recovery_after_hold = true;
|
||||
}
|
||||
max_hold_rate_bps_ = 0;
|
||||
time_last_bitrate_change_ = now_ms;
|
||||
break;
|
||||
}
|
||||
case kRcDecrease: {
|
||||
if (incoming_bitrate_bps < min_configured_bitrate_bps_) {
|
||||
current_bitrate_bps = min_configured_bitrate_bps_;
|
||||
} else {
|
||||
// Set bit rate to something slightly lower than max
|
||||
// to get rid of any self-induced delay.
|
||||
current_bitrate_bps = static_cast<uint32_t>(beta_ *
|
||||
incoming_bitrate_bps + 0.5);
|
||||
if (current_bitrate_bps > current_bitrate_bps_) {
|
||||
// Avoid increasing the rate when over-using.
|
||||
if (rate_control_region_ != kRcMaxUnknown) {
|
||||
current_bitrate_bps = static_cast<uint32_t>(
|
||||
beta_ * avg_max_bitrate_kbps_ * 1000 + 0.5f);
|
||||
}
|
||||
current_bitrate_bps = std::min(current_bitrate_bps,
|
||||
current_bitrate_bps_);
|
||||
}
|
||||
ChangeRegion(kRcNearMax);
|
||||
|
||||
if (incoming_bitrate_kbps < avg_max_bitrate_kbps_ -
|
||||
3 * std_max_bit_rate) {
|
||||
avg_max_bitrate_kbps_ = -1.0f;
|
||||
}
|
||||
|
||||
UpdateMaxBitRateEstimate(incoming_bitrate_kbps);
|
||||
}
|
||||
// Stay on hold until the pipes are cleared.
|
||||
ChangeState(kRcHold);
|
||||
time_last_bitrate_change_ = now_ms;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
if (!fast_recovery_after_hold && (incoming_bitrate_bps > 100000 ||
|
||||
current_bitrate_bps > 150000) &&
|
||||
current_bitrate_bps > 1.5 * incoming_bitrate_bps) {
|
||||
// Allow changing the bit rate if we are operating at very low rates
|
||||
// Don't change the bit rate if the send side is too far off
|
||||
current_bitrate_bps = current_bitrate_bps_;
|
||||
time_last_bitrate_change_ = now_ms;
|
||||
}
|
||||
return current_bitrate_bps;
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::MultiplicativeRateIncrease(
|
||||
int64_t now_ms, int64_t last_ms, uint32_t current_bitrate_bps) const {
|
||||
double alpha = 1.08;
|
||||
if (last_ms > -1) {
|
||||
int time_since_last_update_ms = std::min(static_cast<int>(now_ms - last_ms),
|
||||
1000);
|
||||
alpha = pow(alpha, time_since_last_update_ms / 1000.0);
|
||||
}
|
||||
uint32_t multiplicative_increase_bps = std::max(
|
||||
current_bitrate_bps * (alpha - 1.0), 1000.0);
|
||||
return multiplicative_increase_bps;
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::AdditiveRateIncrease(
|
||||
int64_t now_ms, int64_t last_ms, uint32_t response_time_ms) const {
|
||||
assert(response_time_ms > 0);
|
||||
double beta = 0.0;
|
||||
if (last_ms > 0) {
|
||||
beta = std::min((now_ms - last_ms) /
|
||||
static_cast<double>(response_time_ms), 1.0);
|
||||
}
|
||||
double bits_per_frame = static_cast<double>(current_bitrate_bps_) / 30.0;
|
||||
double packets_per_frame = std::ceil(bits_per_frame / (8.0 * 1200.0));
|
||||
double avg_packet_size_bits = bits_per_frame / packets_per_frame;
|
||||
uint32_t additive_increase_bps = std::max(
|
||||
1000.0, beta * avg_packet_size_bits);
|
||||
return additive_increase_bps;
|
||||
}
|
||||
|
||||
void AimdRateControl::UpdateMaxBitRateEstimate(float incoming_bitrate_kbps) {
|
||||
const float alpha = 0.05f;
|
||||
if (avg_max_bitrate_kbps_ == -1.0f) {
|
||||
avg_max_bitrate_kbps_ = incoming_bitrate_kbps;
|
||||
} else {
|
||||
avg_max_bitrate_kbps_ = (1 - alpha) * avg_max_bitrate_kbps_ +
|
||||
alpha * incoming_bitrate_kbps;
|
||||
}
|
||||
// Estimate the max bit rate variance and normalize the variance
|
||||
// with the average max bit rate.
|
||||
const float norm = std::max(avg_max_bitrate_kbps_, 1.0f);
|
||||
var_max_bitrate_kbps_ = (1 - alpha) * var_max_bitrate_kbps_ +
|
||||
alpha * (avg_max_bitrate_kbps_ - incoming_bitrate_kbps) *
|
||||
(avg_max_bitrate_kbps_ - incoming_bitrate_kbps) / norm;
|
||||
// 0.4 ~= 14 kbit/s at 500 kbit/s
|
||||
if (var_max_bitrate_kbps_ < 0.4f) {
|
||||
var_max_bitrate_kbps_ = 0.4f;
|
||||
}
|
||||
// 2.5f ~= 35 kbit/s at 500 kbit/s
|
||||
if (var_max_bitrate_kbps_ > 2.5f) {
|
||||
var_max_bitrate_kbps_ = 2.5f;
|
||||
}
|
||||
}
|
||||
|
||||
void AimdRateControl::ChangeState(const RateControlInput& input,
|
||||
int64_t now_ms) {
|
||||
switch (current_input_._bwState) {
|
||||
case kBwNormal:
|
||||
if (rate_control_state_ == kRcHold) {
|
||||
time_last_bitrate_change_ = now_ms;
|
||||
ChangeState(kRcIncrease);
|
||||
}
|
||||
break;
|
||||
case kBwOverusing:
|
||||
if (rate_control_state_ != kRcDecrease) {
|
||||
ChangeState(kRcDecrease);
|
||||
}
|
||||
break;
|
||||
case kBwUnderusing:
|
||||
ChangeState(kRcHold);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AimdRateControl::ChangeRegion(RateControlRegion region) {
|
||||
rate_control_region_ = region;
|
||||
switch (rate_control_region_) {
|
||||
case kRcAboveMax:
|
||||
case kRcMaxUnknown:
|
||||
beta_ = 0.9f;
|
||||
break;
|
||||
case kRcNearMax:
|
||||
beta_ = 0.95f;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AimdRateControl::ChangeState(RateControlState new_state) {
|
||||
came_from_state_ = rate_control_state_;
|
||||
rate_control_state_ = new_state;
|
||||
}
|
||||
} // namespace webrtc
|
90
webrtc/modules/remote_bitrate_estimator/aimd_rate_control.h
Normal file
90
webrtc/modules/remote_bitrate_estimator/aimd_rate_control.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2014 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 WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_
|
||||
#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A RemoteRateControl implementation based on additive increases of
|
||||
// bitrate when no over-use is detected and multiplicative decreases when
|
||||
// over-uses are detected. When we think the available bandwidth has changes or
|
||||
// is unknown, we will switch to a "slow-start mode" where we increase
|
||||
// multiplicatively.
|
||||
class AimdRateControl : public RemoteRateControl {
|
||||
public:
|
||||
explicit AimdRateControl(uint32_t min_bitrate_bps);
|
||||
virtual ~AimdRateControl() {}
|
||||
|
||||
// Implements RemoteRateControl.
|
||||
virtual bool ValidEstimate() const OVERRIDE;
|
||||
virtual RateControlType GetControlType() const OVERRIDE;
|
||||
virtual uint32_t GetMinBitrate() const OVERRIDE;
|
||||
virtual int GetFeedbackInterval() const OVERRIDE;
|
||||
// Returns true if the bitrate estimate hasn't been changed for more than
|
||||
// an RTT, or if the incoming_bitrate is more than 5% above the current
|
||||
// estimate. Should be used to decide if we should reduce the rate further
|
||||
// when over-using.
|
||||
virtual bool TimeToReduceFurther(
|
||||
int64_t time_now, uint32_t incoming_bitrate_bps) const OVERRIDE;
|
||||
virtual uint32_t LatestEstimate() const OVERRIDE;
|
||||
virtual uint32_t UpdateBandwidthEstimate(int64_t now_ms) OVERRIDE;
|
||||
virtual void SetRtt(uint32_t rtt) OVERRIDE;
|
||||
virtual RateControlRegion Update(const RateControlInput* input,
|
||||
int64_t now_ms) OVERRIDE;
|
||||
virtual void SetEstimate(int bitrate_bps, int64_t now_ms) OVERRIDE;
|
||||
|
||||
private:
|
||||
// Update the target bitrate according based on, among other things,
|
||||
// the current rate control state, the current target bitrate and the incoming
|
||||
// bitrate. When in the "increase" state the bitrate will be increased either
|
||||
// additively or multiplicatively depending on the rate control region. When
|
||||
// in the "decrease" state the bitrate will be decreased to slightly below the
|
||||
// incoming bitrate. When in the "hold" state the bitrate will be kept
|
||||
// constant to allow built up queues to drain.
|
||||
uint32_t ChangeBitrate(uint32_t current_bit_rate,
|
||||
uint32_t incoming_bit_rate,
|
||||
int64_t now_ms);
|
||||
uint32_t MultiplicativeRateIncrease(int64_t now_ms, int64_t last_ms,
|
||||
uint32_t current_bitrate_bps) const;
|
||||
uint32_t AdditiveRateIncrease(int64_t now_ms, int64_t last_ms,
|
||||
uint32_t response_time_ms) const;
|
||||
void UpdateChangePeriod(int64_t now_ms);
|
||||
void UpdateMaxBitRateEstimate(float incoming_bit_rate_kbps);
|
||||
void ChangeState(const RateControlInput& input, int64_t now_ms);
|
||||
void ChangeState(RateControlState new_state);
|
||||
void ChangeRegion(RateControlRegion region);
|
||||
|
||||
uint32_t min_configured_bitrate_bps_;
|
||||
uint32_t max_configured_bitrate_bps_;
|
||||
uint32_t current_bitrate_bps_;
|
||||
uint32_t max_hold_rate_bps_;
|
||||
float avg_max_bitrate_kbps_;
|
||||
float var_max_bitrate_kbps_;
|
||||
RateControlState rate_control_state_;
|
||||
RateControlState came_from_state_;
|
||||
RateControlRegion rate_control_region_;
|
||||
int64_t time_last_bitrate_change_;
|
||||
RateControlInput current_input_;
|
||||
bool updated_;
|
||||
int64_t time_first_incoming_estimate_;
|
||||
bool bitrate_is_initialized_;
|
||||
float beta_;
|
||||
uint32_t rtt_;
|
||||
int64_t time_of_last_log_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(AimdRateControl);
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_
|
121
webrtc/modules/remote_bitrate_estimator/inter_arrival.cc
Normal file
121
webrtc/modules/remote_bitrate_estimator/inter_arrival.cc
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2013 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 "webrtc/modules/remote_bitrate_estimator/inter_arrival.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "webrtc/modules/interface/module_common_types.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
static const int kBurstDeltaThresholdMs = 5;
|
||||
|
||||
InterArrival::InterArrival(uint32_t timestamp_group_length_ticks,
|
||||
double timestamp_to_ms_coeff,
|
||||
bool enable_burst_grouping)
|
||||
: kTimestampGroupLengthTicks(timestamp_group_length_ticks),
|
||||
current_timestamp_group_(),
|
||||
prev_timestamp_group_(),
|
||||
timestamp_to_ms_coeff_(timestamp_to_ms_coeff),
|
||||
burst_grouping_(enable_burst_grouping) {}
|
||||
|
||||
bool InterArrival::ComputeDeltas(uint32_t timestamp,
|
||||
int64_t arrival_time_ms,
|
||||
size_t packet_size,
|
||||
uint32_t* timestamp_delta,
|
||||
int64_t* arrival_time_delta_ms,
|
||||
int* packet_size_delta) {
|
||||
assert(timestamp_delta != NULL);
|
||||
assert(arrival_time_delta_ms != NULL);
|
||||
assert(packet_size_delta != NULL);
|
||||
bool calculated_deltas = false;
|
||||
if (current_timestamp_group_.IsFirstPacket()) {
|
||||
// We don't have enough data to update the filter, so we store it until we
|
||||
// have two frames of data to process.
|
||||
current_timestamp_group_.timestamp = timestamp;
|
||||
current_timestamp_group_.first_timestamp = timestamp;
|
||||
} else if (!PacketInOrder(timestamp)) {
|
||||
return false;
|
||||
} else if (NewTimestampGroup(arrival_time_ms, timestamp)) {
|
||||
// First packet of a later frame, the previous frame sample is ready.
|
||||
if (prev_timestamp_group_.complete_time_ms >= 0) {
|
||||
*timestamp_delta = current_timestamp_group_.timestamp -
|
||||
prev_timestamp_group_.timestamp;
|
||||
*arrival_time_delta_ms = current_timestamp_group_.complete_time_ms -
|
||||
prev_timestamp_group_.complete_time_ms;
|
||||
assert(*arrival_time_delta_ms >= 0);
|
||||
*packet_size_delta = static_cast<int>(current_timestamp_group_.size) -
|
||||
static_cast<int>(prev_timestamp_group_.size);
|
||||
calculated_deltas = true;
|
||||
}
|
||||
prev_timestamp_group_ = current_timestamp_group_;
|
||||
// The new timestamp is now the current frame.
|
||||
current_timestamp_group_.first_timestamp = timestamp;
|
||||
current_timestamp_group_.timestamp = timestamp;
|
||||
current_timestamp_group_.size = 0;
|
||||
}
|
||||
else {
|
||||
current_timestamp_group_.timestamp = LatestTimestamp(
|
||||
current_timestamp_group_.timestamp, timestamp);
|
||||
}
|
||||
// Accumulate the frame size.
|
||||
current_timestamp_group_.size += packet_size;
|
||||
current_timestamp_group_.complete_time_ms = arrival_time_ms;
|
||||
|
||||
return calculated_deltas;
|
||||
}
|
||||
|
||||
bool InterArrival::PacketInOrder(uint32_t timestamp) {
|
||||
if (current_timestamp_group_.IsFirstPacket()) {
|
||||
return true;
|
||||
} else {
|
||||
// Assume that a diff which is bigger than half the timestamp interval
|
||||
// (32 bits) must be due to reordering. This code is almost identical to
|
||||
// that in IsNewerTimestamp() in module_common_types.h.
|
||||
uint32_t timestamp_diff = timestamp -
|
||||
current_timestamp_group_.first_timestamp;
|
||||
return timestamp_diff < 0x80000000;
|
||||
}
|
||||
}
|
||||
|
||||
// Assumes that |timestamp| is not reordered compared to
|
||||
// |current_timestamp_group_|.
|
||||
bool InterArrival::NewTimestampGroup(int64_t arrival_time_ms,
|
||||
uint32_t timestamp) const {
|
||||
if (current_timestamp_group_.IsFirstPacket()) {
|
||||
return false;
|
||||
} else if (BelongsToBurst(arrival_time_ms, timestamp)) {
|
||||
return false;
|
||||
} else {
|
||||
uint32_t timestamp_diff = timestamp -
|
||||
current_timestamp_group_.first_timestamp;
|
||||
return timestamp_diff > kTimestampGroupLengthTicks;
|
||||
}
|
||||
}
|
||||
|
||||
bool InterArrival::BelongsToBurst(int64_t arrival_time_ms,
|
||||
uint32_t timestamp) const {
|
||||
if (!burst_grouping_) {
|
||||
return false;
|
||||
}
|
||||
assert(current_timestamp_group_.complete_time_ms >= 0);
|
||||
int64_t arrival_time_delta_ms = arrival_time_ms -
|
||||
current_timestamp_group_.complete_time_ms;
|
||||
uint32_t timestamp_diff = timestamp - current_timestamp_group_.timestamp;
|
||||
int64_t ts_delta_ms = timestamp_to_ms_coeff_ * timestamp_diff + 0.5;
|
||||
if (ts_delta_ms == 0)
|
||||
return true;
|
||||
int propagation_delta_ms = arrival_time_delta_ms - ts_delta_ms;
|
||||
return propagation_delta_ms < 0 &&
|
||||
arrival_time_delta_ms <= kBurstDeltaThresholdMs;
|
||||
}
|
||||
} // namespace webrtc
|
85
webrtc/modules/remote_bitrate_estimator/inter_arrival.h
Normal file
85
webrtc/modules/remote_bitrate_estimator/inter_arrival.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2013 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 WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_
|
||||
#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Helper class to compute the inter-arrival time delta and the size delta
|
||||
// between two timestamp groups. A timestamp is a 32 bit unsigned number with
|
||||
// a client defined rate.
|
||||
class InterArrival {
|
||||
public:
|
||||
// A timestamp group is defined as all packets with a timestamp which are at
|
||||
// most timestamp_group_length_ticks older than the first timestamp in that
|
||||
// group.
|
||||
InterArrival(uint32_t timestamp_group_length_ticks,
|
||||
double timestamp_to_ms_coeff,
|
||||
bool enable_burst_grouping);
|
||||
|
||||
// This function returns true if a delta was computed, or false if the current
|
||||
// group is still incomplete or if only one group has been completed.
|
||||
// |timestamp| is the timestamp.
|
||||
// |arrival_time_ms| is the local time at which the packet arrived.
|
||||
// |packet_size| is the size of the packet.
|
||||
// |timestamp_delta| (output) is the computed timestamp delta.
|
||||
// |arrival_time_delta_ms| (output) is the computed arrival-time delta.
|
||||
// |packet_size_delta| (output) is the computed size delta.
|
||||
bool ComputeDeltas(uint32_t timestamp,
|
||||
int64_t arrival_time_ms,
|
||||
size_t packet_size,
|
||||
uint32_t* timestamp_delta,
|
||||
int64_t* arrival_time_delta_ms,
|
||||
int* packet_size_delta);
|
||||
|
||||
private:
|
||||
struct TimestampGroup {
|
||||
TimestampGroup()
|
||||
: size(0),
|
||||
first_timestamp(0),
|
||||
timestamp(0),
|
||||
complete_time_ms(-1) {}
|
||||
|
||||
bool IsFirstPacket() const {
|
||||
return complete_time_ms == -1;
|
||||
}
|
||||
|
||||
size_t size;
|
||||
uint32_t first_timestamp;
|
||||
uint32_t timestamp;
|
||||
int64_t complete_time_ms;
|
||||
};
|
||||
|
||||
// Returns true if the packet with timestamp |timestamp| arrived in order.
|
||||
bool PacketInOrder(uint32_t timestamp);
|
||||
|
||||
// Returns true if the last packet was the end of the current batch and the
|
||||
// packet with |timestamp| is the first of a new batch.
|
||||
bool NewTimestampGroup(int64_t arrival_time_ms, uint32_t timestamp) const;
|
||||
|
||||
bool BelongsToBurst(int64_t arrival_time_ms, uint32_t timestamp) const;
|
||||
|
||||
const uint32_t kTimestampGroupLengthTicks;
|
||||
TimestampGroup current_timestamp_group_;
|
||||
TimestampGroup prev_timestamp_group_;
|
||||
double timestamp_to_ms_coeff_;
|
||||
bool burst_grouping_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(InterArrival);
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_
|
@ -0,0 +1,422 @@
|
||||
/*
|
||||
* Copyright (c) 2013 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 "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/inter_arrival.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace testing {
|
||||
|
||||
enum {
|
||||
kTimestampGroupLengthUs = 5000,
|
||||
kMinStep = 20,
|
||||
kTriggerNewGroupUs = kTimestampGroupLengthUs + kMinStep,
|
||||
kBurstThresholdMs = 5,
|
||||
kAbsSendTimeFraction = 18,
|
||||
kAbsSendTimeInterArrivalUpshift = 8,
|
||||
kInterArrivalShift = kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift,
|
||||
};
|
||||
|
||||
const double kRtpTimestampToMs = 1.0 / 90.0;
|
||||
const double kAstToMs = 1000.0 / static_cast<double>(1 << kInterArrivalShift);
|
||||
|
||||
class InterArrivalTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
inter_arrival_rtp_.reset(new InterArrival(
|
||||
MakeRtpTimestamp(kTimestampGroupLengthUs),
|
||||
kRtpTimestampToMs,
|
||||
true));
|
||||
inter_arrival_ast_.reset(new InterArrival(
|
||||
MakeAbsSendTime(kTimestampGroupLengthUs),
|
||||
kAstToMs,
|
||||
true));
|
||||
}
|
||||
|
||||
// Test that neither inter_arrival instance complete the timestamp group from
|
||||
// the given data.
|
||||
void ExpectFalse(int64_t timestamp_us, int64_t arrival_time_ms,
|
||||
size_t packet_size) {
|
||||
InternalExpectFalse(inter_arrival_rtp_.get(),
|
||||
MakeRtpTimestamp(timestamp_us), arrival_time_ms,
|
||||
packet_size);
|
||||
InternalExpectFalse(inter_arrival_ast_.get(), MakeAbsSendTime(timestamp_us),
|
||||
arrival_time_ms, packet_size);
|
||||
}
|
||||
|
||||
// Test that both inter_arrival instances complete the timestamp group from
|
||||
// the given data and that all returned deltas are as expected (except
|
||||
// timestamp delta, which is rounded from us to different ranges and must
|
||||
// match within an interval, given in |timestamp_near].
|
||||
void ExpectTrue(int64_t timestamp_us, int64_t arrival_time_ms,
|
||||
size_t packet_size, int64_t expected_timestamp_delta_us,
|
||||
int64_t expected_arrival_time_delta_ms,
|
||||
int expected_packet_size_delta,
|
||||
uint32_t timestamp_near) {
|
||||
InternalExpectTrue(inter_arrival_rtp_.get(), MakeRtpTimestamp(timestamp_us),
|
||||
arrival_time_ms, packet_size,
|
||||
MakeRtpTimestamp(expected_timestamp_delta_us),
|
||||
expected_arrival_time_delta_ms,
|
||||
expected_packet_size_delta, timestamp_near);
|
||||
InternalExpectTrue(inter_arrival_ast_.get(), MakeAbsSendTime(timestamp_us),
|
||||
arrival_time_ms, packet_size,
|
||||
MakeAbsSendTime(expected_timestamp_delta_us),
|
||||
expected_arrival_time_delta_ms,
|
||||
expected_packet_size_delta, timestamp_near << 8);
|
||||
}
|
||||
|
||||
void WrapTestHelper(int64_t wrap_start_us, uint32_t timestamp_near,
|
||||
bool unorderly_within_group) {
|
||||
// Step through the range of a 32 bit int, 1/4 at a time to not cause
|
||||
// packets close to wraparound to be judged as out of order.
|
||||
|
||||
// G1
|
||||
int64_t arrival_time = 17;
|
||||
ExpectFalse(0, arrival_time, 1);
|
||||
|
||||
// G2
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
ExpectFalse(wrap_start_us / 4, arrival_time, 1);
|
||||
|
||||
// G3
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
ExpectTrue(wrap_start_us / 2, arrival_time, 1,
|
||||
wrap_start_us / 4, 6, 0, // Delta G2-G1
|
||||
0);
|
||||
|
||||
// G4
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
int64_t g4_arrival_time = arrival_time;
|
||||
ExpectTrue(wrap_start_us / 2 + wrap_start_us / 4, arrival_time, 1,
|
||||
wrap_start_us / 4, 6, 0, // Delta G3-G2
|
||||
timestamp_near);
|
||||
|
||||
// G5
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
ExpectTrue(wrap_start_us, arrival_time, 2,
|
||||
wrap_start_us / 4, 6, 0, // Delta G4-G3
|
||||
timestamp_near);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
// Slowly step across the wrap point.
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
if (unorderly_within_group) {
|
||||
// These packets arrive with timestamps in decreasing order but are
|
||||
// nevertheless accumulated to group because their timestamps are higher
|
||||
// than the initial timestamp of the group.
|
||||
ExpectFalse(wrap_start_us + kMinStep * (9 - i), arrival_time, 1);
|
||||
} else {
|
||||
ExpectFalse(wrap_start_us + kMinStep * i, arrival_time, 1);
|
||||
}
|
||||
}
|
||||
int64_t g5_arrival_time = arrival_time;
|
||||
|
||||
// This packet is out of order and should be dropped.
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
ExpectFalse(wrap_start_us - 100, arrival_time, 100);
|
||||
|
||||
// G6
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
int64_t g6_arrival_time = arrival_time;
|
||||
ExpectTrue(wrap_start_us + kTriggerNewGroupUs, arrival_time, 10,
|
||||
wrap_start_us / 4 + 9 * kMinStep,
|
||||
g5_arrival_time - g4_arrival_time,
|
||||
(2 + 10) - 1, // Delta G5-G4
|
||||
timestamp_near);
|
||||
|
||||
// This packet is out of order and should be dropped.
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
ExpectFalse(wrap_start_us + kTimestampGroupLengthUs, arrival_time, 100);
|
||||
|
||||
// G7
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
ExpectTrue(wrap_start_us + 2 * kTriggerNewGroupUs,
|
||||
arrival_time, 100,
|
||||
// Delta G6-G5
|
||||
kTriggerNewGroupUs - 9 * kMinStep,
|
||||
g6_arrival_time - g5_arrival_time,
|
||||
10 - (2 + 10),
|
||||
timestamp_near);
|
||||
}
|
||||
|
||||
private:
|
||||
static uint32_t MakeRtpTimestamp(int64_t us) {
|
||||
return static_cast<uint32_t>(static_cast<uint64_t>(us * 90 + 500) / 1000);
|
||||
}
|
||||
|
||||
static uint32_t MakeAbsSendTime(int64_t us) {
|
||||
uint32_t absolute_send_time = static_cast<uint32_t>(
|
||||
((static_cast<uint64_t>(us) << 18) + 500000) / 1000000) & 0x00FFFFFFul;
|
||||
return absolute_send_time << 8;
|
||||
}
|
||||
|
||||
static void InternalExpectFalse(InterArrival* inter_arrival,
|
||||
uint32_t timestamp, int64_t arrival_time_ms,
|
||||
size_t packet_size) {
|
||||
uint32_t dummy_timestamp = 101;
|
||||
int64_t dummy_arrival_time_ms = 303;
|
||||
int dummy_packet_size = 909;
|
||||
bool computed = inter_arrival->ComputeDeltas(timestamp,
|
||||
arrival_time_ms,
|
||||
packet_size,
|
||||
&dummy_timestamp,
|
||||
&dummy_arrival_time_ms,
|
||||
&dummy_packet_size);
|
||||
EXPECT_EQ(computed, false);
|
||||
EXPECT_EQ(101ul, dummy_timestamp);
|
||||
EXPECT_EQ(303, dummy_arrival_time_ms);
|
||||
EXPECT_EQ(909, dummy_packet_size);
|
||||
}
|
||||
|
||||
static void InternalExpectTrue(InterArrival* inter_arrival,
|
||||
uint32_t timestamp, int64_t arrival_time_ms,
|
||||
size_t packet_size,
|
||||
uint32_t expected_timestamp_delta,
|
||||
int64_t expected_arrival_time_delta_ms,
|
||||
int expected_packet_size_delta,
|
||||
uint32_t timestamp_near) {
|
||||
uint32_t delta_timestamp = 101;
|
||||
int64_t delta_arrival_time_ms = 303;
|
||||
int delta_packet_size = 909;
|
||||
bool computed = inter_arrival->ComputeDeltas(timestamp,
|
||||
arrival_time_ms,
|
||||
packet_size,
|
||||
&delta_timestamp,
|
||||
&delta_arrival_time_ms,
|
||||
&delta_packet_size);
|
||||
EXPECT_EQ(true, computed);
|
||||
EXPECT_NEAR(expected_timestamp_delta, delta_timestamp, timestamp_near);
|
||||
EXPECT_EQ(expected_arrival_time_delta_ms, delta_arrival_time_ms);
|
||||
EXPECT_EQ(expected_packet_size_delta, delta_packet_size);
|
||||
}
|
||||
|
||||
scoped_ptr<InterArrival> inter_arrival_rtp_;
|
||||
scoped_ptr<InterArrival> inter_arrival_ast_;
|
||||
};
|
||||
|
||||
TEST_F(InterArrivalTest, FirstPacket) {
|
||||
ExpectFalse(0, 17, 1);
|
||||
}
|
||||
|
||||
TEST_F(InterArrivalTest, FirstGroup) {
|
||||
// G1
|
||||
int64_t arrival_time = 17;
|
||||
int64_t g1_arrival_time = arrival_time;
|
||||
ExpectFalse(0, arrival_time, 1);
|
||||
|
||||
// G2
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
int64_t g2_arrival_time = arrival_time;
|
||||
ExpectFalse(kTriggerNewGroupUs, arrival_time, 2);
|
||||
|
||||
// G3
|
||||
// Only once the first packet of the third group arrives, do we see the deltas
|
||||
// between the first two.
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
ExpectTrue(2 * kTriggerNewGroupUs, arrival_time, 1,
|
||||
// Delta G2-G1
|
||||
kTriggerNewGroupUs, g2_arrival_time - g1_arrival_time, 1,
|
||||
0);
|
||||
}
|
||||
|
||||
TEST_F(InterArrivalTest, SecondGroup) {
|
||||
// G1
|
||||
int64_t arrival_time = 17;
|
||||
int64_t g1_arrival_time = arrival_time;
|
||||
ExpectFalse(0, arrival_time, 1);
|
||||
|
||||
// G2
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
int64_t g2_arrival_time = arrival_time;
|
||||
ExpectFalse(kTriggerNewGroupUs, arrival_time, 2);
|
||||
|
||||
// G3
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
int64_t g3_arrival_time = arrival_time;
|
||||
ExpectTrue(2 * kTriggerNewGroupUs, arrival_time, 1,
|
||||
// Delta G2-G1
|
||||
kTriggerNewGroupUs, g2_arrival_time - g1_arrival_time, 1,
|
||||
0);
|
||||
|
||||
// G4
|
||||
// First packet of 4th group yields deltas between group 2 and 3.
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
ExpectTrue(3 * kTriggerNewGroupUs, arrival_time, 2,
|
||||
// Delta G3-G2
|
||||
kTriggerNewGroupUs, g3_arrival_time - g2_arrival_time, -1,
|
||||
0);
|
||||
}
|
||||
|
||||
TEST_F(InterArrivalTest, AccumulatedGroup) {
|
||||
// G1
|
||||
int64_t arrival_time = 17;
|
||||
int64_t g1_arrival_time = arrival_time;
|
||||
ExpectFalse(0, arrival_time, 1);
|
||||
|
||||
// G2
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
ExpectFalse(kTriggerNewGroupUs, 28, 2);
|
||||
int64_t timestamp = kTriggerNewGroupUs;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
// A bunch of packets arriving within the same group.
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
timestamp += kMinStep;
|
||||
ExpectFalse(timestamp, arrival_time, 1);
|
||||
}
|
||||
int64_t g2_arrival_time = arrival_time;
|
||||
int64_t g2_timestamp = timestamp;
|
||||
|
||||
// G3
|
||||
arrival_time = 500;
|
||||
ExpectTrue(2 * kTriggerNewGroupUs, arrival_time, 100,
|
||||
g2_timestamp, g2_arrival_time - g1_arrival_time,
|
||||
(2 + 10) - 1, // Delta G2-G1
|
||||
0);
|
||||
}
|
||||
|
||||
TEST_F(InterArrivalTest, OutOfOrderPacket) {
|
||||
// G1
|
||||
int64_t arrival_time = 17;
|
||||
int64_t timestamp = 0;
|
||||
ExpectFalse(timestamp, arrival_time, 1);
|
||||
int64_t g1_timestamp = timestamp;
|
||||
int64_t g1_arrival_time = arrival_time;
|
||||
|
||||
// G2
|
||||
arrival_time += 11;
|
||||
timestamp += kTriggerNewGroupUs;
|
||||
ExpectFalse(timestamp, 28, 2);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
timestamp += kMinStep;
|
||||
ExpectFalse(timestamp, arrival_time, 1);
|
||||
}
|
||||
int64_t g2_timestamp = timestamp;
|
||||
int64_t g2_arrival_time = arrival_time;
|
||||
|
||||
// This packet is out of order and should be dropped.
|
||||
arrival_time = 281;
|
||||
ExpectFalse(g1_timestamp, arrival_time, 100);
|
||||
|
||||
// G3
|
||||
arrival_time = 500;
|
||||
timestamp = 2 * kTriggerNewGroupUs;
|
||||
ExpectTrue(timestamp, arrival_time, 100,
|
||||
// Delta G2-G1
|
||||
g2_timestamp - g1_timestamp, g2_arrival_time - g1_arrival_time,
|
||||
(2 + 10) - 1,
|
||||
0);
|
||||
}
|
||||
|
||||
TEST_F(InterArrivalTest, OutOfOrderWithinGroup) {
|
||||
// G1
|
||||
int64_t arrival_time = 17;
|
||||
int64_t timestamp = 0;
|
||||
ExpectFalse(timestamp, arrival_time, 1);
|
||||
int64_t g1_timestamp = timestamp;
|
||||
int64_t g1_arrival_time = arrival_time;
|
||||
|
||||
// G2
|
||||
timestamp += kTriggerNewGroupUs;
|
||||
arrival_time += 11;
|
||||
ExpectFalse(kTriggerNewGroupUs, 28, 2);
|
||||
timestamp += 10 * kMinStep;
|
||||
int64_t g2_timestamp = timestamp;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
// These packets arrive with timestamps in decreasing order but are
|
||||
// nevertheless accumulated to group because their timestamps are higher
|
||||
// than the initial timestamp of the group.
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
ExpectFalse(timestamp, arrival_time, 1);
|
||||
timestamp -= kMinStep;
|
||||
}
|
||||
int64_t g2_arrival_time = arrival_time;
|
||||
|
||||
// However, this packet is deemed out of order and should be dropped.
|
||||
arrival_time = 281;
|
||||
timestamp = g1_timestamp;
|
||||
ExpectFalse(timestamp, arrival_time, 100);
|
||||
|
||||
// G3
|
||||
timestamp = 2 * kTriggerNewGroupUs;
|
||||
arrival_time = 500;
|
||||
ExpectTrue(timestamp, arrival_time, 100,
|
||||
g2_timestamp - g1_timestamp, g2_arrival_time - g1_arrival_time,
|
||||
(2 + 10) - 1,
|
||||
0);
|
||||
}
|
||||
|
||||
TEST_F(InterArrivalTest, TwoBursts) {
|
||||
// G1
|
||||
int64_t g1_arrival_time = 17;
|
||||
ExpectFalse(0, g1_arrival_time, 1);
|
||||
|
||||
// G2
|
||||
int64_t timestamp = kTriggerNewGroupUs;
|
||||
int64_t arrival_time = 100; // Simulate no packets arriving for 100 ms.
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
// A bunch of packets arriving in one burst (within 5 ms apart).
|
||||
timestamp += 30000;
|
||||
arrival_time += kBurstThresholdMs;
|
||||
ExpectFalse(timestamp, arrival_time, 1);
|
||||
}
|
||||
int64_t g2_arrival_time = arrival_time;
|
||||
int64_t g2_timestamp = timestamp;
|
||||
|
||||
// G3
|
||||
timestamp += 30000;
|
||||
arrival_time += kBurstThresholdMs + 1;
|
||||
ExpectTrue(timestamp, arrival_time, 100,
|
||||
g2_timestamp, g2_arrival_time - g1_arrival_time,
|
||||
10 - 1, // Delta G2-G1
|
||||
0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(InterArrivalTest, NoBursts) {
|
||||
// G1
|
||||
ExpectFalse(0, 17, 1);
|
||||
|
||||
// G2
|
||||
int64_t timestamp = kTriggerNewGroupUs;
|
||||
int64_t arrival_time = 28;
|
||||
ExpectFalse(timestamp, arrival_time, 2);
|
||||
|
||||
// G3
|
||||
ExpectTrue(kTriggerNewGroupUs + 30000, arrival_time + kBurstThresholdMs + 1,
|
||||
100, timestamp - 0, arrival_time - 17,
|
||||
2 - 1, // Delta G2-G1
|
||||
0);
|
||||
}
|
||||
|
||||
// Yields 0xfffffffe when converted to internal representation in
|
||||
// inter_arrival_rtp_ and inter_arrival_ast_ respectively.
|
||||
static const int64_t kStartRtpTimestampWrapUs = 47721858827;
|
||||
static const int64_t kStartAbsSendTimeWrapUs = 63999995;
|
||||
|
||||
TEST_F(InterArrivalTest, RtpTimestampWrap) {
|
||||
WrapTestHelper(kStartRtpTimestampWrapUs, 1, false);
|
||||
}
|
||||
|
||||
TEST_F(InterArrivalTest, AbsSendTimeWrap) {
|
||||
WrapTestHelper(kStartAbsSendTimeWrapUs, 1, false);
|
||||
}
|
||||
|
||||
TEST_F(InterArrivalTest, RtpTimestampWrapOutOfOrderWithinGroup) {
|
||||
WrapTestHelper(kStartRtpTimestampWrapUs, 1, true);
|
||||
}
|
||||
|
||||
TEST_F(InterArrivalTest, AbsSendTimeWrapOutOfOrderWithinGroup) {
|
||||
WrapTestHelper(kStartAbsSendTimeWrapUs, 1, true);
|
||||
}
|
||||
} // namespace testing
|
||||
} // namespace webrtc
|
327
webrtc/modules/remote_bitrate_estimator/mimd_rate_control.cc
Normal file
327
webrtc/modules/remote_bitrate_estimator/mimd_rate_control.cc
Normal file
@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright (c) 2014 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 "webrtc/modules/remote_bitrate_estimator/mimd_rate_control.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
const uint32_t kDefaultRttMs = 200;
|
||||
const int64_t kLogIntervalMs = 1000;
|
||||
|
||||
MimdRateControl::MimdRateControl(uint32_t min_bitrate_bps)
|
||||
: min_configured_bit_rate_(min_bitrate_bps),
|
||||
max_configured_bit_rate_(30000000),
|
||||
current_bit_rate_(max_configured_bit_rate_),
|
||||
max_hold_rate_(0),
|
||||
avg_max_bit_rate_(-1.0f),
|
||||
var_max_bit_rate_(0.4f),
|
||||
rate_control_state_(kRcHold),
|
||||
came_from_state_(kRcDecrease),
|
||||
rate_control_region_(kRcMaxUnknown),
|
||||
last_bit_rate_change_(-1),
|
||||
current_input_(kBwNormal, 0, 1.0),
|
||||
updated_(false),
|
||||
time_first_incoming_estimate_(-1),
|
||||
initialized_bit_rate_(false),
|
||||
avg_change_period_(1000.0f),
|
||||
last_change_ms_(-1),
|
||||
beta_(0.9f),
|
||||
rtt_(kDefaultRttMs),
|
||||
time_of_last_log_(-1)
|
||||
{
|
||||
}
|
||||
|
||||
RateControlType MimdRateControl::GetControlType() const {
|
||||
return kMimdControl;
|
||||
}
|
||||
|
||||
uint32_t MimdRateControl::GetMinBitrate() const {
|
||||
return min_configured_bit_rate_;
|
||||
}
|
||||
|
||||
bool MimdRateControl::ValidEstimate() const {
|
||||
return initialized_bit_rate_;
|
||||
}
|
||||
|
||||
int MimdRateControl::GetFeedbackInterval() const {
|
||||
return kMaxFeedbackIntervalMs;
|
||||
}
|
||||
|
||||
bool MimdRateControl::TimeToReduceFurther(int64_t time_now,
|
||||
uint32_t incoming_bitrate_bps) const {
|
||||
const int bitrate_reduction_interval = std::max(std::min(rtt_, 200u), 10u);
|
||||
if (time_now - last_bit_rate_change_ >= bitrate_reduction_interval) {
|
||||
return true;
|
||||
}
|
||||
if (ValidEstimate()) {
|
||||
const int threshold = static_cast<int>(1.05 * incoming_bitrate_bps);
|
||||
const int bitrate_difference = LatestEstimate() - incoming_bitrate_bps;
|
||||
return bitrate_difference > threshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t MimdRateControl::LatestEstimate() const {
|
||||
return current_bit_rate_;
|
||||
}
|
||||
|
||||
uint32_t MimdRateControl::UpdateBandwidthEstimate(int64_t now_ms) {
|
||||
current_bit_rate_ = ChangeBitRate(current_bit_rate_,
|
||||
current_input_._incomingBitRate,
|
||||
current_input_._noiseVar,
|
||||
now_ms);
|
||||
if (now_ms - time_of_last_log_ > kLogIntervalMs) {
|
||||
time_of_last_log_ = now_ms;
|
||||
}
|
||||
return current_bit_rate_;
|
||||
}
|
||||
|
||||
void MimdRateControl::SetRtt(uint32_t rtt) {
|
||||
rtt_ = rtt;
|
||||
}
|
||||
|
||||
RateControlRegion MimdRateControl::Update(const RateControlInput* input,
|
||||
int64_t now_ms) {
|
||||
assert(input);
|
||||
|
||||
// Set the initial bit rate value to what we're receiving the first half
|
||||
// second.
|
||||
if (!initialized_bit_rate_) {
|
||||
if (time_first_incoming_estimate_ < 0) {
|
||||
if (input->_incomingBitRate > 0) {
|
||||
time_first_incoming_estimate_ = now_ms;
|
||||
}
|
||||
} else if (now_ms - time_first_incoming_estimate_ > 500 &&
|
||||
input->_incomingBitRate > 0) {
|
||||
current_bit_rate_ = input->_incomingBitRate;
|
||||
initialized_bit_rate_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updated_ && current_input_._bwState == kBwOverusing) {
|
||||
// Only update delay factor and incoming bit rate. We always want to react
|
||||
// on an over-use.
|
||||
current_input_._noiseVar = input->_noiseVar;
|
||||
current_input_._incomingBitRate = input->_incomingBitRate;
|
||||
return rate_control_region_;
|
||||
}
|
||||
updated_ = true;
|
||||
current_input_ = *input;
|
||||
return rate_control_region_;
|
||||
}
|
||||
|
||||
void MimdRateControl::SetEstimate(int bitrate_bps, int64_t now_ms) {
|
||||
}
|
||||
|
||||
uint32_t MimdRateControl::ChangeBitRate(uint32_t current_bit_rate,
|
||||
uint32_t incoming_bit_rate,
|
||||
double noise_var,
|
||||
int64_t now_ms) {
|
||||
if (!updated_) {
|
||||
return current_bit_rate_;
|
||||
}
|
||||
updated_ = false;
|
||||
UpdateChangePeriod(now_ms);
|
||||
ChangeState(current_input_, now_ms);
|
||||
// calculated here because it's used in multiple places
|
||||
const float incoming_bit_rate_kbps = incoming_bit_rate / 1000.0f;
|
||||
// Calculate the max bit rate std dev given the normalized
|
||||
// variance and the current incoming bit rate.
|
||||
const float std_max_bit_rate = sqrt(var_max_bit_rate_ * avg_max_bit_rate_);
|
||||
bool recovery = false;
|
||||
switch (rate_control_state_) {
|
||||
case kRcHold: {
|
||||
max_hold_rate_ = std::max(max_hold_rate_, incoming_bit_rate);
|
||||
break;
|
||||
}
|
||||
case kRcIncrease: {
|
||||
if (avg_max_bit_rate_ >= 0) {
|
||||
if (incoming_bit_rate_kbps > avg_max_bit_rate_ + 3 * std_max_bit_rate) {
|
||||
ChangeRegion(kRcMaxUnknown);
|
||||
avg_max_bit_rate_ = -1.0;
|
||||
} else if (incoming_bit_rate_kbps > avg_max_bit_rate_ + 2.5 *
|
||||
std_max_bit_rate) {
|
||||
ChangeRegion(kRcAboveMax);
|
||||
}
|
||||
}
|
||||
const uint32_t response_time = static_cast<uint32_t>(avg_change_period_ +
|
||||
0.5f) + rtt_ + 300;
|
||||
double alpha = RateIncreaseFactor(now_ms, last_bit_rate_change_,
|
||||
response_time, noise_var);
|
||||
|
||||
current_bit_rate = static_cast<uint32_t>(current_bit_rate * alpha) + 1000;
|
||||
if (max_hold_rate_ > 0 && beta_ * max_hold_rate_ > current_bit_rate) {
|
||||
current_bit_rate = static_cast<uint32_t>(beta_ * max_hold_rate_);
|
||||
avg_max_bit_rate_ = beta_ * max_hold_rate_ / 1000.0f;
|
||||
ChangeRegion(kRcNearMax);
|
||||
recovery = true;
|
||||
}
|
||||
max_hold_rate_ = 0;
|
||||
last_bit_rate_change_ = now_ms;
|
||||
break;
|
||||
}
|
||||
case kRcDecrease: {
|
||||
if (incoming_bit_rate < min_configured_bit_rate_) {
|
||||
current_bit_rate = min_configured_bit_rate_;
|
||||
} else {
|
||||
// Set bit rate to something slightly lower than max
|
||||
// to get rid of any self-induced delay.
|
||||
current_bit_rate = static_cast<uint32_t>(beta_ * incoming_bit_rate +
|
||||
0.5);
|
||||
if (current_bit_rate > current_bit_rate_) {
|
||||
// Avoid increasing the rate when over-using.
|
||||
if (rate_control_region_ != kRcMaxUnknown) {
|
||||
current_bit_rate = static_cast<uint32_t>(beta_ * avg_max_bit_rate_ *
|
||||
1000 + 0.5f);
|
||||
}
|
||||
current_bit_rate = std::min(current_bit_rate, current_bit_rate_);
|
||||
}
|
||||
ChangeRegion(kRcNearMax);
|
||||
|
||||
if (incoming_bit_rate_kbps < avg_max_bit_rate_ - 3 * std_max_bit_rate) {
|
||||
avg_max_bit_rate_ = -1.0f;
|
||||
}
|
||||
|
||||
UpdateMaxBitRateEstimate(incoming_bit_rate_kbps);
|
||||
}
|
||||
// Stay on hold until the pipes are cleared.
|
||||
ChangeState(kRcHold);
|
||||
last_bit_rate_change_ = now_ms;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
if (!recovery && (incoming_bit_rate > 100000 || current_bit_rate > 150000) &&
|
||||
current_bit_rate > 1.5 * incoming_bit_rate) {
|
||||
// Allow changing the bit rate if we are operating at very low rates
|
||||
// Don't change the bit rate if the send side is too far off
|
||||
current_bit_rate = current_bit_rate_;
|
||||
last_bit_rate_change_ = now_ms;
|
||||
}
|
||||
return current_bit_rate;
|
||||
}
|
||||
|
||||
double MimdRateControl::RateIncreaseFactor(int64_t now_ms,
|
||||
int64_t last_ms,
|
||||
uint32_t reaction_time_ms,
|
||||
double noise_var) const {
|
||||
// alpha = 1.02 + B ./ (1 + exp(b*(tr - (c1*s2 + c2))))
|
||||
// Parameters
|
||||
const double B = 0.0407;
|
||||
const double b = 0.0025;
|
||||
const double c1 = -6700.0 / (33 * 33);
|
||||
const double c2 = 800.0;
|
||||
const double d = 0.85;
|
||||
|
||||
double alpha = 1.005 + B / (1 + exp( b * (d * reaction_time_ms -
|
||||
(c1 * noise_var + c2))));
|
||||
|
||||
if (alpha < 1.005) {
|
||||
alpha = 1.005;
|
||||
} else if (alpha > 1.3) {
|
||||
alpha = 1.3;
|
||||
}
|
||||
|
||||
if (last_ms > -1) {
|
||||
alpha = pow(alpha, (now_ms - last_ms) / 1000.0);
|
||||
}
|
||||
|
||||
if (rate_control_region_ == kRcNearMax) {
|
||||
// We're close to our previous maximum. Try to stabilize the
|
||||
// bit rate in this region, by increasing in smaller steps.
|
||||
alpha = alpha - (alpha - 1.0) / 2.0;
|
||||
} else if (rate_control_region_ == kRcMaxUnknown) {
|
||||
alpha = alpha + (alpha - 1.0) * 2.0;
|
||||
}
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
void MimdRateControl::UpdateChangePeriod(int64_t now_ms) {
|
||||
int64_t change_period = 0;
|
||||
if (last_change_ms_ > -1) {
|
||||
change_period = now_ms - last_change_ms_;
|
||||
}
|
||||
last_change_ms_ = now_ms;
|
||||
avg_change_period_ = 0.9f * avg_change_period_ + 0.1f * change_period;
|
||||
}
|
||||
|
||||
void MimdRateControl::UpdateMaxBitRateEstimate(float incoming_bit_rate_kbps) {
|
||||
const float alpha = 0.05f;
|
||||
if (avg_max_bit_rate_ == -1.0f) {
|
||||
avg_max_bit_rate_ = incoming_bit_rate_kbps;
|
||||
} else {
|
||||
avg_max_bit_rate_ = (1 - alpha) * avg_max_bit_rate_ +
|
||||
alpha * incoming_bit_rate_kbps;
|
||||
}
|
||||
// Estimate the max bit rate variance and normalize the variance
|
||||
// with the average max bit rate.
|
||||
const float norm = std::max(avg_max_bit_rate_, 1.0f);
|
||||
var_max_bit_rate_ = (1 - alpha) * var_max_bit_rate_ +
|
||||
alpha * (avg_max_bit_rate_ - incoming_bit_rate_kbps) *
|
||||
(avg_max_bit_rate_ - incoming_bit_rate_kbps) / norm;
|
||||
// 0.4 ~= 14 kbit/s at 500 kbit/s
|
||||
if (var_max_bit_rate_ < 0.4f) {
|
||||
var_max_bit_rate_ = 0.4f;
|
||||
}
|
||||
// 2.5f ~= 35 kbit/s at 500 kbit/s
|
||||
if (var_max_bit_rate_ > 2.5f) {
|
||||
var_max_bit_rate_ = 2.5f;
|
||||
}
|
||||
}
|
||||
|
||||
void MimdRateControl::ChangeState(const RateControlInput& input,
|
||||
int64_t now_ms) {
|
||||
switch (current_input_._bwState) {
|
||||
case kBwNormal:
|
||||
if (rate_control_state_ == kRcHold) {
|
||||
last_bit_rate_change_ = now_ms;
|
||||
ChangeState(kRcIncrease);
|
||||
}
|
||||
break;
|
||||
case kBwOverusing:
|
||||
if (rate_control_state_ != kRcDecrease) {
|
||||
ChangeState(kRcDecrease);
|
||||
}
|
||||
break;
|
||||
case kBwUnderusing:
|
||||
ChangeState(kRcHold);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MimdRateControl::ChangeRegion(RateControlRegion region) {
|
||||
rate_control_region_ = region;
|
||||
switch (rate_control_region_) {
|
||||
case kRcAboveMax:
|
||||
case kRcMaxUnknown:
|
||||
beta_ = 0.9f;
|
||||
break;
|
||||
case kRcNearMax:
|
||||
beta_ = 0.95f;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MimdRateControl::ChangeState(RateControlState new_state) {
|
||||
came_from_state_ = rate_control_state_;
|
||||
rate_control_state_ = new_state;
|
||||
}
|
||||
} // namespace webrtc
|
80
webrtc/modules/remote_bitrate_estimator/mimd_rate_control.h
Normal file
80
webrtc/modules/remote_bitrate_estimator/mimd_rate_control.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2014 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 WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_MIMD_RATE_CONTROL_H_
|
||||
#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_MIMD_RATE_CONTROL_H_
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A RemoteRateControl implementation based on multiplicative increases of
|
||||
// bitrate when no over-use is detected and multiplicative decreases when
|
||||
// over-uses are detected.
|
||||
class MimdRateControl : public RemoteRateControl {
|
||||
public:
|
||||
explicit MimdRateControl(uint32_t min_bitrate_bps);
|
||||
virtual ~MimdRateControl() {}
|
||||
|
||||
// Implements RemoteRateControl.
|
||||
virtual RateControlType GetControlType() const OVERRIDE;
|
||||
virtual uint32_t GetMinBitrate() const OVERRIDE;
|
||||
virtual bool ValidEstimate() const OVERRIDE;
|
||||
virtual int GetFeedbackInterval() const OVERRIDE;
|
||||
virtual bool TimeToReduceFurther(
|
||||
int64_t time_now, uint32_t incoming_bitrate_bps) const OVERRIDE;
|
||||
virtual uint32_t LatestEstimate() const OVERRIDE;
|
||||
virtual uint32_t UpdateBandwidthEstimate(int64_t now_ms) OVERRIDE;
|
||||
virtual void SetRtt(uint32_t rtt) OVERRIDE;
|
||||
virtual RateControlRegion Update(const RateControlInput* input,
|
||||
int64_t now_ms) OVERRIDE;
|
||||
virtual void SetEstimate(int bitrate_bps, int64_t now_ms) OVERRIDE;
|
||||
|
||||
private:
|
||||
uint32_t ChangeBitRate(uint32_t current_bit_rate,
|
||||
uint32_t incoming_bit_rate,
|
||||
double delay_factor,
|
||||
int64_t now_ms);
|
||||
double RateIncreaseFactor(int64_t now_ms,
|
||||
int64_t last_ms,
|
||||
uint32_t reaction_time_ms,
|
||||
double noise_var) const;
|
||||
void UpdateChangePeriod(int64_t now_ms);
|
||||
void UpdateMaxBitRateEstimate(float incoming_bit_rate_kbps);
|
||||
void ChangeState(const RateControlInput& input, int64_t now_ms);
|
||||
void ChangeState(RateControlState new_state);
|
||||
void ChangeRegion(RateControlRegion region);
|
||||
|
||||
uint32_t min_configured_bit_rate_;
|
||||
uint32_t max_configured_bit_rate_;
|
||||
uint32_t current_bit_rate_;
|
||||
uint32_t max_hold_rate_;
|
||||
float avg_max_bit_rate_;
|
||||
float var_max_bit_rate_;
|
||||
RateControlState rate_control_state_;
|
||||
RateControlState came_from_state_;
|
||||
RateControlRegion rate_control_region_;
|
||||
int64_t last_bit_rate_change_;
|
||||
RateControlInput current_input_;
|
||||
bool updated_;
|
||||
int64_t time_first_incoming_estimate_;
|
||||
bool initialized_bit_rate_;
|
||||
float avg_change_period_;
|
||||
int64_t last_change_ms_;
|
||||
float beta_;
|
||||
uint32_t rtt_;
|
||||
int64_t time_of_last_log_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(MimdRateControl);
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_MIMD_RATE_CONTROL_H_
|
@ -8,88 +8,34 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h> // fabsf
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/overuse_detector.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "webrtc/system_wrappers/interface/trace.h"
|
||||
|
||||
enum { kOverUsingTimeThreshold = 100 };
|
||||
enum { kMinFramePeriodHistoryLength = 60 };
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
enum { kOverUsingTimeThreshold = 100 };
|
||||
|
||||
OveruseDetector::OveruseDetector(const OverUseDetectorOptions& options)
|
||||
: options_(options),
|
||||
current_frame_(),
|
||||
prev_frame_(),
|
||||
num_of_deltas_(0),
|
||||
slope_(options_.initial_slope),
|
||||
offset_(options_.initial_offset),
|
||||
E_(),
|
||||
process_noise_(),
|
||||
avg_noise_(options_.initial_avg_noise),
|
||||
var_noise_(options_.initial_var_noise),
|
||||
threshold_(options_.initial_threshold),
|
||||
ts_delta_hist_(),
|
||||
prev_offset_(0.0),
|
||||
time_over_using_(-1),
|
||||
over_use_counter_(0),
|
||||
hypothesis_(kBwNormal) {
|
||||
memcpy(E_, options_.initial_e, sizeof(E_));
|
||||
memcpy(process_noise_, options_.initial_process_noise,
|
||||
sizeof(process_noise_));
|
||||
}
|
||||
overuse_counter_(0),
|
||||
hypothesis_(kBwNormal) {}
|
||||
|
||||
OveruseDetector::~OveruseDetector() {
|
||||
ts_delta_hist_.clear();
|
||||
}
|
||||
|
||||
void OveruseDetector::Update(size_t packet_size,
|
||||
int64_t timestamp_ms,
|
||||
uint32_t timestamp,
|
||||
const int64_t arrival_time_ms) {
|
||||
bool new_timestamp = (timestamp != current_frame_.timestamp);
|
||||
if (timestamp_ms >= 0) {
|
||||
if (prev_frame_.timestamp_ms == -1 && current_frame_.timestamp_ms == -1) {
|
||||
SwitchTimeBase();
|
||||
}
|
||||
new_timestamp = (timestamp_ms != current_frame_.timestamp_ms);
|
||||
}
|
||||
if (current_frame_.timestamp == -1) {
|
||||
// This is the first incoming packet. We don't have enough data to update
|
||||
// the filter, so we store it until we have two frames of data to process.
|
||||
current_frame_.timestamp = timestamp;
|
||||
current_frame_.timestamp_ms = timestamp_ms;
|
||||
} else if (!PacketInOrder(timestamp, timestamp_ms)) {
|
||||
return;
|
||||
} else if (new_timestamp) {
|
||||
// First packet of a later frame, the previous frame sample is ready.
|
||||
if (prev_frame_.complete_time_ms >= 0) { // This is our second frame.
|
||||
int64_t t_delta = 0;
|
||||
double ts_delta = 0;
|
||||
TimeDeltas(current_frame_, prev_frame_, &t_delta, &ts_delta);
|
||||
UpdateKalman(t_delta, ts_delta, current_frame_.size, prev_frame_.size);
|
||||
}
|
||||
prev_frame_ = current_frame_;
|
||||
// The new timestamp is now the current frame.
|
||||
current_frame_.timestamp = timestamp;
|
||||
current_frame_.timestamp_ms = timestamp_ms;
|
||||
current_frame_.size = 0;
|
||||
}
|
||||
// Accumulate the frame size
|
||||
current_frame_.size += packet_size;
|
||||
current_frame_.complete_time_ms = arrival_time_ms;
|
||||
}
|
||||
OveruseDetector::~OveruseDetector() {}
|
||||
|
||||
BandwidthUsage OveruseDetector::State() const {
|
||||
return hypothesis_;
|
||||
}
|
||||
|
||||
double OveruseDetector::NoiseVar() const {
|
||||
return var_noise_;
|
||||
}
|
||||
|
||||
void OveruseDetector::SetRateControlRegion(RateControlRegion region) {
|
||||
switch (region) {
|
||||
@ -105,192 +51,40 @@ void OveruseDetector::SetRateControlRegion(RateControlRegion region) {
|
||||
}
|
||||
}
|
||||
|
||||
void OveruseDetector::SwitchTimeBase() {
|
||||
current_frame_.size = 0;
|
||||
current_frame_.complete_time_ms = -1;
|
||||
current_frame_.timestamp = -1;
|
||||
prev_frame_ = current_frame_;
|
||||
}
|
||||
|
||||
void OveruseDetector::TimeDeltas(const FrameSample& current_frame,
|
||||
const FrameSample& prev_frame,
|
||||
int64_t* t_delta,
|
||||
double* ts_delta) {
|
||||
assert(t_delta);
|
||||
assert(ts_delta);
|
||||
num_of_deltas_++;
|
||||
if (num_of_deltas_ > 1000) {
|
||||
num_of_deltas_ = 1000;
|
||||
}
|
||||
if (current_frame.timestamp_ms == -1) {
|
||||
uint32_t timestamp_diff = current_frame.timestamp - prev_frame.timestamp;
|
||||
*ts_delta = timestamp_diff / 90.0;
|
||||
} else {
|
||||
*ts_delta = current_frame.timestamp_ms - prev_frame.timestamp_ms;
|
||||
}
|
||||
*t_delta = current_frame.complete_time_ms - prev_frame.complete_time_ms;
|
||||
assert(*ts_delta > 0);
|
||||
}
|
||||
|
||||
bool OveruseDetector::PacketInOrder(uint32_t timestamp, int64_t timestamp_ms) {
|
||||
if (current_frame_.timestamp_ms == -1 && current_frame_.timestamp > -1) {
|
||||
return InOrderTimestamp(timestamp, current_frame_.timestamp);
|
||||
} else if (current_frame_.timestamp_ms > 0) {
|
||||
// Using timestamps converted to NTP time.
|
||||
return timestamp_ms > current_frame_.timestamp_ms;
|
||||
}
|
||||
// This is the first packet.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OveruseDetector::InOrderTimestamp(uint32_t timestamp,
|
||||
uint32_t prev_timestamp) {
|
||||
uint32_t timestamp_diff = timestamp - prev_timestamp;
|
||||
// Assume that a diff this big must be due to reordering. Don't update
|
||||
// with reordered samples.
|
||||
return (timestamp_diff < 0x80000000);
|
||||
}
|
||||
|
||||
double OveruseDetector::CurrentDrift() {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
void OveruseDetector::UpdateKalman(int64_t t_delta,
|
||||
double ts_delta,
|
||||
size_t frame_size,
|
||||
size_t prev_frame_size) {
|
||||
const double min_frame_period = UpdateMinFramePeriod(ts_delta);
|
||||
const double drift = CurrentDrift();
|
||||
// Compensate for drift
|
||||
const double t_ts_delta = t_delta - ts_delta / drift;
|
||||
double fs_delta = static_cast<double>(frame_size) - prev_frame_size;
|
||||
|
||||
// Update the Kalman filter
|
||||
const double scale_factor = min_frame_period / (1000.0 / 30.0);
|
||||
E_[0][0] += process_noise_[0] * scale_factor;
|
||||
E_[1][1] += process_noise_[1] * scale_factor;
|
||||
|
||||
if ((hypothesis_ == kBwOverusing && offset_ < prev_offset_) ||
|
||||
(hypothesis_ == kBwUnderusing && offset_ > prev_offset_)) {
|
||||
E_[1][1] += 10 * process_noise_[1] * scale_factor;
|
||||
}
|
||||
|
||||
const double h[2] = {fs_delta, 1.0};
|
||||
const double Eh[2] = {E_[0][0]*h[0] + E_[0][1]*h[1],
|
||||
E_[1][0]*h[0] + E_[1][1]*h[1]};
|
||||
|
||||
const double residual = t_ts_delta - slope_*h[0] - offset_;
|
||||
|
||||
const bool stable_state =
|
||||
(BWE_MIN(num_of_deltas_, 60) * fabs(offset_) < threshold_);
|
||||
// We try to filter out very late frames. For instance periodic key
|
||||
// frames doesn't fit the Gaussian model well.
|
||||
if (fabs(residual) < 3 * sqrt(var_noise_)) {
|
||||
UpdateNoiseEstimate(residual, min_frame_period, stable_state);
|
||||
} else {
|
||||
UpdateNoiseEstimate(3 * sqrt(var_noise_), min_frame_period, stable_state);
|
||||
}
|
||||
|
||||
const double denom = var_noise_ + h[0]*Eh[0] + h[1]*Eh[1];
|
||||
|
||||
const double K[2] = {Eh[0] / denom,
|
||||
Eh[1] / denom};
|
||||
|
||||
const double IKh[2][2] = {{1.0 - K[0]*h[0], -K[0]*h[1]},
|
||||
{-K[1]*h[0], 1.0 - K[1]*h[1]}};
|
||||
const double e00 = E_[0][0];
|
||||
const double e01 = E_[0][1];
|
||||
|
||||
// Update state
|
||||
E_[0][0] = e00 * IKh[0][0] + E_[1][0] * IKh[0][1];
|
||||
E_[0][1] = e01 * IKh[0][0] + E_[1][1] * IKh[0][1];
|
||||
E_[1][0] = e00 * IKh[1][0] + E_[1][0] * IKh[1][1];
|
||||
E_[1][1] = e01 * IKh[1][0] + E_[1][1] * IKh[1][1];
|
||||
|
||||
// Covariance matrix, must be positive semi-definite
|
||||
assert(E_[0][0] + E_[1][1] >= 0 &&
|
||||
E_[0][0] * E_[1][1] - E_[0][1] * E_[1][0] >= 0 &&
|
||||
E_[0][0] >= 0);
|
||||
|
||||
slope_ = slope_ + K[0] * residual;
|
||||
prev_offset_ = offset_;
|
||||
offset_ = offset_ + K[1] * residual;
|
||||
|
||||
Detect(ts_delta);
|
||||
}
|
||||
|
||||
double OveruseDetector::UpdateMinFramePeriod(double ts_delta) {
|
||||
double min_frame_period = ts_delta;
|
||||
if (ts_delta_hist_.size() >= kMinFramePeriodHistoryLength) {
|
||||
std::list<double>::iterator first_item = ts_delta_hist_.begin();
|
||||
ts_delta_hist_.erase(first_item);
|
||||
}
|
||||
std::list<double>::iterator it = ts_delta_hist_.begin();
|
||||
for (; it != ts_delta_hist_.end(); it++) {
|
||||
min_frame_period = BWE_MIN(*it, min_frame_period);
|
||||
}
|
||||
ts_delta_hist_.push_back(ts_delta);
|
||||
return min_frame_period;
|
||||
}
|
||||
|
||||
void OveruseDetector::UpdateNoiseEstimate(double residual,
|
||||
double ts_delta,
|
||||
bool stable_state) {
|
||||
if (!stable_state) {
|
||||
return;
|
||||
}
|
||||
// Faster filter during startup to faster adapt to the jitter level
|
||||
// of the network alpha is tuned for 30 frames per second, but
|
||||
double alpha = 0.01;
|
||||
if (num_of_deltas_ > 10*30) {
|
||||
alpha = 0.002;
|
||||
}
|
||||
// Only update the noise estimate if we're not over-using
|
||||
// beta is a function of alpha and the time delta since
|
||||
// the previous update.
|
||||
const double beta = pow(1 - alpha, ts_delta * 30.0 / 1000.0);
|
||||
avg_noise_ = beta * avg_noise_
|
||||
+ (1 - beta) * residual;
|
||||
var_noise_ = beta * var_noise_
|
||||
+ (1 - beta) * (avg_noise_ - residual) * (avg_noise_ - residual);
|
||||
if (var_noise_ < 1e-7) {
|
||||
var_noise_ = 1e-7;
|
||||
}
|
||||
}
|
||||
|
||||
BandwidthUsage OveruseDetector::Detect(double ts_delta) {
|
||||
if (num_of_deltas_ < 2) {
|
||||
BandwidthUsage OveruseDetector::Detect(double offset, double ts_delta,
|
||||
int num_of_deltas) {
|
||||
if (num_of_deltas < 2) {
|
||||
return kBwNormal;
|
||||
}
|
||||
const double T = BWE_MIN(num_of_deltas_, 60) * offset_;
|
||||
if (fabs(T) > threshold_) {
|
||||
if (offset_ > 0) {
|
||||
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;
|
||||
}
|
||||
over_use_counter_++;
|
||||
if (time_over_using_ > kOverUsingTimeThreshold
|
||||
&& over_use_counter_ > 1) {
|
||||
if (offset_ >= prev_offset_) {
|
||||
time_over_using_ = 0;
|
||||
over_use_counter_ = 0;
|
||||
hypothesis_ = kBwOverusing;
|
||||
}
|
||||
}
|
||||
const double prev_offset = prev_offset_;
|
||||
prev_offset_ = offset;
|
||||
const double T = std::min(num_of_deltas, 60) * offset;
|
||||
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 {
|
||||
time_over_using_ = -1;
|
||||
over_use_counter_ = 0;
|
||||
hypothesis_ = kBwUnderusing;
|
||||
// Increment timer
|
||||
time_over_using_ += ts_delta;
|
||||
}
|
||||
overuse_counter_++;
|
||||
if (time_over_using_ > kOverUsingTimeThreshold
|
||||
&& overuse_counter_ > 1) {
|
||||
if (offset >= prev_offset) {
|
||||
time_over_using_ = 0;
|
||||
overuse_counter_ = 0;
|
||||
hypothesis_ = kBwOverusing;
|
||||
}
|
||||
}
|
||||
} else if (T < -threshold_) {
|
||||
time_over_using_ = -1;
|
||||
overuse_counter_ = 0;
|
||||
hypothesis_ = kBwUnderusing;
|
||||
} else {
|
||||
time_over_using_ = -1;
|
||||
over_use_counter_ = 0;
|
||||
overuse_counter_ = 0;
|
||||
hypothesis_ = kBwNormal;
|
||||
}
|
||||
return hypothesis_;
|
||||
|
@ -7,11 +7,12 @@
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_OVERUSE_DETECTOR_H_
|
||||
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_OVERUSE_DETECTOR_H_
|
||||
#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_
|
||||
#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/modules/interface/module_common_types.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
@ -19,76 +20,39 @@
|
||||
namespace webrtc {
|
||||
enum RateControlRegion;
|
||||
|
||||
// This class is assumed to be protected by the owner if used by multiple
|
||||
// threads.
|
||||
class OveruseDetector {
|
||||
public:
|
||||
explicit OveruseDetector(const OverUseDetectorOptions& options);
|
||||
~OveruseDetector();
|
||||
void Update(size_t packet_size,
|
||||
int64_t timestamp_ms,
|
||||
uint32_t rtp_timestamp,
|
||||
int64_t arrival_time_ms);
|
||||
|
||||
// Update the detection state based on the estimated inter-arrival time delta
|
||||
// offset. |timestamp_delta| is the delta between the last timestamp which the
|
||||
// estimated offset is based on and the last timestamp on which the last
|
||||
// offset was based on, representing the time between detector updates.
|
||||
// |num_of_deltas| is the number of deltas the offset estimate is based on.
|
||||
// Returns the state after the detection update.
|
||||
BandwidthUsage Detect(double offset, double timestamp_delta,
|
||||
int num_of_deltas);
|
||||
|
||||
// Returns the current detector state.
|
||||
BandwidthUsage State() const;
|
||||
double NoiseVar() const;
|
||||
void SetRateControlRegion(RateControlRegion region);
|
||||
|
||||
// Sets the current rate-control region as decided by RemoteRateControl. This
|
||||
// affects the sensitivity of the detector.
|
||||
void SetRateControlRegion(webrtc::RateControlRegion region);
|
||||
|
||||
private:
|
||||
struct FrameSample {
|
||||
FrameSample()
|
||||
: size(0),
|
||||
complete_time_ms(-1),
|
||||
timestamp(-1),
|
||||
timestamp_ms(-1) {}
|
||||
|
||||
size_t size;
|
||||
int64_t complete_time_ms;
|
||||
int64_t timestamp;
|
||||
int64_t timestamp_ms;
|
||||
};
|
||||
|
||||
// Returns true if |timestamp| represent a time which is later than
|
||||
// |prev_timestamp|.
|
||||
static bool InOrderTimestamp(uint32_t timestamp, uint32_t prev_timestamp);
|
||||
|
||||
bool PacketInOrder(uint32_t timestamp, int64_t timestamp_ms);
|
||||
|
||||
// Prepares the overuse detector to start using timestamps in milliseconds
|
||||
// instead of 90 kHz timestamps.
|
||||
void SwitchTimeBase();
|
||||
|
||||
void TimeDeltas(const FrameSample& current_frame,
|
||||
const FrameSample& prev_frame,
|
||||
int64_t* t_delta,
|
||||
double* ts_delta);
|
||||
void UpdateKalman(int64_t t_delta,
|
||||
double ts_elta,
|
||||
size_t frame_size,
|
||||
size_t prev_frame_size);
|
||||
double UpdateMinFramePeriod(double ts_delta);
|
||||
void UpdateNoiseEstimate(double residual, double ts_delta, bool stable_state);
|
||||
BandwidthUsage Detect(double ts_delta);
|
||||
double CurrentDrift();
|
||||
|
||||
OverUseDetectorOptions options_; // Must be first member
|
||||
// variable. Cannot be const
|
||||
// because we need to be copyable.
|
||||
FrameSample current_frame_;
|
||||
FrameSample prev_frame_;
|
||||
uint16_t num_of_deltas_;
|
||||
double slope_;
|
||||
double offset_;
|
||||
double E_[2][2];
|
||||
double process_noise_[2];
|
||||
double avg_noise_;
|
||||
double var_noise_;
|
||||
// Must be first member variable. Cannot be const because we need to be
|
||||
// copyable.
|
||||
webrtc::OverUseDetectorOptions options_;
|
||||
double threshold_;
|
||||
std::list<double> ts_delta_hist_;
|
||||
double prev_offset_;
|
||||
double time_over_using_;
|
||||
uint16_t over_use_counter_;
|
||||
int overuse_counter_;
|
||||
BandwidthUsage hypothesis_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(OveruseDetector);
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_OVERUSE_DETECTOR_H_
|
||||
#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_
|
||||
|
@ -0,0 +1,605 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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 <math.h>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/inter_arrival.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/overuse_detector.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/overuse_estimator.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
#include "webrtc/test/testsupport/gtest_disable.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace testing {
|
||||
|
||||
const double kRtpTimestampToMs = 1.0 / 90.0;
|
||||
|
||||
class OveruseDetectorTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() {
|
||||
srand(1234);
|
||||
now_ms_ = 0;
|
||||
receive_time_ms_ = 0;
|
||||
rtp_timestamp_ = 10 * 90;
|
||||
overuse_detector_.reset(new OveruseDetector(options_));
|
||||
overuse_estimator_.reset(new OveruseEstimator(options_));
|
||||
inter_arrival_.reset(new InterArrival(5 * 90, kRtpTimestampToMs, true));
|
||||
}
|
||||
// Normal Distribution.
|
||||
#define PI 3.14159265
|
||||
int GaussianRandom(int mean_ms, int standard_deviation_ms) {
|
||||
// Creating a Normal distribution variable from two independent uniform
|
||||
// variables based on the Box-Muller transform.
|
||||
double uniform1 = (std::rand() + 1.0) / (RAND_MAX + 1.0);
|
||||
double uniform2 = (std::rand() + 1.0) / (RAND_MAX + 1.0);
|
||||
return static_cast<int>(mean_ms + standard_deviation_ms *
|
||||
sqrt(-2 * log(uniform1)) * cos(2 * PI * uniform2));
|
||||
}
|
||||
|
||||
int Run100000Samples(int packets_per_frame, size_t packet_size, int mean_ms,
|
||||
int standard_deviation_ms) {
|
||||
int unique_overuse = 0;
|
||||
int last_overuse = -1;
|
||||
for (int i = 0; i < 100000; ++i) {
|
||||
for (int j = 0; j < packets_per_frame; ++j) {
|
||||
UpdateDetector(rtp_timestamp_, receive_time_ms_, packet_size);
|
||||
}
|
||||
rtp_timestamp_ += mean_ms * 90;
|
||||
now_ms_ += mean_ms;
|
||||
receive_time_ms_ = std::max(receive_time_ms_,
|
||||
now_ms_ + GaussianRandom(0, standard_deviation_ms));
|
||||
if (kBwOverusing == overuse_detector_->State()) {
|
||||
if (last_overuse + 1 != i) {
|
||||
unique_overuse++;
|
||||
}
|
||||
last_overuse = i;
|
||||
}
|
||||
}
|
||||
return unique_overuse;
|
||||
}
|
||||
|
||||
int RunUntilOveruse(int packets_per_frame, size_t packet_size, int mean_ms,
|
||||
int standard_deviation_ms, int drift_per_frame_ms) {
|
||||
// Simulate a higher send pace, that is too high.
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
for (int j = 0; j < packets_per_frame; ++j) {
|
||||
UpdateDetector(rtp_timestamp_, receive_time_ms_, packet_size);
|
||||
}
|
||||
rtp_timestamp_ += mean_ms * 90;
|
||||
now_ms_ += mean_ms + drift_per_frame_ms;
|
||||
receive_time_ms_ = std::max(receive_time_ms_,
|
||||
now_ms_ + GaussianRandom(0, standard_deviation_ms));
|
||||
if (kBwOverusing == overuse_detector_->State()) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void UpdateDetector(uint32_t rtp_timestamp, int64_t receive_time_ms,
|
||||
size_t packet_size) {
|
||||
uint32_t timestamp_delta;
|
||||
int64_t time_delta;
|
||||
int size_delta;
|
||||
if (inter_arrival_->ComputeDeltas(rtp_timestamp,
|
||||
receive_time_ms,
|
||||
packet_size,
|
||||
×tamp_delta,
|
||||
&time_delta,
|
||||
&size_delta)) {
|
||||
double timestamp_delta_ms = timestamp_delta / 90.0;
|
||||
overuse_estimator_->Update(time_delta, timestamp_delta_ms, size_delta,
|
||||
overuse_detector_->State());
|
||||
overuse_detector_->Detect(overuse_estimator_->offset(),
|
||||
timestamp_delta_ms,
|
||||
overuse_estimator_->num_of_deltas());
|
||||
}
|
||||
}
|
||||
|
||||
int64_t now_ms_;
|
||||
int64_t receive_time_ms_;
|
||||
uint32_t rtp_timestamp_;
|
||||
OverUseDetectorOptions options_;
|
||||
scoped_ptr<OveruseDetector> overuse_detector_;
|
||||
scoped_ptr<OveruseEstimator> overuse_estimator_;
|
||||
scoped_ptr<InterArrival> inter_arrival_;
|
||||
};
|
||||
|
||||
TEST_F(OveruseDetectorTest, GaussianRandom) {
|
||||
int buckets[100];
|
||||
memset(buckets, 0, sizeof(buckets));
|
||||
for (int i = 0; i < 100000; ++i) {
|
||||
int index = GaussianRandom(49, 10);
|
||||
if (index >= 0 && index < 100)
|
||||
buckets[index]++;
|
||||
}
|
||||
for (int n = 0; n < 100; ++n) {
|
||||
printf("Bucket n:%d, %d\n", n, buckets[n]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, SimpleNonOveruse30fps) {
|
||||
size_t packet_size = 1200;
|
||||
uint32_t frame_duration_ms = 33;
|
||||
uint32_t rtp_timestamp = 10 * 90;
|
||||
|
||||
// No variance.
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
now_ms_ += frame_duration_ms;
|
||||
rtp_timestamp += frame_duration_ms * 90;
|
||||
EXPECT_EQ(kBwNormal, overuse_detector_->State());
|
||||
}
|
||||
}
|
||||
|
||||
// Roughly 1 Mbit/s
|
||||
TEST_F(OveruseDetectorTest, SimpleNonOveruseWithReceiveVariance) {
|
||||
uint32_t frame_duration_ms = 10;
|
||||
uint32_t rtp_timestamp = 10 * 90;
|
||||
size_t packet_size = 1200;
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
rtp_timestamp += frame_duration_ms * 90;
|
||||
if (i % 2) {
|
||||
now_ms_ += frame_duration_ms - 5;
|
||||
} else {
|
||||
now_ms_ += frame_duration_ms + 5;
|
||||
}
|
||||
EXPECT_EQ(kBwNormal, overuse_detector_->State());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, SimpleNonOveruseWithRtpTimestampVariance) {
|
||||
// Roughly 1 Mbit/s.
|
||||
uint32_t frame_duration_ms = 10;
|
||||
uint32_t rtp_timestamp = 10 * 90;
|
||||
size_t packet_size = 1200;
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
now_ms_ += frame_duration_ms;
|
||||
if (i % 2) {
|
||||
rtp_timestamp += (frame_duration_ms - 5) * 90;
|
||||
} else {
|
||||
rtp_timestamp += (frame_duration_ms + 5) * 90;
|
||||
}
|
||||
EXPECT_EQ(kBwNormal, overuse_detector_->State());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, SimpleOveruse2000Kbit30fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 6;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 0; // No variance.
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_EQ(6, frames_until_overuse);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, SimpleOveruse100kbit10fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 1;
|
||||
int frame_duration_ms = 100;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 0; // No variance.
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_EQ(4, frames_until_overuse);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, DISABLED_OveruseWithHighVariance100Kbit10fps) {
|
||||
uint32_t frame_duration_ms = 100;
|
||||
uint32_t drift_per_frame_ms = 10;
|
||||
uint32_t rtp_timestamp = frame_duration_ms * 90;
|
||||
size_t packet_size = 1200;
|
||||
int offset = 10;
|
||||
|
||||
// Run 1000 samples to reach steady state.
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
rtp_timestamp += frame_duration_ms * 90;
|
||||
if (i % 2) {
|
||||
offset = rand() % 50;
|
||||
now_ms_ += frame_duration_ms - offset;
|
||||
} else {
|
||||
now_ms_ += frame_duration_ms + offset;
|
||||
}
|
||||
EXPECT_EQ(kBwNormal, overuse_detector_->State());
|
||||
}
|
||||
// Simulate a higher send pace, that is too high.
|
||||
// Above noise generate a standard deviation of approximately 28 ms.
|
||||
// Total build up of 150 ms.
|
||||
for (int j = 0; j < 15; ++j) {
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
now_ms_ += frame_duration_ms + drift_per_frame_ms;
|
||||
rtp_timestamp += frame_duration_ms * 90;
|
||||
EXPECT_EQ(kBwNormal, overuse_detector_->State());
|
||||
}
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
EXPECT_EQ(kBwOverusing, overuse_detector_->State());
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, DISABLED_OveruseWithLowVariance100Kbit10fps) {
|
||||
uint32_t frame_duration_ms = 100;
|
||||
uint32_t drift_per_frame_ms = 1;
|
||||
uint32_t rtp_timestamp = frame_duration_ms * 90;
|
||||
size_t packet_size = 1200;
|
||||
int offset = 10;
|
||||
|
||||
// Run 1000 samples to reach steady state.
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
rtp_timestamp += frame_duration_ms * 90;
|
||||
if (i % 2) {
|
||||
offset = rand() % 2;
|
||||
now_ms_ += frame_duration_ms - offset;
|
||||
} else {
|
||||
now_ms_ += frame_duration_ms + offset;
|
||||
}
|
||||
EXPECT_EQ(kBwNormal, overuse_detector_->State());
|
||||
}
|
||||
// Simulate a higher send pace, that is too high.
|
||||
// Total build up of 6 ms.
|
||||
for (int j = 0; j < 6; ++j) {
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
now_ms_ += frame_duration_ms + drift_per_frame_ms;
|
||||
rtp_timestamp += frame_duration_ms * 90;
|
||||
EXPECT_EQ(kBwNormal, overuse_detector_->State());
|
||||
}
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
EXPECT_EQ(kBwOverusing, overuse_detector_->State());
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, OveruseWithLowVariance2000Kbit30fps) {
|
||||
uint32_t frame_duration_ms = 33;
|
||||
uint32_t drift_per_frame_ms = 1;
|
||||
uint32_t rtp_timestamp = frame_duration_ms * 90;
|
||||
size_t packet_size = 1200;
|
||||
int offset = 0;
|
||||
|
||||
// Run 1000 samples to reach steady state.
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
rtp_timestamp += frame_duration_ms * 90;
|
||||
if (i % 2) {
|
||||
offset = rand() % 2;
|
||||
now_ms_ += frame_duration_ms - offset;
|
||||
} else {
|
||||
now_ms_ += frame_duration_ms + offset;
|
||||
}
|
||||
EXPECT_EQ(kBwNormal, overuse_detector_->State());
|
||||
}
|
||||
// Simulate a higher send pace, that is too high.
|
||||
// Total build up of 30 ms.
|
||||
for (int j = 0; j < 5; ++j) {
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
now_ms_ += frame_duration_ms + drift_per_frame_ms * 6;
|
||||
rtp_timestamp += frame_duration_ms * 90;
|
||||
EXPECT_EQ(kBwNormal, overuse_detector_->State());
|
||||
}
|
||||
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
|
||||
EXPECT_EQ(kBwOverusing, overuse_detector_->State());
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest,
|
||||
DISABLED_ON_ANDROID(LowGaussianVariance30Kbit3fps)) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 1;
|
||||
int frame_duration_ms = 333;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 3;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(29, frames_until_overuse, 5);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, LowGaussianVarianceFastDrift30Kbit3fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 1;
|
||||
int frame_duration_ms = 333;
|
||||
int drift_per_frame_ms = 100;
|
||||
int sigma_ms = 3;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(4, frames_until_overuse, 1);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, HighGaussianVariance30Kbit3fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 1;
|
||||
int frame_duration_ms = 333;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 10;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(79, frames_until_overuse, 30);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift30Kbit3fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 1;
|
||||
int frame_duration_ms = 333;
|
||||
int drift_per_frame_ms = 100;
|
||||
int sigma_ms = 10;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(4, frames_until_overuse, 1);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest,
|
||||
DISABLED_ON_ANDROID(LowGaussianVariance100Kbit5fps)) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 2;
|
||||
int frame_duration_ms = 200;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 3;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(29, frames_until_overuse, 5);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest,
|
||||
DISABLED_ON_ANDROID(HighGaussianVariance100Kbit5fps)) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 2;
|
||||
int frame_duration_ms = 200;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 10;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(79, frames_until_overuse, 15);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest,
|
||||
DISABLED_ON_ANDROID(LowGaussianVariance100Kbit10fps)) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 1;
|
||||
int frame_duration_ms = 100;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 3;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(29, frames_until_overuse, 5);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest,
|
||||
DISABLED_ON_ANDROID(HighGaussianVariance100Kbit10fps)) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 1;
|
||||
int frame_duration_ms = 100;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 10;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(79, frames_until_overuse, 15);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest,
|
||||
DISABLED_ON_ANDROID(LowGaussianVariance300Kbit30fps)) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 1;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 3;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(30, frames_until_overuse, 5);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, LowGaussianVarianceFastDrift300Kbit30fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 1;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 10;
|
||||
int sigma_ms = 3;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(7, frames_until_overuse, 1);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, HighGaussianVariance300Kbit30fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 1;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 10;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(98, frames_until_overuse, 22);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift300Kbit30fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 1;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 10;
|
||||
int sigma_ms = 10;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(12, frames_until_overuse, 2);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest,
|
||||
DISABLED_ON_ANDROID(LowGaussianVariance1000Kbit30fps)) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 3;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 3;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(30, frames_until_overuse, 5);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, LowGaussianVarianceFastDrift1000Kbit30fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 3;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 10;
|
||||
int sigma_ms = 3;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(7, frames_until_overuse, 1);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, HighGaussianVariance1000Kbit30fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 3;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 10;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(98, frames_until_overuse, 22);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift1000Kbit30fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 3;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 10;
|
||||
int sigma_ms = 10;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(12, frames_until_overuse, 2);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest,
|
||||
DISABLED_ON_ANDROID(LowGaussianVariance2000Kbit30fps)) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 6;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 3;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(30, frames_until_overuse, 5);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, LowGaussianVarianceFastDrift2000Kbit30fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 6;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 10;
|
||||
int sigma_ms = 3;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(7, frames_until_overuse, 1);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, HighGaussianVariance2000Kbit30fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 6;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 1;
|
||||
int sigma_ms = 10;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(98, frames_until_overuse, 22);
|
||||
}
|
||||
|
||||
TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift2000Kbit30fps) {
|
||||
size_t packet_size = 1200;
|
||||
int packets_per_frame = 6;
|
||||
int frame_duration_ms = 33;
|
||||
int drift_per_frame_ms = 10;
|
||||
int sigma_ms = 10;
|
||||
int unique_overuse = Run100000Samples(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms);
|
||||
EXPECT_EQ(0, unique_overuse);
|
||||
int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size,
|
||||
frame_duration_ms, sigma_ms, drift_per_frame_ms);
|
||||
EXPECT_NEAR(12, frames_until_overuse, 2);
|
||||
}
|
||||
} // namespace testing
|
||||
} // namespace webrtc
|
153
webrtc/modules/remote_bitrate_estimator/overuse_estimator.cc
Normal file
153
webrtc/modules/remote_bitrate_estimator/overuse_estimator.cc
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2013 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 "webrtc/modules/remote_bitrate_estimator/overuse_estimator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
#include "webrtc/system_wrappers/interface/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
enum { kMinFramePeriodHistoryLength = 60 };
|
||||
enum { kDeltaCounterMax = 1000 };
|
||||
|
||||
OveruseEstimator::OveruseEstimator(const OverUseDetectorOptions& options)
|
||||
: options_(options),
|
||||
num_of_deltas_(0),
|
||||
slope_(options_.initial_slope),
|
||||
offset_(options_.initial_offset),
|
||||
prev_offset_(options_.initial_offset),
|
||||
E_(),
|
||||
process_noise_(),
|
||||
avg_noise_(options_.initial_avg_noise),
|
||||
var_noise_(options_.initial_var_noise),
|
||||
ts_delta_hist_() {
|
||||
memcpy(E_, options_.initial_e, sizeof(E_));
|
||||
memcpy(process_noise_, options_.initial_process_noise,
|
||||
sizeof(process_noise_));
|
||||
}
|
||||
|
||||
OveruseEstimator::~OveruseEstimator() {
|
||||
ts_delta_hist_.clear();
|
||||
}
|
||||
|
||||
void OveruseEstimator::Update(int64_t t_delta,
|
||||
double ts_delta,
|
||||
int size_delta,
|
||||
BandwidthUsage current_hypothesis) {
|
||||
const double min_frame_period = UpdateMinFramePeriod(ts_delta);
|
||||
const double t_ts_delta = t_delta - ts_delta;
|
||||
double fs_delta = size_delta;
|
||||
|
||||
++num_of_deltas_;
|
||||
if (num_of_deltas_ > kDeltaCounterMax) {
|
||||
num_of_deltas_ = kDeltaCounterMax;
|
||||
}
|
||||
|
||||
// Update the Kalman filter.
|
||||
E_[0][0] += process_noise_[0];
|
||||
E_[1][1] += process_noise_[1];
|
||||
|
||||
if ((current_hypothesis == kBwOverusing && offset_ < prev_offset_) ||
|
||||
(current_hypothesis == kBwUnderusing && offset_ > prev_offset_)) {
|
||||
E_[1][1] += 10 * process_noise_[1];
|
||||
}
|
||||
|
||||
const double h[2] = {fs_delta, 1.0};
|
||||
const double Eh[2] = {E_[0][0]*h[0] + E_[0][1]*h[1],
|
||||
E_[1][0]*h[0] + E_[1][1]*h[1]};
|
||||
|
||||
const double residual = t_ts_delta - slope_*h[0] - offset_;
|
||||
|
||||
const bool in_stable_state = (current_hypothesis == kBwNormal);
|
||||
const double max_residual = 3.0 * sqrt(var_noise_);
|
||||
// We try to filter out very late frames. For instance periodic key
|
||||
// frames doesn't fit the Gaussian model well.
|
||||
if (fabs(residual) < max_residual) {
|
||||
UpdateNoiseEstimate(residual, min_frame_period, in_stable_state);
|
||||
} else {
|
||||
UpdateNoiseEstimate(residual < 0 ? -max_residual : max_residual,
|
||||
min_frame_period, in_stable_state);
|
||||
}
|
||||
|
||||
const double denom = var_noise_ + h[0]*Eh[0] + h[1]*Eh[1];
|
||||
|
||||
const double K[2] = {Eh[0] / denom,
|
||||
Eh[1] / denom};
|
||||
|
||||
const double IKh[2][2] = {{1.0 - K[0]*h[0], -K[0]*h[1]},
|
||||
{-K[1]*h[0], 1.0 - K[1]*h[1]}};
|
||||
const double e00 = E_[0][0];
|
||||
const double e01 = E_[0][1];
|
||||
|
||||
// Update state.
|
||||
E_[0][0] = e00 * IKh[0][0] + E_[1][0] * IKh[0][1];
|
||||
E_[0][1] = e01 * IKh[0][0] + E_[1][1] * IKh[0][1];
|
||||
E_[1][0] = e00 * IKh[1][0] + E_[1][0] * IKh[1][1];
|
||||
E_[1][1] = e01 * IKh[1][0] + E_[1][1] * IKh[1][1];
|
||||
|
||||
// The covariance matrix must be positive semi-definite.
|
||||
bool positive_semi_definite = E_[0][0] + E_[1][1] >= 0 &&
|
||||
E_[0][0] * E_[1][1] - E_[0][1] * E_[1][0] >= 0 && E_[0][0] >= 0;
|
||||
assert(positive_semi_definite);
|
||||
if (!positive_semi_definite) {
|
||||
LOG(LS_ERROR) << "The over-use estimator's covariance matrix is no longer "
|
||||
"semi-definite.";
|
||||
}
|
||||
|
||||
slope_ = slope_ + K[0] * residual;
|
||||
prev_offset_ = offset_;
|
||||
offset_ = offset_ + K[1] * residual;
|
||||
}
|
||||
|
||||
double OveruseEstimator::UpdateMinFramePeriod(double ts_delta) {
|
||||
double min_frame_period = ts_delta;
|
||||
if (ts_delta_hist_.size() >= kMinFramePeriodHistoryLength) {
|
||||
ts_delta_hist_.pop_front();
|
||||
}
|
||||
std::list<double>::iterator it = ts_delta_hist_.begin();
|
||||
for (; it != ts_delta_hist_.end(); it++) {
|
||||
min_frame_period = std::min(*it, min_frame_period);
|
||||
}
|
||||
ts_delta_hist_.push_back(ts_delta);
|
||||
return min_frame_period;
|
||||
}
|
||||
|
||||
void OveruseEstimator::UpdateNoiseEstimate(double residual,
|
||||
double ts_delta,
|
||||
bool stable_state) {
|
||||
if (!stable_state) {
|
||||
return;
|
||||
}
|
||||
// Faster filter during startup to faster adapt to the jitter level
|
||||
// of the network. |alpha| is tuned for 30 frames per second, but is scaled
|
||||
// according to |ts_delta|.
|
||||
double alpha = 0.01;
|
||||
if (num_of_deltas_ > 10*30) {
|
||||
alpha = 0.002;
|
||||
}
|
||||
// Only update the noise estimate if we're not over-using. |beta| is a
|
||||
// function of alpha and the time delta since the previous update.
|
||||
const double beta = pow(1 - alpha, ts_delta * 30.0 / 1000.0);
|
||||
avg_noise_ = beta * avg_noise_
|
||||
+ (1 - beta) * residual;
|
||||
var_noise_ = beta * var_noise_
|
||||
+ (1 - beta) * (avg_noise_ - residual) * (avg_noise_ - residual);
|
||||
if (var_noise_ < 1e-7) {
|
||||
var_noise_ = 1e-7;
|
||||
}
|
||||
}
|
||||
} // namespace webrtc
|
70
webrtc/modules/remote_bitrate_estimator/overuse_estimator.h
Normal file
70
webrtc/modules/remote_bitrate_estimator/overuse_estimator.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2013 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 WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_
|
||||
#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class OveruseEstimator {
|
||||
public:
|
||||
explicit OveruseEstimator(const OverUseDetectorOptions& options);
|
||||
~OveruseEstimator();
|
||||
|
||||
// Update the estimator with a new sample. The deltas should represent deltas
|
||||
// between timestamp groups as defined by the InterArrival class.
|
||||
// |current_hypothesis| should be the hypothesis of the over-use detector at
|
||||
// this time.
|
||||
void Update(int64_t t_delta, double ts_delta, int size_delta,
|
||||
BandwidthUsage current_hypothesis);
|
||||
|
||||
// Returns the estimated noise/jitter variance in ms^2.
|
||||
double var_noise() const {
|
||||
return var_noise_;
|
||||
}
|
||||
|
||||
// Returns the estimated inter-arrival time delta offset in ms.
|
||||
double offset() const {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
// Returns the number of deltas which the current over-use estimator state is
|
||||
// based on.
|
||||
unsigned int num_of_deltas() const {
|
||||
return num_of_deltas_;
|
||||
}
|
||||
|
||||
private:
|
||||
double UpdateMinFramePeriod(double ts_delta);
|
||||
void UpdateNoiseEstimate(double residual, double ts_delta, bool stable_state);
|
||||
|
||||
// Must be first member variable. Cannot be const because we need to be
|
||||
// copyable.
|
||||
OverUseDetectorOptions options_;
|
||||
uint16_t num_of_deltas_;
|
||||
double slope_;
|
||||
double offset_;
|
||||
double prev_offset_;
|
||||
double E_[2][2];
|
||||
double process_noise_[2];
|
||||
double avg_noise_;
|
||||
double var_noise_;
|
||||
std::list<double> ts_delta_hist_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(OveruseEstimator);
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_
|
@ -15,8 +15,8 @@
|
||||
'target_name': 'remote_bitrate_estimator',
|
||||
'type': 'static_library',
|
||||
'dependencies': [
|
||||
'<(webrtc_root)/modules/remote_bitrate_estimator/remote_bitrate_estimator_components.gyp:rbe_components',
|
||||
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
||||
'<(rbe_components_path)/remote_bitrate_estimator_components.gyp:rbe_components',
|
||||
],
|
||||
'sources': [
|
||||
'include/bwe_defines.h',
|
||||
|
@ -0,0 +1,499 @@
|
||||
/*
|
||||
* Copyright (c) 2013 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 <math.h>
|
||||
#include <map>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/base/thread_annotations.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/inter_arrival.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/overuse_detector.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/overuse_estimator.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/rate_statistics.h"
|
||||
#include "webrtc/system_wrappers/interface/clock.h"
|
||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||
#include "webrtc/system_wrappers/interface/logging.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
enum {
|
||||
kTimestampGroupLengthMs = 5,
|
||||
kAbsSendTimeFraction = 18,
|
||||
kAbsSendTimeInterArrivalUpshift = 8,
|
||||
kInterArrivalShift = kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift,
|
||||
kInitialProbingIntervalMs = 2000,
|
||||
kMinClusterSize = 4,
|
||||
kMaxProbePackets = 15,
|
||||
kExpectedNumberOfProbes = 3
|
||||
};
|
||||
|
||||
static const size_t kPropagationDeltaQueueMaxSize = 1000;
|
||||
static const int64_t kPropagationDeltaQueueMaxTimeMs = 1000;
|
||||
static const double kTimestampToMs = 1000.0 /
|
||||
static_cast<double>(1 << kInterArrivalShift);
|
||||
|
||||
// Removes the entries at |index| of |time| and |value|, if time[index] is
|
||||
// smaller than or equal to |deadline|. |time| must be sorted ascendingly.
|
||||
static void RemoveStaleEntries(
|
||||
std::vector<int64_t>* time, std::vector<int>* value, int64_t deadline) {
|
||||
assert(time->size() == value->size());
|
||||
std::vector<int64_t>::iterator end_of_removal = std::upper_bound(
|
||||
time->begin(), time->end(), deadline);
|
||||
size_t end_of_removal_index = end_of_removal - time->begin();
|
||||
|
||||
time->erase(time->begin(), end_of_removal);
|
||||
value->erase(value->begin(), value->begin() + end_of_removal_index);
|
||||
}
|
||||
|
||||
template<typename K, typename V>
|
||||
std::vector<K> Keys(const std::map<K, V>& map) {
|
||||
std::vector<K> keys;
|
||||
keys.reserve(map.size());
|
||||
for (typename std::map<K, V>::const_iterator it = map.begin();
|
||||
it != map.end(); ++it) {
|
||||
keys.push_back(it->first);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
class RemoteBitrateEstimatorAbsSendTimeImpl : public RemoteBitrateEstimator {
|
||||
public:
|
||||
RemoteBitrateEstimatorAbsSendTimeImpl(RemoteBitrateObserver* observer,
|
||||
Clock* clock,
|
||||
RateControlType control_type,
|
||||
uint32_t min_bitrate_bps);
|
||||
virtual ~RemoteBitrateEstimatorAbsSendTimeImpl() {}
|
||||
|
||||
virtual void IncomingPacket(int64_t arrival_time_ms,
|
||||
size_t payload_size,
|
||||
const RTPHeader& header) OVERRIDE;
|
||||
// This class relies on Process() being called periodically (at least once
|
||||
// every other second) for streams to be timed out properly. Therefore it
|
||||
// shouldn't be detached from the ProcessThread except if it's about to be
|
||||
// deleted.
|
||||
virtual int32_t Process() OVERRIDE;
|
||||
virtual int32_t TimeUntilNextProcess() OVERRIDE;
|
||||
virtual void OnRttUpdate(uint32_t rtt) OVERRIDE;
|
||||
virtual void RemoveStream(unsigned int ssrc) OVERRIDE;
|
||||
virtual bool LatestEstimate(std::vector<unsigned int>* ssrcs,
|
||||
unsigned int* bitrate_bps) const OVERRIDE;
|
||||
virtual bool GetStats(ReceiveBandwidthEstimatorStats* output) const OVERRIDE;
|
||||
|
||||
private:
|
||||
typedef std::map<unsigned int, int64_t> Ssrcs;
|
||||
|
||||
struct Probe {
|
||||
Probe(int64_t send_time_ms, int64_t recv_time_ms, size_t payload_size)
|
||||
: send_time_ms(send_time_ms),
|
||||
recv_time_ms(recv_time_ms),
|
||||
payload_size(payload_size) {}
|
||||
int64_t send_time_ms;
|
||||
int64_t recv_time_ms;
|
||||
size_t payload_size;
|
||||
};
|
||||
|
||||
struct Cluster {
|
||||
Cluster()
|
||||
: send_mean_ms(0.0f),
|
||||
recv_mean_ms(0.0f),
|
||||
mean_size(0),
|
||||
count(0),
|
||||
num_above_min_delta(0) {}
|
||||
|
||||
float send_mean_ms;
|
||||
float recv_mean_ms;
|
||||
// TODO(holmer): Add some variance metric as well?
|
||||
size_t mean_size;
|
||||
int count;
|
||||
int num_above_min_delta;
|
||||
};
|
||||
|
||||
static bool IsWithinClusterBounds(int send_delta_ms,
|
||||
const Cluster& cluster_aggregate) {
|
||||
if (cluster_aggregate.count == 0)
|
||||
return true;
|
||||
float cluster_mean = cluster_aggregate.send_mean_ms /
|
||||
static_cast<float>(cluster_aggregate.count);
|
||||
return fabs(static_cast<float>(send_delta_ms) - cluster_mean) < 2.5f;
|
||||
}
|
||||
|
||||
static void AddCluster(std::list<Cluster>* clusters, Cluster* cluster) {
|
||||
cluster->send_mean_ms /= static_cast<float>(cluster->count);
|
||||
cluster->recv_mean_ms /= static_cast<float>(cluster->count);
|
||||
cluster->mean_size /= cluster->count;
|
||||
clusters->push_back(*cluster);
|
||||
}
|
||||
|
||||
int Id() const {
|
||||
return static_cast<int>(reinterpret_cast<uint64_t>(this));
|
||||
}
|
||||
|
||||
bool IsProbe(int64_t send_time_ms, int payload_size) const
|
||||
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get());
|
||||
|
||||
// Triggers a new estimate calculation.
|
||||
void UpdateEstimate(int64_t now_ms)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get());
|
||||
|
||||
void UpdateStats(int propagation_delta_ms, int64_t now_ms)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get());
|
||||
|
||||
void ComputeClusters(std::list<Cluster>* clusters) const;
|
||||
|
||||
int FindBestProbeBitrate(const std::list<Cluster>& clusters) const;
|
||||
|
||||
void ProcessClusters(int64_t now_ms)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get());
|
||||
|
||||
scoped_ptr<CriticalSectionWrapper> crit_sect_;
|
||||
RemoteBitrateObserver* observer_ GUARDED_BY(crit_sect_.get());
|
||||
Clock* clock_;
|
||||
Ssrcs ssrcs_ GUARDED_BY(crit_sect_.get());
|
||||
scoped_ptr<InterArrival> inter_arrival_ GUARDED_BY(crit_sect_.get());
|
||||
OveruseEstimator estimator_ GUARDED_BY(crit_sect_.get());
|
||||
OveruseDetector detector_ GUARDED_BY(crit_sect_.get());
|
||||
RateStatistics incoming_bitrate_ GUARDED_BY(crit_sect_.get());
|
||||
scoped_ptr<RemoteRateControl> remote_rate_ GUARDED_BY(crit_sect_.get());
|
||||
int64_t last_process_time_;
|
||||
std::vector<int> recent_propagation_delta_ms_ GUARDED_BY(crit_sect_.get());
|
||||
std::vector<int64_t> recent_update_time_ms_ GUARDED_BY(crit_sect_.get());
|
||||
int process_interval_ms_ GUARDED_BY(crit_sect_.get());
|
||||
int total_propagation_delta_ms_ GUARDED_BY(crit_sect_.get());
|
||||
|
||||
std::list<Probe> probes_;
|
||||
int64_t first_packet_time_ms_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(RemoteBitrateEstimatorAbsSendTimeImpl);
|
||||
};
|
||||
|
||||
RemoteBitrateEstimatorAbsSendTimeImpl::RemoteBitrateEstimatorAbsSendTimeImpl(
|
||||
RemoteBitrateObserver* observer,
|
||||
Clock* clock,
|
||||
RateControlType control_type,
|
||||
uint32_t min_bitrate_bps)
|
||||
: crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||
observer_(observer),
|
||||
clock_(clock),
|
||||
ssrcs_(),
|
||||
inter_arrival_(),
|
||||
estimator_(OverUseDetectorOptions()),
|
||||
detector_(OverUseDetectorOptions()),
|
||||
incoming_bitrate_(1000, 8000),
|
||||
remote_rate_(RemoteRateControl::Create(control_type, min_bitrate_bps)),
|
||||
last_process_time_(-1),
|
||||
process_interval_ms_(kProcessIntervalMs),
|
||||
total_propagation_delta_ms_(0),
|
||||
first_packet_time_ms_(-1) {
|
||||
assert(observer_);
|
||||
assert(clock_);
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorAbsSendTimeImpl::ComputeClusters(
|
||||
std::list<Cluster>* clusters) const {
|
||||
Cluster current;
|
||||
int64_t prev_send_time = -1;
|
||||
int64_t prev_recv_time = -1;
|
||||
for (std::list<Probe>::const_iterator it = probes_.begin();
|
||||
it != probes_.end();
|
||||
++it) {
|
||||
if (prev_send_time >= 0) {
|
||||
int send_delta_ms = it->send_time_ms - prev_send_time;
|
||||
int recv_delta_ms = it->recv_time_ms - prev_recv_time;
|
||||
if (send_delta_ms > 1 && recv_delta_ms > 1) {
|
||||
++current.num_above_min_delta;
|
||||
}
|
||||
if (!IsWithinClusterBounds(send_delta_ms, current)) {
|
||||
if (current.count >= kMinClusterSize)
|
||||
AddCluster(clusters, ¤t);
|
||||
current = Cluster();
|
||||
}
|
||||
current.send_mean_ms += send_delta_ms;
|
||||
current.recv_mean_ms += recv_delta_ms;
|
||||
current.mean_size += it->payload_size;
|
||||
++current.count;
|
||||
}
|
||||
prev_send_time = it->send_time_ms;
|
||||
prev_recv_time = it->recv_time_ms;
|
||||
}
|
||||
if (current.count >= kMinClusterSize)
|
||||
AddCluster(clusters, ¤t);
|
||||
}
|
||||
|
||||
int RemoteBitrateEstimatorAbsSendTimeImpl::FindBestProbeBitrate(
|
||||
const std::list<Cluster>& clusters) const {
|
||||
int highest_probe_bitrate_bps = 0;
|
||||
for (std::list<Cluster>::const_iterator it = clusters.begin();
|
||||
it != clusters.end();
|
||||
++it) {
|
||||
if (it->send_mean_ms == 0 || it->recv_mean_ms == 0)
|
||||
continue;
|
||||
int send_bitrate_bps = it->mean_size * 8 * 1000 / it->send_mean_ms;
|
||||
int recv_bitrate_bps = it->mean_size * 8 * 1000 / it->recv_mean_ms;
|
||||
if (it->num_above_min_delta > it->count / 2 &&
|
||||
(it->recv_mean_ms - it->send_mean_ms <= 1.5f ||
|
||||
it->send_mean_ms - it->recv_mean_ms >= 3.0f)) {
|
||||
int probe_bitrate_bps = std::min(send_bitrate_bps, recv_bitrate_bps);
|
||||
if (probe_bitrate_bps > highest_probe_bitrate_bps)
|
||||
highest_probe_bitrate_bps = probe_bitrate_bps;
|
||||
LOG(LS_INFO) << "Probe successful, sent at " << send_bitrate_bps
|
||||
<< " bps, received at " << recv_bitrate_bps
|
||||
<< " bps. Mean send delta: " << it->send_mean_ms
|
||||
<< " ms, mean recv delta: " << it->recv_mean_ms
|
||||
<< " ms, num probes: " << it->count;
|
||||
} else {
|
||||
LOG(LS_INFO) << "Probe failed, sent at " << send_bitrate_bps
|
||||
<< " bps, received at " << recv_bitrate_bps
|
||||
<< " bps. Mean send delta: " << it->send_mean_ms
|
||||
<< " ms, mean recv delta: " << it->recv_mean_ms
|
||||
<< " ms, num probes: " << it->count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return highest_probe_bitrate_bps;
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorAbsSendTimeImpl::ProcessClusters(int64_t now_ms) {
|
||||
std::list<Cluster> clusters;
|
||||
ComputeClusters(&clusters);
|
||||
if (clusters.empty()) {
|
||||
// If we reach the max number of probe packets and still have no clusters,
|
||||
// we will remove the oldest one.
|
||||
if (probes_.size() >= kMaxProbePackets)
|
||||
probes_.pop_front();
|
||||
return;
|
||||
}
|
||||
int highest_probe_bitrate_bps = FindBestProbeBitrate(clusters);
|
||||
bool initial_probe =
|
||||
!remote_rate_->ValidEstimate() && highest_probe_bitrate_bps > 0;
|
||||
bool probe_above_estimate =
|
||||
remote_rate_->ValidEstimate() &&
|
||||
highest_probe_bitrate_bps >
|
||||
static_cast<int>(remote_rate_->LatestEstimate());
|
||||
if (initial_probe || probe_above_estimate) {
|
||||
LOG(LS_INFO) << "Set new bitrate based on probe: "
|
||||
<< highest_probe_bitrate_bps << " bps.";
|
||||
remote_rate_->SetEstimate(highest_probe_bitrate_bps, now_ms);
|
||||
}
|
||||
// Not probing and received non-probe packet, or finished with current set
|
||||
// of probes.
|
||||
if (clusters.size() >= kExpectedNumberOfProbes)
|
||||
probes_.clear();
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorAbsSendTimeImpl::IncomingPacket(
|
||||
int64_t arrival_time_ms,
|
||||
size_t payload_size,
|
||||
const RTPHeader& header) {
|
||||
if (!header.extension.hasAbsoluteSendTime) {
|
||||
LOG(LS_WARNING) << "RemoteBitrateEstimatorAbsSendTimeImpl: Incoming packet "
|
||||
"is missing absolute send time extension!";
|
||||
}
|
||||
uint32_t absolute_send_time = header.extension.absoluteSendTime;
|
||||
assert(absolute_send_time < (1ul << 24));
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
ssrcs_[header.ssrc] = now_ms;
|
||||
incoming_bitrate_.Update(payload_size, now_ms);
|
||||
const BandwidthUsage prior_state = detector_.State();
|
||||
|
||||
if (first_packet_time_ms_ == -1)
|
||||
first_packet_time_ms_ = clock_->TimeInMilliseconds();
|
||||
|
||||
// Shift up send time to use the full 32 bits that inter_arrival works with,
|
||||
// so wrapping works properly.
|
||||
uint32_t timestamp = absolute_send_time << kAbsSendTimeInterArrivalUpshift;
|
||||
uint32_t ts_delta = 0;
|
||||
int64_t t_delta = 0;
|
||||
int size_delta = 0;
|
||||
// For now only try to detect probes while we don't have a valid estimate.
|
||||
if (!remote_rate_->ValidEstimate() ||
|
||||
now_ms - first_packet_time_ms_ < kInitialProbingIntervalMs) {
|
||||
int64_t send_time_ms = static_cast<int64_t>(timestamp) * kTimestampToMs;
|
||||
// TODO(holmer): Use a map instead to get correct order?
|
||||
if (probes_.empty()) {
|
||||
LOG(LS_INFO) << "Probe packet received: send time=" << send_time_ms
|
||||
<< " ms, recv time=" << arrival_time_ms << " ms";
|
||||
} else {
|
||||
int send_delta_ms = send_time_ms - probes_.back().send_time_ms;
|
||||
int recv_delta_ms = arrival_time_ms - probes_.back().recv_time_ms;
|
||||
LOG(LS_INFO) << "Probe packet received: send time=" << send_time_ms
|
||||
<< " ms, recv time=" << arrival_time_ms
|
||||
<< " ms, send delta=" << send_delta_ms
|
||||
<< " ms, recv delta=" << recv_delta_ms << " ms.";
|
||||
}
|
||||
probes_.push_back(Probe(send_time_ms, arrival_time_ms, payload_size));
|
||||
ProcessClusters(now_ms);
|
||||
}
|
||||
if (!inter_arrival_.get()) {
|
||||
inter_arrival_.reset(new InterArrival(
|
||||
(kTimestampGroupLengthMs << kInterArrivalShift) / 1000,
|
||||
kTimestampToMs, remote_rate_->GetControlType() == kAimdControl));
|
||||
}
|
||||
if (inter_arrival_->ComputeDeltas(timestamp,
|
||||
arrival_time_ms,
|
||||
payload_size,
|
||||
&ts_delta,
|
||||
&t_delta,
|
||||
&size_delta)) {
|
||||
double ts_delta_ms = (1000.0 * ts_delta) / (1 << kInterArrivalShift);
|
||||
estimator_.Update(t_delta, ts_delta_ms, size_delta, detector_.State());
|
||||
detector_.Detect(estimator_.offset(), ts_delta_ms,
|
||||
estimator_.num_of_deltas());
|
||||
UpdateStats(static_cast<int>(t_delta - ts_delta_ms), now_ms);
|
||||
}
|
||||
if (detector_.State() == kBwOverusing) {
|
||||
unsigned int incoming_bitrate = incoming_bitrate_.Rate(now_ms);
|
||||
if (prior_state != kBwOverusing ||
|
||||
remote_rate_->TimeToReduceFurther(now_ms, incoming_bitrate)) {
|
||||
// The first overuse should immediately trigger a new estimate.
|
||||
// We also have to update the estimate immediately if we are overusing
|
||||
// and the target bitrate is too high compared to what we are receiving.
|
||||
UpdateEstimate(now_ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t RemoteBitrateEstimatorAbsSendTimeImpl::Process() {
|
||||
if (TimeUntilNextProcess() > 0) {
|
||||
return 0;
|
||||
}
|
||||
{
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
UpdateEstimate(clock_->TimeInMilliseconds());
|
||||
}
|
||||
last_process_time_ = clock_->TimeInMilliseconds();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t RemoteBitrateEstimatorAbsSendTimeImpl::TimeUntilNextProcess() {
|
||||
if (last_process_time_ < 0) {
|
||||
return 0;
|
||||
}
|
||||
{
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
return last_process_time_ + process_interval_ms_ -
|
||||
clock_->TimeInMilliseconds();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorAbsSendTimeImpl::UpdateEstimate(int64_t now_ms) {
|
||||
if (!inter_arrival_.get()) {
|
||||
// No packets have been received on the active streams.
|
||||
return;
|
||||
}
|
||||
for (Ssrcs::iterator it = ssrcs_.begin(); it != ssrcs_.end();) {
|
||||
if ((now_ms - it->second) > kStreamTimeOutMs) {
|
||||
ssrcs_.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (ssrcs_.empty()) {
|
||||
// We can't update the estimate if we don't have any active streams.
|
||||
inter_arrival_.reset();
|
||||
// We deliberately don't reset the first_packet_time_ms_ here for now since
|
||||
// we only probe for bandwidth in the beginning of a call right now.
|
||||
return;
|
||||
}
|
||||
|
||||
const RateControlInput input(detector_.State(),
|
||||
incoming_bitrate_.Rate(now_ms),
|
||||
estimator_.var_noise());
|
||||
const RateControlRegion region = remote_rate_->Update(&input, now_ms);
|
||||
unsigned int target_bitrate = remote_rate_->UpdateBandwidthEstimate(now_ms);
|
||||
if (remote_rate_->ValidEstimate()) {
|
||||
process_interval_ms_ = remote_rate_->GetFeedbackInterval();
|
||||
observer_->OnReceiveBitrateChanged(Keys(ssrcs_), target_bitrate);
|
||||
}
|
||||
detector_.SetRateControlRegion(region);
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorAbsSendTimeImpl::OnRttUpdate(uint32_t rtt) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
remote_rate_->SetRtt(rtt);
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorAbsSendTimeImpl::RemoveStream(unsigned int ssrc) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
ssrcs_.erase(ssrc);
|
||||
}
|
||||
|
||||
bool RemoteBitrateEstimatorAbsSendTimeImpl::LatestEstimate(
|
||||
std::vector<unsigned int>* ssrcs,
|
||||
unsigned int* bitrate_bps) const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
assert(ssrcs);
|
||||
assert(bitrate_bps);
|
||||
if (!remote_rate_->ValidEstimate()) {
|
||||
return false;
|
||||
}
|
||||
*ssrcs = Keys(ssrcs_);
|
||||
if (ssrcs_.empty()) {
|
||||
*bitrate_bps = 0;
|
||||
} else {
|
||||
*bitrate_bps = remote_rate_->LatestEstimate();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteBitrateEstimatorAbsSendTimeImpl::GetStats(
|
||||
ReceiveBandwidthEstimatorStats* output) const {
|
||||
{
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
output->recent_propagation_time_delta_ms = recent_propagation_delta_ms_;
|
||||
output->recent_arrival_time_ms = recent_update_time_ms_;
|
||||
output->total_propagation_time_delta_ms = total_propagation_delta_ms_;
|
||||
}
|
||||
RemoveStaleEntries(
|
||||
&output->recent_arrival_time_ms,
|
||||
&output->recent_propagation_time_delta_ms,
|
||||
clock_->TimeInMilliseconds() - kPropagationDeltaQueueMaxTimeMs);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorAbsSendTimeImpl::UpdateStats(
|
||||
int propagation_delta_ms, int64_t now_ms) {
|
||||
// The caller must enter crit_sect_ before the call.
|
||||
|
||||
// Remove the oldest entry if the size limit is reached.
|
||||
if (recent_update_time_ms_.size() == kPropagationDeltaQueueMaxSize) {
|
||||
recent_update_time_ms_.erase(recent_update_time_ms_.begin());
|
||||
recent_propagation_delta_ms_.erase(recent_propagation_delta_ms_.begin());
|
||||
}
|
||||
|
||||
recent_propagation_delta_ms_.push_back(propagation_delta_ms);
|
||||
recent_update_time_ms_.push_back(now_ms);
|
||||
|
||||
RemoveStaleEntries(
|
||||
&recent_update_time_ms_,
|
||||
&recent_propagation_delta_ms_,
|
||||
now_ms - kPropagationDeltaQueueMaxTimeMs);
|
||||
|
||||
total_propagation_delta_ms_ =
|
||||
std::max(total_propagation_delta_ms_ + propagation_delta_ms, 0);
|
||||
}
|
||||
|
||||
RemoteBitrateEstimator* AbsoluteSendTimeRemoteBitrateEstimatorFactory::Create(
|
||||
RemoteBitrateObserver* observer,
|
||||
Clock* clock,
|
||||
RateControlType control_type,
|
||||
uint32_t min_bitrate_bps) const {
|
||||
LOG(LS_INFO) << "AbsoluteSendTimeRemoteBitrateEstimatorFactory: "
|
||||
"Instantiating.";
|
||||
return new RemoteBitrateEstimatorAbsSendTimeImpl(observer,
|
||||
clock,
|
||||
control_type,
|
||||
min_bitrate_bps);
|
||||
}
|
||||
} // namespace webrtc
|
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) 2013 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 "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RemoteBitrateEstimatorAbsSendTimeTest :
|
||||
public RemoteBitrateEstimatorTest {
|
||||
public:
|
||||
static const uint32_t kRemoteBitrateEstimatorMinBitrateBps = 30000;
|
||||
|
||||
RemoteBitrateEstimatorAbsSendTimeTest() {}
|
||||
virtual void SetUp() {
|
||||
bitrate_estimator_.reset(
|
||||
AbsoluteSendTimeRemoteBitrateEstimatorFactory().Create(
|
||||
bitrate_observer_.get(),
|
||||
&clock_,
|
||||
kAimdControl,
|
||||
kRemoteBitrateEstimatorMinBitrateBps));
|
||||
}
|
||||
protected:
|
||||
DISALLOW_COPY_AND_ASSIGN(RemoteBitrateEstimatorAbsSendTimeTest);
|
||||
};
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, InitialBehavior) {
|
||||
InitialBehaviorTestHelper(508017);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, RateIncreaseReordering) {
|
||||
RateIncreaseReorderingTestHelper(506422);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, RateIncreaseRtpTimestamps) {
|
||||
RateIncreaseRtpTimestampsTestHelper(1090);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropOneStream) {
|
||||
CapacityDropTestHelper(1, false, 700);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropOneStreamWrap) {
|
||||
CapacityDropTestHelper(1, true, 700);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropTwoStreamsWrap) {
|
||||
CapacityDropTestHelper(2, true, 700);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropThreeStreamsWrap) {
|
||||
CapacityDropTestHelper(3, true, 700);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropThirteenStreamsWrap) {
|
||||
CapacityDropTestHelper(13, true, 666);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropNineteenStreamsWrap) {
|
||||
CapacityDropTestHelper(19, true, 666);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropThirtyStreamsWrap) {
|
||||
CapacityDropTestHelper(30, true, 666);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestTimestampGrouping) {
|
||||
TestTimestampGroupingTestHelper();
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestGetStats) {
|
||||
TestGetStatsHelper();
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, 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(RemoteBitrateEstimatorAbsSendTimeTest, 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(RemoteBitrateEstimatorAbsSendTimeTest, TestProcessAfterTimeout) {
|
||||
// This time constant must be equal to the ones defined for the
|
||||
// RemoteBitrateEstimator.
|
||||
const int kStreamTimeOutMs = 2000;
|
||||
const int kProcessIntervalMs = 1000;
|
||||
IncomingPacket(0, 1000, clock_.TimeInMilliseconds(), 0, 0);
|
||||
clock_.AdvanceTimeMilliseconds(kStreamTimeOutMs + 1);
|
||||
// Trigger timeout.
|
||||
EXPECT_EQ(0, bitrate_estimator_->Process());
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
|
||||
// This shouldn't crash.
|
||||
EXPECT_EQ(0, bitrate_estimator_->Process());
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestProbeDetection) {
|
||||
const int kProbeLength = 5;
|
||||
int64_t now_ms = clock_.TimeInMilliseconds();
|
||||
// First burst sent at 8 * 1000 / 10 = 800 kbps.
|
||||
for (int i = 0; i < kProbeLength; ++i) {
|
||||
clock_.AdvanceTimeMilliseconds(10);
|
||||
now_ms = clock_.TimeInMilliseconds();
|
||||
IncomingPacket(0, 1000, now_ms, 90 * now_ms, AbsSendTime(now_ms, 1000));
|
||||
}
|
||||
|
||||
// Second burst sent at 8 * 1000 / 5 = 1600 kbps.
|
||||
for (int i = 0; i < kProbeLength; ++i) {
|
||||
clock_.AdvanceTimeMilliseconds(5);
|
||||
now_ms = clock_.TimeInMilliseconds();
|
||||
IncomingPacket(0, 1000, now_ms, 90 * now_ms, AbsSendTime(now_ms, 1000));
|
||||
}
|
||||
|
||||
EXPECT_EQ(0, bitrate_estimator_->Process());
|
||||
EXPECT_TRUE(bitrate_observer_->updated());
|
||||
EXPECT_GT(bitrate_observer_->latest_bitrate(), 1500000u);
|
||||
}
|
||||
|
||||
// Packets will require 5 ms to be transmitted to the receiver, causing packets
|
||||
// of the second probe to be dispersed.
|
||||
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest,
|
||||
TestProbeDetectionTooHighBitrate) {
|
||||
const int kProbeLength = 5;
|
||||
int64_t now_ms = clock_.TimeInMilliseconds();
|
||||
// First burst sent at 8 * 1000 / 10 = 800 kbps.
|
||||
for (int i = 0; i < kProbeLength; ++i) {
|
||||
clock_.AdvanceTimeMilliseconds(10);
|
||||
now_ms = clock_.TimeInMilliseconds();
|
||||
IncomingPacket(0, 1000, now_ms, 90 * now_ms, AbsSendTime(now_ms, 1000));
|
||||
}
|
||||
|
||||
// Second burst sent at 8 * 1000 / 5 = 1600 kbps.
|
||||
for (int i = 0; i < kProbeLength; ++i) {
|
||||
clock_.AdvanceTimeMilliseconds(8);
|
||||
now_ms = clock_.TimeInMilliseconds();
|
||||
IncomingPacket(0,
|
||||
1000,
|
||||
now_ms,
|
||||
90 * (now_ms - 3 * i),
|
||||
AbsSendTime(now_ms - 3 * i, 1000));
|
||||
}
|
||||
|
||||
EXPECT_EQ(0, bitrate_estimator_->Process());
|
||||
EXPECT_TRUE(bitrate_observer_->updated());
|
||||
EXPECT_GT(bitrate_observer_->latest_bitrate(), 700000u);
|
||||
EXPECT_LT(bitrate_observer_->latest_bitrate(), 900000u);
|
||||
}
|
||||
} // namespace webrtc
|
@ -8,15 +8,30 @@
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../../build/common.gypi',
|
||||
'../../../webrtc/build/common.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'rbe_components',
|
||||
'type': 'static_library',
|
||||
|
||||
'include_dirs': [
|
||||
'<(webrtc_root)/modules/remote_bitrate_estimator',
|
||||
],
|
||||
'sources': [
|
||||
'<(webrtc_root)/modules/remote_bitrate_estimator/test/bwe_test_logging.cc',
|
||||
'<(webrtc_root)/modules/remote_bitrate_estimator/test/bwe_test_logging.h',
|
||||
'aimd_rate_control.cc',
|
||||
'aimd_rate_control.h',
|
||||
'inter_arrival.cc',
|
||||
'inter_arrival.h',
|
||||
'mimd_rate_control.cc',
|
||||
'mimd_rate_control.h',
|
||||
'overuse_detector.cc',
|
||||
'overuse_detector.h',
|
||||
'overuse_estimator.cc',
|
||||
'overuse_estimator.h',
|
||||
'remote_bitrate_estimator_abs_send_time.cc',
|
||||
'remote_bitrate_estimator_single_stream.cc',
|
||||
'remote_rate_control.cc',
|
||||
'remote_rate_control.h',
|
||||
|
@ -7,12 +7,15 @@
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/base/thread_annotations.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/rate_statistics.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/inter_arrival.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/overuse_detector.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/overuse_estimator.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h"
|
||||
#include "webrtc/system_wrappers/interface/clock.h"
|
||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||
@ -21,91 +24,90 @@
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
class RemoteBitrateEstimatorSingleStream : public RemoteBitrateEstimator {
|
||||
public:
|
||||
RemoteBitrateEstimatorSingleStream(RemoteBitrateObserver* observer,
|
||||
Clock* clock,
|
||||
uint32_t min_bitrate_bps);
|
||||
virtual ~RemoteBitrateEstimatorSingleStream() {}
|
||||
|
||||
// Called for each incoming packet. If this is a new SSRC, a new
|
||||
// BitrateControl will be created. Updates the incoming payload bitrate
|
||||
// estimate and the over-use detector. If an over-use is detected the
|
||||
// remote bitrate estimate will be updated. Note that |payload_size| is the
|
||||
// packet size excluding headers.
|
||||
enum { kTimestampGroupLengthMs = 5 };
|
||||
static const double kTimestampToMs = 1.0 / 90.0;
|
||||
|
||||
class RemoteBitrateEstimatorImpl : public RemoteBitrateEstimator {
|
||||
public:
|
||||
RemoteBitrateEstimatorImpl(RemoteBitrateObserver* observer,
|
||||
Clock* clock,
|
||||
RateControlType control_type,
|
||||
uint32_t min_bitrate_bps);
|
||||
virtual ~RemoteBitrateEstimatorImpl();
|
||||
|
||||
virtual void IncomingPacket(int64_t arrival_time_ms,
|
||||
size_t payload_size,
|
||||
const RTPHeader& header) OVERRIDE;
|
||||
|
||||
// Triggers a new estimate calculation.
|
||||
// Implements the Module interface.
|
||||
virtual int32_t Process() OVERRIDE;
|
||||
virtual int32_t TimeUntilNextProcess() OVERRIDE;
|
||||
// Set the current round-trip time experienced by the stream.
|
||||
// Implements the StatsObserver interface.
|
||||
virtual void OnRttUpdate(uint32_t rtt) OVERRIDE;
|
||||
|
||||
// Removes all data for |ssrc|.
|
||||
virtual void RemoveStream(unsigned int ssrc) OVERRIDE;
|
||||
|
||||
// Returns true if a valid estimate exists and sets |bitrate_bps| to the
|
||||
// estimated payload bitrate in bits per second. |ssrcs| is the list of ssrcs
|
||||
// currently being received and of which the bitrate estimate is based upon.
|
||||
virtual bool LatestEstimate(std::vector<unsigned int>* ssrcs,
|
||||
unsigned int* bitrate_bps) const OVERRIDE;
|
||||
|
||||
virtual bool GetStats(
|
||||
ReceiveBandwidthEstimatorStats* output) const OVERRIDE;
|
||||
virtual bool GetStats(ReceiveBandwidthEstimatorStats* output) const OVERRIDE;
|
||||
|
||||
private:
|
||||
// Map from SSRC to over-use detector and last incoming packet time in
|
||||
// milliseconds, taken from clock_.
|
||||
typedef std::map<unsigned int, std::pair<OveruseDetector, int64_t> >
|
||||
SsrcOveruseDetectorMap;
|
||||
struct Detector {
|
||||
explicit Detector(int64_t last_packet_time_ms,
|
||||
const OverUseDetectorOptions& options,
|
||||
bool enable_burst_grouping)
|
||||
: last_packet_time_ms(last_packet_time_ms),
|
||||
inter_arrival(90 * kTimestampGroupLengthMs, kTimestampToMs,
|
||||
enable_burst_grouping),
|
||||
estimator(options),
|
||||
detector(options) {}
|
||||
int64_t last_packet_time_ms;
|
||||
InterArrival inter_arrival;
|
||||
OveruseEstimator estimator;
|
||||
OveruseDetector detector;
|
||||
};
|
||||
|
||||
static OveruseDetector* GetDetector(
|
||||
const SsrcOveruseDetectorMap::iterator it) {
|
||||
return &it->second.first;
|
||||
}
|
||||
|
||||
static int64_t GetPacketTimeMs(const SsrcOveruseDetectorMap::iterator it) {
|
||||
return it->second.second;
|
||||
}
|
||||
|
||||
static void SetPacketTimeMs(SsrcOveruseDetectorMap::iterator it,
|
||||
int64_t time_ms) {
|
||||
it->second.second = time_ms;
|
||||
}
|
||||
typedef std::map<unsigned int, Detector*> SsrcOveruseEstimatorMap;
|
||||
|
||||
// Triggers a new estimate calculation.
|
||||
void UpdateEstimate(int64_t now_ms);
|
||||
void UpdateEstimate(int64_t time_now)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get());
|
||||
|
||||
void GetSsrcs(std::vector<unsigned int>* ssrcs) const;
|
||||
void GetSsrcs(std::vector<unsigned int>* ssrcs) const
|
||||
SHARED_LOCKS_REQUIRED(crit_sect_.get());
|
||||
|
||||
Clock* clock_;
|
||||
SsrcOveruseDetectorMap overuse_detectors_;
|
||||
RateStatistics incoming_bitrate_;
|
||||
RemoteRateControl remote_rate_;
|
||||
RemoteBitrateObserver* observer_;
|
||||
SsrcOveruseEstimatorMap overuse_detectors_ GUARDED_BY(crit_sect_.get());
|
||||
RateStatistics incoming_bitrate_ GUARDED_BY(crit_sect_.get());
|
||||
scoped_ptr<RemoteRateControl> remote_rate_ GUARDED_BY(crit_sect_.get());
|
||||
RemoteBitrateObserver* observer_ GUARDED_BY(crit_sect_.get());
|
||||
scoped_ptr<CriticalSectionWrapper> crit_sect_;
|
||||
int64_t last_process_time_;
|
||||
int process_interval_ms_ GUARDED_BY(crit_sect_.get());
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(RemoteBitrateEstimatorImpl);
|
||||
};
|
||||
|
||||
RemoteBitrateEstimatorSingleStream::RemoteBitrateEstimatorSingleStream(
|
||||
RemoteBitrateEstimatorImpl::RemoteBitrateEstimatorImpl(
|
||||
RemoteBitrateObserver* observer,
|
||||
Clock* clock,
|
||||
RateControlType control_type,
|
||||
uint32_t min_bitrate_bps)
|
||||
: clock_(clock),
|
||||
incoming_bitrate_(500, 8000),
|
||||
remote_rate_(min_bitrate_bps),
|
||||
incoming_bitrate_(1000, 8000),
|
||||
remote_rate_(RemoteRateControl::Create(control_type, min_bitrate_bps)),
|
||||
observer_(observer),
|
||||
crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||
last_process_time_(-1) {
|
||||
last_process_time_(-1),
|
||||
process_interval_ms_(kProcessIntervalMs) {
|
||||
assert(observer_);
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorSingleStream::IncomingPacket(
|
||||
RemoteBitrateEstimatorImpl::~RemoteBitrateEstimatorImpl() {
|
||||
while (!overuse_detectors_.empty()) {
|
||||
SsrcOveruseEstimatorMap::iterator it = overuse_detectors_.begin();
|
||||
delete it->second;
|
||||
overuse_detectors_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorImpl::IncomingPacket(
|
||||
int64_t arrival_time_ms,
|
||||
size_t payload_size,
|
||||
const RTPHeader& header) {
|
||||
@ -114,7 +116,7 @@ void RemoteBitrateEstimatorSingleStream::IncomingPacket(
|
||||
header.extension.transmissionTimeOffset;
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
SsrcOveruseDetectorMap::iterator it = overuse_detectors_.find(ssrc);
|
||||
SsrcOveruseEstimatorMap::iterator it = overuse_detectors_.find(ssrc);
|
||||
if (it == overuse_detectors_.end()) {
|
||||
// This is a new SSRC. Adding to map.
|
||||
// TODO(holmer): If the channel changes SSRC the old SSRC will still be
|
||||
@ -122,20 +124,34 @@ void RemoteBitrateEstimatorSingleStream::IncomingPacket(
|
||||
// callback will no longer be called for the old SSRC. This will be
|
||||
// automatically cleaned up when we have one RemoteBitrateEstimator per REMB
|
||||
// group.
|
||||
std::pair<SsrcOveruseDetectorMap::iterator, bool> insert_result =
|
||||
overuse_detectors_.insert(std::make_pair(ssrc,
|
||||
std::make_pair(OveruseDetector(OverUseDetectorOptions()), now_ms)));
|
||||
std::pair<SsrcOveruseEstimatorMap::iterator, bool> insert_result =
|
||||
overuse_detectors_.insert(std::make_pair(ssrc, new Detector(
|
||||
now_ms,
|
||||
OverUseDetectorOptions(),
|
||||
remote_rate_->GetControlType() == kAimdControl)));
|
||||
it = insert_result.first;
|
||||
}
|
||||
SetPacketTimeMs(it, now_ms);
|
||||
OveruseDetector* overuse_detector = GetDetector(it);
|
||||
Detector* estimator = it->second;
|
||||
estimator->last_packet_time_ms = now_ms;
|
||||
incoming_bitrate_.Update(payload_size, now_ms);
|
||||
const BandwidthUsage prior_state = overuse_detector->State();
|
||||
overuse_detector->Update(payload_size, -1, rtp_timestamp, arrival_time_ms);
|
||||
if (overuse_detector->State() == kBwOverusing) {
|
||||
const BandwidthUsage prior_state = estimator->detector.State();
|
||||
uint32_t timestamp_delta = 0;
|
||||
int64_t time_delta = 0;
|
||||
int size_delta = 0;
|
||||
if (estimator->inter_arrival.ComputeDeltas(rtp_timestamp, arrival_time_ms,
|
||||
payload_size, ×tamp_delta,
|
||||
&time_delta, &size_delta)) {
|
||||
double timestamp_delta_ms = timestamp_delta * kTimestampToMs;
|
||||
estimator->estimator.Update(time_delta, timestamp_delta_ms, size_delta,
|
||||
estimator->detector.State());
|
||||
estimator->detector.Detect(estimator->estimator.offset(),
|
||||
timestamp_delta_ms,
|
||||
estimator->estimator.num_of_deltas());
|
||||
}
|
||||
if (estimator->detector.State() == kBwOverusing) {
|
||||
uint32_t incoming_bitrate = incoming_bitrate_.Rate(now_ms);
|
||||
if (prior_state != kBwOverusing ||
|
||||
remote_rate_.TimeToReduceFurther(now_ms, incoming_bitrate)) {
|
||||
remote_rate_->TimeToReduceFurther(now_ms, incoming_bitrate)) {
|
||||
// The first overuse should immediately trigger a new estimate.
|
||||
// We also have to update the estimate immediately if we are overusing
|
||||
// and the target bitrate is too high compared to what we are receiving.
|
||||
@ -144,130 +160,130 @@ void RemoteBitrateEstimatorSingleStream::IncomingPacket(
|
||||
}
|
||||
}
|
||||
|
||||
int32_t RemoteBitrateEstimatorSingleStream::Process() {
|
||||
int32_t RemoteBitrateEstimatorImpl::Process() {
|
||||
if (TimeUntilNextProcess() > 0) {
|
||||
return 0;
|
||||
}
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
UpdateEstimate(now_ms);
|
||||
last_process_time_ = now_ms;
|
||||
{
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
UpdateEstimate(clock_->TimeInMilliseconds());
|
||||
}
|
||||
last_process_time_ = clock_->TimeInMilliseconds();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t RemoteBitrateEstimatorSingleStream::TimeUntilNextProcess() {
|
||||
int32_t RemoteBitrateEstimatorImpl::TimeUntilNextProcess() {
|
||||
if (last_process_time_ < 0) {
|
||||
return 0;
|
||||
}
|
||||
return last_process_time_ + kProcessIntervalMs - clock_->TimeInMilliseconds();
|
||||
{
|
||||
CriticalSectionScoped cs_(crit_sect_.get());
|
||||
return last_process_time_ + process_interval_ms_ -
|
||||
clock_->TimeInMilliseconds();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorSingleStream::UpdateEstimate(int64_t now_ms) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
void RemoteBitrateEstimatorImpl::UpdateEstimate(int64_t now_ms) {
|
||||
BandwidthUsage bw_state = kBwNormal;
|
||||
double sum_noise_var = 0.0;
|
||||
SsrcOveruseDetectorMap::iterator it = overuse_detectors_.begin();
|
||||
double sum_var_noise = 0.0;
|
||||
SsrcOveruseEstimatorMap::iterator it = overuse_detectors_.begin();
|
||||
while (it != overuse_detectors_.end()) {
|
||||
if (GetPacketTimeMs(it) >= 0 &&
|
||||
now_ms - GetPacketTimeMs(it) > kStreamTimeOutMs) {
|
||||
const int64_t time_of_last_received_packet =
|
||||
it->second->last_packet_time_ms;
|
||||
if (time_of_last_received_packet >= 0 &&
|
||||
now_ms - time_of_last_received_packet > kStreamTimeOutMs) {
|
||||
// This over-use detector hasn't received packets for |kStreamTimeOutMs|
|
||||
// milliseconds and is considered stale.
|
||||
delete it->second;
|
||||
overuse_detectors_.erase(it++);
|
||||
} else {
|
||||
OveruseDetector* overuse_detector = GetDetector(it);
|
||||
sum_noise_var += overuse_detector->NoiseVar();
|
||||
sum_var_noise += it->second->estimator.var_noise();
|
||||
// Make sure that we trigger an over-use if any of the over-use detectors
|
||||
// is detecting over-use.
|
||||
if (overuse_detector->State() > bw_state) {
|
||||
bw_state = overuse_detector->State();
|
||||
if (it->second->detector.State() > bw_state) {
|
||||
bw_state = it->second->detector.State();
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
// We can't update the estimate if we don't have any active streams.
|
||||
if (overuse_detectors_.empty()) {
|
||||
remote_rate_.Reset();
|
||||
remote_rate_.reset(RemoteRateControl::Create(
|
||||
remote_rate_->GetControlType(), remote_rate_->GetMinBitrate()));
|
||||
return;
|
||||
}
|
||||
double mean_noise_var = sum_noise_var /
|
||||
double mean_noise_var = sum_var_noise /
|
||||
static_cast<double>(overuse_detectors_.size());
|
||||
const RateControlInput input(bw_state,
|
||||
incoming_bitrate_.Rate(now_ms),
|
||||
mean_noise_var);
|
||||
const RateControlRegion region = remote_rate_.Update(&input, now_ms);
|
||||
unsigned int target_bitrate = remote_rate_.UpdateBandwidthEstimate(now_ms);
|
||||
if (remote_rate_.ValidEstimate()) {
|
||||
const RateControlRegion region = remote_rate_->Update(&input, now_ms);
|
||||
unsigned int target_bitrate = remote_rate_->UpdateBandwidthEstimate(now_ms);
|
||||
if (remote_rate_->ValidEstimate()) {
|
||||
process_interval_ms_ = remote_rate_->GetFeedbackInterval();
|
||||
std::vector<unsigned int> ssrcs;
|
||||
GetSsrcs(&ssrcs);
|
||||
observer_->OnReceiveBitrateChanged(ssrcs, target_bitrate);
|
||||
}
|
||||
for (it = overuse_detectors_.begin(); it != overuse_detectors_.end(); ++it) {
|
||||
GetDetector(it)->SetRateControlRegion(region);
|
||||
it->second->detector.SetRateControlRegion(region);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorSingleStream::OnRttUpdate(uint32_t rtt) {
|
||||
void RemoteBitrateEstimatorImpl::OnRttUpdate(uint32_t rtt) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
remote_rate_.SetRtt(rtt);
|
||||
remote_rate_->SetRtt(rtt);
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorSingleStream::RemoveStream(unsigned int ssrc) {
|
||||
void RemoteBitrateEstimatorImpl::RemoveStream(unsigned int ssrc) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
// Ignoring the return value which is the number of elements erased.
|
||||
overuse_detectors_.erase(ssrc);
|
||||
SsrcOveruseEstimatorMap::iterator it = overuse_detectors_.find(ssrc);
|
||||
if (it != overuse_detectors_.end()) {
|
||||
delete it->second;
|
||||
overuse_detectors_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoteBitrateEstimatorSingleStream::LatestEstimate(
|
||||
bool RemoteBitrateEstimatorImpl::LatestEstimate(
|
||||
std::vector<unsigned int>* ssrcs,
|
||||
unsigned int* bitrate_bps) const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
assert(bitrate_bps);
|
||||
if (!remote_rate_.ValidEstimate()) {
|
||||
if (!remote_rate_->ValidEstimate()) {
|
||||
return false;
|
||||
}
|
||||
GetSsrcs(ssrcs);
|
||||
if (ssrcs->empty())
|
||||
*bitrate_bps = 0;
|
||||
else
|
||||
*bitrate_bps = remote_rate_.LatestEstimate();
|
||||
*bitrate_bps = remote_rate_->LatestEstimate();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteBitrateEstimatorSingleStream::GetStats(
|
||||
bool RemoteBitrateEstimatorImpl::GetStats(
|
||||
ReceiveBandwidthEstimatorStats* output) const {
|
||||
// Not implemented.
|
||||
return false;
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorSingleStream::GetSsrcs(
|
||||
void RemoteBitrateEstimatorImpl::GetSsrcs(
|
||||
std::vector<unsigned int>* ssrcs) const {
|
||||
assert(ssrcs);
|
||||
ssrcs->resize(overuse_detectors_.size());
|
||||
int i = 0;
|
||||
for (SsrcOveruseDetectorMap::const_iterator it = overuse_detectors_.begin();
|
||||
for (SsrcOveruseEstimatorMap::const_iterator it = overuse_detectors_.begin();
|
||||
it != overuse_detectors_.end(); ++it, ++i) {
|
||||
(*ssrcs)[i] = it->first;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
RemoteBitrateEstimator* RemoteBitrateEstimatorFactory::Create(
|
||||
RemoteBitrateObserver* observer,
|
||||
Clock* clock,
|
||||
webrtc::RemoteBitrateObserver* observer,
|
||||
webrtc::Clock* clock,
|
||||
RateControlType control_type,
|
||||
uint32_t min_bitrate_bps) const {
|
||||
LOG(LS_INFO) << "RemoteBitrateEstimatorFactory: Instantiating.";
|
||||
return new RemoteBitrateEstimatorSingleStream(observer, clock,
|
||||
min_bitrate_bps);
|
||||
}
|
||||
|
||||
RemoteBitrateEstimator* AbsoluteSendTimeRemoteBitrateEstimatorFactory::Create(
|
||||
RemoteBitrateObserver* observer,
|
||||
Clock* clock,
|
||||
RateControlType control_type,
|
||||
uint32_t min_bitrate_bps) const {
|
||||
LOG(LS_INFO) << "AbsoluteSendTimeRemoteBitrateEstimatorFactory: "
|
||||
"Instantiating.";
|
||||
return new RemoteBitrateEstimatorSingleStream(observer, clock,
|
||||
min_bitrate_bps);
|
||||
return new RemoteBitrateEstimatorImpl(observer, clock, control_type,
|
||||
min_bitrate_bps);
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
@ -8,14 +8,13 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RemoteBitrateEstimatorSingleTest : public RemoteBitrateEstimatorTest {
|
||||
class RemoteBitrateEstimatorSingleTest :
|
||||
public RemoteBitrateEstimatorTest {
|
||||
public:
|
||||
static const uint32_t kRemoteBitrateEstimatorMinBitrateBps = 30000;
|
||||
|
||||
@ -32,53 +31,46 @@ class RemoteBitrateEstimatorSingleTest : public RemoteBitrateEstimatorTest {
|
||||
};
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, InitialBehavior) {
|
||||
InitialBehaviorTestHelper(498075);
|
||||
InitialBehaviorTestHelper(508017);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, RateIncreaseReordering) {
|
||||
RateIncreaseReorderingTestHelper(498136);
|
||||
RateIncreaseReorderingTestHelper(506422);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, RateIncreaseRtpTimestamps) {
|
||||
RateIncreaseRtpTimestampsTestHelper(1621);
|
||||
}
|
||||
|
||||
// Verify that the time it takes for the estimator to reduce the bitrate when
|
||||
// the capacity is tightened stays the same.
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropOneStream) {
|
||||
CapacityDropTestHelper(1, false, 367);
|
||||
CapacityDropTestHelper(1, false, 733);
|
||||
}
|
||||
|
||||
// Verify that the time it takes for the estimator to reduce the bitrate when
|
||||
// the capacity is tightened stays the same. This test also verifies that we
|
||||
// handle wrap-arounds in this scenario.
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropOneStreamWrap) {
|
||||
CapacityDropTestHelper(1, true, 367);
|
||||
CapacityDropTestHelper(1, true, 733);
|
||||
}
|
||||
|
||||
// Verify that the time it takes for the estimator to reduce the bitrate when
|
||||
// the capacity is tightened stays the same. This test also verifies that we
|
||||
// handle wrap-arounds in this scenario. This is a multi-stream test.
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropTwoStreamsWrap) {
|
||||
CapacityDropTestHelper(2, true, 267);
|
||||
CapacityDropTestHelper(2, true, 700);
|
||||
}
|
||||
|
||||
// Verify that the time it takes for the estimator to reduce the bitrate when
|
||||
// the capacity is tightened stays the same. This test also verifies that we
|
||||
// handle wrap-arounds in this scenario. This is a multi-stream test.
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThreeStreamsWrap) {
|
||||
CapacityDropTestHelper(3, true, 333);
|
||||
CapacityDropTestHelper(3, true, 733);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThirteenStreamsWrap) {
|
||||
CapacityDropTestHelper(13, true, 300);
|
||||
CapacityDropTestHelper(13, true, 733);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropNineteenStreamsWrap) {
|
||||
CapacityDropTestHelper(19, true, 300);
|
||||
CapacityDropTestHelper(19, true, 733);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThirtyStreamsWrap) {
|
||||
CapacityDropTestHelper(30, true, 300);
|
||||
CapacityDropTestHelper(30, true, 733);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, TestTimestampGrouping) {
|
||||
TestTimestampGroupingTestHelper();
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
@ -512,4 +512,155 @@ void RemoteBitrateEstimatorTest::CapacityDropTestHelper(
|
||||
EXPECT_EQ(0u, ssrcs.size());
|
||||
EXPECT_EQ(0u, latest_bps);
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorTest::TestTimestampGroupingTestHelper() {
|
||||
const int kFramerate = 50; // 50 fps to avoid rounding errors.
|
||||
const int kFrameIntervalMs = 1000 / kFramerate;
|
||||
const uint32_t kFrameIntervalAbsSendTime = AbsSendTime(1, kFramerate);
|
||||
uint32_t timestamp = 0;
|
||||
// Initialize absolute_send_time (24 bits) so that it will definitely wrap
|
||||
// during the test.
|
||||
uint32_t absolute_send_time =
|
||||
AddAbsSendTime((1 << 24), -int(50 * kFrameIntervalAbsSendTime));
|
||||
// Initial set of frames to increase the bitrate.
|
||||
for (int i = 0; i <= 100; ++i) {
|
||||
IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp,
|
||||
absolute_send_time);
|
||||
bitrate_estimator_->Process();
|
||||
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
|
||||
timestamp += 90 * kFrameIntervalMs;
|
||||
absolute_send_time = AddAbsSendTime(absolute_send_time,
|
||||
kFrameIntervalAbsSendTime);
|
||||
}
|
||||
EXPECT_TRUE(bitrate_observer_->updated());
|
||||
EXPECT_NEAR(450000u, bitrate_observer_->latest_bitrate(), 20000u);
|
||||
|
||||
// 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;
|
||||
const uint32_t kTimestampGroupLengthAbsSendTime =
|
||||
AbsSendTime(kTimestampGroupLength, 90000);
|
||||
const uint32_t kSingleRtpTickAbsSendTime = AbsSendTime(1, 90000);
|
||||
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.
|
||||
IncomingPacket(kDefaultSsrc, 100, clock_.TimeInMilliseconds(), timestamp,
|
||||
absolute_send_time);
|
||||
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs / kTimestampGroupLength);
|
||||
timestamp += 1;
|
||||
absolute_send_time = AddAbsSendTime(absolute_send_time,
|
||||
kSingleRtpTickAbsSendTime);
|
||||
}
|
||||
// Increase time until next batch to simulate over-use.
|
||||
clock_.AdvanceTimeMilliseconds(10);
|
||||
timestamp += 90 * kFrameIntervalMs - kTimestampGroupLength;
|
||||
absolute_send_time = AddAbsSendTime(absolute_send_time, AddAbsSendTime(
|
||||
kFrameIntervalAbsSendTime, -int(kTimestampGroupLengthAbsSendTime)));
|
||||
bitrate_estimator_->Process();
|
||||
}
|
||||
EXPECT_TRUE(bitrate_observer_->updated());
|
||||
// Should have reduced the estimate.
|
||||
EXPECT_EQ(378720u, bitrate_observer_->latest_bitrate());
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorTest::TestGetStatsHelper() {
|
||||
const int kFramerate = 100;
|
||||
const int kFrameIntervalMs = 1000 / kFramerate;
|
||||
const int kBurstThresholdMs = 5;
|
||||
const uint32_t kFrameIntervalAbsSendTime = AbsSendTime(1, kFramerate);
|
||||
uint32_t timestamp = 0;
|
||||
// Initialize absolute_send_time (24 bits) so that it will definitely wrap
|
||||
// during the test.
|
||||
uint32_t absolute_send_time =
|
||||
AddAbsSendTime((1 << 24),
|
||||
-(50 * static_cast<int>(kFrameIntervalAbsSendTime)));
|
||||
|
||||
// Inject propagation_time_delta of kFrameIntervalMs.
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp,
|
||||
absolute_send_time);
|
||||
timestamp += kFrameIntervalMs;
|
||||
// Insert a kFrameIntervalMs propagation_time_delta.
|
||||
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs * 2);
|
||||
absolute_send_time = AddAbsSendTime(absolute_send_time,
|
||||
kFrameIntervalAbsSendTime);
|
||||
}
|
||||
ReceiveBandwidthEstimatorStats stats;
|
||||
EXPECT_TRUE(bitrate_estimator_->GetStats(&stats));
|
||||
EXPECT_EQ(1U, stats.recent_propagation_time_delta_ms.size());
|
||||
EXPECT_EQ(kFrameIntervalMs, stats.recent_propagation_time_delta_ms[0]);
|
||||
EXPECT_EQ(1U, stats.recent_arrival_time_ms.size());
|
||||
EXPECT_EQ(kFrameIntervalMs, stats.total_propagation_time_delta_ms);
|
||||
|
||||
// Inject negative propagation_time_deltas. The total propagation_time_delta
|
||||
// should be adjusted to 0.
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp,
|
||||
absolute_send_time);
|
||||
timestamp += 10 * kFrameIntervalMs;
|
||||
clock_.AdvanceTimeMilliseconds(kBurstThresholdMs + 1);
|
||||
absolute_send_time = AddAbsSendTime(absolute_send_time,
|
||||
10 * kFrameIntervalAbsSendTime);
|
||||
}
|
||||
EXPECT_TRUE(bitrate_estimator_->GetStats(&stats));
|
||||
EXPECT_EQ(0, stats.total_propagation_time_delta_ms);
|
||||
|
||||
// Send more than 1000 frames and make sure the stats queues stays within
|
||||
// limits.
|
||||
for (size_t i = 0; i < 1001; ++i) {
|
||||
IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp,
|
||||
absolute_send_time);
|
||||
timestamp += kFrameIntervalMs;
|
||||
absolute_send_time = AddAbsSendTime(absolute_send_time,
|
||||
kFrameIntervalAbsSendTime);
|
||||
}
|
||||
EXPECT_TRUE(bitrate_estimator_->GetStats(&stats));
|
||||
EXPECT_LE(stats.recent_propagation_time_delta_ms.size(), 1000U);
|
||||
EXPECT_LE(stats.recent_arrival_time_ms.size(), 1000U);
|
||||
|
||||
// Move the clock over the 1000ms limit.
|
||||
clock_.AdvanceTimeMilliseconds(2000);
|
||||
EXPECT_TRUE(bitrate_estimator_->GetStats(&stats));
|
||||
EXPECT_EQ(0U, stats.recent_propagation_time_delta_ms.size());
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorTest::TestWrappingHelper(
|
||||
int silence_time_s) {
|
||||
const int kFramerate = 100;
|
||||
const int kFrameIntervalMs = 1000 / kFramerate;
|
||||
const uint32_t kFrameIntervalAbsSendTime = AbsSendTime(1, kFramerate);
|
||||
uint32_t absolute_send_time = 0;
|
||||
uint32_t timestamp = 0;
|
||||
|
||||
for (size_t i = 0; i < 3000; ++i) {
|
||||
IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp,
|
||||
absolute_send_time);
|
||||
timestamp += kFrameIntervalMs;
|
||||
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
|
||||
absolute_send_time = AddAbsSendTime(absolute_send_time,
|
||||
kFrameIntervalAbsSendTime);
|
||||
bitrate_estimator_->Process();
|
||||
}
|
||||
unsigned int bitrate_before = 0;
|
||||
std::vector<unsigned int> ssrcs;
|
||||
bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_before);
|
||||
|
||||
clock_.AdvanceTimeMilliseconds(silence_time_s * 1000);
|
||||
absolute_send_time = AddAbsSendTime(absolute_send_time,
|
||||
AbsSendTime(silence_time_s, 1));
|
||||
bitrate_estimator_->Process();
|
||||
for (size_t i = 0; i < 100; ++i) {
|
||||
IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp,
|
||||
absolute_send_time);
|
||||
timestamp += kFrameIntervalMs;
|
||||
clock_.AdvanceTimeMilliseconds(2 * kFrameIntervalMs);
|
||||
absolute_send_time = AddAbsSendTime(absolute_send_time,
|
||||
kFrameIntervalAbsSendTime);
|
||||
bitrate_estimator_->Process();
|
||||
}
|
||||
unsigned int bitrate_after = 0;
|
||||
bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_after);
|
||||
EXPECT_LT(bitrate_after, bitrate_before);
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
@ -189,6 +189,13 @@ class RemoteBitrateEstimatorTest : public ::testing::Test {
|
||||
unsigned int max_bitrate,
|
||||
unsigned int target_bitrate);
|
||||
|
||||
|
||||
void TestTimestampGroupingTestHelper();
|
||||
|
||||
void TestGetStatsHelper();
|
||||
|
||||
void TestWrappingHelper(int silence_time_s);
|
||||
|
||||
void InitialBehaviorTestHelper(unsigned int expected_converge_bitrate);
|
||||
void RateIncreaseReorderingTestHelper(unsigned int expected_bitrate);
|
||||
void RateIncreaseRtpTimestampsTestHelper(int expected_iterations);
|
||||
|
@ -10,318 +10,16 @@
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "webrtc/system_wrappers/interface/trace.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/aimd_rate_control.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/mimd_rate_control.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
const unsigned int kDefaultRttMs = 200;
|
||||
|
||||
RemoteRateControl::RemoteRateControl(uint32_t min_bitrate_bps)
|
||||
: min_configured_bit_rate_(min_bitrate_bps),
|
||||
max_configured_bit_rate_(30000000),
|
||||
current_bit_rate_(max_configured_bit_rate_),
|
||||
max_hold_rate_(0),
|
||||
avg_max_bit_rate_(-1.0f),
|
||||
var_max_bit_rate_(0.4f),
|
||||
rate_control_state_(kRcHold),
|
||||
came_from_state_(kRcDecrease),
|
||||
rate_control_region_(kRcMaxUnknown),
|
||||
last_bit_rate_change_(-1),
|
||||
current_input_(kBwNormal, 0, 1.0),
|
||||
updated_(false),
|
||||
time_first_incoming_estimate_(-1),
|
||||
initialized_bit_rate_(false),
|
||||
avg_change_period_(1000.0f),
|
||||
last_change_ms_(-1),
|
||||
beta_(0.9f),
|
||||
rtt_(kDefaultRttMs)
|
||||
{
|
||||
}
|
||||
|
||||
void RemoteRateControl::Reset() {
|
||||
*this = RemoteRateControl(min_configured_bit_rate_);
|
||||
came_from_state_ = kRcHold;
|
||||
}
|
||||
|
||||
bool RemoteRateControl::ValidEstimate() const {
|
||||
return initialized_bit_rate_;
|
||||
}
|
||||
|
||||
bool RemoteRateControl::TimeToReduceFurther(int64_t time_now,
|
||||
unsigned int incoming_bitrate) const {
|
||||
const int bitrate_reduction_interval = std::max(std::min(rtt_, 200u), 10u);
|
||||
if (time_now - last_bit_rate_change_ >= bitrate_reduction_interval) {
|
||||
return true;
|
||||
}
|
||||
if (ValidEstimate()) {
|
||||
const int threshold = static_cast<int>(1.05 * incoming_bitrate);
|
||||
const int bitrate_difference = LatestEstimate() - incoming_bitrate;
|
||||
return bitrate_difference > threshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t RemoteRateControl::SetConfiguredBitRates(uint32_t min_bit_rate_bps,
|
||||
uint32_t max_bit_rate_bps) {
|
||||
if (min_bit_rate_bps > max_bit_rate_bps) {
|
||||
return -1;
|
||||
}
|
||||
min_configured_bit_rate_ = min_bit_rate_bps;
|
||||
max_configured_bit_rate_ = max_bit_rate_bps;
|
||||
current_bit_rate_ = std::min(std::max(min_bit_rate_bps, current_bit_rate_),
|
||||
max_bit_rate_bps);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t RemoteRateControl::LatestEstimate() const {
|
||||
return current_bit_rate_;
|
||||
}
|
||||
|
||||
uint32_t RemoteRateControl::UpdateBandwidthEstimate(int64_t now_ms) {
|
||||
current_bit_rate_ = ChangeBitRate(current_bit_rate_,
|
||||
current_input_._incomingBitRate,
|
||||
current_input_._noiseVar,
|
||||
now_ms);
|
||||
return current_bit_rate_;
|
||||
}
|
||||
|
||||
void RemoteRateControl::SetRtt(unsigned int rtt) {
|
||||
rtt_ = rtt;
|
||||
}
|
||||
|
||||
RateControlRegion RemoteRateControl::Update(const RateControlInput* input,
|
||||
int64_t now_ms) {
|
||||
assert(input);
|
||||
|
||||
// Set the initial bit rate value to what we're receiving the first half
|
||||
// second.
|
||||
if (!initialized_bit_rate_) {
|
||||
if (time_first_incoming_estimate_ < 0) {
|
||||
if (input->_incomingBitRate > 0) {
|
||||
time_first_incoming_estimate_ = now_ms;
|
||||
}
|
||||
} else if (now_ms - time_first_incoming_estimate_ > 500 &&
|
||||
input->_incomingBitRate > 0) {
|
||||
current_bit_rate_ = input->_incomingBitRate;
|
||||
initialized_bit_rate_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updated_ && current_input_._bwState == kBwOverusing) {
|
||||
// Only update delay factor and incoming bit rate. We always want to react
|
||||
// on an over-use.
|
||||
current_input_._noiseVar = input->_noiseVar;
|
||||
current_input_._incomingBitRate = input->_incomingBitRate;
|
||||
return rate_control_region_;
|
||||
}
|
||||
updated_ = true;
|
||||
current_input_ = *input;
|
||||
return rate_control_region_;
|
||||
}
|
||||
|
||||
uint32_t RemoteRateControl::ChangeBitRate(uint32_t current_bit_rate,
|
||||
uint32_t incoming_bit_rate,
|
||||
double noise_var,
|
||||
int64_t now_ms) {
|
||||
if (!updated_) {
|
||||
return current_bit_rate_;
|
||||
}
|
||||
updated_ = false;
|
||||
UpdateChangePeriod(now_ms);
|
||||
ChangeState(current_input_, now_ms);
|
||||
// calculated here because it's used in multiple places
|
||||
const float incoming_bit_rate_kbps = incoming_bit_rate / 1000.0f;
|
||||
// Calculate the max bit rate std dev given the normalized
|
||||
// variance and the current incoming bit rate.
|
||||
const float std_max_bit_rate = sqrt(var_max_bit_rate_ * avg_max_bit_rate_);
|
||||
bool recovery = false;
|
||||
switch (rate_control_state_) {
|
||||
case kRcHold: {
|
||||
max_hold_rate_ = std::max(max_hold_rate_, incoming_bit_rate);
|
||||
break;
|
||||
}
|
||||
case kRcIncrease: {
|
||||
if (avg_max_bit_rate_ >= 0) {
|
||||
if (incoming_bit_rate_kbps > avg_max_bit_rate_ + 3 * std_max_bit_rate) {
|
||||
ChangeRegion(kRcMaxUnknown);
|
||||
avg_max_bit_rate_ = -1.0;
|
||||
} else if (incoming_bit_rate_kbps > avg_max_bit_rate_ + 2.5 *
|
||||
std_max_bit_rate) {
|
||||
ChangeRegion(kRcAboveMax);
|
||||
}
|
||||
}
|
||||
const uint32_t response_time = static_cast<uint32_t>(avg_change_period_ +
|
||||
0.5f) + rtt_ + 300;
|
||||
double alpha = RateIncreaseFactor(now_ms, last_bit_rate_change_,
|
||||
response_time, noise_var);
|
||||
|
||||
current_bit_rate = static_cast<uint32_t>(current_bit_rate * alpha) + 1000;
|
||||
if (max_hold_rate_ > 0 && beta_ * max_hold_rate_ > current_bit_rate) {
|
||||
current_bit_rate = static_cast<uint32_t>(beta_ * max_hold_rate_);
|
||||
avg_max_bit_rate_ = beta_ * max_hold_rate_ / 1000.0f;
|
||||
ChangeRegion(kRcNearMax);
|
||||
recovery = true;
|
||||
}
|
||||
max_hold_rate_ = 0;
|
||||
last_bit_rate_change_ = now_ms;
|
||||
break;
|
||||
}
|
||||
case kRcDecrease: {
|
||||
if (incoming_bit_rate < min_configured_bit_rate_) {
|
||||
current_bit_rate = min_configured_bit_rate_;
|
||||
} else {
|
||||
// Set bit rate to something slightly lower than max
|
||||
// to get rid of any self-induced delay.
|
||||
current_bit_rate = static_cast<uint32_t>(beta_ * incoming_bit_rate +
|
||||
0.5);
|
||||
if (current_bit_rate > current_bit_rate_) {
|
||||
// Avoid increasing the rate when over-using.
|
||||
if (rate_control_region_ != kRcMaxUnknown) {
|
||||
current_bit_rate = static_cast<uint32_t>(beta_ * avg_max_bit_rate_ *
|
||||
1000 + 0.5f);
|
||||
}
|
||||
current_bit_rate = std::min(current_bit_rate, current_bit_rate_);
|
||||
}
|
||||
ChangeRegion(kRcNearMax);
|
||||
|
||||
if (incoming_bit_rate_kbps < avg_max_bit_rate_ - 3 * std_max_bit_rate) {
|
||||
avg_max_bit_rate_ = -1.0f;
|
||||
}
|
||||
|
||||
UpdateMaxBitRateEstimate(incoming_bit_rate_kbps);
|
||||
}
|
||||
// Stay on hold until the pipes are cleared.
|
||||
ChangeState(kRcHold);
|
||||
last_bit_rate_change_ = now_ms;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
if (!recovery && (incoming_bit_rate > 100000 || current_bit_rate > 150000) &&
|
||||
current_bit_rate > 1.5 * incoming_bit_rate) {
|
||||
// Allow changing the bit rate if we are operating at very low rates
|
||||
// Don't change the bit rate if the send side is too far off
|
||||
current_bit_rate = current_bit_rate_;
|
||||
last_bit_rate_change_ = now_ms;
|
||||
}
|
||||
return current_bit_rate;
|
||||
}
|
||||
|
||||
double RemoteRateControl::RateIncreaseFactor(int64_t now_ms,
|
||||
int64_t last_ms,
|
||||
uint32_t reaction_time_ms,
|
||||
double noise_var) const {
|
||||
// alpha = 1.02 + B ./ (1 + exp(b*(tr - (c1*s2 + c2))))
|
||||
// Parameters
|
||||
const double B = 0.0407;
|
||||
const double b = 0.0025;
|
||||
const double c1 = -6700.0 / (33 * 33);
|
||||
const double c2 = 800.0;
|
||||
const double d = 0.85;
|
||||
|
||||
double alpha = 1.005 + B / (1 + exp( b * (d * reaction_time_ms -
|
||||
(c1 * noise_var + c2))));
|
||||
|
||||
if (alpha < 1.005) {
|
||||
alpha = 1.005;
|
||||
} else if (alpha > 1.3) {
|
||||
alpha = 1.3;
|
||||
}
|
||||
|
||||
if (last_ms > -1) {
|
||||
alpha = pow(alpha, (now_ms - last_ms) / 1000.0);
|
||||
}
|
||||
|
||||
if (rate_control_region_ == kRcNearMax) {
|
||||
// We're close to our previous maximum. Try to stabilize the
|
||||
// bit rate in this region, by increasing in smaller steps.
|
||||
alpha = alpha - (alpha - 1.0) / 2.0;
|
||||
} else if (rate_control_region_ == kRcMaxUnknown) {
|
||||
alpha = alpha + (alpha - 1.0) * 2.0;
|
||||
}
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
void RemoteRateControl::UpdateChangePeriod(int64_t now_ms) {
|
||||
int64_t change_period = 0;
|
||||
if (last_change_ms_ > -1) {
|
||||
change_period = now_ms - last_change_ms_;
|
||||
}
|
||||
last_change_ms_ = now_ms;
|
||||
avg_change_period_ = 0.9f * avg_change_period_ + 0.1f * change_period;
|
||||
}
|
||||
|
||||
void RemoteRateControl::UpdateMaxBitRateEstimate(float incoming_bit_rate_kbps) {
|
||||
const float alpha = 0.05f;
|
||||
if (avg_max_bit_rate_ == -1.0f) {
|
||||
avg_max_bit_rate_ = incoming_bit_rate_kbps;
|
||||
RemoteRateControl* RemoteRateControl::Create(RateControlType control_type,
|
||||
uint32_t min_bitrate_bps) {
|
||||
if (control_type == kAimdControl) {
|
||||
return new AimdRateControl(min_bitrate_bps);
|
||||
} else {
|
||||
avg_max_bit_rate_ = (1 - alpha) * avg_max_bit_rate_ +
|
||||
alpha * incoming_bit_rate_kbps;
|
||||
return new MimdRateControl(min_bitrate_bps);
|
||||
}
|
||||
// Estimate the max bit rate variance and normalize the variance
|
||||
// with the average max bit rate.
|
||||
const float norm = std::max(avg_max_bit_rate_, 1.0f);
|
||||
var_max_bit_rate_ = (1 - alpha) * var_max_bit_rate_ +
|
||||
alpha * (avg_max_bit_rate_ - incoming_bit_rate_kbps) *
|
||||
(avg_max_bit_rate_ - incoming_bit_rate_kbps) / norm;
|
||||
// 0.4 ~= 14 kbit/s at 500 kbit/s
|
||||
if (var_max_bit_rate_ < 0.4f) {
|
||||
var_max_bit_rate_ = 0.4f;
|
||||
}
|
||||
// 2.5f ~= 35 kbit/s at 500 kbit/s
|
||||
if (var_max_bit_rate_ > 2.5f) {
|
||||
var_max_bit_rate_ = 2.5f;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteRateControl::ChangeState(const RateControlInput& input,
|
||||
int64_t now_ms) {
|
||||
switch (current_input_._bwState) {
|
||||
case kBwNormal:
|
||||
if (rate_control_state_ == kRcHold) {
|
||||
last_bit_rate_change_ = now_ms;
|
||||
ChangeState(kRcIncrease);
|
||||
}
|
||||
break;
|
||||
case kBwOverusing:
|
||||
if (rate_control_state_ != kRcDecrease) {
|
||||
ChangeState(kRcDecrease);
|
||||
}
|
||||
break;
|
||||
case kBwUnderusing:
|
||||
ChangeState(kRcHold);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteRateControl::ChangeRegion(RateControlRegion region) {
|
||||
rate_control_region_ = region;
|
||||
switch (rate_control_region_) {
|
||||
case kRcAboveMax:
|
||||
case kRcMaxUnknown:
|
||||
beta_ = 0.9f;
|
||||
break;
|
||||
case kRcNearMax:
|
||||
beta_ = 0.95f;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteRateControl::ChangeState(RateControlState new_state) {
|
||||
came_from_state_ = rate_control_state_;
|
||||
rate_control_state_ = new_state;
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
@ -8,71 +8,44 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_REMOTE_RATE_CONTROL_H_
|
||||
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_REMOTE_RATE_CONTROL_H_
|
||||
#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_RATE_CONTROL_H_
|
||||
#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_RATE_CONTROL_H_
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RemoteRateControl {
|
||||
public:
|
||||
explicit RemoteRateControl(uint32_t min_bitrate_bps);
|
||||
~RemoteRateControl() {}
|
||||
static RemoteRateControl* Create(RateControlType control_type,
|
||||
uint32_t min_bitrate_bps);
|
||||
|
||||
void Reset();
|
||||
virtual ~RemoteRateControl() {}
|
||||
|
||||
// Returns true if there is a valid estimate of the incoming bitrate, false
|
||||
// otherwise.
|
||||
bool ValidEstimate() const;
|
||||
virtual bool ValidEstimate() const = 0;
|
||||
virtual RateControlType GetControlType() const = 0;
|
||||
virtual uint32_t GetMinBitrate() const = 0;
|
||||
virtual int GetFeedbackInterval() const = 0;
|
||||
|
||||
// Returns true if the bitrate estimate hasn't been changed for more than
|
||||
// an RTT, or if the incoming_bitrate is more than 5% above the current
|
||||
// estimate. Should be used to decide if we should reduce the rate further
|
||||
// when over-using.
|
||||
bool TimeToReduceFurther(int64_t time_now,
|
||||
unsigned int incoming_bitrate) const;
|
||||
virtual bool TimeToReduceFurther(int64_t time_now,
|
||||
uint32_t incoming_bitrate_bps) const = 0;
|
||||
virtual uint32_t LatestEstimate() const = 0;
|
||||
virtual uint32_t UpdateBandwidthEstimate(int64_t now_ms) = 0;
|
||||
virtual void SetRtt(unsigned int rtt) = 0;
|
||||
virtual RateControlRegion Update(const RateControlInput* input,
|
||||
int64_t now_ms) = 0;
|
||||
virtual void SetEstimate(int bitrate_bps, int64_t time_now_ms) = 0;
|
||||
|
||||
int32_t SetConfiguredBitRates(uint32_t min_bit_rate, uint32_t max_bit_rate);
|
||||
uint32_t LatestEstimate() const;
|
||||
uint32_t UpdateBandwidthEstimate(int64_t now_ms);
|
||||
void SetRtt(unsigned int rtt);
|
||||
RateControlRegion Update(const RateControlInput* input, int64_t now_ms);
|
||||
|
||||
private:
|
||||
uint32_t ChangeBitRate(uint32_t current_bit_rate,
|
||||
uint32_t incoming_bit_rate,
|
||||
double delay_factor,
|
||||
int64_t now_ms);
|
||||
double RateIncreaseFactor(int64_t now_ms,
|
||||
int64_t last_ms,
|
||||
uint32_t reaction_time_ms,
|
||||
double noise_var) const;
|
||||
void UpdateChangePeriod(int64_t now_ms);
|
||||
void UpdateMaxBitRateEstimate(float incoming_bit_rate_kbps);
|
||||
void ChangeState(const RateControlInput& input, int64_t now_ms);
|
||||
void ChangeState(RateControlState new_state);
|
||||
void ChangeRegion(RateControlRegion region);
|
||||
|
||||
uint32_t min_configured_bit_rate_;
|
||||
uint32_t max_configured_bit_rate_;
|
||||
uint32_t current_bit_rate_;
|
||||
uint32_t max_hold_rate_;
|
||||
float avg_max_bit_rate_;
|
||||
float var_max_bit_rate_;
|
||||
RateControlState rate_control_state_;
|
||||
RateControlState came_from_state_;
|
||||
RateControlRegion rate_control_region_;
|
||||
int64_t last_bit_rate_change_;
|
||||
RateControlInput current_input_;
|
||||
bool updated_;
|
||||
int64_t time_first_incoming_estimate_;
|
||||
bool initialized_bit_rate_;
|
||||
float avg_change_period_;
|
||||
int64_t last_change_ms_;
|
||||
float beta_;
|
||||
unsigned int rtt_;
|
||||
protected:
|
||||
static const int kMaxFeedbackIntervalMs = 1000;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_REMOTE_RATE_CONTROL_H_
|
||||
#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_RATE_CONTROL_H_
|
||||
|
Loading…
Reference in New Issue
Block a user