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:
committed by
Commit Bot
parent
6674846b4a
commit
bb547203bf
@ -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
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
153
modules/audio_coding/audio_network_adaptor/config.proto
Normal file
153
modules/audio_coding/audio_network_adaptor/config.proto
Normal 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;
|
||||
}
|
||||
|
||||
19
modules/audio_coding/audio_network_adaptor/controller.cc
Normal file
19
modules/audio_coding/audio_network_adaptor/controller.cc
Normal 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
|
||||
43
modules/audio_coding/audio_network_adaptor/controller.h
Normal file
43
modules/audio_coding/audio_network_adaptor/controller.h
Normal 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_
|
||||
420
modules/audio_coding/audio_network_adaptor/controller_manager.cc
Normal file
420
modules/audio_coding/audio_network_adaptor/controller_manager.cc
Normal 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
|
||||
123
modules/audio_coding/audio_network_adaptor/controller_manager.h
Normal file
123
modules/audio_coding/audio_network_adaptor/controller_manager.h
Normal 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_
|
||||
@ -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
|
||||
42
modules/audio_coding/audio_network_adaptor/debug_dump.proto
Normal file
42
modules/audio_coding/audio_network_adaptor/debug_dump.proto
Normal 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;
|
||||
}
|
||||
|
||||
165
modules/audio_coding/audio_network_adaptor/debug_dump_writer.cc
Normal file
165
modules/audio_coding/audio_network_adaptor/debug_dump_writer.cc
Normal 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
|
||||
@ -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_
|
||||
50
modules/audio_coding/audio_network_adaptor/dtx_controller.cc
Normal file
50
modules/audio_coding/audio_network_adaptor/dtx_controller.cc
Normal 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
|
||||
49
modules/audio_coding/audio_network_adaptor/dtx_controller.h
Normal file
49
modules/audio_coding/audio_network_adaptor/dtx_controller.h
Normal 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_
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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_
|
||||
@ -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_
|
||||
@ -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_
|
||||
@ -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_
|
||||
@ -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_
|
||||
147
modules/audio_coding/audio_network_adaptor/parse_ana_dump.py
Executable file
147
modules/audio_coding/audio_network_adaptor/parse_ana_dump.py
Executable 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()
|
||||
@ -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_
|
||||
@ -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
|
||||
Reference in New Issue
Block a user