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:
@ -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",
|
||||
|
||||
@ -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",
|
||||
]
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
192
modules/remote_bitrate_estimator/packet_arrival_map.cc
Normal file
192
modules/remote_bitrate_estimator/packet_arrival_map.cc
Normal 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
|
||||
146
modules/remote_bitrate_estimator/packet_arrival_map.h
Normal file
146
modules/remote_bitrate_estimator/packet_arrival_map.h
Normal 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_
|
||||
291
modules/remote_bitrate_estimator/packet_arrival_map_test.cc
Normal file
291
modules/remote_bitrate_estimator/packet_arrival_map_test.cc
Normal 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
|
||||
@ -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, ¤t);
|
||||
}
|
||||
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, ¤t);
|
||||
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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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;
|
||||
|
||||
Reference in New Issue
Block a user