FecControler disables FEC when *below* threshold
(This CL concerns both the PLR-based as well as the RPLR-based versions of FecController.) 1. Make FecController disable only when below the disabling-threshold, so as to prevent toggling when the enabling-curve and the disabling-curve are identical. 2. Extend unit-test coverage accordingly. BUG=None Review-Url: https://codereview.webrtc.org/2906663002 Cr-Commit-Position: refs/heads/master@{#18337}
This commit is contained in:
@ -107,8 +107,8 @@ bool FecControllerPlrBased::FecDisablingDecision(
|
||||
if (!uplink_bandwidth_bps_ || !packet_loss) {
|
||||
return false;
|
||||
} else {
|
||||
// Disable when below the curve or exactly on it.
|
||||
return !config_.fec_disabling_threshold.IsAboveCurve(
|
||||
// Disable when below the curve.
|
||||
return config_.fec_disabling_threshold.IsBelowCurve(
|
||||
{static_cast<float>(*uplink_bandwidth_bps_), *packet_loss});
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,29 +45,38 @@ constexpr float kEnablingPacketLossAtLowBw = 0.1f;
|
||||
constexpr int kEnablingBandwidthHigh = 64000;
|
||||
constexpr float kEnablingPacketLossAtHighBw = 0.05f;
|
||||
|
||||
constexpr float kEpsilon = 1e-5f;
|
||||
|
||||
struct FecControllerPlrBasedTestStates {
|
||||
std::unique_ptr<FecControllerPlrBased> controller;
|
||||
MockSmoothingFilter* packet_loss_smoother;
|
||||
};
|
||||
|
||||
FecControllerPlrBasedTestStates CreateFecControllerPlrBased(
|
||||
bool initial_fec_enabled) {
|
||||
bool initial_fec_enabled,
|
||||
const ThresholdCurve& enabling_curve,
|
||||
const ThresholdCurve& disabling_curve) {
|
||||
FecControllerPlrBasedTestStates states;
|
||||
std::unique_ptr<MockSmoothingFilter> mock_smoothing_filter(
|
||||
new NiceMock<MockSmoothingFilter>());
|
||||
states.packet_loss_smoother = mock_smoothing_filter.get();
|
||||
states.controller.reset(new FecControllerPlrBased(
|
||||
FecControllerPlrBased::Config(
|
||||
initial_fec_enabled,
|
||||
ThresholdCurve(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw,
|
||||
kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw),
|
||||
ThresholdCurve(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw,
|
||||
kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw),
|
||||
0),
|
||||
FecControllerPlrBased::Config(initial_fec_enabled, enabling_curve,
|
||||
disabling_curve, 0),
|
||||
std::move(mock_smoothing_filter)));
|
||||
return states;
|
||||
}
|
||||
|
||||
FecControllerPlrBasedTestStates CreateFecControllerPlrBased(
|
||||
bool initial_fec_enabled) {
|
||||
return CreateFecControllerPlrBased(
|
||||
initial_fec_enabled,
|
||||
ThresholdCurve(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw,
|
||||
kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw),
|
||||
ThresholdCurve(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw,
|
||||
kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw));
|
||||
}
|
||||
|
||||
void UpdateNetworkMetrics(FecControllerPlrBasedTestStates* states,
|
||||
const rtc::Optional<int>& uplink_bandwidth_bps,
|
||||
const rtc::Optional<float>& uplink_packet_loss) {
|
||||
@ -90,6 +99,15 @@ void UpdateNetworkMetrics(FecControllerPlrBasedTestStates* states,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(eladalon): In a separate CL (to make reviewers' lives easier), use
|
||||
// this where applicable.
|
||||
void UpdateNetworkMetrics(FecControllerPlrBasedTestStates* states,
|
||||
int uplink_bandwidth_bps,
|
||||
float uplink_packet_loss) {
|
||||
UpdateNetworkMetrics(states, rtc::Optional<int>(uplink_bandwidth_bps),
|
||||
rtc::Optional<float>(uplink_packet_loss));
|
||||
}
|
||||
|
||||
// Checks that the FEC decision and |uplink_packet_loss_fraction| given by
|
||||
// |states->controller->MakeDecision| matches |expected_enable_fec| and
|
||||
// |expected_uplink_packet_loss_fraction|, respectively.
|
||||
@ -105,25 +123,44 @@ void CheckDecision(FecControllerPlrBasedTestStates* states,
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(FecControllerPlrBasedTest, OutputInitValueBeforeAnyInputsAreReceived) {
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
auto states = CreateFecControllerPlrBased(initial_fec_enabled);
|
||||
CheckDecision(&states, initial_fec_enabled, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FecControllerPlrBasedTest, OutputInitValueWhenUplinkBandwidthUnknown) {
|
||||
constexpr bool kInitialFecEnabled = true;
|
||||
auto states = CreateFecControllerPlrBased(kInitialFecEnabled);
|
||||
// Let uplink packet loss fraction be so low that would cause FEC to turn off
|
||||
// if uplink bandwidth was known.
|
||||
UpdateNetworkMetrics(&states, rtc::Optional<int>(),
|
||||
rtc::Optional<float>(kDisablingPacketLossAtHighBw));
|
||||
CheckDecision(&states, kInitialFecEnabled, kDisablingPacketLossAtHighBw);
|
||||
// Regardless of the initial FEC state and the packet-loss rate,
|
||||
// the initial FEC state is maintained as long as the BWE is unknown.
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
for (float packet_loss :
|
||||
{kDisablingPacketLossAtLowBw - kEpsilon, kDisablingPacketLossAtLowBw,
|
||||
kDisablingPacketLossAtLowBw + kEpsilon,
|
||||
kEnablingPacketLossAtLowBw - kEpsilon, kEnablingPacketLossAtLowBw,
|
||||
kEnablingPacketLossAtLowBw + kEpsilon}) {
|
||||
auto states = CreateFecControllerPlrBased(initial_fec_enabled);
|
||||
UpdateNetworkMetrics(&states, rtc::Optional<int>(),
|
||||
rtc::Optional<float>(packet_loss));
|
||||
CheckDecision(&states, initial_fec_enabled, packet_loss);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FecControllerPlrBasedTest,
|
||||
OutputInitValueWhenUplinkPacketLossFractionUnknown) {
|
||||
constexpr bool kInitialFecEnabled = true;
|
||||
auto states = CreateFecControllerPlrBased(kInitialFecEnabled);
|
||||
// Let uplink bandwidth be so low that would cause FEC to turn off if uplink
|
||||
// bandwidth packet loss fraction was known.
|
||||
UpdateNetworkMetrics(&states, rtc::Optional<int>(kDisablingBandwidthLow - 1),
|
||||
rtc::Optional<float>());
|
||||
CheckDecision(&states, kInitialFecEnabled, 0.0);
|
||||
// Regardless of the initial FEC state and the BWE, the initial FEC state
|
||||
// is maintained as long as the packet-loss rate is unknown.
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
for (int bandwidth : {kDisablingBandwidthLow - 1, kDisablingBandwidthLow,
|
||||
kDisablingBandwidthLow + 1, kEnablingBandwidthLow - 1,
|
||||
kEnablingBandwidthLow, kEnablingBandwidthLow + 1}) {
|
||||
auto states = CreateFecControllerPlrBased(initial_fec_enabled);
|
||||
UpdateNetworkMetrics(&states, rtc::Optional<int>(bandwidth),
|
||||
rtc::Optional<float>());
|
||||
CheckDecision(&states, initial_fec_enabled, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FecControllerPlrBasedTest, EnableFecForHighBandwidth) {
|
||||
@ -209,23 +246,25 @@ TEST(FecControllerPlrBasedTest, MaintainFecOffForVeryLowBandwidth) {
|
||||
|
||||
TEST(FecControllerPlrBasedTest, DisableFecForHighBandwidth) {
|
||||
auto states = CreateFecControllerPlrBased(true);
|
||||
constexpr float kPacketLoss = kDisablingPacketLossAtHighBw - kEpsilon;
|
||||
UpdateNetworkMetrics(&states, rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kDisablingPacketLossAtHighBw));
|
||||
CheckDecision(&states, false, kDisablingPacketLossAtHighBw);
|
||||
rtc::Optional<float>(kPacketLoss));
|
||||
CheckDecision(&states, false, kPacketLoss);
|
||||
}
|
||||
|
||||
TEST(FecControllerPlrBasedTest, MaintainFecOnForHighBandwidth) {
|
||||
// Note: Disabling happens when the value is strictly below the threshold.
|
||||
auto states = CreateFecControllerPlrBased(true);
|
||||
constexpr float kPacketLoss = kDisablingPacketLossAtHighBw * 1.01f;
|
||||
UpdateNetworkMetrics(&states, rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kPacketLoss));
|
||||
CheckDecision(&states, true, kPacketLoss);
|
||||
rtc::Optional<float>(kDisablingPacketLossAtHighBw));
|
||||
CheckDecision(&states, true, kDisablingPacketLossAtHighBw);
|
||||
}
|
||||
|
||||
TEST(FecControllerPlrBasedTest, DisableFecOnMediumBandwidth) {
|
||||
auto states = CreateFecControllerPlrBased(true);
|
||||
constexpr float kPacketLoss =
|
||||
(kDisablingPacketLossAtLowBw + kDisablingPacketLossAtHighBw) / 2.0f;
|
||||
(kDisablingPacketLossAtLowBw + kDisablingPacketLossAtHighBw) / 2.0f -
|
||||
kEpsilon;
|
||||
UpdateNetworkMetrics(
|
||||
&states,
|
||||
rtc::Optional<int>((kDisablingBandwidthHigh + kDisablingBandwidthLow) /
|
||||
@ -237,7 +276,7 @@ TEST(FecControllerPlrBasedTest, DisableFecOnMediumBandwidth) {
|
||||
TEST(FecControllerPlrBasedTest, MaintainFecOnForMediumBandwidth) {
|
||||
auto states = CreateFecControllerPlrBased(true);
|
||||
constexpr float kPacketLoss = kDisablingPacketLossAtLowBw * 0.51f +
|
||||
kDisablingPacketLossAtHighBw * 0.49f;
|
||||
kDisablingPacketLossAtHighBw * 0.49f - kEpsilon;
|
||||
UpdateNetworkMetrics(
|
||||
&states,
|
||||
rtc::Optional<int>((kEnablingBandwidthHigh + kDisablingBandwidthLow) / 2),
|
||||
@ -247,9 +286,10 @@ TEST(FecControllerPlrBasedTest, MaintainFecOnForMediumBandwidth) {
|
||||
|
||||
TEST(FecControllerPlrBasedTest, DisableFecForLowBandwidth) {
|
||||
auto states = CreateFecControllerPlrBased(true);
|
||||
constexpr float kPacketLoss = kDisablingPacketLossAtLowBw - kEpsilon;
|
||||
UpdateNetworkMetrics(&states, rtc::Optional<int>(kDisablingBandwidthLow),
|
||||
rtc::Optional<float>(kDisablingPacketLossAtLowBw));
|
||||
CheckDecision(&states, false, kDisablingPacketLossAtLowBw);
|
||||
rtc::Optional<float>(kPacketLoss));
|
||||
CheckDecision(&states, false, kPacketLoss);
|
||||
}
|
||||
|
||||
TEST(FecControllerPlrBasedTest, DisableFecForVeryLowBandwidth) {
|
||||
@ -284,10 +324,9 @@ TEST(FecControllerPlrBasedTest, CheckBehaviorOnChangingNetworkMetrics) {
|
||||
rtc::Optional<float>(kEnablingPacketLossAtHighBw));
|
||||
CheckDecision(&states, true, kEnablingPacketLossAtHighBw);
|
||||
|
||||
UpdateNetworkMetrics(
|
||||
&states, rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kDisablingPacketLossAtHighBw * 1.01f));
|
||||
CheckDecision(&states, true, kDisablingPacketLossAtHighBw * 1.01f);
|
||||
UpdateNetworkMetrics(&states, rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kDisablingPacketLossAtHighBw));
|
||||
CheckDecision(&states, true, kDisablingPacketLossAtHighBw);
|
||||
|
||||
UpdateNetworkMetrics(&states, rtc::Optional<int>(kDisablingBandwidthHigh + 1),
|
||||
rtc::Optional<float>(0.0));
|
||||
@ -335,16 +374,125 @@ TEST(FecControllerPlrBasedTest, CheckBehaviorOnSpecialCurves) {
|
||||
rtc::Optional<float>(kEnablingPacketLossAtHighBw));
|
||||
CheckDecision(&states, true, kEnablingPacketLossAtHighBw);
|
||||
|
||||
UpdateNetworkMetrics(
|
||||
&states, rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kDisablingPacketLossAtHighBw * 1.01f));
|
||||
CheckDecision(&states, true, kDisablingPacketLossAtHighBw * 1.01f);
|
||||
UpdateNetworkMetrics(&states, rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kDisablingPacketLossAtHighBw));
|
||||
CheckDecision(&states, true, kDisablingPacketLossAtHighBw);
|
||||
|
||||
UpdateNetworkMetrics(&states, rtc::Optional<int>(kDisablingBandwidthHigh + 1),
|
||||
rtc::Optional<float>(0.0));
|
||||
CheckDecision(&states, false, 0.0);
|
||||
}
|
||||
|
||||
TEST(FecControllerPlrBasedTest, SingleThresholdCurveForEnablingAndDisabling) {
|
||||
// Note: To avoid numerical errors, keep kPacketLossAtLowBw and
|
||||
// kPacketLossAthighBw as (negative) integer powers of 2.
|
||||
// This is mostly relevant for the O3 case.
|
||||
constexpr int kBandwidthLow = 10000;
|
||||
constexpr float kPacketLossAtLowBw = 0.25f;
|
||||
constexpr int kBandwidthHigh = 20000;
|
||||
constexpr float kPacketLossAtHighBw = 0.125f;
|
||||
auto curve = ThresholdCurve(kBandwidthLow, kPacketLossAtLowBw, kBandwidthHigh,
|
||||
kPacketLossAtHighBw);
|
||||
|
||||
// B* stands for "below-curve", O* for "on-curve", and A* for "above-curve".
|
||||
//
|
||||
// //
|
||||
// packet-loss ^ //
|
||||
// | | //
|
||||
// | B1 O1 //
|
||||
// | | //
|
||||
// | O2 //
|
||||
// | \ A1 //
|
||||
// | \ //
|
||||
// | O3 A2 //
|
||||
// | B2 \ //
|
||||
// | \ //
|
||||
// | O4--O5---- //
|
||||
// | //
|
||||
// | B3 //
|
||||
// |-----------------> bandwidth //
|
||||
|
||||
struct NetworkState {
|
||||
int bandwidth;
|
||||
float packet_loss;
|
||||
};
|
||||
|
||||
std::vector<NetworkState> below{
|
||||
{kBandwidthLow - 1, kPacketLossAtLowBw + 0.1f}, // B1
|
||||
{(kBandwidthLow + kBandwidthHigh) / 2,
|
||||
(kPacketLossAtLowBw + kPacketLossAtHighBw) / 2 - kEpsilon}, // B2
|
||||
{kBandwidthHigh + 1, kPacketLossAtHighBw - kEpsilon} // B3
|
||||
};
|
||||
|
||||
std::vector<NetworkState> on{
|
||||
{kBandwidthLow, kPacketLossAtLowBw + 0.1f}, // O1
|
||||
{kBandwidthLow, kPacketLossAtLowBw}, // O2
|
||||
{(kBandwidthLow + kBandwidthHigh) / 2,
|
||||
(kPacketLossAtLowBw + kPacketLossAtHighBw) / 2}, // O3
|
||||
{kBandwidthHigh, kPacketLossAtHighBw}, // O4
|
||||
{kBandwidthHigh + 1, kPacketLossAtHighBw}, // O5
|
||||
};
|
||||
|
||||
std::vector<NetworkState> above{
|
||||
{(kBandwidthLow + kBandwidthHigh) / 2,
|
||||
(kPacketLossAtLowBw + kPacketLossAtHighBw) / 2 + kEpsilon}, // A1
|
||||
{kBandwidthHigh + 1, kPacketLossAtHighBw + kEpsilon}, // A2
|
||||
};
|
||||
|
||||
// Test that FEC is turned off whenever we're below the curve, independent
|
||||
// of the starting FEC state.
|
||||
for (NetworkState net_state : below) {
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
auto states =
|
||||
CreateFecControllerPlrBased(initial_fec_enabled, curve, curve);
|
||||
UpdateNetworkMetrics(&states, net_state.bandwidth, net_state.packet_loss);
|
||||
CheckDecision(&states, false, net_state.packet_loss);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that FEC is turned on whenever we're on the curve or above it,
|
||||
// independent of the starting FEC state.
|
||||
for (std::vector<NetworkState> states_list : {on, above}) {
|
||||
for (NetworkState net_state : states_list) {
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
auto states =
|
||||
CreateFecControllerPlrBased(initial_fec_enabled, curve, curve);
|
||||
UpdateNetworkMetrics(&states, net_state.bandwidth,
|
||||
net_state.packet_loss);
|
||||
CheckDecision(&states, true, net_state.packet_loss);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FecControllerPlrBasedTest, FecAlwaysOff) {
|
||||
ThresholdCurve always_off_curve(0, 1.0f + kEpsilon, 0, 1.0f + kEpsilon);
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
for (int bandwidth : {0, 10000}) {
|
||||
for (float packet_loss : {0.0f, 0.5f, 1.0f}) {
|
||||
auto states = CreateFecControllerPlrBased(
|
||||
initial_fec_enabled, always_off_curve, always_off_curve);
|
||||
UpdateNetworkMetrics(&states, bandwidth, packet_loss);
|
||||
CheckDecision(&states, false, packet_loss);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FecControllerPlrBasedTest, FecAlwaysOn) {
|
||||
ThresholdCurve always_on_curve(0, 0.0f, 0, 0.0f);
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
for (int bandwidth : {0, 10000}) {
|
||||
for (float packet_loss : {0.0f, 0.5f, 1.0f}) {
|
||||
auto states = CreateFecControllerPlrBased(
|
||||
initial_fec_enabled, always_on_curve, always_on_curve);
|
||||
UpdateNetworkMetrics(&states, bandwidth, packet_loss);
|
||||
CheckDecision(&states, true, packet_loss);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
|
||||
TEST(FecControllerPlrBasedDeathTest, InvalidConfig) {
|
||||
FecControllerPlrBasedTestStates states;
|
||||
|
||||
@ -68,8 +68,8 @@ bool FecControllerRplrBased::FecDisablingDecision() const {
|
||||
if (!uplink_bandwidth_bps_ || !uplink_recoverable_packet_loss_) {
|
||||
return false;
|
||||
} else {
|
||||
// Disable when below the curve or exactly on it.
|
||||
return !config_.fec_disabling_threshold.IsAboveCurve(
|
||||
// Disable when below the curve.
|
||||
return config_.fec_disabling_threshold.IsBelowCurve(
|
||||
{static_cast<float>(*uplink_bandwidth_bps_),
|
||||
*uplink_recoverable_packet_loss_});
|
||||
}
|
||||
|
||||
@ -42,6 +42,8 @@ constexpr float kEnablingRecoverablePacketLossAtLowBw = 0.1f;
|
||||
constexpr int kEnablingBandwidthHigh = 64000;
|
||||
constexpr float kEnablingRecoverablePacketLossAtHighBw = 0.05f;
|
||||
|
||||
constexpr float kEpsilon = 1e-5f;
|
||||
|
||||
rtc::Optional<float> GetRandomProbabilityOrUnknown() {
|
||||
std::random_device rd;
|
||||
std::mt19937 generator(rd());
|
||||
@ -98,7 +100,7 @@ void UpdateNetworkMetrics(
|
||||
FecControllerRplrBased* controller,
|
||||
const rtc::Optional<int>& uplink_bandwidth_bps,
|
||||
const rtc::Optional<float>& uplink_recoveralbe_packet_loss) {
|
||||
// FecControllerRplrBased doesn't current use the PLR (general packet-loss
|
||||
// FecControllerRplrBased doesn't currently use the PLR (general packet-loss
|
||||
// rate) at all. (This might be changed in the future.) The unit-tests will
|
||||
// use a random value (including unknown), to show this does not interfere.
|
||||
UpdateNetworkMetrics(controller, uplink_bandwidth_bps,
|
||||
@ -106,6 +108,15 @@ void UpdateNetworkMetrics(
|
||||
uplink_recoveralbe_packet_loss);
|
||||
}
|
||||
|
||||
// TODO(eladalon): In a separate CL (to make reviewers' lives easier), use
|
||||
// this where applicable.
|
||||
void UpdateNetworkMetrics(FecControllerRplrBased* controller,
|
||||
int uplink_bandwidth_bps,
|
||||
float uplink_recoveralbe_packet_loss) {
|
||||
UpdateNetworkMetrics(controller, rtc::Optional<int>(uplink_bandwidth_bps),
|
||||
rtc::Optional<float>(uplink_recoveralbe_packet_loss));
|
||||
}
|
||||
|
||||
// Checks that the FEC decision and |uplink_packet_loss_fraction| given by
|
||||
// |states->controller->MakeDecision| matches |expected_enable_fec| and
|
||||
// |expected_uplink_packet_loss_fraction|, respectively.
|
||||
@ -129,29 +140,46 @@ void CheckDecision(FecControllerRplrBased* controller,
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(FecControllerRplrBasedTest, OutputInitValueWhenUplinkBandwidthUnknown) {
|
||||
TEST(FecControllerRplrBasedTest, OutputInitValueBeforeAnyInputsAreReceived) {
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
auto controller = CreateFecControllerRplrBased(initial_fec_enabled);
|
||||
// Let uplink recoverable packet loss fraction be so low that it
|
||||
// would cause FEC to turn off if uplink bandwidth was known.
|
||||
UpdateNetworkMetrics(
|
||||
controller.get(), rtc::Optional<int>(),
|
||||
rtc::Optional<float>(kDisablingRecoverablePacketLossAtHighBw));
|
||||
CheckDecision(controller.get(), initial_fec_enabled,
|
||||
kDisablingRecoverablePacketLossAtHighBw);
|
||||
CheckDecision(controller.get(), initial_fec_enabled, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, OutputInitValueWhenUplinkBandwidthUnknown) {
|
||||
// Regardless of the initial FEC state and the recoverable-packet-loss
|
||||
// rate, the initial FEC state is maintained as long as the BWE is unknown.
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
for (float recoverable_packet_loss :
|
||||
{kDisablingRecoverablePacketLossAtHighBw - kEpsilon,
|
||||
kDisablingRecoverablePacketLossAtHighBw,
|
||||
kDisablingRecoverablePacketLossAtHighBw + kEpsilon,
|
||||
kEnablingRecoverablePacketLossAtHighBw - kEpsilon,
|
||||
kEnablingRecoverablePacketLossAtHighBw,
|
||||
kEnablingRecoverablePacketLossAtHighBw + kEpsilon}) {
|
||||
auto controller = CreateFecControllerRplrBased(initial_fec_enabled);
|
||||
UpdateNetworkMetrics(controller.get(), rtc::Optional<int>(),
|
||||
rtc::Optional<float>(recoverable_packet_loss));
|
||||
CheckDecision(controller.get(), initial_fec_enabled,
|
||||
recoverable_packet_loss);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest,
|
||||
OutputInitValueWhenUplinkPacketLossFractionUnknown) {
|
||||
OutputInitValueWhenUplinkRecoverablePacketLossFractionUnknown) {
|
||||
// Regardless of the initial FEC state and the BWE, the initial FEC state
|
||||
// is maintained as long as the recoverable-packet-loss rate is unknown.
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
auto controller = CreateFecControllerRplrBased(initial_fec_enabled);
|
||||
// Let uplink bandwidth be so low that it would cause FEC to turn off
|
||||
// if uplink bandwidth packet loss fraction was known.
|
||||
UpdateNetworkMetrics(controller.get(),
|
||||
rtc::Optional<int>(kDisablingBandwidthLow - 1),
|
||||
rtc::Optional<float>());
|
||||
CheckDecision(controller.get(), initial_fec_enabled, 0.0);
|
||||
for (int bandwidth : {kDisablingBandwidthLow - 1, kDisablingBandwidthLow,
|
||||
kDisablingBandwidthLow + 1, kEnablingBandwidthLow - 1,
|
||||
kEnablingBandwidthLow, kEnablingBandwidthLow + 1}) {
|
||||
auto controller = CreateFecControllerRplrBased(initial_fec_enabled);
|
||||
UpdateNetworkMetrics(controller.get(), rtc::Optional<int>(bandwidth),
|
||||
rtc::Optional<float>());
|
||||
CheckDecision(controller.get(), initial_fec_enabled, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,34 +213,36 @@ TEST(FecControllerRplrBasedTest, UpdateMultipleNetworkMetricsAtOnce) {
|
||||
|
||||
TEST(FecControllerRplrBasedTest, MaintainFecOffForHighBandwidth) {
|
||||
auto controller = CreateFecControllerRplrBased(false);
|
||||
constexpr float kPacketLoss = kEnablingRecoverablePacketLossAtHighBw * 0.99f;
|
||||
constexpr float kRecoverablePacketLoss =
|
||||
kEnablingRecoverablePacketLossAtHighBw * 0.99f;
|
||||
UpdateNetworkMetrics(controller.get(),
|
||||
rtc::Optional<int>(kEnablingBandwidthHigh),
|
||||
rtc::Optional<float>(kPacketLoss));
|
||||
CheckDecision(controller.get(), false, kPacketLoss);
|
||||
rtc::Optional<float>(kRecoverablePacketLoss));
|
||||
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, EnableFecForMediumBandwidth) {
|
||||
auto controller = CreateFecControllerRplrBased(false);
|
||||
constexpr float kPacketLoss = (kEnablingRecoverablePacketLossAtLowBw +
|
||||
kEnablingRecoverablePacketLossAtHighBw) /
|
||||
2.0;
|
||||
constexpr float kRecoverablePacketLoss =
|
||||
(kEnablingRecoverablePacketLossAtLowBw +
|
||||
kEnablingRecoverablePacketLossAtHighBw) / 2.0;
|
||||
UpdateNetworkMetrics(
|
||||
controller.get(),
|
||||
rtc::Optional<int>((kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2),
|
||||
rtc::Optional<float>(kPacketLoss));
|
||||
CheckDecision(controller.get(), true, kPacketLoss);
|
||||
rtc::Optional<float>(kRecoverablePacketLoss));
|
||||
CheckDecision(controller.get(), true, kRecoverablePacketLoss);
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, MaintainFecOffForMediumBandwidth) {
|
||||
auto controller = CreateFecControllerRplrBased(false);
|
||||
constexpr float kPacketLoss = kEnablingRecoverablePacketLossAtLowBw * 0.49f +
|
||||
kEnablingRecoverablePacketLossAtHighBw * 0.51f;
|
||||
constexpr float kRecoverablePacketLoss =
|
||||
kEnablingRecoverablePacketLossAtLowBw * 0.49f +
|
||||
kEnablingRecoverablePacketLossAtHighBw * 0.51f;
|
||||
UpdateNetworkMetrics(
|
||||
controller.get(),
|
||||
rtc::Optional<int>((kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2),
|
||||
rtc::Optional<float>(kPacketLoss));
|
||||
CheckDecision(controller.get(), false, kPacketLoss);
|
||||
rtc::Optional<float>(kRecoverablePacketLoss));
|
||||
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, EnableFecForLowBandwidth) {
|
||||
@ -225,11 +255,12 @@ TEST(FecControllerRplrBasedTest, EnableFecForLowBandwidth) {
|
||||
|
||||
TEST(FecControllerRplrBasedTest, MaintainFecOffForLowBandwidth) {
|
||||
auto controller = CreateFecControllerRplrBased(false);
|
||||
constexpr float kPacketLoss = kEnablingRecoverablePacketLossAtLowBw * 0.99f;
|
||||
constexpr float kRecoverablePacketLoss =
|
||||
kEnablingRecoverablePacketLossAtLowBw * 0.99f;
|
||||
UpdateNetworkMetrics(controller.get(),
|
||||
rtc::Optional<int>(kEnablingBandwidthLow),
|
||||
rtc::Optional<float>(kPacketLoss));
|
||||
CheckDecision(controller.get(), false, kPacketLoss);
|
||||
rtc::Optional<float>(kRecoverablePacketLoss));
|
||||
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, MaintainFecOffForVeryLowBandwidth) {
|
||||
@ -244,53 +275,57 @@ TEST(FecControllerRplrBasedTest, MaintainFecOffForVeryLowBandwidth) {
|
||||
|
||||
TEST(FecControllerRplrBasedTest, DisableFecForHighBandwidth) {
|
||||
auto controller = CreateFecControllerRplrBased(true);
|
||||
UpdateNetworkMetrics(
|
||||
controller.get(), rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kDisablingRecoverablePacketLossAtHighBw));
|
||||
CheckDecision(controller.get(), false,
|
||||
kDisablingRecoverablePacketLossAtHighBw);
|
||||
constexpr float kRecoverablePacketLoss =
|
||||
kDisablingRecoverablePacketLossAtHighBw - kEpsilon;
|
||||
UpdateNetworkMetrics(controller.get(),
|
||||
rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kRecoverablePacketLoss));
|
||||
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, MaintainFecOnForHighBandwidth) {
|
||||
// Note: Disabling happens when the value is strictly below the threshold.
|
||||
auto controller = CreateFecControllerRplrBased(true);
|
||||
constexpr float kPacketLoss = kDisablingRecoverablePacketLossAtHighBw * 1.01f;
|
||||
UpdateNetworkMetrics(controller.get(),
|
||||
rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kPacketLoss));
|
||||
CheckDecision(controller.get(), true, kPacketLoss);
|
||||
UpdateNetworkMetrics(
|
||||
controller.get(), rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kDisablingRecoverablePacketLossAtHighBw));
|
||||
CheckDecision(controller.get(), true,
|
||||
kDisablingRecoverablePacketLossAtHighBw);
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, DisableFecOnMediumBandwidth) {
|
||||
auto controller = CreateFecControllerRplrBased(true);
|
||||
constexpr float kPacketLoss = (kDisablingRecoverablePacketLossAtLowBw +
|
||||
kDisablingRecoverablePacketLossAtHighBw) /
|
||||
2.0f;
|
||||
constexpr float kRecoverablePacketLoss =
|
||||
((kDisablingRecoverablePacketLossAtLowBw +
|
||||
kDisablingRecoverablePacketLossAtHighBw) / 2.0f) - kEpsilon;
|
||||
UpdateNetworkMetrics(
|
||||
controller.get(),
|
||||
rtc::Optional<int>((kDisablingBandwidthHigh + kDisablingBandwidthLow) /
|
||||
2),
|
||||
rtc::Optional<float>(kPacketLoss));
|
||||
CheckDecision(controller.get(), false, kPacketLoss);
|
||||
rtc::Optional<float>(kRecoverablePacketLoss));
|
||||
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, MaintainFecOnForMediumBandwidth) {
|
||||
auto controller = CreateFecControllerRplrBased(true);
|
||||
constexpr float kPacketLoss = kDisablingRecoverablePacketLossAtLowBw * 0.51f +
|
||||
kDisablingRecoverablePacketLossAtHighBw * 0.49f;
|
||||
constexpr float kRecoverablePacketLoss =
|
||||
kDisablingRecoverablePacketLossAtLowBw * 0.51f +
|
||||
kDisablingRecoverablePacketLossAtHighBw * 0.49f - kEpsilon;
|
||||
UpdateNetworkMetrics(
|
||||
controller.get(),
|
||||
rtc::Optional<int>((kEnablingBandwidthHigh + kDisablingBandwidthLow) / 2),
|
||||
rtc::Optional<float>(kPacketLoss));
|
||||
CheckDecision(controller.get(), true, kPacketLoss);
|
||||
rtc::Optional<float>(kRecoverablePacketLoss));
|
||||
CheckDecision(controller.get(), true, kRecoverablePacketLoss);
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, DisableFecForLowBandwidth) {
|
||||
auto controller = CreateFecControllerRplrBased(true);
|
||||
UpdateNetworkMetrics(
|
||||
controller.get(), rtc::Optional<int>(kDisablingBandwidthLow),
|
||||
rtc::Optional<float>(kDisablingRecoverablePacketLossAtLowBw));
|
||||
CheckDecision(controller.get(), false,
|
||||
kDisablingRecoverablePacketLossAtLowBw);
|
||||
constexpr float kRecoverablePacketLoss =
|
||||
kDisablingRecoverablePacketLossAtLowBw - kEpsilon;
|
||||
UpdateNetworkMetrics(controller.get(),
|
||||
rtc::Optional<int>(kDisablingBandwidthLow),
|
||||
rtc::Optional<float>(kRecoverablePacketLoss));
|
||||
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, DisableFecForVeryLowBandwidth) {
|
||||
@ -333,9 +368,9 @@ TEST(FecControllerRplrBasedTest, CheckBehaviorOnChangingNetworkMetrics) {
|
||||
|
||||
UpdateNetworkMetrics(
|
||||
controller.get(), rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kDisablingRecoverablePacketLossAtHighBw * 1.01f));
|
||||
rtc::Optional<float>(kDisablingRecoverablePacketLossAtHighBw));
|
||||
CheckDecision(controller.get(), true,
|
||||
kDisablingRecoverablePacketLossAtHighBw * 1.01f);
|
||||
kDisablingRecoverablePacketLossAtHighBw);
|
||||
|
||||
UpdateNetworkMetrics(controller.get(),
|
||||
rtc::Optional<int>(kDisablingBandwidthHigh + 1),
|
||||
@ -386,9 +421,8 @@ TEST(FecControllerRplrBasedTest, CheckBehaviorOnSpecialCurves) {
|
||||
|
||||
UpdateNetworkMetrics(
|
||||
&controller, rtc::Optional<int>(kDisablingBandwidthHigh),
|
||||
rtc::Optional<float>(kDisablingRecoverablePacketLossAtHighBw * 1.01f));
|
||||
CheckDecision(&controller, true,
|
||||
kDisablingRecoverablePacketLossAtHighBw * 1.01f);
|
||||
rtc::Optional<float>(kDisablingRecoverablePacketLossAtHighBw));
|
||||
CheckDecision(&controller, true, kDisablingRecoverablePacketLossAtHighBw);
|
||||
|
||||
UpdateNetworkMetrics(&controller,
|
||||
rtc::Optional<int>(kDisablingBandwidthHigh + 1),
|
||||
@ -396,6 +430,120 @@ TEST(FecControllerRplrBasedTest, CheckBehaviorOnSpecialCurves) {
|
||||
CheckDecision(&controller, false, 0.0);
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, SingleThresholdCurveForEnablingAndDisabling) {
|
||||
// Note: To avoid numerical errors, keep kRecoverablePacketLossAtLowBw and
|
||||
// kRecoverablePacketLossAthighBw as (negative) integer powers of 2.
|
||||
// This is mostly relevant for the O3 case.
|
||||
constexpr int kBandwidthLow = 10000;
|
||||
constexpr float kRecoverablePacketLossAtLowBw = 0.25f;
|
||||
constexpr int kBandwidthHigh = 20000;
|
||||
constexpr float kRecoverablePacketLossAtHighBw = 0.125f;
|
||||
auto curve = ThresholdCurve(kBandwidthLow, kRecoverablePacketLossAtLowBw,
|
||||
kBandwidthHigh, kRecoverablePacketLossAtHighBw);
|
||||
|
||||
// B* stands for "below-curve", O* for "on-curve", and A* for "above-curve".
|
||||
//
|
||||
// //
|
||||
// recoverable ^ //
|
||||
// packet-loss | | //
|
||||
// | B1 O1 //
|
||||
// | | //
|
||||
// | O2 //
|
||||
// | \ A1 //
|
||||
// | \ //
|
||||
// | O3 A2 //
|
||||
// | B2 \ //
|
||||
// | \ //
|
||||
// | O4--O5---- //
|
||||
// | //
|
||||
// | B3 //
|
||||
// |-----------------> bandwidth //
|
||||
|
||||
struct NetworkState {
|
||||
int bandwidth;
|
||||
float recoverable_packet_loss;
|
||||
};
|
||||
|
||||
std::vector<NetworkState> below{
|
||||
{kBandwidthLow - 1, kRecoverablePacketLossAtLowBw + 0.1f}, // B1
|
||||
{(kBandwidthLow + kBandwidthHigh) / 2,
|
||||
(kRecoverablePacketLossAtLowBw + kRecoverablePacketLossAtHighBw) / 2 -
|
||||
kEpsilon}, // B2
|
||||
{kBandwidthHigh + 1, kRecoverablePacketLossAtHighBw - kEpsilon} // B3
|
||||
};
|
||||
|
||||
std::vector<NetworkState> on{
|
||||
{kBandwidthLow, kRecoverablePacketLossAtLowBw + 0.1f}, // O1
|
||||
{kBandwidthLow, kRecoverablePacketLossAtLowBw}, // O2
|
||||
{(kBandwidthLow + kBandwidthHigh) / 2,
|
||||
(kRecoverablePacketLossAtLowBw + kRecoverablePacketLossAtHighBw) /
|
||||
2}, // O3
|
||||
{kBandwidthHigh, kRecoverablePacketLossAtHighBw}, // O4
|
||||
{kBandwidthHigh + 1, kRecoverablePacketLossAtHighBw}, // O5
|
||||
};
|
||||
|
||||
std::vector<NetworkState> above{
|
||||
{(kBandwidthLow + kBandwidthHigh) / 2,
|
||||
(kRecoverablePacketLossAtLowBw + kRecoverablePacketLossAtHighBw) / 2 +
|
||||
kEpsilon}, // A1
|
||||
{kBandwidthHigh + 1, kRecoverablePacketLossAtHighBw + kEpsilon}, // A2
|
||||
};
|
||||
|
||||
// Test that FEC is turned off whenever we're below the curve, independent
|
||||
// of the starting FEC state.
|
||||
for (NetworkState net_state : below) {
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
FecControllerRplrBased controller(
|
||||
FecControllerRplrBased::Config(initial_fec_enabled, curve, curve));
|
||||
UpdateNetworkMetrics(&controller, net_state.bandwidth,
|
||||
net_state.recoverable_packet_loss);
|
||||
CheckDecision(&controller, false, net_state.recoverable_packet_loss);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that FEC is turned on whenever we're on the curve or above it,
|
||||
// independent of the starting FEC state.
|
||||
for (std::vector<NetworkState> states_list : {on, above}) {
|
||||
for (NetworkState net_state : states_list) {
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
FecControllerRplrBased controller(
|
||||
FecControllerRplrBased::Config(initial_fec_enabled, curve, curve));
|
||||
UpdateNetworkMetrics(&controller, net_state.bandwidth,
|
||||
net_state.recoverable_packet_loss);
|
||||
CheckDecision(&controller, true, net_state.recoverable_packet_loss);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, FecAlwaysOff) {
|
||||
ThresholdCurve always_off_curve(0, 1.0f + kEpsilon, 0, 1.0f + kEpsilon);
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
for (int bandwidth : {0, 10000}) {
|
||||
for (float recoverable_packet_loss : {0.0f, 0.5f, 1.0f}) {
|
||||
FecControllerRplrBased controller(FecControllerRplrBased::Config(
|
||||
initial_fec_enabled, always_off_curve, always_off_curve));
|
||||
UpdateNetworkMetrics(&controller, bandwidth, recoverable_packet_loss);
|
||||
CheckDecision(&controller, false, recoverable_packet_loss);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FecControllerRplrBasedTest, FecAlwaysOn) {
|
||||
ThresholdCurve always_on_curve(0, 0.0f, 0, 0.0f);
|
||||
for (bool initial_fec_enabled : {false, true}) {
|
||||
for (int bandwidth : {0, 10000}) {
|
||||
for (float recoverable_packet_loss : {0.0f, 0.5f, 1.0f}) {
|
||||
FecControllerRplrBased controller(FecControllerRplrBased::Config(
|
||||
initial_fec_enabled, always_on_curve, always_on_curve));
|
||||
UpdateNetworkMetrics(&controller, bandwidth, recoverable_packet_loss);
|
||||
CheckDecision(&controller, true, recoverable_packet_loss);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
|
||||
TEST(FecControllerRplrBasedDeathTest, InvalidConfig) {
|
||||
EXPECT_DEATH(
|
||||
|
||||
Reference in New Issue
Block a user