AEC3: Fix render delay buffer alignment issue at call start
Internal counters in the RenderDelayBuffer can slip out of sync with external counters, leading to buffer misalignment. This CL gives the RenderDelayBuffer an opportunity to update its counters. Tested: Passes: modules_unittests --gtest_filter=BlockProcessor.* Fails as expected due to new unit test: modules_unittests --gtest_filter=BlockProcessor.* --force_fieldtrials="WebRTC-Aec3RenderBufferCallCounterUpdateKillSwitch/Enabled/" audioproc_f with default AEC settings has been verified to be bit-exact on a large number of aecdumps. Bug: webrtc:11803 Change-Id: I9363b834c8c8c934add0335013df60bf131da4bc Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/180126 Reviewed-by: Per Åhgren <peah@webrtc.org> Commit-Queue: Sam Zackrisson <saza@webrtc.org> Cr-Commit-Position: refs/heads/master@{#31795}
This commit is contained in:
parent
49c293f03d
commit
ff571c60a9
@ -128,6 +128,7 @@ void BlockProcessorImpl::ProcessCapture(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If no render data has yet arrived, do not process the capture signal.
|
// If no render data has yet arrived, do not process the capture signal.
|
||||||
|
render_buffer_->HandleSkippedCaptureProcessing();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,7 @@ namespace {
|
|||||||
|
|
||||||
using ::testing::_;
|
using ::testing::_;
|
||||||
using ::testing::AtLeast;
|
using ::testing::AtLeast;
|
||||||
|
using ::testing::NiceMock;
|
||||||
using ::testing::Return;
|
using ::testing::Return;
|
||||||
using ::testing::StrictMock;
|
using ::testing::StrictMock;
|
||||||
|
|
||||||
@ -129,6 +130,14 @@ std::string ProduceDebugText(int sample_rate_hz) {
|
|||||||
return ss.Release();
|
return ss.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FillSampleVector(int call_counter,
|
||||||
|
int delay,
|
||||||
|
rtc::ArrayView<float> samples) {
|
||||||
|
for (size_t i = 0; i < samples.size(); ++i) {
|
||||||
|
samples[i] = (call_counter - delay) * 10000.0f + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// Verifies that the delay controller functionality is properly integrated with
|
// Verifies that the delay controller functionality is properly integrated with
|
||||||
@ -301,4 +310,77 @@ TEST(BlockProcessor, DISABLED_WrongSampleRate) {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Verifies that external delay estimator delays are applied correctly when a
|
||||||
|
// call begins with a sequence of capture blocks.
|
||||||
|
TEST(BlockProcessor, ExternalDelayAppliedCorrectlyWithInitialCaptureCalls) {
|
||||||
|
constexpr int kNumRenderChannels = 1;
|
||||||
|
constexpr int kNumCaptureChannels = 1;
|
||||||
|
constexpr int kSampleRateHz = 16000;
|
||||||
|
|
||||||
|
EchoCanceller3Config config;
|
||||||
|
config.delay.use_external_delay_estimator = true;
|
||||||
|
|
||||||
|
std::unique_ptr<RenderDelayBuffer> delay_buffer(
|
||||||
|
RenderDelayBuffer::Create(config, kSampleRateHz, kNumRenderChannels));
|
||||||
|
|
||||||
|
std::unique_ptr<testing::NiceMock<webrtc::test::MockEchoRemover>>
|
||||||
|
echo_remover_mock(new NiceMock<webrtc::test::MockEchoRemover>());
|
||||||
|
webrtc::test::MockEchoRemover* echo_remover_mock_pointer =
|
||||||
|
echo_remover_mock.get();
|
||||||
|
|
||||||
|
std::unique_ptr<BlockProcessor> block_processor(BlockProcessor::Create(
|
||||||
|
config, kSampleRateHz, kNumRenderChannels, kNumCaptureChannels,
|
||||||
|
std::move(delay_buffer), /*delay_controller=*/nullptr,
|
||||||
|
std::move(echo_remover_mock)));
|
||||||
|
|
||||||
|
std::vector<std::vector<std::vector<float>>> render_block(
|
||||||
|
NumBandsForRate(kSampleRateHz),
|
||||||
|
std::vector<std::vector<float>>(kNumRenderChannels,
|
||||||
|
std::vector<float>(kBlockSize, 0.f)));
|
||||||
|
std::vector<std::vector<std::vector<float>>> capture_block(
|
||||||
|
NumBandsForRate(kSampleRateHz),
|
||||||
|
std::vector<std::vector<float>>(kNumCaptureChannels,
|
||||||
|
std::vector<float>(kBlockSize, 0.f)));
|
||||||
|
|
||||||
|
// Process...
|
||||||
|
// - 10 capture calls, where no render data is available,
|
||||||
|
// - 10 render calls, populating the buffer,
|
||||||
|
// - 2 capture calls, verifying that the delay was applied correctly.
|
||||||
|
constexpr int kDelayInBlocks = 5;
|
||||||
|
constexpr int kDelayInMs = 20;
|
||||||
|
block_processor->SetAudioBufferDelay(kDelayInMs);
|
||||||
|
|
||||||
|
int capture_call_counter = 0;
|
||||||
|
int render_call_counter = 0;
|
||||||
|
for (size_t k = 0; k < 10; ++k) {
|
||||||
|
FillSampleVector(++capture_call_counter, kDelayInBlocks,
|
||||||
|
capture_block[0][0]);
|
||||||
|
block_processor->ProcessCapture(false, false, nullptr, &capture_block);
|
||||||
|
}
|
||||||
|
for (size_t k = 0; k < 10; ++k) {
|
||||||
|
FillSampleVector(++render_call_counter, 0, render_block[0][0]);
|
||||||
|
block_processor->BufferRender(render_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_CALL(*echo_remover_mock_pointer, ProcessCapture)
|
||||||
|
.WillRepeatedly(
|
||||||
|
[](EchoPathVariability /*echo_path_variability*/,
|
||||||
|
bool /*capture_signal_saturation*/,
|
||||||
|
const absl::optional<DelayEstimate>& /*external_delay*/,
|
||||||
|
RenderBuffer* render_buffer,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* /*linear_output*/,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* capture) {
|
||||||
|
const auto& render = render_buffer->Block(0);
|
||||||
|
for (size_t i = 0; i < kBlockSize; ++i) {
|
||||||
|
EXPECT_FLOAT_EQ(render[0][0][i], (*capture)[0][0][i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FillSampleVector(++capture_call_counter, kDelayInBlocks, capture_block[0][0]);
|
||||||
|
block_processor->ProcessCapture(false, false, nullptr, &capture_block);
|
||||||
|
|
||||||
|
FillSampleVector(++capture_call_counter, kDelayInBlocks, capture_block[0][0]);
|
||||||
|
block_processor->ProcessCapture(false, false, nullptr, &capture_block);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -32,6 +32,7 @@ class MockRenderDelayBuffer : public RenderDelayBuffer {
|
|||||||
Insert,
|
Insert,
|
||||||
(const std::vector<std::vector<std::vector<float>>>& block),
|
(const std::vector<std::vector<std::vector<float>>>& block),
|
||||||
(override));
|
(override));
|
||||||
|
MOCK_METHOD(void, HandleSkippedCaptureProcessing, (), (override));
|
||||||
MOCK_METHOD(RenderDelayBuffer::BufferingEvent,
|
MOCK_METHOD(RenderDelayBuffer::BufferingEvent,
|
||||||
PrepareCaptureProcessing,
|
PrepareCaptureProcessing,
|
||||||
(),
|
(),
|
||||||
|
|||||||
@ -35,10 +35,16 @@
|
|||||||
#include "rtc_base/atomic_ops.h"
|
#include "rtc_base/atomic_ops.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
|
#include "system_wrappers/include/field_trial.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
bool UpdateCaptureCallCounterOnSkippedBlocks() {
|
||||||
|
return !field_trial::IsEnabled(
|
||||||
|
"WebRTC-Aec3RenderBufferCallCounterUpdateKillSwitch");
|
||||||
|
}
|
||||||
|
|
||||||
class RenderDelayBufferImpl final : public RenderDelayBuffer {
|
class RenderDelayBufferImpl final : public RenderDelayBuffer {
|
||||||
public:
|
public:
|
||||||
RenderDelayBufferImpl(const EchoCanceller3Config& config,
|
RenderDelayBufferImpl(const EchoCanceller3Config& config,
|
||||||
@ -51,6 +57,7 @@ class RenderDelayBufferImpl final : public RenderDelayBuffer {
|
|||||||
BufferingEvent Insert(
|
BufferingEvent Insert(
|
||||||
const std::vector<std::vector<std::vector<float>>>& block) override;
|
const std::vector<std::vector<std::vector<float>>>& block) override;
|
||||||
BufferingEvent PrepareCaptureProcessing() override;
|
BufferingEvent PrepareCaptureProcessing() override;
|
||||||
|
void HandleSkippedCaptureProcessing() override;
|
||||||
bool AlignFromDelay(size_t delay) override;
|
bool AlignFromDelay(size_t delay) override;
|
||||||
void AlignFromExternalDelay() override;
|
void AlignFromExternalDelay() override;
|
||||||
size_t Delay() const override { return ComputeDelay(); }
|
size_t Delay() const override { return ComputeDelay(); }
|
||||||
@ -72,6 +79,7 @@ class RenderDelayBufferImpl final : public RenderDelayBuffer {
|
|||||||
std::unique_ptr<ApmDataDumper> data_dumper_;
|
std::unique_ptr<ApmDataDumper> data_dumper_;
|
||||||
const Aec3Optimization optimization_;
|
const Aec3Optimization optimization_;
|
||||||
const EchoCanceller3Config config_;
|
const EchoCanceller3Config config_;
|
||||||
|
const bool update_capture_call_counter_on_skipped_blocks_;
|
||||||
const float render_linear_amplitude_gain_;
|
const float render_linear_amplitude_gain_;
|
||||||
const rtc::LoggingSeverity delay_log_level_;
|
const rtc::LoggingSeverity delay_log_level_;
|
||||||
size_t down_sampling_factor_;
|
size_t down_sampling_factor_;
|
||||||
@ -122,6 +130,8 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config,
|
|||||||
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
|
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
|
||||||
optimization_(DetectOptimization()),
|
optimization_(DetectOptimization()),
|
||||||
config_(config),
|
config_(config),
|
||||||
|
update_capture_call_counter_on_skipped_blocks_(
|
||||||
|
UpdateCaptureCallCounterOnSkippedBlocks()),
|
||||||
render_linear_amplitude_gain_(
|
render_linear_amplitude_gain_(
|
||||||
std::pow(10.0f, config_.render_levels.render_power_gain_db / 20.f)),
|
std::pow(10.0f, config_.render_levels.render_power_gain_db / 20.f)),
|
||||||
delay_log_level_(config_.delay.log_warning_on_delay_changes
|
delay_log_level_(config_.delay.log_warning_on_delay_changes
|
||||||
@ -243,6 +253,12 @@ RenderDelayBuffer::BufferingEvent RenderDelayBufferImpl::Insert(
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderDelayBufferImpl::HandleSkippedCaptureProcessing() {
|
||||||
|
if (update_capture_call_counter_on_skipped_blocks_) {
|
||||||
|
++capture_call_counter_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Prepares the render buffers for processing another capture block.
|
// Prepares the render buffers for processing another capture block.
|
||||||
RenderDelayBuffer::BufferingEvent
|
RenderDelayBuffer::BufferingEvent
|
||||||
RenderDelayBufferImpl::PrepareCaptureProcessing() {
|
RenderDelayBufferImpl::PrepareCaptureProcessing() {
|
||||||
|
|||||||
@ -48,6 +48,9 @@ class RenderDelayBuffer {
|
|||||||
// an enum indicating whether there was a special event that occurred.
|
// an enum indicating whether there was a special event that occurred.
|
||||||
virtual BufferingEvent PrepareCaptureProcessing() = 0;
|
virtual BufferingEvent PrepareCaptureProcessing() = 0;
|
||||||
|
|
||||||
|
// Called on capture blocks where PrepareCaptureProcessing is not called.
|
||||||
|
virtual void HandleSkippedCaptureProcessing() = 0;
|
||||||
|
|
||||||
// Sets the buffer delay and returns a bool indicating whether the delay
|
// Sets the buffer delay and returns a bool indicating whether the delay
|
||||||
// changed.
|
// changed.
|
||||||
virtual bool AlignFromDelay(size_t delay) = 0;
|
virtual bool AlignFromDelay(size_t delay) = 0;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user