webrtc_m130/webrtc/modules/audio_processing/aec3/render_delay_controller.cc
peah 69221db534 Adding second layer of the echo canceller 3 functionality.
This CL adds code to the BlockProcessor, which basically constitutes
the second layer in echo canceller 3. The CL includes two incomplete
classes (EchoRemover and EchoPathDelayEstimator) which will be completed
in upcoming CLs. Because of this, some of the unittests are disabled
until those are added.

BUG=webrtc:6018

Review-Url: https://codereview.webrtc.org/2611223003
Cr-Commit-Position: refs/heads/master@{#16319}
2017-01-27 11:28:19 +00:00

176 lines
5.7 KiB
C++

/*
* 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