Merge commit 'upstream-main' into master

Bug: 261600888
Test: none, build files to be updated in follow up cl
Change-Id: Ib520938290c6bbdee4a9f73b6419b6c947a96ec4
This commit is contained in:
Jorge E. Moreira
2022-12-06 16:34:41 -08:00
5393 changed files with 541103 additions and 211666 deletions

View File

@ -9,7 +9,6 @@
import("../../webrtc.gni")
rtc_library("remote_bitrate_estimator") {
visibility = [ "*" ]
sources = [
"aimd_rate_control.cc",
"aimd_rate_control.h",
@ -22,6 +21,8 @@ rtc_library("remote_bitrate_estimator") {
"overuse_detector.h",
"overuse_estimator.cc",
"overuse_estimator.h",
"packet_arrival_map.cc",
"packet_arrival_map.h",
"remote_bitrate_estimator_abs_send_time.cc",
"remote_bitrate_estimator_abs_send_time.h",
"remote_bitrate_estimator_single_stream.cc",
@ -40,21 +41,28 @@ rtc_library("remote_bitrate_estimator") {
}
deps = [
"../../api:field_trials_view",
"../../api:network_state_predictor_api",
"../../api:rtp_headers",
"../../api/transport:field_trial_based_config",
"../../api/transport:network_control",
"../../api/transport:webrtc_key_value_config",
"../../api/units:data_rate",
"../../api/units:data_size",
"../../api/units:time_delta",
"../../api/units:timestamp",
"../../modules:module_api",
"../../modules:module_api_public",
"../../modules/congestion_controller/goog_cc:link_capacity_estimator",
"../../modules/rtp_rtcp:rtp_rtcp_format",
"../../rtc_base:checks",
"../../rtc_base:rtc_base_approved",
"../../rtc_base:logging",
"../../rtc_base:macromagic",
"../../rtc_base:platform_thread",
"../../rtc_base:race_checker",
"../../rtc_base:rate_statistics",
"../../rtc_base:rtc_numerics",
"../../rtc_base:safe_minmax",
"../../rtc_base:stringutils",
"../../rtc_base/experiments:field_trial_parser",
"../../rtc_base/synchronization:mutex",
"../../system_wrappers",
@ -75,10 +83,8 @@ if (!build_with_chromium) {
"tools/bwe_rtp.h",
]
deps = [
":remote_bitrate_estimator",
"../../rtc_base:rtc_base_approved",
"../../test:rtp_test_utils",
"../rtp_rtcp",
"../rtp_rtcp:rtp_rtcp_format",
]
absl_deps = [
"//third_party/abseil-cpp/absl/flags:flag",
@ -91,10 +97,10 @@ if (!build_with_chromium) {
sources = [ "tools/rtp_to_text.cc" ]
deps = [
":bwe_rtp",
"../../modules/rtp_rtcp",
"../../rtc_base:macromagic",
"../../rtc_base:stringutils",
"../../test:rtp_test_utils",
"../rtp_rtcp:rtp_rtcp_format",
]
}
}
@ -107,6 +113,7 @@ if (rtc_include_tests) {
"aimd_rate_control_unittest.cc",
"inter_arrival_unittest.cc",
"overuse_detector_unittest.cc",
"packet_arrival_map_test.cc",
"remote_bitrate_estimator_abs_send_time_unittest.cc",
"remote_bitrate_estimator_single_stream_unittest.cc",
"remote_bitrate_estimator_unittest_helper.cc",
@ -116,13 +123,16 @@ if (rtc_include_tests) {
deps = [
":remote_bitrate_estimator",
"..:module_api_public",
"../..:webrtc_common",
"../../api/transport:field_trial_based_config",
"../../api/transport:mock_network_control",
"../../api/transport:network_control",
"../../api/units:data_rate",
"../../api/units:data_size",
"../../api/units:time_delta",
"../../api/units:timestamp",
"../../rtc_base",
"../../rtc_base:checks",
"../../rtc_base:rtc_base_approved",
"../../rtc_base:random",
"../../system_wrappers",
"../../test:field_trial",
"../../test:fileutils",

View File

@ -1,6 +1,6 @@
include_rules = [
"+logging/rtc_event_log",
"+system_wrappers",
# Avoid directly using field_trial. Instead use WebRtcKeyValueConfig.
# Avoid directly using field_trial. Instead use FieldTrialsView.
"-system_wrappers/include/field_trial.h",
]

View File

@ -1,6 +1,5 @@
danilchap@webrtc.org
stefan@webrtc.org
terelius@webrtc.org
asapersson@webrtc.org
mflodman@webrtc.org
philipel@webrtc.org
srte@webrtc.org
perkj@webrtc.org

View File

@ -13,7 +13,6 @@
#include <inttypes.h>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <string>
@ -36,17 +35,15 @@ constexpr double kDefaultBackoffFactor = 0.85;
constexpr char kBweBackOffFactorExperiment[] = "WebRTC-BweBackOffFactor";
bool IsEnabled(const WebRtcKeyValueConfig& field_trials,
absl::string_view key) {
bool IsEnabled(const FieldTrialsView& field_trials, absl::string_view key) {
return absl::StartsWith(field_trials.Lookup(key), "Enabled");
}
bool IsNotDisabled(const WebRtcKeyValueConfig& field_trials,
absl::string_view key) {
bool IsNotDisabled(const FieldTrialsView& field_trials, absl::string_view key) {
return !absl::StartsWith(field_trials.Lookup(key), "Disabled");
}
double ReadBackoffFactor(const WebRtcKeyValueConfig& key_value_config) {
double ReadBackoffFactor(const FieldTrialsView& key_value_config) {
std::string experiment_string =
key_value_config.Lookup(kBweBackOffFactorExperiment);
double backoff_factor;
@ -54,9 +51,9 @@ double ReadBackoffFactor(const WebRtcKeyValueConfig& key_value_config) {
sscanf(experiment_string.c_str(), "Enabled-%lf", &backoff_factor);
if (parsed_values == 1) {
if (backoff_factor >= 1.0) {
RTC_LOG(WARNING) << "Back-off factor must be less than 1.";
RTC_LOG(LS_WARNING) << "Back-off factor must be less than 1.";
} else if (backoff_factor <= 0.0) {
RTC_LOG(WARNING) << "Back-off factor must be greater than 0.";
RTC_LOG(LS_WARNING) << "Back-off factor must be greater than 0.";
} else {
return backoff_factor;
}
@ -68,17 +65,17 @@ double ReadBackoffFactor(const WebRtcKeyValueConfig& key_value_config) {
} // namespace
AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config)
AimdRateControl::AimdRateControl(const FieldTrialsView* key_value_config)
: AimdRateControl(key_value_config, /* send_side =*/false) {}
AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config,
AimdRateControl::AimdRateControl(const FieldTrialsView* key_value_config,
bool send_side)
: min_configured_bitrate_(congestion_controller::GetMinBitrate()),
: min_configured_bitrate_(kCongestionControllerMinBitrate),
max_configured_bitrate_(DataRate::KilobitsPerSec(30000)),
current_bitrate_(max_configured_bitrate_),
latest_estimated_throughput_(current_bitrate_),
link_capacity_(),
rate_control_state_(kRcHold),
rate_control_state_(RateControlState::kRcHold),
time_last_bitrate_change_(Timestamp::MinusInfinity()),
time_last_bitrate_decrease_(Timestamp::MinusInfinity()),
time_first_throughput_estimate_(Timestamp::MinusInfinity()),
@ -89,18 +86,19 @@ AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config,
in_alr_(false),
rtt_(kDefaultRtt),
send_side_(send_side),
in_experiment_(!AdaptiveThresholdExperimentIsDisabled(*key_value_config)),
no_bitrate_increase_in_alr_(
IsEnabled(*key_value_config,
"WebRTC-DontIncreaseDelayBasedBweInAlr")),
estimate_bounded_backoff_(
IsNotDisabled(*key_value_config,
"WebRTC-Bwe-EstimateBoundedBackoff")),
estimate_bounded_increase_(
IsNotDisabled(*key_value_config,
"WebRTC-Bwe-EstimateBoundedIncrease")),
initial_backoff_interval_("initial_backoff_interval"),
link_capacity_fix_("link_capacity_fix") {
ParseFieldTrial(
{&disable_estimate_bounded_increase_, &estimate_bounded_increase_ratio_,
&ignore_throughput_limit_if_network_estimate_,
&ignore_network_estimate_decrease_, &increase_to_network_estimate_},
key_value_config->Lookup("WebRTC-Bwe-EstimateBoundedIncrease"));
// E.g
// WebRTC-BweAimdRateControlConfig/initial_backoff_interval:100ms/
ParseFieldTrial({&initial_backoff_interval_, &link_capacity_fix_},
@ -234,7 +232,7 @@ double AimdRateControl::GetNearMaxIncreaseRateBpsPerSecond() const {
// Approximate the over-use estimator delay to 100 ms.
TimeDelta response_time = rtt_ + TimeDelta::Millis(100);
if (in_experiment_)
response_time = response_time * 2;
double increase_rate_bps_per_second =
(avg_packet_size / response_time).bps<double>();
@ -273,29 +271,39 @@ void AimdRateControl::ChangeBitrate(const RateControlInput& input,
ChangeState(input, at_time);
// We limit the new bitrate based on the troughput to avoid unlimited bitrate
// increases. We allow a bit more lag at very low rates to not too easily get
// stuck if the encoder produces uneven outputs.
const DataRate troughput_based_limit =
1.5 * estimated_throughput + DataRate::KilobitsPerSec(10);
switch (rate_control_state_) {
case kRcHold:
case RateControlState::kRcHold:
break;
case kRcIncrease:
case RateControlState::kRcIncrease: {
if (estimated_throughput > link_capacity_.UpperBound())
link_capacity_.Reset();
// Do not increase the delay based estimate in alr since the estimator
// will not be able to get transport feedback necessary to detect if
// the new estimate is correct.
// If we have previously increased above the limit (for instance due to
// probing), we don't allow further changes.
if (current_bitrate_ < troughput_based_limit &&
!(send_side_ && in_alr_ && no_bitrate_increase_in_alr_)) {
// We limit the new bitrate based on the troughput to avoid unlimited
// bitrate increases. We allow a bit more lag at very low rates to not too
// easily get stuck if the encoder produces uneven outputs.
DataRate increase_limit =
1.5 * estimated_throughput + DataRate::KilobitsPerSec(10);
if (ignore_throughput_limit_if_network_estimate_ && network_estimate_ &&
network_estimate_->link_capacity_upper.IsFinite()) {
// If we have a Network estimate, we do allow the estimate to increase.
increase_limit = network_estimate_->link_capacity_upper *
estimate_bounded_increase_ratio_.Get();
} else if (send_side_ && in_alr_ && no_bitrate_increase_in_alr_) {
// Do not increase the delay based estimate in alr since the estimator
// will not be able to get transport feedback necessary to detect if
// the new estimate is correct.
// If we have previously increased above the limit (for instance due to
// probing), we don't allow further changes.
increase_limit = current_bitrate_;
}
if (current_bitrate_ < increase_limit) {
DataRate increased_bitrate = DataRate::MinusInfinity();
if (link_capacity_.has_estimate()) {
if (increase_to_network_estimate_ && network_estimate_ &&
network_estimate_->link_capacity_upper.IsFinite()) {
increased_bitrate = increase_limit;
} else if (link_capacity_.has_estimate()) {
// The link_capacity estimate is reset if the measured throughput
// is too far from the estimate. We can therefore assume that our
// target rate is reasonably close to link capacity and use additive
@ -310,13 +318,13 @@ void AimdRateControl::ChangeBitrate(const RateControlInput& input,
at_time, time_last_bitrate_change_, current_bitrate_);
increased_bitrate = current_bitrate_ + multiplicative_increase;
}
new_bitrate = std::min(increased_bitrate, troughput_based_limit);
new_bitrate = std::min(increased_bitrate, increase_limit);
}
time_last_bitrate_change_ = at_time;
break;
}
case kRcDecrease: {
case RateControlState::kRcDecrease: {
DataRate decreased_bitrate = DataRate::PlusInfinity();
// Set bit rate to something slightly lower than the measured throughput
@ -330,11 +338,6 @@ void AimdRateControl::ChangeBitrate(const RateControlInput& input,
decreased_bitrate = beta_ * link_capacity_.estimate();
}
}
if (estimate_bounded_backoff_ && network_estimate_) {
decreased_bitrate = std::max(
decreased_bitrate, network_estimate_->link_capacity_lower * beta_);
}
// Avoid increasing the rate when over-using.
if (decreased_bitrate < current_bitrate_) {
new_bitrate = decreased_bitrate;
@ -356,22 +359,34 @@ void AimdRateControl::ChangeBitrate(const RateControlInput& input,
bitrate_is_initialized_ = true;
link_capacity_.OnOveruseDetected(estimated_throughput);
// Stay on hold until the pipes are cleared.
rate_control_state_ = kRcHold;
rate_control_state_ = RateControlState::kRcHold;
time_last_bitrate_change_ = at_time;
time_last_bitrate_decrease_ = at_time;
break;
}
default:
assert(false);
RTC_DCHECK_NOTREACHED();
}
current_bitrate_ = ClampBitrate(new_bitrate.value_or(current_bitrate_));
}
DataRate AimdRateControl::ClampBitrate(DataRate new_bitrate) const {
if (estimate_bounded_increase_ && network_estimate_) {
DataRate upper_bound = network_estimate_->link_capacity_upper;
new_bitrate = std::min(new_bitrate, upper_bound);
if (!disable_estimate_bounded_increase_ && network_estimate_ &&
network_estimate_->link_capacity_upper.IsFinite()) {
DataRate upper_bound = network_estimate_->link_capacity_upper *
estimate_bounded_increase_ratio_.Get();
if (ignore_network_estimate_decrease_) {
upper_bound = std::max(upper_bound, current_bitrate_);
}
new_bitrate = std::min(upper_bound, new_bitrate);
}
if (estimate_bounded_backoff_ && network_estimate_ &&
network_estimate_->link_capacity_lower.IsFinite() &&
new_bitrate < current_bitrate_) {
new_bitrate = std::min(
current_bitrate_,
std::max(new_bitrate, network_estimate_->link_capacity_lower * beta_));
}
new_bitrate = std::max(new_bitrate, min_configured_bitrate_);
return new_bitrate;
@ -403,21 +418,21 @@ void AimdRateControl::ChangeState(const RateControlInput& input,
Timestamp at_time) {
switch (input.bw_state) {
case BandwidthUsage::kBwNormal:
if (rate_control_state_ == kRcHold) {
if (rate_control_state_ == RateControlState::kRcHold) {
time_last_bitrate_change_ = at_time;
rate_control_state_ = kRcIncrease;
rate_control_state_ = RateControlState::kRcIncrease;
}
break;
case BandwidthUsage::kBwOverusing:
if (rate_control_state_ != kRcDecrease) {
rate_control_state_ = kRcDecrease;
if (rate_control_state_ != RateControlState::kRcDecrease) {
rate_control_state_ = RateControlState::kRcDecrease;
}
break;
case BandwidthUsage::kBwUnderusing:
rate_control_state_ = kRcHold;
rate_control_state_ = RateControlState::kRcHold;
break;
default:
assert(false);
RTC_DCHECK_NOTREACHED();
}
}

View File

@ -14,8 +14,8 @@
#include <stdint.h>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/transport/network_types.h"
#include "api/transport/webrtc_key_value_config.h"
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/goog_cc/link_capacity_estimator.h"
@ -30,8 +30,8 @@ namespace webrtc {
// multiplicatively.
class AimdRateControl {
public:
explicit AimdRateControl(const WebRtcKeyValueConfig* key_value_config);
AimdRateControl(const WebRtcKeyValueConfig* key_value_config, bool send_side);
explicit AimdRateControl(const FieldTrialsView* key_value_config);
AimdRateControl(const FieldTrialsView* key_value_config, bool send_side);
~AimdRateControl();
// Returns true if the target bitrate has been initialized. This happens
@ -65,6 +65,8 @@ class AimdRateControl {
TimeDelta GetExpectedBandwidthPeriod() const;
private:
enum class RateControlState { kRcHold, kRcIncrease, kRcDecrease };
friend class GoogCcStatePrinter;
// Update the target bitrate based on, among other things, the current rate
// control state, the current target bitrate and the estimated throughput.
@ -98,16 +100,22 @@ class AimdRateControl {
bool in_alr_;
TimeDelta rtt_;
const bool send_side_;
const bool in_experiment_;
// Allow the delay based estimate to only increase as long as application
// limited region (alr) is not detected.
const bool no_bitrate_increase_in_alr_;
// Use estimated link capacity lower bound if it is higher than the
// acknowledged rate when backing off due to overuse.
const bool estimate_bounded_backoff_;
// Use estimated link capacity upper bound as upper limit for increasing
// bitrate over the acknowledged rate.
const bool estimate_bounded_increase_;
// If false, uses estimated link capacity upper bound *
// `estimate_bounded_increase_ratio_` as upper limit for the estimate.
FieldTrialFlag disable_estimate_bounded_increase_{"Disabled"};
FieldTrialParameter<double> estimate_bounded_increase_ratio_{"ratio", 1.0};
FieldTrialParameter<bool> ignore_throughput_limit_if_network_estimate_{
"ignore_acked", false};
FieldTrialParameter<bool> increase_to_network_estimate_{"immediate_incr",
false};
FieldTrialParameter<bool> ignore_network_estimate_decrease_{"ignore_decr",
false};
absl::optional<DataRate> last_decrease_;
FieldTrialOptional<TimeDelta> initial_backoff_interval_;
FieldTrialFlag link_capacity_fix_;

View File

@ -12,6 +12,7 @@
#include <memory>
#include "api/transport/field_trial_based_config.h"
#include "api/units/data_rate.h"
#include "system_wrappers/include/clock.h"
#include "test/field_trial.h"
#include "test/gtest.h"
@ -254,6 +255,62 @@ TEST(AimdRateControlTest, SetEstimateIncreaseBweInAlr) {
2 * kInitialBitrateBps);
}
TEST(AimdRateControlTest, SetEstimateUpperLimitedByNetworkEstimate) {
auto states = CreateAimdRateControlStates(/*send_side=*/true);
NetworkStateEstimate network_estimate;
network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(400);
states.aimd_rate_control->SetNetworkStateEstimate(network_estimate);
SetEstimate(states, 500'000);
EXPECT_EQ(states.aimd_rate_control->LatestEstimate(),
network_estimate.link_capacity_upper);
}
TEST(AimdRateControlTest, SetEstimateLowerLimitedByNetworkEstimate) {
auto states = CreateAimdRateControlStates(/*send_side=*/true);
NetworkStateEstimate network_estimate;
network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(400);
states.aimd_rate_control->SetNetworkStateEstimate(network_estimate);
SetEstimate(states, 100'000);
// 0.85 is default backoff factor. (`beta_`)
EXPECT_EQ(states.aimd_rate_control->LatestEstimate(),
network_estimate.link_capacity_lower * 0.85);
}
TEST(AimdRateControlTest,
SetEstimateIgnoredIfLowerThanNetworkEstimateAndCurrent) {
auto states = CreateAimdRateControlStates(/*send_side=*/true);
SetEstimate(states, 200'000);
ASSERT_EQ(states.aimd_rate_control->LatestEstimate().kbps(), 200);
NetworkStateEstimate network_estimate;
network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(400);
states.aimd_rate_control->SetNetworkStateEstimate(network_estimate);
// Ignore the next SetEstimate, since the estimate is lower than 85% of
// the network estimate.
SetEstimate(states, 100'000);
EXPECT_EQ(states.aimd_rate_control->LatestEstimate().kbps(), 200);
}
TEST(AimdRateControlTest, SetEstimateIgnoresNetworkEstimatesLowerThanCurrent) {
test::ScopedFieldTrials override_field_trials(
"WebRTC-Bwe-EstimateBoundedIncrease/"
"ratio:0.85,ignore_acked:true,ignore_decr:true/");
auto states = CreateAimdRateControlStates(/*send_side=*/true);
states.aimd_rate_control->SetStartBitrate(DataRate::KilobitsPerSec(30));
NetworkStateEstimate network_estimate;
network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(400);
states.aimd_rate_control->SetNetworkStateEstimate(network_estimate);
SetEstimate(states, 500'000);
ASSERT_EQ(states.aimd_rate_control->LatestEstimate(),
network_estimate.link_capacity_upper * 0.85);
NetworkStateEstimate lower_network_estimate;
lower_network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(300);
states.aimd_rate_control->SetNetworkStateEstimate(lower_network_estimate);
SetEstimate(states, 500'000);
EXPECT_EQ(states.aimd_rate_control->LatestEstimate(),
network_estimate.link_capacity_upper * 0.85);
}
TEST(AimdRateControlTest, EstimateIncreaseWhileNotInAlr) {
// Allow the estimate to increase as long as alr is not detected to ensure
// tha BWE can not get stuck at a certain bitrate.
@ -274,4 +331,154 @@ TEST(AimdRateControlTest, EstimateIncreaseWhileNotInAlr) {
kInitialBitrateBps);
}
TEST(AimdRateControlTest, EstimateNotLimitedByNetworkEstimateIfDisabled) {
test::ScopedFieldTrials override_field_trials(
"WebRTC-Bwe-EstimateBoundedIncrease/Disabled/");
auto states = CreateAimdRateControlStates(/*send_side=*/true);
constexpr int kInitialBitrateBps = 123000;
SetEstimate(states, kInitialBitrateBps);
states.aimd_rate_control->SetInApplicationLimitedRegion(false);
NetworkStateEstimate network_estimate;
network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(150);
states.aimd_rate_control->SetNetworkStateEstimate(network_estimate);
for (int i = 0; i < 100; ++i) {
UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt,
states.simulated_clock->TimeInMilliseconds());
states.simulated_clock->AdvanceTimeMilliseconds(100);
}
EXPECT_GT(states.aimd_rate_control->LatestEstimate(),
network_estimate.link_capacity_upper);
}
TEST(AimdRateControlTest,
EstimateSlowlyIncreaseToUpperLinkCapacityEstimateIfConfigured) {
// Even if alr is detected, the delay based estimator is allowed to increase
// up to a percentage of upper link capacity.
test::ScopedFieldTrials override_field_trials(
"WebRTC-Bwe-EstimateBoundedIncrease/"
"ratio:0.85,ignore_acked:true,immediate_incr:false/"
"WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/");
auto states = CreateAimdRateControlStates(/*send_side=*/true);
constexpr int kInitialBitrateBps = 123000;
SetEstimate(states, kInitialBitrateBps);
states.aimd_rate_control->SetInApplicationLimitedRegion(true);
NetworkStateEstimate network_estimate;
network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200);
states.aimd_rate_control->SetNetworkStateEstimate(network_estimate);
for (int i = 0; i < 10; ++i) {
UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt,
states.simulated_clock->TimeInMilliseconds());
states.simulated_clock->AdvanceTimeMilliseconds(100);
EXPECT_LT(states.aimd_rate_control->LatestEstimate(),
network_estimate.link_capacity_upper * 0.85);
}
for (int i = 0; i < 50; ++i) {
UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt,
states.simulated_clock->TimeInMilliseconds());
states.simulated_clock->AdvanceTimeMilliseconds(100);
}
EXPECT_EQ(states.aimd_rate_control->LatestEstimate(),
network_estimate.link_capacity_upper * 0.85);
}
TEST(AimdRateControlTest,
EstimateImmediatelyIncreaseToUpperLinkCapacityEstimateIfConfigured) {
// Even if alr is detected, the delay based estimator is allowed to increase
// up to a percentage of upper link capacity.
test::ScopedFieldTrials override_field_trials(
"WebRTC-Bwe-EstimateBoundedIncrease/"
"ratio:0.85,ignore_acked:true,immediate_incr:true/"
"WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/");
auto states = CreateAimdRateControlStates(/*send_side=*/true);
constexpr int kInitialBitrateBps = 123000;
SetEstimate(states, kInitialBitrateBps);
states.aimd_rate_control->SetInApplicationLimitedRegion(true);
NetworkStateEstimate network_estimate;
network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200);
states.aimd_rate_control->SetNetworkStateEstimate(network_estimate);
UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt,
states.simulated_clock->TimeInMilliseconds());
EXPECT_EQ(states.aimd_rate_control->LatestEstimate(),
network_estimate.link_capacity_upper * 0.85);
}
TEST(AimdRateControlTest, EstimateNotLoweredByNetworkEstimate) {
// The delay based estimator is allowed to increase up to a percentage of
// upper link capacity but does not decrease unless the delay detector
// discover an overuse.
test::ScopedFieldTrials override_field_trials(
"WebRTC-Bwe-EstimateBoundedIncrease/"
"ratio:0.85,ignore_acked:true,ignore_decr:true/"
"WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/");
auto states = CreateAimdRateControlStates(/*send_side=*/true);
constexpr int kInitialBitrateBps = 123000;
constexpr int kEstimatedThroughputBps = 30'000;
SetEstimate(states, kInitialBitrateBps);
NetworkStateEstimate network_estimate;
network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200);
states.aimd_rate_control->SetNetworkStateEstimate(network_estimate);
for (int i = 0; i < 100; ++i) {
UpdateRateControl(states, BandwidthUsage::kBwNormal,
kEstimatedThroughputBps,
states.simulated_clock->TimeInMilliseconds());
states.simulated_clock->AdvanceTimeMilliseconds(100);
}
DataRate estimate_after_increase = states.aimd_rate_control->LatestEstimate();
ASSERT_EQ(estimate_after_increase,
network_estimate.link_capacity_upper * 0.85);
// A lower network estimate does not decrease the estimate immediately,
// but the estimate is not allowed to increase.
network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(100);
network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(80);
states.aimd_rate_control->SetNetworkStateEstimate(network_estimate);
for (int i = 0; i < 10; ++i) {
UpdateRateControl(states, BandwidthUsage::kBwNormal,
kEstimatedThroughputBps,
states.simulated_clock->TimeInMilliseconds());
states.simulated_clock->AdvanceTimeMilliseconds(100);
EXPECT_EQ(states.aimd_rate_control->LatestEstimate(),
estimate_after_increase);
}
// If the detector detects and overuse, BWE drops to a value relative the
// network estimate.
UpdateRateControl(states, BandwidthUsage::kBwOverusing,
kEstimatedThroughputBps,
states.simulated_clock->TimeInMilliseconds());
EXPECT_LT(states.aimd_rate_control->LatestEstimate(),
network_estimate.link_capacity_lower);
EXPECT_GT(states.aimd_rate_control->LatestEstimate().bps(),
kEstimatedThroughputBps);
}
TEST(AimdRateControlTest, EstimateDoesNotIncreaseInAlrIfNetworkEstimateNotSet) {
// When alr is detected, the delay based estimator is not allowed to increase
// bwe since there will be no feedback from the network if the new estimate
// is correct.
test::ScopedFieldTrials override_field_trials(
"WebRTC-Bwe-EstimateBoundedIncrease/ratio:0.85,ignore_acked:true/"
"WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/");
auto states = CreateAimdRateControlStates(/*send_side=*/true);
constexpr int kInitialBitrateBps = 123000;
SetEstimate(states, kInitialBitrateBps);
states.aimd_rate_control->SetInApplicationLimitedRegion(true);
UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps,
states.simulated_clock->TimeInMilliseconds());
ASSERT_EQ(states.aimd_rate_control->LatestEstimate().bps(),
kInitialBitrateBps);
for (int i = 0; i < 100; ++i) {
UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt,
states.simulated_clock->TimeInMilliseconds());
states.simulated_clock->AdvanceTimeMilliseconds(100);
}
EXPECT_EQ(states.aimd_rate_control->LatestEstimate().bps(),
kInitialBitrateBps);
}
} // namespace webrtc

View File

@ -10,24 +10,10 @@
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
const char kBweTypeHistogram[] = "WebRTC.BWE.Types";
namespace congestion_controller {
int GetMinBitrateBps() {
constexpr int kMinBitrateBps = 5000;
return kMinBitrateBps;
}
DataRate GetMinBitrate() {
return DataRate::BitsPerSec(GetMinBitrateBps());
}
} // namespace congestion_controller
RateControlInput::RateControlInput(
BandwidthUsage bw_state,
const absl::optional<DataRate>& estimated_throughput)

View File

@ -17,15 +17,9 @@
#include "api/network_state_predictor.h"
#include "api/units/data_rate.h"
#define BWE_MAX(a, b) ((a) > (b) ? (a) : (b))
#define BWE_MIN(a, b) ((a) < (b) ? (a) : (b))
namespace webrtc {
namespace congestion_controller {
int GetMinBitrateBps();
DataRate GetMinBitrate();
} // namespace congestion_controller
constexpr DataRate kCongestionControllerMinBitrate = DataRate::BitsPerSec(5000);
static const int64_t kBitrateWindowMs = 1000;
@ -39,8 +33,6 @@ enum BweNames {
kBweNamesMax = 4
};
enum RateControlState { kRcHold, kRcIncrease, kRcDecrease };
struct RateControlInput {
RateControlInput(BandwidthUsage bw_state,
const absl::optional<DataRate>& estimated_throughput);

View File

@ -17,7 +17,8 @@
#include <memory>
#include <vector>
#include "modules/include/module.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "modules/include/module_common_types.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtcp_packet.h"
@ -38,54 +39,32 @@ class RemoteBitrateObserver {
virtual ~RemoteBitrateObserver() {}
};
class TransportFeedbackSenderInterface {
public:
virtual ~TransportFeedbackSenderInterface() = default;
virtual bool SendCombinedRtcpPacket(
std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets) = 0;
};
// TODO(holmer): Remove when all implementations have been updated.
struct ReceiveBandwidthEstimatorStats {};
class RemoteBitrateEstimator : public CallStatsObserver, public Module {
class RemoteBitrateEstimator : public CallStatsObserver {
public:
~RemoteBitrateEstimator() override {}
// Called for each incoming packet. 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
// remote bitrate estimate will be updated. Note that `payload_size` is the
// packet size excluding headers.
// Note that |arrival_time_ms| can be of an arbitrary time base.
// Note that `arrival_time_ms` can be of an arbitrary time base.
virtual void IncomingPacket(int64_t arrival_time_ms,
size_t payload_size,
const RTPHeader& header) = 0;
// Removes all data for |ssrc|.
// Removes all data for `ssrc`.
virtual void RemoveStream(uint32_t ssrc) = 0;
// 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<uint32_t>* ssrcs,
uint32_t* bitrate_bps) const = 0;
// Returns latest estimate or DataRate::Zero() if estimation is unavailable.
virtual DataRate LatestEstimate() const = 0;
// TODO(holmer): Remove when all implementations have been updated.
virtual bool GetStats(ReceiveBandwidthEstimatorStats* output) const;
virtual void SetMinBitrate(int min_bitrate_bps) = 0;
virtual TimeDelta Process() = 0;
protected:
static const int64_t kProcessIntervalMs = 500;
static const int64_t kStreamTimeOutMs = 2000;
};
inline bool RemoteBitrateEstimator::GetStats(
ReceiveBandwidthEstimatorStats* output) const {
return false;
}
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_

View File

@ -10,8 +10,6 @@
#include "modules/remote_bitrate_estimator/inter_arrival.h"
#include <cassert>
#include "modules/include/module_common_types_public.h"
#include "rtc_base/logging.h"
@ -37,9 +35,9 @@ bool InterArrival::ComputeDeltas(uint32_t timestamp,
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);
RTC_DCHECK(timestamp_delta);
RTC_DCHECK(arrival_time_delta_ms);
RTC_DCHECK(packet_size_delta);
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
@ -85,7 +83,7 @@ bool InterArrival::ComputeDeltas(uint32_t timestamp,
} else {
num_consecutive_reordered_packets_ = 0;
}
assert(*arrival_time_delta_ms >= 0);
RTC_DCHECK_GE(*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;
@ -121,8 +119,8 @@ bool InterArrival::PacketInOrder(uint32_t timestamp) {
}
}
// Assumes that |timestamp| is not reordered compared to
// |current_timestamp_group_|.
// 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()) {
@ -141,7 +139,7 @@ bool InterArrival::BelongsToBurst(int64_t arrival_time_ms,
if (!burst_grouping_) {
return false;
}
assert(current_timestamp_group_.complete_time_ms >= 0);
RTC_DCHECK_GE(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;

View File

@ -14,8 +14,6 @@
#include <stddef.h>
#include <stdint.h>
#include "rtc_base/constructor_magic.h"
namespace webrtc {
// Helper class to compute the inter-arrival time delta and the size delta
@ -35,14 +33,18 @@ class InterArrival {
double timestamp_to_ms_coeff,
bool enable_burst_grouping);
InterArrival() = delete;
InterArrival(const InterArrival&) = delete;
InterArrival& operator=(const InterArrival&) = delete;
// 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.
// `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,
int64_t system_time_ms,
@ -70,11 +72,11 @@ class InterArrival {
int64_t last_system_time_ms;
};
// Returns true if the packet with timestamp |timestamp| arrived in order.
// 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.
// 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;
@ -87,8 +89,6 @@ class InterArrival {
double timestamp_to_ms_coeff_;
bool burst_grouping_;
int num_consecutive_reordered_packets_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(InterArrival);
};
} // namespace webrtc

View File

@ -16,64 +16,28 @@
#include <algorithm>
#include <string>
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_minmax.h"
namespace webrtc {
const char kAdaptiveThresholdExperiment[] = "WebRTC-AdaptiveBweThreshold";
const char kEnabledPrefix[] = "Enabled";
const size_t kEnabledPrefixLength = sizeof(kEnabledPrefix) - 1;
const char kDisabledPrefix[] = "Disabled";
const size_t kDisabledPrefixLength = sizeof(kDisabledPrefix) - 1;
const double kMaxAdaptOffsetMs = 15.0;
const double kOverUsingTimeThreshold = 10;
const int kMaxNumDeltas = 60;
bool AdaptiveThresholdExperimentIsDisabled(
const WebRtcKeyValueConfig& key_value_config) {
std::string experiment_string =
key_value_config.Lookup(kAdaptiveThresholdExperiment);
const size_t kMinExperimentLength = kDisabledPrefixLength;
if (experiment_string.length() < kMinExperimentLength)
return false;
return experiment_string.substr(0, kDisabledPrefixLength) == kDisabledPrefix;
}
// Gets thresholds from the experiment name following the format
// "WebRTC-AdaptiveBweThreshold/Enabled-0.5,0.002/".
bool ReadExperimentConstants(const WebRtcKeyValueConfig& key_value_config,
double* k_up,
double* k_down) {
std::string experiment_string =
key_value_config.Lookup(kAdaptiveThresholdExperiment);
const size_t kMinExperimentLength = kEnabledPrefixLength + 3;
if (experiment_string.length() < kMinExperimentLength ||
experiment_string.substr(0, kEnabledPrefixLength) != kEnabledPrefix)
return false;
return sscanf(experiment_string.substr(kEnabledPrefixLength + 1).c_str(),
"%lf,%lf", k_up, k_down) == 2;
}
OveruseDetector::OveruseDetector(const WebRtcKeyValueConfig* key_value_config)
OveruseDetector::OveruseDetector(const FieldTrialsView* key_value_config)
// Experiment is on by default, but can be disabled with finch by setting
// the field trial string to "WebRTC-AdaptiveBweThreshold/Disabled/".
: in_experiment_(!AdaptiveThresholdExperimentIsDisabled(*key_value_config)),
k_up_(0.0087),
: k_up_(0.0087),
k_down_(0.039),
overusing_time_threshold_(100),
overusing_time_threshold_(kOverUsingTimeThreshold),
threshold_(12.5),
last_update_ms_(-1),
prev_offset_(0.0),
time_over_using_(-1),
overuse_counter_(0),
hypothesis_(BandwidthUsage::kBwNormal) {
if (!AdaptiveThresholdExperimentIsDisabled(*key_value_config))
InitializeExperiment(*key_value_config);
}
hypothesis_(BandwidthUsage::kBwNormal) {}
OveruseDetector::~OveruseDetector() {}
@ -126,9 +90,6 @@ BandwidthUsage OveruseDetector::Detect(double offset,
}
void OveruseDetector::UpdateThreshold(double modified_offset, int64_t now_ms) {
if (!in_experiment_)
return;
if (last_update_ms_ == -1)
last_update_ms_ = now_ms;
@ -147,15 +108,4 @@ void OveruseDetector::UpdateThreshold(double modified_offset, int64_t now_ms) {
last_update_ms_ = now_ms;
}
void OveruseDetector::InitializeExperiment(
const WebRtcKeyValueConfig& key_value_config) {
RTC_DCHECK(in_experiment_);
double k_up = 0.0;
double k_down = 0.0;
overusing_time_threshold_ = kOverUsingTimeThreshold;
if (ReadExperimentConstants(key_value_config, &k_up, &k_down)) {
k_up_ = k_up;
k_down_ = k_down;
}
}
} // namespace webrtc

View File

@ -12,25 +12,24 @@
#include <stdint.h>
#include "api/transport/webrtc_key_value_config.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "rtc_base/constructor_magic.h"
#include "api/field_trials_view.h"
#include "api/network_state_predictor.h"
namespace webrtc {
bool AdaptiveThresholdExperimentIsDisabled(
const WebRtcKeyValueConfig& key_value_config);
class OveruseDetector {
public:
explicit OveruseDetector(const WebRtcKeyValueConfig* key_value_config);
explicit OveruseDetector(const FieldTrialsView* key_value_config);
virtual ~OveruseDetector();
OveruseDetector(const OveruseDetector&) = delete;
OveruseDetector& operator=(const OveruseDetector&) = delete;
// Update the detection state based on the estimated inter-arrival time delta
// offset. |timestamp_delta| is the delta between the last timestamp which the
// 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.
// `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,
@ -42,20 +41,17 @@ class OveruseDetector {
private:
void UpdateThreshold(double modified_offset, int64_t now_ms);
void InitializeExperiment(const WebRtcKeyValueConfig& key_value_config);
void InitializeExperiment(const FieldTrialsView& key_value_config);
bool in_experiment_;
double k_up_;
double k_down_;
double overusing_time_threshold_;
const double k_up_;
const double k_down_;
const double overusing_time_threshold_;
double threshold_;
int64_t last_update_ms_;
double prev_offset_;
double time_over_using_;
int overuse_counter_;
BandwidthUsage hypothesis_;
RTC_DISALLOW_COPY_AND_ASSIGN(OveruseDetector);
};
} // namespace webrtc

View File

@ -21,7 +21,6 @@
#include "modules/remote_bitrate_estimator/inter_arrival.h"
#include "modules/remote_bitrate_estimator/overuse_estimator.h"
#include "rtc_base/random.h"
#include "test/field_trial.h"
#include "test/gtest.h"
namespace webrtc {
@ -218,69 +217,6 @@ TEST_F(OveruseDetectorTest, SimpleOveruse100kbit10fps) {
EXPECT_EQ(7, 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 = random_.Rand(0, 49);
now_ms_ += frame_duration_ms - offset;
} else {
now_ms_ += frame_duration_ms + offset;
}
EXPECT_EQ(BandwidthUsage::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(BandwidthUsage::kBwNormal, overuse_detector_->State());
}
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
EXPECT_EQ(BandwidthUsage::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 = random_.Rand(0, 1);
now_ms_ += frame_duration_ms - offset;
} else {
now_ms_ += frame_duration_ms + offset;
}
EXPECT_EQ(BandwidthUsage::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(BandwidthUsage::kBwNormal, overuse_detector_->State());
}
UpdateDetector(rtp_timestamp, now_ms_, packet_size);
EXPECT_EQ(BandwidthUsage::kBwOverusing, overuse_detector_->State());
}
TEST_F(OveruseDetectorTest, OveruseWithLowVariance2000Kbit30fps) {
uint32_t frame_duration_ms = 33;
uint32_t drift_per_frame_ms = 1;
@ -322,13 +258,7 @@ TEST_F(OveruseDetectorTest, OveruseWithLowVariance2000Kbit30fps) {
EXPECT_EQ(BandwidthUsage::kBwOverusing, overuse_detector_->State());
}
#if defined(WEBRTC_ANDROID)
#define MAYBE_LowGaussianVariance30Kbit3fps \
DISABLED_LowGaussianVariance30Kbit3fps
#else
#define MAYBE_LowGaussianVariance30Kbit3fps LowGaussianVariance30Kbit3fps
#endif
TEST_F(OveruseDetectorTest, MAYBE_LowGaussianVariance30Kbit3fps) {
TEST_F(OveruseDetectorTest, LowGaussianVariance30Kbit3fps) {
size_t packet_size = 1200;
int packets_per_frame = 1;
int frame_duration_ms = 333;
@ -388,13 +318,7 @@ TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift30Kbit3fps) {
EXPECT_EQ(4, frames_until_overuse);
}
#if defined(WEBRTC_ANDROID)
#define MAYBE_LowGaussianVariance100Kbit5fps \
DISABLED_LowGaussianVariance100Kbit5fps
#else
#define MAYBE_LowGaussianVariance100Kbit5fps LowGaussianVariance100Kbit5fps
#endif
TEST_F(OveruseDetectorTest, MAYBE_LowGaussianVariance100Kbit5fps) {
TEST_F(OveruseDetectorTest, LowGaussianVariance100Kbit5fps) {
size_t packet_size = 1200;
int packets_per_frame = 2;
int frame_duration_ms = 200;
@ -409,13 +333,7 @@ TEST_F(OveruseDetectorTest, MAYBE_LowGaussianVariance100Kbit5fps) {
EXPECT_EQ(20, frames_until_overuse);
}
#if defined(WEBRTC_ANDROID)
#define MAYBE_HighGaussianVariance100Kbit5fps \
DISABLED_HighGaussianVariance100Kbit5fps
#else
#define MAYBE_HighGaussianVariance100Kbit5fps HighGaussianVariance100Kbit5fps
#endif
TEST_F(OveruseDetectorTest, MAYBE_HighGaussianVariance100Kbit5fps) {
TEST_F(OveruseDetectorTest, HighGaussianVariance100Kbit5fps) {
size_t packet_size = 1200;
int packets_per_frame = 2;
int frame_duration_ms = 200;
@ -430,13 +348,7 @@ TEST_F(OveruseDetectorTest, MAYBE_HighGaussianVariance100Kbit5fps) {
EXPECT_EQ(44, frames_until_overuse);
}
#if defined(WEBRTC_ANDROID)
#define MAYBE_LowGaussianVariance100Kbit10fps \
DISABLED_LowGaussianVariance100Kbit10fps
#else
#define MAYBE_LowGaussianVariance100Kbit10fps LowGaussianVariance100Kbit10fps
#endif
TEST_F(OveruseDetectorTest, MAYBE_LowGaussianVariance100Kbit10fps) {
TEST_F(OveruseDetectorTest, LowGaussianVariance100Kbit10fps) {
size_t packet_size = 1200;
int packets_per_frame = 1;
int frame_duration_ms = 100;
@ -451,13 +363,7 @@ TEST_F(OveruseDetectorTest, MAYBE_LowGaussianVariance100Kbit10fps) {
EXPECT_EQ(20, frames_until_overuse);
}
#if defined(WEBRTC_ANDROID)
#define MAYBE_HighGaussianVariance100Kbit10fps \
DISABLED_HighGaussianVariance100Kbit10fps
#else
#define MAYBE_HighGaussianVariance100Kbit10fps HighGaussianVariance100Kbit10fps
#endif
TEST_F(OveruseDetectorTest, MAYBE_HighGaussianVariance100Kbit10fps) {
TEST_F(OveruseDetectorTest, HighGaussianVariance100Kbit10fps) {
size_t packet_size = 1200;
int packets_per_frame = 1;
int frame_duration_ms = 100;
@ -472,13 +378,7 @@ TEST_F(OveruseDetectorTest, MAYBE_HighGaussianVariance100Kbit10fps) {
EXPECT_EQ(44, frames_until_overuse);
}
#if defined(WEBRTC_ANDROID)
#define MAYBE_LowGaussianVariance300Kbit30fps \
DISABLED_LowGaussianVariance300Kbit30fps
#else
#define MAYBE_LowGaussianVariance300Kbit30fps LowGaussianVariance300Kbit30fps
#endif
TEST_F(OveruseDetectorTest, MAYBE_LowGaussianVariance300Kbit30fps) {
TEST_F(OveruseDetectorTest, LowGaussianVariance300Kbit30fps) {
size_t packet_size = 1200;
int packets_per_frame = 1;
int frame_duration_ms = 33;
@ -538,13 +438,7 @@ TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift300Kbit30fps) {
EXPECT_EQ(10, frames_until_overuse);
}
#if defined(WEBRTC_ANDROID)
#define MAYBE_LowGaussianVariance1000Kbit30fps \
DISABLED_LowGaussianVariance1000Kbit30fps
#else
#define MAYBE_LowGaussianVariance1000Kbit30fps LowGaussianVariance1000Kbit30fps
#endif
TEST_F(OveruseDetectorTest, MAYBE_LowGaussianVariance1000Kbit30fps) {
TEST_F(OveruseDetectorTest, LowGaussianVariance1000Kbit30fps) {
size_t packet_size = 1200;
int packets_per_frame = 3;
int frame_duration_ms = 33;
@ -604,13 +498,7 @@ TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift1000Kbit30fps) {
EXPECT_EQ(10, frames_until_overuse);
}
#if defined(WEBRTC_ANDROID)
#define MAYBE_LowGaussianVariance2000Kbit30fps \
DISABLED_LowGaussianVariance2000Kbit30fps
#else
#define MAYBE_LowGaussianVariance2000Kbit30fps LowGaussianVariance2000Kbit30fps
#endif
TEST_F(OveruseDetectorTest, MAYBE_LowGaussianVariance2000Kbit30fps) {
TEST_F(OveruseDetectorTest, LowGaussianVariance2000Kbit30fps) {
size_t packet_size = 1200;
int packets_per_frame = 6;
int frame_duration_ms = 33;
@ -670,22 +558,7 @@ TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift2000Kbit30fps) {
EXPECT_EQ(10, frames_until_overuse);
}
class OveruseDetectorExperimentTest : public OveruseDetectorTest {
public:
OveruseDetectorExperimentTest()
: override_field_trials_(
"WebRTC-AdaptiveBweThreshold/Enabled-0.01,0.00018/") {}
protected:
void SetUp() override {
overuse_detector_.reset(new OveruseDetector(&field_trials_));
}
test::ScopedFieldTrials override_field_trials_;
const FieldTrialBasedConfig field_trials_;
};
TEST_F(OveruseDetectorExperimentTest, ThresholdAdapts) {
TEST_F(OveruseDetectorTest, ThresholdAdapts) {
const double kOffset = 0.21;
double kTsDelta = 3000.0;
int64_t now_ms = 0;
@ -756,7 +629,7 @@ TEST_F(OveruseDetectorExperimentTest, ThresholdAdapts) {
EXPECT_TRUE(overuse_detected);
}
TEST_F(OveruseDetectorExperimentTest, DoesntAdaptToSpikes) {
TEST_F(OveruseDetectorTest, DoesntAdaptToSpikes) {
const double kOffset = 1.0;
const double kLargeOffset = 20.0;
double kTsDelta = 3000.0;

View File

@ -10,13 +10,12 @@
#include "modules/remote_bitrate_estimator/overuse_estimator.h"
#include <assert.h>
#include <math.h>
#include <string.h>
#include <algorithm>
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "api/network_state_predictor.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/logging.h"
@ -110,7 +109,7 @@ void OveruseEstimator::Update(int64_t t_delta,
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);
RTC_DCHECK(positive_semi_definite);
if (!positive_semi_definite) {
RTC_LOG(LS_ERROR)
<< "The over-use estimator's covariance matrix is no longer "
@ -146,13 +145,13 @@ void OveruseEstimator::UpdateNoiseEstimate(double residual,
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|.
// 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
// 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;

View File

@ -14,8 +14,7 @@
#include <deque>
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "rtc_base/constructor_magic.h"
#include "api/network_state_predictor.h"
namespace webrtc {
@ -39,9 +38,12 @@ class OveruseEstimator {
explicit OveruseEstimator(const OverUseDetectorOptions& options);
~OveruseEstimator();
OveruseEstimator(const OveruseEstimator&) = delete;
OveruseEstimator& operator=(const OveruseEstimator&) = delete;
// 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
// `current_hypothesis` should be the hypothesis of the over-use detector at
// this time.
void Update(int64_t t_delta,
double ts_delta,
@ -75,8 +77,6 @@ class OveruseEstimator {
double avg_noise_;
double var_noise_;
std::deque<double> ts_delta_hist_;
RTC_DISALLOW_COPY_AND_ASSIGN(OveruseEstimator);
};
} // namespace webrtc

View File

@ -0,0 +1,192 @@
/*
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/remote_bitrate_estimator/packet_arrival_map.h"
#include <algorithm>
#include <cstdint>
#include "api/units/timestamp.h"
#include "rtc_base/checks.h"
namespace webrtc {
void PacketArrivalTimeMap::AddPacket(int64_t sequence_number,
Timestamp arrival_time) {
RTC_DCHECK_GE(arrival_time, Timestamp::Zero());
if (!has_seen_packet()) {
// First packet.
Reallocate(kMinCapacity);
begin_sequence_number_ = sequence_number;
end_sequence_number_ = sequence_number + 1;
arrival_times_[Index(sequence_number)] = arrival_time;
return;
}
if (sequence_number >= begin_sequence_number() &&
sequence_number < end_sequence_number()) {
// The packet is within the buffer - no need to expand it.
arrival_times_[Index(sequence_number)] = arrival_time;
return;
}
if (sequence_number < begin_sequence_number()) {
// The packet goes before the current buffer. Expand to add packet, but only
// if it fits within kMaxNumberOfPackets.
int64_t new_size = end_sequence_number() - sequence_number;
if (new_size > kMaxNumberOfPackets) {
// Don't expand the buffer further, as that would remove newly received
// packets.
return;
}
AdjustToSize(new_size);
arrival_times_[Index(sequence_number)] = arrival_time;
SetNotReceived(sequence_number + 1, begin_sequence_number_);
begin_sequence_number_ = sequence_number;
return;
}
// The packet goes after the buffer.
RTC_DCHECK_GE(sequence_number, end_sequence_number_);
int64_t new_end_sequence_number = sequence_number + 1;
if (new_end_sequence_number >= end_sequence_number_ + kMaxNumberOfPackets) {
// All old packets have to be removed.
begin_sequence_number_ = sequence_number;
end_sequence_number_ = new_end_sequence_number;
arrival_times_[Index(sequence_number)] = arrival_time;
return;
}
if (begin_sequence_number_ < new_end_sequence_number - kMaxNumberOfPackets) {
// Remove oldest entries
begin_sequence_number_ = new_end_sequence_number - kMaxNumberOfPackets;
RTC_DCHECK_GT(end_sequence_number_, begin_sequence_number_);
// Also trim the buffer to remove leading non-received packets, to
// ensure that the buffer only spans received packets.
TrimLeadingNotReceivedEntries();
}
AdjustToSize(new_end_sequence_number - begin_sequence_number_);
// Packets can be received out-of-order. If this isn't the next expected
// packet, add enough placeholders to fill the gap.
SetNotReceived(end_sequence_number_, sequence_number);
end_sequence_number_ = new_end_sequence_number;
arrival_times_[Index(sequence_number)] = arrival_time;
}
void PacketArrivalTimeMap::TrimLeadingNotReceivedEntries() {
const int begin_index = Index(begin_sequence_number_);
const Timestamp* const begin_it = arrival_times_.get() + begin_index;
const Timestamp* const end_it = arrival_times_.get() + capacity();
for (const Timestamp* it = begin_it; it != end_it; ++it) {
if (*it >= Timestamp::Zero()) {
begin_sequence_number_ += (it - begin_it);
return;
}
}
// Reached end of the arrival_times_ and all entries represent not received
// packets. Remove them.
begin_sequence_number_ += (capacity() - begin_index);
// Continue removing entries at the beginning of the circular buffer.
for (const Timestamp* it = arrival_times_.get(); it != begin_it; ++it) {
if (*it >= Timestamp::Zero()) {
begin_sequence_number_ += (it - arrival_times_.get());
return;
}
}
RTC_DCHECK_NOTREACHED() << "There should be at least one non-empty entry";
}
void PacketArrivalTimeMap::SetNotReceived(
int64_t begin_sequence_number_inclusive,
int64_t end_sequence_number_exclusive) {
static constexpr Timestamp value = Timestamp::MinusInfinity();
int begin_index = Index(begin_sequence_number_inclusive);
int end_index = Index(end_sequence_number_exclusive);
if (begin_index <= end_index) {
// Entries to clear are in single block:
// [......{-----}....]
std::fill(arrival_times_.get() + begin_index,
arrival_times_.get() + end_index, value);
} else {
// Entries to clear span across arrival_times_ border:
// [--}..........{---]
std::fill(arrival_times_.get() + begin_index,
arrival_times_.get() + capacity(), value);
std::fill(arrival_times_.get(), arrival_times_.get() + end_index, value);
}
}
void PacketArrivalTimeMap::RemoveOldPackets(int64_t sequence_number,
Timestamp arrival_time_limit) {
int64_t check_to = std::min(sequence_number, end_sequence_number_);
while (begin_sequence_number_ < check_to &&
arrival_times_[Index(begin_sequence_number_)] <= arrival_time_limit) {
++begin_sequence_number_;
}
AdjustToSize(end_sequence_number_ - begin_sequence_number_);
}
void PacketArrivalTimeMap::EraseTo(int64_t sequence_number) {
if (sequence_number < begin_sequence_number_) {
return;
}
if (sequence_number >= end_sequence_number_) {
// Erase all.
begin_sequence_number_ = end_sequence_number_;
return;
}
// Remove some.
begin_sequence_number_ = sequence_number;
AdjustToSize(end_sequence_number_ - begin_sequence_number_);
}
void PacketArrivalTimeMap::AdjustToSize(int new_size) {
if (new_size > capacity()) {
int new_capacity = capacity();
while (new_capacity < new_size)
new_capacity *= 2;
Reallocate(new_capacity);
}
if (capacity() > std::max(kMinCapacity, 4 * new_size)) {
int new_capacity = capacity();
while (new_capacity > 2 * std::max(new_size, kMinCapacity)) {
new_capacity /= 2;
}
Reallocate(new_capacity);
}
RTC_DCHECK_LE(new_size, capacity());
}
void PacketArrivalTimeMap::Reallocate(int new_capacity) {
int new_capacity_minus_1 = new_capacity - 1;
// Check capacity is a power of 2.
RTC_DCHECK_EQ(new_capacity & new_capacity_minus_1, 0);
// Create uninitialized memory.
// All valid entries should be set by `AddPacket` before use.
void* raw = operator new[](new_capacity * sizeof(Timestamp));
Timestamp* new_buffer = static_cast<Timestamp*>(raw);
for (int64_t sequence_number = begin_sequence_number_;
sequence_number < end_sequence_number_; ++sequence_number) {
new_buffer[sequence_number & new_capacity_minus_1] =
arrival_times_[sequence_number & capacity_minus_1_];
}
arrival_times_.reset(new_buffer);
capacity_minus_1_ = new_capacity_minus_1;
}
} // namespace webrtc

View File

@ -0,0 +1,146 @@
/*
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_PACKET_ARRIVAL_MAP_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_PACKET_ARRIVAL_MAP_H_
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include "api/units/timestamp.h"
#include "rtc_base/checks.h"
namespace webrtc {
// PacketArrivalTimeMap is an optimized map of packet sequence number to arrival
// time, limited in size to never exceed `kMaxNumberOfPackets`. It will grow as
// needed, and remove old packets, and will expand to allow earlier packets to
// be added (out-of-order).
//
// Not yet received packets have the arrival time zero. The queue will not span
// larger than necessary and the last packet should always be received. The
// first packet in the queue doesn't have to be received in case of receiving
// packets out-of-order.
class PacketArrivalTimeMap {
public:
struct PacketArrivalTime {
Timestamp arrival_time;
int64_t sequence_number;
};
// Impossible to request feedback older than what can be represented by 15
// bits.
static constexpr int kMaxNumberOfPackets = (1 << 15);
PacketArrivalTimeMap() = default;
PacketArrivalTimeMap(const PacketArrivalTimeMap&) = delete;
PacketArrivalTimeMap& operator=(const PacketArrivalTimeMap&) = delete;
~PacketArrivalTimeMap() = default;
// Indicates if the packet with `sequence_number` has already been received.
bool has_received(int64_t sequence_number) const {
return sequence_number >= begin_sequence_number() &&
sequence_number < end_sequence_number() &&
arrival_times_[Index(sequence_number)] >= Timestamp::Zero();
}
// Returns the sequence number of the first entry in the map, i.e. the
// sequence number that a `begin()` iterator would represent.
int64_t begin_sequence_number() const { return begin_sequence_number_; }
// Returns the sequence number of the element just after the map, i.e. the
// sequence number that an `end()` iterator would represent.
int64_t end_sequence_number() const { return end_sequence_number_; }
// Returns an element by `sequence_number`, which must be valid, i.e.
// between [begin_sequence_number, end_sequence_number).
Timestamp get(int64_t sequence_number) {
RTC_DCHECK_GE(sequence_number, begin_sequence_number());
RTC_DCHECK_LT(sequence_number, end_sequence_number());
return arrival_times_[Index(sequence_number)];
}
// Returns timestamp and sequence number of the received packet with sequence
// number equal or larger than `sequence_number`. `sequence_number` must be in
// range [begin_sequence_number, end_sequence_number).
PacketArrivalTime FindNextAtOrAfter(int64_t sequence_number) const {
RTC_DCHECK_GE(sequence_number, begin_sequence_number());
RTC_DCHECK_LT(sequence_number, end_sequence_number());
while (true) {
Timestamp t = arrival_times_[Index(sequence_number)];
if (t >= Timestamp::Zero()) {
return {.arrival_time = t, .sequence_number = sequence_number};
}
++sequence_number;
}
}
// Clamps `sequence_number` between [begin_sequence_number,
// end_sequence_number].
int64_t clamp(int64_t sequence_number) const {
return std::clamp(sequence_number, begin_sequence_number(),
end_sequence_number());
}
// Erases all elements from the beginning of the map until `sequence_number`.
void EraseTo(int64_t sequence_number);
// Records the fact that a packet with `sequence_number` arrived at
// `arrival_time_ms`.
void AddPacket(int64_t sequence_number, Timestamp arrival_time);
// Removes packets from the beginning of the map as long as they are received
// before `sequence_number` and with an age older than `arrival_time_limit`
void RemoveOldPackets(int64_t sequence_number, Timestamp arrival_time_limit);
private:
static constexpr int kMinCapacity = 128;
// Returns index in the `arrival_times_` for value for `sequence_number`.
int Index(int64_t sequence_number) const {
// Note that sequence_number might be negative, thus taking '%' requires
// extra handling and can be slow. Because capacity is a power of two, it
// is much faster to use '&' operator.
return sequence_number & capacity_minus_1_;
}
void SetNotReceived(int64_t begin_sequence_number_inclusive,
int64_t end_sequence_number_exclusive);
void TrimLeadingNotReceivedEntries();
// Adjust capacity to match new_size, may reduce capacity.
// On return guarantees capacity >= new_size.
void AdjustToSize(int new_size);
void Reallocate(int new_capacity);
int capacity() const { return capacity_minus_1_ + 1; }
bool has_seen_packet() const { return arrival_times_ != nullptr; }
// Circular buffer. Packet with sequence number `sequence_number`
// is stored in the slot `sequence_number % capacity_`
std::unique_ptr<Timestamp[]> arrival_times_ = nullptr;
// Allocated size of the `arrival_times_`
// capacity_ is a power of 2 in range [kMinCapacity, kMaxNumberOfPackets]
// `capacity - 1` is used much more often than `capacity`, thus that value is
// stored.
int capacity_minus_1_ = -1;
// The unwrapped sequence number for valid range of sequence numbers.
// arrival_times_ entries only valid for sequence numbers in range
// `begin_sequence_number_ <= sequence_number < end_sequence_number_`
int64_t begin_sequence_number_ = 0;
int64_t end_sequence_number_ = 0;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_PACKET_ARRIVAL_MAP_H_

View File

@ -0,0 +1,291 @@
/*
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/remote_bitrate_estimator/packet_arrival_map.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
TEST(PacketArrivalMapTest, IsConsistentWhenEmpty) {
PacketArrivalTimeMap map;
EXPECT_EQ(map.begin_sequence_number(), map.end_sequence_number());
EXPECT_FALSE(map.has_received(0));
EXPECT_EQ(map.clamp(-5), 0);
EXPECT_EQ(map.clamp(5), 0);
}
TEST(PacketArrivalMapTest, InsertsFirstItemIntoMap) {
PacketArrivalTimeMap map;
map.AddPacket(42, Timestamp::Millis(10));
EXPECT_EQ(map.begin_sequence_number(), 42);
EXPECT_EQ(map.end_sequence_number(), 43);
EXPECT_FALSE(map.has_received(41));
EXPECT_TRUE(map.has_received(42));
EXPECT_FALSE(map.has_received(44));
EXPECT_EQ(map.clamp(-100), 42);
EXPECT_EQ(map.clamp(42), 42);
EXPECT_EQ(map.clamp(100), 43);
}
TEST(PacketArrivalMapTest, InsertsWithGaps) {
PacketArrivalTimeMap map;
map.AddPacket(42, Timestamp::Zero());
map.AddPacket(45, Timestamp::Millis(11));
EXPECT_EQ(map.begin_sequence_number(), 42);
EXPECT_EQ(map.end_sequence_number(), 46);
EXPECT_FALSE(map.has_received(41));
EXPECT_TRUE(map.has_received(42));
EXPECT_FALSE(map.has_received(43));
EXPECT_FALSE(map.has_received(44));
EXPECT_TRUE(map.has_received(45));
EXPECT_FALSE(map.has_received(46));
EXPECT_EQ(map.get(42), Timestamp::Zero());
EXPECT_LT(map.get(43), Timestamp::Zero());
EXPECT_LT(map.get(44), Timestamp::Zero());
EXPECT_EQ(map.get(45), Timestamp::Millis(11));
EXPECT_EQ(map.clamp(-100), 42);
EXPECT_EQ(map.clamp(44), 44);
EXPECT_EQ(map.clamp(100), 46);
}
TEST(PacketArrivalMapTest, FindNextAtOrAfterWithGaps) {
PacketArrivalTimeMap map;
map.AddPacket(42, Timestamp::Zero());
map.AddPacket(45, Timestamp::Millis(11));
EXPECT_EQ(map.begin_sequence_number(), 42);
EXPECT_EQ(map.end_sequence_number(), 46);
PacketArrivalTimeMap::PacketArrivalTime packet = map.FindNextAtOrAfter(42);
EXPECT_EQ(packet.arrival_time, Timestamp::Zero());
EXPECT_EQ(packet.sequence_number, 42);
packet = map.FindNextAtOrAfter(43);
EXPECT_EQ(packet.arrival_time, Timestamp::Millis(11));
EXPECT_EQ(packet.sequence_number, 45);
}
TEST(PacketArrivalMapTest, InsertsWithinBuffer) {
PacketArrivalTimeMap map;
map.AddPacket(42, Timestamp::Millis(10));
map.AddPacket(45, Timestamp::Millis(11));
map.AddPacket(43, Timestamp::Millis(12));
map.AddPacket(44, Timestamp::Millis(13));
EXPECT_EQ(map.begin_sequence_number(), 42);
EXPECT_EQ(map.end_sequence_number(), 46);
EXPECT_FALSE(map.has_received(41));
EXPECT_TRUE(map.has_received(42));
EXPECT_TRUE(map.has_received(43));
EXPECT_TRUE(map.has_received(44));
EXPECT_TRUE(map.has_received(45));
EXPECT_FALSE(map.has_received(46));
EXPECT_EQ(map.get(42), Timestamp::Millis(10));
EXPECT_EQ(map.get(43), Timestamp::Millis(12));
EXPECT_EQ(map.get(44), Timestamp::Millis(13));
EXPECT_EQ(map.get(45), Timestamp::Millis(11));
}
TEST(PacketArrivalMapTest, GrowsBufferAndRemoveOld) {
PacketArrivalTimeMap map;
constexpr int64_t kLargeSeq = 42 + PacketArrivalTimeMap::kMaxNumberOfPackets;
map.AddPacket(42, Timestamp::Millis(10));
map.AddPacket(43, Timestamp::Millis(11));
map.AddPacket(44, Timestamp::Millis(12));
map.AddPacket(45, Timestamp::Millis(13));
map.AddPacket(kLargeSeq, Timestamp::Millis(12));
EXPECT_EQ(map.begin_sequence_number(), 43);
EXPECT_EQ(map.end_sequence_number(), kLargeSeq + 1);
EXPECT_EQ(map.end_sequence_number() - map.begin_sequence_number(),
PacketArrivalTimeMap::kMaxNumberOfPackets);
EXPECT_FALSE(map.has_received(41));
EXPECT_FALSE(map.has_received(42));
EXPECT_TRUE(map.has_received(43));
EXPECT_TRUE(map.has_received(44));
EXPECT_TRUE(map.has_received(45));
EXPECT_FALSE(map.has_received(46));
EXPECT_TRUE(map.has_received(kLargeSeq));
EXPECT_FALSE(map.has_received(kLargeSeq + 1));
}
TEST(PacketArrivalMapTest, GrowsBufferAndRemoveOldTrimsBeginning) {
PacketArrivalTimeMap map;
constexpr int64_t kLargeSeq = 42 + PacketArrivalTimeMap::kMaxNumberOfPackets;
map.AddPacket(42, Timestamp::Millis(10));
// Missing: 43, 44
map.AddPacket(45, Timestamp::Millis(13));
map.AddPacket(kLargeSeq, Timestamp::Millis(12));
EXPECT_EQ(map.begin_sequence_number(), 45);
EXPECT_EQ(map.end_sequence_number(), kLargeSeq + 1);
EXPECT_FALSE(map.has_received(44));
EXPECT_TRUE(map.has_received(45));
EXPECT_FALSE(map.has_received(46));
EXPECT_TRUE(map.has_received(kLargeSeq));
EXPECT_FALSE(map.has_received(kLargeSeq + 1));
}
TEST(PacketArrivalMapTest, SequenceNumberJumpsDeletesAll) {
PacketArrivalTimeMap map;
constexpr int64_t kLargeSeq =
42 + 2 * PacketArrivalTimeMap::kMaxNumberOfPackets;
map.AddPacket(42, Timestamp::Millis(10));
map.AddPacket(kLargeSeq, Timestamp::Millis(12));
EXPECT_EQ(map.begin_sequence_number(), kLargeSeq);
EXPECT_EQ(map.end_sequence_number(), kLargeSeq + 1);
EXPECT_FALSE(map.has_received(42));
EXPECT_TRUE(map.has_received(kLargeSeq));
EXPECT_FALSE(map.has_received(kLargeSeq + 1));
}
TEST(PacketArrivalMapTest, ExpandsBeforeBeginning) {
PacketArrivalTimeMap map;
map.AddPacket(42, Timestamp::Millis(10));
map.AddPacket(-1000, Timestamp::Millis(13));
EXPECT_EQ(map.begin_sequence_number(), -1000);
EXPECT_EQ(map.end_sequence_number(), 43);
EXPECT_FALSE(map.has_received(-1001));
EXPECT_TRUE(map.has_received(-1000));
EXPECT_FALSE(map.has_received(-999));
EXPECT_TRUE(map.has_received(42));
EXPECT_FALSE(map.has_received(43));
}
TEST(PacketArrivalMapTest, ExpandingBeforeBeginningKeepsReceived) {
PacketArrivalTimeMap map;
map.AddPacket(42, Timestamp::Millis(10));
constexpr int64_t kSmallSeq =
static_cast<int64_t>(42) - 2 * PacketArrivalTimeMap::kMaxNumberOfPackets;
map.AddPacket(kSmallSeq, Timestamp::Millis(13));
EXPECT_EQ(map.begin_sequence_number(), 42);
EXPECT_EQ(map.end_sequence_number(), 43);
}
TEST(PacketArrivalMapTest, ErasesToRemoveElements) {
PacketArrivalTimeMap map;
map.AddPacket(42, Timestamp::Millis(10));
map.AddPacket(43, Timestamp::Millis(11));
map.AddPacket(44, Timestamp::Millis(12));
map.AddPacket(45, Timestamp::Millis(13));
map.EraseTo(44);
EXPECT_EQ(map.begin_sequence_number(), 44);
EXPECT_EQ(map.end_sequence_number(), 46);
EXPECT_FALSE(map.has_received(43));
EXPECT_TRUE(map.has_received(44));
EXPECT_TRUE(map.has_received(45));
EXPECT_FALSE(map.has_received(46));
}
TEST(PacketArrivalMapTest, ErasesInEmptyMap) {
PacketArrivalTimeMap map;
EXPECT_EQ(map.begin_sequence_number(), map.end_sequence_number());
map.EraseTo(map.end_sequence_number());
EXPECT_EQ(map.begin_sequence_number(), map.end_sequence_number());
}
TEST(PacketArrivalMapTest, IsTolerantToWrongArgumentsForErase) {
PacketArrivalTimeMap map;
map.AddPacket(42, Timestamp::Millis(10));
map.AddPacket(43, Timestamp::Millis(11));
map.EraseTo(1);
EXPECT_EQ(map.begin_sequence_number(), 42);
EXPECT_EQ(map.end_sequence_number(), 44);
map.EraseTo(100);
EXPECT_EQ(map.begin_sequence_number(), 44);
EXPECT_EQ(map.end_sequence_number(), 44);
}
TEST(PacketArrivalMapTest, EraseAllRemembersBeginningSeqNbr) {
PacketArrivalTimeMap map;
map.AddPacket(42, Timestamp::Millis(10));
map.AddPacket(43, Timestamp::Millis(11));
map.AddPacket(44, Timestamp::Millis(12));
map.AddPacket(45, Timestamp::Millis(13));
map.EraseTo(46);
map.AddPacket(50, Timestamp::Millis(10));
EXPECT_EQ(map.begin_sequence_number(), 46);
EXPECT_EQ(map.end_sequence_number(), 51);
EXPECT_FALSE(map.has_received(45));
EXPECT_FALSE(map.has_received(46));
EXPECT_FALSE(map.has_received(47));
EXPECT_FALSE(map.has_received(48));
EXPECT_FALSE(map.has_received(49));
EXPECT_TRUE(map.has_received(50));
EXPECT_FALSE(map.has_received(51));
}
TEST(PacketArrivalMapTest, EraseToMissingSequenceNumber) {
PacketArrivalTimeMap map;
map.AddPacket(37, Timestamp::Millis(10));
map.AddPacket(39, Timestamp::Millis(11));
map.AddPacket(40, Timestamp::Millis(12));
map.AddPacket(41, Timestamp::Millis(13));
map.EraseTo(38);
map.AddPacket(42, Timestamp::Millis(40));
EXPECT_EQ(map.begin_sequence_number(), 38);
EXPECT_EQ(map.end_sequence_number(), 43);
EXPECT_FALSE(map.has_received(37));
EXPECT_FALSE(map.has_received(38));
EXPECT_TRUE(map.has_received(39));
EXPECT_TRUE(map.has_received(40));
EXPECT_TRUE(map.has_received(41));
EXPECT_TRUE(map.has_received(42));
}
} // namespace
} // namespace webrtc

View File

@ -13,17 +13,36 @@
#include <math.h>
#include <algorithm>
#include <memory>
#include <utility>
#include "api/transport/field_trial_based_config.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "rtc_base/checks.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/logging.h"
#include "rtc_base/thread_annotations.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
constexpr TimeDelta kMinClusterDelta = TimeDelta::Millis(1);
constexpr TimeDelta kInitialProbingInterval = TimeDelta::Seconds(2);
constexpr int kTimestampGroupLengthMs = 5;
constexpr int kAbsSendTimeInterArrivalUpshift = 8;
constexpr int kInterArrivalShift =
RTPHeaderExtension::kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift;
constexpr int kMinClusterSize = 4;
constexpr int kMaxProbePackets = 15;
constexpr int kExpectedNumberOfProbes = 3;
constexpr double kTimestampToMs =
1000.0 / static_cast<double>(1 << kInterArrivalShift);
absl::optional<DataRate> OptionalRateFromOptionalBps(
absl::optional<int> bitrate_bps) {
if (bitrate_bps) {
@ -32,62 +51,48 @@ absl::optional<DataRate> OptionalRateFromOptionalBps(
return absl::nullopt;
}
}
} // namespace
enum {
kTimestampGroupLengthMs = 5,
kAbsSendTimeInterArrivalUpshift = 8,
kInterArrivalShift = RTPHeaderExtension::kAbsSendTimeFraction +
kAbsSendTimeInterArrivalUpshift,
kInitialProbingIntervalMs = 2000,
kMinClusterSize = 4,
kMaxProbePackets = 15,
kExpectedNumberOfProbes = 3
};
static const double kTimestampToMs =
1000.0 / static_cast<double>(1 << kInterArrivalShift);
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);
for (const auto& kv_pair : map) {
keys.push_back(kv_pair.first);
}
return keys;
}
uint32_t ConvertMsTo24Bits(int64_t time_ms) {
uint32_t time_24_bits =
static_cast<uint32_t>(((static_cast<uint64_t>(time_ms)
<< RTPHeaderExtension::kAbsSendTimeFraction) +
500) /
1000) &
0x00FFFFFF;
return time_24_bits;
}
} // namespace
RemoteBitrateEstimatorAbsSendTime::~RemoteBitrateEstimatorAbsSendTime() =
default;
bool RemoteBitrateEstimatorAbsSendTime::IsWithinClusterBounds(
int send_delta_ms,
TimeDelta send_delta,
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;
TimeDelta cluster_mean =
cluster_aggregate.send_mean / cluster_aggregate.count;
return (send_delta - cluster_mean).Abs() < TimeDelta::Micros(2'500);
}
void RemoteBitrateEstimatorAbsSendTime::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);
void RemoteBitrateEstimatorAbsSendTime::MaybeAddCluster(
const Cluster& cluster_aggregate,
std::list<Cluster>& clusters) {
if (cluster_aggregate.count < kMinClusterSize ||
cluster_aggregate.send_mean <= TimeDelta::Zero() ||
cluster_aggregate.recv_mean <= TimeDelta::Zero()) {
return;
}
Cluster cluster;
cluster.send_mean = cluster_aggregate.send_mean / cluster_aggregate.count;
cluster.recv_mean = cluster_aggregate.recv_mean / cluster_aggregate.count;
cluster.mean_size = cluster_aggregate.mean_size / cluster_aggregate.count;
cluster.count = cluster_aggregate.count;
cluster.num_above_min_delta = cluster_aggregate.num_above_min_delta;
clusters.push_back(cluster);
}
RemoteBitrateEstimatorAbsSendTime::RemoteBitrateEstimatorAbsSendTime(
@ -95,91 +100,77 @@ RemoteBitrateEstimatorAbsSendTime::RemoteBitrateEstimatorAbsSendTime(
Clock* clock)
: clock_(clock),
observer_(observer),
inter_arrival_(),
estimator_(),
detector_(&field_trials_),
incoming_bitrate_(kBitrateWindowMs, 8000),
incoming_bitrate_initialized_(false),
total_probes_received_(0),
first_packet_time_ms_(-1),
last_update_ms_(-1),
uma_recorded_(false),
remote_rate_(&field_trials_) {
RTC_DCHECK(clock_);
RTC_DCHECK(observer_);
RTC_LOG(LS_INFO) << "RemoteBitrateEstimatorAbsSendTime: Instantiating.";
}
void RemoteBitrateEstimatorAbsSendTime::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;
std::list<RemoteBitrateEstimatorAbsSendTime::Cluster>
RemoteBitrateEstimatorAbsSendTime::ComputeClusters() const {
std::list<Cluster> clusters;
Cluster cluster_aggregate;
Timestamp prev_send_time = Timestamp::MinusInfinity();
Timestamp prev_recv_time = Timestamp::MinusInfinity();
for (const Probe& probe : probes_) {
if (prev_send_time.IsFinite()) {
TimeDelta send_delta = probe.send_time - prev_send_time;
TimeDelta recv_delta = probe.recv_time - prev_recv_time;
if (send_delta >= kMinClusterDelta && recv_delta >= kMinClusterDelta) {
++cluster_aggregate.num_above_min_delta;
}
if (!IsWithinClusterBounds(send_delta_ms, current)) {
if (current.count >= kMinClusterSize && current.send_mean_ms > 0.0f &&
current.recv_mean_ms > 0.0f) {
AddCluster(clusters, &current);
}
current = Cluster();
if (!IsWithinClusterBounds(send_delta, cluster_aggregate)) {
MaybeAddCluster(cluster_aggregate, clusters);
cluster_aggregate = Cluster();
}
current.send_mean_ms += send_delta_ms;
current.recv_mean_ms += recv_delta_ms;
current.mean_size += it->payload_size;
++current.count;
cluster_aggregate.send_mean += send_delta;
cluster_aggregate.recv_mean += recv_delta;
cluster_aggregate.mean_size += probe.payload_size;
++cluster_aggregate.count;
}
prev_send_time = it->send_time_ms;
prev_recv_time = it->recv_time_ms;
}
if (current.count >= kMinClusterSize && current.send_mean_ms > 0.0f &&
current.recv_mean_ms > 0.0f) {
AddCluster(clusters, &current);
prev_send_time = probe.send_time;
prev_recv_time = probe.recv_time;
}
MaybeAddCluster(cluster_aggregate, clusters);
return clusters;
}
std::list<Cluster>::const_iterator
const RemoteBitrateEstimatorAbsSendTime::Cluster*
RemoteBitrateEstimatorAbsSendTime::FindBestProbe(
const std::list<Cluster>& clusters) const {
int highest_probe_bitrate_bps = 0;
std::list<Cluster>::const_iterator best_it = clusters.end();
for (std::list<Cluster>::const_iterator it = clusters.begin();
it != clusters.end(); ++it) {
if (it->send_mean_ms == 0 || it->recv_mean_ms == 0)
DataRate highest_probe_bitrate = DataRate::Zero();
const Cluster* best = nullptr;
for (const auto& cluster : clusters) {
if (cluster.send_mean == TimeDelta::Zero() ||
cluster.recv_mean == TimeDelta::Zero()) {
continue;
if (it->num_above_min_delta > it->count / 2 &&
(it->recv_mean_ms - it->send_mean_ms <= 2.0f &&
it->send_mean_ms - it->recv_mean_ms <= 5.0f)) {
int probe_bitrate_bps =
std::min(it->GetSendBitrateBps(), it->GetRecvBitrateBps());
if (probe_bitrate_bps > highest_probe_bitrate_bps) {
highest_probe_bitrate_bps = probe_bitrate_bps;
best_it = it;
}
if (cluster.num_above_min_delta > cluster.count / 2 &&
(cluster.recv_mean - cluster.send_mean <= TimeDelta::Millis(2) &&
cluster.send_mean - cluster.recv_mean <= TimeDelta::Millis(5))) {
DataRate probe_bitrate =
std::min(cluster.SendBitrate(), cluster.RecvBitrate());
if (probe_bitrate > highest_probe_bitrate) {
highest_probe_bitrate = probe_bitrate;
best = &cluster;
}
} else {
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;
RTC_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;
RTC_LOG(LS_INFO) << "Probe failed, sent at "
<< cluster.SendBitrate().bps() << " bps, received at "
<< cluster.RecvBitrate().bps()
<< " bps. Mean send delta: " << cluster.send_mean.ms()
<< " ms, mean recv delta: " << cluster.recv_mean.ms()
<< " ms, num probes: " << cluster.count;
break;
}
}
return best_it;
return best;
}
RemoteBitrateEstimatorAbsSendTime::ProbeResult
RemoteBitrateEstimatorAbsSendTime::ProcessClusters(int64_t now_ms) {
std::list<Cluster> clusters;
ComputeClusters(&clusters);
RemoteBitrateEstimatorAbsSendTime::ProcessClusters(Timestamp now) {
std::list<Cluster> clusters = ComputeClusters();
if (clusters.empty()) {
// If we reach the max number of probe packets and still have no clusters,
// we will remove the oldest one.
@ -188,21 +179,18 @@ RemoteBitrateEstimatorAbsSendTime::ProcessClusters(int64_t now_ms) {
return ProbeResult::kNoUpdate;
}
std::list<Cluster>::const_iterator best_it = FindBestProbe(clusters);
if (best_it != clusters.end()) {
int probe_bitrate_bps =
std::min(best_it->GetSendBitrateBps(), best_it->GetRecvBitrateBps());
if (const Cluster* best = FindBestProbe(clusters)) {
DataRate probe_bitrate = std::min(best->SendBitrate(), best->RecvBitrate());
// Make sure that a probe sent on a lower bitrate than our estimate can't
// reduce the estimate.
if (IsBitrateImproving(probe_bitrate_bps)) {
if (IsBitrateImproving(probe_bitrate)) {
RTC_LOG(LS_INFO) << "Probe successful, sent at "
<< best_it->GetSendBitrateBps() << " bps, received at "
<< best_it->GetRecvBitrateBps()
<< " bps. Mean send delta: " << best_it->send_mean_ms
<< " ms, mean recv delta: " << best_it->recv_mean_ms
<< " ms, num probes: " << best_it->count;
remote_rate_.SetEstimate(DataRate::BitsPerSec(probe_bitrate_bps),
Timestamp::Millis(now_ms));
<< best->SendBitrate().bps() << " bps, received at "
<< best->RecvBitrate().bps()
<< " bps. Mean send delta: " << best->send_mean.ms()
<< " ms, mean recv delta: " << best->recv_mean.ms()
<< " ms, num probes: " << best->count;
remote_rate_.SetEstimate(probe_bitrate, now);
return ProbeResult::kBitrateUpdated;
}
}
@ -215,11 +203,11 @@ RemoteBitrateEstimatorAbsSendTime::ProcessClusters(int64_t now_ms) {
}
bool RemoteBitrateEstimatorAbsSendTime::IsBitrateImproving(
int new_bitrate_bps) const {
bool initial_probe = !remote_rate_.ValidEstimate() && new_bitrate_bps > 0;
bool bitrate_above_estimate =
remote_rate_.ValidEstimate() &&
new_bitrate_bps > remote_rate_.LatestEstimate().bps<int>();
DataRate probe_bitrate) const {
bool initial_probe =
!remote_rate_.ValidEstimate() && probe_bitrate > DataRate::Zero();
bool bitrate_above_estimate = remote_rate_.ValidEstimate() &&
probe_bitrate > remote_rate_.LatestEstimate();
return initial_probe || bitrate_above_estimate;
}
@ -234,14 +222,15 @@ void RemoteBitrateEstimatorAbsSendTime::IncomingPacket(
"is missing absolute send time extension!";
return;
}
IncomingPacketInfo(arrival_time_ms, header.extension.absoluteSendTime,
payload_size, header.ssrc);
IncomingPacketInfo(Timestamp::Millis(arrival_time_ms),
header.extension.absoluteSendTime,
DataSize::Bytes(payload_size), header.ssrc);
}
void RemoteBitrateEstimatorAbsSendTime::IncomingPacketInfo(
int64_t arrival_time_ms,
Timestamp arrival_time,
uint32_t send_time_24bits,
size_t payload_size,
DataSize payload_size,
uint32_t ssrc) {
RTC_CHECK(send_time_24bits < (1ul << 24));
if (!uma_recorded_) {
@ -252,15 +241,16 @@ void RemoteBitrateEstimatorAbsSendTime::IncomingPacketInfo(
// Shift up send time to use the full 32 bits that inter_arrival works with,
// so wrapping works properly.
uint32_t timestamp = send_time_24bits << kAbsSendTimeInterArrivalUpshift;
int64_t send_time_ms = static_cast<int64_t>(timestamp) * kTimestampToMs;
Timestamp send_time =
Timestamp::Millis(static_cast<int64_t>(timestamp) * kTimestampToMs);
int64_t now_ms = clock_->TimeInMilliseconds();
Timestamp now = clock_->CurrentTime();
// TODO(holmer): SSRCs are only needed for REMB, should be broken out from
// here.
// Check if incoming bitrate estimate is valid, and if it needs to be reset.
absl::optional<uint32_t> incoming_bitrate =
incoming_bitrate_.Rate(arrival_time_ms);
incoming_bitrate_.Rate(arrival_time.ms());
if (incoming_bitrate) {
incoming_bitrate_initialized_ = true;
} else if (incoming_bitrate_initialized_) {
@ -270,74 +260,76 @@ void RemoteBitrateEstimatorAbsSendTime::IncomingPacketInfo(
incoming_bitrate_.Reset();
incoming_bitrate_initialized_ = false;
}
incoming_bitrate_.Update(payload_size, arrival_time_ms);
incoming_bitrate_.Update(payload_size.bytes(), arrival_time.ms());
if (first_packet_time_ms_ == -1)
first_packet_time_ms_ = now_ms;
if (first_packet_time_.IsInfinite()) {
first_packet_time_ = now;
}
uint32_t ts_delta = 0;
int64_t t_delta = 0;
int size_delta = 0;
bool update_estimate = false;
uint32_t target_bitrate_bps = 0;
DataRate target_bitrate = DataRate::Zero();
std::vector<uint32_t> ssrcs;
{
MutexLock lock(&mutex_);
TimeoutStreams(now_ms);
RTC_DCHECK(inter_arrival_.get());
RTC_DCHECK(estimator_.get());
ssrcs_[ssrc] = now_ms;
TimeoutStreams(now);
RTC_DCHECK(inter_arrival_);
RTC_DCHECK(estimator_);
ssrcs_.insert_or_assign(ssrc, now);
// For now only try to detect probes while we don't have a valid estimate.
// We currently assume that only packets larger than 200 bytes are paced by
// the sender.
const size_t kMinProbePacketSize = 200;
static constexpr DataSize kMinProbePacketSize = DataSize::Bytes(200);
if (payload_size > kMinProbePacketSize &&
(!remote_rate_.ValidEstimate() ||
now_ms - first_packet_time_ms_ < kInitialProbingIntervalMs)) {
now - first_packet_time_ < kInitialProbingInterval)) {
// TODO(holmer): Use a map instead to get correct order?
if (total_probes_received_ < kMaxProbePackets) {
int send_delta_ms = -1;
int recv_delta_ms = -1;
TimeDelta send_delta = TimeDelta::Millis(-1);
TimeDelta recv_delta = TimeDelta::Millis(-1);
if (!probes_.empty()) {
send_delta_ms = send_time_ms - probes_.back().send_time_ms;
recv_delta_ms = arrival_time_ms - probes_.back().recv_time_ms;
send_delta = send_time - probes_.back().send_time;
recv_delta = arrival_time - probes_.back().recv_time;
}
RTC_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.";
RTC_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));
probes_.emplace_back(send_time, arrival_time, payload_size);
++total_probes_received_;
// Make sure that a probe which updated the bitrate immediately has an
// effect by calling the OnReceiveBitrateChanged callback.
if (ProcessClusters(now_ms) == ProbeResult::kBitrateUpdated)
if (ProcessClusters(now) == ProbeResult::kBitrateUpdated)
update_estimate = true;
}
if (inter_arrival_->ComputeDeltas(timestamp, arrival_time_ms, now_ms,
payload_size, &ts_delta, &t_delta,
if (inter_arrival_->ComputeDeltas(timestamp, arrival_time.ms(), now.ms(),
payload_size.bytes(), &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(),
arrival_time_ms);
arrival_time.ms());
detector_.Detect(estimator_->offset(), ts_delta_ms,
estimator_->num_of_deltas(), arrival_time_ms);
estimator_->num_of_deltas(), arrival_time.ms());
}
if (!update_estimate) {
// Check if it's time for a periodic update or if we should update because
// of an over-use.
if (last_update_ms_ == -1 ||
now_ms - last_update_ms_ > remote_rate_.GetFeedbackInterval().ms()) {
if (last_update_.IsInfinite() ||
now.ms() - last_update_.ms() >
remote_rate_.GetFeedbackInterval().ms()) {
update_estimate = true;
} else if (detector_.State() == BandwidthUsage::kBwOverusing) {
absl::optional<uint32_t> incoming_rate =
incoming_bitrate_.Rate(arrival_time_ms);
incoming_bitrate_.Rate(arrival_time.ms());
if (incoming_rate && remote_rate_.TimeToReduceFurther(
Timestamp::Millis(now_ms),
DataRate::BitsPerSec(*incoming_rate))) {
now, DataRate::BitsPerSec(*incoming_rate))) {
update_estimate = true;
}
}
@ -348,31 +340,26 @@ void RemoteBitrateEstimatorAbsSendTime::IncomingPacketInfo(
// 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.
const RateControlInput input(
detector_.State(),
OptionalRateFromOptionalBps(incoming_bitrate_.Rate(arrival_time_ms)));
target_bitrate_bps =
remote_rate_.Update(&input, Timestamp::Millis(now_ms))
.bps<uint32_t>();
detector_.State(), OptionalRateFromOptionalBps(
incoming_bitrate_.Rate(arrival_time.ms())));
target_bitrate = remote_rate_.Update(&input, now);
update_estimate = remote_rate_.ValidEstimate();
ssrcs = Keys(ssrcs_);
}
}
if (update_estimate) {
last_update_ms_ = now_ms;
observer_->OnReceiveBitrateChanged(ssrcs, target_bitrate_bps);
last_update_ = now;
observer_->OnReceiveBitrateChanged(ssrcs, target_bitrate.bps<uint32_t>());
}
}
void RemoteBitrateEstimatorAbsSendTime::Process() {}
int64_t RemoteBitrateEstimatorAbsSendTime::TimeUntilNextProcess() {
const int64_t kDisabledModuleTime = 1000;
return kDisabledModuleTime;
TimeDelta RemoteBitrateEstimatorAbsSendTime::Process() {
return TimeDelta::PlusInfinity();
}
void RemoteBitrateEstimatorAbsSendTime::TimeoutStreams(int64_t now_ms) {
for (Ssrcs::iterator it = ssrcs_.begin(); it != ssrcs_.end();) {
if ((now_ms - it->second) > kStreamTimeOutMs) {
void RemoteBitrateEstimatorAbsSendTime::TimeoutStreams(Timestamp now) {
for (auto it = ssrcs_.begin(); it != ssrcs_.end();) {
if (now - it->second > TimeDelta::Millis(kStreamTimeOutMs)) {
ssrcs_.erase(it++);
} else {
++it;
@ -380,17 +367,17 @@ void RemoteBitrateEstimatorAbsSendTime::TimeoutStreams(int64_t now_ms) {
}
if (ssrcs_.empty()) {
// We can't update the estimate if we don't have any active streams.
inter_arrival_.reset(
new InterArrival((kTimestampGroupLengthMs << kInterArrivalShift) / 1000,
kTimestampToMs, true));
estimator_.reset(new OveruseEstimator(OverUseDetectorOptions()));
inter_arrival_ = std::make_unique<InterArrival>(
(kTimestampGroupLengthMs << kInterArrivalShift) / 1000, kTimestampToMs,
true);
estimator_ = std::make_unique<OveruseEstimator>(OverUseDetectorOptions());
// 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.
}
}
void RemoteBitrateEstimatorAbsSendTime::OnRttUpdate(int64_t avg_rtt_ms,
int64_t max_rtt_ms) {
int64_t /*max_rtt_ms*/) {
MutexLock lock(&mutex_);
remote_rate_.SetRtt(TimeDelta::Millis(avg_rtt_ms));
}
@ -400,32 +387,13 @@ void RemoteBitrateEstimatorAbsSendTime::RemoveStream(uint32_t ssrc) {
ssrcs_.erase(ssrc);
}
bool RemoteBitrateEstimatorAbsSendTime::LatestEstimate(
std::vector<uint32_t>* ssrcs,
uint32_t* bitrate_bps) const {
// Currently accessed from both the process thread (see
// ModuleRtpRtcpImpl::Process()) and the configuration thread (see
// Call::GetStats()). Should in the future only be accessed from a single
// thread.
RTC_DCHECK(ssrcs);
RTC_DCHECK(bitrate_bps);
DataRate RemoteBitrateEstimatorAbsSendTime::LatestEstimate() const {
// Currently accessed only from the worker thread (see Call::GetStats()).
MutexLock lock(&mutex_);
if (!remote_rate_.ValidEstimate()) {
return false;
if (!remote_rate_.ValidEstimate() || ssrcs_.empty()) {
return DataRate::Zero();
}
*ssrcs = Keys(ssrcs_);
if (ssrcs_.empty()) {
*bitrate_bps = 0;
} else {
*bitrate_bps = remote_rate_.LatestEstimate().bps<uint32_t>();
}
return true;
return remote_rate_.LatestEstimate();
}
void RemoteBitrateEstimatorAbsSendTime::SetMinBitrate(int min_bitrate_bps) {
// Called from both the configuration thread and the network thread. Shouldn't
// be called from the network thread in the future.
MutexLock lock(&mutex_);
remote_rate_.SetMinBitrate(DataRate::BitsPerSec(min_bitrate_bps));
}
} // namespace webrtc

View File

@ -21,13 +21,16 @@
#include "api/rtp_headers.h"
#include "api/transport/field_trial_based_config.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "modules/remote_bitrate_estimator/inter_arrival.h"
#include "modules/remote_bitrate_estimator/overuse_detector.h"
#include "modules/remote_bitrate_estimator/overuse_estimator.h"
#include "rtc_base/checks.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/race_checker.h"
#include "rtc_base/rate_statistics.h"
#include "rtc_base/synchronization/mutex.h"
@ -36,90 +39,76 @@
namespace webrtc {
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) {}
int GetSendBitrateBps() const {
RTC_CHECK_GT(send_mean_ms, 0.0f);
return mean_size * 8 * 1000 / send_mean_ms;
}
int GetRecvBitrateBps() const {
RTC_CHECK_GT(recv_mean_ms, 0.0f);
return mean_size * 8 * 1000 / recv_mean_ms;
}
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;
};
class RemoteBitrateEstimatorAbsSendTime : public RemoteBitrateEstimator {
public:
RemoteBitrateEstimatorAbsSendTime(RemoteBitrateObserver* observer,
Clock* clock);
RemoteBitrateEstimatorAbsSendTime() = delete;
RemoteBitrateEstimatorAbsSendTime(const RemoteBitrateEstimatorAbsSendTime&) =
delete;
RemoteBitrateEstimatorAbsSendTime& operator=(
const RemoteBitrateEstimatorAbsSendTime&) = delete;
~RemoteBitrateEstimatorAbsSendTime() override;
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.
void Process() override;
int64_t TimeUntilNextProcess() override;
TimeDelta Process() override;
void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override;
void RemoveStream(uint32_t ssrc) override;
bool LatestEstimate(std::vector<uint32_t>* ssrcs,
uint32_t* bitrate_bps) const override;
void SetMinBitrate(int min_bitrate_bps) override;
DataRate LatestEstimate() const override;
private:
typedef std::map<uint32_t, int64_t> Ssrcs;
struct Probe {
Probe(Timestamp send_time, Timestamp recv_time, DataSize payload_size)
: send_time(send_time),
recv_time(recv_time),
payload_size(payload_size) {}
Timestamp send_time;
Timestamp recv_time;
DataSize payload_size;
};
struct Cluster {
DataRate SendBitrate() const { return mean_size / send_mean; }
DataRate RecvBitrate() const { return mean_size / recv_mean; }
TimeDelta send_mean = TimeDelta::Zero();
TimeDelta recv_mean = TimeDelta::Zero();
// TODO(holmer): Add some variance metric as well?
DataSize mean_size = DataSize::Zero();
int count = 0;
int num_above_min_delta = 0;
};
enum class ProbeResult { kBitrateUpdated, kNoUpdate };
static bool IsWithinClusterBounds(int send_delta_ms,
static bool IsWithinClusterBounds(TimeDelta send_delta,
const Cluster& cluster_aggregate);
static void AddCluster(std::list<Cluster>* clusters, Cluster* cluster);
static void MaybeAddCluster(const Cluster& cluster_aggregate,
std::list<Cluster>& clusters);
void IncomingPacketInfo(int64_t arrival_time_ms,
void IncomingPacketInfo(Timestamp arrival_time,
uint32_t send_time_24bits,
size_t payload_size,
DataSize payload_size,
uint32_t ssrc);
void ComputeClusters(std::list<Cluster>* clusters) const;
std::list<Cluster> ComputeClusters() const;
std::list<Cluster>::const_iterator FindBestProbe(
const std::list<Cluster>& clusters) const;
const Cluster* FindBestProbe(const std::list<Cluster>& clusters) const;
// Returns true if a probe which changed the estimate was detected.
ProbeResult ProcessClusters(int64_t now_ms)
ProbeResult ProcessClusters(Timestamp now)
RTC_EXCLUSIVE_LOCKS_REQUIRED(&mutex_);
bool IsBitrateImproving(int probe_bitrate_bps) const
bool IsBitrateImproving(DataRate probe_bitrate) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(&mutex_);
void TimeoutStreams(int64_t now_ms) RTC_EXCLUSIVE_LOCKS_REQUIRED(&mutex_);
void TimeoutStreams(Timestamp now) RTC_EXCLUSIVE_LOCKS_REQUIRED(&mutex_);
rtc::RaceChecker network_race_;
Clock* const clock_;
@ -128,21 +117,17 @@ class RemoteBitrateEstimatorAbsSendTime : public RemoteBitrateEstimator {
std::unique_ptr<InterArrival> inter_arrival_;
std::unique_ptr<OveruseEstimator> estimator_;
OveruseDetector detector_;
RateStatistics incoming_bitrate_;
bool incoming_bitrate_initialized_;
std::vector<int> recent_propagation_delta_ms_;
std::vector<int64_t> recent_update_time_ms_;
RateStatistics incoming_bitrate_{kBitrateWindowMs, 8000};
bool incoming_bitrate_initialized_ = false;
std::list<Probe> probes_;
size_t total_probes_received_;
int64_t first_packet_time_ms_;
int64_t last_update_ms_;
bool uma_recorded_;
size_t total_probes_received_ = 0;
Timestamp first_packet_time_ = Timestamp::MinusInfinity();
Timestamp last_update_ = Timestamp::MinusInfinity();
bool uma_recorded_ = false;
mutable Mutex mutex_;
Ssrcs ssrcs_ RTC_GUARDED_BY(&mutex_);
std::map<uint32_t, Timestamp> ssrcs_ RTC_GUARDED_BY(&mutex_);
AimdRateControl remote_rate_ RTC_GUARDED_BY(&mutex_);
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RemoteBitrateEstimatorAbsSendTime);
};
} // namespace webrtc

View File

@ -11,7 +11,6 @@
#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h"
#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h"
#include "rtc_base/constructor_magic.h"
#include "test/gtest.h"
namespace webrtc {
@ -20,13 +19,18 @@ class RemoteBitrateEstimatorAbsSendTimeTest
: public RemoteBitrateEstimatorTest {
public:
RemoteBitrateEstimatorAbsSendTimeTest() {}
RemoteBitrateEstimatorAbsSendTimeTest(
const RemoteBitrateEstimatorAbsSendTimeTest&) = delete;
RemoteBitrateEstimatorAbsSendTimeTest& operator=(
const RemoteBitrateEstimatorAbsSendTimeTest&) = delete;
virtual void SetUp() {
bitrate_estimator_.reset(new RemoteBitrateEstimatorAbsSendTime(
bitrate_observer_.get(), &clock_));
}
protected:
RTC_DISALLOW_COPY_AND_ASSIGN(RemoteBitrateEstimatorAbsSendTimeTest);
};
TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, InitialBehavior) {

View File

@ -10,7 +10,6 @@
#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h"
#include <assert.h>
#include <cstdint>
#include <utility>
@ -45,7 +44,7 @@ struct RemoteBitrateEstimatorSingleStream::Detector {
explicit Detector(int64_t last_packet_time_ms,
const OverUseDetectorOptions& options,
bool enable_burst_grouping,
const WebRtcKeyValueConfig* key_value_config)
const FieldTrialsView* key_value_config)
: last_packet_time_ms(last_packet_time_ms),
inter_arrival(90 * kTimestampGroupLengthMs,
kTimestampToMs,
@ -64,7 +63,7 @@ RemoteBitrateEstimatorSingleStream::RemoteBitrateEstimatorSingleStream(
: clock_(clock),
incoming_bitrate_(kBitrateWindowMs, 8000),
last_valid_incoming_bitrate_(0),
remote_rate_(new AimdRateControl(&field_trials_)),
remote_rate_(&field_trials_),
observer_(observer),
last_process_time_(-1),
process_interval_ms_(kProcessIntervalMs),
@ -145,7 +144,7 @@ void RemoteBitrateEstimatorSingleStream::IncomingPacket(
incoming_bitrate_.Rate(now_ms);
if (incoming_bitrate_bps &&
(prior_state != BandwidthUsage::kBwOverusing ||
GetRemoteRate()->TimeToReduceFurther(
remote_rate_.TimeToReduceFurther(
Timestamp::Millis(now_ms),
DataRate::BitsPerSec(*incoming_bitrate_bps)))) {
// The first overuse should immediately trigger a new estimate.
@ -156,22 +155,17 @@ void RemoteBitrateEstimatorSingleStream::IncomingPacket(
}
}
void RemoteBitrateEstimatorSingleStream::Process() {
{
MutexLock lock(&mutex_);
UpdateEstimate(clock_->TimeInMilliseconds());
TimeDelta RemoteBitrateEstimatorSingleStream::Process() {
MutexLock lock(&mutex_);
int64_t now_ms = clock_->TimeInMilliseconds();
int64_t next_process_time_ms = last_process_time_ + process_interval_ms_;
if (last_process_time_ == -1 || now_ms >= next_process_time_ms) {
UpdateEstimate(now_ms);
last_process_time_ = now_ms;
return TimeDelta::Millis(process_interval_ms_);
}
last_process_time_ = clock_->TimeInMilliseconds();
}
int64_t RemoteBitrateEstimatorSingleStream::TimeUntilNextProcess() {
if (last_process_time_ < 0) {
return 0;
}
MutexLock lock_(&mutex_);
RTC_DCHECK_GT(process_interval_ms_, 0);
return last_process_time_ + process_interval_ms_ -
clock_->TimeInMilliseconds();
return TimeDelta::Millis(next_process_time_ms - now_ms);
}
void RemoteBitrateEstimatorSingleStream::UpdateEstimate(int64_t now_ms) {
@ -182,7 +176,7 @@ void RemoteBitrateEstimatorSingleStream::UpdateEstimate(int64_t now_ms) {
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|
// This over-use detector hasn't received packets for `kStreamTimeOutMs`
// milliseconds and is considered stale.
delete it->second;
overuse_detectors_.erase(it++);
@ -199,14 +193,13 @@ void RemoteBitrateEstimatorSingleStream::UpdateEstimate(int64_t now_ms) {
if (overuse_detectors_.empty()) {
return;
}
AimdRateControl* remote_rate = GetRemoteRate();
const RateControlInput input(
bw_state, OptionalRateFromOptionalBps(incoming_bitrate_.Rate(now_ms)));
uint32_t target_bitrate =
remote_rate->Update(&input, Timestamp::Millis(now_ms)).bps<uint32_t>();
if (remote_rate->ValidEstimate()) {
process_interval_ms_ = remote_rate->GetFeedbackInterval().ms();
remote_rate_.Update(&input, Timestamp::Millis(now_ms)).bps<uint32_t>();
if (remote_rate_.ValidEstimate()) {
process_interval_ms_ = remote_rate_.GetFeedbackInterval().ms();
RTC_DCHECK_GT(process_interval_ms_, 0);
std::vector<uint32_t> ssrcs;
GetSsrcs(&ssrcs);
@ -218,7 +211,7 @@ void RemoteBitrateEstimatorSingleStream::UpdateEstimate(int64_t now_ms) {
void RemoteBitrateEstimatorSingleStream::OnRttUpdate(int64_t avg_rtt_ms,
int64_t max_rtt_ms) {
MutexLock lock(&mutex_);
GetRemoteRate()->SetRtt(TimeDelta::Millis(avg_rtt_ms));
remote_rate_.SetRtt(TimeDelta::Millis(avg_rtt_ms));
}
void RemoteBitrateEstimatorSingleStream::RemoveStream(unsigned int ssrc) {
@ -230,25 +223,17 @@ void RemoteBitrateEstimatorSingleStream::RemoveStream(unsigned int ssrc) {
}
}
bool RemoteBitrateEstimatorSingleStream::LatestEstimate(
std::vector<uint32_t>* ssrcs,
uint32_t* bitrate_bps) const {
DataRate RemoteBitrateEstimatorSingleStream::LatestEstimate() const {
MutexLock lock(&mutex_);
assert(bitrate_bps);
if (!remote_rate_->ValidEstimate()) {
return false;
if (!remote_rate_.ValidEstimate() || overuse_detectors_.empty()) {
return DataRate::Zero();
}
GetSsrcs(ssrcs);
if (ssrcs->empty())
*bitrate_bps = 0;
else
*bitrate_bps = remote_rate_->LatestEstimate().bps<uint32_t>();
return true;
return remote_rate_.LatestEstimate();
}
void RemoteBitrateEstimatorSingleStream::GetSsrcs(
std::vector<uint32_t>* ssrcs) const {
assert(ssrcs);
RTC_DCHECK(ssrcs);
ssrcs->resize(overuse_detectors_.size());
int i = 0;
for (SsrcOveruseEstimatorMap::const_iterator it = overuse_detectors_.begin();
@ -257,15 +242,4 @@ void RemoteBitrateEstimatorSingleStream::GetSsrcs(
}
}
AimdRateControl* RemoteBitrateEstimatorSingleStream::GetRemoteRate() {
if (!remote_rate_)
remote_rate_.reset(new AimdRateControl(&field_trials_));
return remote_rate_.get();
}
void RemoteBitrateEstimatorSingleStream::SetMinBitrate(int min_bitrate_bps) {
MutexLock lock(&mutex_);
remote_rate_->SetMinBitrate(DataRate::BitsPerSec(min_bitrate_bps));
}
} // namespace webrtc

View File

@ -15,13 +15,14 @@
#include <stdint.h>
#include <map>
#include <memory>
#include <vector>
#include "api/transport/field_trial_based_config.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/rate_statistics.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
@ -35,18 +36,22 @@ class RemoteBitrateEstimatorSingleStream : public RemoteBitrateEstimator {
public:
RemoteBitrateEstimatorSingleStream(RemoteBitrateObserver* observer,
Clock* clock);
RemoteBitrateEstimatorSingleStream() = delete;
RemoteBitrateEstimatorSingleStream(
const RemoteBitrateEstimatorSingleStream&) = delete;
RemoteBitrateEstimatorSingleStream& operator=(
const RemoteBitrateEstimatorSingleStream&) = delete;
~RemoteBitrateEstimatorSingleStream() override;
void IncomingPacket(int64_t arrival_time_ms,
size_t payload_size,
const RTPHeader& header) override;
void Process() override;
int64_t TimeUntilNextProcess() override;
TimeDelta Process() override;
void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override;
void RemoveStream(uint32_t ssrc) override;
bool LatestEstimate(std::vector<uint32_t>* ssrcs,
uint32_t* bitrate_bps) const override;
void SetMinBitrate(int min_bitrate_bps) override;
DataRate LatestEstimate() const override;
private:
struct Detector;
@ -59,23 +64,17 @@ class RemoteBitrateEstimatorSingleStream : public RemoteBitrateEstimator {
void GetSsrcs(std::vector<uint32_t>* ssrcs) const
RTC_SHARED_LOCKS_REQUIRED(mutex_);
// Returns |remote_rate_| if the pointed to object exists,
// otherwise creates it.
AimdRateControl* GetRemoteRate() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
Clock* const clock_;
const FieldTrialBasedConfig field_trials_;
SsrcOveruseEstimatorMap overuse_detectors_ RTC_GUARDED_BY(mutex_);
RateStatistics incoming_bitrate_ RTC_GUARDED_BY(mutex_);
uint32_t last_valid_incoming_bitrate_ RTC_GUARDED_BY(mutex_);
std::unique_ptr<AimdRateControl> remote_rate_ RTC_GUARDED_BY(mutex_);
AimdRateControl remote_rate_ RTC_GUARDED_BY(mutex_);
RemoteBitrateObserver* const observer_ RTC_GUARDED_BY(mutex_);
mutable Mutex mutex_;
int64_t last_process_time_;
int64_t process_interval_ms_ RTC_GUARDED_BY(mutex_);
bool uma_recorded_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RemoteBitrateEstimatorSingleStream);
};
} // namespace webrtc

View File

@ -11,7 +11,6 @@
#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h"
#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h"
#include "rtc_base/constructor_magic.h"
#include "test/gtest.h"
namespace webrtc {
@ -19,13 +18,18 @@ namespace webrtc {
class RemoteBitrateEstimatorSingleTest : public RemoteBitrateEstimatorTest {
public:
RemoteBitrateEstimatorSingleTest() {}
RemoteBitrateEstimatorSingleTest(const RemoteBitrateEstimatorSingleTest&) =
delete;
RemoteBitrateEstimatorSingleTest& operator=(
const RemoteBitrateEstimatorSingleTest&) = delete;
virtual void SetUp() {
bitrate_estimator_.reset(new RemoteBitrateEstimatorSingleStream(
bitrate_observer_.get(), &clock_));
}
protected:
RTC_DISALLOW_COPY_AND_ASSIGN(RemoteBitrateEstimatorSingleTest);
};
TEST_F(RemoteBitrateEstimatorSingleTest, InitialBehavior) {

View File

@ -46,7 +46,7 @@ RtpStream::RtpStream(int fps,
next_rtcp_time_(rtcp_receive_time),
rtp_timestamp_offset_(timestamp_offset),
kNtpFracPerMs(4.294967296E6) {
assert(fps_ > 0);
RTC_DCHECK_GT(fps_, 0);
}
void RtpStream::set_rtp_timestamp_offset(uint32_t offset) {
@ -60,7 +60,7 @@ int64_t RtpStream::GenerateFrame(int64_t time_now_us, PacketList* packets) {
if (time_now_us < next_rtp_time_) {
return next_rtp_time_;
}
assert(packets != NULL);
RTC_DCHECK(packets);
size_t bits_per_frame = (bitrate_bps_ + fps_ / 2) / fps_;
size_t n_packets =
std::max<size_t>((bits_per_frame + 4 * kMtu) / (8 * kMtu), 1u);
@ -142,7 +142,7 @@ void StreamGenerator::set_capacity_bps(int capacity_bps) {
capacity_ = capacity_bps;
}
// Divides |bitrate_bps| among all streams. The allocated bitrate per stream
// Divides `bitrate_bps` among all streams. The allocated bitrate per stream
// is decided by the current allocation ratios.
void StreamGenerator::SetBitrateBps(int bitrate_bps) {
ASSERT_GE(streams_.size(), 0u);
@ -164,7 +164,7 @@ void StreamGenerator::SetBitrateBps(int bitrate_bps) {
EXPECT_EQ(total_bitrate_after, bitrate_bps);
}
// Set the RTP timestamp offset for the stream identified by |ssrc|.
// Set the RTP timestamp offset for the stream identified by `ssrc`.
void StreamGenerator::set_rtp_timestamp_offset(uint32_t ssrc, uint32_t offset) {
streams_[ssrc]->set_rtp_timestamp_offset(offset);
}
@ -173,13 +173,12 @@ void StreamGenerator::set_rtp_timestamp_offset(uint32_t ssrc, uint32_t offset) {
// it possible to simulate different types of channels.
int64_t StreamGenerator::GenerateFrame(RtpStream::PacketList* packets,
int64_t time_now_us) {
assert(packets != NULL);
assert(packets->empty());
assert(capacity_ > 0);
RTC_DCHECK(packets);
RTC_DCHECK(packets->empty());
RTC_DCHECK_GT(capacity_, 0);
StreamMap::iterator it =
std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare);
(*it).second->GenerateFrame(time_now_us, packets);
int i = 0;
for (RtpStream::PacketList::iterator packet_it = packets->begin();
packet_it != packets->end(); ++packet_it) {
int capacity_bpus = capacity_ / 1000;
@ -189,7 +188,6 @@ int64_t StreamGenerator::GenerateFrame(RtpStream::PacketList* packets,
std::max(time_now_us + required_network_time_us,
prev_arrival_time_us_ + required_network_time_us);
(*packet_it)->arrival_time = prev_arrival_time_us_;
++i;
}
it = std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare);
return std::max((*it).second->next_rtp_time(), time_now_us);
@ -273,14 +271,13 @@ bool RemoteBitrateEstimatorTest::GenerateAndProcessFrame(uint32_t ssrc,
delete packet;
packets.pop_front();
}
if (bitrate_estimator_->TimeUntilNextProcess() <= 0)
bitrate_estimator_->Process();
bitrate_estimator_->Process();
clock_.AdvanceTimeMicroseconds(next_time_us - clock_.TimeInMicroseconds());
return overuse;
}
// Run the bandwidth estimator with a stream of |number_of_frames| frames, or
// until it reaches |target_bitrate|.
// Run the bandwidth estimator with a stream of `number_of_frames` frames, or
// until it reaches `target_bitrate`.
// Can for instance be used to run the estimator for some time to get it
// into a steady state.
uint32_t RemoteBitrateEstimatorTest::SteadyStateRun(uint32_t ssrc,
@ -291,7 +288,7 @@ uint32_t RemoteBitrateEstimatorTest::SteadyStateRun(uint32_t ssrc,
uint32_t target_bitrate) {
uint32_t bitrate_bps = start_bitrate;
bool bitrate_update_seen = false;
// Produce |number_of_frames| frames and give them to the estimator.
// Produce `number_of_frames` frames and give them to the estimator.
for (int i = 0; i < max_number_of_frames; ++i) {
bool overuse = GenerateAndProcessFrame(ssrc, bitrate_bps);
if (overuse) {
@ -316,15 +313,12 @@ void RemoteBitrateEstimatorTest::InitialBehaviorTestHelper(
const int kFramerate = 50; // 50 fps to avoid rounding errors.
const int kFrameIntervalMs = 1000 / kFramerate;
const uint32_t kFrameIntervalAbsSendTime = AbsSendTime(1, kFramerate);
uint32_t bitrate_bps = 0;
uint32_t timestamp = 0;
uint32_t absolute_send_time = 0;
std::vector<uint32_t> ssrcs;
EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_bps));
EXPECT_EQ(0u, ssrcs.size());
EXPECT_EQ(bitrate_estimator_->LatestEstimate(), DataRate::Zero());
clock_.AdvanceTimeMilliseconds(1000);
bitrate_estimator_->Process();
EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_bps));
EXPECT_EQ(bitrate_estimator_->LatestEstimate(), DataRate::Zero());
EXPECT_FALSE(bitrate_observer_->updated());
bitrate_observer_->Reset();
clock_.AdvanceTimeMilliseconds(1000);
@ -332,8 +326,7 @@ void RemoteBitrateEstimatorTest::InitialBehaviorTestHelper(
for (int i = 0; i < 5 * kFramerate + 1 + kNumInitialPackets; ++i) {
if (i == kNumInitialPackets) {
bitrate_estimator_->Process();
EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_bps));
EXPECT_EQ(0u, ssrcs.size());
EXPECT_EQ(bitrate_estimator_->LatestEstimate(), DataRate::Zero());
EXPECT_FALSE(bitrate_observer_->updated());
bitrate_observer_->Reset();
}
@ -346,17 +339,13 @@ void RemoteBitrateEstimatorTest::InitialBehaviorTestHelper(
AddAbsSendTime(absolute_send_time, kFrameIntervalAbsSendTime);
}
bitrate_estimator_->Process();
EXPECT_TRUE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_bps));
ASSERT_EQ(1u, ssrcs.size());
EXPECT_EQ(kDefaultSsrc, ssrcs.front());
uint32_t bitrate_bps = bitrate_estimator_->LatestEstimate().bps<uint32_t>();
EXPECT_NEAR(expected_converge_bitrate, bitrate_bps, kAcceptedBitrateErrorBps);
EXPECT_TRUE(bitrate_observer_->updated());
bitrate_observer_->Reset();
EXPECT_EQ(bitrate_observer_->latest_bitrate(), bitrate_bps);
bitrate_estimator_->RemoveStream(kDefaultSsrc);
EXPECT_TRUE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_bps));
ASSERT_EQ(0u, ssrcs.size());
EXPECT_EQ(0u, bitrate_bps);
EXPECT_EQ(bitrate_estimator_->LatestEstimate(), DataRate::Zero());
}
void RemoteBitrateEstimatorTest::RateIncreaseReorderingTestHelper(
@ -507,20 +496,11 @@ void RemoteBitrateEstimatorTest::CapacityDropTestHelper(
bitrate_drop_time - overuse_start_time, 33);
// Remove stream one by one.
uint32_t latest_bps = 0;
std::vector<uint32_t> ssrcs;
for (int i = 0; i < number_of_streams; i++) {
EXPECT_TRUE(bitrate_estimator_->LatestEstimate(&ssrcs, &latest_bps));
EXPECT_EQ(number_of_streams - i, static_cast<int>(ssrcs.size()));
EXPECT_EQ(bitrate_bps, latest_bps);
for (int j = i; j < number_of_streams; j++) {
EXPECT_EQ(kDefaultSsrc + j, ssrcs[j - i]);
}
EXPECT_EQ(bitrate_estimator_->LatestEstimate().bps(), bitrate_bps);
bitrate_estimator_->RemoveStream(kDefaultSsrc + i);
}
EXPECT_TRUE(bitrate_estimator_->LatestEstimate(&ssrcs, &latest_bps));
EXPECT_EQ(0u, ssrcs.size());
EXPECT_EQ(0u, latest_bps);
EXPECT_EQ(bitrate_estimator_->LatestEstimate(), DataRate::Zero());
}
void RemoteBitrateEstimatorTest::TestTimestampGroupingTestHelper() {
@ -554,7 +534,7 @@ void RemoteBitrateEstimatorTest::TestTimestampGroupingTestHelper() {
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
// 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);
@ -593,9 +573,7 @@ void RemoteBitrateEstimatorTest::TestWrappingHelper(int silence_time_s) {
AddAbsSendTime(absolute_send_time, kFrameIntervalAbsSendTime);
bitrate_estimator_->Process();
}
uint32_t bitrate_before = 0;
std::vector<uint32_t> ssrcs;
bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_before);
DataRate bitrate_before = bitrate_estimator_->LatestEstimate();
clock_.AdvanceTimeMilliseconds(silence_time_s * 1000);
absolute_send_time =
@ -610,8 +588,7 @@ void RemoteBitrateEstimatorTest::TestWrappingHelper(int silence_time_s) {
AddAbsSendTime(absolute_send_time, kFrameIntervalAbsSendTime);
bitrate_estimator_->Process();
}
uint32_t bitrate_after = 0;
bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_after);
DataRate bitrate_after = bitrate_estimator_->LatestEstimate();
EXPECT_LT(bitrate_after, bitrate_before);
}
} // namespace webrtc

View File

@ -18,7 +18,6 @@
#include <vector>
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "rtc_base/constructor_magic.h"
#include "system_wrappers/include/clock.h"
#include "test/gtest.h"
@ -71,6 +70,10 @@ class RtpStream {
uint32_t frequency,
uint32_t timestamp_offset,
int64_t rtcp_receive_time);
RtpStream(const RtpStream&) = delete;
RtpStream& operator=(const RtpStream&) = delete;
void set_rtp_timestamp_offset(uint32_t offset);
// Generates a new frame for this stream. If called too soon after the
@ -104,8 +107,6 @@ class RtpStream {
int64_t next_rtcp_time_;
uint32_t rtp_timestamp_offset_;
const double kNtpFracPerMs;
RTC_DISALLOW_COPY_AND_ASSIGN(RtpStream);
};
class StreamGenerator {
@ -116,17 +117,20 @@ class StreamGenerator {
~StreamGenerator();
StreamGenerator(const StreamGenerator&) = delete;
StreamGenerator& operator=(const StreamGenerator&) = delete;
// Add a new stream.
void AddStream(RtpStream* stream);
// Set the link capacity.
void set_capacity_bps(int capacity_bps);
// Divides |bitrate_bps| among all streams. The allocated bitrate per stream
// Divides `bitrate_bps` among all streams. The allocated bitrate per stream
// is decided by the initial allocation ratios.
void SetBitrateBps(int bitrate_bps);
// Set the RTP timestamp offset for the stream identified by |ssrc|.
// Set the RTP timestamp offset for the stream identified by `ssrc`.
void set_rtp_timestamp_offset(uint32_t ssrc, uint32_t offset);
// TODO(holmer): Break out the channel simulation part from this class to make
@ -142,8 +146,6 @@ class StreamGenerator {
int64_t prev_arrival_time_us_;
// All streams being transmitted on this simulated channel.
StreamMap streams_;
RTC_DISALLOW_COPY_AND_ASSIGN(StreamGenerator);
};
} // namespace testing
@ -152,15 +154,19 @@ class RemoteBitrateEstimatorTest : public ::testing::Test {
RemoteBitrateEstimatorTest();
virtual ~RemoteBitrateEstimatorTest();
RemoteBitrateEstimatorTest(const RemoteBitrateEstimatorTest&) = delete;
RemoteBitrateEstimatorTest& operator=(const RemoteBitrateEstimatorTest&) =
delete;
protected:
virtual void SetUp() = 0;
void AddDefaultStream();
// Helper to convert some time format to resolution used in absolute send time
// header extension, rounded upwards. |t| is the time to convert, in some
// resolution. |denom| is the value to divide |t| by to get whole seconds,
// e.g. |denom| = 1000 if |t| is in milliseconds.
// header extension, rounded upwards. `t` is the time to convert, in some
// resolution. `denom` is the value to divide `t` by to get whole seconds,
// e.g. `denom` = 1000 if `t` is in milliseconds.
static uint32_t AbsSendTime(int64_t t, int64_t denom);
// Helper to add two absolute send time values and keep it less than 1<<24.
@ -183,8 +189,8 @@ class RemoteBitrateEstimatorTest : public ::testing::Test {
// target bitrate after the call to this function.
bool GenerateAndProcessFrame(uint32_t ssrc, uint32_t bitrate_bps);
// Run the bandwidth estimator with a stream of |number_of_frames| frames, or
// until it reaches |target_bitrate|.
// Run the bandwidth estimator with a stream of `number_of_frames` frames, or
// until it reaches `target_bitrate`.
// Can for instance be used to run the estimator for some time to get it
// into a steady state.
uint32_t SteadyStateRun(uint32_t ssrc,
@ -213,8 +219,6 @@ class RemoteBitrateEstimatorTest : public ::testing::Test {
std::unique_ptr<RemoteBitrateEstimator> bitrate_estimator_;
std::unique_ptr<testing::StreamGenerator> stream_generator_;
int64_t arrival_time_offset_ms_;
RTC_DISALLOW_COPY_AND_ASSIGN(RemoteBitrateEstimatorTest);
};
} // namespace webrtc

View File

@ -15,6 +15,8 @@
#include <memory>
#include <utility>
#include "api/units/data_size.h"
#include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
@ -22,138 +24,146 @@
#include "system_wrappers/include/clock.h"
namespace webrtc {
// Impossible to request feedback older than what can be represented by 15 bits.
const int RemoteEstimatorProxy::kMaxNumberOfPackets = (1 << 15);
namespace {
// The maximum allowed value for a timestamp in milliseconds. This is lower
// than the numerical limit since we often convert to microseconds.
static constexpr int64_t kMaxTimeMs =
std::numeric_limits<int64_t>::max() / 1000;
constexpr int64_t kMaxTimeMs = std::numeric_limits<int64_t>::max() / 1000;
constexpr TimeDelta kBackWindow = TimeDelta::Millis(500);
constexpr TimeDelta kMinInterval = TimeDelta::Millis(50);
constexpr TimeDelta kMaxInterval = TimeDelta::Millis(250);
constexpr TimeDelta kDefaultInterval = TimeDelta::Millis(100);
TimeDelta GetAbsoluteSendTimeDelta(uint32_t new_sendtime,
uint32_t previous_sendtime) {
static constexpr uint32_t kWrapAroundPeriod = 0x0100'0000;
RTC_DCHECK_LT(new_sendtime, kWrapAroundPeriod);
RTC_DCHECK_LT(previous_sendtime, kWrapAroundPeriod);
uint32_t delta = (new_sendtime - previous_sendtime) % kWrapAroundPeriod;
if (delta >= kWrapAroundPeriod / 2) {
// absolute send time wraps around, thus treat deltas larger than half of
// the wrap around period as negative. Ignore reordering of packets and
// treat them as they have approximately the same send time.
return TimeDelta::Zero();
}
return TimeDelta::Micros(int64_t{delta} * 1'000'000 / (1 << 18));
}
} // namespace
RemoteEstimatorProxy::RemoteEstimatorProxy(
Clock* clock,
TransportFeedbackSenderInterface* feedback_sender,
const WebRtcKeyValueConfig* key_value_config,
TransportFeedbackSender feedback_sender,
NetworkStateEstimator* network_state_estimator)
: clock_(clock),
feedback_sender_(feedback_sender),
send_config_(key_value_config),
last_process_time_ms_(-1),
: feedback_sender_(std::move(feedback_sender)),
last_process_time_(Timestamp::MinusInfinity()),
network_state_estimator_(network_state_estimator),
media_ssrc_(0),
feedback_packet_count_(0),
send_interval_ms_(send_config_.default_interval->ms()),
packet_overhead_(DataSize::Zero()),
send_interval_(kDefaultInterval),
send_periodic_feedback_(true),
previous_abs_send_time_(0),
abs_send_timestamp_(clock->CurrentTime()) {
abs_send_timestamp_(Timestamp::Zero()) {
RTC_LOG(LS_INFO)
<< "Maximum interval between transport feedback RTCP messages (ms): "
<< send_config_.max_interval->ms();
<< "Maximum interval between transport feedback RTCP messages: "
<< kMaxInterval;
}
RemoteEstimatorProxy::~RemoteEstimatorProxy() {}
void RemoteEstimatorProxy::MaybeCullOldPackets(int64_t sequence_number,
Timestamp arrival_time) {
if (periodic_window_start_seq_ >=
packet_arrival_times_.end_sequence_number() &&
arrival_time - Timestamp::Zero() >= kBackWindow) {
// Start new feedback packet, cull old packets.
packet_arrival_times_.RemoveOldPackets(sequence_number,
arrival_time - kBackWindow);
}
}
void RemoteEstimatorProxy::IncomingPacket(int64_t arrival_time_ms,
size_t payload_size,
const RTPHeader& header) {
if (arrival_time_ms < 0 || arrival_time_ms > kMaxTimeMs) {
if (arrival_time_ms < 0 || arrival_time_ms >= kMaxTimeMs) {
RTC_LOG(LS_WARNING) << "Arrival time out of bounds: " << arrival_time_ms;
return;
}
Packet packet = {.arrival_time = Timestamp::Millis(arrival_time_ms),
.size = DataSize::Bytes(header.headerLength + payload_size),
.ssrc = header.ssrc};
if (header.extension.hasTransportSequenceNumber) {
packet.transport_sequence_number = header.extension.transportSequenceNumber;
}
if (header.extension.hasAbsoluteSendTime) {
packet.absolute_send_time_24bits = header.extension.absoluteSendTime;
}
packet.feedback_request = header.extension.feedback_request;
IncomingPacket(packet);
}
void RemoteEstimatorProxy::IncomingPacket(Packet packet) {
MutexLock lock(&lock_);
media_ssrc_ = header.ssrc;
media_ssrc_ = packet.ssrc;
int64_t seq = 0;
if (header.extension.hasTransportSequenceNumber) {
seq = unwrapper_.Unwrap(header.extension.transportSequenceNumber);
if (packet.transport_sequence_number.has_value()) {
seq = unwrapper_.Unwrap(*packet.transport_sequence_number);
if (send_periodic_feedback_) {
if (periodic_window_start_seq_ &&
packet_arrival_times_.lower_bound(*periodic_window_start_seq_) ==
packet_arrival_times_.end()) {
// Start new feedback packet, cull old packets.
for (auto it = packet_arrival_times_.begin();
it != packet_arrival_times_.end() && it->first < seq &&
arrival_time_ms - it->second >= send_config_.back_window->ms();) {
it = packet_arrival_times_.erase(it);
}
}
MaybeCullOldPackets(seq, packet.arrival_time);
if (!periodic_window_start_seq_ || seq < *periodic_window_start_seq_) {
periodic_window_start_seq_ = seq;
}
}
// We are only interested in the first time a packet is received.
if (packet_arrival_times_.find(seq) != packet_arrival_times_.end())
if (packet_arrival_times_.has_received(seq)) {
return;
packet_arrival_times_[seq] = arrival_time_ms;
// Limit the range of sequence numbers to send feedback for.
auto first_arrival_time_to_keep = packet_arrival_times_.lower_bound(
packet_arrival_times_.rbegin()->first - kMaxNumberOfPackets);
if (first_arrival_time_to_keep != packet_arrival_times_.begin()) {
packet_arrival_times_.erase(packet_arrival_times_.begin(),
first_arrival_time_to_keep);
if (send_periodic_feedback_) {
// |packet_arrival_times_| cannot be empty since we just added one
// element and the last element is not deleted.
RTC_DCHECK(!packet_arrival_times_.empty());
periodic_window_start_seq_ = packet_arrival_times_.begin()->first;
}
}
if (header.extension.feedback_request) {
packet_arrival_times_.AddPacket(seq, packet.arrival_time);
// Limit the range of sequence numbers to send feedback for.
if (!periodic_window_start_seq_.has_value() ||
periodic_window_start_seq_.value() <
packet_arrival_times_.begin_sequence_number()) {
periodic_window_start_seq_ =
packet_arrival_times_.begin_sequence_number();
}
if (packet.feedback_request) {
// Send feedback packet immediately.
SendFeedbackOnRequest(seq, header.extension.feedback_request.value());
SendFeedbackOnRequest(seq, *packet.feedback_request);
}
}
if (network_state_estimator_ && header.extension.hasAbsoluteSendTime) {
if (network_state_estimator_ && packet.absolute_send_time_24bits) {
PacketResult packet_result;
packet_result.receive_time = Timestamp::Millis(arrival_time_ms);
// Ignore reordering of packets and assume they have approximately the same
// send time.
abs_send_timestamp_ += std::max(
header.extension.GetAbsoluteSendTimeDelta(previous_abs_send_time_),
TimeDelta::Millis(0));
previous_abs_send_time_ = header.extension.absoluteSendTime;
packet_result.receive_time = packet.arrival_time;
abs_send_timestamp_ += GetAbsoluteSendTimeDelta(
*packet.absolute_send_time_24bits, previous_abs_send_time_);
previous_abs_send_time_ = *packet.absolute_send_time_24bits;
packet_result.sent_packet.send_time = abs_send_timestamp_;
// TODO(webrtc:10742): Take IP header and transport overhead into account.
packet_result.sent_packet.size =
DataSize::Bytes(header.headerLength + payload_size);
packet_result.sent_packet.size = packet.size + packet_overhead_;
packet_result.sent_packet.sequence_number = seq;
network_state_estimator_->OnReceivedPacket(packet_result);
}
}
bool RemoteEstimatorProxy::LatestEstimate(std::vector<unsigned int>* ssrcs,
unsigned int* bitrate_bps) const {
return false;
}
int64_t RemoteEstimatorProxy::TimeUntilNextProcess() {
TimeDelta RemoteEstimatorProxy::Process(Timestamp now) {
MutexLock lock(&lock_);
if (!send_periodic_feedback_) {
// Wait a day until next process.
return 24 * 60 * 60 * 1000;
} else if (last_process_time_ms_ != -1) {
int64_t now = clock_->TimeInMilliseconds();
if (now - last_process_time_ms_ < send_interval_ms_)
return last_process_time_ms_ + send_interval_ms_ - now;
return TimeDelta::PlusInfinity();
}
return 0;
}
void RemoteEstimatorProxy::Process() {
MutexLock lock(&lock_);
if (!send_periodic_feedback_) {
return;
Timestamp next_process_time = last_process_time_ + send_interval_;
if (now >= next_process_time) {
last_process_time_ = now;
SendPeriodicFeedbacks();
return send_interval_;
}
last_process_time_ms_ = clock_->TimeInMilliseconds();
SendPeriodicFeedbacks();
return next_process_time - now;
}
void RemoteEstimatorProxy::OnBitrateChanged(int bitrate_bps) {
@ -162,18 +172,21 @@ void RemoteEstimatorProxy::OnBitrateChanged(int bitrate_bps) {
// TwccReport size at 50ms interval is 24 byte.
// TwccReport size at 250ms interval is 36 byte.
// AverageTwccReport = (TwccReport(50ms) + TwccReport(250ms)) / 2
constexpr int kTwccReportSize = 20 + 8 + 10 + 30;
const double kMinTwccRate =
kTwccReportSize * 8.0 * 1000.0 / send_config_.max_interval->ms();
const double kMaxTwccRate =
kTwccReportSize * 8.0 * 1000.0 / send_config_.min_interval->ms();
constexpr DataSize kTwccReportSize = DataSize::Bytes(20 + 8 + 10 + 30);
constexpr DataRate kMinTwccRate = kTwccReportSize / kMaxInterval;
// Let TWCC reports occupy 5% of total bandwidth.
DataRate twcc_bitrate = DataRate::BitsPerSec(0.05 * bitrate_bps);
// Check upper send_interval bound by checking bitrate to avoid overflow when
// dividing by small bitrate, in particular avoid dividing by zero bitrate.
TimeDelta send_interval =
twcc_bitrate <= kMinTwccRate
? kMaxInterval
: std::max(kTwccReportSize / twcc_bitrate, kMinInterval);
MutexLock lock(&lock_);
send_interval_ms_ = static_cast<int>(
0.5 + kTwccReportSize * 8.0 * 1000.0 /
rtc::SafeClamp(send_config_.bandwidth_fraction * bitrate_bps,
kMinTwccRate, kMaxTwccRate));
send_interval_ = send_interval;
}
void RemoteEstimatorProxy::SetSendPeriodicFeedback(
@ -182,10 +195,15 @@ void RemoteEstimatorProxy::SetSendPeriodicFeedback(
send_periodic_feedback_ = send_periodic_feedback;
}
void RemoteEstimatorProxy::SetTransportOverhead(DataSize overhead_per_packet) {
MutexLock lock(&lock_);
packet_overhead_ = overhead_per_packet;
}
void RemoteEstimatorProxy::SendPeriodicFeedbacks() {
// |periodic_window_start_seq_| is the first sequence number to include in the
// current feedback packet. Some older may still be in the map, in case a
// reordering happens and we need to retransmit them.
// `periodic_window_start_seq_` is the first sequence number to include in
// the current feedback packet. Some older may still be in the map, in case
// a reordering happens and we need to retransmit them.
if (!periodic_window_start_seq_)
return;
@ -199,15 +217,17 @@ void RemoteEstimatorProxy::SendPeriodicFeedbacks() {
}
}
for (auto begin_iterator =
packet_arrival_times_.lower_bound(*periodic_window_start_seq_);
begin_iterator != packet_arrival_times_.cend();
begin_iterator =
packet_arrival_times_.lower_bound(*periodic_window_start_seq_)) {
auto feedback_packet = std::make_unique<rtcp::TransportFeedback>();
periodic_window_start_seq_ = BuildFeedbackPacket(
feedback_packet_count_++, media_ssrc_, *periodic_window_start_seq_,
begin_iterator, packet_arrival_times_.cend(), feedback_packet.get());
int64_t packet_arrival_times_end_seq =
packet_arrival_times_.end_sequence_number();
while (periodic_window_start_seq_ < packet_arrival_times_end_seq) {
auto feedback_packet = MaybeBuildFeedbackPacket(
/*include_timestamps=*/true, periodic_window_start_seq_.value(),
packet_arrival_times_end_seq,
/*is_periodic_update=*/true);
if (feedback_packet == nullptr) {
break;
}
RTC_DCHECK(feedback_sender_ != nullptr);
@ -217,10 +237,10 @@ void RemoteEstimatorProxy::SendPeriodicFeedbacks() {
}
packets.push_back(std::move(feedback_packet));
feedback_sender_->SendCombinedRtcpPacket(std::move(packets));
// Note: Don't erase items from packet_arrival_times_ after sending, in case
// they need to be re-sent after a reordering. Removal will be handled
// by OnPacketArrival once packets are too old.
feedback_sender_(std::move(packets));
// Note: Don't erase items from packet_arrival_times_ after sending, in
// case they need to be re-sent after a reordering. Removal will be
// handled by OnPacketArrival once packets are too old.
}
}
@ -231,61 +251,78 @@ void RemoteEstimatorProxy::SendFeedbackOnRequest(
return;
}
auto feedback_packet = std::make_unique<rtcp::TransportFeedback>(
feedback_request.include_timestamps);
int64_t first_sequence_number =
sequence_number - feedback_request.sequence_count + 1;
auto begin_iterator =
packet_arrival_times_.lower_bound(first_sequence_number);
auto end_iterator = packet_arrival_times_.upper_bound(sequence_number);
BuildFeedbackPacket(feedback_packet_count_++, media_ssrc_,
first_sequence_number, begin_iterator, end_iterator,
feedback_packet.get());
auto feedback_packet = MaybeBuildFeedbackPacket(
feedback_request.include_timestamps, first_sequence_number,
sequence_number + 1, /*is_periodic_update=*/false);
// This is called when a packet has just been added.
RTC_DCHECK(feedback_packet != nullptr);
// Clear up to the first packet that is included in this feedback packet.
packet_arrival_times_.erase(packet_arrival_times_.begin(), begin_iterator);
packet_arrival_times_.EraseTo(first_sequence_number);
RTC_DCHECK(feedback_sender_ != nullptr);
std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets;
packets.push_back(std::move(feedback_packet));
feedback_sender_->SendCombinedRtcpPacket(std::move(packets));
feedback_sender_(std::move(packets));
}
int64_t RemoteEstimatorProxy::BuildFeedbackPacket(
uint8_t feedback_packet_count,
uint32_t media_ssrc,
int64_t base_sequence_number,
std::map<int64_t, int64_t>::const_iterator begin_iterator,
std::map<int64_t, int64_t>::const_iterator end_iterator,
rtcp::TransportFeedback* feedback_packet) {
RTC_DCHECK(begin_iterator != end_iterator);
std::unique_ptr<rtcp::TransportFeedback>
RemoteEstimatorProxy::MaybeBuildFeedbackPacket(
bool include_timestamps,
int64_t begin_sequence_number_inclusive,
int64_t end_sequence_number_exclusive,
bool is_periodic_update) {
RTC_DCHECK_LT(begin_sequence_number_inclusive, end_sequence_number_exclusive);
// TODO(sprang): Measure receive times in microseconds and remove the
// conversions below.
feedback_packet->SetMediaSsrc(media_ssrc);
// Base sequence number is the expected first sequence number. This is known,
// but we might not have actually received it, so the base time shall be the
// time of the first received packet in the feedback.
feedback_packet->SetBase(static_cast<uint16_t>(base_sequence_number & 0xFFFF),
begin_iterator->second * 1000);
feedback_packet->SetFeedbackSequenceNumber(feedback_packet_count);
int64_t next_sequence_number = base_sequence_number;
for (auto it = begin_iterator; it != end_iterator; ++it) {
if (!feedback_packet->AddReceivedPacket(
static_cast<uint16_t>(it->first & 0xFFFF), it->second * 1000)) {
// If we can't even add the first seq to the feedback packet, we won't be
// able to build it at all.
RTC_CHECK(begin_iterator != it);
int64_t start_seq =
packet_arrival_times_.clamp(begin_sequence_number_inclusive);
int64_t end_seq = packet_arrival_times_.clamp(end_sequence_number_exclusive);
// Create the packet on demand, as it's not certain that there are packets
// in the range that have been received.
std::unique_ptr<rtcp::TransportFeedback> feedback_packet = nullptr;
int64_t next_sequence_number = begin_sequence_number_inclusive;
for (int64_t seq = start_seq; seq < end_seq; ++seq) {
PacketArrivalTimeMap::PacketArrivalTime packet =
packet_arrival_times_.FindNextAtOrAfter(seq);
seq = packet.sequence_number;
if (seq >= end_seq) {
break;
}
if (feedback_packet == nullptr) {
feedback_packet =
std::make_unique<rtcp::TransportFeedback>(include_timestamps);
feedback_packet->SetMediaSsrc(media_ssrc_);
// Base sequence number is the expected first sequence number. This is
// known, but we might not have actually received it, so the base time
// shall be the time of the first received packet in the feedback.
feedback_packet->SetBase(
static_cast<uint16_t>(begin_sequence_number_inclusive & 0xFFFF),
packet.arrival_time);
feedback_packet->SetFeedbackSequenceNumber(feedback_packet_count_++);
}
if (!feedback_packet->AddReceivedPacket(static_cast<uint16_t>(seq & 0xFFFF),
packet.arrival_time)) {
// Could not add timestamp, feedback packet might be full. Return and
// try again with a fresh packet.
break;
}
next_sequence_number = it->first + 1;
next_sequence_number = seq + 1;
}
return next_sequence_number;
if (is_periodic_update) {
periodic_window_start_seq_ = next_sequence_number;
}
return feedback_packet;
}
} // namespace webrtc

View File

@ -11,98 +11,107 @@
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_ESTIMATOR_PROXY_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_ESTIMATOR_PROXY_H_
#include <map>
#include <deque>
#include <functional>
#include <memory>
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/rtp_headers.h"
#include "api/transport/network_control.h"
#include "api/transport/webrtc_key_value_config.h"
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/remote_bitrate_estimator/packet_arrival_map.h"
#include "modules/rtp_rtcp/source/rtcp_packet.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "rtc_base/numerics/sequence_number_util.h"
#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
class Clock;
class PacketRouter;
namespace rtcp {
class TransportFeedback;
}
// Class used when send-side BWE is enabled: This proxy is instantiated on the
// receive side. It buffers a number of receive timestamps and then sends
// transport feedback messages back too the send side.
class RemoteEstimatorProxy : public RemoteBitrateEstimator {
class RemoteEstimatorProxy {
public:
RemoteEstimatorProxy(Clock* clock,
TransportFeedbackSenderInterface* feedback_sender,
const WebRtcKeyValueConfig* key_value_config,
// Used for sending transport feedback messages when send side
// BWE is used.
using TransportFeedbackSender = std::function<void(
std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets)>;
RemoteEstimatorProxy(TransportFeedbackSender feedback_sender,
NetworkStateEstimator* network_state_estimator);
~RemoteEstimatorProxy() override;
~RemoteEstimatorProxy();
struct Packet {
Timestamp arrival_time;
DataSize size;
uint32_t ssrc;
absl::optional<uint32_t> absolute_send_time_24bits;
absl::optional<uint16_t> transport_sequence_number;
absl::optional<FeedbackRequest> feedback_request;
};
void IncomingPacket(Packet packet);
void IncomingPacket(int64_t arrival_time_ms,
size_t payload_size,
const RTPHeader& header) override;
void RemoveStream(uint32_t ssrc) override {}
bool LatestEstimate(std::vector<unsigned int>* ssrcs,
unsigned int* bitrate_bps) const override;
void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override {}
void SetMinBitrate(int min_bitrate_bps) override {}
int64_t TimeUntilNextProcess() override;
void Process() override;
const RTPHeader& header);
// Sends periodic feedback if it is time to send it.
// Returns time until next call to Process should be made.
TimeDelta Process(Timestamp now);
void OnBitrateChanged(int bitrate);
void SetSendPeriodicFeedback(bool send_periodic_feedback);
void SetTransportOverhead(DataSize overhead_per_packet);
private:
struct TransportWideFeedbackConfig {
FieldTrialParameter<TimeDelta> back_window{"wind", TimeDelta::Millis(500)};
FieldTrialParameter<TimeDelta> min_interval{"min", TimeDelta::Millis(50)};
FieldTrialParameter<TimeDelta> max_interval{"max", TimeDelta::Millis(250)};
FieldTrialParameter<TimeDelta> default_interval{"def",
TimeDelta::Millis(100)};
FieldTrialParameter<double> bandwidth_fraction{"frac", 0.05};
explicit TransportWideFeedbackConfig(
const WebRtcKeyValueConfig* key_value_config) {
ParseFieldTrial({&back_window, &min_interval, &max_interval,
&default_interval, &bandwidth_fraction},
key_value_config->Lookup(
"WebRTC-Bwe-TransportWideFeedbackIntervals"));
}
};
static const int kMaxNumberOfPackets;
void MaybeCullOldPackets(int64_t sequence_number, Timestamp arrival_time)
RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_);
void SendPeriodicFeedbacks() RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_);
void SendFeedbackOnRequest(int64_t sequence_number,
const FeedbackRequest& feedback_request)
RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_);
static int64_t BuildFeedbackPacket(
uint8_t feedback_packet_count,
uint32_t media_ssrc,
int64_t base_sequence_number,
std::map<int64_t, int64_t>::const_iterator
begin_iterator, // |begin_iterator| is inclusive.
std::map<int64_t, int64_t>::const_iterator
end_iterator, // |end_iterator| is exclusive.
rtcp::TransportFeedback* feedback_packet);
Clock* const clock_;
TransportFeedbackSenderInterface* const feedback_sender_;
const TransportWideFeedbackConfig send_config_;
int64_t last_process_time_ms_;
// Returns a Transport Feedback packet with information about as many packets
// that has been received between [`begin_sequence_number_incl`,
// `end_sequence_number_excl`) that can fit in it. If `is_periodic_update`,
// this represents sending a periodic feedback message, which will make it
// update the `periodic_window_start_seq_` variable with the first packet that
// was not included in the feedback packet, so that the next update can
// continue from that sequence number.
//
// If no incoming packets were added, nullptr is returned.
//
// `include_timestamps` decide if the returned TransportFeedback should
// include timestamps.
std::unique_ptr<rtcp::TransportFeedback> MaybeBuildFeedbackPacket(
bool include_timestamps,
int64_t begin_sequence_number_inclusive,
int64_t end_sequence_number_exclusive,
bool is_periodic_update) RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_);
const TransportFeedbackSender feedback_sender_;
Timestamp last_process_time_;
Mutex lock_;
// |network_state_estimator_| may be null.
// `network_state_estimator_` may be null.
NetworkStateEstimator* const network_state_estimator_
RTC_PT_GUARDED_BY(&lock_);
uint32_t media_ssrc_ RTC_GUARDED_BY(&lock_);
uint8_t feedback_packet_count_ RTC_GUARDED_BY(&lock_);
SeqNumUnwrapper<uint16_t> unwrapper_ RTC_GUARDED_BY(&lock_);
DataSize packet_overhead_ RTC_GUARDED_BY(&lock_);
// The next sequence number that should be the start sequence number during
// periodic reporting. Will be absl::nullopt before the first seen packet.
absl::optional<int64_t> periodic_window_start_seq_ RTC_GUARDED_BY(&lock_);
// Map unwrapped seq -> time.
std::map<int64_t, int64_t> packet_arrival_times_ RTC_GUARDED_BY(&lock_);
int64_t send_interval_ms_ RTC_GUARDED_BY(&lock_);
// Packet arrival times, by sequence number.
PacketArrivalTimeMap packet_arrival_times_ RTC_GUARDED_BY(&lock_);
TimeDelta send_interval_ RTC_GUARDED_BY(&lock_);
bool send_periodic_feedback_ RTC_GUARDED_BY(&lock_);
// Unwraps absolute send times.

View File

@ -13,35 +13,39 @@
#include <memory>
#include <utility>
#include "api/transport/field_trial_based_config.h"
#include "api/transport/network_types.h"
#include "api/transport/test/mock_network_control.h"
#include "modules/pacing/packet_router.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "system_wrappers/include/clock.h"
#include "test/gmock.h"
#include "test/gtest.h"
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::SizeIs;
namespace webrtc {
namespace {
constexpr size_t kDefaultPacketSize = 100;
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::Invoke;
using ::testing::MockFunction;
using ::testing::Return;
using ::testing::SizeIs;
constexpr DataSize kDefaultPacketSize = DataSize::Bytes(100);
constexpr uint32_t kMediaSsrc = 456;
constexpr uint16_t kBaseSeq = 10;
constexpr int64_t kBaseTimeMs = 123;
constexpr int64_t kMaxSmallDeltaMs =
(rtcp::TransportFeedback::kDeltaScaleFactor * 0xFF) / 1000;
constexpr Timestamp kBaseTime = Timestamp::Millis(123);
constexpr TimeDelta kBaseTimeWrapAround =
rtcp::TransportFeedback::kDeltaTick * (int64_t{1} << 32);
constexpr TimeDelta kMaxSmallDelta = rtcp::TransportFeedback::kDeltaTick * 0xFF;
constexpr int kBackWindowMs = 500;
constexpr int kMinSendIntervalMs = 50;
constexpr int kMaxSendIntervalMs = 250;
constexpr int kDefaultSendIntervalMs = 100;
constexpr TimeDelta kBackWindow = TimeDelta::Millis(500);
constexpr TimeDelta kMinSendInterval = TimeDelta::Millis(50);
constexpr TimeDelta kMaxSendInterval = TimeDelta::Millis(250);
constexpr TimeDelta kDefaultSendInterval = TimeDelta::Millis(100);
std::vector<uint16_t> SequenceNumbers(
const rtcp::TransportFeedback& feedback_packet) {
@ -52,76 +56,58 @@ std::vector<uint16_t> SequenceNumbers(
return sequence_numbers;
}
std::vector<int64_t> TimestampsMs(
std::vector<Timestamp> Timestamps(
const rtcp::TransportFeedback& feedback_packet) {
std::vector<int64_t> timestamps;
int64_t timestamp_us = feedback_packet.GetBaseTimeUs();
std::vector<Timestamp> timestamps;
Timestamp timestamp = feedback_packet.BaseTime();
// rtcp::TransportFeedback makes no promises about epoch of the base time,
// It may add several kBaseTimeWrapAround periods to make it large enough and
// thus to support negative deltas. Align it close to the kBaseTime to make
// tests expectations simpler.
if (timestamp > kBaseTime) {
timestamp -= (timestamp - kBaseTime).RoundTo(kBaseTimeWrapAround);
}
for (const auto& rtp_packet_received : feedback_packet.GetReceivedPackets()) {
timestamp_us += rtp_packet_received.delta_us();
timestamps.push_back(timestamp_us / 1000);
timestamp += rtp_packet_received.delta();
timestamps.push_back(timestamp);
}
return timestamps;
}
class MockTransportFeedbackSender : public TransportFeedbackSenderInterface {
public:
MOCK_METHOD(bool,
SendCombinedRtcpPacket,
(std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets),
(override));
};
class RemoteEstimatorProxyTest : public ::testing::Test {
public:
RemoteEstimatorProxyTest()
: clock_(0),
proxy_(&clock_,
&router_,
&field_trial_config_,
&network_state_estimator_) {}
proxy_(feedback_sender_.AsStdFunction(), &network_state_estimator_) {}
protected:
void IncomingPacket(
uint16_t seq,
int64_t time_ms,
Timestamp arrival_time,
absl::optional<FeedbackRequest> feedback_request = absl::nullopt) {
proxy_.IncomingPacket(time_ms, kDefaultPacketSize,
CreateHeader(seq, feedback_request, absl::nullopt));
}
RTPHeader CreateHeader(absl::optional<uint16_t> transport_sequence,
absl::optional<FeedbackRequest> feedback_request,
absl::optional<uint32_t> absolute_send_time) {
RTPHeader header;
if (transport_sequence) {
header.extension.hasTransportSequenceNumber = true;
header.extension.transportSequenceNumber = transport_sequence.value();
}
header.extension.feedback_request = feedback_request;
if (absolute_send_time) {
header.extension.hasAbsoluteSendTime = true;
header.extension.absoluteSendTime = absolute_send_time.value();
}
header.ssrc = kMediaSsrc;
return header;
proxy_.IncomingPacket({.arrival_time = arrival_time,
.size = DataSize::Bytes(100),
.ssrc = kMediaSsrc,
.transport_sequence_number = seq,
.feedback_request = feedback_request});
}
void Process() {
clock_.AdvanceTimeMilliseconds(kDefaultSendIntervalMs);
proxy_.Process();
clock_.AdvanceTime(kDefaultSendInterval);
proxy_.Process(clock_.CurrentTime());
}
FieldTrialBasedConfig field_trial_config_;
SimulatedClock clock_;
::testing::StrictMock<MockTransportFeedbackSender> router_;
MockFunction<void(std::vector<std::unique_ptr<rtcp::RtcpPacket>>)>
feedback_sender_;
::testing::NiceMock<MockNetworkStateEstimator> network_state_estimator_;
RemoteEstimatorProxy proxy_;
};
TEST_F(RemoteEstimatorProxyTest, SendsSinglePacketFeedback) {
IncomingPacket(kBaseSeq, kBaseTimeMs);
IncomingPacket(kBaseSeq, kBaseTime);
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -132,19 +118,17 @@ TEST_F(RemoteEstimatorProxyTest, SendsSinglePacketFeedback) {
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs));
return true;
EXPECT_THAT(Timestamps(*feedback_packet), ElementsAre(kBaseTime));
}));
Process();
}
TEST_F(RemoteEstimatorProxyTest, DuplicatedPackets) {
IncomingPacket(kBaseSeq, kBaseTimeMs);
IncomingPacket(kBaseSeq, kBaseTimeMs + 1000);
IncomingPacket(kBaseSeq, kBaseTime);
IncomingPacket(kBaseSeq, kBaseTime + TimeDelta::Seconds(1));
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -155,8 +139,7 @@ TEST_F(RemoteEstimatorProxyTest, DuplicatedPackets) {
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs));
EXPECT_THAT(Timestamps(*feedback_packet), ElementsAre(kBaseTime));
return true;
}));
@ -165,15 +148,15 @@ TEST_F(RemoteEstimatorProxyTest, DuplicatedPackets) {
TEST_F(RemoteEstimatorProxyTest, FeedbackWithMissingStart) {
// First feedback.
IncomingPacket(kBaseSeq, kBaseTimeMs);
IncomingPacket(kBaseSeq + 1, kBaseTimeMs + 1000);
EXPECT_CALL(router_, SendCombinedRtcpPacket).WillOnce(Return(true));
IncomingPacket(kBaseSeq, kBaseTime);
IncomingPacket(kBaseSeq + 1, kBaseTime + TimeDelta::Seconds(1));
EXPECT_CALL(feedback_sender_, Call);
Process();
// Second feedback starts with a missing packet (DROP kBaseSeq + 2).
IncomingPacket(kBaseSeq + 3, kBaseTimeMs + 3000);
IncomingPacket(kBaseSeq + 3, kBaseTime + TimeDelta::Seconds(3));
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -184,20 +167,20 @@ TEST_F(RemoteEstimatorProxyTest, FeedbackWithMissingStart) {
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq + 3));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs + 3000));
return true;
EXPECT_THAT(Timestamps(*feedback_packet),
ElementsAre(kBaseTime + TimeDelta::Seconds(3)));
}));
Process();
}
TEST_F(RemoteEstimatorProxyTest, SendsFeedbackWithVaryingDeltas) {
IncomingPacket(kBaseSeq, kBaseTimeMs);
IncomingPacket(kBaseSeq + 1, kBaseTimeMs + kMaxSmallDeltaMs);
IncomingPacket(kBaseSeq + 2, kBaseTimeMs + (2 * kMaxSmallDeltaMs) + 1);
IncomingPacket(kBaseSeq, kBaseTime);
IncomingPacket(kBaseSeq + 1, kBaseTime + kMaxSmallDelta);
IncomingPacket(kBaseSeq + 2,
kBaseTime + (2 * kMaxSmallDelta) + TimeDelta::Millis(1));
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -208,23 +191,23 @@ TEST_F(RemoteEstimatorProxyTest, SendsFeedbackWithVaryingDeltas) {
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq, kBaseSeq + 1, kBaseSeq + 2));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs, kBaseTimeMs + kMaxSmallDeltaMs,
kBaseTimeMs + (2 * kMaxSmallDeltaMs) + 1));
return true;
EXPECT_THAT(Timestamps(*feedback_packet),
ElementsAre(kBaseTime, kBaseTime + kMaxSmallDelta,
kBaseTime + (2 * kMaxSmallDelta) +
TimeDelta::Millis(1)));
}));
Process();
}
TEST_F(RemoteEstimatorProxyTest, SendsFragmentedFeedback) {
static constexpr int64_t kTooLargeDelta =
rtcp::TransportFeedback::kDeltaScaleFactor * (1 << 16);
static constexpr TimeDelta kTooLargeDelta =
rtcp::TransportFeedback::kDeltaTick * (1 << 16);
IncomingPacket(kBaseSeq, kBaseTimeMs);
IncomingPacket(kBaseSeq + 1, kBaseTimeMs + kTooLargeDelta);
IncomingPacket(kBaseSeq, kBaseTime);
IncomingPacket(kBaseSeq + 1, kBaseTime + kTooLargeDelta);
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -235,9 +218,7 @@ TEST_F(RemoteEstimatorProxyTest, SendsFragmentedFeedback) {
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs));
return true;
EXPECT_THAT(Timestamps(*feedback_packet), ElementsAre(kBaseTime));
}))
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
@ -249,21 +230,20 @@ TEST_F(RemoteEstimatorProxyTest, SendsFragmentedFeedback) {
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq + 1));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs + kTooLargeDelta));
return true;
EXPECT_THAT(Timestamps(*feedback_packet),
ElementsAre(kBaseTime + kTooLargeDelta));
}));
Process();
}
TEST_F(RemoteEstimatorProxyTest, HandlesReorderingAndWrap) {
const int64_t kDeltaMs = 1000;
const TimeDelta kDelta = TimeDelta::Seconds(1);
const uint16_t kLargeSeq = 62762;
IncomingPacket(kBaseSeq, kBaseTimeMs);
IncomingPacket(kLargeSeq, kBaseTimeMs + kDeltaMs);
IncomingPacket(kBaseSeq, kBaseTime);
IncomingPacket(kLargeSeq, kBaseTime + kDelta);
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[&](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -272,9 +252,8 @@ TEST_F(RemoteEstimatorProxyTest, HandlesReorderingAndWrap) {
EXPECT_EQ(kLargeSeq, feedback_packet->GetBaseSequence());
EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc());
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs + kDeltaMs, kBaseTimeMs));
return true;
EXPECT_THAT(Timestamps(*feedback_packet),
ElementsAre(kBaseTime + kDelta, kBaseTime));
}));
Process();
@ -285,15 +264,15 @@ TEST_F(RemoteEstimatorProxyTest, HandlesMalformedSequenceNumbers) {
// When unwrapped, the sequeunce numbers of these 30 incoming packets, will
// span a range of roughly 650k packets. Test that we only send feedback for
// the last packets. Test for regression found in chromium:949020.
const int64_t kDeltaMs = 1000;
const TimeDelta kDelta = TimeDelta::Seconds(1);
for (int i = 0; i < 10; ++i) {
IncomingPacket(kBaseSeq + i, kBaseTimeMs + 3 * i * kDeltaMs);
IncomingPacket(kBaseSeq + 20000 + i, kBaseTimeMs + (3 * i + 1) * kDeltaMs);
IncomingPacket(kBaseSeq + 40000 + i, kBaseTimeMs + (3 * i + 2) * kDeltaMs);
IncomingPacket(kBaseSeq + i, kBaseTime + 3 * i * kDelta);
IncomingPacket(kBaseSeq + 20000 + i, kBaseTime + (3 * i + 1) * kDelta);
IncomingPacket(kBaseSeq + 40000 + i, kBaseTime + (3 * i + 2) * kDelta);
}
// Only expect feedback for the last two packets.
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[&](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -303,10 +282,9 @@ TEST_F(RemoteEstimatorProxyTest, HandlesMalformedSequenceNumbers) {
EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc());
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq + 20009, kBaseSeq + 40009));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs + 28 * kDeltaMs,
kBaseTimeMs + 29 * kDeltaMs));
return true;
EXPECT_THAT(
Timestamps(*feedback_packet),
ElementsAre(kBaseTime + 28 * kDelta, kBaseTime + 29 * kDelta));
}));
Process();
@ -316,15 +294,15 @@ TEST_F(RemoteEstimatorProxyTest, HandlesBackwardsWrappingSequenceNumbers) {
// This test is like HandlesMalformedSequenceNumbers but for negative wrap
// arounds. Test that we only send feedback for the packets with highest
// sequence numbers. Test for regression found in chromium:949020.
const int64_t kDeltaMs = 1000;
const TimeDelta kDelta = TimeDelta::Seconds(1);
for (int i = 0; i < 10; ++i) {
IncomingPacket(kBaseSeq + i, kBaseTimeMs + 3 * i * kDeltaMs);
IncomingPacket(kBaseSeq + 40000 + i, kBaseTimeMs + (3 * i + 1) * kDeltaMs);
IncomingPacket(kBaseSeq + 20000 + i, kBaseTimeMs + (3 * i + 2) * kDeltaMs);
IncomingPacket(kBaseSeq + i, kBaseTime + 3 * i * kDelta);
IncomingPacket(kBaseSeq + 40000 + i, kBaseTime + (3 * i + 1) * kDelta);
IncomingPacket(kBaseSeq + 20000 + i, kBaseTime + (3 * i + 2) * kDelta);
}
// Only expect feedback for the first two packets.
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[&](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -334,19 +312,18 @@ TEST_F(RemoteEstimatorProxyTest, HandlesBackwardsWrappingSequenceNumbers) {
EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc());
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq + 40000, kBaseSeq));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs + kDeltaMs, kBaseTimeMs));
return true;
EXPECT_THAT(Timestamps(*feedback_packet),
ElementsAre(kBaseTime + kDelta, kBaseTime));
}));
Process();
}
TEST_F(RemoteEstimatorProxyTest, ResendsTimestampsOnReordering) {
IncomingPacket(kBaseSeq, kBaseTimeMs);
IncomingPacket(kBaseSeq + 2, kBaseTimeMs + 2);
IncomingPacket(kBaseSeq, kBaseTime);
IncomingPacket(kBaseSeq + 2, kBaseTime + TimeDelta::Millis(2));
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -357,16 +334,16 @@ TEST_F(RemoteEstimatorProxyTest, ResendsTimestampsOnReordering) {
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq, kBaseSeq + 2));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs, kBaseTimeMs + 2));
return true;
EXPECT_THAT(
Timestamps(*feedback_packet),
ElementsAre(kBaseTime, kBaseTime + TimeDelta::Millis(2)));
}));
Process();
IncomingPacket(kBaseSeq + 1, kBaseTimeMs + 1);
IncomingPacket(kBaseSeq + 1, kBaseTime + TimeDelta::Millis(1));
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -377,20 +354,20 @@ TEST_F(RemoteEstimatorProxyTest, ResendsTimestampsOnReordering) {
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq + 1, kBaseSeq + 2));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs + 1, kBaseTimeMs + 2));
return true;
EXPECT_THAT(Timestamps(*feedback_packet),
ElementsAre(kBaseTime + TimeDelta::Millis(1),
kBaseTime + TimeDelta::Millis(2)));
}));
Process();
}
TEST_F(RemoteEstimatorProxyTest, RemovesTimestampsOutOfScope) {
const int64_t kTimeoutTimeMs = kBaseTimeMs + kBackWindowMs;
const Timestamp kTimeoutTime = kBaseTime + kBackWindow;
IncomingPacket(kBaseSeq + 2, kBaseTimeMs);
IncomingPacket(kBaseSeq + 2, kBaseTime);
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -398,16 +375,14 @@ TEST_F(RemoteEstimatorProxyTest, RemovesTimestampsOutOfScope) {
feedback_packets[0].get());
EXPECT_EQ(kBaseSeq + 2, feedback_packet->GetBaseSequence());
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs));
return true;
EXPECT_THAT(Timestamps(*feedback_packet), ElementsAre(kBaseTime));
}));
Process();
IncomingPacket(kBaseSeq + 3, kTimeoutTimeMs); // kBaseSeq + 2 times out here.
IncomingPacket(kBaseSeq + 3, kTimeoutTime); // kBaseSeq + 2 times out here.
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[&](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -415,19 +390,18 @@ TEST_F(RemoteEstimatorProxyTest, RemovesTimestampsOutOfScope) {
feedback_packets[0].get());
EXPECT_EQ(kBaseSeq + 3, feedback_packet->GetBaseSequence());
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kTimeoutTimeMs));
return true;
EXPECT_THAT(Timestamps(*feedback_packet),
ElementsAre(kTimeoutTime));
}));
Process();
// New group, with sequence starting below the first so that they may be
// retransmitted.
IncomingPacket(kBaseSeq, kBaseTimeMs - 1);
IncomingPacket(kBaseSeq + 1, kTimeoutTimeMs - 1);
IncomingPacket(kBaseSeq, kBaseTime - TimeDelta::Millis(1));
IncomingPacket(kBaseSeq + 1, kTimeoutTime - TimeDelta::Millis(1));
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[&](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -437,50 +411,41 @@ TEST_F(RemoteEstimatorProxyTest, RemovesTimestampsOutOfScope) {
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq, kBaseSeq + 1, kBaseSeq + 3));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs - 1, kTimeoutTimeMs - 1,
kTimeoutTimeMs));
return true;
EXPECT_THAT(
Timestamps(*feedback_packet),
ElementsAre(kBaseTime - TimeDelta::Millis(1),
kTimeoutTime - TimeDelta::Millis(1), kTimeoutTime));
}));
Process();
}
TEST_F(RemoteEstimatorProxyTest, TimeUntilNextProcessIsZeroBeforeFirstProcess) {
EXPECT_EQ(0, proxy_.TimeUntilNextProcess());
}
TEST_F(RemoteEstimatorProxyTest, TimeUntilNextProcessIsDefaultOnUnkownBitrate) {
Process();
EXPECT_EQ(kDefaultSendIntervalMs, proxy_.TimeUntilNextProcess());
EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), kDefaultSendInterval);
}
TEST_F(RemoteEstimatorProxyTest, TimeUntilNextProcessIsMinIntervalOn300kbps) {
Process();
proxy_.OnBitrateChanged(300000);
EXPECT_EQ(kMinSendIntervalMs, proxy_.TimeUntilNextProcess());
proxy_.OnBitrateChanged(300'000);
EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), kMinSendInterval);
}
TEST_F(RemoteEstimatorProxyTest, TimeUntilNextProcessIsMaxIntervalOn0kbps) {
Process();
// TimeUntilNextProcess should be limited by |kMaxSendIntervalMs| when
// TimeUntilNextProcess should be limited by `kMaxSendIntervalMs` when
// bitrate is small. We choose 0 bps as a special case, which also tests
// erroneous behaviors like division-by-zero.
proxy_.OnBitrateChanged(0);
EXPECT_EQ(kMaxSendIntervalMs, proxy_.TimeUntilNextProcess());
EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), kMaxSendInterval);
}
TEST_F(RemoteEstimatorProxyTest, TimeUntilNextProcessIsMaxIntervalOn20kbps) {
Process();
proxy_.OnBitrateChanged(20000);
EXPECT_EQ(kMaxSendIntervalMs, proxy_.TimeUntilNextProcess());
proxy_.OnBitrateChanged(20'000);
EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), kMaxSendInterval);
}
TEST_F(RemoteEstimatorProxyTest, TwccReportsUse5PercentOfAvailableBandwidth) {
Process();
proxy_.OnBitrateChanged(80000);
proxy_.OnBitrateChanged(80'000);
// 80kbps * 0.05 = TwccReportSize(68B * 8b/B) * 1000ms / SendInterval(136ms)
EXPECT_EQ(136, proxy_.TimeUntilNextProcess());
EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), TimeDelta::Millis(136));
}
//////////////////////////////////////////////////////////////////////////////
@ -488,25 +453,25 @@ TEST_F(RemoteEstimatorProxyTest, TwccReportsUse5PercentOfAvailableBandwidth) {
// by the sender.
//////////////////////////////////////////////////////////////////////////////
typedef RemoteEstimatorProxyTest RemoteEstimatorProxyOnRequestTest;
TEST_F(RemoteEstimatorProxyOnRequestTest, TimeUntilNextProcessIsHigh) {
TEST_F(RemoteEstimatorProxyOnRequestTest, DisablesPeriodicProcess) {
proxy_.SetSendPeriodicFeedback(false);
EXPECT_GE(proxy_.TimeUntilNextProcess(), 60 * 60 * 1000);
EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), TimeDelta::PlusInfinity());
}
TEST_F(RemoteEstimatorProxyOnRequestTest, ProcessDoesNotSendFeedback) {
proxy_.SetSendPeriodicFeedback(false);
IncomingPacket(kBaseSeq, kBaseTimeMs);
EXPECT_CALL(router_, SendCombinedRtcpPacket).Times(0);
IncomingPacket(kBaseSeq, kBaseTime);
EXPECT_CALL(feedback_sender_, Call).Times(0);
Process();
}
TEST_F(RemoteEstimatorProxyOnRequestTest, RequestSinglePacketFeedback) {
proxy_.SetSendPeriodicFeedback(false);
IncomingPacket(kBaseSeq, kBaseTimeMs);
IncomingPacket(kBaseSeq + 1, kBaseTimeMs + kMaxSmallDeltaMs);
IncomingPacket(kBaseSeq + 2, kBaseTimeMs + 2 * kMaxSmallDeltaMs);
IncomingPacket(kBaseSeq, kBaseTime);
IncomingPacket(kBaseSeq + 1, kBaseTime + kMaxSmallDelta);
IncomingPacket(kBaseSeq + 2, kBaseTime + 2 * kMaxSmallDelta);
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -517,14 +482,13 @@ TEST_F(RemoteEstimatorProxyOnRequestTest, RequestSinglePacketFeedback) {
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq + 3));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs + 3 * kMaxSmallDeltaMs));
return true;
EXPECT_THAT(Timestamps(*feedback_packet),
ElementsAre(kBaseTime + 3 * kMaxSmallDelta));
}));
constexpr FeedbackRequest kSinglePacketFeedbackRequest = {
/*include_timestamps=*/true, /*sequence_count=*/1};
IncomingPacket(kBaseSeq + 3, kBaseTimeMs + 3 * kMaxSmallDeltaMs,
IncomingPacket(kBaseSeq + 3, kBaseTime + 3 * kMaxSmallDelta,
kSinglePacketFeedbackRequest);
}
@ -532,10 +496,10 @@ TEST_F(RemoteEstimatorProxyOnRequestTest, RequestLastFivePacketFeedback) {
proxy_.SetSendPeriodicFeedback(false);
int i = 0;
for (; i < 10; ++i) {
IncomingPacket(kBaseSeq + i, kBaseTimeMs + i * kMaxSmallDeltaMs);
IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta);
}
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -547,18 +511,17 @@ TEST_F(RemoteEstimatorProxyOnRequestTest, RequestLastFivePacketFeedback) {
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq + 6, kBaseSeq + 7, kBaseSeq + 8,
kBaseSeq + 9, kBaseSeq + 10));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs + 6 * kMaxSmallDeltaMs,
kBaseTimeMs + 7 * kMaxSmallDeltaMs,
kBaseTimeMs + 8 * kMaxSmallDeltaMs,
kBaseTimeMs + 9 * kMaxSmallDeltaMs,
kBaseTimeMs + 10 * kMaxSmallDeltaMs));
return true;
EXPECT_THAT(Timestamps(*feedback_packet),
ElementsAre(kBaseTime + 6 * kMaxSmallDelta,
kBaseTime + 7 * kMaxSmallDelta,
kBaseTime + 8 * kMaxSmallDelta,
kBaseTime + 9 * kMaxSmallDelta,
kBaseTime + 10 * kMaxSmallDelta));
}));
constexpr FeedbackRequest kFivePacketsFeedbackRequest = {
/*include_timestamps=*/true, /*sequence_count=*/5};
IncomingPacket(kBaseSeq + i, kBaseTimeMs + i * kMaxSmallDeltaMs,
IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta,
kFivePacketsFeedbackRequest);
}
@ -568,10 +531,10 @@ TEST_F(RemoteEstimatorProxyOnRequestTest,
int i = 0;
for (; i < 10; ++i) {
if (i != 7 && i != 9)
IncomingPacket(kBaseSeq + i, kBaseTimeMs + i * kMaxSmallDeltaMs);
IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta);
}
EXPECT_CALL(router_, SendCombinedRtcpPacket)
EXPECT_CALL(feedback_sender_, Call)
.WillOnce(Invoke(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
rtcp::TransportFeedback* feedback_packet =
@ -582,89 +545,99 @@ TEST_F(RemoteEstimatorProxyOnRequestTest,
EXPECT_THAT(SequenceNumbers(*feedback_packet),
ElementsAre(kBaseSeq + 6, kBaseSeq + 8, kBaseSeq + 10));
EXPECT_THAT(TimestampsMs(*feedback_packet),
ElementsAre(kBaseTimeMs + 6 * kMaxSmallDeltaMs,
kBaseTimeMs + 8 * kMaxSmallDeltaMs,
kBaseTimeMs + 10 * kMaxSmallDeltaMs));
return true;
EXPECT_THAT(Timestamps(*feedback_packet),
ElementsAre(kBaseTime + 6 * kMaxSmallDelta,
kBaseTime + 8 * kMaxSmallDelta,
kBaseTime + 10 * kMaxSmallDelta));
}));
constexpr FeedbackRequest kFivePacketsFeedbackRequest = {
/*include_timestamps=*/true, /*sequence_count=*/5};
IncomingPacket(kBaseSeq + i, kBaseTimeMs + i * kMaxSmallDeltaMs,
IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta,
kFivePacketsFeedbackRequest);
}
TEST_F(RemoteEstimatorProxyTest, ReportsIncomingPacketToNetworkStateEstimator) {
Timestamp first_send_timestamp = Timestamp::Millis(0);
Timestamp first_send_timestamp = Timestamp::Zero();
const DataSize kPacketOverhead = DataSize::Bytes(38);
proxy_.SetTransportOverhead(kPacketOverhead);
EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_))
.WillOnce(Invoke([&first_send_timestamp](const PacketResult& packet) {
EXPECT_EQ(packet.receive_time, Timestamp::Millis(kBaseTimeMs));
.WillOnce(Invoke([&](const PacketResult& packet) {
EXPECT_EQ(packet.receive_time, kBaseTime);
EXPECT_EQ(packet.sent_packet.size,
kDefaultPacketSize + kPacketOverhead);
first_send_timestamp = packet.sent_packet.send_time;
}));
// Incoming packet with abs sendtime but without transport sequence number.
proxy_.IncomingPacket(
kBaseTimeMs, kDefaultPacketSize,
CreateHeader(absl::nullopt, absl::nullopt,
AbsoluteSendTime::MsTo24Bits(kBaseTimeMs)));
{.arrival_time = kBaseTime,
.size = kDefaultPacketSize,
.ssrc = kMediaSsrc,
.absolute_send_time_24bits = AbsoluteSendTime::To24Bits(kBaseTime)});
// Expect packet with older abs send time to be treated as sent at the same
// time as the previous packet due to reordering.
EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_))
.WillOnce(Invoke([&first_send_timestamp](const PacketResult& packet) {
EXPECT_EQ(packet.receive_time, Timestamp::Millis(kBaseTimeMs));
EXPECT_EQ(packet.receive_time, kBaseTime);
EXPECT_EQ(packet.sent_packet.send_time, first_send_timestamp);
}));
proxy_.IncomingPacket(
kBaseTimeMs, kDefaultPacketSize,
CreateHeader(absl::nullopt, absl::nullopt,
AbsoluteSendTime::MsTo24Bits(kBaseTimeMs - 12)));
{.arrival_time = kBaseTime,
.size = kDefaultPacketSize,
.ssrc = kMediaSsrc,
.absolute_send_time_24bits =
AbsoluteSendTime::To24Bits(kBaseTime - TimeDelta::Millis(12))});
}
TEST_F(RemoteEstimatorProxyTest, IncomingPacketHandlesWrapInAbsSendTime) {
// abs send time use 24bit precision.
const uint32_t kFirstAbsSendTime =
AbsoluteSendTime::MsTo24Bits((1 << 24) - 30);
AbsoluteSendTime::To24Bits(Timestamp::Millis((1 << 24) - 30));
// Second abs send time has wrapped.
const uint32_t kSecondAbsSendTime = AbsoluteSendTime::MsTo24Bits((1 << 24));
const uint32_t kSecondAbsSendTime =
AbsoluteSendTime::To24Bits(Timestamp::Millis(1 << 24));
const TimeDelta kExpectedAbsSendTimeDelta = TimeDelta::Millis(30);
Timestamp first_send_timestamp = Timestamp::Millis(0);
Timestamp first_send_timestamp = Timestamp::Zero();
EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_))
.WillOnce(Invoke([&first_send_timestamp](const PacketResult& packet) {
EXPECT_EQ(packet.receive_time, Timestamp::Millis(kBaseTimeMs));
EXPECT_EQ(packet.receive_time, kBaseTime);
first_send_timestamp = packet.sent_packet.send_time;
}));
proxy_.IncomingPacket(
kBaseTimeMs, kDefaultPacketSize,
CreateHeader(kBaseSeq, absl::nullopt, kFirstAbsSendTime));
proxy_.IncomingPacket({.arrival_time = kBaseTime,
.size = kDefaultPacketSize,
.ssrc = kMediaSsrc,
.absolute_send_time_24bits = kFirstAbsSendTime,
.transport_sequence_number = kBaseSeq});
EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_))
.WillOnce(Invoke([first_send_timestamp,
kExpectedAbsSendTimeDelta](const PacketResult& packet) {
EXPECT_EQ(packet.receive_time, Timestamp::Millis(kBaseTimeMs + 123));
EXPECT_EQ(packet.receive_time, kBaseTime + TimeDelta::Millis(123));
EXPECT_EQ(packet.sent_packet.send_time.ms(),
(first_send_timestamp + kExpectedAbsSendTimeDelta).ms());
}));
proxy_.IncomingPacket(
kBaseTimeMs + 123, kDefaultPacketSize,
CreateHeader(kBaseSeq + 1, absl::nullopt, kSecondAbsSendTime));
proxy_.IncomingPacket({.arrival_time = kBaseTime + TimeDelta::Millis(123),
.size = kDefaultPacketSize,
.ssrc = kMediaSsrc,
.absolute_send_time_24bits = kSecondAbsSendTime,
.transport_sequence_number = kBaseSeq + 1});
}
TEST_F(RemoteEstimatorProxyTest, SendTransportFeedbackAndNetworkStateUpdate) {
proxy_.IncomingPacket(
kBaseTimeMs, kDefaultPacketSize,
CreateHeader(kBaseSeq, absl::nullopt,
AbsoluteSendTime::MsTo24Bits(kBaseTimeMs - 1)));
{.arrival_time = kBaseTime,
.size = kDefaultPacketSize,
.ssrc = kMediaSsrc,
.absolute_send_time_24bits =
AbsoluteSendTime::To24Bits(kBaseTime - TimeDelta::Millis(1)),
.transport_sequence_number = kBaseSeq});
EXPECT_CALL(network_state_estimator_, GetCurrentEstimate())
.WillOnce(Return(NetworkStateEstimate()));
EXPECT_CALL(router_, SendCombinedRtcpPacket)
.WillOnce(
[](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) {
EXPECT_THAT(feedback_packets, SizeIs(2));
return true;
});
EXPECT_CALL(feedback_sender_, Call(SizeIs(2)));
Process();
}

View File

@ -12,13 +12,13 @@
#if BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <algorithm>
#include "rtc_base/checks.h"
#include "rtc_base/format_macros.h"
#include "rtc_base/platform_thread.h"
#include "rtc_base/strings/string_builder.h"

View File

@ -53,31 +53,31 @@
// Set a thread-global base logging context. This name will be prepended to all
// hierarchical contexts.
// |name| is a char*, std::string or uint32_t to name the context.
// `name` is a char*, std::string or uint32_t to name the context.
#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name)
// Thread-globally allow/disallow logging.
// |enable| is expected to be a bool.
// `enable` is expected to be a bool.
#define BWE_TEST_LOGGING_GLOBAL_ENABLE(enabled)
// Insert a (hierarchical) logging context.
// |name| is a char*, std::string or uint32_t to name the context.
// `name` is a char*, std::string or uint32_t to name the context.
#define BWE_TEST_LOGGING_CONTEXT(name)
// Allow/disallow logging down the call tree from this point. Logging must be
// enabled all the way to the root of the call tree to take place.
// |enable| is expected to be a bool.
// `enable` is expected to be a bool.
#define BWE_TEST_LOGGING_ENABLE(enabled)
// Set current time (only affects PLOT output). Down the call tree, the latest
// time set always takes precedence.
// |time| is an int64_t time in ms, or -1 to inherit time from previous context.
// `time` is an int64_t time in ms, or -1 to inherit time from previous context.
#define BWE_TEST_LOGGING_TIME(time)
// Print to stdout, e.g.:
// Context1_Context2_Name printf-formated-string
// |name| is a char*, std::string or uint32_t to name the log line.
// |format| is a printf format string.
// `name` is a char*, std::string or uint32_t to name the log line.
// `format` is a printf format string.
// |_1...| are arguments for printf.
#define BWE_TEST_LOGGING_LOG1(name, format, _1)
#define BWE_TEST_LOGGING_LOG2(name, format, _1, _2)
@ -87,12 +87,12 @@
// Print to stdout in tab-separated format suitable for plotting, e.g.:
// PLOT figure Context1_Context2_Name time value
// |figure| is a figure id. Different figures are plotted in different windows.
// |name| is a char*, std::string or uint32_t to name the plotted value.
// |time| is an int64_t time in ms, or -1 to inherit time from previous context.
// |value| is a double precision float to be plotted.
// |ssrc| identifies the source of a stream
// |alg_name| is an optional argument, a string
// `figure` is a figure id. Different figures are plotted in different windows.
// `name` is a char*, std::string or uint32_t to name the plotted value.
// `time` is an int64_t time in ms, or -1 to inherit time from previous context.
// `value` is a double precision float to be plotted.
// `ssrc` identifies the source of a stream
// `alg_name` is an optional argument, a string
#define BWE_TEST_LOGGING_PLOT(figure, name, time, value)
#define BWE_TEST_LOGGING_PLOT_WITH_NAME(figure, name, time, value, alg_name)
#define BWE_TEST_LOGGING_PLOT_WITH_SSRC(figure, name, time, value, ssrc)
@ -101,13 +101,13 @@
// Print to stdout in tab-separated format suitable for plotting, e.g.:
// BAR figure Context1_Context2_Name x_left width value
// |figure| is a figure id. Different figures are plotted in different windows.
// |name| is a char*, std::string or uint32_t to name the plotted value.
// |value| is a double precision float to be plotted.
// |ylow| and |yhigh| are double precision float for the error line.
// |title| is a string and refers to the error label.
// |ymax| is a double precision float for the limit horizontal line.
// |limit_title| is a string and refers to the limit label.
// `figure` is a figure id. Different figures are plotted in different windows.
// `name` is a char*, std::string or uint32_t to name the plotted value.
// `value` is a double precision float to be plotted.
// `ylow` and `yhigh` are double precision float for the error line.
// `title` is a string and refers to the error label.
// `ymax` is a double precision float for the limit horizontal line.
// `limit_title` is a string and refers to the limit label.
#define BWE_TEST_LOGGING_BAR(figure, name, value, flow_id)
#define BWE_TEST_LOGGING_ERRORBAR(figure, name, value, ylow, yhigh, \
error_title, flow_id)
@ -116,9 +116,9 @@
#define BWE_TEST_LOGGING_BASELINEBAR(figure, name, value, flow_id)
// |num_flows| is an integer refering to the number of RMCAT flows in the
// `num_flows` is an integer refering to the number of RMCAT flows in the
// scenario.
// Define |x_label| and |y_label| for plots.
// Define `x_label` and `y_label` for plots.
#define BWE_TEST_LOGGING_LABEL(figure, x_label, y_label, num_flows)
#else // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
@ -128,7 +128,6 @@
#include <stack>
#include <string>
#include "rtc_base/constructor_magic.h"
#include "rtc_base/synchronization/mutex.h"
#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name) \
@ -263,10 +262,11 @@ class Logging {
Context(uint32_t name, int64_t timestamp_ms, bool enabled);
Context(const std::string& name, int64_t timestamp_ms, bool enabled);
Context(const char* name, int64_t timestamp_ms, bool enabled);
~Context();
private:
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Context);
Context() = delete;
Context(const Context&) = delete;
Context& operator=(const Context&) = delete;
~Context();
};
static Logging* GetInstance();
@ -277,7 +277,7 @@ class Logging {
void SetGlobalEnable(bool enabled);
#if defined(__GNUC__)
// Note: Implicit |this| argument counts as the first argument.
// Note: Implicit `this` argument counts as the first argument.
__attribute__((__format__(__printf__, 2, 3)))
#endif
void
@ -340,6 +340,10 @@ class Logging {
Logging();
~Logging();
Logging(const Logging&) = delete;
Logging& operator=(const Logging&) = delete;
void PushState(const std::string& append_to_tag,
int64_t timestamp_ms,
bool enabled);
@ -347,8 +351,6 @@ class Logging {
Mutex mutex_;
ThreadMap thread_map_;
RTC_DISALLOW_COPY_AND_ASSIGN(Logging);
};
} // namespace bwe
} // namespace testing

View File

@ -18,10 +18,8 @@
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h"
#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h"
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "test/rtp_file_reader.h"
#include "test/rtp_header_parser.h"
ABSL_FLAG(std::string,
extension_type,
@ -65,14 +63,11 @@ std::set<uint32_t> SsrcFilter() {
return ssrcs;
}
std::unique_ptr<webrtc::RtpHeaderParser> ParseArgsAndSetupEstimator(
bool ParseArgsAndSetupRtpReader(
int argc,
char** argv,
webrtc::Clock* clock,
webrtc::RemoteBitrateObserver* observer,
std::unique_ptr<webrtc::test::RtpFileReader>* rtp_reader,
std::unique_ptr<webrtc::RemoteBitrateEstimator>* estimator,
std::string* estimator_used) {
std::unique_ptr<webrtc::test::RtpFileReader>& rtp_reader,
webrtc::RtpHeaderExtensionMap& rtp_header_extensions) {
absl::ParseCommandLine(argc, argv);
std::string filename = InputFile();
@ -84,16 +79,16 @@ std::unique_ptr<webrtc::RtpHeaderParser> ParseArgsAndSetupEstimator(
fprintf(stderr, "\n");
if (filename.substr(filename.find_last_of('.')) == ".pcap") {
fprintf(stderr, "Opening as pcap\n");
rtp_reader->reset(webrtc::test::RtpFileReader::Create(
rtp_reader.reset(webrtc::test::RtpFileReader::Create(
webrtc::test::RtpFileReader::kPcap, filename.c_str(), SsrcFilter()));
} else {
fprintf(stderr, "Opening as rtp\n");
rtp_reader->reset(webrtc::test::RtpFileReader::Create(
rtp_reader.reset(webrtc::test::RtpFileReader::Create(
webrtc::test::RtpFileReader::kRtpDump, filename.c_str()));
}
if (!*rtp_reader) {
if (!rtp_reader) {
fprintf(stderr, "Cannot open input file %s\n", filename.c_str());
return nullptr;
return false;
}
fprintf(stderr, "Input file: %s\n\n", filename.c_str());
@ -105,31 +100,10 @@ std::unique_ptr<webrtc::RtpHeaderParser> ParseArgsAndSetupEstimator(
fprintf(stderr, "Extension: abs\n");
} else {
fprintf(stderr, "Unknown extension type\n");
return nullptr;
return false;
}
// Setup the RTP header parser and the bitrate estimator.
auto parser = webrtc::RtpHeaderParser::CreateForTest();
parser->RegisterRtpHeaderExtension(extension, ExtensionId());
if (estimator) {
switch (extension) {
case webrtc::kRtpExtensionAbsoluteSendTime: {
estimator->reset(
new webrtc::RemoteBitrateEstimatorAbsSendTime(observer, clock));
*estimator_used = "AbsoluteSendTimeRemoteBitrateEstimator";
break;
}
case webrtc::kRtpExtensionTransmissionTimeOffset: {
estimator->reset(
new webrtc::RemoteBitrateEstimatorSingleStream(observer, clock));
*estimator_used = "RemoteBitrateEstimator";
break;
}
default:
assert(false);
return nullptr;
}
}
rtp_header_extensions.RegisterByType(ExtensionId(), extension);
return parser;
return true;
}

View File

@ -12,25 +12,14 @@
#define MODULES_REMOTE_BITRATE_ESTIMATOR_TOOLS_BWE_RTP_H_
#include <memory>
#include <string>
namespace webrtc {
class Clock;
class RemoteBitrateEstimator;
class RemoteBitrateObserver;
class RtpHeaderParser;
namespace test {
class RtpFileReader;
}
} // namespace webrtc
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "test/rtp_file_reader.h"
std::unique_ptr<webrtc::RtpHeaderParser> ParseArgsAndSetupEstimator(
bool ParseArgsAndSetupRtpReader(
int argc,
char** argv,
webrtc::Clock* clock,
webrtc::RemoteBitrateObserver* observer,
std::unique_ptr<webrtc::test::RtpFileReader>* rtp_reader,
std::unique_ptr<webrtc::RemoteBitrateEstimator>* estimator,
std::string* estimator_used);
std::unique_ptr<webrtc::test::RtpFileReader>& rtp_reader,
webrtc::RtpHeaderExtensionMap& rtp_header_extensions);
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_TOOLS_BWE_RTP_H_

View File

@ -13,17 +13,18 @@
#include <memory>
#include "modules/remote_bitrate_estimator/tools/bwe_rtp.h"
#include "rtc_base/format_macros.h"
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_packet.h"
#include "rtc_base/strings/string_builder.h"
#include "test/rtp_file_reader.h"
#include "test/rtp_header_parser.h"
int main(int argc, char* argv[]) {
std::unique_ptr<webrtc::test::RtpFileReader> reader;
std::unique_ptr<webrtc::RtpHeaderParser> parser(ParseArgsAndSetupEstimator(
argc, argv, nullptr, nullptr, &reader, nullptr, nullptr));
if (!parser)
webrtc::RtpHeaderExtensionMap rtp_header_extensions;
if (!ParseArgsAndSetupRtpReader(argc, argv, reader, rtp_header_extensions)) {
return -1;
}
bool arrival_time_only = (argc >= 5 && strncmp(argv[4], "-t", 2) == 0);
@ -35,22 +36,24 @@ int main(int argc, char* argv[]) {
int non_zero_ts_offsets = 0;
webrtc::test::RtpPacket packet;
while (reader->NextPacket(&packet)) {
webrtc::RTPHeader header;
parser->Parse(packet.data, packet.length, &header);
if (header.extension.absoluteSendTime != 0)
webrtc::RtpPacket header(&rtp_header_extensions);
header.Parse(packet.data, packet.length);
uint32_t abs_send_time = 0;
if (header.GetExtension<webrtc::AbsoluteSendTime>(&abs_send_time) &&
abs_send_time != 0)
++non_zero_abs_send_time;
if (header.extension.transmissionTimeOffset != 0)
int32_t toffset = 0;
if (header.GetExtension<webrtc::TransmissionOffset>(&toffset) &&
toffset != 0)
++non_zero_ts_offsets;
if (arrival_time_only) {
rtc::StringBuilder ss;
ss << static_cast<int64_t>(packet.time_ms) * 1000000;
fprintf(stdout, "%s\n", ss.str().c_str());
} else {
fprintf(stdout, "%u %u %d %u %u %d %u %" RTC_PRIuS " %" RTC_PRIuS "\n",
header.sequenceNumber, header.timestamp,
header.extension.transmissionTimeOffset,
header.extension.absoluteSendTime, packet.time_ms,
header.markerBit, header.ssrc, packet.length,
fprintf(stdout, "%u %u %d %u %u %d %u %zu %zu\n", header.SequenceNumber(),
header.Timestamp(), toffset, abs_send_time, packet.time_ms,
header.Marker(), header.Ssrc(), packet.length,
packet.original_length);
}
++packet_counter;