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:
pbos@webrtc.org 2014-12-04 15:34:06 +00:00
parent 33ccdfa1f5
commit 9f79fe684a
25 changed files with 3381 additions and 808 deletions

View File

@ -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

View File

@ -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',

View File

@ -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",

View 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

View 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_

View 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

View 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_

View File

@ -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

View 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

View 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_

View File

@ -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_;

View File

@ -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_

View File

@ -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,
&timestamp_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

View 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

View 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_

View File

@ -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',

View File

@ -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, &current);
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, &current);
}
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

View File

@ -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

View File

@ -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',

View File

@ -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, &timestamp_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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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_