blob: c66482f4e254d3cea2a72466d7bffc17ea153cb8 [file] [log] [blame]
/*
* 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