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}
This commit is contained in:
Per Åhgren
2017-12-11 21:34:19 +01:00
committed by Commit Bot
parent efc5fbd8e0
commit c59a576c86
34 changed files with 535 additions and 408 deletions

View File

@ -55,6 +55,7 @@ class BlockProcessorImpl final : public BlockProcessor {
std::unique_ptr<EchoRemover> echo_remover_;
BlockProcessorMetrics metrics_;
RenderDelayBuffer::BufferingEvent render_event_;
size_t capture_call_counter_ = 0;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorImpl);
};
@ -86,6 +87,9 @@ void BlockProcessorImpl::ProcessCapture(
RTC_DCHECK(capture_block);
RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), capture_block->size());
RTC_DCHECK_EQ(kBlockSize, (*capture_block)[0].size());
capture_call_counter_++;
data_dumper_->DumpRaw("aec3_processblock_call_order",
static_cast<int>(BlockProcessorApiCall::kCapture));
data_dumper_->DumpWav("aec3_processblock_capture_input", kBlockSize,
@ -111,30 +115,35 @@ void BlockProcessorImpl::ProcessCapture(
echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kBufferFlush;
delay_controller_->Reset();
render_buffer_->Reset();
RTC_LOG(LS_WARNING) << "Reset due to render buffer overrun.";
RTC_LOG(LS_WARNING) << "Reset due to render buffer overrun at block "
<< capture_call_counter_;
}
// Update the render buffers with any newly arrived render blocks and prepare
// the render buffers for reading the render data corresponding to the current
// capture block.
render_event_ = render_buffer_->PrepareCaptureCall();
render_event_ = render_buffer_->PrepareCaptureProcessing();
RTC_DCHECK(RenderDelayBuffer::BufferingEvent::kRenderOverrun !=
render_event_);
if (render_event_ == RenderDelayBuffer::BufferingEvent::kRenderUnderrun) {
echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kBufferReadjustment;
EchoPathVariability::DelayAdjustment::kDelayReset;
delay_controller_->Reset();
render_buffer_->Reset();
capture_properly_started_ = false;
render_properly_started_ = false;
RTC_LOG(LS_WARNING) << "Reset due to render buffer underrrun at block "
<< capture_call_counter_;
} else if (render_event_ == RenderDelayBuffer::BufferingEvent::kApiCallSkew) {
// There have been too many render calls in a row. Reset to avoid noncausal
// echo.
echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kDelayReset;
delay_controller_->Reset();
render_buffer_->Reset();
capture_properly_started_ = false;
render_properly_started_ = false;
RTC_LOG(LS_WARNING) << "Reset due to render buffer api skew at block "
<< capture_call_counter_;
}
data_dumper_->DumpWav("aec3_processblock_capture_input2", kBlockSize,
@ -143,30 +152,32 @@ void BlockProcessorImpl::ProcessCapture(
// Compute and and apply the render delay required to achieve proper signal
// alignment.
const size_t estimated_delay = delay_controller_->GetDelay(
rtc::Optional<size_t> estimated_delay = delay_controller_->GetDelay(
render_buffer_->GetDownsampledRenderBuffer(), (*capture_block)[0]);
const size_t new_delay =
std::min(render_buffer_->MaxDelay(), estimated_delay);
bool delay_change = render_buffer_->Delay() != new_delay;
if (delay_change && new_delay >= config_.delay.min_echo_path_delay_blocks) {
echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kNewDetectedDelay;
render_buffer_->SetDelay(new_delay);
RTC_DCHECK_EQ(render_buffer_->Delay(), new_delay);
delay_controller_->SetDelay(new_delay);
} else if (delay_change &&
new_delay < config_.delay.min_echo_path_delay_blocks) {
// A noncausal delay has been detected. This can only happen if there is
// clockdrift, an audio pipeline issue has occurred or the specified minimum
// delay is too short. Perform a full reset.
echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kDelayReset;
delay_controller_->Reset();
render_buffer_->Reset();
capture_properly_started_ = false;
render_properly_started_ = false;
RTC_LOG(LS_WARNING) << "Reset due to noncausal delay.";
if (estimated_delay) {
bool delay_change = render_buffer_->SetDelay(*estimated_delay);
if (delay_change) {
RTC_LOG(LS_WARNING) << "Delay changed to " << *estimated_delay
<< " at block " << capture_call_counter_;
if (render_buffer_->CausalDelay()) {
echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kNewDetectedDelay;
} else {
// A noncausal delay has been detected. This can only happen if there is
// clockdrift, an audio pipeline issue has occurred or the specified
// minimum delay is too short. Perform a full reset.
echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kDelayReset;
delay_controller_->Reset();
render_buffer_->Reset();
capture_properly_started_ = false;
render_properly_started_ = false;
RTC_LOG(LS_WARNING) << "Reset due to noncausal delay at block "
<< capture_call_counter_;
}
}
}
// Remove the echo from the capture signal.
@ -207,7 +218,8 @@ void BlockProcessorImpl::UpdateEchoLeakageStatus(bool leakage_detected) {
void BlockProcessorImpl::GetMetrics(EchoControl::Metrics* metrics) const {
echo_remover_->GetMetrics(metrics);
const int block_size_ms = sample_rate_hz_ == 8000 ? 8 : 4;
metrics->delay_ms = static_cast<int>(render_buffer_->Delay()) * block_size_ms;
rtc::Optional<size_t> delay = render_buffer_->Delay();
metrics->delay_ms = delay ? static_cast<int>(*delay) * block_size_ms : 0;
}
} // namespace
@ -217,7 +229,9 @@ BlockProcessor* BlockProcessor::Create(const EchoCanceller3Config& config,
std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create(config, NumBandsForRate(sample_rate_hz)));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, sample_rate_hz));
RenderDelayController::Create(
config, RenderDelayBuffer::DelayEstimatorOffset(config),
sample_rate_hz));
std::unique_ptr<EchoRemover> echo_remover(
EchoRemover::Create(config, sample_rate_hz));
return Create(config, sample_rate_hz, std::move(render_buffer),
@ -229,7 +243,9 @@ BlockProcessor* BlockProcessor::Create(
int sample_rate_hz,
std::unique_ptr<RenderDelayBuffer> render_buffer) {
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, sample_rate_hz));
RenderDelayController::Create(
config, RenderDelayBuffer::DelayEstimatorOffset(config),
sample_rate_hz));
std::unique_ptr<EchoRemover> echo_remover(
EchoRemover::Create(config, sample_rate_hz));
return Create(config, sample_rate_hz, std::move(render_buffer),