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}
174 lines
6.1 KiB
C++
174 lines
6.1 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 "modules/audio_processing/aec3/render_delay_controller.h"
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "modules/audio_processing/aec3/aec3_common.h"
|
|
#include "modules/audio_processing/aec3/echo_path_delay_estimator.h"
|
|
#include "modules/audio_processing/aec3/render_delay_controller_metrics.h"
|
|
#include "modules/audio_processing/include/audio_processing.h"
|
|
#include "rtc_base/atomicops.h"
|
|
#include "rtc_base/constructormagic.h"
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
|
|
class RenderDelayControllerImpl final : public RenderDelayController {
|
|
public:
|
|
RenderDelayControllerImpl(
|
|
const AudioProcessing::Config::EchoCanceller3& config,
|
|
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> AlignmentHeadroomSamples() const override {
|
|
return headroom_samples_;
|
|
}
|
|
|
|
private:
|
|
static int instance_count_;
|
|
std::unique_ptr<ApmDataDumper> data_dumper_;
|
|
size_t delay_ = kMinEchoPathDelayBlocks;
|
|
EchoPathDelayEstimator delay_estimator_;
|
|
size_t blocks_since_last_delay_estimate_ = 300000;
|
|
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);
|
|
};
|
|
|
|
size_t ComputeNewBufferDelay(size_t current_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 = current_delay;
|
|
}
|
|
|
|
return new_delay;
|
|
}
|
|
|
|
int RenderDelayControllerImpl::instance_count_ = 0;
|
|
|
|
RenderDelayControllerImpl::RenderDelayControllerImpl(
|
|
const AudioProcessing::Config::EchoCanceller3& config,
|
|
int sample_rate_hz)
|
|
: data_dumper_(
|
|
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
|
|
delay_estimator_(data_dumper_.get(), config),
|
|
capture_delay_buffer_(kBlockSize * (kMaxApiCallsJitterBlocks + 2), 0.f) {
|
|
RTC_DCHECK(ValidFullBandRate(sample_rate_hz));
|
|
}
|
|
|
|
RenderDelayControllerImpl::~RenderDelayControllerImpl() = default;
|
|
|
|
void RenderDelayControllerImpl::Reset() {
|
|
delay_ = kMinEchoPathDelayBlocks;
|
|
blocks_since_last_delay_estimate_ = 300000;
|
|
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();
|
|
}
|
|
|
|
void RenderDelayControllerImpl::SetDelay(size_t render_delay) {
|
|
if (delay_ != render_delay) {
|
|
// If a the delay set does not match the actual delay, reset the delay
|
|
// controller.
|
|
Reset();
|
|
delay_ = render_delay;
|
|
}
|
|
}
|
|
|
|
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));
|
|
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;
|
|
|
|
// 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 =
|
|
ComputeNewBufferDelay(delay_, echo_path_delay_samples_);
|
|
if (align_call_counter_ > kNumBlocksPerSecond) {
|
|
delay_ = new_delay;
|
|
|
|
// 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_ = rtc::Optional<size_t>(headroom);
|
|
} else {
|
|
headroom_samples_ = rtc::Optional<size_t>();
|
|
}
|
|
}
|
|
|
|
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_);
|
|
data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay", delay_);
|
|
|
|
return delay_;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
RenderDelayController* RenderDelayController::Create(
|
|
const AudioProcessing::Config::EchoCanceller3& config,
|
|
int sample_rate_hz) {
|
|
return new RenderDelayControllerImpl(config, sample_rate_hz);
|
|
}
|
|
|
|
} // namespace webrtc
|