Added a new echo likelihood stat that reports the maximum value from a previous time period.
BUG=webrtc:6797
Review-Url: https://codereview.webrtc.org/2629563003
Cr-Commit-Position: refs/heads/master@{#16079}
diff --git a/webrtc/api/mediastreaminterface.h b/webrtc/api/mediastreaminterface.h
index 693e5d0..a52f0c7 100644
--- a/webrtc/api/mediastreaminterface.h
+++ b/webrtc/api/mediastreaminterface.h
@@ -203,14 +203,16 @@
class AudioProcessorInterface : public rtc::RefCountInterface {
public:
struct AudioProcessorStats {
- AudioProcessorStats() : typing_noise_detected(false),
- echo_return_loss(0),
- echo_return_loss_enhancement(0),
- echo_delay_median_ms(0),
- echo_delay_std_ms(0),
- aec_quality_min(0.0),
- residual_echo_likelihood(0.0f),
- aec_divergent_filter_fraction(0.0) {}
+ AudioProcessorStats()
+ : typing_noise_detected(false),
+ echo_return_loss(0),
+ echo_return_loss_enhancement(0),
+ echo_delay_median_ms(0),
+ echo_delay_std_ms(0),
+ aec_quality_min(0.0),
+ residual_echo_likelihood(0.0f),
+ residual_echo_likelihood_recent_max(0.0f),
+ aec_divergent_filter_fraction(0.0) {}
~AudioProcessorStats() {}
bool typing_noise_detected;
@@ -220,6 +222,7 @@
int echo_delay_std_ms;
float aec_quality_min;
float residual_echo_likelihood;
+ float residual_echo_likelihood_recent_max;
float aec_divergent_filter_fraction;
};
diff --git a/webrtc/api/statscollector.cc b/webrtc/api/statscollector.cc
index 8347424..dd5673e 100644
--- a/webrtc/api/statscollector.cc
+++ b/webrtc/api/statscollector.cc
@@ -104,7 +104,8 @@
int echo_delay_median_ms,
float aec_quality_min,
int echo_delay_std_ms,
- float residual_echo_likelihood) {
+ float residual_echo_likelihood,
+ float residual_echo_likelihood_recent_max) {
report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
typing_noise_detected);
if (aec_quality_min >= 0.0f) {
@@ -127,6 +128,9 @@
if (residual_echo_likelihood >= 0.0f) {
report->AddFloat(StatsReport::kStatsValueNameResidualEchoLikelihood,
residual_echo_likelihood);
+ report->AddFloat(
+ StatsReport::kStatsValueNameResidualEchoLikelihoodRecentMax,
+ residual_echo_likelihood_recent_max);
}
}
@@ -187,7 +191,7 @@
report, info.typing_noise_detected, info.echo_return_loss,
info.echo_return_loss_enhancement, info.echo_delay_median_ms,
info.aec_quality_min, info.echo_delay_std_ms,
- info.residual_echo_likelihood);
+ info.residual_echo_likelihood, info.residual_echo_likelihood_recent_max);
RTC_DCHECK_GE(info.audio_level, 0);
const IntForAdd ints[] = {
@@ -940,7 +944,8 @@
report, stats.typing_noise_detected, stats.echo_return_loss,
stats.echo_return_loss_enhancement, stats.echo_delay_median_ms,
stats.aec_quality_min, stats.echo_delay_std_ms,
- stats.residual_echo_likelihood);
+ stats.residual_echo_likelihood,
+ stats.residual_echo_likelihood_recent_max);
report->AddFloat(StatsReport::kStatsValueNameAecDivergentFilterFraction,
stats.aec_divergent_filter_fraction);
diff --git a/webrtc/api/statscollector_unittest.cc b/webrtc/api/statscollector_unittest.cc
index 5a0614e..0f0c0fc 100644
--- a/webrtc/api/statscollector_unittest.cc
+++ b/webrtc/api/statscollector_unittest.cc
@@ -414,6 +414,11 @@
&value_in_report));
EXPECT_EQ(rtc::ToString<float>(sinfo.residual_echo_likelihood),
value_in_report);
+ EXPECT_TRUE(GetValue(
+ report, StatsReport::kStatsValueNameResidualEchoLikelihoodRecentMax,
+ &value_in_report));
+ EXPECT_EQ(rtc::ToString<float>(sinfo.residual_echo_likelihood_recent_max),
+ value_in_report);
EXPECT_TRUE(GetValue(report, StatsReport::kStatsValueNameAudioInputLevel,
&value_in_report));
EXPECT_EQ(rtc::ToString<int>(sinfo.audio_level), value_in_report);
diff --git a/webrtc/api/statstypes.cc b/webrtc/api/statstypes.cc
index 0b3e38e..80b0d76 100644
--- a/webrtc/api/statstypes.cc
+++ b/webrtc/api/statstypes.cc
@@ -576,6 +576,8 @@
return "remoteCertificateId";
case kStatsValueNameResidualEchoLikelihood:
return "googResidualEchoLikelihood";
+ case kStatsValueNameResidualEchoLikelihoodRecentMax:
+ return "googResidualEchoLikelihoodRecentMax";
case kStatsValueNameRetransmitBitrate:
return "googRetransmitBitrate";
case kStatsValueNameRtt:
diff --git a/webrtc/api/statstypes.h b/webrtc/api/statstypes.h
index e30760b..1a55145 100644
--- a/webrtc/api/statstypes.h
+++ b/webrtc/api/statstypes.h
@@ -202,6 +202,7 @@
kStatsValueNameRemoteCertificateId,
kStatsValueNameRenderDelayMs,
kStatsValueNameResidualEchoLikelihood,
+ kStatsValueNameResidualEchoLikelihoodRecentMax,
kStatsValueNameRetransmitBitrate,
kStatsValueNameRtt,
kStatsValueNameSecondaryDecodedRate,
diff --git a/webrtc/audio/audio_send_stream.cc b/webrtc/audio/audio_send_stream.cc
index aff605f..3831b8d 100644
--- a/webrtc/audio/audio_send_stream.cc
+++ b/webrtc/audio/audio_send_stream.cc
@@ -210,6 +210,8 @@
audio_processing_stats.echo_return_loss_enhancement.instant();
stats.residual_echo_likelihood =
audio_processing_stats.residual_echo_likelihood;
+ stats.residual_echo_likelihood_recent_max =
+ audio_processing_stats.residual_echo_likelihood_recent_max;
internal::AudioState* audio_state =
static_cast<internal::AudioState*>(audio_state_.get());
diff --git a/webrtc/call/audio_send_stream.h b/webrtc/call/audio_send_stream.h
index 2063589..4291430 100644
--- a/webrtc/call/audio_send_stream.h
+++ b/webrtc/call/audio_send_stream.h
@@ -52,6 +52,7 @@
int32_t echo_return_loss = -100;
int32_t echo_return_loss_enhancement = -100;
float residual_echo_likelihood = -1.0f;
+ float residual_echo_likelihood_recent_max = -1.0f;
bool typing_noise_detected = false;
};
diff --git a/webrtc/media/base/mediachannel.h b/webrtc/media/base/mediachannel.h
index bf17606..70d6268 100644
--- a/webrtc/media/base/mediachannel.h
+++ b/webrtc/media/base/mediachannel.h
@@ -632,8 +632,8 @@
echo_return_loss(0),
echo_return_loss_enhancement(0),
residual_echo_likelihood(0.0f),
- typing_noise_detected(false) {
- }
+ residual_echo_likelihood_recent_max(0.0f),
+ typing_noise_detected(false) {}
int ext_seqnum;
int jitter_ms;
@@ -644,6 +644,7 @@
int echo_return_loss;
int echo_return_loss_enhancement;
float residual_echo_likelihood;
+ float residual_echo_likelihood_recent_max;
bool typing_noise_detected;
};
diff --git a/webrtc/media/engine/webrtcvoiceengine.cc b/webrtc/media/engine/webrtcvoiceengine.cc
index 2ae2514..e6fa12e 100644
--- a/webrtc/media/engine/webrtcvoiceengine.cc
+++ b/webrtc/media/engine/webrtcvoiceengine.cc
@@ -2615,6 +2615,8 @@
sinfo.echo_return_loss = stats.echo_return_loss;
sinfo.echo_return_loss_enhancement = stats.echo_return_loss_enhancement;
sinfo.residual_echo_likelihood = stats.residual_echo_likelihood;
+ sinfo.residual_echo_likelihood_recent_max =
+ stats.residual_echo_likelihood_recent_max;
sinfo.typing_noise_detected = (send_ ? stats.typing_noise_detected : false);
info->senders.push_back(sinfo);
}
diff --git a/webrtc/media/engine/webrtcvoiceengine_unittest.cc b/webrtc/media/engine/webrtcvoiceengine_unittest.cc
index 8ad5d97..f15bde6 100644
--- a/webrtc/media/engine/webrtcvoiceengine_unittest.cc
+++ b/webrtc/media/engine/webrtcvoiceengine_unittest.cc
@@ -480,6 +480,7 @@
stats.echo_return_loss = 890;
stats.echo_return_loss_enhancement = 1234;
stats.residual_echo_likelihood = 0.432f;
+ stats.residual_echo_likelihood_recent_max = 0.6f;
stats.typing_noise_detected = true;
return stats;
}
@@ -509,6 +510,8 @@
EXPECT_EQ(info.echo_return_loss_enhancement,
stats.echo_return_loss_enhancement);
EXPECT_EQ(info.residual_echo_likelihood, stats.residual_echo_likelihood);
+ EXPECT_EQ(info.residual_echo_likelihood_recent_max,
+ stats.residual_echo_likelihood_recent_max);
EXPECT_EQ(info.typing_noise_detected,
stats.typing_noise_detected && is_sending);
}
diff --git a/webrtc/modules/BUILD.gn b/webrtc/modules/BUILD.gn
index f3b2ac9..ff059ac 100644
--- a/webrtc/modules/BUILD.gn
+++ b/webrtc/modules/BUILD.gn
@@ -606,6 +606,7 @@
"audio_processing/echo_control_mobile_unittest.cc",
"audio_processing/echo_detector/circular_buffer_unittest.cc",
"audio_processing/echo_detector/mean_variance_estimator_unittest.cc",
+ "audio_processing/echo_detector/moving_max_unittest.cc",
"audio_processing/echo_detector/normalized_covariance_estimator_unittest.cc",
"audio_processing/gain_control_unittest.cc",
"audio_processing/level_controller/level_controller_unittest.cc",
diff --git a/webrtc/modules/audio_processing/BUILD.gn b/webrtc/modules/audio_processing/BUILD.gn
index 5cbd97d..6c55ac8 100644
--- a/webrtc/modules/audio_processing/BUILD.gn
+++ b/webrtc/modules/audio_processing/BUILD.gn
@@ -71,6 +71,8 @@
"echo_detector/circular_buffer.h",
"echo_detector/mean_variance_estimator.cc",
"echo_detector/mean_variance_estimator.h",
+ "echo_detector/moving_max.cc",
+ "echo_detector/moving_max.h",
"echo_detector/normalized_covariance_estimator.cc",
"echo_detector/normalized_covariance_estimator.h",
"gain_control_for_experimental_agc.cc",
diff --git a/webrtc/modules/audio_processing/audio_processing_impl.cc b/webrtc/modules/audio_processing/audio_processing_impl.cc
index 3a8f7f5..c253775 100644
--- a/webrtc/modules/audio_processing/audio_processing_impl.cc
+++ b/webrtc/modules/audio_processing/audio_processing_impl.cc
@@ -1585,6 +1585,19 @@
#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP
}
+AudioProcessing::AudioProcessingStatistics::AudioProcessingStatistics() {
+ residual_echo_return_loss.Set(-100.0f, -100.0f, -100.0f, -100.0f);
+ echo_return_loss.Set(-100.0f, -100.0f, -100.0f, -100.0f);
+ echo_return_loss_enhancement.Set(-100.0f, -100.0f, -100.0f, -100.0f);
+ a_nlp.Set(-100.0f, -100.0f, -100.0f, -100.0f);
+}
+
+AudioProcessing::AudioProcessingStatistics::AudioProcessingStatistics(
+ const AudioProcessingStatistics& other) = default;
+
+AudioProcessing::AudioProcessingStatistics::~AudioProcessingStatistics() =
+ default;
+
// TODO(ivoc): Remove this when GetStatistics() becomes pure virtual.
AudioProcessing::AudioProcessingStatistics AudioProcessing::GetStatistics()
const {
@@ -1606,6 +1619,8 @@
}
stats.residual_echo_likelihood =
private_submodules_->residual_echo_detector->echo_likelihood();
+ stats.residual_echo_likelihood_recent_max =
+ private_submodules_->residual_echo_detector->echo_likelihood_recent_max();
public_submodules_->echo_cancellation->GetDelayMetrics(
&stats.delay_median, &stats.delay_standard_deviation,
&stats.fraction_poor_delays);
diff --git a/webrtc/modules/audio_processing/echo_detector/moving_max.cc b/webrtc/modules/audio_processing/echo_detector/moving_max.cc
new file mode 100644
index 0000000..699a025
--- /dev/null
+++ b/webrtc/modules/audio_processing/echo_detector/moving_max.cc
@@ -0,0 +1,52 @@
+/*
+ * 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/echo_detector/moving_max.h"
+
+#include "webrtc/base/checks.h"
+
+namespace webrtc {
+namespace {
+
+// Parameter for controlling how fast the estimated maximum decays after the
+// previous maximum is no longer valid. With a value of 0.99, the maximum will
+// decay to 1% of its former value after 460 updates.
+constexpr float kDecayFactor = 0.99f;
+
+} // namespace
+
+MovingMax::MovingMax(size_t window_size) : window_size_(window_size) {
+ RTC_DCHECK_GT(window_size, 0);
+}
+
+MovingMax::~MovingMax() {}
+
+void MovingMax::Update(float value) {
+ if (counter_ >= window_size_ - 1) {
+ max_value_ *= kDecayFactor;
+ } else {
+ ++counter_;
+ }
+ if (value > max_value_) {
+ max_value_ = value;
+ counter_ = 0;
+ }
+}
+
+float MovingMax::max() const {
+ return max_value_;
+}
+
+void MovingMax::Clear() {
+ max_value_ = 0.f;
+ counter_ = 0;
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/audio_processing/echo_detector/moving_max.h b/webrtc/modules/audio_processing/echo_detector/moving_max.h
new file mode 100644
index 0000000..556facf
--- /dev/null
+++ b/webrtc/modules/audio_processing/echo_detector/moving_max.h
@@ -0,0 +1,36 @@
+/*
+ * 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_ECHO_DETECTOR_MOVING_MAX_H_
+#define WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MOVING_MAX_H_
+
+#include <stddef.h>
+
+namespace webrtc {
+
+class MovingMax {
+ public:
+ explicit MovingMax(size_t window_size);
+ ~MovingMax();
+
+ void Update(float value);
+ float max() const;
+ // Reset all of the state in this class.
+ void Clear();
+
+ private:
+ float max_value_ = 0.f;
+ size_t counter_ = 0;
+ size_t window_size_ = 1;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MOVING_MAX_H_
diff --git a/webrtc/modules/audio_processing/echo_detector/moving_max_unittest.cc b/webrtc/modules/audio_processing/echo_detector/moving_max_unittest.cc
new file mode 100644
index 0000000..720010d
--- /dev/null
+++ b/webrtc/modules/audio_processing/echo_detector/moving_max_unittest.cc
@@ -0,0 +1,67 @@
+/*
+ * 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/echo_detector/moving_max.h"
+#include "webrtc/test/gtest.h"
+
+namespace webrtc {
+
+// Test if the maximum is correctly found.
+TEST(MovingMaxTests, SimpleTest) {
+ MovingMax test_moving_max(5);
+ test_moving_max.Update(1.0f);
+ test_moving_max.Update(1.1f);
+ test_moving_max.Update(1.9f);
+ test_moving_max.Update(1.87f);
+ test_moving_max.Update(1.89f);
+ EXPECT_EQ(1.9f, test_moving_max.max());
+}
+
+// Test if values fall out of the window when expected.
+TEST(MovingMaxTests, SlidingWindowTest) {
+ MovingMax test_moving_max(5);
+ test_moving_max.Update(1.0f);
+ test_moving_max.Update(1.9f);
+ test_moving_max.Update(1.7f);
+ test_moving_max.Update(1.87f);
+ test_moving_max.Update(1.89f);
+ test_moving_max.Update(1.3f);
+ test_moving_max.Update(1.2f);
+ EXPECT_LT(test_moving_max.max(), 1.9f);
+}
+
+// Test if Clear() works as expected.
+TEST(MovingMaxTests, ClearTest) {
+ MovingMax test_moving_max(5);
+ test_moving_max.Update(1.0f);
+ test_moving_max.Update(1.1f);
+ test_moving_max.Update(1.9f);
+ test_moving_max.Update(1.87f);
+ test_moving_max.Update(1.89f);
+ EXPECT_EQ(1.9f, test_moving_max.max());
+ test_moving_max.Clear();
+ EXPECT_EQ(0.f, test_moving_max.max());
+}
+
+// Test the decay of the estimated maximum.
+TEST(MovingMaxTests, DecayTest) {
+ MovingMax test_moving_max(1);
+ test_moving_max.Update(1.0f);
+ float previous_value = 1.0f;
+ for (int i = 0; i < 500; i++) {
+ test_moving_max.Update(0.0f);
+ EXPECT_LT(test_moving_max.max(), previous_value);
+ EXPECT_GT(test_moving_max.max(), 0.0f);
+ previous_value = test_moving_max.max();
+ }
+ EXPECT_LT(test_moving_max.max(), 0.01f);
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/audio_processing/include/audio_processing.h b/webrtc/modules/audio_processing/include/audio_processing.h
index 1b93cc0..ab924b7 100644
--- a/webrtc/modules/audio_processing/include/audio_processing.h
+++ b/webrtc/modules/audio_processing/include/audio_processing.h
@@ -510,12 +510,9 @@
};
struct AudioProcessingStatistics {
- AudioProcessingStatistics() {
- residual_echo_return_loss.Set(-100.0f, -100.0f, -100.0f, -100.0f);
- echo_return_loss.Set(-100.0f, -100.0f, -100.0f, -100.0f);
- echo_return_loss_enhancement.Set(-100.0f, -100.0f, -100.0f, -100.0f);
- a_nlp.Set(-100.0f, -100.0f, -100.0f, -100.0f);
- }
+ AudioProcessingStatistics();
+ AudioProcessingStatistics(const AudioProcessingStatistics& other);
+ ~AudioProcessingStatistics();
// AEC Statistics.
// RERL = ERL + ERLE
@@ -541,10 +538,10 @@
int delay_standard_deviation = -1;
float fraction_poor_delays = -1.0f;
- // Residual echo detector likelihood. This value is not yet calculated and
- // is currently always set to zero.
- // TODO(ivoc): Implement this stat.
+ // Residual echo detector likelihood.
float residual_echo_likelihood = -1.0f;
+ // Maximum residual echo likelihood from the last time period.
+ float residual_echo_likelihood_recent_max = -1.0f;
};
// TODO(ivoc): Make this pure virtual when all subclasses have been updated.
diff --git a/webrtc/modules/audio_processing/residual_echo_detector.cc b/webrtc/modules/audio_processing/residual_echo_detector.cc
index 45ef180..78b673e 100644
--- a/webrtc/modules/audio_processing/residual_echo_detector.cc
+++ b/webrtc/modules/audio_processing/residual_echo_detector.cc
@@ -26,6 +26,8 @@
// TODO(ivoc): Verify the size of this buffer.
constexpr size_t kRenderBufferSize = 30;
constexpr float kAlpha = 0.001f;
+// 10 seconds of data, updated every 10 ms.
+constexpr size_t kAggregationBufferSize = 10 * 100;
} // namespace
@@ -36,7 +38,8 @@
render_power_(kLookbackFrames),
render_power_mean_(kLookbackFrames),
render_power_std_dev_(kLookbackFrames),
- covariances_(kLookbackFrames){};
+ covariances_(kLookbackFrames),
+ recent_likelihood_max_(kAggregationBufferSize) {}
ResidualEchoDetector::~ResidualEchoDetector() = default;
@@ -107,6 +110,9 @@
RTC_HISTOGRAM_COUNTS("WebRTC.Audio.ResidualEchoDetector.EchoLikelihood",
echo_percentage, 0, 100, 100 /* number of bins */);
+ // Update the buffer of recent likelihood values.
+ recent_likelihood_max_.Update(echo_likelihood_);
+
// Update the next insertion index.
++next_insertion_index_;
next_insertion_index_ %= kLookbackFrames;
@@ -119,6 +125,7 @@
std::fill(render_power_std_dev_.begin(), render_power_std_dev_.end(), 0.f);
render_statistics_.Clear();
capture_statistics_.Clear();
+ recent_likelihood_max_.Clear();
for (auto& cov : covariances_) {
cov.Clear();
}
diff --git a/webrtc/modules/audio_processing/residual_echo_detector.h b/webrtc/modules/audio_processing/residual_echo_detector.h
index e8f51cd..ba0d0d3c 100644
--- a/webrtc/modules/audio_processing/residual_echo_detector.h
+++ b/webrtc/modules/audio_processing/residual_echo_detector.h
@@ -16,6 +16,7 @@
#include "webrtc/base/array_view.h"
#include "webrtc/modules/audio_processing/echo_detector/circular_buffer.h"
#include "webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.h"
+#include "webrtc/modules/audio_processing/echo_detector/moving_max.h"
#include "webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.h"
namespace webrtc {
@@ -46,6 +47,10 @@
// This function should be called while holding the capture lock.
float echo_likelihood() const { return echo_likelihood_; }
+ float echo_likelihood_recent_max() const {
+ return recent_likelihood_max_.max();
+ }
+
private:
// Keep track if the |Process| function has been previously called.
bool first_process_call_ = true;
@@ -76,6 +81,7 @@
float echo_likelihood_ = 0.f;
// Reliability of the current likelihood.
float reliability_ = 0.f;
+ MovingMax recent_likelihood_max_;
};
} // namespace webrtc