diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn index 6c7766d65b..ea3a1b716b 100644 --- a/webrtc/modules/audio_coding/BUILD.gn +++ b/webrtc/modules/audio_coding/BUILD.gn @@ -916,6 +916,7 @@ rtc_static_library("audio_network_adaptor") { "audio_network_adaptor/frame_length_controller.cc", "audio_network_adaptor/frame_length_controller.h", "audio_network_adaptor/include/audio_network_adaptor.h", + "audio_network_adaptor/util/threshold_curve.h", ] deps = [ @@ -1995,6 +1996,7 @@ if (rtc_include_tests) { "audio_network_adaptor/frame_length_controller_unittest.cc", "audio_network_adaptor/mock/mock_controller.h", "audio_network_adaptor/mock/mock_controller_manager.h", + "audio_network_adaptor/util/threshold_curve_unittest.cc", "codecs/builtin_audio_decoder_factory_unittest.cc", "codecs/cng/audio_encoder_cng_unittest.cc", "codecs/cng/cng_unittest.cc", 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 3e4aa2b541..b0d4aed63b 100644 --- a/webrtc/modules/audio_coding/audio_network_adaptor/controller_manager.cc +++ b/webrtc/modules/audio_coding/audio_network_adaptor/controller_manager.cc @@ -20,6 +20,7 @@ #include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h" #include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.h" #include "webrtc/modules/audio_coding/audio_network_adaptor/frame_length_controller.h" +#include "webrtc/modules/audio_coding/audio_network_adaptor/util/threshold_curve.h" #include "webrtc/system_wrappers/include/clock.h" #ifdef WEBRTC_AUDIO_NETWORK_ADAPTOR_DEBUG_DUMP @@ -61,16 +62,14 @@ std::unique_ptr CreateFecControllerPlrBased( 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()), + ThresholdCurve(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()), + ThresholdCurve(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))); } @@ -98,12 +97,12 @@ std::unique_ptr CreateFecControllerRplrBased( return std::unique_ptr( new FecControllerRplrBased(FecControllerRplrBased::Config( initial_fec_enabled, - FecControllerRplrBased::Config::Threshold( + ThresholdCurve( fec_enabling_threshold.low_bandwidth_bps(), fec_enabling_threshold.low_bandwidth_recoverable_packet_loss(), fec_enabling_threshold.high_bandwidth_bps(), fec_enabling_threshold.high_bandwidth_recoverable_packet_loss()), - FecControllerRplrBased::Config::Threshold( + ThresholdCurve( fec_disabling_threshold.low_bandwidth_bps(), fec_disabling_threshold.low_bandwidth_recoverable_packet_loss(), fec_disabling_threshold.high_bandwidth_bps(), diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.cc b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.cc index 6252364cd5..835970f17d 100644 --- a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.cc +++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.cc @@ -37,21 +37,12 @@ class NullSmoothingFilter final : public SmoothingFilter { }; } -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) {} - -FecControllerPlrBased::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 ThresholdCurve& fec_enabling_threshold, + const ThresholdCurve& 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), @@ -63,21 +54,8 @@ FecControllerPlrBased::FecControllerPlrBased( std::unique_ptr smoothing_filter) : config_(config), fec_enabled_(config.initial_fec_enabled), - packet_loss_smoother_(std::move(smoothing_filter)), - 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_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_packet_loss); + packet_loss_smoother_(std::move(smoothing_filter)) { + RTC_DCHECK(config_.fec_disabling_threshold <= config_.fec_enabling_threshold); } FecControllerPlrBased::FecControllerPlrBased(const Config& config) @@ -117,53 +95,26 @@ void FecControllerPlrBased::MakeDecision( rtc::Optional(packet_loss ? *packet_loss : 0.0); } -FecControllerPlrBased::ThresholdInfo::ThresholdInfo( - const Config::Threshold& threshold) { - int bandwidth_diff_bps = - threshold.high_bandwidth_bps - threshold.low_bandwidth_bps; - float packet_loss_diff = threshold.high_bandwidth_packet_loss - - threshold.low_bandwidth_packet_loss; - slope = bandwidth_diff_bps == 0 ? 0.0 : packet_loss_diff / bandwidth_diff_bps; - offset = - threshold.low_bandwidth_packet_loss - slope * threshold.low_bandwidth_bps; -} - -float FecControllerPlrBased::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_packet_loss; - } 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 FecControllerPlrBased::FecEnablingDecision( const rtc::Optional& packet_loss) const { - if (!uplink_bandwidth_bps_) + if (!uplink_bandwidth_bps_ || !packet_loss) { return false; - if (!packet_loss) - return false; - return *packet_loss >= GetPacketLossThreshold(*uplink_bandwidth_bps_, - config_.fec_enabling_threshold, - fec_enabling_threshold_info_); + } else { + // Enable when above the curve or exactly on it. + return !config_.fec_enabling_threshold.IsBelowCurve( + {static_cast(*uplink_bandwidth_bps_), *packet_loss}); + } } bool FecControllerPlrBased::FecDisablingDecision( const rtc::Optional& packet_loss) const { - if (!uplink_bandwidth_bps_) + if (!uplink_bandwidth_bps_ || !packet_loss) { return false; - if (!packet_loss) - return false; - return *packet_loss <= GetPacketLossThreshold(*uplink_bandwidth_bps_, - config_.fec_disabling_threshold, - fec_disabling_threshold_info_); + } else { + // Disable when below the curve or exactly on it. + return !config_.fec_disabling_threshold.IsAboveCurve( + {static_cast(*uplink_bandwidth_bps_), *packet_loss}); + } } } // namespace webrtc diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h index 386470bb32..98d85435de 100644 --- a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h +++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h @@ -16,32 +16,13 @@ #include "webrtc/base/constructormagic.h" #include "webrtc/common_audio/smoothing_filter.h" #include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h" +#include "webrtc/modules/audio_coding/audio_network_adaptor/util/threshold_curve.h" namespace webrtc { class FecControllerPlrBased 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. - // - // packet ^ | - // loss | A| - // | \ A: (low_bandwidth_bps, low_bandwidth_packet_loss) - // | \ B: (high_bandwidth_bps, high_bandwidth_packet_loss) - // | B\________ - // |---------------> bandwidth - Threshold(int low_bandwidth_bps, - float low_bandwidth_packet_loss, - int high_bandwidth_bps, - float high_bandwidth_packet_loss); - int low_bandwidth_bps; - float low_bandwidth_packet_loss; - int high_bandwidth_bps; - float high_bandwidth_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 @@ -53,13 +34,13 @@ class FecControllerPlrBased final : public Controller { // | OFF \_________ fec_disabling_threshold // |-----------------> bandwidth Config(bool initial_fec_enabled, - const Threshold& fec_enabling_threshold, - const Threshold& fec_disabling_threshold, + const ThresholdCurve& fec_enabling_threshold, + const ThresholdCurve& fec_disabling_threshold, int time_constant_ms, const Clock* clock); bool initial_fec_enabled; - Threshold fec_enabling_threshold; - Threshold fec_disabling_threshold; + ThresholdCurve fec_enabling_threshold; + ThresholdCurve fec_disabling_threshold; int time_constant_ms; const Clock* clock; }; @@ -77,17 +58,6 @@ class FecControllerPlrBased final : public Controller { void MakeDecision(AudioNetworkAdaptor::EncoderRuntimeConfig* config) override; private: - // Characterize Threshold with 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 rtc::Optional& packet_loss) const; bool FecDisablingDecision(const rtc::Optional& packet_loss) const; @@ -96,9 +66,6 @@ class FecControllerPlrBased final : public Controller { rtc::Optional uplink_bandwidth_bps_; const std::unique_ptr packet_loss_smoother_; - const ThresholdInfo fec_enabling_threshold_info_; - const ThresholdInfo fec_disabling_threshold_info_; - RTC_DISALLOW_COPY_AND_ASSIGN(FecControllerPlrBased); }; diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based_unittest.cc b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based_unittest.cc index 34f16c8272..f55a443107 100644 --- a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based_unittest.cc +++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based_unittest.cc @@ -56,14 +56,13 @@ FecControllerPlrBasedTestStates CreateFecControllerPlrBased( std::unique_ptr mock_smoothing_filter( new NiceMock()); states.packet_loss_smoother = mock_smoothing_filter.get(); - using Threshold = FecControllerPlrBased::Config::Threshold; states.controller.reset(new FecControllerPlrBased( FecControllerPlrBased::Config( initial_fec_enabled, - Threshold(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw, - kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw), - Threshold(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw, - kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw), + ThresholdCurve(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw, + kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw), + ThresholdCurve(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw, + kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw), 0, nullptr), std::move(mock_smoothing_filter))); return states; @@ -313,14 +312,13 @@ TEST(FecControllerPlrBasedTest, CheckBehaviorOnSpecialCurves) { std::unique_ptr mock_smoothing_filter( new NiceMock()); states.packet_loss_smoother = mock_smoothing_filter.get(); - using Threshold = FecControllerPlrBased::Config::Threshold; states.controller.reset(new FecControllerPlrBased( FecControllerPlrBased::Config( true, - Threshold(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw, - kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw), - Threshold(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw, - kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw), + ThresholdCurve(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw, + kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw), + ThresholdCurve(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw, + kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw), 0, nullptr), std::move(mock_smoothing_filter))); @@ -353,15 +351,16 @@ TEST(FecControllerPlrBasedDeathTest, InvalidConfig) { std::unique_ptr mock_smoothing_filter( new NiceMock()); states.packet_loss_smoother = mock_smoothing_filter.get(); - using Threshold = FecControllerPlrBased::Config::Threshold; EXPECT_DEATH( states.controller.reset(new FecControllerPlrBased( FecControllerPlrBased::Config( true, - Threshold(kDisablingBandwidthLow - 1, kEnablingPacketLossAtLowBw, - kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw), - Threshold(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw, - kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw), + ThresholdCurve(kDisablingBandwidthLow - 1, + kEnablingPacketLossAtLowBw, kEnablingBandwidthHigh, + kEnablingPacketLossAtHighBw), + ThresholdCurve( + kDisablingBandwidthLow, kDisablingPacketLossAtLowBw, + kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw), 0, nullptr), std::move(mock_smoothing_filter))), "Check failed"); 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 index 0a59ab6752..1cab719db9 100644 --- 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 @@ -17,42 +17,17 @@ 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) +FecControllerRplrBased::Config::Config( + bool initial_fec_enabled, + const ThresholdCurve& fec_enabling_threshold, + const ThresholdCurve& fec_disabling_threshold) : initial_fec_enabled(initial_fec_enabled), fec_enabling_threshold(fec_enabling_threshold), fec_disabling_threshold(fec_disabling_threshold) {} 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); + : config_(config), fec_enabled_(config.initial_fec_enabled) { + RTC_DCHECK(config_.fec_disabling_threshold <= config_.fec_enabling_threshold); } FecControllerRplrBased::~FecControllerRplrBased() = default; @@ -79,44 +54,14 @@ void FecControllerRplrBased::MakeDecision( 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_); + // Enable when above the curve or exactly on it. + return !config_.fec_enabling_threshold.IsBelowCurve( + {static_cast(*uplink_bandwidth_bps_), + *uplink_recoverable_packet_loss_}); } } @@ -124,10 +69,10 @@ 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_); + // Disable when below the curve or exactly on it. + return !config_.fec_disabling_threshold.IsAboveCurve( + {static_cast(*uplink_bandwidth_bps_), + *uplink_recoverable_packet_loss_}); } } 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 index e03ccacf28..849f43cc0b 100644 --- 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 @@ -15,35 +15,13 @@ #include "webrtc/base/constructormagic.h" #include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h" +#include "webrtc/modules/audio_coding/audio_network_adaptor/util/threshold_curve.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 @@ -56,11 +34,11 @@ class FecControllerRplrBased final : public Controller { // | OFF \_________ fec_disabling_threshold // |-----------------> bandwidth Config(bool initial_fec_enabled, - const Threshold& fec_enabling_threshold, - const Threshold& fec_disabling_threshold); + const ThresholdCurve& fec_enabling_threshold, + const ThresholdCurve& fec_disabling_threshold); bool initial_fec_enabled; - Threshold fec_enabling_threshold; - Threshold fec_disabling_threshold; + ThresholdCurve fec_enabling_threshold; + ThresholdCurve fec_disabling_threshold; }; explicit FecControllerRplrBased(const Config& config); @@ -72,18 +50,6 @@ class FecControllerRplrBased final : public Controller { 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; @@ -92,9 +58,6 @@ class FecControllerRplrBased final : public Controller { 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); }; 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 index 9221b6e554..6cb63cd9b9 100644 --- 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 @@ -57,17 +57,16 @@ rtc::Optional GetRandomProbabilityOrUnknown() { 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( + ThresholdCurve( kEnablingBandwidthLow, kEnablingRecoverablePacketLossAtLowBw, kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw), - Threshold(kDisablingBandwidthLow, - kDisablingRecoverablePacketLossAtLowBw, - kDisablingBandwidthHigh, - kDisablingRecoverablePacketLossAtHighBw)))); + ThresholdCurve(kDisablingBandwidthLow, + kDisablingRecoverablePacketLossAtLowBw, + kDisablingBandwidthHigh, + kDisablingRecoverablePacketLossAtHighBw)))); } void UpdateNetworkMetrics( @@ -361,14 +360,14 @@ TEST(FecControllerRplrBasedTest, CheckBehaviorOnSpecialCurves) { 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))); + ThresholdCurve( + kEnablingBandwidthLow, kEnablingRecoverablePacketLossAtLowBw, + kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw), + ThresholdCurve( + kDisablingBandwidthLow, kDisablingRecoverablePacketLossAtLowBw, + kDisablingBandwidthHigh, kDisablingRecoverablePacketLossAtHighBw))); UpdateNetworkMetrics(&controller, rtc::Optional(kDisablingBandwidthLow - 1), @@ -400,17 +399,16 @@ TEST(FecControllerRplrBasedTest, CheckBehaviorOnSpecialCurves) { #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( + ThresholdCurve( kDisablingBandwidthLow - 1, kEnablingRecoverablePacketLossAtLowBw, kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw), - Threshold(kDisablingBandwidthLow, - kDisablingRecoverablePacketLossAtLowBw, - kDisablingBandwidthHigh, - kDisablingRecoverablePacketLossAtHighBw))), + ThresholdCurve(kDisablingBandwidthLow, + kDisablingRecoverablePacketLossAtLowBw, + kDisablingBandwidthHigh, + kDisablingRecoverablePacketLossAtHighBw))), "Check failed"); } #endif diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/util/threshold_curve.h b/webrtc/modules/audio_coding/audio_network_adaptor/util/threshold_curve.h new file mode 100644 index 0000000000..2af4a932d1 --- /dev/null +++ b/webrtc/modules/audio_coding/audio_network_adaptor/util/threshold_curve.h @@ -0,0 +1,118 @@ +/* + * 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_UTIL_THRESHOLD_CURVE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_UTIL_THRESHOLD_CURVE_H_ + +#include "webrtc/base/checks.h" + +namespace webrtc { + +class ThresholdCurve { + public: + struct Point { + constexpr Point(float x, float y) : x(x), y(y) {} + float x; + float y; + }; + + // ThresholdCurve defines a curve. The curve is characterized by the two + // conjunction points: A and B. The curve segments the metric space into + // three domains - above the curve, on it and below it. + // + // y-axis ^ | + // | A| + // | \ A: (a.x, a.y) + // | \ B: (b.x, b.y) + // | B\________ + // |---------------> bandwidth + // + // If either a.x == b.x or a.y == b.y, the curve can be defined + // by a single point. (We merge the two points into one - either the lower or + // the leftmost one - for easier treatment.) + // + // y-axis ^ | + // | | + // | | + // | | + // | P|__________ + // |---------------> bandwidth + ThresholdCurve(const Point& left, const Point& right) + : a(GetPoint(left, right, true)), + b(GetPoint(left, right, false)), + slope(b.x - a.x == 0.0f ? 0.0f : (b.y - a.y) / (b.x - a.x)), + offset(a.y - slope * a.x) { + // TODO(elad.alon): We might want to introduce some numerical validations. + } + + ThresholdCurve(float a_x, float a_y, float b_x, float b_y) + : ThresholdCurve(Point{a_x, a_y}, Point{b_x, b_y}) {} + + // Checks if a point is strictly below the curve. + bool IsBelowCurve(const Point& p) const { + if (p.x < a.x) { + return true; + } else if (p.x == a.x) { + // In principle, we could merge this into the next else, but to avoid + // numerical errors, we treat it separately. + return p.y < a.y; + } else if (a.x < p.x && p.x < b.x) { + return p.y < offset + slope * p.x; + } else { // if (b.x <= p.x) + return p.y < b.y; + } + } + + // Checks if a point is strictly above the curve. + bool IsAboveCurve(const Point& p) const { + if (p.x <= a.x) { + return false; + } else if (a.x < p.x && p.x < b.x) { + return p.y > offset + slope * p.x; + } else { // if (b.x <= p.x) + return p.y > b.y; + } + } + + bool operator<=(const ThresholdCurve& rhs) const { + // This curve is <= the rhs curve if no point from this curve is + // above a corresponding point from the rhs curve. + return !IsBelowCurve(rhs.a) && !IsBelowCurve(rhs.b) && + !rhs.IsAboveCurve(a) && !rhs.IsAboveCurve(b); + } + + private: + static const Point& GetPoint(const Point& left, + const Point& right, + bool is_for_left) { + RTC_DCHECK_LE(left.x, right.x); + RTC_DCHECK_GE(left.y, right.y); + + // Same X-value or Y-value triggers merging both points to the + // lower and/or left of the two points, respectively. + if (left.x == right.x) { + return right; + } else if (left.y == right.y) { + return left; + } + + // If unmerged, boolean flag determines which of the points is desired. + return is_for_left ? left : right; + } + + const Point a; + const Point b; + const float slope; + const float offset; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_UTIL_THRESHOLD_CURVE_H_ diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/util/threshold_curve_unittest.cc b/webrtc/modules/audio_coding/audio_network_adaptor/util/threshold_curve_unittest.cc new file mode 100644 index 0000000000..e6c45e65fe --- /dev/null +++ b/webrtc/modules/audio_coding/audio_network_adaptor/util/threshold_curve_unittest.cc @@ -0,0 +1,576 @@ +/* + * 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 "webrtc/modules/audio_coding/audio_network_adaptor/util/threshold_curve.h" +#include "webrtc/test/gtest.h" + +namespace webrtc { + +namespace { +enum RelativePosition { kBelow, kOn, kAbove }; + +void CheckRelativePosition(const ThresholdCurve& curve, + ThresholdCurve::Point point, + RelativePosition pos) { + RTC_CHECK(pos == kBelow || pos == kOn || pos == kAbove); + + EXPECT_EQ(pos == kBelow, curve.IsBelowCurve(point)); + EXPECT_EQ(pos == kAbove, curve.IsAboveCurve(point)); +} +} // namespace + +TEST(ThresholdCurveTest, PointPosition) { + // The points (P1-P2) define the curve. // + // All other points are above/below/on the curve. // + // // + // ^ // + // | | // + // | A F J R V // + // | | // + // | B P1 K S W // + // | \ // + // | \ // + // | \ L // + // | \ // + // | C G M T X // + // | \ // + // | N \ // + // | \ // + // | D H O P2--Y---------------- // + // | E I Q U Z // + // *----------------------------------> // + constexpr ThresholdCurve::Point p1{1000, 2000}; + constexpr ThresholdCurve::Point p2{2000, 1000}; + + RTC_CHECK_GT((p1.x + p2.x) / 2, p1.x); + RTC_CHECK_LT((p1.x + p2.x) / 2, p2.x); + RTC_CHECK_LT((p1.y + p2.y) / 2, p1.y); + RTC_CHECK_GT((p1.y + p2.y) / 2, p2.y); + + const ThresholdCurve curve(p1, p2); + + { + // All cases where the point lies to the left of P1. + constexpr float x = p1.x - 1; + CheckRelativePosition(curve, {x, p1.y + 1}, kBelow); // A + CheckRelativePosition(curve, {x, p1.y + 0}, kBelow); // B + CheckRelativePosition(curve, {x, (p1.y + p2.y) / 2}, kBelow); // C + CheckRelativePosition(curve, {x, p2.y + 0}, kBelow); // D + CheckRelativePosition(curve, {x, p2.y - 1}, kBelow); // E + } + + { + // All cases where the point has the same x-value as P1. + constexpr float x = p1.x; + CheckRelativePosition(curve, {x, p1.y + 1}, kOn); // F + CheckRelativePosition(curve, {x, p1.y + 0}, kOn); // P1 + CheckRelativePosition(curve, {x, (p1.y + p2.y) / 2}, kBelow); // G + CheckRelativePosition(curve, {x, p2.y + 0}, kBelow); // H + CheckRelativePosition(curve, {x, p2.y - 1}, kBelow); // I + } + + { + // To make sure we're really covering all of the cases, make sure that P1 + // and P2 were chosen so that L would really be below K, and O would really + // be below N. (This would not hold if the Y values are too close together.) + RTC_CHECK_LT(((p1.y + p2.y) / 2) + 1, p1.y); + RTC_CHECK_LT(p2.y, ((p1.y + p2.y) / 2) - 1); + + // All cases where the point's x-value is between P1 and P2. + constexpr float x = (p1.x + p2.x) / 2; + CheckRelativePosition(curve, {x, p1.y + 1}, kAbove); // J + CheckRelativePosition(curve, {x, p1.y + 0}, kAbove); // K + CheckRelativePosition(curve, {x, ((p1.y + p2.y) / 2) + 1}, kAbove); // L + CheckRelativePosition(curve, {x, (p1.y + p2.y) / 2}, kOn); // M + CheckRelativePosition(curve, {x, ((p1.y + p2.y) / 2) - 1}, kBelow); // N + CheckRelativePosition(curve, {x, p2.y + 0}, kBelow); // O + CheckRelativePosition(curve, {x, p2.y - 1}, kBelow); // Q + } + + { + // All cases where the point has the same x-value as P2. + constexpr float x = p2.x; + CheckRelativePosition(curve, {x, p1.y + 1}, kAbove); // R + CheckRelativePosition(curve, {x, p1.y + 0}, kAbove); // S + CheckRelativePosition(curve, {x, (p1.y + p2.y) / 2}, kAbove); // T + CheckRelativePosition(curve, {x, p2.y + 0}, kOn); // P2 + CheckRelativePosition(curve, {x, p2.y - 1}, kBelow); // U + } + + { + // All cases where the point lies to the right of P2. + constexpr float x = p2.x + 1; + CheckRelativePosition(curve, {x, p1.y + 1}, kAbove); // V + CheckRelativePosition(curve, {x, p1.y + 0}, kAbove); // W + CheckRelativePosition(curve, {x, (p1.y + p2.y) / 2}, kAbove); // X + CheckRelativePosition(curve, {x, p2.y + 0}, kOn); // Y + CheckRelativePosition(curve, {x, p2.y - 1}, kBelow); // Z + } +} + +TEST(ThresholdCurveTest, CurvePointsOnHorizontalLine) { + // The points (P1-P2) define the curve. + // All other points are above/below/on the curve. + // + // ^ + // | | + // | | + // | A D F I K + // | | + // | | + // | B P1--G--P2-L-- + // | C E H J M + // *------------------> + + constexpr ThresholdCurve::Point p1{100, 200}; + constexpr ThresholdCurve::Point p2{p1.x + 1, p1.y}; + + RTC_CHECK_GT((p1.x + p2.x) / 2, p1.x); + RTC_CHECK_LT((p1.x + p2.x) / 2, p2.x); + + const ThresholdCurve curve(p1, p2); + + { + // All cases where the point lies to the left of P1. + constexpr float x = p1.x - 1; + CheckRelativePosition(curve, {x, p1.y + 1}, kBelow); // A + CheckRelativePosition(curve, {x, p1.y + 0}, kBelow); // B + CheckRelativePosition(curve, {x, p1.y - 1}, kBelow); // C + } + + { + // All cases where the point has the same x-value as P1. + constexpr float x = p1.x; + CheckRelativePosition(curve, {x, p1.y + 1}, kOn); // D + CheckRelativePosition(curve, {x, p1.y + 0}, kOn); // P1 + CheckRelativePosition(curve, {x, p1.y - 1}, kBelow); // E + } + + { + // All cases where the point's x-value is between P1 and P2. + constexpr float x = (p1.x + p2.x) / 2; + CheckRelativePosition(curve, {x, p1.y + 1}, kAbove); // F + CheckRelativePosition(curve, {x, p1.y + 0}, kOn); // G + CheckRelativePosition(curve, {x, p1.y - 1}, kBelow); // H + } + + { + // All cases where the point has the same x-value as P2. + constexpr float x = p2.x; + CheckRelativePosition(curve, {x, p1.y + 1}, kAbove); // I + CheckRelativePosition(curve, {x, p1.y + 0}, kOn); // P2 + CheckRelativePosition(curve, {x, p1.y - 1}, kBelow); // J + } + + { + // All cases where the point lies to the right of P2. + constexpr float x = p2.x + 1; + CheckRelativePosition(curve, {x, p1.y + 1}, kAbove); // K + CheckRelativePosition(curve, {x, p1.y + 0}, kOn); // L + CheckRelativePosition(curve, {x, p1.y - 1}, kBelow); // M + } +} + +TEST(ThresholdCurveTest, CurvePointsOnVerticalLine) { + // The points (P1-P2) define the curve. + // All other points are above/below/on the curve. + // + // ^ + // | | + // | A B C + // | | + // | D P1 E + // | | + // | F G H + // | | + // | I P2--J------ + // | K L M + // *------------------> + + constexpr ThresholdCurve::Point p1{100, 200}; + constexpr ThresholdCurve::Point p2{p1.x, p1.y - 1}; + + constexpr float left = p1.x - 1; + constexpr float on = p1.x; + constexpr float right = p1.x + 1; + + RTC_CHECK_LT((p1.y + p2.y) / 2, p1.y); + RTC_CHECK_GT((p1.y + p2.y) / 2, p2.y); + + const ThresholdCurve curve(p1, p2); + + { + // All cases where the point lies above P1. + constexpr float y = p1.y + 1; + CheckRelativePosition(curve, {left, y}, kBelow); // A + CheckRelativePosition(curve, {on, y}, kOn); // B + CheckRelativePosition(curve, {right, y}, kAbove); // C + } + + { + // All cases where the point has the same y-value as P1. + constexpr float y = p1.y; + CheckRelativePosition(curve, {left, y}, kBelow); // D + CheckRelativePosition(curve, {on, y}, kOn); // P1 + CheckRelativePosition(curve, {right, y}, kAbove); // E + } + + { + // All cases where the point's y-value is between P1 and P2. + constexpr float y = (p1.y + p2.y) / 2; + CheckRelativePosition(curve, {left, y}, kBelow); // F + CheckRelativePosition(curve, {on, y}, kOn); // G + CheckRelativePosition(curve, {right, y}, kAbove); // H + } + + { + // All cases where the point has the same y-value as P2. + constexpr float y = p2.y; + CheckRelativePosition(curve, {left, y}, kBelow); // I + CheckRelativePosition(curve, {on, y}, kOn); // P2 + CheckRelativePosition(curve, {right, y}, kOn); // J + } + + { + // All cases where the point lies below P2. + constexpr float y = p2.y - 1; + CheckRelativePosition(curve, {left, y}, kBelow); // K + CheckRelativePosition(curve, {on, y}, kBelow); // L + CheckRelativePosition(curve, {right, y}, kBelow); // M + } +} + +TEST(ThresholdCurveTest, SinglePointCurve) { + // The points (P1-P2) define the curve. + // All other points are above/below/on the curve. + // + // ^ + // | | + // | A D F + // | | + // | B P---G------ + // | C E H + // *------------------> + + constexpr ThresholdCurve::Point p{100, 200}; + + const ThresholdCurve curve(p, p); + + { + // All cases where the point lies to the left of P. + constexpr float x = p.x - 1; + CheckRelativePosition(curve, {x, p.y + 1}, kBelow); // A + CheckRelativePosition(curve, {x, p.y + 0}, kBelow); // B + CheckRelativePosition(curve, {x, p.y - 1}, kBelow); // C + } + + { + // All cases where the point has the same x-value as P. + constexpr float x = p.x + 0; + CheckRelativePosition(curve, {x, p.y + 1}, kOn); // D + CheckRelativePosition(curve, {x, p.y + 0}, kOn); // P + CheckRelativePosition(curve, {x, p.y - 1}, kBelow); // E + } + + { + // All cases where the point lies to the right of P. + constexpr float x = p.x + 1; + CheckRelativePosition(curve, {x, p.y + 1}, kAbove); // F + CheckRelativePosition(curve, {x, p.y + 0}, kOn); // G + CheckRelativePosition(curve, {x, p.y - 1}, kBelow); // H + } +} + +TEST(ThresholdCurveTest, TwoCurvesSameProjection) { + // ^ // + // | C1 + C2 // + // | | // + // | |\ // + // | | \ // + // | \ \ // + // | \ \ // + // | \ \ // + // | \ -------- C2 // + // | --------- C1 // + // *---------------------> // + + constexpr ThresholdCurve::Point c1_left{5, 10}; + constexpr ThresholdCurve::Point c1_right{10, 5}; + const ThresholdCurve c1_curve(c1_left, c1_right); + + // Same x-values, but higher on Y. (Can be parallel, but doesn't have to be.) + constexpr ThresholdCurve::Point c2_left{c1_left.x, c1_left.y + 20}; + constexpr ThresholdCurve::Point c2_right{c1_right.x, c1_right.y + 10}; + const ThresholdCurve c2_curve(c2_left, c2_right); + + EXPECT_TRUE(c1_curve <= c2_curve); + EXPECT_FALSE(c2_curve <= c1_curve); +} + +TEST(ThresholdCurveTest, HigherCurveProjectionWithinLowerProjection) { + // ^ // + // | C1 C2 // + // | | | // + // | | | // + // | \ | // + // | \ | // + // | \ \ // + // | \ \ // + // | \ --------- C2 // + // | \ // + // | \ // + // | ---------C1 // + // *---------------------> // + + constexpr ThresholdCurve::Point c1_left{5, 10}; + constexpr ThresholdCurve::Point c1_right{10, 5}; + const ThresholdCurve c1_curve(c1_left, c1_right); + + constexpr ThresholdCurve::Point c2_left{6, 11}; + constexpr ThresholdCurve::Point c2_right{9, 7}; + const ThresholdCurve c2_curve(c2_left, c2_right); + + EXPECT_TRUE(c1_curve <= c2_curve); + EXPECT_FALSE(c2_curve <= c1_curve); +} + +TEST(ThresholdCurveTest, SecondCurvePointsOnFirstCurveExtensions) { + // ^ + // | C1 + C2 // + // | | // + // | |\ // + // | | \ // + // | \ \ // + // | \ \ // + // | \ \ // + // | \ \ // + // | ----- C1 + C2 // + // *---------------------> // + + constexpr ThresholdCurve::Point c1_left{5, 10}; + constexpr ThresholdCurve::Point c1_right{10, 5}; + const ThresholdCurve c1_curve(c1_left, c1_right); + + // Same x-values, but one of the points is higher on Y (the other isn't). + constexpr ThresholdCurve::Point c2_left{c1_left.x, c1_left.y + 2}; + constexpr ThresholdCurve::Point c2_right{c1_right.x + 3, c1_right.y}; + const ThresholdCurve c2_curve(c2_left, c2_right); + + EXPECT_TRUE(c1_curve <= c2_curve); + EXPECT_FALSE(c2_curve <= c1_curve); +} + +TEST(ThresholdCurveTest, SecondCurveCrossesLeftExtension) { + // ^ // + // | C2 C1 // + // | | | // + // | \| // + // | | // + // | |\ // + // | | \ // + // | \ \ // + // | \ \ // + // | \ \ // + // | \ ------- C2 // + // | -------- C1 // + // *---------------------> // + + constexpr ThresholdCurve::Point c1_left{5, 10}; + constexpr ThresholdCurve::Point c1_right{10, 5}; + const ThresholdCurve c1_curve(c1_left, c1_right); + + constexpr ThresholdCurve::Point c2_left{c1_left.x - 1, c1_left.y + 1}; + constexpr ThresholdCurve::Point c2_right{c1_right.x, c1_right.y + 1}; + const ThresholdCurve c2_curve(c2_left, c2_right); + + EXPECT_FALSE(c1_curve <= c2_curve); + EXPECT_FALSE(c2_curve <= c1_curve); +} + +TEST(ThresholdCurveTest, SecondCurveCrossesRightExtension) { + // ^ // + // | C1 + C2 // + // | | // + // | |\ // + // | \ \ // + // | \ \ // + // | \ \ // + // | \ \ // + // | ----------- C1 // + // | \ // + // | ------- C2 // + // *--------------------> // + + constexpr ThresholdCurve::Point c1_left{5, 10}; + constexpr ThresholdCurve::Point c1_right{10, 5}; + const ThresholdCurve c1_curve(c1_left, c1_right); + + constexpr ThresholdCurve::Point c2_left{c1_left.x, c1_left.y + 1}; + constexpr ThresholdCurve::Point c2_right{c1_right.x + 2, c1_right.y - 1}; + const ThresholdCurve c2_curve(c2_left, c2_right); + + EXPECT_FALSE(c1_curve <= c2_curve); + EXPECT_FALSE(c2_curve <= c1_curve); +} + +TEST(ThresholdCurveTest, SecondCurveCrossesFirstCurveBetweenPoints) { + // ^ // + // | C2 C1 // + // | | | // + // | | | // + // | | \ // + // | | \ // + // | -_ \ // + // | -_ \ // + // | -_\ // + // | -_ // + // | \-_ // + // | \ ---------- C2 // + // | ----------- C1 // + // | // + // | // + // *-------------------------> // + + constexpr ThresholdCurve::Point c1_left{5, 10}; + constexpr ThresholdCurve::Point c1_right{10, 5}; + const ThresholdCurve c1_curve(c1_left, c1_right); + + constexpr ThresholdCurve::Point c2_left{4, 9}; + constexpr ThresholdCurve::Point c2_right{10, 6}; + const ThresholdCurve c2_curve(c2_left, c2_right); + + // The test is structured so that the two curves intersect at (8, 7). + RTC_CHECK(!c1_curve.IsAboveCurve({8, 7})); + RTC_CHECK(!c1_curve.IsBelowCurve({8, 7})); + RTC_CHECK(!c2_curve.IsAboveCurve({8, 7})); + RTC_CHECK(!c2_curve.IsBelowCurve({8, 7})); + + EXPECT_FALSE(c1_curve <= c2_curve); + EXPECT_FALSE(c2_curve <= c1_curve); +} + +TEST(ThresholdCurveTest, SecondCurveRightPointAboveFirstRightExtension) { + // ^ // + // | C1 + C2 // + // | | // + // | |\ // + // | | \ // + // | | \ // + // | | \ // + // | | \ // + // | \ \ // + // | \ \ // + // | \ \ // + // | \ ----- C2 // + // | --------- C1 // + // *---------------------> // + + constexpr ThresholdCurve::Point c1_left{5, 10}; + constexpr ThresholdCurve::Point c1_right{10, 5}; + const ThresholdCurve c1_curve(c1_left, c1_right); + + constexpr ThresholdCurve::Point c2_left{c1_left.x, c1_left.y + 1}; + constexpr ThresholdCurve::Point c2_right{c1_right.x + 1, c1_right.y + 1}; + const ThresholdCurve c2_curve(c2_left, c2_right); + + EXPECT_TRUE(c1_curve <= c2_curve); + EXPECT_FALSE(c2_curve <= c1_curve); +} + +TEST(ThresholdCurveTest, IdenticalCurves) { + // ^ // + // | C1 + C2 // + // | | // + // | | // + // | \ // + // | \ // + // | \ // + // | ------- C1 + C2 // + // *---------------------> // + + constexpr ThresholdCurve::Point left{5, 10}; + constexpr ThresholdCurve::Point right{10, 5}; + + const ThresholdCurve c1_curve(left, right); + const ThresholdCurve c2_curve(left, right); + + EXPECT_TRUE(c1_curve <= c2_curve); + EXPECT_TRUE(c2_curve <= c1_curve); +} + +TEST(ThresholdCurveTest, AlmostIdenticalCurvesSecondContinuesOnOtherLeftSide) { + // ^ // + // | C2 C1 // + // | | | // + // | | | // + // | \| // + // | | // + // | \ // + // | \ // + // | \ // + // | ----- C1 + C2 // + // *---------------------> // + + constexpr ThresholdCurve::Point c1_left{5, 10}; + constexpr ThresholdCurve::Point c1_right{10, 5}; + const ThresholdCurve c1_curve(c1_left, c1_left); + + constexpr ThresholdCurve::Point c2_left{c1_left.x - 1, c1_left.y + 1}; + constexpr ThresholdCurve::Point c2_right = c1_right; + const ThresholdCurve c2_curve(c2_left, c2_right); + + EXPECT_FALSE(c1_curve <= c2_curve); + EXPECT_TRUE(c2_curve <= c1_curve); +} + +TEST(ThresholdCurveTest, AlmostIdenticalCurvesSecondContinuesOnOtherRightSide) { + // ^ // + // | C1 + C2 // + // | | // + // | | // + // | \ // + // | \ // + // | \ // + // | \----------- C1 // + // | \ // + // | ---------- C2 // + // *---------------------> // + + constexpr ThresholdCurve::Point c1_left{5, 10}; + constexpr ThresholdCurve::Point c1_right{10, 5}; + const ThresholdCurve c1_curve(c1_left, c1_left); + + constexpr ThresholdCurve::Point c2_left = c1_left; + constexpr ThresholdCurve::Point c2_right{c1_right.x + 1, c1_right.y - 1}; + const ThresholdCurve c2_curve(c2_left, c2_right); + + EXPECT_FALSE(c1_curve <= c2_curve); + EXPECT_TRUE(c2_curve <= c1_curve); +} + +#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) +TEST(ThresholdCurveTest, WrongOrderPoints) { + std::unique_ptr curve; + constexpr ThresholdCurve::Point left{5, 10}; + constexpr ThresholdCurve::Point right{10, 5}; + EXPECT_DEATH(curve.reset(new ThresholdCurve(right, left)), ""); +} + +TEST(ThresholdCurveTest, SlopeMustBeNonPositive) { + std::unique_ptr curve; + constexpr ThresholdCurve::Point left{5, 5}; + constexpr ThresholdCurve::Point right{10, 10}; + EXPECT_DEATH(curve.reset(new ThresholdCurve(right, left)), ""); +} +#endif + +} // namespace webrtc