Index: webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based_unittest.cc |
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based_unittest.cc b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ce51ff00d80fc9c103b0bdebca99082ff957fc6a |
--- /dev/null |
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based_unittest.cc |
@@ -0,0 +1,423 @@ |
+/* |
+ * Copyright (c) 2017 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 <random> |
+#include <utility> |
+ |
+#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.h" |
+#include "webrtc/test/gmock.h" |
+#include "webrtc/test/gtest.h" |
+ |
+namespace webrtc { |
+ |
+using ::testing::NiceMock; |
+using ::testing::Return; |
+using ::testing::_; |
+ |
+namespace { |
+ |
+// The test uses the following settings: |
+// |
+// recoverable ^ |
+// packet-loss | | | |
+// | A| C| FEC |
+// | \ \ ON |
+// | FEC \ D\_______ |
+// | OFF B\_________ |
+// |-----------------> bandwidth |
+// |
+// A : (kDisablingBandwidthLow, kDisablingRecoverablePacketLossAtLowBw) |
+// B : (kDisablingBandwidthHigh, kDisablingRecoverablePacketLossAtHighBw) |
+// C : (kEnablingBandwidthLow, kEnablingRecoverablePacketLossAtLowBw) |
+// D : (kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw) |
+ |
+constexpr int kDisablingBandwidthLow = 15000; |
+constexpr float kDisablingRecoverablePacketLossAtLowBw = 0.08f; |
+constexpr int kDisablingBandwidthHigh = 64000; |
+constexpr float kDisablingRecoverablePacketLossAtHighBw = 0.01f; |
+constexpr int kEnablingBandwidthLow = 17000; |
+constexpr float kEnablingRecoverablePacketLossAtLowBw = 0.1f; |
+constexpr int kEnablingBandwidthHigh = 64000; |
+constexpr float kEnablingRecoverablePacketLossAtHighBw = 0.05f; |
+ |
+rtc::Optional<float> GetRandomProbabilityOrUnknown() { |
+ std::random_device rd; |
+ std::mt19937 generator(rd()); |
+ std::uniform_real_distribution<> distribution(0, 1); |
+ |
+ if (distribution(generator) < 0.2) { |
+ return rtc::Optional<float>(); |
+ } else { |
+ return rtc::Optional<float>(distribution(generator)); |
+ } |
+} |
+ |
+std::unique_ptr<FecControllerRplrBased> CreateFecControllerRplrBased( |
+ bool initial_fec_enabled) { |
+ using Threshold = FecControllerRplrBased::Config::Threshold; |
+ return std::unique_ptr<FecControllerRplrBased>( |
+ new FecControllerRplrBased(FecControllerRplrBased::Config( |
+ initial_fec_enabled, |
+ Threshold( |
+ kEnablingBandwidthLow, kEnablingRecoverablePacketLossAtLowBw, |
+ kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw), |
+ Threshold( |
+ kDisablingBandwidthLow, kDisablingRecoverablePacketLossAtLowBw, |
+ kDisablingBandwidthHigh, kDisablingRecoverablePacketLossAtHighBw), |
+ 0, nullptr))); |
+} |
+ |
+void UpdateNetworkMetrics( |
+ FecControllerRplrBased* controller, |
+ const rtc::Optional<int>& uplink_bandwidth_bps, |
+ const rtc::Optional<float>& uplink_packet_loss, |
+ const rtc::Optional<float>& uplink_recoveralbe_packet_loss) { |
+ // UpdateNetworkMetrics can accept multiple network metric updates at once. |
+ // However, currently, the most used case is to update one metric at a time. |
+ // To reflect this fact, we separate the calls. |
+ if (uplink_bandwidth_bps) { |
+ Controller::NetworkMetrics network_metrics; |
+ network_metrics.uplink_bandwidth_bps = uplink_bandwidth_bps; |
+ controller->UpdateNetworkMetrics(network_metrics); |
+ } |
+ if (uplink_packet_loss) { |
+ Controller::NetworkMetrics network_metrics; |
+ network_metrics.uplink_packet_loss_fraction = uplink_packet_loss; |
+ controller->UpdateNetworkMetrics(network_metrics); |
+ } |
+ if (uplink_recoveralbe_packet_loss) { |
+ Controller::NetworkMetrics network_metrics; |
+ network_metrics.uplink_recoverable_packet_loss_fraction = |
+ uplink_recoveralbe_packet_loss; |
+ controller->UpdateNetworkMetrics(network_metrics); |
+ } |
+} |
+ |
+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 |
+ // 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, |
+ GetRandomProbabilityOrUnknown(), |
+ 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. |
+void CheckDecision(FecControllerRplrBased* controller, |
+ bool expected_enable_fec, |
+ float expected_uplink_packet_loss_fraction) { |
+ AudioNetworkAdaptor::EncoderRuntimeConfig config; |
+ controller->MakeDecision(&config); |
+ |
+ // Less compact than comparing optionals, but yields more readable errors. |
+ EXPECT_TRUE(config.enable_fec); |
+ if (config.enable_fec) { |
+ EXPECT_EQ(expected_enable_fec, *config.enable_fec); |
+ } |
+ EXPECT_TRUE(config.uplink_packet_loss_fraction); |
+ if (config.uplink_packet_loss_fraction) { |
+ EXPECT_EQ(expected_uplink_packet_loss_fraction, |
+ *config.uplink_packet_loss_fraction); |
+ } |
+} |
+ |
+} // namespace |
+ |
+TEST(FecControllerRplrBasedTest, OutputInitValueWhenUplinkBandwidthUnknown) { |
+ 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); |
+ } |
+} |
+ |
+TEST(FecControllerRplrBasedTest, |
+ OutputInitValueWhenUplinkPacketLossFractionUnknown) { |
+ 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); |
+ } |
+} |
+ |
+TEST(FecControllerRplrBasedTest, EnableFecForHighBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(false); |
+ UpdateNetworkMetrics( |
+ controller.get(), rtc::Optional<int>(kEnablingBandwidthHigh), |
+ rtc::Optional<float>(kEnablingRecoverablePacketLossAtHighBw)); |
+ CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtHighBw); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, UpdateMultipleNetworkMetricsAtOnce) { |
+ // This test is similar to EnableFecForHighBandwidth. But instead of |
+ // using ::UpdateNetworkMetrics(...), which calls |
+ // FecControllerRplrBasedTest::UpdateNetworkMetrics(...) multiple times, we |
+ // we call it only once. This is to verify that |
+ // FecControllerRplrBasedTest::UpdateNetworkMetrics(...) can handle multiple |
+ // network updates at once. This is, however, not a common use case in current |
+ // audio_network_adaptor_impl.cc. |
+ auto controller = CreateFecControllerRplrBased(false); |
+ Controller::NetworkMetrics network_metrics; |
+ network_metrics.uplink_bandwidth_bps = |
+ rtc::Optional<int>(kEnablingBandwidthHigh); |
+ network_metrics.uplink_packet_loss_fraction = |
+ rtc::Optional<float>(GetRandomProbabilityOrUnknown()); |
+ network_metrics.uplink_recoverable_packet_loss_fraction = |
+ rtc::Optional<float>(kEnablingRecoverablePacketLossAtHighBw); |
+ controller->UpdateNetworkMetrics(network_metrics); |
+ CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtHighBw); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, MaintainFecOffForHighBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(false); |
+ constexpr float kPacketLoss = kEnablingRecoverablePacketLossAtHighBw * 0.99f; |
+ UpdateNetworkMetrics(controller.get(), |
+ rtc::Optional<int>(kEnablingBandwidthHigh), |
+ rtc::Optional<float>(kPacketLoss)); |
+ CheckDecision(controller.get(), false, kPacketLoss); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, EnableFecForMediumBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(false); |
+ constexpr float kPacketLoss = (kEnablingRecoverablePacketLossAtLowBw + |
+ kEnablingRecoverablePacketLossAtHighBw) / |
+ 2.0; |
+ UpdateNetworkMetrics( |
+ controller.get(), |
+ rtc::Optional<int>((kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2), |
+ rtc::Optional<float>(kPacketLoss)); |
+ CheckDecision(controller.get(), true, kPacketLoss); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, MaintainFecOffForMediumBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(false); |
+ constexpr float kPacketLoss = kEnablingRecoverablePacketLossAtLowBw * 0.49f + |
+ kEnablingRecoverablePacketLossAtHighBw * 0.51f; |
+ UpdateNetworkMetrics( |
+ controller.get(), |
+ rtc::Optional<int>((kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2), |
+ rtc::Optional<float>(kPacketLoss)); |
+ CheckDecision(controller.get(), false, kPacketLoss); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, EnableFecForLowBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(false); |
+ UpdateNetworkMetrics( |
+ controller.get(), rtc::Optional<int>(kEnablingBandwidthLow), |
+ rtc::Optional<float>(kEnablingRecoverablePacketLossAtLowBw)); |
+ CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtLowBw); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, MaintainFecOffForLowBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(false); |
+ constexpr float kPacketLoss = kEnablingRecoverablePacketLossAtLowBw * 0.99f; |
+ UpdateNetworkMetrics(controller.get(), |
+ rtc::Optional<int>(kEnablingBandwidthLow), |
+ rtc::Optional<float>(kPacketLoss)); |
+ CheckDecision(controller.get(), false, kPacketLoss); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, MaintainFecOffForVeryLowBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(false); |
+ // Below |kEnablingBandwidthLow|, no recoverable packet loss fraction can |
+ // cause FEC to turn on. |
+ UpdateNetworkMetrics(controller.get(), |
+ rtc::Optional<int>(kEnablingBandwidthLow - 1), |
+ rtc::Optional<float>(1.0)); |
+ CheckDecision(controller.get(), false, 1.0); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, DisableFecForHighBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(true); |
+ UpdateNetworkMetrics( |
+ controller.get(), rtc::Optional<int>(kDisablingBandwidthHigh), |
+ rtc::Optional<float>(kDisablingRecoverablePacketLossAtHighBw)); |
+ CheckDecision(controller.get(), false, |
+ kDisablingRecoverablePacketLossAtHighBw); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, MaintainFecOnForHighBandwidth) { |
+ 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); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, DisableFecOnMediumBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(true); |
+ constexpr float kPacketLoss = (kDisablingRecoverablePacketLossAtLowBw + |
+ kDisablingRecoverablePacketLossAtHighBw) / |
+ 2.0f; |
+ UpdateNetworkMetrics( |
+ controller.get(), |
+ rtc::Optional<int>((kDisablingBandwidthHigh + kDisablingBandwidthLow) / |
+ 2), |
+ rtc::Optional<float>(kPacketLoss)); |
+ CheckDecision(controller.get(), false, kPacketLoss); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, MaintainFecOnForMediumBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(true); |
+ constexpr float kPacketLoss = kDisablingRecoverablePacketLossAtLowBw * 0.51f + |
+ kDisablingRecoverablePacketLossAtHighBw * 0.49f; |
+ UpdateNetworkMetrics( |
+ controller.get(), |
+ rtc::Optional<int>((kEnablingBandwidthHigh + kDisablingBandwidthLow) / 2), |
+ rtc::Optional<float>(kPacketLoss)); |
+ CheckDecision(controller.get(), true, kPacketLoss); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, DisableFecForLowBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(true); |
+ UpdateNetworkMetrics( |
+ controller.get(), rtc::Optional<int>(kDisablingBandwidthLow), |
+ rtc::Optional<float>(kDisablingRecoverablePacketLossAtLowBw)); |
+ CheckDecision(controller.get(), false, |
+ kDisablingRecoverablePacketLossAtLowBw); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, DisableFecForVeryLowBandwidth) { |
+ auto controller = CreateFecControllerRplrBased(true); |
+ // Below |kEnablingBandwidthLow|, any recoverable packet loss fraction can |
+ // cause FEC to turn off. |
+ UpdateNetworkMetrics(controller.get(), |
+ rtc::Optional<int>(kDisablingBandwidthLow - 1), |
+ rtc::Optional<float>(1.0)); |
+ CheckDecision(controller.get(), false, 1.0); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, CheckBehaviorOnChangingNetworkMetrics) { |
+ // In this test, we let the network metrics to traverse from 1 to 5. |
+ // |
+ // recoverable ^ |
+ // packet-loss | 1 | | |
+ // | | 2| |
+ // | \ \ 3 |
+ // | \4 \_______ |
+ // | \_________ |
+ // |---------5-------> bandwidth |
+ |
+ auto controller = CreateFecControllerRplrBased(true); |
+ UpdateNetworkMetrics(controller.get(), |
+ rtc::Optional<int>(kDisablingBandwidthLow - 1), |
+ rtc::Optional<float>(1.0)); |
+ CheckDecision(controller.get(), false, 1.0); |
+ |
+ UpdateNetworkMetrics( |
+ controller.get(), rtc::Optional<int>(kEnablingBandwidthLow), |
+ rtc::Optional<float>(kEnablingRecoverablePacketLossAtLowBw * 0.99f)); |
+ CheckDecision(controller.get(), false, |
+ kEnablingRecoverablePacketLossAtLowBw * 0.99f); |
+ |
+ UpdateNetworkMetrics( |
+ controller.get(), rtc::Optional<int>(kEnablingBandwidthHigh), |
+ rtc::Optional<float>(kEnablingRecoverablePacketLossAtHighBw)); |
+ CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtHighBw); |
+ |
+ UpdateNetworkMetrics( |
+ controller.get(), rtc::Optional<int>(kDisablingBandwidthHigh), |
+ rtc::Optional<float>(kDisablingRecoverablePacketLossAtHighBw * 1.01f)); |
+ CheckDecision(controller.get(), true, |
+ kDisablingRecoverablePacketLossAtHighBw * 1.01f); |
+ |
+ UpdateNetworkMetrics(controller.get(), |
+ rtc::Optional<int>(kDisablingBandwidthHigh + 1), |
+ rtc::Optional<float>(0.0)); |
+ CheckDecision(controller.get(), false, 0.0); |
+} |
+ |
+TEST(FecControllerRplrBasedTest, CheckBehaviorOnSpecialCurves) { |
+ // We test a special configuration, where the points to define the FEC |
+ // enabling/disabling curves are placed like the following, otherwise the test |
+ // is the same as CheckBehaviorOnChangingNetworkMetrics. |
+ // |
+ // recoverable ^ |
+ // packet-loss | | | |
+ // | | C| |
+ // | | | |
+ // | | D|_______ |
+ // | A|___B______ |
+ // |-----------------> bandwidth |
+ |
+ constexpr int kEnablingBandwidthHigh = kEnablingBandwidthLow; |
+ constexpr float kDisablingRecoverablePacketLossAtLowBw = |
+ kDisablingRecoverablePacketLossAtHighBw; |
+ using Threshold = FecControllerRplrBased::Config::Threshold; |
+ FecControllerRplrBased controller(FecControllerRplrBased::Config( |
+ true, |
+ Threshold(kEnablingBandwidthLow, kEnablingRecoverablePacketLossAtLowBw, |
+ kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw), |
+ Threshold(kDisablingBandwidthLow, kDisablingRecoverablePacketLossAtLowBw, |
+ kDisablingBandwidthHigh, |
+ kDisablingRecoverablePacketLossAtHighBw), |
+ 0, nullptr)); |
+ |
+ UpdateNetworkMetrics(&controller, |
+ rtc::Optional<int>(kDisablingBandwidthLow - 1), |
+ rtc::Optional<float>(1.0)); |
+ CheckDecision(&controller, false, 1.0); |
+ |
+ UpdateNetworkMetrics( |
+ &controller, rtc::Optional<int>(kEnablingBandwidthLow), |
+ rtc::Optional<float>(kEnablingRecoverablePacketLossAtHighBw * 0.99f)); |
+ CheckDecision(&controller, false, |
+ kEnablingRecoverablePacketLossAtHighBw * 0.99f); |
+ |
+ UpdateNetworkMetrics( |
+ &controller, rtc::Optional<int>(kEnablingBandwidthHigh), |
+ rtc::Optional<float>(kEnablingRecoverablePacketLossAtHighBw)); |
+ CheckDecision(&controller, true, kEnablingRecoverablePacketLossAtHighBw); |
+ |
+ UpdateNetworkMetrics( |
+ &controller, rtc::Optional<int>(kDisablingBandwidthHigh), |
+ rtc::Optional<float>(kDisablingRecoverablePacketLossAtHighBw * 1.01f)); |
+ CheckDecision(&controller, true, |
+ kDisablingRecoverablePacketLossAtHighBw * 1.01f); |
+ |
+ UpdateNetworkMetrics(&controller, |
+ rtc::Optional<int>(kDisablingBandwidthHigh + 1), |
+ rtc::Optional<float>(0.0)); |
+ CheckDecision(&controller, false, 0.0); |
+} |
+ |
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) |
+TEST(FecControllerRplrBasedDeathTest, InvalidConfig) { |
+ using Threshold = FecControllerRplrBased::Config::Threshold; |
+ EXPECT_DEATH( |
+ FecControllerRplrBased controller(FecControllerRplrBased::Config( |
+ true, |
+ Threshold( |
+ kDisablingBandwidthLow - 1, kEnablingRecoverablePacketLossAtLowBw, |
+ kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw), |
+ Threshold( |
+ kDisablingBandwidthLow, kDisablingRecoverablePacketLossAtLowBw, |
+ kDisablingBandwidthHigh, kDisablingRecoverablePacketLossAtHighBw), |
+ 0, nullptr)), |
+ "Check failed"); |
+} |
+#endif |
+ |
+} // namespace webrtc |