Eliminating the risk of sustained echo during capture data loss in AEC3.
This CL adds an offset to the delay estimation used in AEC3 for
determining the alignment between the render and capture signals.
This ensures that there is no possibility for the capture loss to
cause the delay estimation to miss aligning the signals.
BUG=webrtc:8247, chromium:765242
Change-Id: I526dc7971b13425a28e99d69168fd3722a4cfdae
Reviewed-on: https://webrtc-review.googlesource.com/1232
Reviewed-by: Alex Loiko <aleloi@webrtc.org>
Commit-Queue: Per Åhgren <peah@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#19871}
diff --git a/modules/audio_processing/aec3/render_delay_controller.cc b/modules/audio_processing/aec3/render_delay_controller.cc
index e651da9..9b400ff 100644
--- a/modules/audio_processing/aec3/render_delay_controller.cc
+++ b/modules/audio_processing/aec3/render_delay_controller.cc
@@ -48,6 +48,8 @@
int echo_path_delay_samples_ = kMinEchoPathDelayBlocks * kBlockSize;
size_t align_call_counter_ = 0;
rtc::Optional<size_t> headroom_samples_;
+ std::vector<float> capture_delay_buffer_;
+ int capture_delay_buffer_index_ = 0;
RenderDelayControllerMetrics metrics_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl);
};
@@ -76,7 +78,8 @@
int sample_rate_hz)
: data_dumper_(
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
- delay_estimator_(data_dumper_.get(), config) {
+ delay_estimator_(data_dumper_.get(), config),
+ capture_delay_buffer_(kBlockSize * (kMaxApiCallsJitterBlocks + 2), 0.f) {
RTC_DCHECK(ValidFullBandRate(sample_rate_hz));
}
@@ -88,7 +91,7 @@
echo_path_delay_samples_ = delay_ * kBlockSize;
align_call_counter_ = 0;
headroom_samples_ = rtc::Optional<size_t>();
-
+ std::fill(capture_delay_buffer_.begin(), capture_delay_buffer_.end(), 0.f);
delay_estimator_.Reset();
}
@@ -107,11 +110,29 @@
RTC_DCHECK_EQ(kBlockSize, capture.size());
++align_call_counter_;
- rtc::Optional<size_t> echo_path_delay_samples =
- delay_estimator_.EstimateDelay(render_buffer, capture);
- if (echo_path_delay_samples) {
+
+ // Estimate the delay with a delayed capture signal in order to catch
+ // noncausal delays.
+ RTC_DCHECK_LT(capture_delay_buffer_index_ + kBlockSize - 1,
+ capture_delay_buffer_.size());
+ const rtc::Optional<size_t> echo_path_delay_samples_shifted =
+ delay_estimator_.EstimateDelay(
+ render_buffer,
+ rtc::ArrayView<const float>(
+ &capture_delay_buffer_[capture_delay_buffer_index_], kBlockSize));
+ std::copy(capture.begin(), capture.end(),
+ capture_delay_buffer_.begin() + capture_delay_buffer_index_);
+ capture_delay_buffer_index_ =
+ (capture_delay_buffer_index_ + kBlockSize) % capture_delay_buffer_.size();
+
+ if (echo_path_delay_samples_shifted) {
blocks_since_last_delay_estimate_ = 0;
- echo_path_delay_samples_ = *echo_path_delay_samples;
+
+ // Correct for the capture signal delay.
+ const int echo_path_delay_samples_corrected =
+ static_cast<int>(*echo_path_delay_samples_shifted) -
+ static_cast<int>(capture_delay_buffer_.size());
+ echo_path_delay_samples_ = std::max(0, echo_path_delay_samples_corrected);
// Compute and set new render delay buffer delay.
const size_t new_delay =
@@ -120,13 +141,19 @@
delay_ = new_delay;
// Update render delay buffer headroom.
- const int headroom = echo_path_delay_samples_ - delay_ * kBlockSize;
- RTC_DCHECK_LE(0, headroom);
- headroom_samples_ = rtc::Optional<size_t>(headroom);
+ if (echo_path_delay_samples_corrected >= 0) {
+ const int headroom = echo_path_delay_samples_ - delay_ * kBlockSize;
+ RTC_DCHECK_LE(0, headroom);
+ headroom_samples_ = rtc::Optional<size_t>(headroom);
+ } else {
+ headroom_samples_ = rtc::Optional<size_t>();
+ }
}
- }
- metrics_.Update(echo_path_delay_samples, delay_);
+ metrics_.Update(rtc::Optional<size_t>(echo_path_delay_samples_), delay_);
+ } else {
+ metrics_.Update(rtc::Optional<size_t>(), delay_);
+ }
data_dumper_->DumpRaw("aec3_render_delay_controller_delay", 1,
&echo_path_delay_samples_);