Adding FecController to audio network adaptor.
BUG=webrtc:6303
Review-Url: https://codereview.webrtc.org/2337103006
Cr-Commit-Position: refs/heads/master@{#14351}
diff --git a/webrtc/modules/BUILD.gn b/webrtc/modules/BUILD.gn
index 11a5ef0..6b7928f 100644
--- a/webrtc/modules/BUILD.gn
+++ b/webrtc/modules/BUILD.gn
@@ -243,6 +243,7 @@
"audio_coding/audio_network_adaptor/channel_controller_unittest.cc",
"audio_coding/audio_network_adaptor/controller_manager_unittest.cc",
"audio_coding/audio_network_adaptor/dtx_controller_unittest.cc",
+ "audio_coding/audio_network_adaptor/fec_controller_unittest.cc",
"audio_coding/audio_network_adaptor/frame_length_controller_unittest.cc",
"audio_coding/audio_network_adaptor/mock/mock_controller.h",
"audio_coding/audio_network_adaptor/mock/mock_controller_manager.h",
diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn
index b55829d..eaf2e74 100644
--- a/webrtc/modules/audio_coding/BUILD.gn
+++ b/webrtc/modules/audio_coding/BUILD.gn
@@ -711,6 +711,8 @@
"audio_network_adaptor/controller_manager.h",
"audio_network_adaptor/dtx_controller.cc",
"audio_network_adaptor/dtx_controller.h",
+ "audio_network_adaptor/fec_controller.cc",
+ "audio_network_adaptor/fec_controller.h",
"audio_network_adaptor/frame_length_controller.cc",
"audio_network_adaptor/frame_length_controller.h",
"audio_network_adaptor/include/audio_network_adaptor.h",
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor.gypi b/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor.gypi
index ea3ef13..af6ba55 100644
--- a/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor.gypi
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor.gypi
@@ -24,6 +24,8 @@
'controller_manager.h',
'dtx_controller.h',
'dtx_controller.cc',
+ 'fec_controller.h',
+ 'fec_controller.cc',
'frame_length_controller.cc',
'frame_length_controller.h',
'include/audio_network_adaptor.h',
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.cc b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.cc
new file mode 100644
index 0000000..fcf1959
--- /dev/null
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.h"
+
+#include <limits>
+#include <utility>
+
+#include "webrtc/base/checks.h"
+
+namespace webrtc {
+
+FecController::Config::Threshold::Threshold(int low_bandwidth_bps,
+ float low_bandwidth_packet_loss,
+ int high_bandwidth_bps,
+ float high_bandwidth_packet_loss)
+ : low_bandwidth_bps(low_bandwidth_bps),
+ low_bandwidth_packet_loss(low_bandwidth_packet_loss),
+ high_bandwidth_bps(high_bandwidth_bps),
+ high_bandwidth_packet_loss(high_bandwidth_packet_loss) {}
+
+FecController::Config::Config(bool initial_fec_enabled,
+ const Threshold& fec_enabling_threshold,
+ const Threshold& fec_disabling_threshold,
+ int time_constant_ms,
+ Clock* clock)
+ : initial_fec_enabled(initial_fec_enabled),
+ fec_enabling_threshold(fec_enabling_threshold),
+ fec_disabling_threshold(fec_disabling_threshold),
+ time_constant_ms(time_constant_ms),
+ clock(clock) {}
+
+FecController::FecController(const Config& config)
+ : config_(config),
+ fec_enabled_(config.initial_fec_enabled),
+ packet_loss_smoothed_(
+ new SmoothingFilterImpl(config_.time_constant_ms, config_.clock)),
+ 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);
+}
+
+FecController::FecController(const Config& config,
+ std::unique_ptr<SmoothingFilter> smoothing_filter)
+ : FecController(config) {
+ packet_loss_smoothed_ = std::move(smoothing_filter);
+}
+
+FecController::~FecController() = default;
+
+void FecController::MakeDecision(
+ const NetworkMetrics& metrics,
+ AudioNetworkAdaptor::EncoderRuntimeConfig* config) {
+ RTC_DCHECK(!config->enable_fec);
+ RTC_DCHECK(!config->uplink_packet_loss_fraction);
+
+ if (metrics.uplink_packet_loss_fraction)
+ packet_loss_smoothed_->AddSample(*metrics.uplink_packet_loss_fraction);
+
+ fec_enabled_ = fec_enabled_ ? !FecDisablingDecision(metrics)
+ : FecEnablingDecision(metrics);
+
+ config->enable_fec = rtc::Optional<bool>(fec_enabled_);
+
+ auto packet_loss_fraction = packet_loss_smoothed_->GetAverage();
+ config->uplink_packet_loss_fraction = rtc::Optional<float>(
+ packet_loss_fraction ? *packet_loss_fraction : 0.0);
+}
+
+FecController::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 FecController::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();
+ if (bandwidth_bps >= threshold.high_bandwidth_bps)
+ return threshold.high_bandwidth_packet_loss;
+ return threshold_info.offset + threshold_info.slope * bandwidth_bps;
+}
+
+bool FecController::FecEnablingDecision(const NetworkMetrics& metrics) const {
+ if (!metrics.uplink_bandwidth_bps)
+ return false;
+
+ auto packet_loss = packet_loss_smoothed_->GetAverage();
+ if (!packet_loss)
+ return false;
+
+ return *packet_loss >= GetPacketLossThreshold(*metrics.uplink_bandwidth_bps,
+ config_.fec_enabling_threshold,
+ fec_enabling_threshold_info_);
+}
+
+bool FecController::FecDisablingDecision(const NetworkMetrics& metrics) const {
+ if (!metrics.uplink_bandwidth_bps)
+ return false;
+
+ auto packet_loss = packet_loss_smoothed_->GetAverage();
+ if (!packet_loss)
+ return false;
+
+ return *packet_loss <= GetPacketLossThreshold(*metrics.uplink_bandwidth_bps,
+ config_.fec_disabling_threshold,
+ fec_disabling_threshold_info_);
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.h b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.h
new file mode 100644
index 0000000..17aa65f
--- /dev/null
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_H_
+#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_H_
+
+#include <memory>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h"
+#include "webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.h"
+
+namespace webrtc {
+
+class FecController 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
+ //
+ // packet-loss ^ | |
+ // | | | FEC
+ // | \ \ ON
+ // | FEC \ \_______ fec_enabling_threshold
+ // | OFF \_________ fec_disabling_threshold
+ // |-----------------> bandwidth
+ Config(bool initial_fec_enabled,
+ const Threshold& fec_enabling_threshold,
+ const Threshold& fec_disabling_threshold,
+ int time_constant_ms,
+ Clock* clock);
+ bool initial_fec_enabled;
+ Threshold fec_enabling_threshold;
+ Threshold fec_disabling_threshold;
+ int time_constant_ms;
+ Clock* clock;
+ };
+
+ explicit FecController(const Config& config);
+
+ // Dependency injection for testing.
+ FecController(const Config& config,
+ std::unique_ptr<SmoothingFilter> smoothing_filter);
+
+ ~FecController() override;
+
+ void MakeDecision(const NetworkMetrics& metrics,
+ 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 NetworkMetrics& metrics) const;
+ bool FecDisablingDecision(const NetworkMetrics& metrics) const;
+
+ const Config config_;
+ bool fec_enabled_;
+ std::unique_ptr<SmoothingFilter> packet_loss_smoothed_;
+
+ const ThresholdInfo fec_enabling_threshold_info_;
+ const ThresholdInfo fec_disabling_threshold_info_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(FecController);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_H_
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_unittest.cc b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_unittest.cc
new file mode 100644
index 0000000..c66482f
--- /dev/null
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_unittest.cc
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2016 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 <utility>
+
+#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller.h"
+#include "webrtc/modules/audio_coding/audio_network_adaptor/mock/mock_smoothing_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webrtc {
+
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::_;
+
+namespace {
+
+// The test uses the following settings:
+//
+// packet-loss ^ | |
+// | A| C| FEC
+// | \ \ ON
+// | FEC \ D\_______
+// | OFF B\_________
+// |-----------------> bandwidth
+//
+// A : (kDisablingBandwidthLow, kDisablingPacketLossAtLowBw)
+// B : (kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw)
+// C : (kEnablingBandwidthLow, kEnablingPacketLossAtLowBw)
+// D : (kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw)
+
+constexpr int kDisablingBandwidthLow = 15000;
+constexpr float kDisablingPacketLossAtLowBw = 0.08f;
+constexpr int kDisablingBandwidthHigh = 64000;
+constexpr float kDisablingPacketLossAtHighBw = 0.01f;
+constexpr int kEnablingBandwidthLow = 17000;
+constexpr float kEnablingPacketLossAtLowBw = 0.1f;
+constexpr int kEnablingBandwidthHigh = 64000;
+constexpr float kEnablingPacketLossAtHighBw = 0.05f;
+
+struct FecControllerStates {
+ std::unique_ptr<FecController> controller;
+ MockSmoothingFilter* packet_loss_smoothed;
+};
+
+FecControllerStates CreateFecController(bool initial_fec_enabled) {
+ FecControllerStates states;
+ std::unique_ptr<MockSmoothingFilter> mock_smoothing_filter(
+ new NiceMock<MockSmoothingFilter>());
+ states.packet_loss_smoothed = mock_smoothing_filter.get();
+ EXPECT_CALL(*states.packet_loss_smoothed, Die());
+ using Threshold = FecController::Config::Threshold;
+ states.controller.reset(new FecController(
+ FecController::Config(
+ initial_fec_enabled,
+ Threshold(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw,
+ kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw),
+ Threshold(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw,
+ kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw),
+ 0, nullptr),
+ std::move(mock_smoothing_filter)));
+ return states;
+}
+
+// Checks that the FEC decision given by |states->controller->MakeDecision|
+// matches |expected_enable_fec|. It also checks that
+// |uplink_packet_loss_fraction| returned by |states->controller->MakeDecision|
+// matches |uplink_packet_loss|.
+void CheckDecision(FecControllerStates* states,
+ const rtc::Optional<int>& uplink_bandwidth_bps,
+ const rtc::Optional<float>& uplink_packet_loss,
+ bool expected_enable_fec) {
+ Controller::NetworkMetrics metrics;
+ metrics.uplink_bandwidth_bps = uplink_bandwidth_bps;
+ metrics.uplink_packet_loss_fraction = uplink_packet_loss;
+
+ if (uplink_packet_loss) {
+ // Check that smoothing filter is updated.
+ EXPECT_CALL(*states->packet_loss_smoothed, AddSample(*uplink_packet_loss));
+ }
+
+ EXPECT_CALL(*states->packet_loss_smoothed, GetAverage())
+ .WillRepeatedly(Return(uplink_packet_loss));
+
+ AudioNetworkAdaptor::EncoderRuntimeConfig config;
+ states->controller->MakeDecision(metrics, &config);
+ EXPECT_EQ(rtc::Optional<bool>(expected_enable_fec), config.enable_fec);
+
+ // Check that |config.uplink_packet_loss_fraction| is properly filled.
+ EXPECT_EQ(uplink_packet_loss ? uplink_packet_loss : rtc::Optional<float>(0.0),
+ config.uplink_packet_loss_fraction);
+}
+
+} // namespace
+
+TEST(FecControllerTest, OutputInitValueWhenUplinkBandwidthUnknown) {
+ constexpr bool kInitialFecEnabled = true;
+ auto states = CreateFecController(kInitialFecEnabled);
+ // Let uplink packet loss fraction be so low that would cause FEC to turn off
+ // if uplink bandwidth was known.
+ CheckDecision(&states, rtc::Optional<int>(),
+ rtc::Optional<float>(kDisablingPacketLossAtHighBw),
+ kInitialFecEnabled);
+}
+
+TEST(FecControllerTest, OutputInitValueWhenUplinkPacketLossFractionUnknown) {
+ constexpr bool kInitialFecEnabled = true;
+ auto states = CreateFecController(kInitialFecEnabled);
+ // Let uplink bandwidth be so low that would cause FEC to turn off if uplink
+ // bandwidth packet loss fraction was known.
+ CheckDecision(&states, rtc::Optional<int>(kDisablingBandwidthLow - 1),
+ rtc::Optional<float>(), kInitialFecEnabled);
+}
+
+TEST(FecControllerTest, EnableFecForHighBandwidth) {
+ auto states = CreateFecController(false);
+ CheckDecision(&states, rtc::Optional<int>(kEnablingBandwidthHigh),
+ rtc::Optional<float>(kEnablingPacketLossAtHighBw), true);
+}
+
+TEST(FecControllerTest, MaintainFecOffForHighBandwidth) {
+ auto states = CreateFecController(false);
+ CheckDecision(&states, rtc::Optional<int>(kEnablingBandwidthHigh),
+ rtc::Optional<float>(kEnablingPacketLossAtHighBw * 0.99f),
+ false);
+}
+
+TEST(FecControllerTest, EnableFecForMediumBandwidth) {
+ auto states = CreateFecController(false);
+ CheckDecision(
+ &states,
+ rtc::Optional<int>((kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2),
+ rtc::Optional<float>(
+ (kEnablingPacketLossAtLowBw + kEnablingPacketLossAtHighBw) / 2.0),
+ true);
+}
+
+TEST(FecControllerTest, MaintainFecOffForMediumBandwidth) {
+ auto states = CreateFecController(false);
+ CheckDecision(
+ &states,
+ rtc::Optional<int>((kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2),
+ rtc::Optional<float>(kEnablingPacketLossAtLowBw * 0.49f +
+ kEnablingPacketLossAtHighBw * 0.51f),
+ false);
+}
+
+TEST(FecControllerTest, EnableFecForLowBandwidth) {
+ auto states = CreateFecController(false);
+ CheckDecision(&states, rtc::Optional<int>(kEnablingBandwidthLow),
+ rtc::Optional<float>(kEnablingPacketLossAtLowBw), true);
+}
+
+TEST(FecControllerTest, MaintainFecOffForLowBandwidth) {
+ auto states = CreateFecController(false);
+ CheckDecision(&states, rtc::Optional<int>(kEnablingBandwidthLow),
+ rtc::Optional<float>(kEnablingPacketLossAtLowBw * 0.99f),
+ false);
+}
+
+TEST(FecControllerTest, MaintainFecOffForVeryLowBandwidth) {
+ auto states = CreateFecController(false);
+ // Below |kEnablingBandwidthLow|, no packet loss fraction can cause FEC to
+ // turn on.
+ CheckDecision(&states, rtc::Optional<int>(kEnablingBandwidthLow - 1),
+ rtc::Optional<float>(1.0), false);
+}
+
+TEST(FecControllerTest, DisableFecForHighBandwidth) {
+ auto states = CreateFecController(true);
+ CheckDecision(&states, rtc::Optional<int>(kDisablingBandwidthHigh),
+ rtc::Optional<float>(kDisablingPacketLossAtHighBw), false);
+}
+
+TEST(FecControllerTest, MaintainFecOnForHighBandwidth) {
+ auto states = CreateFecController(true);
+ CheckDecision(&states, rtc::Optional<int>(kDisablingBandwidthHigh),
+ rtc::Optional<float>(kDisablingPacketLossAtHighBw * 1.01f),
+ true);
+}
+
+TEST(FecControllerTest, DisableFecOnMediumBandwidth) {
+ auto states = CreateFecController(true);
+ CheckDecision(
+ &states, rtc::Optional<int>(
+ (kDisablingBandwidthHigh + kDisablingBandwidthLow) / 2),
+ rtc::Optional<float>(
+ (kDisablingPacketLossAtLowBw + kDisablingPacketLossAtHighBw) / 2.0f),
+ false);
+}
+
+TEST(FecControllerTest, MaintainFecOnForMediumBandwidth) {
+ auto states = CreateFecController(true);
+ CheckDecision(
+ &states,
+ rtc::Optional<int>((kEnablingBandwidthHigh + kDisablingBandwidthLow) / 2),
+ rtc::Optional<float>(kDisablingPacketLossAtLowBw * 0.51f +
+ kDisablingPacketLossAtHighBw * 0.49f),
+ true);
+}
+
+TEST(FecControllerTest, DisableFecForLowBandwidth) {
+ auto states = CreateFecController(true);
+ CheckDecision(&states, rtc::Optional<int>(kDisablingBandwidthLow),
+ rtc::Optional<float>(kDisablingPacketLossAtLowBw), false);
+}
+
+TEST(FecControllerTest, DisableFecForVeryLowBandwidth) {
+ auto states = CreateFecController(true);
+ // Below |kEnablingBandwidthLow|, any packet loss fraction can cause FEC to
+ // turn off.
+ CheckDecision(&states, rtc::Optional<int>(kDisablingBandwidthLow - 1),
+ rtc::Optional<float>(1.0), false);
+}
+
+TEST(FecControllerTest, CheckBehaviorOnChangingNetworkMetrics) {
+ // In this test, we let the network metrics to traverse from 1 to 5.
+ // packet-loss ^ 1 | |
+ // | | 2|
+ // | \ \ 3
+ // | \4 \_______
+ // | \_________
+ // |---------5-------> bandwidth
+
+ auto states = CreateFecController(true);
+ CheckDecision(&states, rtc::Optional<int>(kDisablingBandwidthLow - 1),
+ rtc::Optional<float>(1.0), false);
+ CheckDecision(&states, rtc::Optional<int>(kEnablingBandwidthLow),
+ rtc::Optional<float>(kEnablingPacketLossAtLowBw * 0.99f),
+ false);
+ CheckDecision(&states, rtc::Optional<int>(kEnablingBandwidthHigh),
+ rtc::Optional<float>(kEnablingPacketLossAtHighBw), true);
+ CheckDecision(&states, rtc::Optional<int>(kDisablingBandwidthHigh),
+ rtc::Optional<float>(kDisablingPacketLossAtHighBw * 1.01f),
+ true);
+ CheckDecision(&states, rtc::Optional<int>(kDisablingBandwidthHigh + 1),
+ rtc::Optional<float>(0.0), false);
+}
+
+TEST(FecControllerTest, CheckBehaviorOnSpecialCurves) {
+ // We test a special configuration, where the points to define the FEC
+ // enabling/disabling curves are placed like the following, otherwise the test
+ // is the same as CheckBehaviorOnChangingNetworkMetrics.
+ //
+ // packet-loss ^ | |
+ // | | C|
+ // | | |
+ // | | D|_______
+ // | A|___B______
+ // |-----------------> bandwidth
+
+ constexpr int kEnablingBandwidthHigh = kEnablingBandwidthLow;
+ constexpr float kDisablingPacketLossAtLowBw = kDisablingPacketLossAtHighBw;
+ FecControllerStates states;
+ std::unique_ptr<MockSmoothingFilter> mock_smoothing_filter(
+ new NiceMock<MockSmoothingFilter>());
+ states.packet_loss_smoothed = mock_smoothing_filter.get();
+ EXPECT_CALL(*states.packet_loss_smoothed, Die());
+ using Threshold = FecController::Config::Threshold;
+ states.controller.reset(new FecController(
+ FecController::Config(
+ true, Threshold(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw,
+ kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw),
+ Threshold(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw,
+ kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw),
+ 0, nullptr),
+ std::move(mock_smoothing_filter)));
+
+ CheckDecision(&states, rtc::Optional<int>(kDisablingBandwidthLow - 1),
+ rtc::Optional<float>(1.0), false);
+ CheckDecision(&states, rtc::Optional<int>(kEnablingBandwidthLow),
+ rtc::Optional<float>(kEnablingPacketLossAtHighBw * 0.99f),
+ false);
+ CheckDecision(&states, rtc::Optional<int>(kEnablingBandwidthHigh),
+ rtc::Optional<float>(kEnablingPacketLossAtHighBw), true);
+ CheckDecision(&states, rtc::Optional<int>(kDisablingBandwidthHigh),
+ rtc::Optional<float>(kDisablingPacketLossAtHighBw * 1.01f),
+ true);
+ CheckDecision(&states, rtc::Optional<int>(kDisablingBandwidthHigh + 1),
+ rtc::Optional<float>(0.0), false);
+}
+
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+TEST(FecControllerDeathTest, InvalidConfig) {
+ FecControllerStates states;
+ std::unique_ptr<MockSmoothingFilter> mock_smoothing_filter(
+ new NiceMock<MockSmoothingFilter>());
+ states.packet_loss_smoothed = mock_smoothing_filter.get();
+ EXPECT_CALL(*states.packet_loss_smoothed, Die());
+ using Threshold = FecController::Config::Threshold;
+ EXPECT_DEATH(
+ states.controller.reset(new FecController(
+ FecController::Config(
+ true,
+ Threshold(kDisablingBandwidthLow - 1, kEnablingPacketLossAtLowBw,
+ kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw),
+ Threshold(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw,
+ kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw),
+ 0, nullptr),
+ std::move(mock_smoothing_filter))),
+ "Check failed");
+}
+#endif
+
+} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/mock/mock_smoothing_filter.h b/webrtc/modules/audio_coding/audio_network_adaptor/mock/mock_smoothing_filter.h
new file mode 100644
index 0000000..f004d1b
--- /dev/null
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/mock/mock_smoothing_filter.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 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_MOCK_MOCK_SMOOTHING_FILTER_H_
+#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_MOCK_MOCK_SMOOTHING_FILTER_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.h"
+
+namespace webrtc {
+
+class MockSmoothingFilter : public SmoothingFilter {
+ public:
+ virtual ~MockSmoothingFilter() { Die(); }
+ MOCK_METHOD0(Die, void());
+ MOCK_METHOD1(AddSample, void(float));
+ MOCK_CONST_METHOD0(GetAverage, rtc::Optional<float>());
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_MOCK_MOCK_SMOOTHING_FILTER_H_