Corrections of the render buffering scheme in AEC3 to ensure causality

This CL modifies the refactored render buffering scheme in AEC3
so that:
-A non-causal state can never occur which means that situations with
 nonrecoverable echo should not occur.
-For a stable audio pipeline with a predefined API call jitter,
 render overruns and underruns can never occur.

Bug: webrtc:8629,chromium:793305
Change-Id: I06ba1c368f92db95274090b08475dd02dbb85145
Reviewed-on: https://webrtc-review.googlesource.com/29861
Commit-Queue: Per Åhgren <peah@webrtc.org>
Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21215}
diff --git a/modules/audio_processing/aec3/render_delay_controller.cc b/modules/audio_processing/aec3/render_delay_controller.cc
index 05121f2..e37207e 100644
--- a/modules/audio_processing/aec3/render_delay_controller.cc
+++ b/modules/audio_processing/aec3/render_delay_controller.cc
@@ -28,12 +28,13 @@
 class RenderDelayControllerImpl final : public RenderDelayController {
  public:
   RenderDelayControllerImpl(const EchoCanceller3Config& config,
+                            int non_causal_offset,
                             int sample_rate_hz);
   ~RenderDelayControllerImpl() override;
   void Reset() override;
   void SetDelay(size_t render_delay) override;
-  size_t GetDelay(const DownsampledRenderBuffer& render_buffer,
-                  rtc::ArrayView<const float> capture) override;
+  rtc::Optional<size_t> GetDelay(const DownsampledRenderBuffer& render_buffer,
+                                 rtc::ArrayView<const float> capture) override;
   rtc::Optional<size_t> AlignmentHeadroomSamples() const override {
     return headroom_samples_;
   }
@@ -41,32 +42,30 @@
  private:
   static int instance_count_;
   std::unique_ptr<ApmDataDumper> data_dumper_;
-  const size_t min_echo_path_delay_;
-  const size_t default_delay_;
-  size_t delay_;
+  rtc::Optional<size_t> delay_;
   EchoPathDelayEstimator delay_estimator_;
-  size_t blocks_since_last_delay_estimate_ = 300000;
-  int echo_path_delay_samples_;
   size_t align_call_counter_ = 0;
   rtc::Optional<size_t> headroom_samples_;
-  std::vector<float> capture_delay_buffer_;
-  int capture_delay_buffer_index_ = 0;
+  std::vector<float> delay_buf_;
+  int delay_buf_index_ = 0;
   RenderDelayControllerMetrics metrics_;
   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl);
 };
 
-size_t ComputeNewBufferDelay(size_t current_delay,
-                             size_t echo_path_delay_samples) {
+size_t ComputeNewBufferDelay(rtc::Optional<size_t> current_delay,
+                             size_t delay_samples) {
   // The below division is not exact and the truncation is intended.
-  const int echo_path_delay_blocks = echo_path_delay_samples / kBlockSize;
+  const int echo_path_delay_blocks = delay_samples >> kBlockSizeLog2;
   constexpr int kDelayHeadroomBlocks = 1;
 
   // Compute the buffer delay increase required to achieve the desired latency.
   size_t new_delay = std::max(echo_path_delay_blocks - kDelayHeadroomBlocks, 0);
 
   // Add hysteresis.
-  if (new_delay == current_delay + 1) {
-    new_delay = current_delay;
+  if (current_delay) {
+    if (new_delay == *current_delay + 1) {
+      new_delay = *current_delay;
+    }
   }
 
   return new_delay;
@@ -76,32 +75,24 @@
 
 RenderDelayControllerImpl::RenderDelayControllerImpl(
     const EchoCanceller3Config& config,
+    int non_causal_offset,
     int sample_rate_hz)
     : data_dumper_(
           new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
-      min_echo_path_delay_(config.delay.min_echo_path_delay_blocks),
-      default_delay_(
-          std::max(config.delay.default_delay, min_echo_path_delay_)),
-      delay_(default_delay_),
       delay_estimator_(data_dumper_.get(), config),
-      echo_path_delay_samples_(default_delay_ * kBlockSize),
-      capture_delay_buffer_(
-          kBlockSize * (config.delay.api_call_jitter_blocks + 2),
-          0.f) {
+      delay_buf_(kBlockSize * non_causal_offset, 0.f) {
   RTC_DCHECK(ValidFullBandRate(sample_rate_hz));
   delay_estimator_.LogDelayEstimationProperties(sample_rate_hz,
-                                                capture_delay_buffer_.size());
+                                                delay_buf_.size());
 }
 
 RenderDelayControllerImpl::~RenderDelayControllerImpl() = default;
 
 void RenderDelayControllerImpl::Reset() {
-  delay_ = default_delay_;
-  blocks_since_last_delay_estimate_ = 300000;
-  echo_path_delay_samples_ = delay_ * kBlockSize;
+  delay_ = rtc::nullopt;
   align_call_counter_ = 0;
   headroom_samples_ = rtc::nullopt;
-  std::fill(capture_delay_buffer_.begin(), capture_delay_buffer_.end(), 0.f);
+  std::fill(delay_buf_.begin(), delay_buf_.end(), 0.f);
   delay_estimator_.Reset();
 }
 
@@ -114,60 +105,43 @@
   }
 }
 
-size_t RenderDelayControllerImpl::GetDelay(
+rtc::Optional<size_t> RenderDelayControllerImpl::GetDelay(
     const DownsampledRenderBuffer& render_buffer,
     rtc::ArrayView<const float> capture) {
   RTC_DCHECK_EQ(kBlockSize, capture.size());
-
   ++align_call_counter_;
 
-  // 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));
+  // Estimate the delay with a delayed capture.
+  RTC_DCHECK_LT(delay_buf_index_ + kBlockSize - 1, delay_buf_.size());
+  rtc::ArrayView<const float> capture_delayed(&delay_buf_[delay_buf_index_],
+                                              kBlockSize);
+  auto delay_samples =
+      delay_estimator_.EstimateDelay(render_buffer, capture_delayed);
+
   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();
+            delay_buf_.begin() + delay_buf_index_);
+  delay_buf_index_ = (delay_buf_index_ + kBlockSize) % delay_buf_.size();
 
-  if (echo_path_delay_samples_shifted) {
-    blocks_since_last_delay_estimate_ = 0;
-
-    // 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);
-
+  if (delay_samples) {
     // Compute and set new render delay buffer delay.
-    const size_t new_delay =
-        ComputeNewBufferDelay(delay_, echo_path_delay_samples_);
     if (align_call_counter_ > kNumBlocksPerSecond) {
-      delay_ = new_delay;
-
+      delay_ = ComputeNewBufferDelay(delay_, static_cast<int>(*delay_samples));
       // Update render delay buffer headroom.
-      if (echo_path_delay_samples_corrected >= 0) {
-        const int headroom = echo_path_delay_samples_ - delay_ * kBlockSize;
-        RTC_DCHECK_LE(0, headroom);
-        headroom_samples_ = headroom;
-      } else {
-        headroom_samples_ = rtc::nullopt;
-      }
+      const int headroom =
+          static_cast<int>(*delay_samples) - *delay_ * kBlockSize;
+      RTC_DCHECK_LE(0, headroom);
+      headroom_samples_ = headroom;
     }
 
-    metrics_.Update(echo_path_delay_samples_, delay_);
+    metrics_.Update(static_cast<int>(*delay_samples), delay_ ? *delay_ : 0);
   } else {
-    metrics_.Update(rtc::nullopt, delay_);
+    metrics_.Update(rtc::nullopt, delay_ ? *delay_ : 0);
   }
 
-  data_dumper_->DumpRaw("aec3_render_delay_controller_delay", 1,
-                        &echo_path_delay_samples_);
-  data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay", delay_);
+  data_dumper_->DumpRaw("aec3_render_delay_controller_delay",
+                        delay_samples ? *delay_samples : 0);
+  data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay",
+                        delay_ ? *delay_ : 0);
 
   return delay_;
 }
@@ -176,8 +150,10 @@
 
 RenderDelayController* RenderDelayController::Create(
     const EchoCanceller3Config& config,
+    int non_causal_offset,
     int sample_rate_hz) {
-  return new RenderDelayControllerImpl(config, sample_rate_hz);
+  return new RenderDelayControllerImpl(config, non_causal_offset,
+                                       sample_rate_hz);
 }
 
 }  // namespace webrtc