diff options
Diffstat (limited to 'third_party/webrtc/src/webrtc/modules/audio_processing/aec/system_delay_unittest.cc')
-rw-r--r-- | third_party/webrtc/src/webrtc/modules/audio_processing/aec/system_delay_unittest.cc | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/third_party/webrtc/src/webrtc/modules/audio_processing/aec/system_delay_unittest.cc b/third_party/webrtc/src/webrtc/modules/audio_processing/aec/system_delay_unittest.cc new file mode 100644 index 00000000..07e3cf8a --- /dev/null +++ b/third_party/webrtc/src/webrtc/modules/audio_processing/aec/system_delay_unittest.cc @@ -0,0 +1,602 @@ +/* + * Copyright (c) 2012 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 "testing/gtest/include/gtest/gtest.h" +extern "C" { +#include "webrtc/modules/audio_processing/aec/aec_core.h" +} +#include "webrtc/modules/audio_processing/aec/echo_cancellation_internal.h" +#include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h" +#include "webrtc/test/testsupport/gtest_disable.h" +#include "webrtc/typedefs.h" + +namespace { + +class SystemDelayTest : public ::testing::Test { + protected: + SystemDelayTest(); + virtual void SetUp(); + virtual void TearDown(); + + // Initialization of AEC handle with respect to |sample_rate_hz|. Since the + // device sample rate is unimportant we set that value to 48000 Hz. + void Init(int sample_rate_hz); + + // Makes one render call and one capture call in that specific order. + void RenderAndCapture(int device_buffer_ms); + + // Fills up the far-end buffer with respect to the default device buffer size. + size_t BufferFillUp(); + + // Runs and verifies the behavior in a stable startup procedure. + void RunStableStartup(); + + // Maps buffer size in ms into samples, taking the unprocessed frame into + // account. + int MapBufferSizeToSamples(int size_in_ms, bool extended_filter); + + void* handle_; + Aec* self_; + size_t samples_per_frame_; + // Dummy input/output speech data. + static const int kSamplesPerChunk = 160; + float far_[kSamplesPerChunk]; + float near_[kSamplesPerChunk]; + float out_[kSamplesPerChunk]; + const float* near_ptr_; + float* out_ptr_; +}; + +SystemDelayTest::SystemDelayTest() + : handle_(NULL), self_(NULL), samples_per_frame_(0) { + // Dummy input data are set with more or less arbitrary non-zero values. + for (int i = 0; i < kSamplesPerChunk; i++) { + far_[i] = 257.0; + near_[i] = 514.0; + } + memset(out_, 0, sizeof(out_)); + near_ptr_ = near_; + out_ptr_ = out_; +} + +void SystemDelayTest::SetUp() { + handle_ = WebRtcAec_Create(); + ASSERT_TRUE(handle_); + self_ = reinterpret_cast<Aec*>(handle_); +} + +void SystemDelayTest::TearDown() { + // Free AEC + WebRtcAec_Free(handle_); + handle_ = NULL; +} + +// In SWB mode nothing is added to the buffer handling with respect to +// functionality compared to WB. We therefore only verify behavior in NB and WB. +static const int kSampleRateHz[] = {8000, 16000}; +static const size_t kNumSampleRates = + sizeof(kSampleRateHz) / sizeof(*kSampleRateHz); + +// Default audio device buffer size used. +static const int kDeviceBufMs = 100; + +// Requirement for a stable device convergence time in ms. Should converge in +// less than |kStableConvergenceMs|. +static const int kStableConvergenceMs = 100; + +// Maximum convergence time in ms. This means that we should leave the startup +// phase after |kMaxConvergenceMs| independent of device buffer stability +// conditions. +static const int kMaxConvergenceMs = 500; + +void SystemDelayTest::Init(int sample_rate_hz) { + // Initialize AEC + EXPECT_EQ(0, WebRtcAec_Init(handle_, sample_rate_hz, 48000)); + EXPECT_EQ(0, WebRtcAec_system_delay(self_->aec)); + + // One frame equals 10 ms of data. + samples_per_frame_ = static_cast<size_t>(sample_rate_hz / 100); +} + +void SystemDelayTest::RenderAndCapture(int device_buffer_ms) { + EXPECT_EQ(0, WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); + EXPECT_EQ(0, + WebRtcAec_Process(handle_, + &near_ptr_, + 1, + &out_ptr_, + samples_per_frame_, + device_buffer_ms, + 0)); +} + +size_t SystemDelayTest::BufferFillUp() { + // To make sure we have a full buffer when we verify stability we first fill + // up the far-end buffer with the same amount as we will report in through + // Process(). + size_t buffer_size = 0; + for (int i = 0; i < kDeviceBufMs / 10; i++) { + EXPECT_EQ(0, WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); + buffer_size += samples_per_frame_; + EXPECT_EQ(static_cast<int>(buffer_size), + WebRtcAec_system_delay(self_->aec)); + } + return buffer_size; +} + +void SystemDelayTest::RunStableStartup() { + // To make sure we have a full buffer when we verify stability we first fill + // up the far-end buffer with the same amount as we will report in through + // Process(). + size_t buffer_size = BufferFillUp(); + + if (WebRtcAec_delay_agnostic_enabled(self_->aec) == 1) { + // In extended_filter mode we set the buffer size after the first processed + // 10 ms chunk. Hence, we don't need to wait for the reported system delay + // values to become stable. + RenderAndCapture(kDeviceBufMs); + buffer_size += samples_per_frame_; + EXPECT_EQ(0, self_->startup_phase); + } else { + // A stable device should be accepted and put in a regular process mode + // within |kStableConvergenceMs|. + int process_time_ms = 0; + for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) { + RenderAndCapture(kDeviceBufMs); + buffer_size += samples_per_frame_; + if (self_->startup_phase == 0) { + // We have left the startup phase. + break; + } + } + // Verify convergence time. + EXPECT_GT(kStableConvergenceMs, process_time_ms); + } + // Verify that the buffer has been flushed. + EXPECT_GE(static_cast<int>(buffer_size), + WebRtcAec_system_delay(self_->aec)); +} + + int SystemDelayTest::MapBufferSizeToSamples(int size_in_ms, + bool extended_filter) { + // If extended_filter is disabled we add an extra 10 ms for the unprocessed + // frame. That is simply how the algorithm is constructed. + return static_cast<int>( + (size_in_ms + (extended_filter ? 0 : 10)) * samples_per_frame_ / 10); +} + +// The tests should meet basic requirements and not be adjusted to what is +// actually implemented. If we don't get good code coverage this way we either +// lack in tests or have unnecessary code. +// General requirements: +// 1) If we add far-end data the system delay should be increased with the same +// amount we add. +// 2) If the far-end buffer is full we should flush the oldest data to make room +// for the new. In this case the system delay is unaffected. +// 3) There should exist a startup phase in which the buffer size is to be +// determined. In this phase no cancellation should be performed. +// 4) Under stable conditions (small variations in device buffer sizes) the AEC +// should determine an appropriate local buffer size within +// |kStableConvergenceMs| ms. +// 5) Under unstable conditions the AEC should make a decision within +// |kMaxConvergenceMs| ms. +// 6) If the local buffer runs out of data we should stuff the buffer with older +// frames. +// 7) The system delay should within |kMaxConvergenceMs| ms heal from +// disturbances like drift, data glitches, toggling events and outliers. +// 8) The system delay should never become negative. + +TEST_F(SystemDelayTest, CorrectIncreaseWhenBufferFarend) { + // When we add data to the AEC buffer the internal system delay should be + // incremented with the same amount as the size of data. + // This process should be independent of DA-AEC and extended_filter mode. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_extended_filter(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); + EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + // Loop through a couple of calls to make sure the system delay + // increments correctly. + for (int j = 1; j <= 5; j++) { + EXPECT_EQ(0, + WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); + EXPECT_EQ(static_cast<int>(j * samples_per_frame_), + WebRtcAec_system_delay(self_->aec)); + } + } + } + } +} + +// TODO(bjornv): Add a test to verify behavior if the far-end buffer is full +// when adding new data. + +TEST_F(SystemDelayTest, CorrectDelayAfterStableStartup) { + // We run the system in a stable startup. After that we verify that the system + // delay meets the requirements. + // This process should be independent of DA-AEC and extended_filter mode. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_extended_filter(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); + EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + + // Verify system delay with respect to requirements, i.e., the + // |system_delay| is in the interval [75%, 100%] of what's reported on + // the average. + // In extended_filter mode we target 50% and measure after one processed + // 10 ms chunk. + int average_reported_delay = + static_cast<int>(kDeviceBufMs * samples_per_frame_ / 10); + EXPECT_GE(average_reported_delay, WebRtcAec_system_delay(self_->aec)); + int lower_bound = WebRtcAec_extended_filter_enabled(self_->aec) + ? average_reported_delay / 2 - samples_per_frame_ + : average_reported_delay * 3 / 4; + EXPECT_LE(lower_bound, WebRtcAec_system_delay(self_->aec)); + } + } + } +} + +TEST_F(SystemDelayTest, CorrectDelayAfterUnstableStartup) { + // This test does not apply in extended_filter mode, since we only use the + // the first 10 ms chunk to determine a reasonable buffer size. Neither does + // it apply if DA-AEC is on because that overrides the startup procedure. + WebRtcAec_enable_extended_filter(self_->aec, 0); + EXPECT_EQ(0, WebRtcAec_extended_filter_enabled(self_->aec)); + WebRtcAec_enable_delay_agnostic(self_->aec, 0); + EXPECT_EQ(0, WebRtcAec_delay_agnostic_enabled(self_->aec)); + + // In an unstable system we would start processing after |kMaxConvergenceMs|. + // On the last frame the AEC buffer is adjusted to 60% of the last reported + // device buffer size. + // We construct an unstable system by altering the device buffer size between + // two values |kDeviceBufMs| +- 25 ms. + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + + // To make sure we have a full buffer when we verify stability we first fill + // up the far-end buffer with the same amount as we will report in on the + // average through Process(). + size_t buffer_size = BufferFillUp(); + + int buffer_offset_ms = 25; + int reported_delay_ms = 0; + int process_time_ms = 0; + for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) { + reported_delay_ms = kDeviceBufMs + buffer_offset_ms; + RenderAndCapture(reported_delay_ms); + buffer_size += samples_per_frame_; + buffer_offset_ms = -buffer_offset_ms; + if (self_->startup_phase == 0) { + // We have left the startup phase. + break; + } + } + // Verify convergence time. + EXPECT_GE(kMaxConvergenceMs, process_time_ms); + // Verify that the buffer has been flushed. + EXPECT_GE(static_cast<int>(buffer_size), + WebRtcAec_system_delay(self_->aec)); + + // Verify system delay with respect to requirements, i.e., the + // |system_delay| is in the interval [60%, 100%] of what's last reported. + EXPECT_GE(static_cast<int>(reported_delay_ms * samples_per_frame_ / 10), + WebRtcAec_system_delay(self_->aec)); + EXPECT_LE( + static_cast<int>(reported_delay_ms * samples_per_frame_ / 10 * 3 / 5), + WebRtcAec_system_delay(self_->aec)); + } +} + +TEST_F(SystemDelayTest, CorrectDelayAfterStableBufferBuildUp) { + // This test does not apply in extended_filter mode, since we only use the + // the first 10 ms chunk to determine a reasonable buffer size. Neither does + // it apply if DA-AEC is on because that overrides the startup procedure. + WebRtcAec_enable_extended_filter(self_->aec, 0); + EXPECT_EQ(0, WebRtcAec_extended_filter_enabled(self_->aec)); + WebRtcAec_enable_delay_agnostic(self_->aec, 0); + EXPECT_EQ(0, WebRtcAec_delay_agnostic_enabled(self_->aec)); + + // In this test we start by establishing the device buffer size during stable + // conditions, but with an empty internal far-end buffer. Once that is done we + // verify that the system delay is increased correctly until we have reach an + // internal buffer size of 75% of what's been reported. + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + + // We assume that running |kStableConvergenceMs| calls will put the + // algorithm in a state where the device buffer size has been determined. We + // can make that assumption since we have a separate stability test. + int process_time_ms = 0; + for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) { + EXPECT_EQ(0, + WebRtcAec_Process(handle_, + &near_ptr_, + 1, + &out_ptr_, + samples_per_frame_, + kDeviceBufMs, + 0)); + } + // Verify that a buffer size has been established. + EXPECT_EQ(0, self_->checkBuffSize); + + // We now have established the required buffer size. Let us verify that we + // fill up before leaving the startup phase for normal processing. + size_t buffer_size = 0; + size_t target_buffer_size = kDeviceBufMs * samples_per_frame_ / 10 * 3 / 4; + process_time_ms = 0; + for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) { + RenderAndCapture(kDeviceBufMs); + buffer_size += samples_per_frame_; + if (self_->startup_phase == 0) { + // We have left the startup phase. + break; + } + } + // Verify convergence time. + EXPECT_GT(kMaxConvergenceMs, process_time_ms); + // Verify that the buffer has reached the desired size. + EXPECT_LE(static_cast<int>(target_buffer_size), + WebRtcAec_system_delay(self_->aec)); + + // Verify normal behavior (system delay is kept constant) after startup by + // running a couple of calls to BufferFarend() and Process(). + for (int j = 0; j < 6; j++) { + int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); + RenderAndCapture(kDeviceBufMs); + EXPECT_EQ(system_delay_before_calls, WebRtcAec_system_delay(self_->aec)); + } + } +} + +TEST_F(SystemDelayTest, CorrectDelayWhenBufferUnderrun) { + // Here we test a buffer under run scenario. If we keep on calling + // WebRtcAec_Process() we will finally run out of data, but should + // automatically stuff the buffer. We verify this behavior by checking if the + // system delay goes negative. + // This process should be independent of DA-AEC and extended_filter mode. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_extended_filter(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); + EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + + // The AEC has now left the Startup phase. We now have at most + // |kStableConvergenceMs| in the buffer. Keep on calling Process() until + // we run out of data and verify that the system delay is non-negative. + for (int j = 0; j <= kStableConvergenceMs; j += 10) { + EXPECT_EQ(0, WebRtcAec_Process(handle_, &near_ptr_, 1, &out_ptr_, + samples_per_frame_, kDeviceBufMs, 0)); + EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); + } + } + } + } +} + +TEST_F(SystemDelayTest, CorrectDelayDuringDrift) { + // This drift test should verify that the system delay is never exceeding the + // device buffer. The drift is simulated by decreasing the reported device + // buffer size by 1 ms every 100 ms. If the device buffer size goes below 30 + // ms we jump (add) 10 ms to give a repeated pattern. + + // This process should be independent of DA-AEC and extended_filter mode. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_extended_filter(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); + EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + + // We have left the startup phase and proceed with normal processing. + int jump = 0; + for (int j = 0; j < 1000; j++) { + // Drift = -1 ms per 100 ms of data. + int device_buf_ms = kDeviceBufMs - (j / 10) + jump; + int device_buf = MapBufferSizeToSamples(device_buf_ms, + extended_filter == 1); + + if (device_buf_ms < 30) { + // Add 10 ms data, taking affect next frame. + jump += 10; + } + RenderAndCapture(device_buf_ms); + + // Verify that the system delay does not exceed the device buffer. + EXPECT_GE(device_buf, WebRtcAec_system_delay(self_->aec)); + + // Verify that the system delay is non-negative. + EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); + } + } + } + } +} + +TEST_F(SystemDelayTest, ShouldRecoverAfterGlitch) { + // This glitch test should verify that the system delay recovers if there is + // a glitch in data. The data glitch is constructed as 200 ms of buffering + // after which the stable procedure continues. The glitch is never reported by + // the device. + // The system is said to be in a non-causal state if the difference between + // the device buffer and system delay is less than a block (64 samples). + + // This process should be independent of DA-AEC and extended_filter mode. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_extended_filter(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); + EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + int device_buf = MapBufferSizeToSamples(kDeviceBufMs, + extended_filter == 1); + // Glitch state. + for (int j = 0; j < 20; j++) { + EXPECT_EQ(0, + WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); + // No need to verify system delay, since that is done in a separate + // test. + } + // Verify that we are in a non-causal state, i.e., + // |system_delay| > |device_buf|. + EXPECT_LT(device_buf, WebRtcAec_system_delay(self_->aec)); + + // Recover state. Should recover at least 4 ms of data per 10 ms, hence + // a glitch of 200 ms will take at most 200 * 10 / 4 = 500 ms to recover + // from. + bool non_causal = true; // We are currently in a non-causal state. + for (int j = 0; j < 50; j++) { + int system_delay_before = WebRtcAec_system_delay(self_->aec); + RenderAndCapture(kDeviceBufMs); + int system_delay_after = WebRtcAec_system_delay(self_->aec); + // We have recovered if + // |device_buf| - |system_delay_after| >= PART_LEN (1 block). + // During recovery, |system_delay_after| < |system_delay_before|, + // otherwise they are equal. + if (non_causal) { + EXPECT_LT(system_delay_after, system_delay_before); + if (device_buf - system_delay_after >= PART_LEN) { + non_causal = false; + } + } else { + EXPECT_EQ(system_delay_before, system_delay_after); + } + // Verify that the system delay is non-negative. + EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); + } + // Check that we have recovered. + EXPECT_FALSE(non_causal); + } + } + } +} + +TEST_F(SystemDelayTest, UnaffectedWhenSpuriousDeviceBufferValues) { + // This test does not apply in extended_filter mode, since we only use the + // the first 10 ms chunk to determine a reasonable buffer size. + const int extended_filter = 0; + WebRtcAec_enable_extended_filter(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); + + // Should be DA-AEC independent. + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); + EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); + // This spurious device buffer data test aims at verifying that the system + // delay is unaffected by large outliers. + // The system is said to be in a non-causal state if the difference between + // the device buffer and system delay is less than a block (64 samples). + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + int device_buf = MapBufferSizeToSamples(kDeviceBufMs, + extended_filter == 1); + + // Normal state. We are currently not in a non-causal state. + bool non_causal = false; + + // Run 1 s and replace device buffer size with 500 ms every 100 ms. + for (int j = 0; j < 100; j++) { + int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); + int device_buf_ms = j % 10 == 0 ? 500 : kDeviceBufMs; + RenderAndCapture(device_buf_ms); + + // Check for non-causality. + if (device_buf - WebRtcAec_system_delay(self_->aec) < PART_LEN) { + non_causal = true; + } + EXPECT_FALSE(non_causal); + EXPECT_EQ(system_delay_before_calls, + WebRtcAec_system_delay(self_->aec)); + + // Verify that the system delay is non-negative. + EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); + } + } + } +} + +TEST_F(SystemDelayTest, CorrectImpactWhenTogglingDeviceBufferValues) { + // This test aims at verifying that the system delay is "unaffected" by + // toggling values reported by the device. + // The test is constructed such that every other device buffer value is zero + // and then 2 * |kDeviceBufMs|, hence the size is constant on the average. The + // zero values will force us into a non-causal state and thereby lowering the + // system delay until we basically run out of data. Once that happens the + // buffer will be stuffed. + // TODO(bjornv): This test will have a better impact if we verified that the + // delay estimate goes up when the system delay goes down to meet the average + // device buffer size. + + // This test does not apply if DA-AEC is enabled and extended_filter mode + // disabled. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_extended_filter(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); + EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); + if (extended_filter == 0 && da_aec == 1) { + continue; + } + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + const int device_buf = MapBufferSizeToSamples(kDeviceBufMs, + extended_filter == 1); + + // Normal state. We are currently not in a non-causal state. + bool non_causal = false; + + // Loop through 100 frames (both render and capture), which equals 1 s + // of data. Every odd frame we set the device buffer size to + // 2 * |kDeviceBufMs| and even frames we set the device buffer size to + // zero. + for (int j = 0; j < 100; j++) { + int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); + int device_buf_ms = 2 * (j % 2) * kDeviceBufMs; + RenderAndCapture(device_buf_ms); + + // Check for non-causality, compared with the average device buffer + // size. + non_causal |= (device_buf - WebRtcAec_system_delay(self_->aec) < 64); + EXPECT_GE(system_delay_before_calls, + WebRtcAec_system_delay(self_->aec)); + + // Verify that the system delay is non-negative. + EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); + } + // Verify we are not in a non-causal state. + EXPECT_FALSE(non_causal); + } + } + } +} + +} // namespace |