Files
platform-external-webrtc/webrtc/modules/audio_processing/aec3/render_delay_controller.cc
peah 219208991b Adding full initial version of delay estimation functionality in echo
canceller 3

This CL adds code to the all the delay estimation functionality that is
available for the first version of echo canceller 3. The code completes
the class EchoPathDelayEstimator.

Note that this code does not yet include any handling of clock-drift so
there will be upcoming versions of this code.

Also note that the CL includes some minor changes in other files for
echo canceller 3.

BUG=webrtc:6018

Review-Url: https://codereview.webrtc.org/2644123002
Cr-Commit-Position: refs/heads/master@{#16489}
2017-02-08 13:08:56 +00:00

175 lines
5.6 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()) {
RTC_DCHECK(ValidFullBandRate(sample_rate_hz));
}
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