Avoid code duplication between PLR/RPLR-based FecController
BUG=webrtc:7058 Review-Url: https://codereview.webrtc.org/2688613003 Cr-Commit-Position: refs/heads/master@{#17437}
This commit is contained in:
parent
61abe15829
commit
326263a678
@ -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",
|
||||
|
||||
@ -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<FecControllerPlrBased> CreateFecControllerPlrBased(
|
||||
return std::unique_ptr<FecControllerPlrBased>(
|
||||
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<FecControllerRplrBased> CreateFecControllerRplrBased(
|
||||
return std::unique_ptr<FecControllerRplrBased>(
|
||||
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(),
|
||||
|
||||
@ -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<SmoothingFilter> 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<float>(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<float>::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<float>& 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<float>(*uplink_bandwidth_bps_), *packet_loss});
|
||||
}
|
||||
}
|
||||
|
||||
bool FecControllerPlrBased::FecDisablingDecision(
|
||||
const rtc::Optional<float>& 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<float>(*uplink_bandwidth_bps_), *packet_loss});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -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<float>& packet_loss) const;
|
||||
bool FecDisablingDecision(const rtc::Optional<float>& packet_loss) const;
|
||||
|
||||
@ -96,9 +66,6 @@ class FecControllerPlrBased final : public Controller {
|
||||
rtc::Optional<int> uplink_bandwidth_bps_;
|
||||
const std::unique_ptr<SmoothingFilter> packet_loss_smoother_;
|
||||
|
||||
const ThresholdInfo fec_enabling_threshold_info_;
|
||||
const ThresholdInfo fec_disabling_threshold_info_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(FecControllerPlrBased);
|
||||
};
|
||||
|
||||
|
||||
@ -56,14 +56,13 @@ FecControllerPlrBasedTestStates CreateFecControllerPlrBased(
|
||||
std::unique_ptr<MockSmoothingFilter> mock_smoothing_filter(
|
||||
new NiceMock<MockSmoothingFilter>());
|
||||
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<MockSmoothingFilter> mock_smoothing_filter(
|
||||
new NiceMock<MockSmoothingFilter>());
|
||||
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<MockSmoothingFilter> mock_smoothing_filter(
|
||||
new NiceMock<MockSmoothingFilter>());
|
||||
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");
|
||||
|
||||
@ -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<float>::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<float>(*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<float>(*uplink_bandwidth_bps_),
|
||||
*uplink_recoverable_packet_loss_});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<int> uplink_bandwidth_bps_;
|
||||
rtc::Optional<float> uplink_recoverable_packet_loss_;
|
||||
|
||||
const ThresholdInfo fec_enabling_threshold_info_;
|
||||
const ThresholdInfo fec_disabling_threshold_info_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(FecControllerRplrBased);
|
||||
};
|
||||
|
||||
|
||||
@ -57,17 +57,16 @@ rtc::Optional<float> GetRandomProbabilityOrUnknown() {
|
||||
|
||||
std::unique_ptr<FecControllerRplrBased> CreateFecControllerRplrBased(
|
||||
bool initial_fec_enabled) {
|
||||
using Threshold = FecControllerRplrBased::Config::Threshold;
|
||||
return std::unique_ptr<FecControllerRplrBased>(
|
||||
new FecControllerRplrBased(FecControllerRplrBased::Config(
|
||||
initial_fec_enabled,
|
||||
Threshold(
|
||||
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<int>(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
|
||||
|
||||
@ -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_
|
||||
@ -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 <memory>
|
||||
|
||||
#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<ThresholdCurve> 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<ThresholdCurve> curve;
|
||||
constexpr ThresholdCurve::Point left{5, 5};
|
||||
constexpr ThresholdCurve::Point right{10, 10};
|
||||
EXPECT_DEATH(curve.reset(new ThresholdCurve(right, left)), "");
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace webrtc
|
||||
Loading…
x
Reference in New Issue
Block a user