Adding metrics to AEC3.

This CL adds metrics reporting to AEC3.

BUG=webrtc:6018

Review-Url: https://codereview.webrtc.org/2722453002
Cr-Commit-Position: refs/heads/master@{#16929}
This commit is contained in:
peah
2017-02-28 22:08:53 -08:00
committed by Commit bot
parent 999cf2bd7c
commit e985b3fe49
14 changed files with 915 additions and 5 deletions

View File

@ -38,6 +38,8 @@ rtc_static_library("audio_processing") {
"aec3/block_framer.h",
"aec3/block_processor.cc",
"aec3/block_processor.h",
"aec3/block_processor_metrics.cc",
"aec3/block_processor_metrics.h",
"aec3/cascaded_biquad_filter.cc",
"aec3/cascaded_biquad_filter.h",
"aec3/comfort_noise_generator.cc",
@ -52,6 +54,8 @@ rtc_static_library("audio_processing") {
"aec3/echo_path_variability.h",
"aec3/echo_remover.cc",
"aec3/echo_remover.h",
"aec3/echo_remover_metrics.cc",
"aec3/echo_remover_metrics.h",
"aec3/erl_estimator.cc",
"aec3/erl_estimator.h",
"aec3/erle_estimator.cc",
@ -75,6 +79,8 @@ rtc_static_library("audio_processing") {
"aec3/render_delay_buffer.h",
"aec3/render_delay_controller.cc",
"aec3/render_delay_controller.h",
"aec3/render_delay_controller_metrics.cc",
"aec3/render_delay_controller_metrics.h",
"aec3/render_signal_analyzer.cc",
"aec3/render_signal_analyzer.h",
"aec3/residual_echo_estimator.cc",
@ -563,6 +569,7 @@ if (rtc_include_tests) {
"aec3/aec3_fft_unittest.cc",
"aec3/aec_state_unittest.cc",
"aec3/block_framer_unittest.cc",
"aec3/block_processor_metrics_unittest.cc",
"aec3/block_processor_unittest.cc",
"aec3/cascaded_biquad_filter_unittest.cc",
"aec3/comfort_noise_generator_unittest.cc",
@ -570,6 +577,7 @@ if (rtc_include_tests) {
"aec3/echo_canceller3_unittest.cc",
"aec3/echo_path_delay_estimator_unittest.cc",
"aec3/echo_path_variability_unittest.cc",
"aec3/echo_remover_metrics_unittest.cc",
"aec3/echo_remover_unittest.cc",
"aec3/erl_estimator_unittest.cc",
"aec3/erle_estimator_unittest.cc",
@ -582,6 +590,7 @@ if (rtc_include_tests) {
"aec3/output_selector_unittest.cc",
"aec3/power_echo_model_unittest.cc",
"aec3/render_delay_buffer_unittest.cc",
"aec3/render_delay_controller_metrics_unittest.cc",
"aec3/render_delay_controller_unittest.cc",
"aec3/render_signal_analyzer_unittest.cc",
"aec3/residual_echo_estimator_unittest.cc",

View File

@ -26,6 +26,11 @@ namespace webrtc {
enum class Aec3Optimization { kNone, kSse2 };
constexpr int kMetricsReportingIntervalBlocks = 10 * 250;
constexpr int kMetricsComputationBlocks = 9;
constexpr int kMetricsCollectionBlocks =
kMetricsReportingIntervalBlocks - kMetricsComputationBlocks;
constexpr size_t kFftLengthBy2 = 64;
constexpr size_t kFftLengthBy2Plus1 = kFftLengthBy2 + 1;
constexpr size_t kFftLengthBy2Minus1 = kFftLengthBy2 - 1;

View File

@ -13,9 +13,9 @@
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/optional.h"
#include "webrtc/modules/audio_processing/aec3/aec3_common.h"
#include "webrtc/modules/audio_processing/aec3/block_processor_metrics.h"
#include "webrtc/modules/audio_processing/aec3/echo_path_variability.h"
#include "webrtc/modules/audio_processing/logging/apm_data_dumper.h"
#include "webrtc/system_wrappers/include/logging.h"
namespace webrtc {
namespace {
@ -44,6 +44,7 @@ class BlockProcessorImpl final : public BlockProcessor {
std::unique_ptr<RenderDelayBuffer> render_buffer_;
std::unique_ptr<RenderDelayController> delay_controller_;
std::unique_ptr<EchoRemover> echo_remover_;
BlockProcessorMetrics metrics_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorImpl);
};
@ -88,8 +89,9 @@ void BlockProcessorImpl::ProcessCapture(
delay_controller_->AlignmentHeadroomSamples(),
EchoPathVariability(echo_path_gain_change, render_delay_change),
capture_signal_saturation, render_block, capture_block);
metrics_.UpdateCapture(false);
} else {
LOG(LS_INFO) << "AEC3 empty render buffer";
metrics_.UpdateCapture(true);
}
}
@ -102,10 +104,10 @@ bool BlockProcessorImpl::BufferRender(std::vector<std::vector<float>>* block) {
!delay_controller_->AnalyzeRender((*block)[0]);
const bool render_buffer_overrun = !render_buffer_->Insert(block);
if (delay_controller_overrun || render_buffer_overrun) {
LOG(LS_INFO) << "AEC3 buffer overrrun";
metrics_.UpdateRender(true);
return false;
}
metrics_.UpdateRender(false);
return true;
}

View File

@ -0,0 +1,103 @@
/*
* 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_processing/aec3/block_processor_metrics.h"
#include "webrtc/modules/audio_processing/aec3/aec3_common.h"
#include "webrtc/system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
enum class RenderUnderrunCategory {
kNone,
kFew,
kSeveral,
kMany,
kConstant,
kNumCategories
};
enum class RenderOverrunCategory {
kNone,
kFew,
kSeveral,
kMany,
kConstant,
kNumCategories
};
} // namespace
void BlockProcessorMetrics::UpdateCapture(bool underrun) {
++capture_block_counter_;
if (underrun) {
++render_buffer_underruns_;
}
if (capture_block_counter_ == kMetricsReportingIntervalBlocks) {
metrics_reported_ = true;
RenderUnderrunCategory underrun_category;
if (render_buffer_underruns_ == 0) {
underrun_category = RenderUnderrunCategory::kNone;
} else if (render_buffer_underruns_ > (capture_block_counter_ >> 1)) {
underrun_category = RenderUnderrunCategory::kConstant;
} else if (render_buffer_underruns_ > 100) {
underrun_category = RenderUnderrunCategory::kMany;
} else if (render_buffer_underruns_ > 10) {
underrun_category = RenderUnderrunCategory::kSeveral;
} else {
underrun_category = RenderUnderrunCategory::kFew;
}
RTC_HISTOGRAM_ENUMERATION(
"WebRTC.Audio.EchoCanceller.RenderUnderruns",
static_cast<int>(underrun_category),
static_cast<int>(RenderUnderrunCategory::kNumCategories));
RenderOverrunCategory overrun_category;
if (render_buffer_overruns_ == 0) {
overrun_category = RenderOverrunCategory::kNone;
} else if (render_buffer_overruns_ > (buffer_render_calls_ >> 1)) {
overrun_category = RenderOverrunCategory::kConstant;
} else if (render_buffer_overruns_ > 100) {
overrun_category = RenderOverrunCategory::kMany;
} else if (render_buffer_overruns_ > 10) {
overrun_category = RenderOverrunCategory::kSeveral;
} else {
overrun_category = RenderOverrunCategory::kFew;
}
RTC_HISTOGRAM_ENUMERATION(
"WebRTC.Audio.EchoCanceller.RenderOverruns",
static_cast<int>(overrun_category),
static_cast<int>(RenderOverrunCategory::kNumCategories));
ResetMetrics();
capture_block_counter_ = 0;
} else {
metrics_reported_ = false;
}
}
void BlockProcessorMetrics::UpdateRender(bool overrun) {
++buffer_render_calls_;
if (overrun) {
++render_buffer_overruns_;
}
}
void BlockProcessorMetrics::ResetMetrics() {
render_buffer_underruns_ = 0;
render_buffer_overruns_ = 0;
buffer_render_calls_ = 0;
}
} // namespace webrtc

View File

@ -0,0 +1,47 @@
/*
* 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_PROCESSING_AEC3_BLOCK_PROCESSOR_METRICS_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_METRICS_H_
#include "webrtc/base/constructormagic.h"
namespace webrtc {
// Handles the reporting of metrics for the block_processor.
class BlockProcessorMetrics {
public:
BlockProcessorMetrics() = default;
// Updates the metric with new capture data.
void UpdateCapture(bool underrun);
// Updates the metric with new render data.
void UpdateRender(bool overrun);
// Returns true if the metrics have just been reported, otherwise false.
bool MetricsReported() { return metrics_reported_; }
private:
// Resets the metrics.
void ResetMetrics();
int capture_block_counter_ = 0;
bool metrics_reported_ = false;
int render_buffer_underruns_ = 0;
int render_buffer_overruns_ = 0;
int buffer_render_calls_ = 0;
RTC_DISALLOW_COPY_AND_ASSIGN(BlockProcessorMetrics);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_METRICS_H_

View File

@ -0,0 +1,34 @@
/*
* 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_processing/aec3/aec3_common.h"
#include "webrtc/modules/audio_processing/aec3/block_processor_metrics.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
// Verify the general functionality of BlockProcessorMetrics.
TEST(BlockProcessorMetrics, NormalUsage) {
BlockProcessorMetrics metrics;
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < kMetricsReportingIntervalBlocks - 1; ++k) {
metrics.UpdateRender(false);
metrics.UpdateRender(false);
metrics.UpdateCapture(false);
EXPECT_FALSE(metrics.MetricsReported());
}
metrics.UpdateCapture(false);
EXPECT_TRUE(metrics.MetricsReported());
}
}
} // namespace webrtc

View File

@ -21,6 +21,7 @@
#include "webrtc/modules/audio_processing/aec3/aec_state.h"
#include "webrtc/modules/audio_processing/aec3/comfort_noise_generator.h"
#include "webrtc/modules/audio_processing/aec3/echo_path_variability.h"
#include "webrtc/modules/audio_processing/aec3/echo_remover_metrics.h"
#include "webrtc/modules/audio_processing/aec3/fft_buffer.h"
#include "webrtc/modules/audio_processing/aec3/fft_data.h"
#include "webrtc/modules/audio_processing/aec3/output_selector.h"
@ -90,6 +91,7 @@ class EchoRemoverImpl final : public EchoRemover {
bool echo_leakage_detected_ = false;
std::array<float, kBlockSize> x_old_;
AecState aec_state_;
EchoRemoverMetrics metrics_;
RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverImpl);
};
@ -209,6 +211,9 @@ void EchoRemoverImpl::ProcessBlock(
doubletalk ? 0.001f : 0.0001f, &G);
suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G, y);
// Update the metrics.
metrics_.Update(aec_state_, cng_.NoiseSpectrum(), G);
// Debug outputs for the purpose of development and analysis.
data_dumper_->DumpRaw("aec3_N2", cng_.NoiseSpectrum());
data_dumper_->DumpRaw("aec3_suppressor_gain", G);

View File

@ -0,0 +1,284 @@
/*
* 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_processing/aec3/echo_remover_metrics.h"
#include <math.h>
#include <algorithm>
#include <numeric>
#include "webrtc/system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
constexpr float kOneByMetricsCollectionBlocks = 1.f / kMetricsCollectionBlocks;
} // namespace
EchoRemoverMetrics::DbMetric::DbMetric() : DbMetric(0.f, 0.f, 0.f) {}
EchoRemoverMetrics::DbMetric::DbMetric(float sum_value,
float floor_value,
float ceil_value)
: sum_value(sum_value), floor_value(floor_value), ceil_value(ceil_value) {}
void EchoRemoverMetrics::DbMetric::Update(float value) {
sum_value += value;
floor_value = std::min(floor_value, value);
ceil_value = std::max(ceil_value, value);
}
EchoRemoverMetrics::EchoRemoverMetrics() {
ResetMetrics();
}
void EchoRemoverMetrics::ResetMetrics() {
erl_.fill(DbMetric(0.f, 10000.f, 0.000f));
erle_.fill(DbMetric(0.f, 0.f, 1000.f));
comfort_noise_.fill(DbMetric(0.f, 100000000.f, 0.f));
suppressor_gain_.fill(DbMetric(0.f, 1.f, 0.f));
active_render_count_ = 0;
saturated_capture_ = false;
}
void EchoRemoverMetrics::Update(
const AecState& aec_state,
const std::array<float, kFftLengthBy2Plus1>& comfort_noise_spectrum,
const std::array<float, kFftLengthBy2Plus1>& suppressor_gain) {
metrics_reported_ = false;
if (++block_counter_ <= kMetricsCollectionBlocks) {
aec3::UpdateDbMetric(aec_state.Erl(), &erl_);
aec3::UpdateDbMetric(aec_state.Erle(), &erle_);
aec3::UpdateDbMetric(comfort_noise_spectrum, &comfort_noise_);
aec3::UpdateDbMetric(suppressor_gain, &suppressor_gain_);
active_render_count_ += (aec_state.ActiveRender() ? 1 : 0);
saturated_capture_ = saturated_capture_ || aec_state.SaturatedCapture();
} else {
// Report the metrics over several frames in order to lower the impact of
// the logarithms involved on the computational complexity.
constexpr int kMetricsCollectionBlocksBy2 = kMetricsCollectionBlocks / 2;
constexpr float kComfortNoiseScaling = 1.f / (kBlockSize * kBlockSize);
switch (block_counter_) {
case kMetricsCollectionBlocks + 1:
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErleBand0.Average",
aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f,
kOneByMetricsCollectionBlocks,
erle_[0].sum_value),
0, 19, 20);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErleBand0.Max",
aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f, 1.f,
erle_[0].ceil_value),
0, 19, 20);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErleBand0.Min",
aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f, 1.f,
erle_[0].floor_value),
0, 19, 20);
break;
case kMetricsCollectionBlocks + 2:
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErleBand1.Average",
aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f,
kOneByMetricsCollectionBlocks,
erle_[1].sum_value),
0, 19, 20);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErleBand1.Max",
aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f, 1.f,
erle_[1].ceil_value),
0, 19, 20);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErleBand1.Min",
aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f, 1.f,
erle_[1].floor_value),
0, 19, 20);
break;
case kMetricsCollectionBlocks + 3:
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErlBand0.Average",
aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f,
kOneByMetricsCollectionBlocks,
erl_[0].sum_value),
0, 59, 30);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErlBand0.Max",
aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f,
erl_[0].ceil_value),
0, 59, 30);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErlBand0.Min",
aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f,
erl_[0].floor_value),
0, 59, 30);
break;
case kMetricsCollectionBlocks + 4:
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErlBand1.Average",
aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f,
kOneByMetricsCollectionBlocks,
erl_[1].sum_value),
0, 59, 30);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErlBand1.Max",
aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f,
erl_[1].ceil_value),
0, 59, 30);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ErlBand1.Min",
aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f,
erl_[1].floor_value),
0, 59, 30);
break;
case kMetricsCollectionBlocks + 5:
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ComfortNoiseBand0.Average",
aec3::TransformDbMetricForReporting(
true, 0.f, 89.f, -90.3f,
kComfortNoiseScaling * kOneByMetricsCollectionBlocks,
comfort_noise_[0].sum_value),
0, 89, 45);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ComfortNoiseBand0.Max",
aec3::TransformDbMetricForReporting(true, 0.f, 89.f, -90.3f,
kComfortNoiseScaling,
comfort_noise_[0].ceil_value),
0, 89, 45);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ComfortNoiseBand0.Min",
aec3::TransformDbMetricForReporting(true, 0.f, 89.f, -90.3f,
kComfortNoiseScaling,
comfort_noise_[0].floor_value),
0, 89, 45);
break;
case kMetricsCollectionBlocks + 6:
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ComfortNoiseBand1.Average",
aec3::TransformDbMetricForReporting(
true, 0.f, 89.f, -90.3f,
kComfortNoiseScaling * kOneByMetricsCollectionBlocks,
comfort_noise_[1].sum_value),
0, 89, 45);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ComfortNoiseBand1.Max",
aec3::TransformDbMetricForReporting(true, 0.f, 89.f, -90.3f,
kComfortNoiseScaling,
comfort_noise_[1].ceil_value),
0, 89, 45);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.ComfortNoiseBand1.Min",
aec3::TransformDbMetricForReporting(true, 0.f, 89.f, -90.3f,
kComfortNoiseScaling,
comfort_noise_[1].floor_value),
0, 89, 45);
break;
case kMetricsCollectionBlocks + 7:
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.SuppressorGainBand0.Average",
aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 0.f,
kOneByMetricsCollectionBlocks,
suppressor_gain_[0].sum_value),
0, 59, 30);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.SuppressorGainBand0.Max",
aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 0.f, 1.f,
suppressor_gain_[0].ceil_value),
0, 59, 30);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.SuppressorGainBand0.Min",
aec3::TransformDbMetricForReporting(
true, 0.f, 59.f, 0.f, 1.f, suppressor_gain_[0].floor_value),
0, 59, 30);
break;
case kMetricsCollectionBlocks + 8:
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.SuppressorGainBand1.Average",
aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 0.f,
kOneByMetricsCollectionBlocks,
suppressor_gain_[1].sum_value),
0, 59, 30);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.SuppressorGainBand1.Max",
aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 0.f, 1.f,
suppressor_gain_[1].ceil_value),
0, 59, 30);
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.SuppressorGainBand1.Min",
aec3::TransformDbMetricForReporting(
true, 0.f, 59.f, 0.f, 1.f, suppressor_gain_[1].floor_value),
0, 59, 30);
break;
case kMetricsCollectionBlocks + 9:
RTC_HISTOGRAM_BOOLEAN(
"WebRTC.Audio.EchoCanceller.UsableLinearEstimate",
static_cast<int>(aec_state.UsableLinearEstimate() ? 1 : 0));
RTC_HISTOGRAM_BOOLEAN(
"WebRTC.Audio.EchoCanceller.ModelBasedAecFeasible",
static_cast<int>(aec_state.ModelBasedAecFeasible() ? 1 : 0));
RTC_HISTOGRAM_BOOLEAN(
"WebRTC.Audio.EchoCanceller.ActiveRender",
static_cast<int>(
active_render_count_ > kMetricsCollectionBlocksBy2 ? 1 : 0));
RTC_HISTOGRAM_COUNTS_LINEAR(
"WebRTC.Audio.EchoCanceller.FilterDelay",
aec_state.FilterDelay() ? *aec_state.FilterDelay() + 1 : 0, 0, 30,
31);
RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.EchoCanceller.CaptureSaturation",
static_cast<int>(saturated_capture_ ? 1 : 0));
metrics_reported_ = true;
RTC_DCHECK_EQ(kMetricsReportingIntervalBlocks, block_counter_);
block_counter_ = 0;
ResetMetrics();
break;
default:
RTC_NOTREACHED();
break;
}
}
}
namespace aec3 {
void UpdateDbMetric(const std::array<float, kFftLengthBy2Plus1>& value,
std::array<EchoRemoverMetrics::DbMetric, 2>* statistic) {
RTC_DCHECK(statistic);
// Truncation is intended in the band width computation.
constexpr int kNumBands = 2;
constexpr int kBandWidth = 65 / kNumBands;
constexpr float kOneByBandWidth = 1.f / kBandWidth;
RTC_DCHECK_EQ(kNumBands, statistic->size());
RTC_DCHECK_EQ(65, value.size());
for (size_t k = 0; k < statistic->size(); ++k) {
float average_band =
std::accumulate(value.begin() + kBandWidth * k,
value.begin() + kBandWidth * (k + 1), 0.f) *
kOneByBandWidth;
(*statistic)[k].Update(average_band);
}
}
int TransformDbMetricForReporting(bool negate,
float min_value,
float max_value,
float offset,
float scaling,
float value) {
float new_value = 10.f * log10(value * scaling + 1e-10f) + offset;
if (negate) {
new_value = -new_value;
}
return static_cast<int>(std::max(min_value, std::min(max_value, new_value)));
}
} // namespace aec3
} // namespace webrtc

View File

@ -0,0 +1,77 @@
/*
* 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_PROCESSING_AEC3_ECHO_REMOVER_METRICS_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_METRICS_H_
#include "webrtc/base/constructormagic.h"
#include "webrtc/modules/audio_processing/aec3/aec_state.h"
namespace webrtc {
// Handles the reporting of metrics for the echo remover.
class EchoRemoverMetrics {
public:
struct DbMetric {
DbMetric();
DbMetric(float sum_value, float floor_value, float ceil_value);
void Update(float value);
float sum_value;
float floor_value;
float ceil_value;
};
EchoRemoverMetrics();
// Updates the metric with new data.
void Update(
const AecState& aec_state,
const std::array<float, kFftLengthBy2Plus1>& comfort_noise_spectrum,
const std::array<float, kFftLengthBy2Plus1>& suppressor_gain);
// Returns true if the metrics have just been reported, otherwise false.
bool MetricsReported() { return metrics_reported_; }
private:
// Resets the metrics.
void ResetMetrics();
int block_counter_ = 0;
std::array<DbMetric, 2> erl_;
std::array<DbMetric, 2> erle_;
std::array<DbMetric, 2> comfort_noise_;
std::array<DbMetric, 2> suppressor_gain_;
int active_render_count_ = 0;
bool saturated_capture_ = false;
bool metrics_reported_ = false;
RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverMetrics);
};
namespace aec3 {
// Updates a banded metric of type DbMetric with the values in the supplied
// array.
void UpdateDbMetric(const std::array<float, kFftLengthBy2Plus1>& value,
std::array<EchoRemoverMetrics::DbMetric, 2>* statistic);
// Transforms a DbMetric from the linear domain into the logarithmic domain.
int TransformDbMetricForReporting(bool negate,
float min_value,
float max_value,
float offset,
float scaling,
float value);
} // namespace aec3
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_METRICS_H_

View File

@ -0,0 +1,144 @@
/*
* 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_processing/aec3/echo_remover_metrics.h"
#include <math.h>
#include "webrtc/modules/audio_processing/aec3/aec_state.h"
#include "webrtc/modules/audio_processing/aec3/aec3_fft.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
// Verifies the check for non-null input.
TEST(UpdateDbMetric, NullValue) {
std::array<float, kFftLengthBy2Plus1> value;
value.fill(0.f);
EXPECT_DEATH(aec3::UpdateDbMetric(value, nullptr), "");
}
#endif
// Verifies the updating functionality of UpdateDbMetric.
TEST(UpdateDbMetric, Updating) {
std::array<float, kFftLengthBy2Plus1> value;
std::array<EchoRemoverMetrics::DbMetric, 2> statistic;
statistic.fill(EchoRemoverMetrics::DbMetric(0.f, 100.f, -100.f));
constexpr float kValue0 = 10.f;
constexpr float kValue1 = 20.f;
std::fill(value.begin(), value.begin() + 32, kValue0);
std::fill(value.begin() + 32, value.begin() + 64, kValue1);
aec3::UpdateDbMetric(value, &statistic);
EXPECT_FLOAT_EQ(kValue0, statistic[0].sum_value);
EXPECT_FLOAT_EQ(kValue0, statistic[0].ceil_value);
EXPECT_FLOAT_EQ(kValue0, statistic[0].floor_value);
EXPECT_FLOAT_EQ(kValue1, statistic[1].sum_value);
EXPECT_FLOAT_EQ(kValue1, statistic[1].ceil_value);
EXPECT_FLOAT_EQ(kValue1, statistic[1].floor_value);
aec3::UpdateDbMetric(value, &statistic);
EXPECT_FLOAT_EQ(2.f * kValue0, statistic[0].sum_value);
EXPECT_FLOAT_EQ(kValue0, statistic[0].ceil_value);
EXPECT_FLOAT_EQ(kValue0, statistic[0].floor_value);
EXPECT_FLOAT_EQ(2.f * kValue1, statistic[1].sum_value);
EXPECT_FLOAT_EQ(kValue1, statistic[1].ceil_value);
EXPECT_FLOAT_EQ(kValue1, statistic[1].floor_value);
}
// Verifies that the TransformDbMetricForReporting method produces the desired
// output for values for dBFS.
TEST(TransformDbMetricForReporting, DbFsScaling) {
std::array<float, kBlockSize> x;
FftData X;
std::array<float, kFftLengthBy2Plus1> X2;
Aec3Fft fft;
x.fill(1000.f);
fft.ZeroPaddedFft(x, &X);
X.Spectrum(Aec3Optimization::kNone, &X2);
float offset = -10.f * log10(32768.f * 32768.f);
EXPECT_NEAR(offset, -90.3f, 0.1f);
EXPECT_EQ(
static_cast<int>(30.3f),
aec3::TransformDbMetricForReporting(
true, 0.f, 90.f, offset, 1.f / (kBlockSize * kBlockSize), X2[0]));
}
// Verifies that the TransformDbMetricForReporting method is able to properly
// limit the output.
TEST(TransformDbMetricForReporting, Limits) {
EXPECT_EQ(
0,
aec3::TransformDbMetricForReporting(false, 0.f, 10.f, 0.f, 1.f, 0.001f));
EXPECT_EQ(
10,
aec3::TransformDbMetricForReporting(false, 0.f, 10.f, 0.f, 1.f, 100.f));
}
// Verifies that the TransformDbMetricForReporting method is able to properly
// negate output.
TEST(TransformDbMetricForReporting, Negate) {
EXPECT_EQ(
10,
aec3::TransformDbMetricForReporting(true, -20.f, 20.f, 0.f, 1.f, 0.1f));
EXPECT_EQ(
-10,
aec3::TransformDbMetricForReporting(true, -20.f, 20.f, 0.f, 1.f, 10.f));
}
// Verify the Update functionality of DbMetric.
TEST(DbMetric, Update) {
EchoRemoverMetrics::DbMetric metric(0.f, 20.f, -20.f);
constexpr int kNumValues = 100;
constexpr float kValue = 10.f;
for (int k = 0; k < kNumValues; ++k) {
metric.Update(kValue);
}
EXPECT_FLOAT_EQ(kValue * kNumValues, metric.sum_value);
EXPECT_FLOAT_EQ(kValue, metric.ceil_value);
EXPECT_FLOAT_EQ(kValue, metric.floor_value);
}
// Verify the constructor functionality of DbMetric.
TEST(DbMetric, Constructor) {
EchoRemoverMetrics::DbMetric metric;
EXPECT_FLOAT_EQ(0.f, metric.sum_value);
EXPECT_FLOAT_EQ(0.f, metric.ceil_value);
EXPECT_FLOAT_EQ(0.f, metric.floor_value);
metric = EchoRemoverMetrics::DbMetric(1.f, 2.f, 3.f);
EXPECT_FLOAT_EQ(1.f, metric.sum_value);
EXPECT_FLOAT_EQ(2.f, metric.floor_value);
EXPECT_FLOAT_EQ(3.f, metric.ceil_value);
}
// Verify the general functionality of EchoRemoverMetrics.
TEST(EchoRemoverMetrics, NormalUsage) {
EchoRemoverMetrics metrics;
AecState aec_state;
std::array<float, kFftLengthBy2Plus1> comfort_noise_spectrum;
std::array<float, kFftLengthBy2Plus1> suppressor_gain;
comfort_noise_spectrum.fill(10.f);
suppressor_gain.fill(1.f);
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < kMetricsReportingIntervalBlocks - 1; ++k) {
metrics.Update(aec_state, comfort_noise_spectrum, suppressor_gain);
EXPECT_FALSE(metrics.MetricsReported());
}
metrics.Update(aec_state, comfort_noise_spectrum, suppressor_gain);
EXPECT_TRUE(metrics.MetricsReported());
}
}
} // namespace webrtc

View File

@ -18,7 +18,7 @@
#include "webrtc/base/constructormagic.h"
#include "webrtc/modules/audio_processing/aec3/aec3_common.h"
#include "webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.h"
#include "webrtc/system_wrappers/include/logging.h"
#include "webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.h"
namespace webrtc {
@ -81,6 +81,7 @@ class RenderDelayControllerImpl final : public RenderDelayController {
int echo_path_delay_samples_ = 0;
size_t align_call_counter_ = 0;
rtc::Optional<size_t> headroom_samples_;
RenderDelayControllerMetrics metrics_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl);
};
@ -151,6 +152,8 @@ size_t RenderDelayControllerImpl::GetDelay(
headroom_samples_ = rtc::Optional<size_t>();
}
metrics_.Update(echo_path_delay_samples, delay_);
data_dumper_->DumpRaw("aec3_render_delay_controller_delay", 1,
&echo_path_delay_samples_);
data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay", delay_);

View File

@ -0,0 +1,117 @@
/*
* 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_processing/aec3/render_delay_controller_metrics.h"
#include <algorithm>
#include "webrtc/modules/audio_processing/aec3/aec3_common.h"
#include "webrtc/system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
enum class DelayReliabilityCategory {
kNone,
kPoor,
kMedium,
kGood,
kExcellent,
kNumCategories
};
enum class DelayChangesCategory {
kNone,
kFew,
kSeveral,
kMany,
kConstant,
kNumCategories
};
} // namespace
void RenderDelayControllerMetrics::Update(rtc::Optional<size_t> delay_samples,
size_t buffer_delay_blocks) {
++call_counter_;
if (!initial_update) {
if (delay_samples) {
++reliable_delay_estimate_counter_;
size_t delay_blocks = (*delay_samples) / kBlockSize;
if (delay_blocks != delay_blocks_) {
++delay_change_counter_;
delay_blocks_ = delay_blocks;
}
}
} else if (++initial_call_counter_ == 5 * 250) {
initial_update = false;
}
if (call_counter_ == kMetricsReportingIntervalBlocks) {
int value_to_report = static_cast<int>(delay_blocks_);
value_to_report = std::min(124, value_to_report);
RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.EchoCanceller.EchoPathDelay",
value_to_report, 0, 124, 125);
value_to_report = static_cast<int>(buffer_delay_blocks);
value_to_report = std::min(124, value_to_report);
RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.EchoCanceller.BufferDelay",
value_to_report, 0, 124, 125);
DelayReliabilityCategory delay_reliability;
if (reliable_delay_estimate_counter_ == 0) {
delay_reliability = DelayReliabilityCategory::kNone;
} else if (reliable_delay_estimate_counter_ > (call_counter_ >> 1)) {
delay_reliability = DelayReliabilityCategory::kExcellent;
} else if (reliable_delay_estimate_counter_ > 100) {
delay_reliability = DelayReliabilityCategory::kGood;
} else if (reliable_delay_estimate_counter_ > 10) {
delay_reliability = DelayReliabilityCategory::kMedium;
} else {
delay_reliability = DelayReliabilityCategory::kPoor;
}
RTC_HISTOGRAM_ENUMERATION(
"WebRTC.Audio.EchoCanceller.ReliableDelayEstimates",
static_cast<int>(delay_reliability),
static_cast<int>(DelayReliabilityCategory::kNumCategories));
DelayChangesCategory delay_changes;
if (delay_change_counter_ == 0) {
delay_changes = DelayChangesCategory::kNone;
} else if (delay_change_counter_ > 10) {
delay_changes = DelayChangesCategory::kConstant;
} else if (delay_change_counter_ > 5) {
delay_changes = DelayChangesCategory::kMany;
} else if (delay_change_counter_ > 2) {
delay_changes = DelayChangesCategory::kSeveral;
} else {
delay_changes = DelayChangesCategory::kFew;
}
RTC_HISTOGRAM_ENUMERATION(
"WebRTC.Audio.EchoCanceller.DelayChanges",
static_cast<int>(delay_changes),
static_cast<int>(DelayChangesCategory::kNumCategories));
metrics_reported_ = true;
call_counter_ = 0;
ResetMetrics();
} else {
metrics_reported_ = false;
}
}
void RenderDelayControllerMetrics::ResetMetrics() {
delay_change_counter_ = 0;
reliable_delay_estimate_counter_ = 0;
}
} // namespace webrtc

View File

@ -0,0 +1,47 @@
/*
* 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_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_METRICS_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_METRICS_H_
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/optional.h"
namespace webrtc {
// Handles the reporting of metrics for the render delay controller.
class RenderDelayControllerMetrics {
public:
RenderDelayControllerMetrics() = default;
// Updates the metric with new data.
void Update(rtc::Optional<size_t> delay_samples, size_t buffer_delay_blocks);
// Returns true if the metrics have just been reported, otherwise false.
bool MetricsReported() { return metrics_reported_; }
private:
// Resets the metrics.
void ResetMetrics();
size_t delay_blocks_ = 0;
int reliable_delay_estimate_counter_ = 0;
int delay_change_counter_ = 0;
int call_counter_ = 0;
int initial_call_counter_ = 0;
bool metrics_reported_ = false;
bool initial_update = true;
RTC_DISALLOW_COPY_AND_ASSIGN(RenderDelayControllerMetrics);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_METRICS_H_

View File

@ -0,0 +1,33 @@
/*
* 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/base/optional.h"
#include "webrtc/modules/audio_processing/aec3/aec3_common.h"
#include "webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
// Verify the general functionality of RenderDelayControllerMetrics.
TEST(RenderDelayControllerMetrics, NormalUsage) {
RenderDelayControllerMetrics metrics;
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < kMetricsReportingIntervalBlocks - 1; ++k) {
metrics.Update(rtc::Optional<size_t>(), 0);
EXPECT_FALSE(metrics.MetricsReported());
}
metrics.Update(rtc::Optional<size_t>(), 0);
EXPECT_TRUE(metrics.MetricsReported());
}
}
} // namespace webrtc