diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn index 67ed45307a..6c7766d65b 100644 --- a/webrtc/modules/audio_coding/BUILD.gn +++ b/webrtc/modules/audio_coding/BUILD.gn @@ -909,8 +909,10 @@ rtc_static_library("audio_network_adaptor") { "audio_network_adaptor/dtx_controller.h", "audio_network_adaptor/event_log_writer.cc", "audio_network_adaptor/event_log_writer.h", - "audio_network_adaptor/fec_controller.cc", - "audio_network_adaptor/fec_controller.h", + "audio_network_adaptor/fec_controller_plr_based.cc", + "audio_network_adaptor/fec_controller_plr_based.h", + "audio_network_adaptor/fec_controller_rplr_based.cc", + "audio_network_adaptor/fec_controller_rplr_based.h", "audio_network_adaptor/frame_length_controller.cc", "audio_network_adaptor/frame_length_controller.h", "audio_network_adaptor/include/audio_network_adaptor.h", @@ -1988,7 +1990,8 @@ if (rtc_include_tests) { "audio_network_adaptor/controller_manager_unittest.cc", "audio_network_adaptor/dtx_controller_unittest.cc", "audio_network_adaptor/event_log_writer_unittest.cc", - "audio_network_adaptor/fec_controller_unittest.cc", + "audio_network_adaptor/fec_controller_plr_based_unittest.cc", + "audio_network_adaptor/fec_controller_rplr_based_unittest.cc", "audio_network_adaptor/frame_length_controller_unittest.cc", "audio_network_adaptor/mock/mock_controller.h", "audio_network_adaptor/mock/mock_controller_manager.h", diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/controller_manager.cc b/webrtc/modules/audio_coding/audio_network_adaptor/controller_manager.cc index 1eb0857cd0..6f3bda261a 100644 --- a/webrtc/modules/audio_coding/audio_network_adaptor/controller_manager.cc +++ b/webrtc/modules/audio_coding/audio_network_adaptor/controller_manager.cc @@ -17,7 +17,7 @@ #include "webrtc/modules/audio_coding/audio_network_adaptor/bitrate_controller.h" #include "webrtc/modules/audio_coding/audio_network_adaptor/channel_controller.h" #include "webrtc/modules/audio_coding/audio_network_adaptor/dtx_controller.h" -#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.h" +#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h" #include "webrtc/modules/audio_coding/audio_network_adaptor/frame_length_controller.h" #include "webrtc/system_wrappers/include/clock.h" @@ -37,7 +37,7 @@ namespace { #ifdef WEBRTC_AUDIO_NETWORK_ADAPTOR_DEBUG_DUMP -std::unique_ptr CreateFecController( +std::unique_ptr CreateFecControllerPlrBased( const audio_network_adaptor::config::FecController& config, bool initial_fec_enabled, const Clock* clock) { @@ -57,19 +57,20 @@ std::unique_ptr CreateFecController( RTC_CHECK(fec_disabling_threshold.has_high_bandwidth_bps()); RTC_CHECK(fec_disabling_threshold.has_high_bandwidth_packet_loss()); - return std::unique_ptr(new FecController(FecController::Config( - initial_fec_enabled, - FecController::Config::Threshold( - fec_enabling_threshold.low_bandwidth_bps(), - fec_enabling_threshold.low_bandwidth_packet_loss(), - fec_enabling_threshold.high_bandwidth_bps(), - fec_enabling_threshold.high_bandwidth_packet_loss()), - FecController::Config::Threshold( - fec_disabling_threshold.low_bandwidth_bps(), - fec_disabling_threshold.low_bandwidth_packet_loss(), - fec_disabling_threshold.high_bandwidth_bps(), - fec_disabling_threshold.high_bandwidth_packet_loss()), - config.time_constant_ms(), clock))); + return std::unique_ptr( + new FecControllerPlrBased(FecControllerPlrBased::Config( + initial_fec_enabled, + FecControllerPlrBased::Config::Threshold( + fec_enabling_threshold.low_bandwidth_bps(), + fec_enabling_threshold.low_bandwidth_packet_loss(), + fec_enabling_threshold.high_bandwidth_bps(), + fec_enabling_threshold.high_bandwidth_packet_loss()), + FecControllerPlrBased::Config::Threshold( + fec_disabling_threshold.low_bandwidth_bps(), + fec_disabling_threshold.low_bandwidth_packet_loss(), + fec_disabling_threshold.high_bandwidth_bps(), + fec_disabling_threshold.high_bandwidth_packet_loss()), + config.time_constant_ms(), clock))); } std::unique_ptr CreateFrameLengthController( @@ -179,8 +180,8 @@ std::unique_ptr ControllerManagerImpl::Create( std::unique_ptr controller; switch (controller_config.controller_case()) { case audio_network_adaptor::config::Controller::kFecController: - controller = CreateFecController(controller_config.fec_controller(), - initial_fec_enabled, clock); + controller = CreateFecControllerPlrBased( + controller_config.fec_controller(), initial_fec_enabled, clock); break; case audio_network_adaptor::config::Controller::kFrameLengthController: controller = CreateFrameLengthController( diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.cc b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.cc similarity index 74% rename from webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.cc rename to webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.cc index df9275a6ed..c3c27c6bed 100644 --- a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.cc +++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.h" +#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h" #include #include @@ -17,28 +17,30 @@ namespace webrtc { -FecController::Config::Threshold::Threshold(int low_bandwidth_bps, - float low_bandwidth_packet_loss, - int high_bandwidth_bps, - float high_bandwidth_packet_loss) +FecControllerPlrBased::Config::Threshold::Threshold( + int low_bandwidth_bps, + float low_bandwidth_packet_loss, + int high_bandwidth_bps, + float high_bandwidth_packet_loss) : low_bandwidth_bps(low_bandwidth_bps), low_bandwidth_packet_loss(low_bandwidth_packet_loss), high_bandwidth_bps(high_bandwidth_bps), high_bandwidth_packet_loss(high_bandwidth_packet_loss) {} -FecController::Config::Config(bool initial_fec_enabled, - const Threshold& fec_enabling_threshold, - const Threshold& fec_disabling_threshold, - int time_constant_ms, - const Clock* clock) +FecControllerPlrBased::Config::Config(bool initial_fec_enabled, + const Threshold& fec_enabling_threshold, + const Threshold& fec_disabling_threshold, + int time_constant_ms, + const Clock* clock) : initial_fec_enabled(initial_fec_enabled), fec_enabling_threshold(fec_enabling_threshold), fec_disabling_threshold(fec_disabling_threshold), time_constant_ms(time_constant_ms), clock(clock) {} -FecController::FecController(const Config& config, - std::unique_ptr smoothing_filter) +FecControllerPlrBased::FecControllerPlrBased( + const Config& config, + std::unique_ptr smoothing_filter) : config_(config), fec_enabled_(config.initial_fec_enabled), packet_loss_smoother_(std::move(smoothing_filter)), @@ -58,16 +60,16 @@ FecController::FecController(const Config& config, config_.fec_enabling_threshold.high_bandwidth_packet_loss); } -FecController::FecController(const Config& config) - : FecController( +FecControllerPlrBased::FecControllerPlrBased(const Config& config) + : FecControllerPlrBased( config, std::unique_ptr( new SmoothingFilterImpl(config.time_constant_ms, config.clock))) { } -FecController::~FecController() = default; +FecControllerPlrBased::~FecControllerPlrBased() = default; -void FecController::UpdateNetworkMetrics( +void FecControllerPlrBased::UpdateNetworkMetrics( const NetworkMetrics& network_metrics) { if (network_metrics.uplink_bandwidth_bps) uplink_bandwidth_bps_ = network_metrics.uplink_bandwidth_bps; @@ -77,7 +79,7 @@ void FecController::UpdateNetworkMetrics( } } -void FecController::MakeDecision( +void FecControllerPlrBased::MakeDecision( AudioNetworkAdaptor::EncoderRuntimeConfig* config) { RTC_DCHECK(!config->enable_fec); RTC_DCHECK(!config->uplink_packet_loss_fraction); @@ -93,7 +95,7 @@ void FecController::MakeDecision( rtc::Optional(packet_loss ? *packet_loss : 0.0); } -FecController::ThresholdInfo::ThresholdInfo( +FecControllerPlrBased::ThresholdInfo::ThresholdInfo( const Config::Threshold& threshold) { int bandwidth_diff_bps = threshold.high_bandwidth_bps - threshold.low_bandwidth_bps; @@ -104,18 +106,23 @@ FecController::ThresholdInfo::ThresholdInfo( threshold.low_bandwidth_packet_loss - slope * threshold.low_bandwidth_bps; } -float FecController::GetPacketLossThreshold( +float FecControllerPlrBased::GetPacketLossThreshold( int bandwidth_bps, const Config::Threshold& threshold, const ThresholdInfo& threshold_info) const { - if (bandwidth_bps < threshold.low_bandwidth_bps) + if (bandwidth_bps < threshold.low_bandwidth_bps) { return std::numeric_limits::max(); - if (bandwidth_bps >= threshold.high_bandwidth_bps) + } else if (bandwidth_bps >= threshold.high_bandwidth_bps) { return threshold.high_bandwidth_packet_loss; - return threshold_info.offset + threshold_info.slope * bandwidth_bps; + } else { + float rc = threshold_info.offset + threshold_info.slope * bandwidth_bps; + RTC_DCHECK_LE(rc, threshold.low_bandwidth_packet_loss); + RTC_DCHECK_GE(rc, threshold.high_bandwidth_packet_loss); + return rc; + } } -bool FecController::FecEnablingDecision( +bool FecControllerPlrBased::FecEnablingDecision( const rtc::Optional& packet_loss) const { if (!uplink_bandwidth_bps_) return false; @@ -126,7 +133,7 @@ bool FecController::FecEnablingDecision( fec_enabling_threshold_info_); } -bool FecController::FecDisablingDecision( +bool FecControllerPlrBased::FecDisablingDecision( const rtc::Optional& packet_loss) const { if (!uplink_bandwidth_bps_) return false; diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.h b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h similarity index 89% rename from webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.h rename to webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h index 8d9cbb03f9..386470bb32 100644 --- a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.h +++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_H_ +#ifndef WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_PLR_BASED_H_ +#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_PLR_BASED_H_ #include @@ -19,7 +19,7 @@ namespace webrtc { -class FecController final : public Controller { +class FecControllerPlrBased final : public Controller { public: struct Config { struct Threshold { @@ -65,12 +65,12 @@ class FecController final : public Controller { }; // Dependency injection for testing. - FecController(const Config& config, - std::unique_ptr smoothing_filter); + FecControllerPlrBased(const Config& config, + std::unique_ptr smoothing_filter); - explicit FecController(const Config& config); + explicit FecControllerPlrBased(const Config& config); - ~FecController() override; + ~FecControllerPlrBased() override; void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override; @@ -99,9 +99,9 @@ class FecController final : public Controller { const ThresholdInfo fec_enabling_threshold_info_; const ThresholdInfo fec_disabling_threshold_info_; - RTC_DISALLOW_COPY_AND_ASSIGN(FecController); + RTC_DISALLOW_COPY_AND_ASSIGN(FecControllerPlrBased); }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_H_ +#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_PLR_BASED_H_ diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_unittest.cc b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based_unittest.cc similarity index 78% rename from webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_unittest.cc rename to webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based_unittest.cc index 8fd62dfbd4..34f16c8272 100644 --- a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_unittest.cc +++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based_unittest.cc @@ -11,7 +11,7 @@ #include #include "webrtc/common_audio/mocks/mock_smoothing_filter.h" -#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.h" +#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h" #include "webrtc/test/gtest.h" namespace webrtc { @@ -45,19 +45,20 @@ constexpr float kEnablingPacketLossAtLowBw = 0.1f; constexpr int kEnablingBandwidthHigh = 64000; constexpr float kEnablingPacketLossAtHighBw = 0.05f; -struct FecControllerStates { - std::unique_ptr controller; +struct FecControllerPlrBasedTestStates { + std::unique_ptr controller; MockSmoothingFilter* packet_loss_smoother; }; -FecControllerStates CreateFecController(bool initial_fec_enabled) { - FecControllerStates states; +FecControllerPlrBasedTestStates CreateFecControllerPlrBased( + bool initial_fec_enabled) { + FecControllerPlrBasedTestStates states; std::unique_ptr mock_smoothing_filter( new NiceMock()); states.packet_loss_smoother = mock_smoothing_filter.get(); - using Threshold = FecController::Config::Threshold; - states.controller.reset(new FecController( - FecController::Config( + using Threshold = FecControllerPlrBased::Config::Threshold; + states.controller.reset(new FecControllerPlrBased( + FecControllerPlrBased::Config( initial_fec_enabled, Threshold(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw, kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw), @@ -68,7 +69,7 @@ FecControllerStates CreateFecController(bool initial_fec_enabled) { return states; } -void UpdateNetworkMetrics(FecControllerStates* states, +void UpdateNetworkMetrics(FecControllerPlrBasedTestStates* states, const rtc::Optional& uplink_bandwidth_bps, const rtc::Optional& uplink_packet_loss) { // UpdateNetworkMetrics can accept multiple network metric updates at once. @@ -93,7 +94,7 @@ void UpdateNetworkMetrics(FecControllerStates* states, // 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(FecControllerStates* states, +void CheckDecision(FecControllerPlrBasedTestStates* states, bool expected_enable_fec, float expected_uplink_packet_loss_fraction) { AudioNetworkAdaptor::EncoderRuntimeConfig config; @@ -105,9 +106,9 @@ void CheckDecision(FecControllerStates* states, } // namespace -TEST(FecControllerTest, OutputInitValueWhenUplinkBandwidthUnknown) { +TEST(FecControllerPlrBasedTest, OutputInitValueWhenUplinkBandwidthUnknown) { constexpr bool kInitialFecEnabled = true; - auto states = CreateFecController(kInitialFecEnabled); + 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(), @@ -115,9 +116,10 @@ TEST(FecControllerTest, OutputInitValueWhenUplinkBandwidthUnknown) { CheckDecision(&states, kInitialFecEnabled, kDisablingPacketLossAtHighBw); } -TEST(FecControllerTest, OutputInitValueWhenUplinkPacketLossFractionUnknown) { +TEST(FecControllerPlrBasedTest, + OutputInitValueWhenUplinkPacketLossFractionUnknown) { constexpr bool kInitialFecEnabled = true; - auto states = CreateFecController(kInitialFecEnabled); + 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(kDisablingBandwidthLow - 1), @@ -125,22 +127,22 @@ TEST(FecControllerTest, OutputInitValueWhenUplinkPacketLossFractionUnknown) { CheckDecision(&states, kInitialFecEnabled, 0.0); } -TEST(FecControllerTest, EnableFecForHighBandwidth) { - auto states = CreateFecController(false); +TEST(FecControllerPlrBasedTest, EnableFecForHighBandwidth) { + auto states = CreateFecControllerPlrBased(false); UpdateNetworkMetrics(&states, rtc::Optional(kEnablingBandwidthHigh), rtc::Optional(kEnablingPacketLossAtHighBw)); CheckDecision(&states, true, kEnablingPacketLossAtHighBw); } -TEST(FecControllerTest, UpdateMultipleNetworkMetricsAtOnce) { +TEST(FecControllerPlrBasedTest, UpdateMultipleNetworkMetricsAtOnce) { // This test is similar to EnableFecForHighBandwidth. But instead of // using ::UpdateNetworkMetrics(...), which calls - // FecController::UpdateNetworkMetrics(...) multiple times, we + // FecControllerPlrBased::UpdateNetworkMetrics(...) multiple times, we // we call it only once. This is to verify that - // FecController::UpdateNetworkMetrics(...) can handle multiple + // FecControllerPlrBased::UpdateNetworkMetrics(...) can handle multiple // network updates at once. This is, however, not a common use case in current // audio_network_adaptor_impl.cc. - auto states = CreateFecController(false); + auto states = CreateFecControllerPlrBased(false); Controller::NetworkMetrics network_metrics; network_metrics.uplink_bandwidth_bps = rtc::Optional(kEnablingBandwidthHigh); @@ -152,16 +154,16 @@ TEST(FecControllerTest, UpdateMultipleNetworkMetricsAtOnce) { CheckDecision(&states, true, kEnablingPacketLossAtHighBw); } -TEST(FecControllerTest, MaintainFecOffForHighBandwidth) { - auto states = CreateFecController(false); +TEST(FecControllerPlrBasedTest, MaintainFecOffForHighBandwidth) { + auto states = CreateFecControllerPlrBased(false); constexpr float kPacketLoss = kEnablingPacketLossAtHighBw * 0.99f; UpdateNetworkMetrics(&states, rtc::Optional(kEnablingBandwidthHigh), rtc::Optional(kPacketLoss)); CheckDecision(&states, false, kPacketLoss); } -TEST(FecControllerTest, EnableFecForMediumBandwidth) { - auto states = CreateFecController(false); +TEST(FecControllerPlrBasedTest, EnableFecForMediumBandwidth) { + auto states = CreateFecControllerPlrBased(false); constexpr float kPacketLoss = (kEnablingPacketLossAtLowBw + kEnablingPacketLossAtHighBw) / 2.0; UpdateNetworkMetrics( @@ -171,8 +173,8 @@ TEST(FecControllerTest, EnableFecForMediumBandwidth) { CheckDecision(&states, true, kPacketLoss); } -TEST(FecControllerTest, MaintainFecOffForMediumBandwidth) { - auto states = CreateFecController(false); +TEST(FecControllerPlrBasedTest, MaintainFecOffForMediumBandwidth) { + auto states = CreateFecControllerPlrBased(false); constexpr float kPacketLoss = kEnablingPacketLossAtLowBw * 0.49f + kEnablingPacketLossAtHighBw * 0.51f; UpdateNetworkMetrics( @@ -182,23 +184,23 @@ TEST(FecControllerTest, MaintainFecOffForMediumBandwidth) { CheckDecision(&states, false, kPacketLoss); } -TEST(FecControllerTest, EnableFecForLowBandwidth) { - auto states = CreateFecController(false); +TEST(FecControllerPlrBasedTest, EnableFecForLowBandwidth) { + auto states = CreateFecControllerPlrBased(false); UpdateNetworkMetrics(&states, rtc::Optional(kEnablingBandwidthLow), rtc::Optional(kEnablingPacketLossAtLowBw)); CheckDecision(&states, true, kEnablingPacketLossAtLowBw); } -TEST(FecControllerTest, MaintainFecOffForLowBandwidth) { - auto states = CreateFecController(false); +TEST(FecControllerPlrBasedTest, MaintainFecOffForLowBandwidth) { + auto states = CreateFecControllerPlrBased(false); constexpr float kPacketLoss = kEnablingPacketLossAtLowBw * 0.99f; UpdateNetworkMetrics(&states, rtc::Optional(kEnablingBandwidthLow), rtc::Optional(kPacketLoss)); CheckDecision(&states, false, kPacketLoss); } -TEST(FecControllerTest, MaintainFecOffForVeryLowBandwidth) { - auto states = CreateFecController(false); +TEST(FecControllerPlrBasedTest, MaintainFecOffForVeryLowBandwidth) { + auto states = CreateFecControllerPlrBased(false); // Below |kEnablingBandwidthLow|, no packet loss fraction can cause FEC to // turn on. UpdateNetworkMetrics(&states, rtc::Optional(kEnablingBandwidthLow - 1), @@ -206,34 +208,35 @@ TEST(FecControllerTest, MaintainFecOffForVeryLowBandwidth) { CheckDecision(&states, false, 1.0); } -TEST(FecControllerTest, DisableFecForHighBandwidth) { - auto states = CreateFecController(true); +TEST(FecControllerPlrBasedTest, DisableFecForHighBandwidth) { + auto states = CreateFecControllerPlrBased(true); UpdateNetworkMetrics(&states, rtc::Optional(kDisablingBandwidthHigh), rtc::Optional(kDisablingPacketLossAtHighBw)); CheckDecision(&states, false, kDisablingPacketLossAtHighBw); } -TEST(FecControllerTest, MaintainFecOnForHighBandwidth) { - auto states = CreateFecController(true); +TEST(FecControllerPlrBasedTest, MaintainFecOnForHighBandwidth) { + auto states = CreateFecControllerPlrBased(true); constexpr float kPacketLoss = kDisablingPacketLossAtHighBw * 1.01f; UpdateNetworkMetrics(&states, rtc::Optional(kDisablingBandwidthHigh), rtc::Optional(kPacketLoss)); CheckDecision(&states, true, kPacketLoss); } -TEST(FecControllerTest, DisableFecOnMediumBandwidth) { - auto states = CreateFecController(true); +TEST(FecControllerPlrBasedTest, DisableFecOnMediumBandwidth) { + auto states = CreateFecControllerPlrBased(true); constexpr float kPacketLoss = (kDisablingPacketLossAtLowBw + kDisablingPacketLossAtHighBw) / 2.0f; UpdateNetworkMetrics( - &states, rtc::Optional( - (kDisablingBandwidthHigh + kDisablingBandwidthLow) / 2), + &states, + rtc::Optional((kDisablingBandwidthHigh + kDisablingBandwidthLow) / + 2), rtc::Optional(kPacketLoss)); CheckDecision(&states, false, kPacketLoss); } -TEST(FecControllerTest, MaintainFecOnForMediumBandwidth) { - auto states = CreateFecController(true); +TEST(FecControllerPlrBasedTest, MaintainFecOnForMediumBandwidth) { + auto states = CreateFecControllerPlrBased(true); constexpr float kPacketLoss = kDisablingPacketLossAtLowBw * 0.51f + kDisablingPacketLossAtHighBw * 0.49f; UpdateNetworkMetrics( @@ -243,15 +246,15 @@ TEST(FecControllerTest, MaintainFecOnForMediumBandwidth) { CheckDecision(&states, true, kPacketLoss); } -TEST(FecControllerTest, DisableFecForLowBandwidth) { - auto states = CreateFecController(true); +TEST(FecControllerPlrBasedTest, DisableFecForLowBandwidth) { + auto states = CreateFecControllerPlrBased(true); UpdateNetworkMetrics(&states, rtc::Optional(kDisablingBandwidthLow), rtc::Optional(kDisablingPacketLossAtLowBw)); CheckDecision(&states, false, kDisablingPacketLossAtLowBw); } -TEST(FecControllerTest, DisableFecForVeryLowBandwidth) { - auto states = CreateFecController(true); +TEST(FecControllerPlrBasedTest, DisableFecForVeryLowBandwidth) { + auto states = CreateFecControllerPlrBased(true); // Below |kEnablingBandwidthLow|, any packet loss fraction can cause FEC to // turn off. UpdateNetworkMetrics(&states, rtc::Optional(kDisablingBandwidthLow - 1), @@ -259,7 +262,7 @@ TEST(FecControllerTest, DisableFecForVeryLowBandwidth) { CheckDecision(&states, false, 1.0); } -TEST(FecControllerTest, CheckBehaviorOnChangingNetworkMetrics) { +TEST(FecControllerPlrBasedTest, CheckBehaviorOnChangingNetworkMetrics) { // In this test, we let the network metrics to traverse from 1 to 5. // packet-loss ^ 1 | | // | | 2| @@ -268,7 +271,7 @@ TEST(FecControllerTest, CheckBehaviorOnChangingNetworkMetrics) { // | \_________ // |---------5-------> bandwidth - auto states = CreateFecController(true); + auto states = CreateFecControllerPlrBased(true); UpdateNetworkMetrics(&states, rtc::Optional(kDisablingBandwidthLow - 1), rtc::Optional(1.0)); CheckDecision(&states, false, 1.0); @@ -292,7 +295,7 @@ TEST(FecControllerTest, CheckBehaviorOnChangingNetworkMetrics) { CheckDecision(&states, false, 0.0); } -TEST(FecControllerTest, CheckBehaviorOnSpecialCurves) { +TEST(FecControllerPlrBasedTest, 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. @@ -306,15 +309,16 @@ TEST(FecControllerTest, CheckBehaviorOnSpecialCurves) { constexpr int kEnablingBandwidthHigh = kEnablingBandwidthLow; constexpr float kDisablingPacketLossAtLowBw = kDisablingPacketLossAtHighBw; - FecControllerStates states; + FecControllerPlrBasedTestStates states; std::unique_ptr mock_smoothing_filter( new NiceMock()); states.packet_loss_smoother = mock_smoothing_filter.get(); - using Threshold = FecController::Config::Threshold; - states.controller.reset(new FecController( - FecController::Config( - true, Threshold(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw, - kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw), + using Threshold = FecControllerPlrBased::Config::Threshold; + states.controller.reset(new FecControllerPlrBased( + FecControllerPlrBased::Config( + true, + Threshold(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw, + kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw), Threshold(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw, kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw), 0, nullptr), @@ -344,15 +348,15 @@ TEST(FecControllerTest, CheckBehaviorOnSpecialCurves) { } #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) -TEST(FecControllerDeathTest, InvalidConfig) { - FecControllerStates states; +TEST(FecControllerPlrBasedDeathTest, InvalidConfig) { + FecControllerPlrBasedTestStates states; std::unique_ptr mock_smoothing_filter( new NiceMock()); states.packet_loss_smoother = mock_smoothing_filter.get(); - using Threshold = FecController::Config::Threshold; + using Threshold = FecControllerPlrBased::Config::Threshold; EXPECT_DEATH( - states.controller.reset(new FecController( - FecController::Config( + states.controller.reset(new FecControllerPlrBased( + FecControllerPlrBased::Config( true, Threshold(kDisablingBandwidthLow - 1, kEnablingPacketLossAtLowBw, kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw), diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.cc b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.cc new file mode 100644 index 0000000000..2658107348 --- /dev/null +++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.cc @@ -0,0 +1,138 @@ +/* + * 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 "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.h" + +#include +#include + +#include "webrtc/base/checks.h" + +namespace webrtc { + +FecControllerRplrBased::Config::Threshold::Threshold( + int low_bandwidth_bps, + float low_bandwidth_recoverable_packet_loss, + int high_bandwidth_bps, + float high_bandwidth_recoverable_packet_loss) + : low_bandwidth_bps(low_bandwidth_bps), + low_bandwidth_recoverable_packet_loss( + low_bandwidth_recoverable_packet_loss), + high_bandwidth_bps(high_bandwidth_bps), + high_bandwidth_recoverable_packet_loss( + high_bandwidth_recoverable_packet_loss) {} + +FecControllerRplrBased::Config::Config(bool initial_fec_enabled, + const Threshold& fec_enabling_threshold, + const Threshold& fec_disabling_threshold, + int time_constant_ms, + const Clock* clock) + : initial_fec_enabled(initial_fec_enabled), + fec_enabling_threshold(fec_enabling_threshold), + fec_disabling_threshold(fec_disabling_threshold), + time_constant_ms(time_constant_ms), + clock(clock) {} + +FecControllerRplrBased::FecControllerRplrBased(const Config& config) + : config_(config), + fec_enabled_(config.initial_fec_enabled), + fec_enabling_threshold_info_(config_.fec_enabling_threshold), + fec_disabling_threshold_info_(config_.fec_disabling_threshold) { + RTC_DCHECK_LE(fec_enabling_threshold_info_.slope, 0); + RTC_DCHECK_LE(fec_enabling_threshold_info_.slope, 0); + RTC_DCHECK_LE( + GetPacketLossThreshold(config_.fec_enabling_threshold.low_bandwidth_bps, + config_.fec_disabling_threshold, + fec_disabling_threshold_info_), + config_.fec_enabling_threshold.low_bandwidth_recoverable_packet_loss); + RTC_DCHECK_LE( + GetPacketLossThreshold(config_.fec_enabling_threshold.high_bandwidth_bps, + config_.fec_disabling_threshold, + fec_disabling_threshold_info_), + config_.fec_enabling_threshold.high_bandwidth_recoverable_packet_loss); +} + +FecControllerRplrBased::~FecControllerRplrBased() = default; + +void FecControllerRplrBased::UpdateNetworkMetrics( + const NetworkMetrics& network_metrics) { + if (network_metrics.uplink_bandwidth_bps) + uplink_bandwidth_bps_ = network_metrics.uplink_bandwidth_bps; + if (network_metrics.uplink_recoverable_packet_loss_fraction) { + uplink_recoverable_packet_loss_ = + network_metrics.uplink_recoverable_packet_loss_fraction; + } +} + +void FecControllerRplrBased::MakeDecision( + AudioNetworkAdaptor::EncoderRuntimeConfig* config) { + RTC_DCHECK(!config->enable_fec); + RTC_DCHECK(!config->uplink_packet_loss_fraction); + + fec_enabled_ = fec_enabled_ ? !FecDisablingDecision() : FecEnablingDecision(); + + config->enable_fec = rtc::Optional(fec_enabled_); + config->uplink_packet_loss_fraction = rtc::Optional( + uplink_recoverable_packet_loss_ ? *uplink_recoverable_packet_loss_ : 0.0); +} + +FecControllerRplrBased::ThresholdInfo::ThresholdInfo( + const Config::Threshold& threshold) { + int bandwidth_diff_bps = + threshold.high_bandwidth_bps - threshold.low_bandwidth_bps; + float recoverable_packet_loss_diff = + threshold.high_bandwidth_recoverable_packet_loss - + threshold.low_bandwidth_recoverable_packet_loss; + slope = bandwidth_diff_bps == 0 + ? 0.0 + : recoverable_packet_loss_diff / bandwidth_diff_bps; + offset = threshold.low_bandwidth_recoverable_packet_loss - + slope * threshold.low_bandwidth_bps; +} + +float FecControllerRplrBased::GetPacketLossThreshold( + int bandwidth_bps, + const Config::Threshold& threshold, + const ThresholdInfo& threshold_info) const { + if (bandwidth_bps < threshold.low_bandwidth_bps) { + return std::numeric_limits::max(); + } else if (bandwidth_bps >= threshold.high_bandwidth_bps) { + return threshold.high_bandwidth_recoverable_packet_loss; + } else { + float rc = threshold_info.offset + threshold_info.slope * bandwidth_bps; + RTC_DCHECK_LE(rc, threshold.low_bandwidth_recoverable_packet_loss); + RTC_DCHECK_GE(rc, threshold.high_bandwidth_recoverable_packet_loss); + return rc; + } +} + +bool FecControllerRplrBased::FecEnablingDecision() const { + if (!uplink_bandwidth_bps_ || !uplink_recoverable_packet_loss_) { + return false; + } else { + return *uplink_recoverable_packet_loss_ >= + GetPacketLossThreshold(*uplink_bandwidth_bps_, + config_.fec_enabling_threshold, + fec_enabling_threshold_info_); + } +} + +bool FecControllerRplrBased::FecDisablingDecision() const { + if (!uplink_bandwidth_bps_ || !uplink_recoverable_packet_loss_) { + return false; + } else { + return *uplink_recoverable_packet_loss_ <= + GetPacketLossThreshold(*uplink_bandwidth_bps_, + config_.fec_disabling_threshold, + fec_disabling_threshold_info_); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.h b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.h new file mode 100644 index 0000000000..d3ddbd41a3 --- /dev/null +++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.h @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_RPLR_BASED_H_ +#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_RPLR_BASED_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h" +#include "webrtc/system_wrappers/include/clock.h" + +namespace webrtc { + +class FecControllerRplrBased final : public Controller { + public: + struct Config { + struct Threshold { + // Threshold defines a curve in the bandwidth/packet-loss domain. The + // curve is characterized by the two conjunction points: A and B. + // + // recoverable + // packet ^ | + // loss | A | + // | \ A: (low_bandwidth_bps, + // | \ low_bandwidth_recoverable_packet_loss) + // | \ B: (high_bandwidth_bps, + // | \ high_bandwidth_recoverable_packet_loss) + // | B \________ + // |---------------> bandwidth + Threshold(int low_bandwidth_bps, + float low_bandwidth_recoverable_packet_loss, + int high_bandwidth_bps, + float high_bandwidth_recoverable_packet_loss); + int low_bandwidth_bps; + float low_bandwidth_recoverable_packet_loss; + int high_bandwidth_bps; + float high_bandwidth_recoverable_packet_loss; + }; + + // |fec_enabling_threshold| defines a curve, above which FEC should be + // enabled. |fec_disabling_threshold| defines a curve, under which FEC + // should be disabled. See below + // + // recoverable + // packet-loss ^ | | + // | | | FEC + // | \ \ ON + // | FEC \ \_______ fec_enabling_threshold + // | OFF \_________ fec_disabling_threshold + // |-----------------> bandwidth + Config(bool initial_fec_enabled, + const Threshold& fec_enabling_threshold, + const Threshold& fec_disabling_threshold, + int time_constant_ms, + const Clock* clock); + bool initial_fec_enabled; + Threshold fec_enabling_threshold; + Threshold fec_disabling_threshold; + int time_constant_ms; + const Clock* clock; + }; + + explicit FecControllerRplrBased(const Config& config); + + ~FecControllerRplrBased() override; + + void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override; + + void MakeDecision(AudioNetworkAdaptor::EncoderRuntimeConfig* config) override; + + private: + // Characterize Threshold with: + // recoverable_packet_loss = slope * bandwidth + offset. + struct ThresholdInfo { + explicit ThresholdInfo(const Config::Threshold& threshold); + float slope; + float offset; + }; + + float GetPacketLossThreshold(int bandwidth_bps, + const Config::Threshold& threshold, + const ThresholdInfo& threshold_info) const; + + bool FecEnablingDecision() const; + bool FecDisablingDecision() const; + + const Config config_; + bool fec_enabled_; + rtc::Optional uplink_bandwidth_bps_; + rtc::Optional uplink_recoverable_packet_loss_; + + const ThresholdInfo fec_enabling_threshold_info_; + const ThresholdInfo fec_disabling_threshold_info_; + + RTC_DISALLOW_COPY_AND_ASSIGN(FecControllerRplrBased); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_RPLR_BASED_H_ 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 0000000000..ce51ff00d8 --- /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 +#include + +#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 GetRandomProbabilityOrUnknown() { + std::random_device rd; + std::mt19937 generator(rd()); + std::uniform_real_distribution<> distribution(0, 1); + + if (distribution(generator) < 0.2) { + return rtc::Optional(); + } else { + return rtc::Optional(distribution(generator)); + } +} + +std::unique_ptr CreateFecControllerRplrBased( + bool initial_fec_enabled) { + using Threshold = FecControllerRplrBased::Config::Threshold; + return std::unique_ptr( + 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& uplink_bandwidth_bps, + const rtc::Optional& uplink_packet_loss, + const rtc::Optional& 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& uplink_bandwidth_bps, + const rtc::Optional& 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(), + rtc::Optional(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(kDisablingBandwidthLow - 1), + rtc::Optional()); + CheckDecision(controller.get(), initial_fec_enabled, 0.0); + } +} + +TEST(FecControllerRplrBasedTest, EnableFecForHighBandwidth) { + auto controller = CreateFecControllerRplrBased(false); + UpdateNetworkMetrics( + controller.get(), rtc::Optional(kEnablingBandwidthHigh), + rtc::Optional(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(kEnablingBandwidthHigh); + network_metrics.uplink_packet_loss_fraction = + rtc::Optional(GetRandomProbabilityOrUnknown()); + network_metrics.uplink_recoverable_packet_loss_fraction = + rtc::Optional(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(kEnablingBandwidthHigh), + rtc::Optional(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((kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2), + rtc::Optional(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((kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2), + rtc::Optional(kPacketLoss)); + CheckDecision(controller.get(), false, kPacketLoss); +} + +TEST(FecControllerRplrBasedTest, EnableFecForLowBandwidth) { + auto controller = CreateFecControllerRplrBased(false); + UpdateNetworkMetrics( + controller.get(), rtc::Optional(kEnablingBandwidthLow), + rtc::Optional(kEnablingRecoverablePacketLossAtLowBw)); + CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtLowBw); +} + +TEST(FecControllerRplrBasedTest, MaintainFecOffForLowBandwidth) { + auto controller = CreateFecControllerRplrBased(false); + constexpr float kPacketLoss = kEnablingRecoverablePacketLossAtLowBw * 0.99f; + UpdateNetworkMetrics(controller.get(), + rtc::Optional(kEnablingBandwidthLow), + rtc::Optional(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(kEnablingBandwidthLow - 1), + rtc::Optional(1.0)); + CheckDecision(controller.get(), false, 1.0); +} + +TEST(FecControllerRplrBasedTest, DisableFecForHighBandwidth) { + auto controller = CreateFecControllerRplrBased(true); + UpdateNetworkMetrics( + controller.get(), rtc::Optional(kDisablingBandwidthHigh), + rtc::Optional(kDisablingRecoverablePacketLossAtHighBw)); + CheckDecision(controller.get(), false, + kDisablingRecoverablePacketLossAtHighBw); +} + +TEST(FecControllerRplrBasedTest, MaintainFecOnForHighBandwidth) { + auto controller = CreateFecControllerRplrBased(true); + constexpr float kPacketLoss = kDisablingRecoverablePacketLossAtHighBw * 1.01f; + UpdateNetworkMetrics(controller.get(), + rtc::Optional(kDisablingBandwidthHigh), + rtc::Optional(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((kDisablingBandwidthHigh + kDisablingBandwidthLow) / + 2), + rtc::Optional(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((kEnablingBandwidthHigh + kDisablingBandwidthLow) / 2), + rtc::Optional(kPacketLoss)); + CheckDecision(controller.get(), true, kPacketLoss); +} + +TEST(FecControllerRplrBasedTest, DisableFecForLowBandwidth) { + auto controller = CreateFecControllerRplrBased(true); + UpdateNetworkMetrics( + controller.get(), rtc::Optional(kDisablingBandwidthLow), + rtc::Optional(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(kDisablingBandwidthLow - 1), + rtc::Optional(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(kDisablingBandwidthLow - 1), + rtc::Optional(1.0)); + CheckDecision(controller.get(), false, 1.0); + + UpdateNetworkMetrics( + controller.get(), rtc::Optional(kEnablingBandwidthLow), + rtc::Optional(kEnablingRecoverablePacketLossAtLowBw * 0.99f)); + CheckDecision(controller.get(), false, + kEnablingRecoverablePacketLossAtLowBw * 0.99f); + + UpdateNetworkMetrics( + controller.get(), rtc::Optional(kEnablingBandwidthHigh), + rtc::Optional(kEnablingRecoverablePacketLossAtHighBw)); + CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtHighBw); + + UpdateNetworkMetrics( + controller.get(), rtc::Optional(kDisablingBandwidthHigh), + rtc::Optional(kDisablingRecoverablePacketLossAtHighBw * 1.01f)); + CheckDecision(controller.get(), true, + kDisablingRecoverablePacketLossAtHighBw * 1.01f); + + UpdateNetworkMetrics(controller.get(), + rtc::Optional(kDisablingBandwidthHigh + 1), + rtc::Optional(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(kDisablingBandwidthLow - 1), + rtc::Optional(1.0)); + CheckDecision(&controller, false, 1.0); + + UpdateNetworkMetrics( + &controller, rtc::Optional(kEnablingBandwidthLow), + rtc::Optional(kEnablingRecoverablePacketLossAtHighBw * 0.99f)); + CheckDecision(&controller, false, + kEnablingRecoverablePacketLossAtHighBw * 0.99f); + + UpdateNetworkMetrics( + &controller, rtc::Optional(kEnablingBandwidthHigh), + rtc::Optional(kEnablingRecoverablePacketLossAtHighBw)); + CheckDecision(&controller, true, kEnablingRecoverablePacketLossAtHighBw); + + UpdateNetworkMetrics( + &controller, rtc::Optional(kDisablingBandwidthHigh), + rtc::Optional(kDisablingRecoverablePacketLossAtHighBw * 1.01f)); + CheckDecision(&controller, true, + kDisablingRecoverablePacketLossAtHighBw * 1.01f); + + UpdateNetworkMetrics(&controller, + rtc::Optional(kDisablingBandwidthHigh + 1), + rtc::Optional(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