Index: webrtc/modules/audio_processing/aec3/render_delay_controller.cc |
diff --git a/webrtc/modules/audio_processing/aec3/render_delay_controller.cc b/webrtc/modules/audio_processing/aec3/render_delay_controller.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e18701e5ea3181e762bb1e808a056d732caf6df5 |
--- /dev/null |
+++ b/webrtc/modules/audio_processing/aec3/render_delay_controller.cc |
@@ -0,0 +1,175 @@ |
+/* |
+ * 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.h" |
+ |
+#include <algorithm> |
+#include <memory> |
+#include <string> |
+#include <vector> |
+ |
+#include "webrtc/base/atomicops.h" |
+#include "webrtc/base/constructormagic.h" |
+#include "webrtc/modules/audio_processing/aec3/aec3_constants.h" |
+#include "webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.h" |
+#include "webrtc/system_wrappers/include/logging.h" |
+ |
+namespace webrtc { |
+ |
+namespace { |
+ |
+class RenderBuffer { |
+ public: |
+ explicit RenderBuffer(size_t size) |
+ : buffer_(size, std::vector<float>(kBlockSize, 0.f)) {} |
+ ~RenderBuffer() = default; |
+ |
+ bool Insert(rtc::ArrayView<const float> v) { |
+ if (size_ >= buffer_.size() - 1) { |
+ return false; |
+ } |
+ |
+ last_insert_index_ = (last_insert_index_ + 1) % buffer_.size(); |
+ RTC_DCHECK_EQ(buffer_[last_insert_index_].size(), v.size()); |
+ |
+ buffer_[last_insert_index_].clear(); |
+ buffer_[last_insert_index_].insert(buffer_[last_insert_index_].begin(), |
+ v.begin(), v.end()); |
+ ++size_; |
+ return true; |
+ } |
+ rtc::ArrayView<const float> Get() { |
+ RTC_DCHECK_LT(0, size_); |
+ --size_; |
+ return buffer_[(last_insert_index_ - size_ + buffer_.size()) % |
+ buffer_.size()]; |
+ } |
+ |
+ size_t Size() { return size_; } |
+ |
+ private: |
+ std::vector<std::vector<float>> buffer_; |
+ size_t size_ = 0; |
+ int last_insert_index_ = 0; |
+}; |
+ |
+class RenderDelayControllerImpl final : public RenderDelayController { |
+ public: |
+ RenderDelayControllerImpl(int sample_rate_hz, |
+ const RenderDelayBuffer& render_delay_buffer); |
+ ~RenderDelayControllerImpl() override; |
+ size_t GetDelay(rtc::ArrayView<const float> capture) override; |
+ bool AnalyzeRender(rtc::ArrayView<const float> render) override; |
+ rtc::Optional<size_t> AlignmentHeadroomSamples() const override { |
+ return headroom_samples_; |
+ } |
+ |
+ private: |
+ static int instance_count_; |
+ std::unique_ptr<ApmDataDumper> data_dumper_; |
+ const size_t max_delay_; |
+ size_t delay_; |
+ RenderBuffer render_buffer_; |
+ EchoPathDelayEstimator delay_estimator_; |
+ size_t blocks_since_last_delay_estimate_ = 300000; |
+ int echo_path_delay_samples_ = 0; |
+ size_t align_call_counter_ = 0; |
+ rtc::Optional<size_t> headroom_samples_; |
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl); |
+}; |
+ |
+size_t ComputeNewBufferDelay(size_t current_delay, |
+ size_t max_delay, |
+ size_t echo_path_delay_samples) { |
+ // The below division is not exact and the truncation is intended. |
+ const int echo_path_delay_blocks = echo_path_delay_samples / kBlockSize; |
+ 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 + 1 == current_delay) { |
+ new_delay = current_delay; |
+ } |
+ |
+ // Limit the delay to what is possible. |
+ new_delay = std::min(new_delay, max_delay); |
+ |
+ return new_delay; |
+} |
+ |
+int RenderDelayControllerImpl::instance_count_ = 0; |
+ |
+RenderDelayControllerImpl::RenderDelayControllerImpl( |
+ int sample_rate_hz, |
+ const RenderDelayBuffer& render_delay_buffer) |
+ : data_dumper_( |
+ new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), |
+ max_delay_(render_delay_buffer.MaxDelay()), |
+ delay_(render_delay_buffer.Delay()), |
+ render_buffer_(render_delay_buffer.MaxApiJitter() + 1), |
+ delay_estimator_(data_dumper_.get(), sample_rate_hz) { |
+ RTC_DCHECK(sample_rate_hz == 8000 || sample_rate_hz == 16000 || |
+ sample_rate_hz == 32000 || sample_rate_hz == 48000); |
+} |
+ |
+RenderDelayControllerImpl::~RenderDelayControllerImpl() = default; |
+ |
+size_t RenderDelayControllerImpl::GetDelay( |
+ rtc::ArrayView<const float> capture) { |
+ RTC_DCHECK_EQ(kBlockSize, capture.size()); |
+ if (render_buffer_.Size() == 0) { |
+ return delay_; |
+ } |
+ |
+ ++align_call_counter_; |
+ rtc::ArrayView<const float> render = render_buffer_.Get(); |
+ rtc::Optional<size_t> echo_path_delay_samples = |
+ delay_estimator_.EstimateDelay(render, capture); |
+ if (echo_path_delay_samples) { |
+ echo_path_delay_samples_ = *echo_path_delay_samples; |
+ |
+ // Compute and set new render delay buffer delay. |
+ const size_t new_delay = |
+ ComputeNewBufferDelay(delay_, max_delay_, echo_path_delay_samples_); |
+ if (new_delay != delay_ && align_call_counter_ > 250) { |
+ delay_ = new_delay; |
+ } |
+ |
+ // Update render delay buffer headroom. |
+ blocks_since_last_delay_estimate_ = 0; |
+ const int headroom = echo_path_delay_samples_ - delay_ * kBlockSize; |
+ RTC_DCHECK_LE(0, headroom); |
+ headroom_samples_ = rtc::Optional<size_t>(headroom); |
+ } else if (++blocks_since_last_delay_estimate_ > 25000) { |
+ headroom_samples_ = rtc::Optional<size_t>(); |
+ } |
+ |
+ data_dumper_->DumpRaw("aec3_render_delay_controller_delay", 1, |
+ &echo_path_delay_samples_); |
+ data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay", delay_); |
+ |
+ return delay_; |
+} |
+ |
+bool RenderDelayControllerImpl::AnalyzeRender( |
+ rtc::ArrayView<const float> render) { |
+ return render_buffer_.Insert(render); |
+} |
+ |
+} // namespace |
+ |
+RenderDelayController* RenderDelayController::Create( |
+ int sample_rate_hz, |
+ const RenderDelayBuffer& render_delay_buffer) { |
+ return new RenderDelayControllerImpl(sample_rate_hz, render_delay_buffer); |
+} |
+ |
+} // namespace webrtc |