Moving src/webrtc into src/.

In order to eliminate the WebRTC Subtree mirror in Chromium, 
WebRTC is moving the content of the src/webrtc directory up
to the src/ directory.

NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
TBR=tommi@webrtc.org

Bug: chromium:611808
Change-Id: Iac59c5b51b950f174119565bac87955a7994bc38
Reviewed-on: https://webrtc-review.googlesource.com/1560
Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Henrik Kjellander <kjellander@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#19845}
This commit is contained in:
Mirko Bonadei
2017-09-15 06:15:48 +02:00
committed by Commit Bot
parent 6674846b4a
commit bb547203bf
4576 changed files with 1092 additions and 1196 deletions

View File

@ -0,0 +1,25 @@
/*
* 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/include/audio_network_adaptor_config.h"
namespace webrtc {
AudioEncoderRuntimeConfig::AudioEncoderRuntimeConfig() = default;
AudioEncoderRuntimeConfig::~AudioEncoderRuntimeConfig() = default;
AudioEncoderRuntimeConfig::AudioEncoderRuntimeConfig(
const AudioEncoderRuntimeConfig& other) = default;
AudioEncoderRuntimeConfig& AudioEncoderRuntimeConfig::operator=(
const AudioEncoderRuntimeConfig& other) = default;
} // namespace webrtc

View File

@ -0,0 +1,213 @@
/*
* 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/audio_network_adaptor_impl.h"
#include <utility>
#include "webrtc/rtc_base/logging.h"
#include "webrtc/rtc_base/timeutils.h"
#include "webrtc/system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
constexpr int kEventLogMinBitrateChangeBps = 5000;
constexpr float kEventLogMinBitrateChangeFraction = 0.25;
constexpr float kEventLogMinPacketLossChangeFraction = 0.5;
} // namespace
AudioNetworkAdaptorImpl::Config::Config() : event_log(nullptr){};
AudioNetworkAdaptorImpl::Config::~Config() = default;
AudioNetworkAdaptorImpl::AudioNetworkAdaptorImpl(
const Config& config,
std::unique_ptr<ControllerManager> controller_manager,
std::unique_ptr<DebugDumpWriter> debug_dump_writer)
: config_(config),
controller_manager_(std::move(controller_manager)),
debug_dump_writer_(std::move(debug_dump_writer)),
event_log_writer_(
config.event_log
? new EventLogWriter(config.event_log,
kEventLogMinBitrateChangeBps,
kEventLogMinBitrateChangeFraction,
kEventLogMinPacketLossChangeFraction)
: nullptr),
enable_bitrate_adaptation_(
webrtc::field_trial::IsEnabled("WebRTC-Audio-BitrateAdaptation")),
enable_dtx_adaptation_(
webrtc::field_trial::IsEnabled("WebRTC-Audio-DtxAdaptation")),
enable_fec_adaptation_(
webrtc::field_trial::IsEnabled("WebRTC-Audio-FecAdaptation")),
enable_channel_adaptation_(
webrtc::field_trial::IsEnabled("WebRTC-Audio-ChannelAdaptation")),
enable_frame_length_adaptation_(webrtc::field_trial::IsEnabled(
"WebRTC-Audio-FrameLengthAdaptation")) {
RTC_DCHECK(controller_manager_);
}
AudioNetworkAdaptorImpl::~AudioNetworkAdaptorImpl() = default;
void AudioNetworkAdaptorImpl::SetUplinkBandwidth(int uplink_bandwidth_bps) {
last_metrics_.uplink_bandwidth_bps = rtc::Optional<int>(uplink_bandwidth_bps);
DumpNetworkMetrics();
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_bandwidth_bps =
rtc::Optional<int>(uplink_bandwidth_bps);
UpdateNetworkMetrics(network_metrics);
}
void AudioNetworkAdaptorImpl::SetUplinkPacketLossFraction(
float uplink_packet_loss_fraction) {
last_metrics_.uplink_packet_loss_fraction =
rtc::Optional<float>(uplink_packet_loss_fraction);
DumpNetworkMetrics();
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_packet_loss_fraction =
rtc::Optional<float>(uplink_packet_loss_fraction);
UpdateNetworkMetrics(network_metrics);
}
void AudioNetworkAdaptorImpl::SetUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) {
last_metrics_.uplink_recoverable_packet_loss_fraction =
rtc::Optional<float>(uplink_recoverable_packet_loss_fraction);
DumpNetworkMetrics();
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_recoverable_packet_loss_fraction =
rtc::Optional<float>(uplink_recoverable_packet_loss_fraction);
UpdateNetworkMetrics(network_metrics);
}
void AudioNetworkAdaptorImpl::SetRtt(int rtt_ms) {
last_metrics_.rtt_ms = rtc::Optional<int>(rtt_ms);
DumpNetworkMetrics();
Controller::NetworkMetrics network_metrics;
network_metrics.rtt_ms = rtc::Optional<int>(rtt_ms);
UpdateNetworkMetrics(network_metrics);
}
void AudioNetworkAdaptorImpl::SetTargetAudioBitrate(
int target_audio_bitrate_bps) {
last_metrics_.target_audio_bitrate_bps =
rtc::Optional<int>(target_audio_bitrate_bps);
DumpNetworkMetrics();
Controller::NetworkMetrics network_metrics;
network_metrics.target_audio_bitrate_bps =
rtc::Optional<int>(target_audio_bitrate_bps);
UpdateNetworkMetrics(network_metrics);
}
void AudioNetworkAdaptorImpl::SetOverhead(size_t overhead_bytes_per_packet) {
last_metrics_.overhead_bytes_per_packet =
rtc::Optional<size_t>(overhead_bytes_per_packet);
DumpNetworkMetrics();
Controller::NetworkMetrics network_metrics;
network_metrics.overhead_bytes_per_packet =
rtc::Optional<size_t>(overhead_bytes_per_packet);
UpdateNetworkMetrics(network_metrics);
}
AudioEncoderRuntimeConfig AudioNetworkAdaptorImpl::GetEncoderRuntimeConfig() {
AudioEncoderRuntimeConfig config;
for (auto& controller :
controller_manager_->GetSortedControllers(last_metrics_))
controller->MakeDecision(&config);
// Update ANA stats.
auto increment_opt = [](rtc::Optional<uint32_t>& a) {
a = rtc::Optional<uint32_t>(a.value_or(0) + 1);
};
if (prev_config_) {
if (config.bitrate_bps != prev_config_->bitrate_bps) {
increment_opt(stats_.bitrate_action_counter);
}
if (config.enable_dtx != prev_config_->enable_dtx) {
increment_opt(stats_.dtx_action_counter);
}
if (config.enable_fec != prev_config_->enable_fec) {
increment_opt(stats_.fec_action_counter);
}
if (config.frame_length_ms && prev_config_->frame_length_ms) {
if (*config.frame_length_ms > *prev_config_->frame_length_ms) {
increment_opt(stats_.frame_length_increase_counter);
} else if (*config.frame_length_ms < *prev_config_->frame_length_ms) {
increment_opt(stats_.frame_length_decrease_counter);
}
}
if (config.num_channels != prev_config_->num_channels) {
increment_opt(stats_.channel_action_counter);
}
if (config.uplink_packet_loss_fraction) {
stats_.uplink_packet_loss_fraction =
rtc::Optional<float>(*config.uplink_packet_loss_fraction);
}
}
prev_config_ = rtc::Optional<AudioEncoderRuntimeConfig>(config);
// Prevent certain controllers from taking action (determined by field trials)
if (!enable_bitrate_adaptation_ && config.bitrate_bps) {
config.bitrate_bps.reset();
}
if (!enable_dtx_adaptation_ && config.enable_dtx) {
config.enable_dtx.reset();
}
if (!enable_fec_adaptation_ && config.enable_fec) {
config.enable_fec.reset();
config.uplink_packet_loss_fraction.reset();
}
if (!enable_frame_length_adaptation_ && config.frame_length_ms) {
config.frame_length_ms.reset();
}
if (!enable_channel_adaptation_ && config.num_channels) {
config.num_channels.reset();
}
if (debug_dump_writer_)
debug_dump_writer_->DumpEncoderRuntimeConfig(config, rtc::TimeMillis());
if (event_log_writer_)
event_log_writer_->MaybeLogEncoderConfig(config);
return config;
}
void AudioNetworkAdaptorImpl::StartDebugDump(FILE* file_handle) {
debug_dump_writer_ = DebugDumpWriter::Create(file_handle);
}
void AudioNetworkAdaptorImpl::StopDebugDump() {
debug_dump_writer_.reset(nullptr);
}
ANAStats AudioNetworkAdaptorImpl::GetStats() const {
return stats_;
}
void AudioNetworkAdaptorImpl::DumpNetworkMetrics() {
if (debug_dump_writer_)
debug_dump_writer_->DumpNetworkMetrics(last_metrics_, rtc::TimeMillis());
}
void AudioNetworkAdaptorImpl::UpdateNetworkMetrics(
const Controller::NetworkMetrics& network_metrics) {
for (auto& controller : controller_manager_->GetControllers())
controller->UpdateNetworkMetrics(network_metrics);
}
} // namespace webrtc

View File

@ -0,0 +1,93 @@
/*
* 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_AUDIO_NETWORK_ADAPTOR_IMPL_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_AUDIO_NETWORK_ADAPTOR_IMPL_H_
#include <memory>
#include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/controller_manager.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
#include "webrtc/rtc_base/constructormagic.h"
namespace webrtc {
class RtcEventLog;
class AudioNetworkAdaptorImpl final : public AudioNetworkAdaptor {
public:
struct Config {
Config();
~Config();
RtcEventLog* event_log;
};
AudioNetworkAdaptorImpl(
const Config& config,
std::unique_ptr<ControllerManager> controller_manager,
std::unique_ptr<DebugDumpWriter> debug_dump_writer = nullptr);
~AudioNetworkAdaptorImpl() override;
void SetUplinkBandwidth(int uplink_bandwidth_bps) override;
void SetUplinkPacketLossFraction(float uplink_packet_loss_fraction) override;
void SetUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) override;
void SetRtt(int rtt_ms) override;
void SetTargetAudioBitrate(int target_audio_bitrate_bps) override;
void SetOverhead(size_t overhead_bytes_per_packet) override;
AudioEncoderRuntimeConfig GetEncoderRuntimeConfig() override;
void StartDebugDump(FILE* file_handle) override;
void StopDebugDump() override;
ANAStats GetStats() const override;
private:
void DumpNetworkMetrics();
void UpdateNetworkMetrics(const Controller::NetworkMetrics& network_metrics);
const Config config_;
std::unique_ptr<ControllerManager> controller_manager_;
std::unique_ptr<DebugDumpWriter> debug_dump_writer_;
const std::unique_ptr<EventLogWriter> event_log_writer_;
Controller::NetworkMetrics last_metrics_;
rtc::Optional<AudioEncoderRuntimeConfig> prev_config_;
ANAStats stats_;
const bool enable_bitrate_adaptation_;
const bool enable_dtx_adaptation_;
const bool enable_fec_adaptation_;
const bool enable_channel_adaptation_;
const bool enable_frame_length_adaptation_;
RTC_DISALLOW_COPY_AND_ASSIGN(AudioNetworkAdaptorImpl);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_AUDIO_NETWORK_ADAPTOR_IMPL_H_

View File

@ -0,0 +1,319 @@
/*
* 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 <vector>
#include "webrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/mock/mock_controller.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/mock/mock_controller_manager.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/mock/mock_debug_dump_writer.h"
#include "webrtc/rtc_base/fakeclock.h"
#include "webrtc/test/field_trial.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SetArgPointee;
namespace {
constexpr size_t kNumControllers = 2;
constexpr int64_t kClockInitialTimeMs = 12345678;
MATCHER_P(NetworkMetricsIs, metric, "") {
return arg.uplink_bandwidth_bps == metric.uplink_bandwidth_bps &&
arg.target_audio_bitrate_bps == metric.target_audio_bitrate_bps &&
arg.rtt_ms == metric.rtt_ms &&
arg.overhead_bytes_per_packet == metric.overhead_bytes_per_packet &&
arg.uplink_packet_loss_fraction ==
metric.uplink_packet_loss_fraction &&
arg.uplink_recoverable_packet_loss_fraction ==
metric.uplink_recoverable_packet_loss_fraction;
}
MATCHER_P(EncoderRuntimeConfigIs, config, "") {
return arg.bitrate_bps == config.bitrate_bps &&
arg.frame_length_ms == config.frame_length_ms &&
arg.uplink_packet_loss_fraction ==
config.uplink_packet_loss_fraction &&
arg.enable_fec == config.enable_fec &&
arg.enable_dtx == config.enable_dtx &&
arg.num_channels == config.num_channels;
}
struct AudioNetworkAdaptorStates {
std::unique_ptr<AudioNetworkAdaptorImpl> audio_network_adaptor;
std::vector<std::unique_ptr<MockController>> mock_controllers;
std::unique_ptr<MockRtcEventLog> event_log;
MockDebugDumpWriter* mock_debug_dump_writer;
};
AudioNetworkAdaptorStates CreateAudioNetworkAdaptor() {
AudioNetworkAdaptorStates states;
std::vector<Controller*> controllers;
for (size_t i = 0; i < kNumControllers; ++i) {
auto controller =
std::unique_ptr<MockController>(new NiceMock<MockController>());
EXPECT_CALL(*controller, Die());
controllers.push_back(controller.get());
states.mock_controllers.push_back(std::move(controller));
}
auto controller_manager = std::unique_ptr<MockControllerManager>(
new NiceMock<MockControllerManager>());
EXPECT_CALL(*controller_manager, Die());
EXPECT_CALL(*controller_manager, GetControllers())
.WillRepeatedly(Return(controllers));
EXPECT_CALL(*controller_manager, GetSortedControllers(_))
.WillRepeatedly(Return(controllers));
states.event_log.reset(new NiceMock<MockRtcEventLog>());
auto debug_dump_writer =
std::unique_ptr<MockDebugDumpWriter>(new NiceMock<MockDebugDumpWriter>());
EXPECT_CALL(*debug_dump_writer, Die());
states.mock_debug_dump_writer = debug_dump_writer.get();
AudioNetworkAdaptorImpl::Config config;
config.event_log = states.event_log.get();
// AudioNetworkAdaptorImpl governs the lifetime of controller manager.
states.audio_network_adaptor.reset(new AudioNetworkAdaptorImpl(
config,
std::move(controller_manager), std::move(debug_dump_writer)));
return states;
}
void SetExpectCallToUpdateNetworkMetrics(
const std::vector<std::unique_ptr<MockController>>& controllers,
const Controller::NetworkMetrics& check) {
for (auto& mock_controller : controllers) {
EXPECT_CALL(*mock_controller,
UpdateNetworkMetrics(NetworkMetricsIs(check)));
}
}
} // namespace
TEST(AudioNetworkAdaptorImplTest,
UpdateNetworkMetricsIsCalledOnSetUplinkBandwidth) {
auto states = CreateAudioNetworkAdaptor();
constexpr int kBandwidth = 16000;
Controller::NetworkMetrics check;
check.uplink_bandwidth_bps = rtc::Optional<int>(kBandwidth);
SetExpectCallToUpdateNetworkMetrics(states.mock_controllers, check);
states.audio_network_adaptor->SetUplinkBandwidth(kBandwidth);
}
TEST(AudioNetworkAdaptorImplTest,
UpdateNetworkMetricsIsCalledOnSetUplinkPacketLossFraction) {
auto states = CreateAudioNetworkAdaptor();
constexpr float kPacketLoss = 0.7f;
Controller::NetworkMetrics check;
check.uplink_packet_loss_fraction = rtc::Optional<float>(kPacketLoss);
SetExpectCallToUpdateNetworkMetrics(states.mock_controllers, check);
states.audio_network_adaptor->SetUplinkPacketLossFraction(kPacketLoss);
}
TEST(AudioNetworkAdaptorImplTest,
UpdateNetworkMetricsIsCalledOnSetUplinkRecoverablePacketLossFraction) {
auto states = CreateAudioNetworkAdaptor();
constexpr float kRecoverablePacketLoss = 0.1f;
Controller::NetworkMetrics check;
check.uplink_recoverable_packet_loss_fraction =
rtc::Optional<float>(kRecoverablePacketLoss);
SetExpectCallToUpdateNetworkMetrics(states.mock_controllers, check);
states.audio_network_adaptor->SetUplinkRecoverablePacketLossFraction(
kRecoverablePacketLoss);
}
TEST(AudioNetworkAdaptorImplTest, UpdateNetworkMetricsIsCalledOnSetRtt) {
auto states = CreateAudioNetworkAdaptor();
constexpr int kRtt = 100;
Controller::NetworkMetrics check;
check.rtt_ms = rtc::Optional<int>(kRtt);
SetExpectCallToUpdateNetworkMetrics(states.mock_controllers, check);
states.audio_network_adaptor->SetRtt(kRtt);
}
TEST(AudioNetworkAdaptorImplTest,
UpdateNetworkMetricsIsCalledOnSetTargetAudioBitrate) {
auto states = CreateAudioNetworkAdaptor();
constexpr int kTargetAudioBitrate = 15000;
Controller::NetworkMetrics check;
check.target_audio_bitrate_bps = rtc::Optional<int>(kTargetAudioBitrate);
SetExpectCallToUpdateNetworkMetrics(states.mock_controllers, check);
states.audio_network_adaptor->SetTargetAudioBitrate(kTargetAudioBitrate);
}
TEST(AudioNetworkAdaptorImplTest, UpdateNetworkMetricsIsCalledOnSetOverhead) {
auto states = CreateAudioNetworkAdaptor();
constexpr size_t kOverhead = 64;
Controller::NetworkMetrics check;
check.overhead_bytes_per_packet = rtc::Optional<size_t>(kOverhead);
SetExpectCallToUpdateNetworkMetrics(states.mock_controllers, check);
states.audio_network_adaptor->SetOverhead(kOverhead);
}
TEST(AudioNetworkAdaptorImplTest,
MakeDecisionIsCalledOnGetEncoderRuntimeConfig) {
auto states = CreateAudioNetworkAdaptor();
for (auto& mock_controller : states.mock_controllers)
EXPECT_CALL(*mock_controller, MakeDecision(_));
states.audio_network_adaptor->GetEncoderRuntimeConfig();
}
TEST(AudioNetworkAdaptorImplTest,
DumpEncoderRuntimeConfigIsCalledOnGetEncoderRuntimeConfig) {
test::ScopedFieldTrials override_field_trials(
"WebRTC-Audio-BitrateAdaptation/Enabled/WebRTC-Audio-FecAdaptation/"
"Enabled/");
rtc::ScopedFakeClock fake_clock;
fake_clock.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kClockInitialTimeMs));
auto states = CreateAudioNetworkAdaptor();
AudioEncoderRuntimeConfig config;
config.bitrate_bps = rtc::Optional<int>(32000);
config.enable_fec = rtc::Optional<bool>(true);
EXPECT_CALL(*states.mock_controllers[0], MakeDecision(_))
.WillOnce(SetArgPointee<0>(config));
EXPECT_CALL(*states.mock_debug_dump_writer,
DumpEncoderRuntimeConfig(EncoderRuntimeConfigIs(config),
kClockInitialTimeMs));
states.audio_network_adaptor->GetEncoderRuntimeConfig();
}
TEST(AudioNetworkAdaptorImplTest,
DumpNetworkMetricsIsCalledOnSetNetworkMetrics) {
rtc::ScopedFakeClock fake_clock;
fake_clock.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kClockInitialTimeMs));
auto states = CreateAudioNetworkAdaptor();
constexpr int kBandwidth = 16000;
constexpr float kPacketLoss = 0.7f;
const auto kRecoverablePacketLoss = 0.2f;
constexpr int kRtt = 100;
constexpr int kTargetAudioBitrate = 15000;
constexpr size_t kOverhead = 64;
Controller::NetworkMetrics check;
check.uplink_bandwidth_bps = rtc::Optional<int>(kBandwidth);
int64_t timestamp_check = kClockInitialTimeMs;
EXPECT_CALL(*states.mock_debug_dump_writer,
DumpNetworkMetrics(NetworkMetricsIs(check), timestamp_check));
states.audio_network_adaptor->SetUplinkBandwidth(kBandwidth);
fake_clock.AdvanceTime(rtc::TimeDelta::FromMilliseconds(100));
timestamp_check += 100;
check.uplink_packet_loss_fraction = rtc::Optional<float>(kPacketLoss);
EXPECT_CALL(*states.mock_debug_dump_writer,
DumpNetworkMetrics(NetworkMetricsIs(check), timestamp_check));
states.audio_network_adaptor->SetUplinkPacketLossFraction(kPacketLoss);
fake_clock.AdvanceTime(rtc::TimeDelta::FromMilliseconds(50));
timestamp_check += 50;
check.uplink_recoverable_packet_loss_fraction =
rtc::Optional<float>(kRecoverablePacketLoss);
EXPECT_CALL(*states.mock_debug_dump_writer,
DumpNetworkMetrics(NetworkMetricsIs(check), timestamp_check));
states.audio_network_adaptor->SetUplinkRecoverablePacketLossFraction(
kRecoverablePacketLoss);
fake_clock.AdvanceTime(rtc::TimeDelta::FromMilliseconds(200));
timestamp_check += 200;
check.rtt_ms = rtc::Optional<int>(kRtt);
EXPECT_CALL(*states.mock_debug_dump_writer,
DumpNetworkMetrics(NetworkMetricsIs(check), timestamp_check));
states.audio_network_adaptor->SetRtt(kRtt);
fake_clock.AdvanceTime(rtc::TimeDelta::FromMilliseconds(150));
timestamp_check += 150;
check.target_audio_bitrate_bps = rtc::Optional<int>(kTargetAudioBitrate);
EXPECT_CALL(*states.mock_debug_dump_writer,
DumpNetworkMetrics(NetworkMetricsIs(check), timestamp_check));
states.audio_network_adaptor->SetTargetAudioBitrate(kTargetAudioBitrate);
fake_clock.AdvanceTime(rtc::TimeDelta::FromMilliseconds(50));
timestamp_check += 50;
check.overhead_bytes_per_packet = rtc::Optional<size_t>(kOverhead);
EXPECT_CALL(*states.mock_debug_dump_writer,
DumpNetworkMetrics(NetworkMetricsIs(check), timestamp_check));
states.audio_network_adaptor->SetOverhead(kOverhead);
}
TEST(AudioNetworkAdaptorImplTest, LogRuntimeConfigOnGetEncoderRuntimeConfig) {
test::ScopedFieldTrials override_field_trials(
"WebRTC-Audio-BitrateAdaptation/Enabled/WebRTC-Audio-FecAdaptation/"
"Enabled/");
auto states = CreateAudioNetworkAdaptor();
AudioEncoderRuntimeConfig config;
config.bitrate_bps = rtc::Optional<int>(32000);
config.enable_fec = rtc::Optional<bool>(true);
EXPECT_CALL(*states.mock_controllers[0], MakeDecision(_))
.WillOnce(SetArgPointee<0>(config));
EXPECT_CALL(*states.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(config)))
.Times(1);
states.audio_network_adaptor->GetEncoderRuntimeConfig();
}
TEST(AudioNetworkAdaptorImplTest, TestANAStats) {
auto states = CreateAudioNetworkAdaptor();
// Simulate some adaptation, otherwise the stats will not show anything.
AudioEncoderRuntimeConfig config1, config2;
config1.bitrate_bps = rtc::Optional<int>(32000);
config1.num_channels = rtc::Optional<size_t>(2);
config1.enable_fec = rtc::Optional<bool>(true);
config1.enable_dtx = rtc::Optional<bool>(true);
config1.frame_length_ms = rtc::Optional<int>(120);
config1.uplink_packet_loss_fraction = rtc::Optional<float>(0.1f);
config2.bitrate_bps = rtc::Optional<int>(16000);
config2.num_channels = rtc::Optional<size_t>(1);
config2.enable_fec = rtc::Optional<bool>(false);
config2.enable_dtx = rtc::Optional<bool>(false);
config2.frame_length_ms = rtc::Optional<int>(60);
config1.uplink_packet_loss_fraction = rtc::Optional<float>(0.1f);
EXPECT_CALL(*states.mock_controllers[0], MakeDecision(_))
.WillOnce(SetArgPointee<0>(config1));
states.audio_network_adaptor->GetEncoderRuntimeConfig();
EXPECT_CALL(*states.mock_controllers[0], MakeDecision(_))
.WillOnce(SetArgPointee<0>(config2));
states.audio_network_adaptor->GetEncoderRuntimeConfig();
EXPECT_CALL(*states.mock_controllers[0], MakeDecision(_))
.WillOnce(SetArgPointee<0>(config1));
states.audio_network_adaptor->GetEncoderRuntimeConfig();
auto ana_stats = states.audio_network_adaptor->GetStats();
EXPECT_EQ(ana_stats.bitrate_action_counter, 2);
EXPECT_EQ(ana_stats.channel_action_counter, 2);
EXPECT_EQ(ana_stats.dtx_action_counter, 2);
EXPECT_EQ(ana_stats.fec_action_counter, 2);
EXPECT_EQ(ana_stats.frame_length_increase_counter, 1);
EXPECT_EQ(ana_stats.frame_length_decrease_counter, 1);
EXPECT_EQ(ana_stats.uplink_packet_loss_fraction, 0.1f);
}
} // namespace webrtc

View File

@ -0,0 +1,66 @@
/*
* 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/bitrate_controller.h"
#include <algorithm>
#include "webrtc/rtc_base/checks.h"
#include "webrtc/system_wrappers/include/field_trial.h"
namespace webrtc {
namespace audio_network_adaptor {
BitrateController::Config::Config(int initial_bitrate_bps,
int initial_frame_length_ms)
: initial_bitrate_bps(initial_bitrate_bps),
initial_frame_length_ms(initial_frame_length_ms) {}
BitrateController::Config::~Config() = default;
BitrateController::BitrateController(const Config& config)
: config_(config),
bitrate_bps_(config_.initial_bitrate_bps),
frame_length_ms_(config_.initial_frame_length_ms) {
RTC_DCHECK_GT(bitrate_bps_, 0);
RTC_DCHECK_GT(frame_length_ms_, 0);
}
BitrateController::~BitrateController() = default;
void BitrateController::UpdateNetworkMetrics(
const NetworkMetrics& network_metrics) {
if (network_metrics.target_audio_bitrate_bps)
target_audio_bitrate_bps_ = network_metrics.target_audio_bitrate_bps;
if (network_metrics.overhead_bytes_per_packet)
overhead_bytes_per_packet_ = network_metrics.overhead_bytes_per_packet;
}
void BitrateController::MakeDecision(AudioEncoderRuntimeConfig* config) {
// Decision on |bitrate_bps| should not have been made.
RTC_DCHECK(!config->bitrate_bps);
if (target_audio_bitrate_bps_ && overhead_bytes_per_packet_) {
// Current implementation of BitrateController can only work when
// |metrics.target_audio_bitrate_bps| includes overhead is enabled. This is
// currently governed by the following field trial.
RTC_DCHECK(
webrtc::field_trial::IsEnabled("WebRTC-SendSideBwe-WithOverhead"));
if (config->frame_length_ms)
frame_length_ms_ = *config->frame_length_ms;
int overhead_rate_bps =
static_cast<int>(*overhead_bytes_per_packet_ * 8 * 1000 /
frame_length_ms_);
bitrate_bps_ = std::max(0, *target_audio_bitrate_bps_ - overhead_rate_bps);
}
config->bitrate_bps = rtc::Optional<int>(bitrate_bps_);
}
} // namespace audio_network_adaptor
} // namespace webrtc

View File

@ -0,0 +1,49 @@
/*
* 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_BITRATE_CONTROLLER_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_BITRATE_CONTROLLER_H_
#include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h"
#include "webrtc/rtc_base/constructormagic.h"
namespace webrtc {
namespace audio_network_adaptor {
class BitrateController final : public Controller {
public:
struct Config {
Config(int initial_bitrate_bps, int initial_frame_length_ms);
~Config();
int initial_bitrate_bps;
int initial_frame_length_ms;
};
explicit BitrateController(const Config& config);
~BitrateController() override;
void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override;
void MakeDecision(AudioEncoderRuntimeConfig* config) override;
private:
const Config config_;
int bitrate_bps_;
int frame_length_ms_;
rtc::Optional<int> target_audio_bitrate_bps_;
rtc::Optional<size_t> overhead_bytes_per_packet_;
RTC_DISALLOW_COPY_AND_ASSIGN(BitrateController);
};
} // namespace audio_network_adaptor
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_BITRATE_CONTROLLER_H_

View File

@ -0,0 +1,272 @@
/*
* 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/bitrate_controller.h"
#include "webrtc/test/field_trial.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
namespace audio_network_adaptor {
namespace {
void UpdateNetworkMetrics(
BitrateController* controller,
const rtc::Optional<int>& target_audio_bitrate_bps,
const rtc::Optional<size_t>& overhead_bytes_per_packet) {
// UpdateNetworkMetrics can accept multiple network metric updates at once.
// However, currently, the most used case is to update one metric at a time.
// To reflect this fact, we separate the calls.
if (target_audio_bitrate_bps) {
Controller::NetworkMetrics network_metrics;
network_metrics.target_audio_bitrate_bps = target_audio_bitrate_bps;
controller->UpdateNetworkMetrics(network_metrics);
}
if (overhead_bytes_per_packet) {
Controller::NetworkMetrics network_metrics;
network_metrics.overhead_bytes_per_packet = overhead_bytes_per_packet;
controller->UpdateNetworkMetrics(network_metrics);
}
}
void CheckDecision(BitrateController* controller,
const rtc::Optional<int>& frame_length_ms,
int expected_bitrate_bps) {
AudioEncoderRuntimeConfig config;
config.frame_length_ms = frame_length_ms;
controller->MakeDecision(&config);
EXPECT_EQ(rtc::Optional<int>(expected_bitrate_bps), config.bitrate_bps);
}
} // namespace
// These tests are named AnaBitrateControllerTest to distinguish from
// BitrateControllerTest in
// modules/bitrate_controller/bitrate_controller_unittest.cc.
TEST(AnaBitrateControllerTest, OutputInitValueWhenTargetBitrateUnknown) {
constexpr int kInitialBitrateBps = 32000;
constexpr int kInitialFrameLengthMs = 20;
constexpr size_t kOverheadBytesPerPacket = 64;
BitrateController controller(
BitrateController::Config(kInitialBitrateBps, kInitialFrameLengthMs));
UpdateNetworkMetrics(&controller, rtc::Optional<int>(),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(&controller, rtc::Optional<int>(kInitialFrameLengthMs * 2),
kInitialBitrateBps);
}
TEST(AnaBitrateControllerTest, OutputInitValueWhenOverheadUnknown) {
constexpr int kInitialBitrateBps = 32000;
constexpr int kInitialFrameLengthMs = 20;
constexpr int kTargetBitrateBps = 48000;
BitrateController controller(
BitrateController::Config(kInitialBitrateBps, kInitialFrameLengthMs));
UpdateNetworkMetrics(&controller, rtc::Optional<int>(kTargetBitrateBps),
rtc::Optional<size_t>());
CheckDecision(&controller, rtc::Optional<int>(kInitialFrameLengthMs * 2),
kInitialBitrateBps);
}
TEST(AnaBitrateControllerTest, ChangeBitrateOnTargetBitrateChanged) {
test::ScopedFieldTrials override_field_trials(
"WebRTC-SendSideBwe-WithOverhead/Enabled/");
constexpr int kInitialFrameLengthMs = 20;
BitrateController controller(
BitrateController::Config(32000, kInitialFrameLengthMs));
constexpr int kTargetBitrateBps = 48000;
constexpr size_t kOverheadBytesPerPacket = 64;
constexpr int kBitrateBps =
kTargetBitrateBps -
kOverheadBytesPerPacket * 8 * 1000 / kInitialFrameLengthMs;
// Frame length unchanged, bitrate changes in accordance with
// |metrics.target_audio_bitrate_bps| and |metrics.overhead_bytes_per_packet|.
UpdateNetworkMetrics(&controller, rtc::Optional<int>(kTargetBitrateBps),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(&controller, rtc::Optional<int>(kInitialFrameLengthMs),
kBitrateBps);
}
TEST(AnaBitrateControllerTest, UpdateMultipleNetworkMetricsAtOnce) {
// This test is similar to ChangeBitrateOnTargetBitrateChanged. But instead of
// using ::UpdateNetworkMetrics(...), which calls
// BitrateController::UpdateNetworkMetrics(...) multiple times, we
// we call it only once. This is to verify that
// BitrateController::UpdateNetworkMetrics(...) can handle multiple
// network updates at once. This is, however, not a common use case in current
// audio_network_adaptor_impl.cc.
test::ScopedFieldTrials override_field_trials(
"WebRTC-SendSideBwe-WithOverhead/Enabled/");
constexpr int kInitialFrameLengthMs = 20;
BitrateController controller(
BitrateController::Config(32000, kInitialFrameLengthMs));
constexpr int kTargetBitrateBps = 48000;
constexpr size_t kOverheadBytesPerPacket = 64;
constexpr int kBitrateBps =
kTargetBitrateBps -
kOverheadBytesPerPacket * 8 * 1000 / kInitialFrameLengthMs;
Controller::NetworkMetrics network_metrics;
network_metrics.target_audio_bitrate_bps =
rtc::Optional<int>(kTargetBitrateBps);
network_metrics.overhead_bytes_per_packet =
rtc::Optional<size_t>(kOverheadBytesPerPacket);
controller.UpdateNetworkMetrics(network_metrics);
CheckDecision(&controller, rtc::Optional<int>(kInitialFrameLengthMs),
kBitrateBps);
}
TEST(AnaBitrateControllerTest, TreatUnknownFrameLengthAsFrameLengthUnchanged) {
test::ScopedFieldTrials override_field_trials(
"WebRTC-SendSideBwe-WithOverhead/Enabled/");
constexpr int kInitialFrameLengthMs = 20;
BitrateController controller(
BitrateController::Config(32000, kInitialFrameLengthMs));
constexpr int kTargetBitrateBps = 48000;
constexpr size_t kOverheadBytesPerPacket = 64;
constexpr int kBitrateBps =
kTargetBitrateBps -
kOverheadBytesPerPacket * 8 * 1000 / kInitialFrameLengthMs;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(kTargetBitrateBps),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(&controller, rtc::Optional<int>(), kBitrateBps);
}
TEST(AnaBitrateControllerTest, IncreaseBitrateOnFrameLengthIncreased) {
test::ScopedFieldTrials override_field_trials(
"WebRTC-SendSideBwe-WithOverhead/Enabled/");
constexpr int kInitialFrameLengthMs = 20;
BitrateController controller(
BitrateController::Config(32000, kInitialFrameLengthMs));
constexpr int kTargetBitrateBps = 48000;
constexpr size_t kOverheadBytesPerPacket = 64;
constexpr int kBitrateBps =
kTargetBitrateBps -
kOverheadBytesPerPacket * 8 * 1000 / kInitialFrameLengthMs;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(kTargetBitrateBps),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(&controller, rtc::Optional<int>(), kBitrateBps);
constexpr int kFrameLengthMs = 60;
constexpr size_t kPacketOverheadRateDiff =
kOverheadBytesPerPacket * 8 * 1000 / 20 -
kOverheadBytesPerPacket * 8 * 1000 / 60;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(kTargetBitrateBps),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(&controller, rtc::Optional<int>(kFrameLengthMs),
kBitrateBps + kPacketOverheadRateDiff);
}
TEST(AnaBitrateControllerTest, DecreaseBitrateOnFrameLengthDecreased) {
test::ScopedFieldTrials override_field_trials(
"WebRTC-SendSideBwe-WithOverhead/Enabled/");
constexpr int kInitialFrameLengthMs = 60;
BitrateController controller(
BitrateController::Config(32000, kInitialFrameLengthMs));
constexpr int kTargetBitrateBps = 48000;
constexpr size_t kOverheadBytesPerPacket = 64;
constexpr int kBitrateBps =
kTargetBitrateBps -
kOverheadBytesPerPacket * 8 * 1000 / kInitialFrameLengthMs;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(kTargetBitrateBps),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(&controller, rtc::Optional<int>(), kBitrateBps);
constexpr int kFrameLengthMs = 20;
constexpr size_t kPacketOverheadRateDiff =
kOverheadBytesPerPacket * 8 * 1000 / 20 -
kOverheadBytesPerPacket * 8 * 1000 / 60;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(kTargetBitrateBps),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(&controller, rtc::Optional<int>(kFrameLengthMs),
kBitrateBps - kPacketOverheadRateDiff);
}
TEST(AnaBitrateControllerTest, BitrateNeverBecomesNegative) {
test::ScopedFieldTrials override_field_trials(
"WebRTC-SendSideBwe-WithOverhead/Enabled/");
BitrateController controller(BitrateController::Config(32000, 20));
constexpr size_t kOverheadBytesPerPacket = 64;
constexpr int kFrameLengthMs = 60;
// Set a target rate smaller than overhead rate, the bitrate is bounded by 0.
constexpr int kTargetBitrateBps =
kOverheadBytesPerPacket * 8 * 1000 / kFrameLengthMs - 1;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(kTargetBitrateBps),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(&controller, rtc::Optional<int>(kFrameLengthMs), 0);
}
TEST(AnaBitrateControllerTest, CheckBehaviorOnChangingCondition) {
test::ScopedFieldTrials override_field_trials(
"WebRTC-SendSideBwe-WithOverhead/Enabled/");
BitrateController controller(BitrateController::Config(32000, 20));
// Start from an arbitrary overall bitrate.
int overall_bitrate = 34567;
size_t overhead_bytes_per_packet = 64;
int frame_length_ms = 20;
int current_bitrate =
overall_bitrate - overhead_bytes_per_packet * 8 * 1000 / frame_length_ms;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(overall_bitrate),
rtc::Optional<size_t>(overhead_bytes_per_packet));
CheckDecision(&controller, rtc::Optional<int>(frame_length_ms),
current_bitrate);
// Next: increase overall bitrate.
overall_bitrate += 100;
current_bitrate += 100;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(overall_bitrate),
rtc::Optional<size_t>(overhead_bytes_per_packet));
CheckDecision(&controller, rtc::Optional<int>(frame_length_ms),
current_bitrate);
// Next: change frame length.
frame_length_ms = 60;
current_bitrate += overhead_bytes_per_packet * 8 * 1000 / 20 -
overhead_bytes_per_packet * 8 * 1000 / 60;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(overall_bitrate),
rtc::Optional<size_t>(overhead_bytes_per_packet));
CheckDecision(&controller, rtc::Optional<int>(frame_length_ms),
current_bitrate);
// Next: change overhead.
overhead_bytes_per_packet -= 30;
current_bitrate += 30 * 8 * 1000 / frame_length_ms;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(overall_bitrate),
rtc::Optional<size_t>(overhead_bytes_per_packet));
CheckDecision(&controller, rtc::Optional<int>(frame_length_ms),
current_bitrate);
// Next: change frame length.
frame_length_ms = 20;
current_bitrate -= overhead_bytes_per_packet * 8 * 1000 / 20 -
overhead_bytes_per_packet * 8 * 1000 / 60;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(overall_bitrate),
rtc::Optional<size_t>(overhead_bytes_per_packet));
CheckDecision(&controller, rtc::Optional<int>(frame_length_ms),
current_bitrate);
// Next: decrease overall bitrate and frame length.
overall_bitrate -= 100;
current_bitrate -= 100;
frame_length_ms = 60;
current_bitrate += overhead_bytes_per_packet * 8 * 1000 / 20 -
overhead_bytes_per_packet * 8 * 1000 / 60;
UpdateNetworkMetrics(&controller, rtc::Optional<int>(overall_bitrate),
rtc::Optional<size_t>(overhead_bytes_per_packet));
CheckDecision(&controller, rtc::Optional<int>(frame_length_ms),
current_bitrate);
}
} // namespace audio_network_adaptor
} // namespace webrtc

View File

@ -0,0 +1,61 @@
/*
* 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 <algorithm>
#include "webrtc/modules/audio_coding/audio_network_adaptor/channel_controller.h"
#include "webrtc/rtc_base/checks.h"
namespace webrtc {
ChannelController::Config::Config(size_t num_encoder_channels,
size_t intial_channels_to_encode,
int channel_1_to_2_bandwidth_bps,
int channel_2_to_1_bandwidth_bps)
: num_encoder_channels(num_encoder_channels),
intial_channels_to_encode(intial_channels_to_encode),
channel_1_to_2_bandwidth_bps(channel_1_to_2_bandwidth_bps),
channel_2_to_1_bandwidth_bps(channel_2_to_1_bandwidth_bps) {}
ChannelController::ChannelController(const Config& config)
: config_(config), channels_to_encode_(config_.intial_channels_to_encode) {
RTC_DCHECK_GT(config_.intial_channels_to_encode, 0lu);
// Currently, we require |intial_channels_to_encode| to be <= 2.
RTC_DCHECK_LE(config_.intial_channels_to_encode, 2lu);
RTC_DCHECK_GE(config_.num_encoder_channels,
config_.intial_channels_to_encode);
}
ChannelController::~ChannelController() = default;
void ChannelController::UpdateNetworkMetrics(
const NetworkMetrics& network_metrics) {
if (network_metrics.uplink_bandwidth_bps)
uplink_bandwidth_bps_ = network_metrics.uplink_bandwidth_bps;
}
void ChannelController::MakeDecision(AudioEncoderRuntimeConfig* config) {
// Decision on |num_channels| should not have been made.
RTC_DCHECK(!config->num_channels);
if (uplink_bandwidth_bps_) {
if (channels_to_encode_ == 2 &&
*uplink_bandwidth_bps_ <= config_.channel_2_to_1_bandwidth_bps) {
channels_to_encode_ = 1;
} else if (channels_to_encode_ == 1 &&
*uplink_bandwidth_bps_ >= config_.channel_1_to_2_bandwidth_bps) {
channels_to_encode_ =
std::min(static_cast<size_t>(2), config_.num_encoder_channels);
}
}
config->num_channels = rtc::Optional<size_t>(channels_to_encode_);
}
} // namespace webrtc

View File

@ -0,0 +1,53 @@
/*
* 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_CHANNEL_CONTROLLER_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_CHANNEL_CONTROLLER_H_
#include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h"
#include "webrtc/rtc_base/constructormagic.h"
namespace webrtc {
class ChannelController final : public Controller {
public:
struct Config {
Config(size_t num_encoder_channels,
size_t intial_channels_to_encode,
int channel_1_to_2_bandwidth_bps,
int channel_2_to_1_bandwidth_bps);
size_t num_encoder_channels;
size_t intial_channels_to_encode;
// Uplink bandwidth above which the number of encoded channels should switch
// from 1 to 2.
int channel_1_to_2_bandwidth_bps;
// Uplink bandwidth below which the number of encoded channels should switch
// from 2 to 1.
int channel_2_to_1_bandwidth_bps;
};
explicit ChannelController(const Config& config);
~ChannelController() override;
void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override;
void MakeDecision(AudioEncoderRuntimeConfig* config) override;
private:
const Config config_;
size_t channels_to_encode_;
rtc::Optional<int> uplink_bandwidth_bps_;
RTC_DISALLOW_COPY_AND_ASSIGN(ChannelController);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_CHANNEL_CONTROLLER_H_

View File

@ -0,0 +1,104 @@
/*
* 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 <memory>
#include "webrtc/modules/audio_coding/audio_network_adaptor/channel_controller.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
namespace {
constexpr int kNumChannels = 2;
constexpr int kChannel1To2BandwidthBps = 31000;
constexpr int kChannel2To1BandwidthBps = 29000;
constexpr int kMediumBandwidthBps =
(kChannel1To2BandwidthBps + kChannel2To1BandwidthBps) / 2;
std::unique_ptr<ChannelController> CreateChannelController(int init_channels) {
std::unique_ptr<ChannelController> controller(
new ChannelController(ChannelController::Config(
kNumChannels, init_channels, kChannel1To2BandwidthBps,
kChannel2To1BandwidthBps)));
return controller;
}
void CheckDecision(ChannelController* controller,
const rtc::Optional<int>& uplink_bandwidth_bps,
size_t expected_num_channels) {
if (uplink_bandwidth_bps) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_bandwidth_bps = uplink_bandwidth_bps;
controller->UpdateNetworkMetrics(network_metrics);
}
AudioEncoderRuntimeConfig config;
controller->MakeDecision(&config);
EXPECT_EQ(rtc::Optional<size_t>(expected_num_channels), config.num_channels);
}
} // namespace
TEST(ChannelControllerTest, OutputInitValueWhenUplinkBandwidthUnknown) {
constexpr int kInitChannels = 2;
auto controller = CreateChannelController(kInitChannels);
CheckDecision(controller.get(), rtc::Optional<int>(), kInitChannels);
}
TEST(ChannelControllerTest, SwitchTo2ChannelsOnHighUplinkBandwidth) {
constexpr int kInitChannels = 1;
auto controller = CreateChannelController(kInitChannels);
// Use high bandwidth to check output switch to 2.
CheckDecision(controller.get(), rtc::Optional<int>(kChannel1To2BandwidthBps),
2);
}
TEST(ChannelControllerTest, SwitchTo1ChannelOnLowUplinkBandwidth) {
constexpr int kInitChannels = 2;
auto controller = CreateChannelController(kInitChannels);
// Use low bandwidth to check output switch to 1.
CheckDecision(controller.get(), rtc::Optional<int>(kChannel2To1BandwidthBps),
1);
}
TEST(ChannelControllerTest, Maintain1ChannelOnMediumUplinkBandwidth) {
constexpr int kInitChannels = 1;
auto controller = CreateChannelController(kInitChannels);
// Use between-thresholds bandwidth to check output remains at 1.
CheckDecision(controller.get(), rtc::Optional<int>(kMediumBandwidthBps), 1);
}
TEST(ChannelControllerTest, Maintain2ChannelsOnMediumUplinkBandwidth) {
constexpr int kInitChannels = 2;
auto controller = CreateChannelController(kInitChannels);
// Use between-thresholds bandwidth to check output remains at 2.
CheckDecision(controller.get(), rtc::Optional<int>(kMediumBandwidthBps), 2);
}
TEST(ChannelControllerTest, CheckBehaviorOnChangingUplinkBandwidth) {
constexpr int kInitChannels = 1;
auto controller = CreateChannelController(kInitChannels);
// Use between-thresholds bandwidth to check output remains at 1.
CheckDecision(controller.get(), rtc::Optional<int>(kMediumBandwidthBps), 1);
// Use high bandwidth to check output switch to 2.
CheckDecision(controller.get(), rtc::Optional<int>(kChannel1To2BandwidthBps),
2);
// Use between-thresholds bandwidth to check output remains at 2.
CheckDecision(controller.get(), rtc::Optional<int>(kMediumBandwidthBps), 2);
// Use low bandwidth to check output switch to 1.
CheckDecision(controller.get(), rtc::Optional<int>(kChannel2To1BandwidthBps),
1);
}
} // namespace webrtc

View File

@ -0,0 +1,153 @@
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
option java_package = "org.webrtc.AudioNetworkAdaptor";
option java_outer_classname = "Config";
package webrtc.audio_network_adaptor.config;
message FecController {
message 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
optional int32 low_bandwidth_bps = 1;
optional float low_bandwidth_packet_loss = 2;
optional int32 high_bandwidth_bps = 3;
optional float high_bandwidth_packet_loss = 4;
}
// |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
optional Threshold fec_enabling_threshold = 1;
optional Threshold fec_disabling_threshold = 2;
// |time_constant_ms| is the time constant for an exponential filter, which
// is used for smoothing the packet loss fraction.
optional int32 time_constant_ms = 3;
}
message FecControllerRplrBased {
message Threshold {
// Threshold defines a curve in the bandwidth/recoverable-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
optional int32 low_bandwidth_bps = 1;
optional float low_bandwidth_recoverable_packet_loss = 2;
optional int32 high_bandwidth_bps = 3;
optional float high_bandwidth_recoverable_packet_loss = 4;
}
// |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
optional Threshold fec_enabling_threshold = 1;
optional Threshold fec_disabling_threshold = 2;
}
message FrameLengthController {
// Uplink packet loss fraction below which frame length can increase.
optional float fl_increasing_packet_loss_fraction = 1;
// Uplink packet loss fraction above which frame length should decrease.
optional float fl_decreasing_packet_loss_fraction = 2;
// Uplink bandwidth below which frame length can switch from 20ms to 60ms.
optional int32 fl_20ms_to_60ms_bandwidth_bps = 3;
// Uplink bandwidth above which frame length should switch from 60ms to 20ms.
optional int32 fl_60ms_to_20ms_bandwidth_bps = 4;
// Uplink bandwidth below which frame length can switch from 60ms to 120ms.
optional int32 fl_60ms_to_120ms_bandwidth_bps = 5;
// Uplink bandwidth above which frame length should switch from 120ms to 60ms.
optional int32 fl_120ms_to_60ms_bandwidth_bps = 6;
}
message ChannelController {
// Uplink bandwidth above which the number of encoded channels should switch
// from 1 to 2.
optional int32 channel_1_to_2_bandwidth_bps = 1;
// Uplink bandwidth below which the number of encoded channels should switch
// from 2 to 1.
optional int32 channel_2_to_1_bandwidth_bps = 2;
}
message DtxController {
// Uplink bandwidth below which DTX should be switched on.
optional int32 dtx_enabling_bandwidth_bps = 1;
// Uplink bandwidth above which DTX should be switched off.
optional int32 dtx_disabling_bandwidth_bps = 2;
}
message BitrateController {}
message Controller {
message ScoringPoint {
// |ScoringPoint| is a subspace of network condition. It is used for
// comparing the significance of controllers.
optional int32 uplink_bandwidth_bps = 1;
optional float uplink_packet_loss_fraction = 2;
}
// The distance from |scoring_point| to a given network condition defines
// the significance of this controller with respect that network condition.
// Shorter distance means higher significance. The significances of
// controllers determine their order in the processing pipeline. Controllers
// without |scoring_point| follow their default order in
// |ControllerManager::controllers|.
optional ScoringPoint scoring_point = 1;
oneof controller {
FecController fec_controller = 21;
FrameLengthController frame_length_controller = 22;
ChannelController channel_controller = 23;
DtxController dtx_controller = 24;
BitrateController bitrate_controller = 25;
FecControllerRplrBased fec_controller_rplr_based = 26;
}
}
message ControllerManager {
repeated Controller controllers = 1;
// Least time since last reordering for a new reordering to be made.
optional int32 min_reordering_time_ms = 2;
// Least squared distance from last scoring point for a new reordering to be
// made.
optional float min_reordering_squared_distance = 3;
}

View File

@ -0,0 +1,19 @@
/*
* 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/controller.h"
namespace webrtc {
Controller::NetworkMetrics::NetworkMetrics() = default;
Controller::NetworkMetrics::~NetworkMetrics() = default;
} // namespace webrtc

View File

@ -0,0 +1,43 @@
/*
* 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_CONTROLLER_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_CONTROLLER_H_
#include "webrtc/api/optional.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
namespace webrtc {
class Controller {
public:
struct NetworkMetrics {
NetworkMetrics();
~NetworkMetrics();
rtc::Optional<int> uplink_bandwidth_bps;
rtc::Optional<float> uplink_packet_loss_fraction;
rtc::Optional<float> uplink_recoverable_packet_loss_fraction;
rtc::Optional<int> target_audio_bitrate_bps;
rtc::Optional<int> rtt_ms;
rtc::Optional<size_t> overhead_bytes_per_packet;
};
virtual ~Controller() = default;
// Informs network metrics update to this controller. Any non-empty field
// indicates an update on the corresponding network metric.
virtual void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) = 0;
virtual void MakeDecision(AudioEncoderRuntimeConfig* config) = 0;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_CONTROLLER_H_

View File

@ -0,0 +1,420 @@
/*
* 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/controller_manager.h"
#include <cmath>
#include <utility>
#include "webrtc/modules/audio_coding/audio_network_adaptor/bitrate_controller.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/channel_controller.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/dtx_controller.h"
#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/rtc_base/ignore_wundef.h"
#include "webrtc/rtc_base/timeutils.h"
#if WEBRTC_ENABLE_PROTOBUF
RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h"
#else
#include "webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h"
#endif
RTC_POP_IGNORING_WUNDEF()
#endif
namespace webrtc {
namespace {
#if WEBRTC_ENABLE_PROTOBUF
std::unique_ptr<FecControllerPlrBased> CreateFecControllerPlrBased(
const audio_network_adaptor::config::FecController& config,
bool initial_fec_enabled) {
RTC_CHECK(config.has_fec_enabling_threshold());
RTC_CHECK(config.has_fec_disabling_threshold());
RTC_CHECK(config.has_time_constant_ms());
auto& fec_enabling_threshold = config.fec_enabling_threshold();
RTC_CHECK(fec_enabling_threshold.has_low_bandwidth_bps());
RTC_CHECK(fec_enabling_threshold.has_low_bandwidth_packet_loss());
RTC_CHECK(fec_enabling_threshold.has_high_bandwidth_bps());
RTC_CHECK(fec_enabling_threshold.has_high_bandwidth_packet_loss());
auto& fec_disabling_threshold = config.fec_disabling_threshold();
RTC_CHECK(fec_disabling_threshold.has_low_bandwidth_bps());
RTC_CHECK(fec_disabling_threshold.has_low_bandwidth_packet_loss());
RTC_CHECK(fec_disabling_threshold.has_high_bandwidth_bps());
RTC_CHECK(fec_disabling_threshold.has_high_bandwidth_packet_loss());
return std::unique_ptr<FecControllerPlrBased>(
new FecControllerPlrBased(FecControllerPlrBased::Config(
initial_fec_enabled,
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())));
}
std::unique_ptr<FecControllerRplrBased> CreateFecControllerRplrBased(
const audio_network_adaptor::config::FecControllerRplrBased& config,
bool initial_fec_enabled) {
RTC_CHECK(config.has_fec_enabling_threshold());
RTC_CHECK(config.has_fec_disabling_threshold());
auto& fec_enabling_threshold = config.fec_enabling_threshold();
RTC_CHECK(fec_enabling_threshold.has_low_bandwidth_bps());
RTC_CHECK(fec_enabling_threshold.has_low_bandwidth_recoverable_packet_loss());
RTC_CHECK(fec_enabling_threshold.has_high_bandwidth_bps());
RTC_CHECK(
fec_enabling_threshold.has_high_bandwidth_recoverable_packet_loss());
auto& fec_disabling_threshold = config.fec_disabling_threshold();
RTC_CHECK(fec_disabling_threshold.has_low_bandwidth_bps());
RTC_CHECK(
fec_disabling_threshold.has_low_bandwidth_recoverable_packet_loss());
RTC_CHECK(fec_disabling_threshold.has_high_bandwidth_bps());
RTC_CHECK(
fec_disabling_threshold.has_high_bandwidth_recoverable_packet_loss());
return std::unique_ptr<FecControllerRplrBased>(
new FecControllerRplrBased(FecControllerRplrBased::Config(
initial_fec_enabled,
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()),
ThresholdCurve(
fec_disabling_threshold.low_bandwidth_bps(),
fec_disabling_threshold.low_bandwidth_recoverable_packet_loss(),
fec_disabling_threshold.high_bandwidth_bps(),
fec_disabling_threshold
.high_bandwidth_recoverable_packet_loss()))));
}
std::unique_ptr<FrameLengthController> CreateFrameLengthController(
const audio_network_adaptor::config::FrameLengthController& config,
rtc::ArrayView<const int> encoder_frame_lengths_ms,
int initial_frame_length_ms,
int min_encoder_bitrate_bps) {
RTC_CHECK(config.has_fl_increasing_packet_loss_fraction());
RTC_CHECK(config.has_fl_decreasing_packet_loss_fraction());
RTC_CHECK(config.has_fl_20ms_to_60ms_bandwidth_bps());
RTC_CHECK(config.has_fl_60ms_to_20ms_bandwidth_bps());
std::map<FrameLengthController::Config::FrameLengthChange, int>
fl_changing_bandwidths_bps = {
{FrameLengthController::Config::FrameLengthChange(20, 60),
config.fl_20ms_to_60ms_bandwidth_bps()},
{FrameLengthController::Config::FrameLengthChange(60, 20),
config.fl_60ms_to_20ms_bandwidth_bps()}};
if (config.has_fl_60ms_to_120ms_bandwidth_bps() &&
config.has_fl_120ms_to_60ms_bandwidth_bps()) {
fl_changing_bandwidths_bps.insert(std::make_pair(
FrameLengthController::Config::FrameLengthChange(60, 120),
config.fl_60ms_to_120ms_bandwidth_bps()));
fl_changing_bandwidths_bps.insert(std::make_pair(
FrameLengthController::Config::FrameLengthChange(120, 60),
config.fl_120ms_to_60ms_bandwidth_bps()));
}
FrameLengthController::Config ctor_config(
std::vector<int>(), initial_frame_length_ms, min_encoder_bitrate_bps,
config.fl_increasing_packet_loss_fraction(),
config.fl_decreasing_packet_loss_fraction(),
std::move(fl_changing_bandwidths_bps));
for (auto frame_length : encoder_frame_lengths_ms)
ctor_config.encoder_frame_lengths_ms.push_back(frame_length);
return std::unique_ptr<FrameLengthController>(
new FrameLengthController(ctor_config));
}
std::unique_ptr<ChannelController> CreateChannelController(
const audio_network_adaptor::config::ChannelController& config,
size_t num_encoder_channels,
size_t intial_channels_to_encode) {
RTC_CHECK(config.has_channel_1_to_2_bandwidth_bps());
RTC_CHECK(config.has_channel_2_to_1_bandwidth_bps());
return std::unique_ptr<ChannelController>(new ChannelController(
ChannelController::Config(num_encoder_channels, intial_channels_to_encode,
config.channel_1_to_2_bandwidth_bps(),
config.channel_2_to_1_bandwidth_bps())));
}
std::unique_ptr<DtxController> CreateDtxController(
const audio_network_adaptor::config::DtxController& dtx_config,
bool initial_dtx_enabled) {
RTC_CHECK(dtx_config.has_dtx_enabling_bandwidth_bps());
RTC_CHECK(dtx_config.has_dtx_disabling_bandwidth_bps());
return std::unique_ptr<DtxController>(new DtxController(DtxController::Config(
initial_dtx_enabled, dtx_config.dtx_enabling_bandwidth_bps(),
dtx_config.dtx_disabling_bandwidth_bps())));
}
using audio_network_adaptor::BitrateController;
std::unique_ptr<BitrateController> CreateBitrateController(
int initial_bitrate_bps,
int initial_frame_length_ms) {
return std::unique_ptr<BitrateController>(new BitrateController(
BitrateController::Config(initial_bitrate_bps, initial_frame_length_ms)));
}
#endif // WEBRTC_ENABLE_PROTOBUF
} // namespace
ControllerManagerImpl::Config::Config(int min_reordering_time_ms,
float min_reordering_squared_distance)
: min_reordering_time_ms(min_reordering_time_ms),
min_reordering_squared_distance(min_reordering_squared_distance) {}
ControllerManagerImpl::Config::~Config() = default;
std::unique_ptr<ControllerManager> ControllerManagerImpl::Create(
const ProtoString& config_string,
size_t num_encoder_channels,
rtc::ArrayView<const int> encoder_frame_lengths_ms,
int min_encoder_bitrate_bps,
size_t intial_channels_to_encode,
int initial_frame_length_ms,
int initial_bitrate_bps,
bool initial_fec_enabled,
bool initial_dtx_enabled) {
return Create(config_string, num_encoder_channels, encoder_frame_lengths_ms,
min_encoder_bitrate_bps, intial_channels_to_encode,
initial_frame_length_ms, initial_bitrate_bps,
initial_fec_enabled, initial_dtx_enabled, nullptr);
}
std::unique_ptr<ControllerManager> ControllerManagerImpl::Create(
const ProtoString& config_string,
size_t num_encoder_channels,
rtc::ArrayView<const int> encoder_frame_lengths_ms,
int min_encoder_bitrate_bps,
size_t intial_channels_to_encode,
int initial_frame_length_ms,
int initial_bitrate_bps,
bool initial_fec_enabled,
bool initial_dtx_enabled,
DebugDumpWriter* debug_dump_writer) {
#if WEBRTC_ENABLE_PROTOBUF
audio_network_adaptor::config::ControllerManager controller_manager_config;
RTC_CHECK(controller_manager_config.ParseFromString(config_string));
if (debug_dump_writer)
debug_dump_writer->DumpControllerManagerConfig(controller_manager_config,
rtc::TimeMillis());
std::vector<std::unique_ptr<Controller>> controllers;
std::map<const Controller*, std::pair<int, float>> scoring_points;
for (int i = 0; i < controller_manager_config.controllers_size(); ++i) {
auto& controller_config = controller_manager_config.controllers(i);
std::unique_ptr<Controller> controller;
switch (controller_config.controller_case()) {
case audio_network_adaptor::config::Controller::kFecController:
controller = CreateFecControllerPlrBased(
controller_config.fec_controller(), initial_fec_enabled);
break;
case audio_network_adaptor::config::Controller::kFecControllerRplrBased:
controller = CreateFecControllerRplrBased(
controller_config.fec_controller_rplr_based(), initial_fec_enabled);
break;
case audio_network_adaptor::config::Controller::kFrameLengthController:
controller = CreateFrameLengthController(
controller_config.frame_length_controller(),
encoder_frame_lengths_ms, initial_frame_length_ms,
min_encoder_bitrate_bps);
break;
case audio_network_adaptor::config::Controller::kChannelController:
controller = CreateChannelController(
controller_config.channel_controller(), num_encoder_channels,
intial_channels_to_encode);
break;
case audio_network_adaptor::config::Controller::kDtxController:
controller = CreateDtxController(controller_config.dtx_controller(),
initial_dtx_enabled);
break;
case audio_network_adaptor::config::Controller::kBitrateController:
controller = CreateBitrateController(initial_bitrate_bps,
initial_frame_length_ms);
break;
default:
RTC_NOTREACHED();
}
if (controller_config.has_scoring_point()) {
auto& scoring_point = controller_config.scoring_point();
RTC_CHECK(scoring_point.has_uplink_bandwidth_bps());
RTC_CHECK(scoring_point.has_uplink_packet_loss_fraction());
scoring_points[controller.get()] = std::make_pair<int, float>(
scoring_point.uplink_bandwidth_bps(),
scoring_point.uplink_packet_loss_fraction());
}
controllers.push_back(std::move(controller));
}
if (scoring_points.size() == 0) {
return std::unique_ptr<ControllerManagerImpl>(new ControllerManagerImpl(
ControllerManagerImpl::Config(0, 0), std::move(controllers),
scoring_points));
} else {
RTC_CHECK(controller_manager_config.has_min_reordering_time_ms());
RTC_CHECK(controller_manager_config.has_min_reordering_squared_distance());
return std::unique_ptr<ControllerManagerImpl>(new ControllerManagerImpl(
ControllerManagerImpl::Config(
controller_manager_config.min_reordering_time_ms(),
controller_manager_config.min_reordering_squared_distance()),
std::move(controllers), scoring_points));
}
#else
RTC_NOTREACHED();
return nullptr;
#endif // WEBRTC_ENABLE_PROTOBUF
}
ControllerManagerImpl::ControllerManagerImpl(const Config& config)
: ControllerManagerImpl(
config,
std::vector<std::unique_ptr<Controller>>(),
std::map<const Controller*, std::pair<int, float>>()) {}
ControllerManagerImpl::ControllerManagerImpl(
const Config& config,
std::vector<std::unique_ptr<Controller>> controllers,
const std::map<const Controller*, std::pair<int, float>>& scoring_points)
: config_(config),
controllers_(std::move(controllers)),
last_reordering_time_ms_(rtc::Optional<int64_t>()),
last_scoring_point_(0, 0.0) {
for (auto& controller : controllers_)
default_sorted_controllers_.push_back(controller.get());
sorted_controllers_ = default_sorted_controllers_;
for (auto& controller_point : scoring_points) {
controller_scoring_points_.insert(std::make_pair(
controller_point.first, ScoringPoint(controller_point.second.first,
controller_point.second.second)));
}
}
ControllerManagerImpl::~ControllerManagerImpl() = default;
std::vector<Controller*> ControllerManagerImpl::GetSortedControllers(
const Controller::NetworkMetrics& metrics) {
if (controller_scoring_points_.size() == 0)
return default_sorted_controllers_;
if (!metrics.uplink_bandwidth_bps || !metrics.uplink_packet_loss_fraction)
return sorted_controllers_;
const int64_t now_ms = rtc::TimeMillis();
if (last_reordering_time_ms_ &&
now_ms - *last_reordering_time_ms_ < config_.min_reordering_time_ms)
return sorted_controllers_;
ScoringPoint scoring_point(*metrics.uplink_bandwidth_bps,
*metrics.uplink_packet_loss_fraction);
if (last_reordering_time_ms_ &&
last_scoring_point_.SquaredDistanceTo(scoring_point) <
config_.min_reordering_squared_distance)
return sorted_controllers_;
// Sort controllers according to the distances of |scoring_point| to the
// scoring points of controllers.
//
// A controller that does not associate with any scoring point
// are treated as if
// 1) they are less important than any controller that has a scoring point,
// 2) they are equally important to any controller that has no scoring point,
// and their relative order will follow |default_sorted_controllers_|.
std::vector<Controller*> sorted_controllers(default_sorted_controllers_);
std::stable_sort(
sorted_controllers.begin(), sorted_controllers.end(),
[this, &scoring_point](const Controller* lhs, const Controller* rhs) {
auto lhs_scoring_point = controller_scoring_points_.find(lhs);
auto rhs_scoring_point = controller_scoring_points_.find(rhs);
if (lhs_scoring_point == controller_scoring_points_.end())
return false;
if (rhs_scoring_point == controller_scoring_points_.end())
return true;
return lhs_scoring_point->second.SquaredDistanceTo(scoring_point) <
rhs_scoring_point->second.SquaredDistanceTo(scoring_point);
});
if (sorted_controllers_ != sorted_controllers) {
sorted_controllers_ = sorted_controllers;
last_reordering_time_ms_ = rtc::Optional<int64_t>(now_ms);
last_scoring_point_ = scoring_point;
}
return sorted_controllers_;
}
std::vector<Controller*> ControllerManagerImpl::GetControllers() const {
return default_sorted_controllers_;
}
ControllerManagerImpl::ScoringPoint::ScoringPoint(
int uplink_bandwidth_bps,
float uplink_packet_loss_fraction)
: uplink_bandwidth_bps(uplink_bandwidth_bps),
uplink_packet_loss_fraction(uplink_packet_loss_fraction) {}
namespace {
constexpr int kMinUplinkBandwidthBps = 0;
constexpr int kMaxUplinkBandwidthBps = 120000;
float NormalizeUplinkBandwidth(int uplink_bandwidth_bps) {
uplink_bandwidth_bps =
std::min(kMaxUplinkBandwidthBps,
std::max(kMinUplinkBandwidthBps, uplink_bandwidth_bps));
return static_cast<float>(uplink_bandwidth_bps - kMinUplinkBandwidthBps) /
(kMaxUplinkBandwidthBps - kMinUplinkBandwidthBps);
}
float NormalizePacketLossFraction(float uplink_packet_loss_fraction) {
// |uplink_packet_loss_fraction| is seldom larger than 0.3, so we scale it up
// by 3.3333f.
return std::min(uplink_packet_loss_fraction * 3.3333f, 1.0f);
}
} // namespace
float ControllerManagerImpl::ScoringPoint::SquaredDistanceTo(
const ScoringPoint& scoring_point) const {
float diff_normalized_bitrate_bps =
NormalizeUplinkBandwidth(scoring_point.uplink_bandwidth_bps) -
NormalizeUplinkBandwidth(uplink_bandwidth_bps);
float diff_normalized_packet_loss =
NormalizePacketLossFraction(scoring_point.uplink_packet_loss_fraction) -
NormalizePacketLossFraction(uplink_packet_loss_fraction);
return std::pow(diff_normalized_bitrate_bps, 2) +
std::pow(diff_normalized_packet_loss, 2);
}
} // namespace webrtc

View File

@ -0,0 +1,123 @@
/*
* 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_CONTROLLER_MANAGER_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_CONTROLLER_MANAGER_H_
#include <map>
#include <memory>
#include <vector>
#include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h"
#include "webrtc/rtc_base/constructormagic.h"
#include "webrtc/rtc_base/protobuf_utils.h"
namespace webrtc {
class DebugDumpWriter;
class ControllerManager {
public:
virtual ~ControllerManager() = default;
// Sort controllers based on their significance.
virtual std::vector<Controller*> GetSortedControllers(
const Controller::NetworkMetrics& metrics) = 0;
virtual std::vector<Controller*> GetControllers() const = 0;
};
class ControllerManagerImpl final : public ControllerManager {
public:
struct Config {
Config(int min_reordering_time_ms, float min_reordering_squared_distance);
~Config();
// Least time since last reordering for a new reordering to be made.
int min_reordering_time_ms;
// Least squared distance from last scoring point for a new reordering to be
// made.
float min_reordering_squared_distance;
};
static std::unique_ptr<ControllerManager> Create(
const ProtoString& config_string,
size_t num_encoder_channels,
rtc::ArrayView<const int> encoder_frame_lengths_ms,
int min_encoder_bitrate_bps,
size_t intial_channels_to_encode,
int initial_frame_length_ms,
int initial_bitrate_bps,
bool initial_fec_enabled,
bool initial_dtx_enabled);
static std::unique_ptr<ControllerManager> Create(
const ProtoString& config_string,
size_t num_encoder_channels,
rtc::ArrayView<const int> encoder_frame_lengths_ms,
int min_encoder_bitrate_bps,
size_t intial_channels_to_encode,
int initial_frame_length_ms,
int initial_bitrate_bps,
bool initial_fec_enabled,
bool initial_dtx_enabled,
DebugDumpWriter* debug_dump_writer);
explicit ControllerManagerImpl(const Config& config);
// Dependency injection for testing.
ControllerManagerImpl(
const Config& config,
std::vector<std::unique_ptr<Controller>> controllers,
const std::map<const Controller*, std::pair<int, float>>&
chracteristic_points);
~ControllerManagerImpl() override;
// Sort controllers based on their significance.
std::vector<Controller*> GetSortedControllers(
const Controller::NetworkMetrics& metrics) override;
std::vector<Controller*> GetControllers() const override;
private:
// Scoring point is a subset of NetworkMetrics that is used for comparing the
// significance of controllers.
struct ScoringPoint {
// TODO(eladalon): Do we want to experiment with RPLR-based scoring?
ScoringPoint(int uplink_bandwidth_bps, float uplink_packet_loss_fraction);
// Calculate the normalized [0,1] distance between two scoring points.
float SquaredDistanceTo(const ScoringPoint& scoring_point) const;
int uplink_bandwidth_bps;
float uplink_packet_loss_fraction;
};
const Config config_;
std::vector<std::unique_ptr<Controller>> controllers_;
rtc::Optional<int64_t> last_reordering_time_ms_;
ScoringPoint last_scoring_point_;
std::vector<Controller*> default_sorted_controllers_;
std::vector<Controller*> sorted_controllers_;
// |scoring_points_| saves the scoring points of various
// controllers.
std::map<const Controller*, ScoringPoint> controller_scoring_points_;
RTC_DISALLOW_COPY_AND_ASSIGN(ControllerManagerImpl);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_CONTROLLER_MANAGER_H_

View File

@ -0,0 +1,488 @@
/*
* 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/controller_manager.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/mock/mock_controller.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/mock/mock_debug_dump_writer.h"
#include "webrtc/rtc_base/fakeclock.h"
#include "webrtc/rtc_base/ignore_wundef.h"
#include "webrtc/rtc_base/protobuf_utils.h"
#include "webrtc/test/gtest.h"
#if WEBRTC_ENABLE_PROTOBUF
RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h"
#else
#include "webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h"
#endif
RTC_POP_IGNORING_WUNDEF()
#endif
namespace webrtc {
using ::testing::_;
using ::testing::NiceMock;
namespace {
constexpr size_t kNumControllers = 4;
constexpr int kChracteristicBandwithBps[2] = {15000, 0};
constexpr float kChracteristicPacketLossFraction[2] = {0.2f, 0.0f};
constexpr int kMinReorderingTimeMs = 200;
constexpr int kFactor = 100;
constexpr float kMinReorderingSquareDistance = 1.0f / kFactor / kFactor;
// |kMinUplinkBandwidthBps| and |kMaxUplinkBandwidthBps| are copied from
// controller_manager.cc
constexpr int kMinUplinkBandwidthBps = 0;
constexpr int kMaxUplinkBandwidthBps = 120000;
constexpr int kMinBandwithChangeBps =
(kMaxUplinkBandwidthBps - kMinUplinkBandwidthBps) / kFactor;
struct ControllerManagerStates {
std::unique_ptr<ControllerManager> controller_manager;
std::vector<MockController*> mock_controllers;
};
ControllerManagerStates CreateControllerManager() {
ControllerManagerStates states;
std::vector<std::unique_ptr<Controller>> controllers;
std::map<const Controller*, std::pair<int, float>> chracteristic_points;
for (size_t i = 0; i < kNumControllers; ++i) {
auto controller =
std::unique_ptr<MockController>(new NiceMock<MockController>());
EXPECT_CALL(*controller, Die());
states.mock_controllers.push_back(controller.get());
controllers.push_back(std::move(controller));
}
// Assign characteristic points to the last two controllers.
chracteristic_points[states.mock_controllers[kNumControllers - 2]] =
std::make_pair(kChracteristicBandwithBps[0],
kChracteristicPacketLossFraction[0]);
chracteristic_points[states.mock_controllers[kNumControllers - 1]] =
std::make_pair(kChracteristicBandwithBps[1],
kChracteristicPacketLossFraction[1]);
states.controller_manager.reset(new ControllerManagerImpl(
ControllerManagerImpl::Config(kMinReorderingTimeMs,
kMinReorderingSquareDistance),
std::move(controllers), chracteristic_points));
return states;
}
// |expected_order| contains the expected indices of all controllers in the
// vector of controllers returned by GetSortedControllers(). A negative index
// means that we do not care about its exact place, but we do check that it
// exists in the vector.
void CheckControllersOrder(
ControllerManagerStates* states,
const rtc::Optional<int>& uplink_bandwidth_bps,
const rtc::Optional<float>& uplink_packet_loss_fraction,
const std::vector<int>& expected_order) {
RTC_DCHECK_EQ(kNumControllers, expected_order.size());
Controller::NetworkMetrics metrics;
metrics.uplink_bandwidth_bps = uplink_bandwidth_bps;
metrics.uplink_packet_loss_fraction = uplink_packet_loss_fraction;
auto check = states->controller_manager->GetSortedControllers(metrics);
EXPECT_EQ(states->mock_controllers.size(), check.size());
for (size_t i = 0; i < states->mock_controllers.size(); ++i) {
if (expected_order[i] >= 0) {
EXPECT_EQ(states->mock_controllers[i], check[expected_order[i]]);
} else {
EXPECT_NE(check.end(), std::find(check.begin(), check.end(),
states->mock_controllers[i]));
}
}
}
} // namespace
TEST(ControllerManagerTest, GetControllersReturnAllControllers) {
auto states = CreateControllerManager();
auto check = states.controller_manager->GetControllers();
// Verify that controllers in |check| are one-to-one mapped to those in
// |mock_controllers_|.
EXPECT_EQ(states.mock_controllers.size(), check.size());
for (auto& controller : check)
EXPECT_NE(states.mock_controllers.end(),
std::find(states.mock_controllers.begin(),
states.mock_controllers.end(), controller));
}
TEST(ControllerManagerTest, ControllersInDefaultOrderOnEmptyNetworkMetrics) {
auto states = CreateControllerManager();
// |network_metrics| are empty, and the controllers are supposed to follow the
// default order.
CheckControllersOrder(&states, rtc::Optional<int>(), rtc::Optional<float>(),
{0, 1, 2, 3});
}
TEST(ControllerManagerTest, ControllersWithoutCharPointAtEndAndInDefaultOrder) {
auto states = CreateControllerManager();
CheckControllersOrder(&states, rtc::Optional<int>(0),
rtc::Optional<float>(0.0),
{kNumControllers - 2, kNumControllers - 1, -1, -1});
}
TEST(ControllerManagerTest, ControllersWithCharPointDependOnNetworkMetrics) {
auto states = CreateControllerManager();
CheckControllersOrder(
&states, rtc::Optional<int>(kChracteristicBandwithBps[1]),
rtc::Optional<float>(kChracteristicPacketLossFraction[1]),
{kNumControllers - 2, kNumControllers - 1, 1, 0});
}
TEST(ControllerManagerTest, DoNotReorderBeforeMinReordingTime) {
rtc::ScopedFakeClock fake_clock;
auto states = CreateControllerManager();
CheckControllersOrder(
&states, rtc::Optional<int>(kChracteristicBandwithBps[0]),
rtc::Optional<float>(kChracteristicPacketLossFraction[0]),
{kNumControllers - 2, kNumControllers - 1, 0, 1});
fake_clock.AdvanceTime(
rtc::TimeDelta::FromMilliseconds(kMinReorderingTimeMs - 1));
// Move uplink bandwidth and packet loss fraction to the other controller's
// characteristic point, which would cause controller manager to reorder the
// controllers if time had reached min reordering time.
CheckControllersOrder(
&states, rtc::Optional<int>(kChracteristicBandwithBps[1]),
rtc::Optional<float>(kChracteristicPacketLossFraction[1]),
{kNumControllers - 2, kNumControllers - 1, 0, 1});
}
TEST(ControllerManagerTest, ReorderBeyondMinReordingTimeAndMinDistance) {
rtc::ScopedFakeClock fake_clock;
auto states = CreateControllerManager();
constexpr int kBandwidthBps =
(kChracteristicBandwithBps[0] + kChracteristicBandwithBps[1]) / 2;
constexpr float kPacketLossFraction = (kChracteristicPacketLossFraction[0] +
kChracteristicPacketLossFraction[1]) /
2.0f;
// Set network metrics to be in the middle between the characteristic points
// of two controllers.
CheckControllersOrder(&states, rtc::Optional<int>(kBandwidthBps),
rtc::Optional<float>(kPacketLossFraction),
{kNumControllers - 2, kNumControllers - 1, 0, 1});
fake_clock.AdvanceTime(
rtc::TimeDelta::FromMilliseconds(kMinReorderingTimeMs));
// Then let network metrics move a little towards the other controller.
CheckControllersOrder(
&states, rtc::Optional<int>(kBandwidthBps - kMinBandwithChangeBps - 1),
rtc::Optional<float>(kPacketLossFraction),
{kNumControllers - 2, kNumControllers - 1, 1, 0});
}
TEST(ControllerManagerTest, DoNotReorderIfNetworkMetricsChangeTooSmall) {
rtc::ScopedFakeClock fake_clock;
auto states = CreateControllerManager();
constexpr int kBandwidthBps =
(kChracteristicBandwithBps[0] + kChracteristicBandwithBps[1]) / 2;
constexpr float kPacketLossFraction = (kChracteristicPacketLossFraction[0] +
kChracteristicPacketLossFraction[1]) /
2.0f;
// Set network metrics to be in the middle between the characteristic points
// of two controllers.
CheckControllersOrder(&states, rtc::Optional<int>(kBandwidthBps),
rtc::Optional<float>(kPacketLossFraction),
{kNumControllers - 2, kNumControllers - 1, 0, 1});
fake_clock.AdvanceTime(
rtc::TimeDelta::FromMilliseconds(kMinReorderingTimeMs));
// Then let network metrics move a little towards the other controller.
CheckControllersOrder(
&states, rtc::Optional<int>(kBandwidthBps - kMinBandwithChangeBps + 1),
rtc::Optional<float>(kPacketLossFraction),
{kNumControllers - 2, kNumControllers - 1, 0, 1});
}
#if WEBRTC_ENABLE_PROTOBUF
namespace {
void AddBitrateControllerConfig(
audio_network_adaptor::config::ControllerManager* config) {
config->add_controllers()->mutable_bitrate_controller();
}
void AddChannelControllerConfig(
audio_network_adaptor::config::ControllerManager* config) {
auto controller_config =
config->add_controllers()->mutable_channel_controller();
controller_config->set_channel_1_to_2_bandwidth_bps(31000);
controller_config->set_channel_2_to_1_bandwidth_bps(29000);
}
void AddDtxControllerConfig(
audio_network_adaptor::config::ControllerManager* config) {
auto controller_config = config->add_controllers()->mutable_dtx_controller();
controller_config->set_dtx_enabling_bandwidth_bps(55000);
controller_config->set_dtx_disabling_bandwidth_bps(65000);
}
void AddFecControllerConfig(
audio_network_adaptor::config::ControllerManager* config) {
auto controller_config_ext = config->add_controllers();
auto controller_config = controller_config_ext->mutable_fec_controller();
auto fec_enabling_threshold =
controller_config->mutable_fec_enabling_threshold();
fec_enabling_threshold->set_low_bandwidth_bps(17000);
fec_enabling_threshold->set_low_bandwidth_packet_loss(0.1f);
fec_enabling_threshold->set_high_bandwidth_bps(64000);
fec_enabling_threshold->set_high_bandwidth_packet_loss(0.05f);
auto fec_disabling_threshold =
controller_config->mutable_fec_disabling_threshold();
fec_disabling_threshold->set_low_bandwidth_bps(15000);
fec_disabling_threshold->set_low_bandwidth_packet_loss(0.08f);
fec_disabling_threshold->set_high_bandwidth_bps(64000);
fec_disabling_threshold->set_high_bandwidth_packet_loss(0.01f);
controller_config->set_time_constant_ms(500);
auto scoring_point = controller_config_ext->mutable_scoring_point();
scoring_point->set_uplink_bandwidth_bps(kChracteristicBandwithBps[0]);
scoring_point->set_uplink_packet_loss_fraction(
kChracteristicPacketLossFraction[0]);
}
void AddFrameLengthControllerConfig(
audio_network_adaptor::config::ControllerManager* config) {
auto controller_config_ext = config->add_controllers();
auto controller_config =
controller_config_ext->mutable_frame_length_controller();
controller_config->set_fl_decreasing_packet_loss_fraction(0.05f);
controller_config->set_fl_increasing_packet_loss_fraction(0.04f);
controller_config->set_fl_20ms_to_60ms_bandwidth_bps(72000);
controller_config->set_fl_60ms_to_20ms_bandwidth_bps(88000);
auto scoring_point = controller_config_ext->mutable_scoring_point();
scoring_point->set_uplink_bandwidth_bps(kChracteristicBandwithBps[1]);
scoring_point->set_uplink_packet_loss_fraction(
kChracteristicPacketLossFraction[1]);
}
constexpr int kInitialBitrateBps = 24000;
constexpr size_t kIntialChannelsToEncode = 1;
constexpr bool kInitialDtxEnabled = true;
constexpr bool kInitialFecEnabled = true;
constexpr int kInitialFrameLengthMs = 60;
constexpr int kMinBitrateBps = 6000;
ControllerManagerStates CreateControllerManager(
const ProtoString& config_string) {
ControllerManagerStates states;
constexpr size_t kNumEncoderChannels = 2;
const std::vector<int> encoder_frame_lengths_ms = {20, 60};
states.controller_manager = ControllerManagerImpl::Create(
config_string, kNumEncoderChannels, encoder_frame_lengths_ms,
kMinBitrateBps, kIntialChannelsToEncode, kInitialFrameLengthMs,
kInitialBitrateBps, kInitialFecEnabled, kInitialDtxEnabled);
return states;
}
enum class ControllerType : int8_t {
FEC,
CHANNEL,
DTX,
FRAME_LENGTH,
BIT_RATE
};
void CheckControllersOrder(const std::vector<Controller*>& controllers,
const std::vector<ControllerType>& expected_types) {
ASSERT_EQ(expected_types.size(), controllers.size());
// We also check that the controllers follow the initial settings.
AudioEncoderRuntimeConfig encoder_config;
for (size_t i = 0; i < controllers.size(); ++i) {
AudioEncoderRuntimeConfig encoder_config;
// We check the order of |controllers| by judging their decisions.
controllers[i]->MakeDecision(&encoder_config);
// Since controllers are not provided with network metrics, they give the
// initial values.
switch (expected_types[i]) {
case ControllerType::FEC:
EXPECT_EQ(rtc::Optional<bool>(kInitialFecEnabled),
encoder_config.enable_fec);
break;
case ControllerType::CHANNEL:
EXPECT_EQ(rtc::Optional<size_t>(kIntialChannelsToEncode),
encoder_config.num_channels);
break;
case ControllerType::DTX:
EXPECT_EQ(rtc::Optional<bool>(kInitialDtxEnabled),
encoder_config.enable_dtx);
break;
case ControllerType::FRAME_LENGTH:
EXPECT_EQ(rtc::Optional<int>(kInitialFrameLengthMs),
encoder_config.frame_length_ms);
break;
case ControllerType::BIT_RATE:
EXPECT_EQ(rtc::Optional<int>(kInitialBitrateBps),
encoder_config.bitrate_bps);
}
}
}
MATCHER_P(ControllerManagerEqual, value, "") {
ProtoString value_string;
ProtoString arg_string;
EXPECT_TRUE(arg.SerializeToString(&arg_string));
EXPECT_TRUE(value.SerializeToString(&value_string));
return arg_string == value_string;
}
} // namespace
TEST(ControllerManagerTest, DebugDumpLoggedWhenCreateFromConfigString) {
audio_network_adaptor::config::ControllerManager config;
config.set_min_reordering_time_ms(kMinReorderingTimeMs);
config.set_min_reordering_squared_distance(kMinReorderingSquareDistance);
AddFecControllerConfig(&config);
AddChannelControllerConfig(&config);
AddDtxControllerConfig(&config);
AddFrameLengthControllerConfig(&config);
AddBitrateControllerConfig(&config);
ProtoString config_string;
config.SerializeToString(&config_string);
constexpr size_t kNumEncoderChannels = 2;
const std::vector<int> encoder_frame_lengths_ms = {20, 60};
constexpr int64_t kClockInitialTimeMs = 12345678;
rtc::ScopedFakeClock fake_clock;
fake_clock.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kClockInitialTimeMs));
auto debug_dump_writer =
std::unique_ptr<MockDebugDumpWriter>(new NiceMock<MockDebugDumpWriter>());
EXPECT_CALL(*debug_dump_writer, Die());
EXPECT_CALL(*debug_dump_writer,
DumpControllerManagerConfig(ControllerManagerEqual(config),
kClockInitialTimeMs));
ControllerManagerImpl::Create(config_string, kNumEncoderChannels,
encoder_frame_lengths_ms, kMinBitrateBps,
kIntialChannelsToEncode, kInitialFrameLengthMs,
kInitialBitrateBps, kInitialFecEnabled,
kInitialDtxEnabled, debug_dump_writer.get());
}
TEST(ControllerManagerTest, CreateFromConfigStringAndCheckDefaultOrder) {
audio_network_adaptor::config::ControllerManager config;
config.set_min_reordering_time_ms(kMinReorderingTimeMs);
config.set_min_reordering_squared_distance(kMinReorderingSquareDistance);
AddFecControllerConfig(&config);
AddChannelControllerConfig(&config);
AddDtxControllerConfig(&config);
AddFrameLengthControllerConfig(&config);
AddBitrateControllerConfig(&config);
ProtoString config_string;
config.SerializeToString(&config_string);
auto states = CreateControllerManager(config_string);
Controller::NetworkMetrics metrics;
auto controllers = states.controller_manager->GetSortedControllers(metrics);
CheckControllersOrder(
controllers,
std::vector<ControllerType>{
ControllerType::FEC, ControllerType::CHANNEL, ControllerType::DTX,
ControllerType::FRAME_LENGTH, ControllerType::BIT_RATE});
}
TEST(ControllerManagerTest, CreateCharPointFreeConfigAndCheckDefaultOrder) {
audio_network_adaptor::config::ControllerManager config;
// Following controllers have no characteristic points.
AddChannelControllerConfig(&config);
AddDtxControllerConfig(&config);
AddBitrateControllerConfig(&config);
ProtoString config_string;
config.SerializeToString(&config_string);
auto states = CreateControllerManager(config_string);
Controller::NetworkMetrics metrics;
auto controllers = states.controller_manager->GetSortedControllers(metrics);
CheckControllersOrder(
controllers,
std::vector<ControllerType>{ControllerType::CHANNEL, ControllerType::DTX,
ControllerType::BIT_RATE});
}
TEST(ControllerManagerTest, CreateFromConfigStringAndCheckReordering) {
rtc::ScopedFakeClock fake_clock;
audio_network_adaptor::config::ControllerManager config;
config.set_min_reordering_time_ms(kMinReorderingTimeMs);
config.set_min_reordering_squared_distance(kMinReorderingSquareDistance);
AddChannelControllerConfig(&config);
// Internally associated with characteristic point 0.
AddFecControllerConfig(&config);
AddDtxControllerConfig(&config);
// Internally associated with characteristic point 1.
AddFrameLengthControllerConfig(&config);
AddBitrateControllerConfig(&config);
ProtoString config_string;
config.SerializeToString(&config_string);
auto states = CreateControllerManager(config_string);
Controller::NetworkMetrics metrics;
metrics.uplink_bandwidth_bps =
rtc::Optional<int>(kChracteristicBandwithBps[0]);
metrics.uplink_packet_loss_fraction =
rtc::Optional<float>(kChracteristicPacketLossFraction[0]);
auto controllers = states.controller_manager->GetSortedControllers(metrics);
CheckControllersOrder(controllers,
std::vector<ControllerType>{
ControllerType::FEC, ControllerType::FRAME_LENGTH,
ControllerType::CHANNEL, ControllerType::DTX,
ControllerType::BIT_RATE});
metrics.uplink_bandwidth_bps =
rtc::Optional<int>(kChracteristicBandwithBps[1]);
metrics.uplink_packet_loss_fraction =
rtc::Optional<float>(kChracteristicPacketLossFraction[1]);
fake_clock.AdvanceTime(
rtc::TimeDelta::FromMilliseconds(kMinReorderingTimeMs - 1));
controllers = states.controller_manager->GetSortedControllers(metrics);
// Should not reorder since min reordering time is not met.
CheckControllersOrder(controllers,
std::vector<ControllerType>{
ControllerType::FEC, ControllerType::FRAME_LENGTH,
ControllerType::CHANNEL, ControllerType::DTX,
ControllerType::BIT_RATE});
fake_clock.AdvanceTime(rtc::TimeDelta::FromMilliseconds(1));
controllers = states.controller_manager->GetSortedControllers(metrics);
// Reorder now.
CheckControllersOrder(controllers,
std::vector<ControllerType>{
ControllerType::FRAME_LENGTH, ControllerType::FEC,
ControllerType::CHANNEL, ControllerType::DTX,
ControllerType::BIT_RATE});
}
#endif // WEBRTC_ENABLE_PROTOBUF
} // namespace webrtc

View File

@ -0,0 +1,42 @@
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package webrtc.audio_network_adaptor.debug_dump;
import "config.proto";
message NetworkMetrics {
optional int32 uplink_bandwidth_bps = 1;
optional float uplink_packet_loss_fraction = 2;
optional int32 target_audio_bitrate_bps = 3;
optional int32 rtt_ms = 4;
optional int32 uplink_recoverable_packet_loss_fraction = 5;
}
message EncoderRuntimeConfig {
optional int32 bitrate_bps = 1;
optional int32 frame_length_ms = 2;
// Note: This is what we tell the encoder. It doesn't have to reflect
// the actual NetworkMetrics; it's subject to our decision.
optional float uplink_packet_loss_fraction = 3;
optional bool enable_fec = 4;
optional bool enable_dtx = 5;
// Some encoders can encode fewer channels than the actual input to make
// better use of the bandwidth. |num_channels| sets the number of channels
// to encode.
optional uint32 num_channels = 6;
}
message Event {
enum Type {
NETWORK_METRICS = 0;
ENCODER_RUNTIME_CONFIG = 1;
CONTROLLER_MANAGER_CONFIG = 2;
}
required Type type = 1;
required uint32 timestamp = 2;
optional NetworkMetrics network_metrics = 3;
optional EncoderRuntimeConfig encoder_runtime_config = 4;
optional webrtc.audio_network_adaptor.config.ControllerManager
controller_manager_config = 5;
}

View File

@ -0,0 +1,165 @@
/*
* 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/debug_dump_writer.h"
#include "webrtc/rtc_base/checks.h"
#include "webrtc/rtc_base/ignore_wundef.h"
#include "webrtc/rtc_base/protobuf_utils.h"
#if WEBRTC_ENABLE_PROTOBUF
RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/audio_network_adaptor/debug_dump.pb.h"
#else
#include "webrtc/modules/audio_coding/audio_network_adaptor/debug_dump.pb.h"
#endif
RTC_POP_IGNORING_WUNDEF()
#endif
namespace webrtc {
#if WEBRTC_ENABLE_PROTOBUF
namespace {
using audio_network_adaptor::debug_dump::Event;
using audio_network_adaptor::debug_dump::NetworkMetrics;
using audio_network_adaptor::debug_dump::EncoderRuntimeConfig;
void DumpEventToFile(const Event& event, FileWrapper* dump_file) {
RTC_CHECK(dump_file->is_open());
ProtoString dump_data;
event.SerializeToString(&dump_data);
int32_t size = event.ByteSize();
dump_file->Write(&size, sizeof(size));
dump_file->Write(dump_data.data(), dump_data.length());
}
} // namespace
#endif // WEBRTC_ENABLE_PROTOBUF
class DebugDumpWriterImpl final : public DebugDumpWriter {
public:
explicit DebugDumpWriterImpl(FILE* file_handle);
~DebugDumpWriterImpl() override = default;
void DumpEncoderRuntimeConfig(const AudioEncoderRuntimeConfig& config,
int64_t timestamp) override;
void DumpNetworkMetrics(const Controller::NetworkMetrics& metrics,
int64_t timestamp) override;
#if WEBRTC_ENABLE_PROTOBUF
void DumpControllerManagerConfig(
const audio_network_adaptor::config::ControllerManager&
controller_manager_config,
int64_t timestamp) override;
#endif
private:
std::unique_ptr<FileWrapper> dump_file_;
};
DebugDumpWriterImpl::DebugDumpWriterImpl(FILE* file_handle)
: dump_file_(FileWrapper::Create()) {
#if WEBRTC_ENABLE_PROTOBUF
dump_file_->OpenFromFileHandle(file_handle);
RTC_CHECK(dump_file_->is_open());
#else
RTC_NOTREACHED();
#endif
}
void DebugDumpWriterImpl::DumpNetworkMetrics(
const Controller::NetworkMetrics& metrics,
int64_t timestamp) {
#if WEBRTC_ENABLE_PROTOBUF
Event event;
event.set_timestamp(timestamp);
event.set_type(Event::NETWORK_METRICS);
auto dump_metrics = event.mutable_network_metrics();
if (metrics.uplink_bandwidth_bps)
dump_metrics->set_uplink_bandwidth_bps(*metrics.uplink_bandwidth_bps);
if (metrics.uplink_packet_loss_fraction) {
dump_metrics->set_uplink_packet_loss_fraction(
*metrics.uplink_packet_loss_fraction);
}
if (metrics.target_audio_bitrate_bps) {
dump_metrics->set_target_audio_bitrate_bps(
*metrics.target_audio_bitrate_bps);
}
if (metrics.rtt_ms)
dump_metrics->set_rtt_ms(*metrics.rtt_ms);
if (metrics.uplink_recoverable_packet_loss_fraction) {
dump_metrics->set_uplink_recoverable_packet_loss_fraction(
*metrics.uplink_recoverable_packet_loss_fraction);
}
DumpEventToFile(event, dump_file_.get());
#endif // WEBRTC_ENABLE_PROTOBUF
}
void DebugDumpWriterImpl::DumpEncoderRuntimeConfig(
const AudioEncoderRuntimeConfig& config,
int64_t timestamp) {
#if WEBRTC_ENABLE_PROTOBUF
Event event;
event.set_timestamp(timestamp);
event.set_type(Event::ENCODER_RUNTIME_CONFIG);
auto dump_config = event.mutable_encoder_runtime_config();
if (config.bitrate_bps)
dump_config->set_bitrate_bps(*config.bitrate_bps);
if (config.frame_length_ms)
dump_config->set_frame_length_ms(*config.frame_length_ms);
if (config.uplink_packet_loss_fraction) {
dump_config->set_uplink_packet_loss_fraction(
*config.uplink_packet_loss_fraction);
}
if (config.enable_fec)
dump_config->set_enable_fec(*config.enable_fec);
if (config.enable_dtx)
dump_config->set_enable_dtx(*config.enable_dtx);
if (config.num_channels)
dump_config->set_num_channels(*config.num_channels);
DumpEventToFile(event, dump_file_.get());
#endif // WEBRTC_ENABLE_PROTOBUF
}
#if WEBRTC_ENABLE_PROTOBUF
void DebugDumpWriterImpl::DumpControllerManagerConfig(
const audio_network_adaptor::config::ControllerManager&
controller_manager_config,
int64_t timestamp) {
Event event;
event.set_timestamp(timestamp);
event.set_type(Event::CONTROLLER_MANAGER_CONFIG);
event.mutable_controller_manager_config()->CopyFrom(
controller_manager_config);
DumpEventToFile(event, dump_file_.get());
}
#endif // WEBRTC_ENABLE_PROTOBUF
std::unique_ptr<DebugDumpWriter> DebugDumpWriter::Create(FILE* file_handle) {
return std::unique_ptr<DebugDumpWriter>(new DebugDumpWriterImpl(file_handle));
}
} // namespace webrtc

View File

@ -0,0 +1,55 @@
/*
* 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_DEBUG_DUMP_WRITER_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_DEBUG_DUMP_WRITER_H_
#include <memory>
#include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
#include "webrtc/rtc_base/constructormagic.h"
#include "webrtc/rtc_base/ignore_wundef.h"
#include "webrtc/system_wrappers/include/file_wrapper.h"
#if WEBRTC_ENABLE_PROTOBUF
RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h"
#else
#include "webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h"
#endif
RTC_POP_IGNORING_WUNDEF()
#endif
namespace webrtc {
class DebugDumpWriter {
public:
static std::unique_ptr<DebugDumpWriter> Create(FILE* file_handle);
virtual ~DebugDumpWriter() = default;
virtual void DumpEncoderRuntimeConfig(const AudioEncoderRuntimeConfig& config,
int64_t timestamp) = 0;
virtual void DumpNetworkMetrics(const Controller::NetworkMetrics& metrics,
int64_t timestamp) = 0;
#if WEBRTC_ENABLE_PROTOBUF
virtual void DumpControllerManagerConfig(
const audio_network_adaptor::config::ControllerManager&
controller_manager_config,
int64_t timestamp) = 0;
#endif
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_DEBUG_DUMP_WRITER_H_

View File

@ -0,0 +1,50 @@
/*
* 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/dtx_controller.h"
#include "webrtc/rtc_base/checks.h"
namespace webrtc {
DtxController::Config::Config(bool initial_dtx_enabled,
int dtx_enabling_bandwidth_bps,
int dtx_disabling_bandwidth_bps)
: initial_dtx_enabled(initial_dtx_enabled),
dtx_enabling_bandwidth_bps(dtx_enabling_bandwidth_bps),
dtx_disabling_bandwidth_bps(dtx_disabling_bandwidth_bps) {}
DtxController::DtxController(const Config& config)
: config_(config), dtx_enabled_(config_.initial_dtx_enabled) {}
DtxController::~DtxController() = default;
void DtxController::UpdateNetworkMetrics(
const NetworkMetrics& network_metrics) {
if (network_metrics.uplink_bandwidth_bps)
uplink_bandwidth_bps_ = network_metrics.uplink_bandwidth_bps;
}
void DtxController::MakeDecision(AudioEncoderRuntimeConfig* config) {
// Decision on |enable_dtx| should not have been made.
RTC_DCHECK(!config->enable_dtx);
if (uplink_bandwidth_bps_) {
if (dtx_enabled_ &&
*uplink_bandwidth_bps_ >= config_.dtx_disabling_bandwidth_bps) {
dtx_enabled_ = false;
} else if (!dtx_enabled_ &&
*uplink_bandwidth_bps_ <= config_.dtx_enabling_bandwidth_bps) {
dtx_enabled_ = true;
}
}
config->enable_dtx = rtc::Optional<bool>(dtx_enabled_);
}
} // namespace webrtc

View File

@ -0,0 +1,49 @@
/*
* 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_DTX_CONTROLLER_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_DTX_CONTROLLER_H_
#include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h"
#include "webrtc/rtc_base/constructormagic.h"
namespace webrtc {
class DtxController final : public Controller {
public:
struct Config {
Config(bool initial_dtx_enabled,
int dtx_enabling_bandwidth_bps,
int dtx_disabling_bandwidth_bps);
bool initial_dtx_enabled;
// Uplink bandwidth below which DTX should be switched on.
int dtx_enabling_bandwidth_bps;
// Uplink bandwidth above which DTX should be switched off.
int dtx_disabling_bandwidth_bps;
};
explicit DtxController(const Config& config);
~DtxController() override;
void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override;
void MakeDecision(AudioEncoderRuntimeConfig* config) override;
private:
const Config config_;
bool dtx_enabled_;
rtc::Optional<int> uplink_bandwidth_bps_;
RTC_DISALLOW_COPY_AND_ASSIGN(DtxController);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_DTX_CONTROLLER_H_

View File

@ -0,0 +1,89 @@
/*
* 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 <memory>
#include "webrtc/modules/audio_coding/audio_network_adaptor/dtx_controller.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
namespace {
constexpr int kDtxEnablingBandwidthBps = 55000;
constexpr int kDtxDisablingBandwidthBps = 65000;
constexpr int kMediumBandwidthBps =
(kDtxEnablingBandwidthBps + kDtxDisablingBandwidthBps) / 2;
std::unique_ptr<DtxController> CreateController(int initial_dtx_enabled) {
std::unique_ptr<DtxController> controller(new DtxController(
DtxController::Config(initial_dtx_enabled, kDtxEnablingBandwidthBps,
kDtxDisablingBandwidthBps)));
return controller;
}
void CheckDecision(DtxController* controller,
const rtc::Optional<int>& uplink_bandwidth_bps,
bool expected_dtx_enabled) {
if (uplink_bandwidth_bps) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_bandwidth_bps = uplink_bandwidth_bps;
controller->UpdateNetworkMetrics(network_metrics);
}
AudioEncoderRuntimeConfig config;
controller->MakeDecision(&config);
EXPECT_EQ(rtc::Optional<bool>(expected_dtx_enabled), config.enable_dtx);
}
} // namespace
TEST(DtxControllerTest, OutputInitValueWhenUplinkBandwidthUnknown) {
constexpr bool kInitialDtxEnabled = true;
auto controller = CreateController(kInitialDtxEnabled);
CheckDecision(controller.get(), rtc::Optional<int>(), kInitialDtxEnabled);
}
TEST(DtxControllerTest, TurnOnDtxForLowUplinkBandwidth) {
auto controller = CreateController(false);
CheckDecision(controller.get(), rtc::Optional<int>(kDtxEnablingBandwidthBps),
true);
}
TEST(DtxControllerTest, TurnOffDtxForHighUplinkBandwidth) {
auto controller = CreateController(true);
CheckDecision(controller.get(), rtc::Optional<int>(kDtxDisablingBandwidthBps),
false);
}
TEST(DtxControllerTest, MaintainDtxOffForMediumUplinkBandwidth) {
auto controller = CreateController(false);
CheckDecision(controller.get(), rtc::Optional<int>(kMediumBandwidthBps),
false);
}
TEST(DtxControllerTest, MaintainDtxOnForMediumUplinkBandwidth) {
auto controller = CreateController(true);
CheckDecision(controller.get(), rtc::Optional<int>(kMediumBandwidthBps),
true);
}
TEST(DtxControllerTest, CheckBehaviorOnChangingUplinkBandwidth) {
auto controller = CreateController(false);
CheckDecision(controller.get(), rtc::Optional<int>(kMediumBandwidthBps),
false);
CheckDecision(controller.get(), rtc::Optional<int>(kDtxEnablingBandwidthBps),
true);
CheckDecision(controller.get(), rtc::Optional<int>(kMediumBandwidthBps),
true);
CheckDecision(controller.get(), rtc::Optional<int>(kDtxDisablingBandwidthBps),
false);
}
} // namespace webrtc

View File

@ -0,0 +1,67 @@
/*
* 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 <math.h>
#include <algorithm>
#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.h"
namespace webrtc {
EventLogWriter::EventLogWriter(RtcEventLog* event_log,
int min_bitrate_change_bps,
float min_bitrate_change_fraction,
float min_packet_loss_change_fraction)
: event_log_(event_log),
min_bitrate_change_bps_(min_bitrate_change_bps),
min_bitrate_change_fraction_(min_bitrate_change_fraction),
min_packet_loss_change_fraction_(min_packet_loss_change_fraction) {
RTC_DCHECK(event_log_);
}
EventLogWriter::~EventLogWriter() = default;
void EventLogWriter::MaybeLogEncoderConfig(
const AudioEncoderRuntimeConfig& config) {
if (last_logged_config_.num_channels != config.num_channels)
return LogEncoderConfig(config);
if (last_logged_config_.enable_dtx != config.enable_dtx)
return LogEncoderConfig(config);
if (last_logged_config_.enable_fec != config.enable_fec)
return LogEncoderConfig(config);
if (last_logged_config_.frame_length_ms != config.frame_length_ms)
return LogEncoderConfig(config);
if ((!last_logged_config_.bitrate_bps && config.bitrate_bps) ||
(last_logged_config_.bitrate_bps && config.bitrate_bps &&
std::abs(*last_logged_config_.bitrate_bps - *config.bitrate_bps) >=
std::min(static_cast<int>(*last_logged_config_.bitrate_bps *
min_bitrate_change_fraction_),
min_bitrate_change_bps_))) {
return LogEncoderConfig(config);
}
if ((!last_logged_config_.uplink_packet_loss_fraction &&
config.uplink_packet_loss_fraction) ||
(last_logged_config_.uplink_packet_loss_fraction &&
config.uplink_packet_loss_fraction &&
fabs(*last_logged_config_.uplink_packet_loss_fraction -
*config.uplink_packet_loss_fraction) >=
min_packet_loss_change_fraction_ *
*last_logged_config_.uplink_packet_loss_fraction)) {
return LogEncoderConfig(config);
}
}
void EventLogWriter::LogEncoderConfig(const AudioEncoderRuntimeConfig& config) {
event_log_->LogAudioNetworkAdaptation(config);
last_logged_config_ = config;
}
} // namespace webrtc

View File

@ -0,0 +1,42 @@
/*
* 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_EVENT_LOG_WRITER_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_EVENT_LOG_WRITER_H_
#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
#include "webrtc/rtc_base/constructormagic.h"
namespace webrtc {
class RtcEventLog;
class EventLogWriter final {
public:
EventLogWriter(RtcEventLog* event_log,
int min_bitrate_change_bps,
float min_bitrate_change_fraction,
float min_packet_loss_change_fraction);
~EventLogWriter();
void MaybeLogEncoderConfig(const AudioEncoderRuntimeConfig& config);
private:
void LogEncoderConfig(const AudioEncoderRuntimeConfig& config);
RtcEventLog* const event_log_;
const int min_bitrate_change_bps_;
const float min_bitrate_change_fraction_;
const float min_packet_loss_change_fraction_;
AudioEncoderRuntimeConfig last_logged_config_;
RTC_DISALLOW_COPY_AND_ASSIGN(EventLogWriter);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_EVENT_LOG_WRITER_H_

View File

@ -0,0 +1,265 @@
/*
* 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 <memory>
#include "webrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
namespace {
constexpr int kMinBitrateChangeBps = 5000;
constexpr float kMinPacketLossChangeFraction = 0.5;
constexpr float kMinBitrateChangeFraction = 0.25;
constexpr int kHighBitrateBps = 70000;
constexpr int kLowBitrateBps = 10000;
constexpr int kFrameLengthMs = 60;
constexpr bool kEnableFec = true;
constexpr bool kEnableDtx = true;
constexpr float kPacketLossFraction = 0.05f;
constexpr size_t kNumChannels = 1;
MATCHER_P(EncoderRuntimeConfigIs, config, "") {
return arg.bitrate_bps == config.bitrate_bps &&
arg.frame_length_ms == config.frame_length_ms &&
arg.uplink_packet_loss_fraction ==
config.uplink_packet_loss_fraction &&
arg.enable_fec == config.enable_fec &&
arg.enable_dtx == config.enable_dtx &&
arg.num_channels == config.num_channels;
}
struct EventLogWriterStates {
std::unique_ptr<EventLogWriter> event_log_writer;
std::unique_ptr<testing::StrictMock<MockRtcEventLog>> event_log;
AudioEncoderRuntimeConfig runtime_config;
};
EventLogWriterStates CreateEventLogWriter() {
EventLogWriterStates state;
state.event_log.reset(new testing::StrictMock<MockRtcEventLog>());
state.event_log_writer.reset(new EventLogWriter(
state.event_log.get(), kMinBitrateChangeBps, kMinBitrateChangeFraction,
kMinPacketLossChangeFraction));
state.runtime_config.bitrate_bps = rtc::Optional<int>(kHighBitrateBps);
state.runtime_config.frame_length_ms = rtc::Optional<int>(kFrameLengthMs);
state.runtime_config.uplink_packet_loss_fraction =
rtc::Optional<float>(kPacketLossFraction);
state.runtime_config.enable_fec = rtc::Optional<bool>(kEnableFec);
state.runtime_config.enable_dtx = rtc::Optional<bool>(kEnableDtx);
state.runtime_config.num_channels = rtc::Optional<size_t>(kNumChannels);
return state;
}
} // namespace
TEST(EventLogWriterTest, FirstConfigIsLogged) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, SameConfigIsNotLogged) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, LogFecStateChange) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
state.runtime_config.enable_fec = rtc::Optional<bool>(!kEnableFec);
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, LogDtxStateChange) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
state.runtime_config.enable_dtx = rtc::Optional<bool>(!kEnableDtx);
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, LogChannelChange) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
state.runtime_config.num_channels = rtc::Optional<size_t>(kNumChannels + 1);
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, LogFrameLengthChange) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
state.runtime_config.frame_length_ms = rtc::Optional<int>(20);
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, DoNotLogSmallBitrateChange) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
state.runtime_config.bitrate_bps =
rtc::Optional<int>(kHighBitrateBps + kMinBitrateChangeBps - 1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, LogLargeBitrateChange) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
// At high bitrate, the min fraction rule requires a larger change than the
// min change rule. We make sure that the min change rule applies.
RTC_DCHECK_GT(kHighBitrateBps * kMinBitrateChangeFraction,
kMinBitrateChangeBps);
state.runtime_config.bitrate_bps =
rtc::Optional<int>(kHighBitrateBps + kMinBitrateChangeBps);
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, LogMinBitrateChangeFractionOnLowBitrateChange) {
auto state = CreateEventLogWriter();
state.runtime_config.bitrate_bps = rtc::Optional<int>(kLowBitrateBps);
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
// At high bitrate, the min change rule requires a larger change than the min
// fraction rule. We make sure that the min fraction rule applies.
state.runtime_config.bitrate_bps = rtc::Optional<int>(
kLowBitrateBps + kLowBitrateBps * kMinBitrateChangeFraction);
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, DoNotLogSmallPacketLossFractionChange) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
state.runtime_config.uplink_packet_loss_fraction = rtc::Optional<float>(
kPacketLossFraction + kMinPacketLossChangeFraction * kPacketLossFraction -
0.001f);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, LogLargePacketLossFractionChange) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
state.runtime_config.uplink_packet_loss_fraction = rtc::Optional<float>(
kPacketLossFraction + kMinPacketLossChangeFraction * kPacketLossFraction);
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, LogJustOnceOnMultipleChanges) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
state.runtime_config.uplink_packet_loss_fraction = rtc::Optional<float>(
kPacketLossFraction + kMinPacketLossChangeFraction * kPacketLossFraction);
state.runtime_config.frame_length_ms = rtc::Optional<int>(20);
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
TEST(EventLogWriterTest, LogAfterGradualChange) {
auto state = CreateEventLogWriter();
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
state.runtime_config.bitrate_bps =
rtc::Optional<int>(kHighBitrateBps + kMinBitrateChangeBps);
EXPECT_CALL(
*state.event_log,
LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
.Times(1);
for (int bitrate_bps = kHighBitrateBps;
bitrate_bps <= kHighBitrateBps + kMinBitrateChangeBps; bitrate_bps++) {
state.runtime_config.bitrate_bps = rtc::Optional<int>(bitrate_bps);
state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
}
}
} // namespace webrtc

View File

@ -0,0 +1,116 @@
/*
* 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_plr_based.h"
#include <limits>
#include <utility>
#include "webrtc/rtc_base/checks.h"
#include "webrtc/system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
class NullSmoothingFilter final : public SmoothingFilter {
public:
void AddSample(float sample) override {
last_sample_ = rtc::Optional<float>(sample);
}
rtc::Optional<float> GetAverage() override { return last_sample_; }
bool SetTimeConstantMs(int time_constant_ms) override {
RTC_NOTREACHED();
return false;
}
private:
rtc::Optional<float> last_sample_;
};
}
FecControllerPlrBased::Config::Config(
bool initial_fec_enabled,
const ThresholdCurve& fec_enabling_threshold,
const ThresholdCurve& fec_disabling_threshold,
int time_constant_ms)
: initial_fec_enabled(initial_fec_enabled),
fec_enabling_threshold(fec_enabling_threshold),
fec_disabling_threshold(fec_disabling_threshold),
time_constant_ms(time_constant_ms) {}
FecControllerPlrBased::FecControllerPlrBased(
const Config& config,
std::unique_ptr<SmoothingFilter> smoothing_filter)
: config_(config),
fec_enabled_(config.initial_fec_enabled),
packet_loss_smoother_(std::move(smoothing_filter)) {
RTC_DCHECK(config_.fec_disabling_threshold <= config_.fec_enabling_threshold);
}
FecControllerPlrBased::FecControllerPlrBased(const Config& config)
: FecControllerPlrBased(
config,
webrtc::field_trial::FindFullName("UseTwccPlrForAna") == "Enabled"
? std::unique_ptr<NullSmoothingFilter>(new NullSmoothingFilter())
: std::unique_ptr<SmoothingFilter>(
new SmoothingFilterImpl(config.time_constant_ms))) {}
FecControllerPlrBased::~FecControllerPlrBased() = default;
void FecControllerPlrBased::UpdateNetworkMetrics(
const NetworkMetrics& network_metrics) {
if (network_metrics.uplink_bandwidth_bps)
uplink_bandwidth_bps_ = network_metrics.uplink_bandwidth_bps;
if (network_metrics.uplink_packet_loss_fraction) {
packet_loss_smoother_->AddSample(
*network_metrics.uplink_packet_loss_fraction);
}
}
void FecControllerPlrBased::MakeDecision(AudioEncoderRuntimeConfig* config) {
RTC_DCHECK(!config->enable_fec);
RTC_DCHECK(!config->uplink_packet_loss_fraction);
const auto& packet_loss = packet_loss_smoother_->GetAverage();
fec_enabled_ = fec_enabled_ ? !FecDisablingDecision(packet_loss)
: FecEnablingDecision(packet_loss);
config->enable_fec = rtc::Optional<bool>(fec_enabled_);
config->uplink_packet_loss_fraction =
rtc::Optional<float>(packet_loss ? *packet_loss : 0.0);
}
bool FecControllerPlrBased::FecEnablingDecision(
const rtc::Optional<float>& packet_loss) const {
if (!uplink_bandwidth_bps_ || !packet_loss) {
return false;
} 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_ || !packet_loss) {
return false;
} else {
// Disable when below the curve.
return config_.fec_disabling_threshold.IsBelowCurve(
{static_cast<float>(*uplink_bandwidth_bps_), *packet_loss});
}
}
} // namespace webrtc

View File

@ -0,0 +1,72 @@
/*
* 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_PLR_BASED_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_PLR_BASED_H_
#include <memory>
#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"
#include "webrtc/rtc_base/constructormagic.h"
namespace webrtc {
class FecControllerPlrBased final : public Controller {
public:
struct Config {
// |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 ThresholdCurve& fec_enabling_threshold,
const ThresholdCurve& fec_disabling_threshold,
int time_constant_ms);
bool initial_fec_enabled;
ThresholdCurve fec_enabling_threshold;
ThresholdCurve fec_disabling_threshold;
int time_constant_ms;
};
// Dependency injection for testing.
FecControllerPlrBased(const Config& config,
std::unique_ptr<SmoothingFilter> smoothing_filter);
explicit FecControllerPlrBased(const Config& config);
~FecControllerPlrBased() override;
void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override;
void MakeDecision(AudioEncoderRuntimeConfig* config) override;
private:
bool FecEnablingDecision(const rtc::Optional<float>& packet_loss) const;
bool FecDisablingDecision(const rtc::Optional<float>& packet_loss) const;
const Config config_;
bool fec_enabled_;
rtc::Optional<int> uplink_bandwidth_bps_;
const std::unique_ptr<SmoothingFilter> packet_loss_smoother_;
RTC_DISALLOW_COPY_AND_ASSIGN(FecControllerPlrBased);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_PLR_BASED_H_

View File

@ -0,0 +1,499 @@
/*
* 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/common_audio/mocks/mock_smoothing_filter.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h"
#include "webrtc/test/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;
constexpr float kEpsilon = 1e-5f;
struct FecControllerPlrBasedTestStates {
std::unique_ptr<FecControllerPlrBased> controller;
MockSmoothingFilter* packet_loss_smoother;
};
FecControllerPlrBasedTestStates CreateFecControllerPlrBased(
bool initial_fec_enabled,
const ThresholdCurve& enabling_curve,
const ThresholdCurve& disabling_curve) {
FecControllerPlrBasedTestStates states;
std::unique_ptr<MockSmoothingFilter> mock_smoothing_filter(
new NiceMock<MockSmoothingFilter>());
states.packet_loss_smoother = mock_smoothing_filter.get();
states.controller.reset(new FecControllerPlrBased(
FecControllerPlrBased::Config(initial_fec_enabled, enabling_curve,
disabling_curve, 0),
std::move(mock_smoothing_filter)));
return states;
}
FecControllerPlrBasedTestStates CreateFecControllerPlrBased(
bool initial_fec_enabled) {
return CreateFecControllerPlrBased(
initial_fec_enabled,
ThresholdCurve(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw,
kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw),
ThresholdCurve(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw,
kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw));
}
void UpdateNetworkMetrics(FecControllerPlrBasedTestStates* states,
const rtc::Optional<int>& uplink_bandwidth_bps,
const rtc::Optional<float>& uplink_packet_loss) {
// UpdateNetworkMetrics can accept multiple network metric updates at once.
// However, currently, the most used case is to update one metric at a time.
// To reflect this fact, we separate the calls.
if (uplink_bandwidth_bps) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_bandwidth_bps = uplink_bandwidth_bps;
states->controller->UpdateNetworkMetrics(network_metrics);
}
if (uplink_packet_loss) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_packet_loss_fraction = uplink_packet_loss;
EXPECT_CALL(*states->packet_loss_smoother, AddSample(*uplink_packet_loss));
states->controller->UpdateNetworkMetrics(network_metrics);
// This is called during CheckDecision().
EXPECT_CALL(*states->packet_loss_smoother, GetAverage())
.WillOnce(Return(rtc::Optional<float>(*uplink_packet_loss)));
}
}
void UpdateNetworkMetrics(FecControllerPlrBasedTestStates* states,
int uplink_bandwidth_bps,
float uplink_packet_loss) {
UpdateNetworkMetrics(states, rtc::Optional<int>(uplink_bandwidth_bps),
rtc::Optional<float>(uplink_packet_loss));
}
// Checks that the FEC decision and |uplink_packet_loss_fraction| given by
// |states->controller->MakeDecision| matches |expected_enable_fec| and
// |expected_uplink_packet_loss_fraction|, respectively.
void CheckDecision(FecControllerPlrBasedTestStates* states,
bool expected_enable_fec,
float expected_uplink_packet_loss_fraction) {
AudioEncoderRuntimeConfig config;
states->controller->MakeDecision(&config);
EXPECT_EQ(rtc::Optional<bool>(expected_enable_fec), config.enable_fec);
EXPECT_EQ(rtc::Optional<float>(expected_uplink_packet_loss_fraction),
config.uplink_packet_loss_fraction);
}
} // namespace
TEST(FecControllerPlrBasedTest, OutputInitValueBeforeAnyInputsAreReceived) {
for (bool initial_fec_enabled : {false, true}) {
auto states = CreateFecControllerPlrBased(initial_fec_enabled);
CheckDecision(&states, initial_fec_enabled, 0);
}
}
TEST(FecControllerPlrBasedTest, OutputInitValueWhenUplinkBandwidthUnknown) {
// Regardless of the initial FEC state and the packet-loss rate,
// the initial FEC state is maintained as long as the BWE is unknown.
for (bool initial_fec_enabled : {false, true}) {
for (float packet_loss :
{kDisablingPacketLossAtLowBw - kEpsilon, kDisablingPacketLossAtLowBw,
kDisablingPacketLossAtLowBw + kEpsilon,
kEnablingPacketLossAtLowBw - kEpsilon, kEnablingPacketLossAtLowBw,
kEnablingPacketLossAtLowBw + kEpsilon}) {
auto states = CreateFecControllerPlrBased(initial_fec_enabled);
UpdateNetworkMetrics(&states, rtc::Optional<int>(),
rtc::Optional<float>(packet_loss));
CheckDecision(&states, initial_fec_enabled, packet_loss);
}
}
}
TEST(FecControllerPlrBasedTest,
OutputInitValueWhenUplinkPacketLossFractionUnknown) {
// Regardless of the initial FEC state and the BWE, the initial FEC state
// is maintained as long as the packet-loss rate is unknown.
for (bool initial_fec_enabled : {false, true}) {
for (int bandwidth : {kDisablingBandwidthLow - 1, kDisablingBandwidthLow,
kDisablingBandwidthLow + 1, kEnablingBandwidthLow - 1,
kEnablingBandwidthLow, kEnablingBandwidthLow + 1}) {
auto states = CreateFecControllerPlrBased(initial_fec_enabled);
UpdateNetworkMetrics(&states, rtc::Optional<int>(bandwidth),
rtc::Optional<float>());
CheckDecision(&states, initial_fec_enabled, 0.0);
}
}
}
TEST(FecControllerPlrBasedTest, EnableFecForHighBandwidth) {
auto states = CreateFecControllerPlrBased(false);
UpdateNetworkMetrics(&states, kEnablingBandwidthHigh,
kEnablingPacketLossAtHighBw);
CheckDecision(&states, true, kEnablingPacketLossAtHighBw);
}
TEST(FecControllerPlrBasedTest, UpdateMultipleNetworkMetricsAtOnce) {
// This test is similar to EnableFecForHighBandwidth. But instead of
// using ::UpdateNetworkMetrics(...), which calls
// FecControllerPlrBased::UpdateNetworkMetrics(...) multiple times, we
// we call it only once. This is to verify that
// FecControllerPlrBased::UpdateNetworkMetrics(...) can handle multiple
// network updates at once. This is, however, not a common use case in current
// audio_network_adaptor_impl.cc.
auto states = CreateFecControllerPlrBased(false);
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_bandwidth_bps =
rtc::Optional<int>(kEnablingBandwidthHigh);
network_metrics.uplink_packet_loss_fraction =
rtc::Optional<float>(kEnablingPacketLossAtHighBw);
EXPECT_CALL(*states.packet_loss_smoother, GetAverage())
.WillOnce(Return(rtc::Optional<float>(kEnablingPacketLossAtHighBw)));
states.controller->UpdateNetworkMetrics(network_metrics);
CheckDecision(&states, true, kEnablingPacketLossAtHighBw);
}
TEST(FecControllerPlrBasedTest, MaintainFecOffForHighBandwidth) {
auto states = CreateFecControllerPlrBased(false);
constexpr float kPacketLoss = kEnablingPacketLossAtHighBw * 0.99f;
UpdateNetworkMetrics(&states, kEnablingBandwidthHigh, kPacketLoss);
CheckDecision(&states, false, kPacketLoss);
}
TEST(FecControllerPlrBasedTest, EnableFecForMediumBandwidth) {
auto states = CreateFecControllerPlrBased(false);
constexpr float kPacketLoss =
(kEnablingPacketLossAtLowBw + kEnablingPacketLossAtHighBw) / 2.0;
UpdateNetworkMetrics(&states,
(kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2,
kPacketLoss);
CheckDecision(&states, true, kPacketLoss);
}
TEST(FecControllerPlrBasedTest, MaintainFecOffForMediumBandwidth) {
auto states = CreateFecControllerPlrBased(false);
constexpr float kPacketLoss =
kEnablingPacketLossAtLowBw * 0.49f + kEnablingPacketLossAtHighBw * 0.51f;
UpdateNetworkMetrics(&states,
(kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2,
kPacketLoss);
CheckDecision(&states, false, kPacketLoss);
}
TEST(FecControllerPlrBasedTest, EnableFecForLowBandwidth) {
auto states = CreateFecControllerPlrBased(false);
UpdateNetworkMetrics(&states, kEnablingBandwidthLow,
kEnablingPacketLossAtLowBw);
CheckDecision(&states, true, kEnablingPacketLossAtLowBw);
}
TEST(FecControllerPlrBasedTest, MaintainFecOffForLowBandwidth) {
auto states = CreateFecControllerPlrBased(false);
constexpr float kPacketLoss = kEnablingPacketLossAtLowBw * 0.99f;
UpdateNetworkMetrics(&states, kEnablingBandwidthLow, kPacketLoss);
CheckDecision(&states, false, kPacketLoss);
}
TEST(FecControllerPlrBasedTest, MaintainFecOffForVeryLowBandwidth) {
auto states = CreateFecControllerPlrBased(false);
// Below |kEnablingBandwidthLow|, no packet loss fraction can cause FEC to
// turn on.
UpdateNetworkMetrics(&states, kEnablingBandwidthLow - 1, 1.0);
CheckDecision(&states, false, 1.0);
}
TEST(FecControllerPlrBasedTest, DisableFecForHighBandwidth) {
auto states = CreateFecControllerPlrBased(true);
constexpr float kPacketLoss = kDisablingPacketLossAtHighBw - kEpsilon;
UpdateNetworkMetrics(&states, kDisablingBandwidthHigh, kPacketLoss);
CheckDecision(&states, false, kPacketLoss);
}
TEST(FecControllerPlrBasedTest, MaintainFecOnForHighBandwidth) {
// Note: Disabling happens when the value is strictly below the threshold.
auto states = CreateFecControllerPlrBased(true);
UpdateNetworkMetrics(&states, kDisablingBandwidthHigh,
kDisablingPacketLossAtHighBw);
CheckDecision(&states, true, kDisablingPacketLossAtHighBw);
}
TEST(FecControllerPlrBasedTest, DisableFecOnMediumBandwidth) {
auto states = CreateFecControllerPlrBased(true);
constexpr float kPacketLoss =
(kDisablingPacketLossAtLowBw + kDisablingPacketLossAtHighBw) / 2.0f -
kEpsilon;
UpdateNetworkMetrics(&states,
(kDisablingBandwidthHigh + kDisablingBandwidthLow) / 2,
kPacketLoss);
CheckDecision(&states, false, kPacketLoss);
}
TEST(FecControllerPlrBasedTest, MaintainFecOnForMediumBandwidth) {
auto states = CreateFecControllerPlrBased(true);
constexpr float kPacketLoss = kDisablingPacketLossAtLowBw * 0.51f +
kDisablingPacketLossAtHighBw * 0.49f - kEpsilon;
UpdateNetworkMetrics(&states,
(kEnablingBandwidthHigh + kDisablingBandwidthLow) / 2,
kPacketLoss);
CheckDecision(&states, true, kPacketLoss);
}
TEST(FecControllerPlrBasedTest, DisableFecForLowBandwidth) {
auto states = CreateFecControllerPlrBased(true);
constexpr float kPacketLoss = kDisablingPacketLossAtLowBw - kEpsilon;
UpdateNetworkMetrics(&states, kDisablingBandwidthLow, kPacketLoss);
CheckDecision(&states, false, kPacketLoss);
}
TEST(FecControllerPlrBasedTest, DisableFecForVeryLowBandwidth) {
auto states = CreateFecControllerPlrBased(true);
// Below |kEnablingBandwidthLow|, any packet loss fraction can cause FEC to
// turn off.
UpdateNetworkMetrics(&states, kDisablingBandwidthLow - 1, 1.0);
CheckDecision(&states, false, 1.0);
}
TEST(FecControllerPlrBasedTest, 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 = CreateFecControllerPlrBased(true);
UpdateNetworkMetrics(&states, kDisablingBandwidthLow - 1, 1.0);
CheckDecision(&states, false, 1.0);
UpdateNetworkMetrics(&states, kEnablingBandwidthLow,
kEnablingPacketLossAtLowBw * 0.99f);
CheckDecision(&states, false, kEnablingPacketLossAtLowBw * 0.99f);
UpdateNetworkMetrics(&states, kEnablingBandwidthHigh,
kEnablingPacketLossAtHighBw);
CheckDecision(&states, true, kEnablingPacketLossAtHighBw);
UpdateNetworkMetrics(&states, kDisablingBandwidthHigh,
kDisablingPacketLossAtHighBw);
CheckDecision(&states, true, kDisablingPacketLossAtHighBw);
UpdateNetworkMetrics(&states, kDisablingBandwidthHigh + 1, 0.0);
CheckDecision(&states, false, 0.0);
}
TEST(FecControllerPlrBasedTest, CheckBehaviorOnSpecialCurves) {
// We test a special configuration, where the points to define the FEC
// enabling/disabling curves are placed like the following, otherwise the test
// is the same as CheckBehaviorOnChangingNetworkMetrics.
//
// packet-loss ^ | |
// | | C|
// | | |
// | | D|_______
// | A|___B______
// |-----------------> bandwidth
constexpr int kEnablingBandwidthHigh = kEnablingBandwidthLow;
constexpr float kDisablingPacketLossAtLowBw = kDisablingPacketLossAtHighBw;
FecControllerPlrBasedTestStates states;
std::unique_ptr<MockSmoothingFilter> mock_smoothing_filter(
new NiceMock<MockSmoothingFilter>());
states.packet_loss_smoother = mock_smoothing_filter.get();
states.controller.reset(new FecControllerPlrBased(
FecControllerPlrBased::Config(
true,
ThresholdCurve(kEnablingBandwidthLow, kEnablingPacketLossAtLowBw,
kEnablingBandwidthHigh, kEnablingPacketLossAtHighBw),
ThresholdCurve(kDisablingBandwidthLow, kDisablingPacketLossAtLowBw,
kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw),
0),
std::move(mock_smoothing_filter)));
UpdateNetworkMetrics(&states, kDisablingBandwidthLow - 1, 1.0);
CheckDecision(&states, false, 1.0);
UpdateNetworkMetrics(&states, kEnablingBandwidthLow,
kEnablingPacketLossAtHighBw * 0.99f);
CheckDecision(&states, false, kEnablingPacketLossAtHighBw * 0.99f);
UpdateNetworkMetrics(&states, kEnablingBandwidthHigh,
kEnablingPacketLossAtHighBw);
CheckDecision(&states, true, kEnablingPacketLossAtHighBw);
UpdateNetworkMetrics(&states, kDisablingBandwidthHigh,
kDisablingPacketLossAtHighBw);
CheckDecision(&states, true, kDisablingPacketLossAtHighBw);
UpdateNetworkMetrics(&states, kDisablingBandwidthHigh + 1, 0.0);
CheckDecision(&states, false, 0.0);
}
TEST(FecControllerPlrBasedTest, SingleThresholdCurveForEnablingAndDisabling) {
// Note: To avoid numerical errors, keep kPacketLossAtLowBw and
// kPacketLossAthighBw as (negative) integer powers of 2.
// This is mostly relevant for the O3 case.
constexpr int kBandwidthLow = 10000;
constexpr float kPacketLossAtLowBw = 0.25f;
constexpr int kBandwidthHigh = 20000;
constexpr float kPacketLossAtHighBw = 0.125f;
auto curve = ThresholdCurve(kBandwidthLow, kPacketLossAtLowBw, kBandwidthHigh,
kPacketLossAtHighBw);
// B* stands for "below-curve", O* for "on-curve", and A* for "above-curve".
//
// //
// packet-loss ^ //
// | | //
// | B1 O1 //
// | | //
// | O2 //
// | \ A1 //
// | \ //
// | O3 A2 //
// | B2 \ //
// | \ //
// | O4--O5---- //
// | //
// | B3 //
// |-----------------> bandwidth //
struct NetworkState {
int bandwidth;
float packet_loss;
};
std::vector<NetworkState> below{
{kBandwidthLow - 1, kPacketLossAtLowBw + 0.1f}, // B1
{(kBandwidthLow + kBandwidthHigh) / 2,
(kPacketLossAtLowBw + kPacketLossAtHighBw) / 2 - kEpsilon}, // B2
{kBandwidthHigh + 1, kPacketLossAtHighBw - kEpsilon} // B3
};
std::vector<NetworkState> on{
{kBandwidthLow, kPacketLossAtLowBw + 0.1f}, // O1
{kBandwidthLow, kPacketLossAtLowBw}, // O2
{(kBandwidthLow + kBandwidthHigh) / 2,
(kPacketLossAtLowBw + kPacketLossAtHighBw) / 2}, // O3
{kBandwidthHigh, kPacketLossAtHighBw}, // O4
{kBandwidthHigh + 1, kPacketLossAtHighBw}, // O5
};
std::vector<NetworkState> above{
{(kBandwidthLow + kBandwidthHigh) / 2,
(kPacketLossAtLowBw + kPacketLossAtHighBw) / 2 + kEpsilon}, // A1
{kBandwidthHigh + 1, kPacketLossAtHighBw + kEpsilon}, // A2
};
// Test that FEC is turned off whenever we're below the curve, independent
// of the starting FEC state.
for (NetworkState net_state : below) {
for (bool initial_fec_enabled : {false, true}) {
auto states =
CreateFecControllerPlrBased(initial_fec_enabled, curve, curve);
UpdateNetworkMetrics(&states, net_state.bandwidth, net_state.packet_loss);
CheckDecision(&states, false, net_state.packet_loss);
}
}
// Test that FEC is turned on whenever we're on the curve or above it,
// independent of the starting FEC state.
for (std::vector<NetworkState> states_list : {on, above}) {
for (NetworkState net_state : states_list) {
for (bool initial_fec_enabled : {false, true}) {
auto states =
CreateFecControllerPlrBased(initial_fec_enabled, curve, curve);
UpdateNetworkMetrics(&states, net_state.bandwidth,
net_state.packet_loss);
CheckDecision(&states, true, net_state.packet_loss);
}
}
}
}
TEST(FecControllerPlrBasedTest, FecAlwaysOff) {
ThresholdCurve always_off_curve(0, 1.0f + kEpsilon, 0, 1.0f + kEpsilon);
for (bool initial_fec_enabled : {false, true}) {
for (int bandwidth : {0, 10000}) {
for (float packet_loss : {0.0f, 0.5f, 1.0f}) {
auto states = CreateFecControllerPlrBased(
initial_fec_enabled, always_off_curve, always_off_curve);
UpdateNetworkMetrics(&states, bandwidth, packet_loss);
CheckDecision(&states, false, packet_loss);
}
}
}
}
TEST(FecControllerPlrBasedTest, FecAlwaysOn) {
ThresholdCurve always_on_curve(0, 0.0f, 0, 0.0f);
for (bool initial_fec_enabled : {false, true}) {
for (int bandwidth : {0, 10000}) {
for (float packet_loss : {0.0f, 0.5f, 1.0f}) {
auto states = CreateFecControllerPlrBased(
initial_fec_enabled, always_on_curve, always_on_curve);
UpdateNetworkMetrics(&states, bandwidth, packet_loss);
CheckDecision(&states, true, packet_loss);
}
}
}
}
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
TEST(FecControllerPlrBasedDeathTest, InvalidConfig) {
FecControllerPlrBasedTestStates states;
std::unique_ptr<MockSmoothingFilter> mock_smoothing_filter(
new NiceMock<MockSmoothingFilter>());
states.packet_loss_smoother = mock_smoothing_filter.get();
EXPECT_DEATH(
states.controller.reset(new FecControllerPlrBased(
FecControllerPlrBased::Config(
true,
ThresholdCurve(kDisablingBandwidthLow - 1,
kEnablingPacketLossAtLowBw, kEnablingBandwidthHigh,
kEnablingPacketLossAtHighBw),
ThresholdCurve(
kDisablingBandwidthLow, kDisablingPacketLossAtLowBw,
kDisablingBandwidthHigh, kDisablingPacketLossAtHighBw),
0),
std::move(mock_smoothing_filter))),
"Check failed");
}
#endif
} // namespace webrtc

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.h"
#include <limits>
#include <utility>
#include "webrtc/rtc_base/checks.h"
namespace webrtc {
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) {
RTC_DCHECK(config_.fec_disabling_threshold <= config_.fec_enabling_threshold);
}
FecControllerRplrBased::~FecControllerRplrBased() = default;
void FecControllerRplrBased::UpdateNetworkMetrics(
const NetworkMetrics& network_metrics) {
if (network_metrics.uplink_bandwidth_bps)
uplink_bandwidth_bps_ = network_metrics.uplink_bandwidth_bps;
if (network_metrics.uplink_recoverable_packet_loss_fraction) {
uplink_recoverable_packet_loss_ =
network_metrics.uplink_recoverable_packet_loss_fraction;
}
}
void FecControllerRplrBased::MakeDecision(AudioEncoderRuntimeConfig* config) {
RTC_DCHECK(!config->enable_fec);
RTC_DCHECK(!config->uplink_packet_loss_fraction);
fec_enabled_ = fec_enabled_ ? !FecDisablingDecision() : FecEnablingDecision();
config->enable_fec = rtc::Optional<bool>(fec_enabled_);
config->uplink_packet_loss_fraction = rtc::Optional<float>(
uplink_recoverable_packet_loss_ ? *uplink_recoverable_packet_loss_ : 0.0);
}
bool FecControllerRplrBased::FecEnablingDecision() const {
if (!uplink_bandwidth_bps_ || !uplink_recoverable_packet_loss_) {
return false;
} else {
// 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_});
}
}
bool FecControllerRplrBased::FecDisablingDecision() const {
if (!uplink_bandwidth_bps_ || !uplink_recoverable_packet_loss_) {
return false;
} else {
// Disable when below the curve.
return config_.fec_disabling_threshold.IsBelowCurve(
{static_cast<float>(*uplink_bandwidth_bps_),
*uplink_recoverable_packet_loss_});
}
}
} // namespace webrtc

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_RPLR_BASED_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_RPLR_BASED_H_
#include <memory>
#include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/util/threshold_curve.h"
#include "webrtc/rtc_base/constructormagic.h"
namespace webrtc {
class FecControllerRplrBased final : public Controller {
public:
struct Config {
// |fec_enabling_threshold| defines a curve, above which FEC should be
// enabled. |fec_disabling_threshold| defines a curve, under which FEC
// should be disabled. See below
//
// recoverable
// packet-loss ^ | |
// | | | FEC
// | \ \ ON
// | FEC \ \_______ fec_enabling_threshold
// | OFF \_________ fec_disabling_threshold
// |-----------------> bandwidth
Config(bool initial_fec_enabled,
const ThresholdCurve& fec_enabling_threshold,
const ThresholdCurve& fec_disabling_threshold);
bool initial_fec_enabled;
ThresholdCurve fec_enabling_threshold;
ThresholdCurve fec_disabling_threshold;
};
explicit FecControllerRplrBased(const Config& config);
~FecControllerRplrBased() override;
void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override;
void MakeDecision(AudioEncoderRuntimeConfig* config) override;
private:
bool FecEnablingDecision() const;
bool FecDisablingDecision() const;
const Config config_;
bool fec_enabled_;
rtc::Optional<int> uplink_bandwidth_bps_;
rtc::Optional<float> uplink_recoverable_packet_loss_;
RTC_DISALLOW_COPY_AND_ASSIGN(FecControllerRplrBased);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_RPLR_BASED_H_

View File

@ -0,0 +1,532 @@
/*
* 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 <random>
#include <utility>
#include "webrtc/modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
namespace {
// The test uses the following settings:
//
// recoverable ^
// packet-loss | | |
// | A| C| FEC
// | \ \ ON
// | FEC \ D\_______
// | OFF B\_________
// |-----------------> bandwidth
//
// A : (kDisablingBandwidthLow, kDisablingRecoverablePacketLossAtLowBw)
// B : (kDisablingBandwidthHigh, kDisablingRecoverablePacketLossAtHighBw)
// C : (kEnablingBandwidthLow, kEnablingRecoverablePacketLossAtLowBw)
// D : (kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw)
constexpr int kDisablingBandwidthLow = 15000;
constexpr float kDisablingRecoverablePacketLossAtLowBw = 0.08f;
constexpr int kDisablingBandwidthHigh = 64000;
constexpr float kDisablingRecoverablePacketLossAtHighBw = 0.01f;
constexpr int kEnablingBandwidthLow = 17000;
constexpr float kEnablingRecoverablePacketLossAtLowBw = 0.1f;
constexpr int kEnablingBandwidthHigh = 64000;
constexpr float kEnablingRecoverablePacketLossAtHighBw = 0.05f;
constexpr float kEpsilon = 1e-5f;
rtc::Optional<float> GetRandomProbabilityOrUnknown() {
std::random_device rd;
std::mt19937 generator(rd());
std::uniform_real_distribution<> distribution(0, 1);
if (distribution(generator) < 0.2) {
return rtc::Optional<float>();
} else {
return rtc::Optional<float>(distribution(generator));
}
}
std::unique_ptr<FecControllerRplrBased> CreateFecControllerRplrBased(
bool initial_fec_enabled) {
return std::unique_ptr<FecControllerRplrBased>(
new FecControllerRplrBased(FecControllerRplrBased::Config(
initial_fec_enabled,
ThresholdCurve(
kEnablingBandwidthLow, kEnablingRecoverablePacketLossAtLowBw,
kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw),
ThresholdCurve(kDisablingBandwidthLow,
kDisablingRecoverablePacketLossAtLowBw,
kDisablingBandwidthHigh,
kDisablingRecoverablePacketLossAtHighBw))));
}
void UpdateNetworkMetrics(
FecControllerRplrBased* controller,
const rtc::Optional<int>& uplink_bandwidth_bps,
const rtc::Optional<float>& uplink_packet_loss,
const rtc::Optional<float>& uplink_recoveralbe_packet_loss) {
// UpdateNetworkMetrics can accept multiple network metric updates at once.
// However, currently, the most used case is to update one metric at a time.
// To reflect this fact, we separate the calls.
if (uplink_bandwidth_bps) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_bandwidth_bps = uplink_bandwidth_bps;
controller->UpdateNetworkMetrics(network_metrics);
}
if (uplink_packet_loss) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_packet_loss_fraction = uplink_packet_loss;
controller->UpdateNetworkMetrics(network_metrics);
}
if (uplink_recoveralbe_packet_loss) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_recoverable_packet_loss_fraction =
uplink_recoveralbe_packet_loss;
controller->UpdateNetworkMetrics(network_metrics);
}
}
void UpdateNetworkMetrics(
FecControllerRplrBased* controller,
const rtc::Optional<int>& uplink_bandwidth_bps,
const rtc::Optional<float>& uplink_recoveralbe_packet_loss) {
// FecControllerRplrBased doesn't currently use the PLR (general packet-loss
// rate) at all. (This might be changed in the future.) The unit-tests will
// use a random value (including unknown), to show this does not interfere.
UpdateNetworkMetrics(controller, uplink_bandwidth_bps,
GetRandomProbabilityOrUnknown(),
uplink_recoveralbe_packet_loss);
}
void UpdateNetworkMetrics(FecControllerRplrBased* controller,
int uplink_bandwidth_bps,
float uplink_recoveralbe_packet_loss) {
UpdateNetworkMetrics(controller, rtc::Optional<int>(uplink_bandwidth_bps),
rtc::Optional<float>(uplink_recoveralbe_packet_loss));
}
// Checks that the FEC decision and |uplink_packet_loss_fraction| given by
// |states->controller->MakeDecision| matches |expected_enable_fec| and
// |expected_uplink_packet_loss_fraction|, respectively.
void CheckDecision(FecControllerRplrBased* controller,
bool expected_enable_fec,
float expected_uplink_packet_loss_fraction) {
AudioEncoderRuntimeConfig config;
controller->MakeDecision(&config);
// Less compact than comparing optionals, but yields more readable errors.
EXPECT_TRUE(config.enable_fec);
if (config.enable_fec) {
EXPECT_EQ(expected_enable_fec, *config.enable_fec);
}
EXPECT_TRUE(config.uplink_packet_loss_fraction);
if (config.uplink_packet_loss_fraction) {
EXPECT_EQ(expected_uplink_packet_loss_fraction,
*config.uplink_packet_loss_fraction);
}
}
} // namespace
TEST(FecControllerRplrBasedTest, OutputInitValueBeforeAnyInputsAreReceived) {
for (bool initial_fec_enabled : {false, true}) {
auto controller = CreateFecControllerRplrBased(initial_fec_enabled);
CheckDecision(controller.get(), initial_fec_enabled, 0);
}
}
TEST(FecControllerRplrBasedTest, OutputInitValueWhenUplinkBandwidthUnknown) {
// Regardless of the initial FEC state and the recoverable-packet-loss
// rate, the initial FEC state is maintained as long as the BWE is unknown.
for (bool initial_fec_enabled : {false, true}) {
for (float recoverable_packet_loss :
{kDisablingRecoverablePacketLossAtHighBw - kEpsilon,
kDisablingRecoverablePacketLossAtHighBw,
kDisablingRecoverablePacketLossAtHighBw + kEpsilon,
kEnablingRecoverablePacketLossAtHighBw - kEpsilon,
kEnablingRecoverablePacketLossAtHighBw,
kEnablingRecoverablePacketLossAtHighBw + kEpsilon}) {
auto controller = CreateFecControllerRplrBased(initial_fec_enabled);
UpdateNetworkMetrics(controller.get(), rtc::Optional<int>(),
rtc::Optional<float>(recoverable_packet_loss));
CheckDecision(controller.get(), initial_fec_enabled,
recoverable_packet_loss);
}
}
}
TEST(FecControllerRplrBasedTest,
OutputInitValueWhenUplinkRecoverablePacketLossFractionUnknown) {
// Regardless of the initial FEC state and the BWE, the initial FEC state
// is maintained as long as the recoverable-packet-loss rate is unknown.
for (bool initial_fec_enabled : {false, true}) {
for (int bandwidth : {kDisablingBandwidthLow - 1, kDisablingBandwidthLow,
kDisablingBandwidthLow + 1, kEnablingBandwidthLow - 1,
kEnablingBandwidthLow, kEnablingBandwidthLow + 1}) {
auto controller = CreateFecControllerRplrBased(initial_fec_enabled);
UpdateNetworkMetrics(controller.get(), rtc::Optional<int>(bandwidth),
rtc::Optional<float>());
CheckDecision(controller.get(), initial_fec_enabled, 0.0);
}
}
}
TEST(FecControllerRplrBasedTest, EnableFecForHighBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthHigh,
kEnablingRecoverablePacketLossAtHighBw);
CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtHighBw);
}
TEST(FecControllerRplrBasedTest, UpdateMultipleNetworkMetricsAtOnce) {
// This test is similar to EnableFecForHighBandwidth. But instead of
// using ::UpdateNetworkMetrics(...), which calls
// FecControllerRplrBasedTest::UpdateNetworkMetrics(...) multiple times, we
// we call it only once. This is to verify that
// FecControllerRplrBasedTest::UpdateNetworkMetrics(...) can handle multiple
// network updates at once. This is, however, not a common use case in current
// audio_network_adaptor_impl.cc.
auto controller = CreateFecControllerRplrBased(false);
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_bandwidth_bps =
rtc::Optional<int>(kEnablingBandwidthHigh);
network_metrics.uplink_packet_loss_fraction =
rtc::Optional<float>(GetRandomProbabilityOrUnknown());
network_metrics.uplink_recoverable_packet_loss_fraction =
rtc::Optional<float>(kEnablingRecoverablePacketLossAtHighBw);
controller->UpdateNetworkMetrics(network_metrics);
CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtHighBw);
}
TEST(FecControllerRplrBasedTest, MaintainFecOffForHighBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
constexpr float kRecoverablePacketLoss =
kEnablingRecoverablePacketLossAtHighBw * 0.99f;
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthHigh,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, EnableFecForMediumBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
constexpr float kRecoverablePacketLoss =
(kEnablingRecoverablePacketLossAtLowBw +
kEnablingRecoverablePacketLossAtHighBw) / 2.0;
UpdateNetworkMetrics(
controller.get(),
(kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2,
kRecoverablePacketLoss);
CheckDecision(controller.get(), true, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, MaintainFecOffForMediumBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
constexpr float kRecoverablePacketLoss =
kEnablingRecoverablePacketLossAtLowBw * 0.49f +
kEnablingRecoverablePacketLossAtHighBw * 0.51f;
UpdateNetworkMetrics(controller.get(),
(kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, EnableFecForLowBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthLow,
kEnablingRecoverablePacketLossAtLowBw);
CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtLowBw);
}
TEST(FecControllerRplrBasedTest, MaintainFecOffForLowBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
constexpr float kRecoverablePacketLoss =
kEnablingRecoverablePacketLossAtLowBw * 0.99f;
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthLow,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, MaintainFecOffForVeryLowBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
// Below |kEnablingBandwidthLow|, no recoverable packet loss fraction can
// cause FEC to turn on.
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthLow - 1, 1.0);
CheckDecision(controller.get(), false, 1.0);
}
TEST(FecControllerRplrBasedTest, DisableFecForHighBandwidth) {
auto controller = CreateFecControllerRplrBased(true);
constexpr float kRecoverablePacketLoss =
kDisablingRecoverablePacketLossAtHighBw - kEpsilon;
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthHigh,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, MaintainFecOnForHighBandwidth) {
// Note: Disabling happens when the value is strictly below the threshold.
auto controller = CreateFecControllerRplrBased(true);
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthHigh,
kDisablingRecoverablePacketLossAtHighBw);
CheckDecision(controller.get(), true,
kDisablingRecoverablePacketLossAtHighBw);
}
TEST(FecControllerRplrBasedTest, DisableFecOnMediumBandwidth) {
auto controller = CreateFecControllerRplrBased(true);
constexpr float kRecoverablePacketLoss =
((kDisablingRecoverablePacketLossAtLowBw +
kDisablingRecoverablePacketLossAtHighBw) / 2.0f) - kEpsilon;
UpdateNetworkMetrics(
controller.get(),
(kDisablingBandwidthHigh + kDisablingBandwidthLow) / 2,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, MaintainFecOnForMediumBandwidth) {
auto controller = CreateFecControllerRplrBased(true);
constexpr float kRecoverablePacketLoss =
kDisablingRecoverablePacketLossAtLowBw * 0.51f +
kDisablingRecoverablePacketLossAtHighBw * 0.49f - kEpsilon;
UpdateNetworkMetrics(controller.get(),
(kEnablingBandwidthHigh + kDisablingBandwidthLow) / 2,
kRecoverablePacketLoss);
CheckDecision(controller.get(), true, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, DisableFecForLowBandwidth) {
auto controller = CreateFecControllerRplrBased(true);
constexpr float kRecoverablePacketLoss =
kDisablingRecoverablePacketLossAtLowBw - kEpsilon;
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthLow,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, DisableFecForVeryLowBandwidth) {
auto controller = CreateFecControllerRplrBased(true);
// Below |kEnablingBandwidthLow|, any recoverable packet loss fraction can
// cause FEC to turn off.
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthLow - 1, 1.0);
CheckDecision(controller.get(), false, 1.0);
}
TEST(FecControllerRplrBasedTest, CheckBehaviorOnChangingNetworkMetrics) {
// In this test, we let the network metrics to traverse from 1 to 5.
//
// recoverable ^
// packet-loss | 1 | |
// | | 2|
// | \ \ 3
// | \4 \_______
// | \_________
// |---------5-------> bandwidth
auto controller = CreateFecControllerRplrBased(true);
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthLow - 1, 1.0);
CheckDecision(controller.get(), false, 1.0);
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthLow,
kEnablingRecoverablePacketLossAtLowBw * 0.99f);
CheckDecision(controller.get(), false,
kEnablingRecoverablePacketLossAtLowBw * 0.99f);
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthHigh,
kEnablingRecoverablePacketLossAtHighBw);
CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtHighBw);
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthHigh,
kDisablingRecoverablePacketLossAtHighBw);
CheckDecision(controller.get(), true,
kDisablingRecoverablePacketLossAtHighBw);
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthHigh + 1, 0.0);
CheckDecision(controller.get(), false, 0.0);
}
TEST(FecControllerRplrBasedTest, CheckBehaviorOnSpecialCurves) {
// We test a special configuration, where the points to define the FEC
// enabling/disabling curves are placed like the following, otherwise the test
// is the same as CheckBehaviorOnChangingNetworkMetrics.
//
// recoverable ^
// packet-loss | | |
// | | C|
// | | |
// | | D|_______
// | A|___B______
// |-----------------> bandwidth
constexpr int kEnablingBandwidthHigh = kEnablingBandwidthLow;
constexpr float kDisablingRecoverablePacketLossAtLowBw =
kDisablingRecoverablePacketLossAtHighBw;
FecControllerRplrBased controller(FecControllerRplrBased::Config(
true,
ThresholdCurve(
kEnablingBandwidthLow, kEnablingRecoverablePacketLossAtLowBw,
kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw),
ThresholdCurve(
kDisablingBandwidthLow, kDisablingRecoverablePacketLossAtLowBw,
kDisablingBandwidthHigh, kDisablingRecoverablePacketLossAtHighBw)));
UpdateNetworkMetrics(&controller, kDisablingBandwidthLow - 1, 1.0);
CheckDecision(&controller, false, 1.0);
UpdateNetworkMetrics(&controller, kEnablingBandwidthLow,
kEnablingRecoverablePacketLossAtHighBw * 0.99f);
CheckDecision(&controller, false,
kEnablingRecoverablePacketLossAtHighBw * 0.99f);
UpdateNetworkMetrics(&controller, kEnablingBandwidthHigh,
kEnablingRecoverablePacketLossAtHighBw);
CheckDecision(&controller, true, kEnablingRecoverablePacketLossAtHighBw);
UpdateNetworkMetrics(&controller, kDisablingBandwidthHigh,
kDisablingRecoverablePacketLossAtHighBw);
CheckDecision(&controller, true, kDisablingRecoverablePacketLossAtHighBw);
UpdateNetworkMetrics(&controller, kDisablingBandwidthHigh + 1, 0.0);
CheckDecision(&controller, false, 0.0);
}
TEST(FecControllerRplrBasedTest, SingleThresholdCurveForEnablingAndDisabling) {
// Note: To avoid numerical errors, keep kRecoverablePacketLossAtLowBw and
// kRecoverablePacketLossAthighBw as (negative) integer powers of 2.
// This is mostly relevant for the O3 case.
constexpr int kBandwidthLow = 10000;
constexpr float kRecoverablePacketLossAtLowBw = 0.25f;
constexpr int kBandwidthHigh = 20000;
constexpr float kRecoverablePacketLossAtHighBw = 0.125f;
auto curve = ThresholdCurve(kBandwidthLow, kRecoverablePacketLossAtLowBw,
kBandwidthHigh, kRecoverablePacketLossAtHighBw);
// B* stands for "below-curve", O* for "on-curve", and A* for "above-curve".
//
// //
// recoverable ^ //
// packet-loss | | //
// | B1 O1 //
// | | //
// | O2 //
// | \ A1 //
// | \ //
// | O3 A2 //
// | B2 \ //
// | \ //
// | O4--O5---- //
// | //
// | B3 //
// |-----------------> bandwidth //
struct NetworkState {
int bandwidth;
float recoverable_packet_loss;
};
std::vector<NetworkState> below{
{kBandwidthLow - 1, kRecoverablePacketLossAtLowBw + 0.1f}, // B1
{(kBandwidthLow + kBandwidthHigh) / 2,
(kRecoverablePacketLossAtLowBw + kRecoverablePacketLossAtHighBw) / 2 -
kEpsilon}, // B2
{kBandwidthHigh + 1, kRecoverablePacketLossAtHighBw - kEpsilon} // B3
};
std::vector<NetworkState> on{
{kBandwidthLow, kRecoverablePacketLossAtLowBw + 0.1f}, // O1
{kBandwidthLow, kRecoverablePacketLossAtLowBw}, // O2
{(kBandwidthLow + kBandwidthHigh) / 2,
(kRecoverablePacketLossAtLowBw + kRecoverablePacketLossAtHighBw) /
2}, // O3
{kBandwidthHigh, kRecoverablePacketLossAtHighBw}, // O4
{kBandwidthHigh + 1, kRecoverablePacketLossAtHighBw}, // O5
};
std::vector<NetworkState> above{
{(kBandwidthLow + kBandwidthHigh) / 2,
(kRecoverablePacketLossAtLowBw + kRecoverablePacketLossAtHighBw) / 2 +
kEpsilon}, // A1
{kBandwidthHigh + 1, kRecoverablePacketLossAtHighBw + kEpsilon}, // A2
};
// Test that FEC is turned off whenever we're below the curve, independent
// of the starting FEC state.
for (NetworkState net_state : below) {
for (bool initial_fec_enabled : {false, true}) {
FecControllerRplrBased controller(
FecControllerRplrBased::Config(initial_fec_enabled, curve, curve));
UpdateNetworkMetrics(&controller, net_state.bandwidth,
net_state.recoverable_packet_loss);
CheckDecision(&controller, false, net_state.recoverable_packet_loss);
}
}
// Test that FEC is turned on whenever we're on the curve or above it,
// independent of the starting FEC state.
for (std::vector<NetworkState> states_list : {on, above}) {
for (NetworkState net_state : states_list) {
for (bool initial_fec_enabled : {false, true}) {
FecControllerRplrBased controller(
FecControllerRplrBased::Config(initial_fec_enabled, curve, curve));
UpdateNetworkMetrics(&controller, net_state.bandwidth,
net_state.recoverable_packet_loss);
CheckDecision(&controller, true, net_state.recoverable_packet_loss);
}
}
}
}
TEST(FecControllerRplrBasedTest, FecAlwaysOff) {
ThresholdCurve always_off_curve(0, 1.0f + kEpsilon, 0, 1.0f + kEpsilon);
for (bool initial_fec_enabled : {false, true}) {
for (int bandwidth : {0, 10000}) {
for (float recoverable_packet_loss : {0.0f, 0.5f, 1.0f}) {
FecControllerRplrBased controller(FecControllerRplrBased::Config(
initial_fec_enabled, always_off_curve, always_off_curve));
UpdateNetworkMetrics(&controller, bandwidth, recoverable_packet_loss);
CheckDecision(&controller, false, recoverable_packet_loss);
}
}
}
}
TEST(FecControllerRplrBasedTest, FecAlwaysOn) {
ThresholdCurve always_on_curve(0, 0.0f, 0, 0.0f);
for (bool initial_fec_enabled : {false, true}) {
for (int bandwidth : {0, 10000}) {
for (float recoverable_packet_loss : {0.0f, 0.5f, 1.0f}) {
FecControllerRplrBased controller(FecControllerRplrBased::Config(
initial_fec_enabled, always_on_curve, always_on_curve));
UpdateNetworkMetrics(&controller, bandwidth, recoverable_packet_loss);
CheckDecision(&controller, true, recoverable_packet_loss);
}
}
}
}
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
TEST(FecControllerRplrBasedDeathTest, InvalidConfig) {
EXPECT_DEATH(
FecControllerRplrBased controller(FecControllerRplrBased::Config(
true,
ThresholdCurve(
kDisablingBandwidthLow - 1, kEnablingRecoverablePacketLossAtLowBw,
kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw),
ThresholdCurve(kDisablingBandwidthLow,
kDisablingRecoverablePacketLossAtLowBw,
kDisablingBandwidthHigh,
kDisablingRecoverablePacketLossAtHighBw))),
"Check failed");
}
#endif
} // namespace webrtc

View File

@ -0,0 +1,162 @@
/*
* 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/frame_length_controller.h"
#include <utility>
#include "webrtc/rtc_base/checks.h"
#include "webrtc/rtc_base/logging.h"
namespace webrtc {
namespace {
constexpr int kPreventOveruseMarginBps = 5000;
int OverheadRateBps(size_t overhead_bytes_per_packet, int frame_length_ms) {
return static_cast<int>(overhead_bytes_per_packet * 8 * 1000 /
frame_length_ms);
}
}
FrameLengthController::Config::Config(
const std::vector<int>& encoder_frame_lengths_ms,
int initial_frame_length_ms,
int min_encoder_bitrate_bps,
float fl_increasing_packet_loss_fraction,
float fl_decreasing_packet_loss_fraction,
std::map<FrameLengthChange, int> fl_changing_bandwidths_bps)
: encoder_frame_lengths_ms(encoder_frame_lengths_ms),
initial_frame_length_ms(initial_frame_length_ms),
min_encoder_bitrate_bps(min_encoder_bitrate_bps),
fl_increasing_packet_loss_fraction(fl_increasing_packet_loss_fraction),
fl_decreasing_packet_loss_fraction(fl_decreasing_packet_loss_fraction),
fl_changing_bandwidths_bps(std::move(fl_changing_bandwidths_bps)) {}
FrameLengthController::Config::Config(const Config& other) = default;
FrameLengthController::Config::~Config() = default;
FrameLengthController::FrameLengthController(const Config& config)
: config_(config) {
frame_length_ms_ = std::find(config_.encoder_frame_lengths_ms.begin(),
config_.encoder_frame_lengths_ms.end(),
config_.initial_frame_length_ms);
// |encoder_frame_lengths_ms| must contain |initial_frame_length_ms|.
RTC_DCHECK(frame_length_ms_ != config_.encoder_frame_lengths_ms.end());
}
FrameLengthController::~FrameLengthController() = default;
void FrameLengthController::UpdateNetworkMetrics(
const NetworkMetrics& network_metrics) {
if (network_metrics.uplink_bandwidth_bps)
uplink_bandwidth_bps_ = network_metrics.uplink_bandwidth_bps;
if (network_metrics.uplink_packet_loss_fraction)
uplink_packet_loss_fraction_ = network_metrics.uplink_packet_loss_fraction;
if (network_metrics.overhead_bytes_per_packet)
overhead_bytes_per_packet_ = network_metrics.overhead_bytes_per_packet;
}
void FrameLengthController::MakeDecision(AudioEncoderRuntimeConfig* config) {
// Decision on |frame_length_ms| should not have been made.
RTC_DCHECK(!config->frame_length_ms);
if (FrameLengthIncreasingDecision(*config)) {
++frame_length_ms_;
} else if (FrameLengthDecreasingDecision(*config)) {
--frame_length_ms_;
}
config->frame_length_ms = rtc::Optional<int>(*frame_length_ms_);
}
FrameLengthController::Config::FrameLengthChange::FrameLengthChange(
int from_frame_length_ms,
int to_frame_length_ms)
: from_frame_length_ms(from_frame_length_ms),
to_frame_length_ms(to_frame_length_ms) {}
bool FrameLengthController::Config::FrameLengthChange::operator<(
const FrameLengthChange& rhs) const {
return from_frame_length_ms < rhs.from_frame_length_ms ||
(from_frame_length_ms == rhs.from_frame_length_ms &&
to_frame_length_ms < rhs.to_frame_length_ms);
}
bool FrameLengthController::FrameLengthIncreasingDecision(
const AudioEncoderRuntimeConfig& config) const {
// Increase frame length if
// 1. |uplink_bandwidth_bps| is known to be smaller or equal than
// |min_encoder_bitrate_bps| plus |prevent_overuse_margin_bps| plus the
// current overhead rate OR all the following:
// 2. longer frame length is available AND
// 3. |uplink_bandwidth_bps| is known to be smaller than a threshold AND
// 4. |uplink_packet_loss_fraction| is known to be smaller than a threshold.
auto longer_frame_length_ms = std::next(frame_length_ms_);
if (longer_frame_length_ms == config_.encoder_frame_lengths_ms.end())
return false;
auto increase_threshold = config_.fl_changing_bandwidths_bps.find(
Config::FrameLengthChange(*frame_length_ms_, *longer_frame_length_ms));
if (increase_threshold == config_.fl_changing_bandwidths_bps.end())
return false;
if (uplink_bandwidth_bps_ && overhead_bytes_per_packet_ &&
*uplink_bandwidth_bps_ <=
config_.min_encoder_bitrate_bps + kPreventOveruseMarginBps +
OverheadRateBps(*overhead_bytes_per_packet_, *frame_length_ms_)) {
return true;
}
return (uplink_bandwidth_bps_ &&
*uplink_bandwidth_bps_ <= increase_threshold->second) &&
(uplink_packet_loss_fraction_ &&
*uplink_packet_loss_fraction_ <=
config_.fl_increasing_packet_loss_fraction);
}
bool FrameLengthController::FrameLengthDecreasingDecision(
const AudioEncoderRuntimeConfig& config) const {
// Decrease frame length if
// 1. shorter frame length is available AND
// 2. |uplink_bandwidth_bps| is known to be bigger than
// |min_encoder_bitrate_bps| plus |prevent_overuse_margin_bps| plus the
// overhead which would be produced with the shorter frame length AND
// one or more of the followings:
// 3. |uplink_bandwidth_bps| is known to be larger than a threshold,
// 4. |uplink_packet_loss_fraction| is known to be larger than a threshold,
if (frame_length_ms_ == config_.encoder_frame_lengths_ms.begin())
return false;
auto shorter_frame_length_ms = std::prev(frame_length_ms_);
auto decrease_threshold = config_.fl_changing_bandwidths_bps.find(
Config::FrameLengthChange(*frame_length_ms_, *shorter_frame_length_ms));
if (decrease_threshold == config_.fl_changing_bandwidths_bps.end())
return false;
if (uplink_bandwidth_bps_ && overhead_bytes_per_packet_ &&
*uplink_bandwidth_bps_ <= config_.min_encoder_bitrate_bps +
kPreventOveruseMarginBps +
OverheadRateBps(*overhead_bytes_per_packet_,
*shorter_frame_length_ms)) {
return false;
}
return (uplink_bandwidth_bps_ &&
*uplink_bandwidth_bps_ >= decrease_threshold->second) ||
(uplink_packet_loss_fraction_ &&
*uplink_packet_loss_fraction_ >=
config_.fl_decreasing_packet_loss_fraction);
}
} // namespace webrtc

View File

@ -0,0 +1,81 @@
/*
* 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_FRAME_LENGTH_CONTROLLER_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FRAME_LENGTH_CONTROLLER_H_
#include <map>
#include <vector>
#include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h"
#include "webrtc/rtc_base/constructormagic.h"
namespace webrtc {
// Determines target frame length based on the network metrics and the decision
// of FEC controller.
class FrameLengthController final : public Controller {
public:
struct Config {
struct FrameLengthChange {
FrameLengthChange(int from_frame_length_ms, int to_frame_length_ms);
bool operator<(const FrameLengthChange& rhs) const;
int from_frame_length_ms;
int to_frame_length_ms;
};
Config(const std::vector<int>& encoder_frame_lengths_ms,
int initial_frame_length_ms,
int min_encoder_bitrate_bps,
float fl_increasing_packet_loss_fraction,
float fl_decreasing_packet_loss_fraction,
std::map<FrameLengthChange, int> fl_changing_bandwidths_bps);
Config(const Config& other);
~Config();
std::vector<int> encoder_frame_lengths_ms;
int initial_frame_length_ms;
int min_encoder_bitrate_bps;
// Uplink packet loss fraction below which frame length can increase.
float fl_increasing_packet_loss_fraction;
// Uplink packet loss fraction below which frame length should decrease.
float fl_decreasing_packet_loss_fraction;
std::map<FrameLengthChange, int> fl_changing_bandwidths_bps;
};
explicit FrameLengthController(const Config& config);
~FrameLengthController() override;
void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override;
void MakeDecision(AudioEncoderRuntimeConfig* config) override;
private:
bool FrameLengthIncreasingDecision(
const AudioEncoderRuntimeConfig& config) const;
bool FrameLengthDecreasingDecision(
const AudioEncoderRuntimeConfig& config) const;
const Config config_;
std::vector<int>::const_iterator frame_length_ms_;
rtc::Optional<int> uplink_bandwidth_bps_;
rtc::Optional<float> uplink_packet_loss_fraction_;
rtc::Optional<size_t> overhead_bytes_per_packet_;
RTC_DISALLOW_COPY_AND_ASSIGN(FrameLengthController);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FRAME_LENGTH_CONTROLLER_H_

View File

@ -0,0 +1,383 @@
/*
* 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 <memory>
#include <utility>
#include "webrtc/modules/audio_coding/audio_network_adaptor/frame_length_controller.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
namespace {
constexpr float kFlIncreasingPacketLossFraction = 0.04f;
constexpr float kFlDecreasingPacketLossFraction = 0.05f;
constexpr int kMinEncoderBitrateBps = 6000;
constexpr int kPreventOveruseMarginBps = 5000;
constexpr size_t kOverheadBytesPerPacket = 20;
constexpr int kFl20msTo60msBandwidthBps = 40000;
constexpr int kFl60msTo20msBandwidthBps = 50000;
constexpr int kFl60msTo120msBandwidthBps = 30000;
constexpr int kFl120msTo60msBandwidthBps = 40000;
constexpr int kMediumBandwidthBps =
(kFl60msTo20msBandwidthBps + kFl20msTo60msBandwidthBps) / 2;
constexpr float kMediumPacketLossFraction =
(kFlDecreasingPacketLossFraction + kFlIncreasingPacketLossFraction) / 2;
int VeryLowBitrate(int frame_length_ms) {
return kMinEncoderBitrateBps + kPreventOveruseMarginBps +
(kOverheadBytesPerPacket * 8 * 1000 / frame_length_ms);
}
std::unique_ptr<FrameLengthController> CreateController(
const std::map<FrameLengthController::Config::FrameLengthChange, int>&
frame_length_change_criteria,
const std::vector<int>& encoder_frame_lengths_ms,
int initial_frame_length_ms) {
std::unique_ptr<FrameLengthController> controller(
new FrameLengthController(FrameLengthController::Config(
encoder_frame_lengths_ms, initial_frame_length_ms,
kMinEncoderBitrateBps, kFlIncreasingPacketLossFraction,
kFlDecreasingPacketLossFraction, frame_length_change_criteria)));
return controller;
}
std::map<FrameLengthController::Config::FrameLengthChange, int>
CreateChangeCriteriaFor20msAnd60ms() {
return std::map<FrameLengthController::Config::FrameLengthChange, int>{
{FrameLengthController::Config::FrameLengthChange(20, 60),
kFl20msTo60msBandwidthBps},
{FrameLengthController::Config::FrameLengthChange(60, 20),
kFl60msTo20msBandwidthBps}};
}
std::map<FrameLengthController::Config::FrameLengthChange, int>
CreateChangeCriteriaFor20ms60msAnd120ms() {
return std::map<FrameLengthController::Config::FrameLengthChange, int>{
{FrameLengthController::Config::FrameLengthChange(20, 60),
kFl20msTo60msBandwidthBps},
{FrameLengthController::Config::FrameLengthChange(60, 20),
kFl60msTo20msBandwidthBps},
{FrameLengthController::Config::FrameLengthChange(60, 120),
kFl60msTo120msBandwidthBps},
{FrameLengthController::Config::FrameLengthChange(120, 60),
kFl120msTo60msBandwidthBps}};
}
void UpdateNetworkMetrics(
FrameLengthController* controller,
const rtc::Optional<int>& uplink_bandwidth_bps,
const rtc::Optional<float>& uplink_packet_loss_fraction,
const rtc::Optional<size_t>& overhead_bytes_per_packet) {
// UpdateNetworkMetrics can accept multiple network metric updates at once.
// However, currently, the most used case is to update one metric at a time.
// To reflect this fact, we separate the calls.
if (uplink_bandwidth_bps) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_bandwidth_bps = uplink_bandwidth_bps;
controller->UpdateNetworkMetrics(network_metrics);
}
if (uplink_packet_loss_fraction) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_packet_loss_fraction = uplink_packet_loss_fraction;
controller->UpdateNetworkMetrics(network_metrics);
}
if (overhead_bytes_per_packet) {
Controller::NetworkMetrics network_metrics;
network_metrics.overhead_bytes_per_packet = overhead_bytes_per_packet;
controller->UpdateNetworkMetrics(network_metrics);
}
}
void CheckDecision(FrameLengthController* controller,
int expected_frame_length_ms) {
AudioEncoderRuntimeConfig config;
controller->MakeDecision(&config);
EXPECT_EQ(rtc::Optional<int>(expected_frame_length_ms),
config.frame_length_ms);
}
} // namespace
TEST(FrameLengthControllerTest, DecreaseTo20MsOnHighUplinkBandwidth) {
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {20, 60}, 60);
UpdateNetworkMetrics(
controller.get(), rtc::Optional<int>(kFl60msTo20msBandwidthBps),
rtc::Optional<float>(), rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 20);
}
TEST(FrameLengthControllerTest, DecreaseTo20MsOnHighUplinkPacketLossFraction) {
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {20, 60}, 60);
UpdateNetworkMetrics(controller.get(), rtc::Optional<int>(),
rtc::Optional<float>(kFlDecreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 20);
}
TEST(FrameLengthControllerTest,
Maintain60MsIf20MsNotInReceiverFrameLengthRange) {
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {60}, 60);
// Set FEC on that would cause frame length to decrease if receiver frame
// length range included 20ms.
CheckDecision(controller.get(), 60);
}
TEST(FrameLengthControllerTest, Maintain60MsOnMultipleConditions) {
// Maintain 60ms frame length if
// 1. |uplink_bandwidth_bps| is at medium level,
// 2. |uplink_packet_loss_fraction| is at medium,
// 3. FEC is not decided ON.
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {20, 60}, 60);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kMediumBandwidthBps),
rtc::Optional<float>(kMediumPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
}
TEST(FrameLengthControllerTest, IncreaseTo60MsOnMultipleConditions) {
// Increase to 60ms frame length if
// 1. |uplink_bandwidth_bps| is known to be smaller than a threshold AND
// 2. |uplink_packet_loss_fraction| is known to be smaller than a threshold
// AND
// 3. FEC is not decided or OFF.
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {20, 60}, 20);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl20msTo60msBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
}
TEST(FrameLengthControllerTest, IncreaseTo60MsOnVeryLowUplinkBandwidth) {
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {20, 60}, 20);
// We set packet loss fraction to kFlDecreasingPacketLossFraction, which
// should have prevented frame length to increase, if the uplink bandwidth
// was not this low.
UpdateNetworkMetrics(controller.get(), rtc::Optional<int>(VeryLowBitrate(20)),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
}
TEST(FrameLengthControllerTest, Maintain60MsOnVeryLowUplinkBandwidth) {
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {20, 60}, 60);
// We set packet loss fraction to FlDecreasingPacketLossFraction, which should
// have caused the frame length to decrease, if the uplink bandwidth was not
// this low.
UpdateNetworkMetrics(controller.get(), rtc::Optional<int>(VeryLowBitrate(20)),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
}
TEST(FrameLengthControllerTest, UpdateMultipleNetworkMetricsAtOnce) {
// This test is similar to IncreaseTo60MsOnMultipleConditions. But instead of
// using ::UpdateNetworkMetrics(...), which calls
// FrameLengthController::UpdateNetworkMetrics(...) multiple times, we
// we call it only once. This is to verify that
// FrameLengthController::UpdateNetworkMetrics(...) can handle multiple
// network updates at once. This is, however, not a common use case in current
// audio_network_adaptor_impl.cc.
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {20, 60}, 20);
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_bandwidth_bps =
rtc::Optional<int>(kFl20msTo60msBandwidthBps);
network_metrics.uplink_packet_loss_fraction =
rtc::Optional<float>(kFlIncreasingPacketLossFraction);
controller->UpdateNetworkMetrics(network_metrics);
CheckDecision(controller.get(), 60);
}
TEST(FrameLengthControllerTest,
Maintain20MsIf60MsNotInReceiverFrameLengthRange) {
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {20}, 20);
// Use a low uplink bandwidth and a low uplink packet loss fraction that would
// cause frame length to increase if receiver frame length included 60ms.
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl20msTo60msBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 20);
}
TEST(FrameLengthControllerTest, Maintain20MsOnMediumUplinkBandwidth) {
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {20, 60}, 20);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kMediumBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 20);
}
TEST(FrameLengthControllerTest, Maintain20MsOnMediumUplinkPacketLossFraction) {
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {20, 60}, 20);
// Use a low uplink bandwidth that would cause frame length to increase if
// uplink packet loss fraction was low.
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl20msTo60msBandwidthBps),
rtc::Optional<float>(kMediumPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 20);
}
TEST(FrameLengthControllerTest, Maintain60MsWhenNo120msCriteriaIsSet) {
auto controller =
CreateController(CreateChangeCriteriaFor20msAnd60ms(), {20, 60, 120}, 60);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl60msTo120msBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
}
TEST(FrameLengthControllerTest, From120MsTo20MsOnHighUplinkBandwidth) {
auto controller = CreateController(CreateChangeCriteriaFor20ms60msAnd120ms(),
{20, 60, 120}, 120);
// It takes two steps for frame length to go from 120ms to 20ms.
UpdateNetworkMetrics(
controller.get(), rtc::Optional<int>(kFl60msTo20msBandwidthBps),
rtc::Optional<float>(), rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
UpdateNetworkMetrics(
controller.get(), rtc::Optional<int>(kFl60msTo20msBandwidthBps),
rtc::Optional<float>(), rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 20);
}
TEST(FrameLengthControllerTest, From120MsTo20MsOnHighUplinkPacketLossFraction) {
auto controller = CreateController(CreateChangeCriteriaFor20ms60msAnd120ms(),
{20, 60, 120}, 120);
// It takes two steps for frame length to go from 120ms to 20ms.
UpdateNetworkMetrics(controller.get(), rtc::Optional<int>(),
rtc::Optional<float>(kFlDecreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
UpdateNetworkMetrics(controller.get(), rtc::Optional<int>(),
rtc::Optional<float>(kFlDecreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 20);
}
TEST(FrameLengthControllerTest, Maintain120MsOnVeryLowUplinkBandwidth) {
auto controller = CreateController(CreateChangeCriteriaFor20ms60msAnd120ms(),
{20, 60, 120}, 120);
// We set packet loss fraction to FlDecreasingPacketLossFraction, which should
// have caused the frame length to decrease, if the uplink bandwidth was not
// this low.
UpdateNetworkMetrics(controller.get(), rtc::Optional<int>(VeryLowBitrate(60)),
rtc::Optional<float>(kFlDecreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 120);
}
TEST(FrameLengthControllerTest, From60MsTo120MsOnVeryLowUplinkBandwidth) {
auto controller = CreateController(CreateChangeCriteriaFor20ms60msAnd120ms(),
{20, 60, 120}, 60);
// We set packet loss fraction to FlDecreasingPacketLossFraction, which should
// have prevented frame length to increase, if the uplink bandwidth was not
// this low.
UpdateNetworkMetrics(controller.get(), rtc::Optional<int>(VeryLowBitrate(60)),
rtc::Optional<float>(kFlDecreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 120);
}
TEST(FrameLengthControllerTest, From20MsTo120MsOnMultipleConditions) {
// Increase to 120ms frame length if
// 1. |uplink_bandwidth_bps| is known to be smaller than a threshold AND
// 2. |uplink_packet_loss_fraction| is known to be smaller than a threshold.
auto controller = CreateController(CreateChangeCriteriaFor20ms60msAnd120ms(),
{20, 60, 120}, 20);
// It takes two steps for frame length to go from 20ms to 120ms.
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl60msTo120msBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl60msTo120msBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 120);
}
TEST(FrameLengthControllerTest, Stall60MsIf120MsNotInReceiverFrameLengthRange) {
auto controller =
CreateController(CreateChangeCriteriaFor20ms60msAnd120ms(), {20, 60}, 20);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl60msTo120msBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl60msTo120msBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
}
TEST(FrameLengthControllerTest, CheckBehaviorOnChangingNetworkMetrics) {
auto controller = CreateController(CreateChangeCriteriaFor20ms60msAnd120ms(),
{20, 60, 120}, 20);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kMediumBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 20);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl20msTo60msBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl60msTo120msBandwidthBps),
rtc::Optional<float>(kMediumPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl60msTo120msBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 120);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kFl120msTo60msBandwidthBps),
rtc::Optional<float>(kFlIncreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 60);
UpdateNetworkMetrics(controller.get(),
rtc::Optional<int>(kMediumBandwidthBps),
rtc::Optional<float>(kFlDecreasingPacketLossFraction),
rtc::Optional<size_t>(kOverheadBytesPerPacket));
CheckDecision(controller.get(), 20);
}
} // namespace webrtc

View File

@ -0,0 +1,52 @@
/*
* 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_INCLUDE_AUDIO_NETWORK_ADAPTOR_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_INCLUDE_AUDIO_NETWORK_ADAPTOR_H_
#include "webrtc/api/audio_codecs/audio_encoder.h"
#include "webrtc/api/optional.h"
#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h"
namespace webrtc {
// An AudioNetworkAdaptor optimizes the audio experience by suggesting a
// suitable runtime configuration (bit rate, frame length, FEC, etc.) to the
// encoder based on network metrics.
class AudioNetworkAdaptor {
public:
virtual ~AudioNetworkAdaptor() = default;
virtual void SetUplinkBandwidth(int uplink_bandwidth_bps) = 0;
virtual void SetUplinkPacketLossFraction(
float uplink_packet_loss_fraction) = 0;
virtual void SetUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) = 0;
virtual void SetRtt(int rtt_ms) = 0;
virtual void SetTargetAudioBitrate(int target_audio_bitrate_bps) = 0;
virtual void SetOverhead(size_t overhead_bytes_per_packet) = 0;
virtual AudioEncoderRuntimeConfig GetEncoderRuntimeConfig() = 0;
virtual void StartDebugDump(FILE* file_handle) = 0;
virtual void StopDebugDump() = 0;
virtual ANAStats GetStats() const = 0;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_INCLUDE_AUDIO_NETWORK_ADAPTOR_H_

View File

@ -0,0 +1,39 @@
/*
* 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_INCLUDE_AUDIO_NETWORK_ADAPTOR_CONFIG_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_INCLUDE_AUDIO_NETWORK_ADAPTOR_CONFIG_H_
#include "webrtc/api/optional.h"
namespace webrtc {
struct AudioEncoderRuntimeConfig {
AudioEncoderRuntimeConfig();
AudioEncoderRuntimeConfig(const AudioEncoderRuntimeConfig& other);
AudioEncoderRuntimeConfig& operator=(const AudioEncoderRuntimeConfig& other);
~AudioEncoderRuntimeConfig();
rtc::Optional<int> bitrate_bps;
rtc::Optional<int> frame_length_ms;
// Note: This is what we tell the encoder. It doesn't have to reflect
// the actual NetworkMetrics; it's subject to our decision.
rtc::Optional<float> uplink_packet_loss_fraction;
rtc::Optional<bool> enable_fec;
rtc::Optional<bool> enable_dtx;
// Some encoders can encode fewer channels than the actual input to make
// better use of the bandwidth. |num_channels| sets the number of channels
// to encode.
rtc::Optional<size_t> num_channels;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_INCLUDE_AUDIO_NETWORK_ADAPTOR_CONFIG_H_

View File

@ -0,0 +1,49 @@
/*
* 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_AUDIO_NETWORK_ADAPTOR_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_MOCK_MOCK_AUDIO_NETWORK_ADAPTOR_H_
#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
#include "webrtc/test/gmock.h"
namespace webrtc {
class MockAudioNetworkAdaptor : public AudioNetworkAdaptor {
public:
virtual ~MockAudioNetworkAdaptor() { Die(); }
MOCK_METHOD0(Die, void());
MOCK_METHOD1(SetUplinkBandwidth, void(int uplink_bandwidth_bps));
MOCK_METHOD1(SetUplinkPacketLossFraction,
void(float uplink_packet_loss_fraction));
MOCK_METHOD1(SetUplinkRecoverablePacketLossFraction,
void(float uplink_recoverable_packet_loss_fraction));
MOCK_METHOD1(SetRtt, void(int rtt_ms));
MOCK_METHOD1(SetTargetAudioBitrate, void(int target_audio_bitrate_bps));
MOCK_METHOD1(SetOverhead, void(size_t overhead_bytes_per_packet));
MOCK_METHOD0(GetEncoderRuntimeConfig, AudioEncoderRuntimeConfig());
MOCK_METHOD1(StartDebugDump, void(FILE* file_handle));
MOCK_METHOD0(StopDebugDump, void());
MOCK_CONST_METHOD0(GetStats, ANAStats());
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_MOCK_MOCK_AUDIO_NETWORK_ADAPTOR_H_

View File

@ -0,0 +1,30 @@
/*
* 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_CONTROLLER_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_MOCK_MOCK_CONTROLLER_H_
#include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h"
#include "webrtc/test/gmock.h"
namespace webrtc {
class MockController : public Controller {
public:
virtual ~MockController() { Die(); }
MOCK_METHOD0(Die, void());
MOCK_METHOD1(UpdateNetworkMetrics,
void(const NetworkMetrics& network_metrics));
MOCK_METHOD1(MakeDecision, void(AudioEncoderRuntimeConfig* config));
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_MOCK_MOCK_CONTROLLER_H_

View File

@ -0,0 +1,33 @@
/*
* 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_CONTROLLER_MANAGER_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_MOCK_MOCK_CONTROLLER_MANAGER_H_
#include <vector>
#include "webrtc/modules/audio_coding/audio_network_adaptor/controller_manager.h"
#include "webrtc/test/gmock.h"
namespace webrtc {
class MockControllerManager : public ControllerManager {
public:
virtual ~MockControllerManager() { Die(); }
MOCK_METHOD0(Die, void());
MOCK_METHOD1(
GetSortedControllers,
std::vector<Controller*>(const Controller::NetworkMetrics& metrics));
MOCK_CONST_METHOD0(GetControllers, std::vector<Controller*>());
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_MOCK_MOCK_CONTROLLER_MANAGER_H_

View File

@ -0,0 +1,40 @@
/*
* 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_DEBUG_DUMP_WRITER_H_
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_MOCK_MOCK_DEBUG_DUMP_WRITER_H_
#include "webrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h"
#include "webrtc/test/gmock.h"
namespace webrtc {
class MockDebugDumpWriter : public DebugDumpWriter {
public:
virtual ~MockDebugDumpWriter() { Die(); }
MOCK_METHOD0(Die, void());
MOCK_METHOD2(DumpEncoderRuntimeConfig,
void(const AudioEncoderRuntimeConfig& config,
int64_t timestamp));
MOCK_METHOD2(DumpNetworkMetrics,
void(const Controller::NetworkMetrics& metrics,
int64_t timestamp));
#if WEBRTC_ENABLE_PROTOBUF
MOCK_METHOD2(DumpControllerManagerConfig,
void(const audio_network_adaptor::config::ControllerManager&
controller_manager_config,
int64_t timestamp));
#endif
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_MOCK_MOCK_DEBUG_DUMP_WRITER_H_

View File

@ -0,0 +1,147 @@
#!/usr/bin/python2
# 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.
# To run this script please copy "out/<build_name>//pyproto/webrtc/modules/
# audio_coding/audio_network_adaptor/debug_dump_pb2.py" to this folder.
# The you can run this script with:
# "python parse_ana_dump.py -m uplink_bandwidth_bps -f dump_file.dat"
# You can add as may metrics or decisions to the plot as you like.
# form more information call:
# "python parse_ana_dump.py --help"
import struct
from optparse import OptionParser
import matplotlib.pyplot as plt
import debug_dump_pb2
def GetNextMessageSize(file_to_parse):
data = file_to_parse.read(4)
if data == '':
return 0
return struct.unpack('<I', data)[0]
def GetNextMessageFromFile(file_to_parse):
message_size = GetNextMessageSize(file_to_parse)
if message_size == 0:
return None
try:
event = debug_dump_pb2.Event()
event.ParseFromString(file_to_parse.read(message_size))
except IOError:
print 'Invalid message in file'
return None
return event
def InitMetrics():
metrics = {}
event = debug_dump_pb2.Event()
for metric in event.network_metrics.DESCRIPTOR.fields:
metrics[metric.name] = {'time': [], 'value': []}
return metrics
def InitDecisions():
decisions = {}
event = debug_dump_pb2.Event()
for decision in event.encoder_runtime_config.DESCRIPTOR.fields:
decisions[decision.name] = {'time': [], 'value': []}
return decisions
def ParseAnaDump(dump_file_to_parse):
with open(dump_file_to_parse, 'rb') as file_to_parse:
metrics = InitMetrics()
decisions = InitDecisions()
first_time_stamp = None
while True:
event = GetNextMessageFromFile(file_to_parse)
if event == None:
break
if first_time_stamp == None:
first_time_stamp = event.timestamp
if event.type == debug_dump_pb2.Event.ENCODER_RUNTIME_CONFIG:
for decision in event.encoder_runtime_config.DESCRIPTOR.fields:
if event.encoder_runtime_config.HasField(decision.name):
decisions[decision.name]['time'].append(event.timestamp -
first_time_stamp)
decisions[decision.name]['value'].append(
getattr(event.encoder_runtime_config, decision.name))
if event.type == debug_dump_pb2.Event.NETWORK_METRICS:
for metric in event.network_metrics.DESCRIPTOR.fields:
if event.network_metrics.HasField(metric.name):
metrics[metric.name]['time'].append(event.timestamp -
first_time_stamp)
metrics[metric.name]['value'].append(
getattr(event.network_metrics, metric.name))
return (metrics, decisions)
def main():
parser = OptionParser()
parser.add_option(
"-f", "--dump_file", dest="dump_file_to_parse", help="dump file to parse")
parser.add_option(
'-m',
'--metric_plot',
default=[],
type=str,
help='metric key (name of the metric) to plot',
dest='metric_keys',
action='append')
parser.add_option(
'-d',
'--decision_plot',
default=[],
type=str,
help='decision key (name of the decision) to plot',
dest='decision_keys',
action='append')
options = parser.parse_args()[0]
if options.dump_file_to_parse == None:
print "No dump file to parse is set.\n"
parser.print_help()
exit()
(metrics, decisions) = ParseAnaDump(options.dump_file_to_parse)
metric_keys = options.metric_keys
decision_keys = options.decision_keys
plot_count = len(metric_keys) + len(decision_keys)
if plot_count == 0:
print "You have to set at least one metric or decision to plot.\n"
parser.print_help()
exit()
plots = []
if plot_count == 1:
f, mp_plot = plt.subplots()
plots.append(mp_plot)
else:
f, mp_plots = plt.subplots(plot_count, sharex=True)
plots.extend(mp_plots.tolist())
for key in metric_keys:
plot = plots.pop()
plot.grid(True)
plot.set_title(key + " (metric)")
plot.plot(metrics[key]['time'], metrics[key]['value'])
for key in decision_keys:
plot = plots.pop()
plot.grid(True)
plot.set_title(key + " (decision)")
plot.plot(decisions[key]['time'], decisions[key]['value'])
f.subplots_adjust(hspace=0.3)
plt.show()
if __name__ == "__main__":
main()

View File

@ -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/rtc_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(eladalon): 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_

View File

@ -0,0 +1,631 @@
/*
* 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"
// A threshold curve divides 2D space into three domains - below, on and above
// the threshold curve.
// The curve is defined by two points. Those points, P1 and P2, are ordered so
// that (P1.x <= P2.x && P1.y >= P2.y).
// The part of the curve which is between the two points is hereon referred
// to as the "segment".
// A "ray" extends from P1 directly upwards into infinity; that's the "vertical
// ray". Likewise, a "horizontal ray" extends from P2 directly rightwards.
//
// ^ | //
// | | vertical ray //
// | | //
// | | //
// | P1| //
// | \ //
// | \ segment //
// | \ //
// | \ horizontal ray //
// | P2 ------------------ //
// *---------------------------> //
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 that the curve correctly reports the below/above position of points,
// when the curve is a "normal" one - P1 and P2 are different in both their
// X and Y values.
TEST(ThresholdCurveTest, PointPositionToCommonCurve) {
// 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 that the curve correctly reports the below/above position of points,
// when the curve is defined by two points with the same Y value.
TEST(ThresholdCurveTest, PointPositionToCurveWithHorizaontalSegment) {
// 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 that the curve correctly reports the below/above position of points,
// when the curve is defined by two points with the same X value.
TEST(ThresholdCurveTest, PointPositionToCurveWithVerticalSegment) {
// 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 that the curve correctly reports the below/above position of points,
// when the curve is defined by two points which are identical.
TEST(ThresholdCurveTest, PointPositionCurveWithNullSegment) {
// 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 that the relative position of two curves is computed correctly when
// the two curves have the same projection on the X-axis.
TEST(ThresholdCurveTest, TwoCurvesSegmentHasSameProjectionAxisX) {
// ^ //
// | 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 that the relative position of two curves is computed correctly when
// the higher curve's projection on the X-axis is a strict subset of the
// lower curve's projection on the X-axis (on both ends).
TEST(ThresholdCurveTest, TwoCurvesSegmentOfHigherSubsetProjectionAxisX) {
// ^ //
// | 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 that the relative position of two curves is computed correctly when
// the higher curve's right point is above lower curve's horizontal ray (meaning
// the higher curve's projection on the X-axis extends further right than
// the lower curve's).
TEST(ThresholdCurveTest,
TwoCurvesRightPointOfHigherCurveAboveHorizontalRayOfLower) {
// ^ //
// | 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 that the relative position of two curves is computed correctly when
// the higher curve's points are on the lower curve's rays (left point on the
// veritcal ray, right point on the horizontal ray).
TEST(ThresholdCurveTest, TwoCurvesPointsOfHigherOnRaysOfLower) {
// ^
// | 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 that the relative position of two curves is computed correctly when
// the second curve's segment intersects the first curve's vertical ray.
TEST(ThresholdCurveTest, SecondCurveCrossesVerticalRayOfFirstCurve) {
// ^ //
// | 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 that the relative position of two curves is computed correctly when
// the second curve's segment intersects the first curve's horizontal ray.
TEST(ThresholdCurveTest, SecondCurveCrossesHorizontalRayOfFirstCurve) {
// ^ //
// | 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 that the relative position of two curves is computed correctly when
// the second curve's segment intersects the first curve's segment.
TEST(ThresholdCurveTest, TwoCurvesWithCrossingSegments) {
// ^ //
// | 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 that the relative position of two curves is computed correctly when
// both curves are identical.
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 that the relative position of two curves is computed correctly when
// they are "nearly identical" - the first curve's segment is contained within
// the second curve's segment, but the second curve's segment extends further
// to the left (which also produces separate vertical rays for the curves).
TEST(ThresholdCurveTest, NearlyIdenticalCurvesSecondContinuesOnOtherLeftSide) {
// ^ //
// | 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 that the relative position of two curves is computed correctly when
// they are "nearly identical" - the first curve's segment is contained within
// the second curve's segment, but the second curve's segment extends further
// to the right (which also produces separate horizontal rays for the curves).
TEST(ThresholdCurveTest, NearlyIdenticalCurvesSecondContinuesOnOtherRightSide) {
// ^ //
// | 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)
// The higher-left point must be given as the first point, and the lower-right
// point must be given as the second.
// This necessarily produces a non-positive slope.
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)), "");
}
#endif
} // namespace webrtc