Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(220)

Unified Diff: webrtc/modules/audio_processing/audio_processing_impl_locking_unittest.cc

Issue 1394803002: Unittest for the locking in APM (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Changes in rensponse to reviewer comments. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | webrtc/modules/modules.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webrtc/modules/audio_processing/audio_processing_impl_locking_unittest.cc
diff --git a/webrtc/modules/audio_processing/audio_processing_impl_locking_unittest.cc b/webrtc/modules/audio_processing/audio_processing_impl_locking_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e08d311a1ac0c0616296f799b787fbc8671b9d20
--- /dev/null
+++ b/webrtc/modules/audio_processing/audio_processing_impl_locking_unittest.cc
@@ -0,0 +1,1151 @@
+/*
+ * Copyright (c) 2015 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/audio_processing_impl.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/config.h"
+#include "webrtc/modules/audio_processing/test/test_utils.h"
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/system_wrappers/include/event_wrapper.h"
+#include "webrtc/system_wrappers/include/sleep.h"
+#include "webrtc/system_wrappers/include/thread_wrapper.h"
+#include "webrtc/test/random.h"
+
+namespace webrtc {
+
+namespace {
+
+class AudioProcessingImplLockTest;
+
+// Sleeps a random time between 0 and max_sleep milliseconds.
+void SleepRandomMs(int max_sleep, test::Random* rand_gen) {
+ int sleeptime = rand_gen->Rand(0, max_sleep);
+ SleepMs(sleeptime);
+}
+
+// Populates a float audio frame with random data.
+void PopulateAudioFrame(float** frame,
+ float amplitude,
+ size_t num_channels,
+ size_t samples_per_channel,
+ test::Random* rand_gen) {
+ for (size_t ch = 0; ch < num_channels; ch++) {
+ for (size_t k = 0; k < samples_per_channel; k++) {
+ // Store random 16 bit quantized float number between the specified
+ // limits.
+ frame[ch][k] = amplitude *
+ static_cast<float>(rand_gen->Rand(-32767 - 1, 32767)) /
+ 32768.0f;
+ }
+ }
+}
+
+// Populates an audioframe frame of AudioFrame type with random data.
+void PopulateAudioFrame(AudioFrame* frame,
+ int16_t amplitude,
+ test::Random* rand_gen) {
+ ASSERT_GT(amplitude, 0);
+ ASSERT_LE(amplitude, 32767);
+ for (int ch = 0; ch < frame->num_channels_; ch++) {
+ for (int k = 0; k < static_cast<int>(frame->samples_per_channel_); k++) {
+ // Store random 16 bit quantized float number between -(amplitude+1) and
+ // amplitude.
+ frame->data_[k * ch] = rand_gen->Rand(-amplitude - 1, amplitude);
+ }
+ }
+}
+
+// Type of the render thread APM API call to use in the test.
+enum class RenderApiImpl {
+ ProcessReverseStreamImpl1,
+ ProcessReverseStreamImpl2,
+ AnalyzeReverseStreamImpl1,
+ AnalyzeReverseStreamImpl2
+};
+
+// Type of the capture thread APM API call to use in the test.
+enum class CaptureApiImpl {
+ ProcessStreamImpl1,
+ ProcessStreamImpl2,
+ ProcessStreamImpl3
+};
+
+// The runtime parameter setting scheme to use in the test.
+enum class RuntimeParameterSettingScheme {
+ SparseStreamMetadataChangeScheme,
+ ExtremeStreamMetadataChangeScheme,
+ FixedMonoStreamMetadataScheme,
+ FixedStereoStreamMetadataScheme
+};
+
+// Variant of echo canceller settings to use in the test.
+enum class AecType {
+ BasicWebRtcAecSettings,
+ AecTurnedOff,
+ BasicWebRtcAecSettingsWithExtentedFilter,
+ BasicWebRtcAecSettingsWithDelayAgnosticAec,
+ BasicWebRtcAecSettingsWithAecMobile
+};
+
+// Variables related to the audio data and formats.
+struct AudioFrameData {
+ explicit AudioFrameData(int max_frame_size) {
+ // Set up the two-dimensional arrays needed for the APM API calls.
+ input_framechannels.resize(2 * max_frame_size);
+ input_frame.resize(2);
+ input_frame[0] = &input_framechannels[0];
+ input_frame[1] = &input_framechannels[max_frame_size];
+
+ output_frame_channels.resize(2 * max_frame_size);
+ output_frame.resize(2);
+ output_frame[0] = &output_frame_channels[0];
+ output_frame[1] = &output_frame_channels[max_frame_size];
+ }
+
+ AudioFrame frame;
+ std::vector<float*> output_frame;
+ std::vector<float> output_frame_channels;
+ AudioProcessing::ChannelLayout output_channel_layout;
+ int input_sample_rate_hz = 16000;
+ int input_number_of_channels;
+ std::vector<float*> input_frame;
+ std::vector<float> input_framechannels;
+ AudioProcessing::ChannelLayout input_channel_layout;
+ int output_sample_rate_hz = 16000;
+ int output_number_of_channels;
+ StreamConfig input_stream_config;
+ StreamConfig output_stream_config;
+ int input_samples_per_channel;
+ int output_samples_per_channel;
+};
+
+// The configuration for the test.
+struct TestConfig {
+ // Test case generator for the test configurations to use in the brief tests.
+ static std::vector<TestConfig> GenerateBriefTestConfigs() {
+ std::vector<TestConfig> test_configs;
+ for (int aec = static_cast<int>(
+ AecType::BasicWebRtcAecSettingsWithDelayAgnosticAec);
+ aec <= static_cast<int>(AecType::BasicWebRtcAecSettingsWithAecMobile);
+ aec++) {
+ TestConfig test_config;
+
+ test_config.min_number_of_calls = 300;
+
+ // Perform tests only with the extreme runtime parameter setting scheme.
+ test_config.runtime_parameter_setting_scheme =
+ RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme;
+
+ // Only test 16 kHz for this test suite.
+ test_config.initial_sample_rate_hz = 16000;
+
+ // Create test config for the second processing API function set.
+ test_config.render_api_function =
+ RenderApiImpl::ProcessReverseStreamImpl2;
+ test_config.capture_api_function = CaptureApiImpl::ProcessStreamImpl2;
+
+ // Create test config for the first processing API function set.
+ test_configs.push_back(test_config);
+ test_config.render_api_function =
+ RenderApiImpl::AnalyzeReverseStreamImpl2;
+ test_config.capture_api_function = CaptureApiImpl::ProcessStreamImpl3;
+ test_configs.push_back(test_config);
+ }
+
+ // Return the created test configurations.
+ return test_configs;
+ }
+
+ // Checker for whether the a test configuration is valid.
+ static bool ValidTestConfig(const TestConfig& test_config) {
+ return ((((test_config.render_api_function ==
+ RenderApiImpl::ProcessReverseStreamImpl1) ||
+ (test_config.render_api_function ==
+ RenderApiImpl::AnalyzeReverseStreamImpl1)) &&
+ (test_config.capture_api_function ==
+ CaptureApiImpl::ProcessStreamImpl1)) ||
+ (((test_config.render_api_function !=
+ RenderApiImpl::ProcessReverseStreamImpl1) &&
+ (test_config.render_api_function !=
+ RenderApiImpl::AnalyzeReverseStreamImpl1)) &&
+ (test_config.capture_api_function !=
+ CaptureApiImpl::ProcessStreamImpl1)));
+ }
+
+ // Test case generator for the test configurations to use in the extensive
+ // tests.
+ static std::vector<TestConfig> GenerateExtensiveTestConfigs() {
+ std::vector<TestConfig> test_configs;
+ std::vector<TestConfig> tmp_test_configs;
+
+ // Create test configurations for al the render API function
+ // implementations.
+ for (int render =
+ static_cast<int>(RenderApiImpl::ProcessReverseStreamImpl1);
+ render <= static_cast<int>(RenderApiImpl::AnalyzeReverseStreamImpl2);
+ render++) {
+ TestConfig test_config;
+ test_config.render_api_function = static_cast<RenderApiImpl>(render);
+ test_configs.push_back(test_config);
+ }
+
+ // Set the miminum number of calls for the test configurations.
+ for (TestConfig& test_config : test_configs) {
+ test_config.min_number_of_calls = 10000;
+ }
+
+ // Combine the existing set of test configurations with the capture API
+ // function implementations.
+ tmp_test_configs.swap(test_configs);
+ test_configs.clear();
+ for (TestConfig test_config : tmp_test_configs) {
+ for (int capture = static_cast<int>(CaptureApiImpl::ProcessStreamImpl1);
+ capture <= static_cast<int>(CaptureApiImpl::ProcessStreamImpl3);
+ capture++) {
+ test_config.capture_api_function = static_cast<CaptureApiImpl>(capture);
+ test_configs.push_back(test_config);
+ }
+ }
+
+ // Combine the existing set of test configurations with the AEC
+ // configurations.
+ tmp_test_configs.swap(test_configs);
+ test_configs.clear();
+ for (TestConfig test_config : tmp_test_configs) {
+ for (int aec = static_cast<int>(AecType::BasicWebRtcAecSettings);
+ aec <=
+ static_cast<int>(AecType::BasicWebRtcAecSettingsWithAecMobile);
+ aec++) {
+ test_config.aec_type = static_cast<AecType>(aec);
+ test_configs.push_back(test_config);
+ }
+ }
+
+ // Combine the existing set of test configurations with the parameter
+ // setting schemes.
+ tmp_test_configs.swap(test_configs);
+ test_configs.clear();
+ for (TestConfig test_config : tmp_test_configs) {
+ for (int scheme = static_cast<int>(
+ RuntimeParameterSettingScheme::SparseStreamMetadataChangeScheme);
+ scheme <=
+ static_cast<int>(
+ RuntimeParameterSettingScheme::FixedStereoStreamMetadataScheme);
+ scheme++) {
+ // Only produce test configs with compatible render and capture API
+ // calls.
+ if (ValidTestConfig(test_config)) {
+ // Add test configs with different initial sample rates and
+ // parameter setting schemes.
+ test_config.runtime_parameter_setting_scheme =
+ static_cast<RuntimeParameterSettingScheme>(scheme);
+ test_configs.push_back(test_config);
+ }
+ }
+ }
+
+ // Replicate the test configurations for all possible initial sample rates.
+ tmp_test_configs.swap(test_configs);
+ test_configs.clear();
+ for (TestConfig test_config : tmp_test_configs) {
+ test_config.initial_sample_rate_hz = 8000;
+ test_configs.push_back(test_config);
+
+ test_config.initial_sample_rate_hz = 16000;
+ test_configs.push_back(test_config);
+
+ if (test_config.aec_type !=
+ AecType::BasicWebRtcAecSettingsWithAecMobile) {
+ test_config.initial_sample_rate_hz = 32000;
+ test_configs.push_back(test_config);
+
+ test_config.initial_sample_rate_hz = 48000;
+ test_configs.push_back(test_config);
+ }
+
+ test_config.initial_sample_rate_hz = 8000;
+ test_configs.push_back(test_config);
+
+ test_config.initial_sample_rate_hz = 16000;
+ test_configs.push_back(test_config);
+ }
+
+ // Return the created test configurations.
+ return test_configs;
+ }
+
+ RenderApiImpl render_api_function;
+ CaptureApiImpl capture_api_function;
+ RuntimeParameterSettingScheme runtime_parameter_setting_scheme;
+ int initial_sample_rate_hz;
+ AecType aec_type;
+ int min_number_of_calls;
+};
+
+// Handler for the frame counters.
+class FrameCounters {
+ public:
+ void IncreaseRenderCounter() {
+ rtc::CritScope cs(&crit_);
+ render_count++;
+ }
+
+ void IncreaseCaptureCounter() {
+ rtc::CritScope cs(&crit_);
+ capture_count++;
+ }
+
+ int GetCaptureCounter() {
+ rtc::CritScope cs(&crit_);
+ return capture_count;
+ }
+
+ int GetRenderCounter() {
+ rtc::CritScope cs(&crit_);
+ return capture_count;
+ }
+
+ int CaptureMinusRenderCounters() {
+ rtc::CritScope cs(&crit_);
+ return capture_count - render_count;
+ }
+
+ bool BothCountersExceedeThreshold(int threshold) {
+ rtc::CritScope cs(&crit_);
+ return (render_count > threshold && capture_count > threshold);
+ }
+
+ private:
+ rtc::CriticalSection crit_;
+ int render_count GUARDED_BY(crit_) = 0;
+ int capture_count GUARDED_BY(crit_) = 0;
+};
+
+// Checker for whether the capture side has been called.
+class CaptureSideCalledChecker {
+ public:
+ bool CaptureSideCalled() {
+ rtc::CritScope cs(&crit_);
+ return capture_side_called_;
+ }
+
+ void FlagCaptureSideCalled() {
+ rtc::CritScope cs(&crit_);
+ capture_side_called_ = true;
+ }
+
+ private:
+ rtc::CriticalSection crit_;
+ bool capture_side_called_ GUARDED_BY(crit_) = false;
+};
+
+// Class for handling the capture side processing.
+class CaptureProcessor {
+ public:
+ CaptureProcessor(int max_frame_size,
+ test::Random* rand_gen,
+ FrameCounters* shared_counters_state,
+ CaptureSideCalledChecker* capture_call_checker,
+ AudioProcessingImplLockTest* test_framework,
+ TestConfig* test_config,
+ AudioProcessing* apm);
+
+ bool Process();
+
+ private:
+ static const int kMaxCallDifference = 10;
+ static const float kCaptureInputFloatLevel;
+ static const int kCaptureInputFixLevel = 1024;
+
+ void CallApmCaptureSide();
+ void PrepareFrame();
+ void ApplyRuntimeSettingScheme();
+
+ test::Random* rand_gen_;
+ FrameCounters* frame_counters_;
+ CaptureSideCalledChecker* capture_call_checker_;
+ AudioProcessingImplLockTest* test_;
+ TestConfig* test_config_;
+ AudioProcessing* apm_;
+ AudioFrameData frame_data_;
+};
+
+// Class for handling the stats processing.
+class StatsProcessor {
+ public:
+ StatsProcessor(test::Random* rand_gen,
+ TestConfig* test_config,
+ AudioProcessing* apm);
+
+ bool Process();
+
+ private:
+ test::Random* rand_gen_;
+ TestConfig* test_config_;
+ AudioProcessing* apm_;
+};
+
+// Class for handling the render side processing.
+class RenderProcessor {
+ public:
+ RenderProcessor(int max_frame_size,
+ test::Random* rand_gen,
+ FrameCounters* shared_counters_state,
+ CaptureSideCalledChecker* capture_call_checker,
+ AudioProcessingImplLockTest* test_framework,
+ TestConfig* test_config,
+ AudioProcessing* apm);
+
+ bool Process();
+
+ private:
+ static const int kMaxCallDifference = 10;
+ static const int kRenderInputFixLevel = 16384;
+ static const float kRenderInputFloatLevel;
+
+ void CallApmRenderSide();
+ void ApplyRuntimeSettingScheme();
+ void PrepareFrame();
+
+ test::Random* rand_gen_;
+ FrameCounters* frame_counters_;
+ CaptureSideCalledChecker* capture_call_checker_;
+ AudioProcessingImplLockTest* test_;
+ TestConfig* test_config_;
+ AudioProcessing* apm_;
+ bool first_render_side_call_ = true;
+ AudioFrameData frame_data_;
+};
+
+// Class for implementing the tests of the locks in the audio processing module.
+class AudioProcessingImplLockTest
+ : public ::testing::TestWithParam<TestConfig> {
+ public:
+ AudioProcessingImplLockTest();
+ EventTypeWrapper RunTest();
+ void SetUp() override;
+ void TearDown() override;
+ void CheckTestCompleteness();
+
+ private:
+ static const int kTestTimeOutLimit = 10 * 60 * 1000;
+ static const int kMaxFrameSize = 480;
+
+ // Thread callback for the render thread
+ static bool RenderProcessorCallback(void* context) {
+ return reinterpret_cast<AudioProcessingImplLockTest*>(context)
+ ->render_thread_state_.Process();
+ }
+
+ // Thread callback for the capture thread
+ static bool CaptureProcessorCallback(void* context) {
+ return reinterpret_cast<AudioProcessingImplLockTest*>(context)
+ ->capture_thread_state_.Process();
+ }
+
+ // Thread callback for the stats thread
+ static bool StatsProcessorCallback(void* context) {
+ return reinterpret_cast<AudioProcessingImplLockTest*>(context)
+ ->stats_thread_state_.Process();
+ }
+
+ // Tests whether all the required render and capture side calls have been
+ // done.
+ bool TestDone() {
+ return frame_counters_.BothCountersExceedeThreshold(
+ test_config_.min_number_of_calls);
+ }
+
+ // Start the threads used in the test.
+ void StartThreads() {
+ ASSERT_TRUE(render_thread_->Start());
+ render_thread_->SetPriority(kRealtimePriority);
+ ASSERT_TRUE(capture_thread_->Start());
+ capture_thread_->SetPriority(kRealtimePriority);
+ ASSERT_TRUE(stats_thread_->Start());
+ stats_thread_->SetPriority(kNormalPriority);
+ }
+
+ // Event handler for the test.
+ const rtc::scoped_ptr<EventWrapper> test_complete_;
+
+ // Thread related variables.
+ rtc::scoped_ptr<ThreadWrapper> render_thread_;
+ rtc::scoped_ptr<ThreadWrapper> capture_thread_;
+ rtc::scoped_ptr<ThreadWrapper> stats_thread_;
+ mutable test::Random rand_gen_;
+
+ rtc::scoped_ptr<AudioProcessing> apm_;
+ TestConfig test_config_;
+ FrameCounters frame_counters_;
+ CaptureSideCalledChecker capture_call_checker_;
+ RenderProcessor render_thread_state_;
+ CaptureProcessor capture_thread_state_;
+ StatsProcessor stats_thread_state_;
+};
+
+AudioProcessingImplLockTest::AudioProcessingImplLockTest()
+ : test_complete_(EventWrapper::Create()),
+ render_thread_(
+ ThreadWrapper::CreateThread(RenderProcessorCallback, this, "render")),
+ capture_thread_(ThreadWrapper::CreateThread(CaptureProcessorCallback,
+ this,
+ "capture")),
+ stats_thread_(
+ ThreadWrapper::CreateThread(StatsProcessorCallback, this, "stats")),
+ rand_gen_(42U),
+ apm_(AudioProcessingImpl::Create()),
+ render_thread_state_(kMaxFrameSize,
+ &rand_gen_,
+ &frame_counters_,
+ &capture_call_checker_,
+ this,
+ &test_config_,
+ apm_.get()),
+ capture_thread_state_(kMaxFrameSize,
+ &rand_gen_,
+ &frame_counters_,
+ &capture_call_checker_,
+ this,
+ &test_config_,
+ apm_.get()),
+ stats_thread_state_(&rand_gen_, &test_config_, apm_.get()) {}
+
+// Run the test with a timeout.
+EventTypeWrapper AudioProcessingImplLockTest::RunTest() {
+ StartThreads();
+ return test_complete_->Wait(kTestTimeOutLimit);
+}
+
+// Setup of test and APM.
+void AudioProcessingImplLockTest::SetUp() {
+ // apm_.reset(AudioProcessingImpl::Create());
+ test_config_ = static_cast<TestConfig>(GetParam());
+
+ ASSERT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true));
+ ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
+
+ ASSERT_EQ(apm_->kNoError,
+ apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog));
+ ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
+
+ ASSERT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true));
+ ASSERT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true));
+
+ Config config;
+ if (test_config_.aec_type == AecType::AecTurnedOff) {
+ ASSERT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(false));
+ ASSERT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(false));
+ } else if (test_config_.aec_type ==
+ AecType::BasicWebRtcAecSettingsWithAecMobile) {
+ ASSERT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true));
+ ASSERT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(false));
+ } else {
+ ASSERT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(false));
+ ASSERT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true));
+ ASSERT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_metrics(true));
+ ASSERT_EQ(apm_->kNoError,
+ apm_->echo_cancellation()->enable_delay_logging(true));
+
+ config.Set<ExtendedFilter>(
+ new ExtendedFilter(test_config_.aec_type ==
+ AecType::BasicWebRtcAecSettingsWithExtentedFilter));
+
+ config.Set<DelayAgnostic>(
+ new DelayAgnostic(test_config_.aec_type ==
+ AecType::BasicWebRtcAecSettingsWithDelayAgnosticAec));
+
+ apm_->SetExtraOptions(config);
+ }
+}
+
+void AudioProcessingImplLockTest::TearDown() {
+ render_thread_->Stop();
+ capture_thread_->Stop();
+ stats_thread_->Stop();
+}
+
+StatsProcessor::StatsProcessor(test::Random* rand_gen,
+ TestConfig* test_config,
+ AudioProcessing* apm)
+ : rand_gen_(rand_gen), test_config_(test_config), apm_(apm) {}
+
+// Implements the callback functionality for the statistics
+// collection thread.
+bool StatsProcessor::Process() {
+ SleepRandomMs(100, rand_gen_);
+
+ EXPECT_EQ(apm_->echo_cancellation()->is_enabled(),
+ ((test_config_->aec_type != AecType::AecTurnedOff) &&
+ (test_config_->aec_type !=
+ AecType::BasicWebRtcAecSettingsWithAecMobile)));
+ apm_->echo_cancellation()->stream_drift_samples();
+ EXPECT_EQ(apm_->echo_control_mobile()->is_enabled(),
+ (test_config_->aec_type != AecType::AecTurnedOff) &&
+ (test_config_->aec_type ==
+ AecType::BasicWebRtcAecSettingsWithAecMobile));
+ EXPECT_TRUE(apm_->gain_control()->is_enabled());
+ apm_->gain_control()->stream_analog_level();
+ EXPECT_TRUE(apm_->noise_suppression()->is_enabled());
+ float speech_probablitity = apm_->noise_suppression()->speech_probability();
+ EXPECT_TRUE(speech_probablitity < (apm_->kUnsupportedFunctionError + 0.5f) ||
+ speech_probablitity >= 0);
+ apm_->voice_detection()->is_enabled();
+
+ return true;
+}
+
+const float CaptureProcessor::kCaptureInputFloatLevel = 0.03125f;
+
+// Applies the capture side processing API call.
+void CaptureProcessor::CallApmCaptureSide() {
+ // Prepare a proper capture side processing API call input.
+ PrepareFrame();
+
+ // Set the stream delay
+ apm_->set_stream_delay_ms(30);
+
+ // Call the specified capture side API processing method.
+ int result = AudioProcessing::kNoError;
+ switch (test_config_->capture_api_function) {
+ case CaptureApiImpl::ProcessStreamImpl1:
+ result = apm_->ProcessStream(&frame_data_.frame);
+ break;
+ case CaptureApiImpl::ProcessStreamImpl2:
+ result = apm_->ProcessStream(
+ &frame_data_.input_frame[0], frame_data_.input_samples_per_channel,
+ frame_data_.input_sample_rate_hz, frame_data_.input_channel_layout,
+ frame_data_.output_sample_rate_hz, frame_data_.output_channel_layout,
+ &frame_data_.output_frame[0]);
+ break;
+ case CaptureApiImpl::ProcessStreamImpl3:
+ result = apm_->ProcessStream(
+ &frame_data_.input_frame[0], frame_data_.input_stream_config,
+ frame_data_.output_stream_config, &frame_data_.output_frame[0]);
+ break;
+ default:
+ assert(false);
the sun 2015/11/04 12:36:40 FAIL(), RTC_NOTREACHED() or ASSERT_TRUE(false)
peah-webrtc 2015/11/05 10:10:09 Done.
kwiberg-webrtc 2015/11/05 10:15:23 Nit: I still count three assert()s in the latest p
peah-webrtc 2015/11/05 10:21:55 Done.
peah-webrtc 2015/11/05 10:21:55 Great! Thanks! Done.
+ }
+
+ // Check the return code for error.
+ ASSERT_EQ(AudioProcessing::kNoError, result);
+}
+
+// Applies any runtime capture APM API calls and audio stream characteristics
+// specified by the scheme for the test.
+void CaptureProcessor::ApplyRuntimeSettingScheme() {
+ const int capture_count_local = frame_counters_->GetCaptureCounter();
+
+ // Update the number of channels and sample rates for the input and output.
+ // Note that the counts frequencies for when to set parameters
+ // are set using prime numbers in order to ensure that the
+ // permutation scheme in the parameter setting changes.
+ switch (test_config_->runtime_parameter_setting_scheme) {
+ case RuntimeParameterSettingScheme::SparseStreamMetadataChangeScheme:
+ if (capture_count_local == 0)
+ frame_data_.input_sample_rate_hz = 16000;
+ else if (capture_count_local % 11 == 0)
+ frame_data_.input_sample_rate_hz = 32000;
+ else if (capture_count_local % 73 == 0)
+ frame_data_.input_sample_rate_hz = 48000;
+ else if (capture_count_local % 89 == 0)
+ frame_data_.input_sample_rate_hz = 16000;
+ else if (capture_count_local % 97 == 0)
+ frame_data_.input_sample_rate_hz = 8000;
+
+ if (capture_count_local == 0)
+ frame_data_.input_number_of_channels = 1;
+ else if (capture_count_local % 4 == 0)
+ frame_data_.input_number_of_channels =
+ (frame_data_.input_number_of_channels == 1 ? 2 : 1);
+
+ if (capture_count_local == 0)
+ frame_data_.output_sample_rate_hz = 16000;
+ else if (capture_count_local % 5 == 0)
+ frame_data_.output_sample_rate_hz = 32000;
+ else if (capture_count_local % 47 == 0)
+ frame_data_.output_sample_rate_hz = 48000;
+ else if (capture_count_local % 53 == 0)
+ frame_data_.output_sample_rate_hz = 16000;
+ else if (capture_count_local % 71 == 0)
+ frame_data_.output_sample_rate_hz = 8000;
+
+ if (capture_count_local == 0)
+ frame_data_.output_number_of_channels = 1;
+ else if (capture_count_local % 8 == 0)
+ frame_data_.output_number_of_channels =
+ (frame_data_.output_number_of_channels == 1 ? 2 : 1);
+ break;
+ case RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme:
+ if (capture_count_local % 2 == 0) {
+ frame_data_.input_number_of_channels = 1;
+ frame_data_.input_sample_rate_hz = 16000;
+ frame_data_.output_number_of_channels = 1;
+ frame_data_.output_sample_rate_hz = 16000;
+ } else {
+ frame_data_.input_number_of_channels =
+ (frame_data_.input_number_of_channels == 1 ? 2 : 1);
+ if (frame_data_.input_sample_rate_hz == 8000)
+ frame_data_.input_sample_rate_hz = 16000;
+ else if (frame_data_.input_sample_rate_hz == 16000)
+ frame_data_.input_sample_rate_hz = 32000;
+ else if (frame_data_.input_sample_rate_hz == 32000)
+ frame_data_.input_sample_rate_hz = 48000;
+ else if (frame_data_.input_sample_rate_hz == 48000)
+ frame_data_.input_sample_rate_hz = 8000;
+
+ frame_data_.output_number_of_channels =
+ (frame_data_.output_number_of_channels == 1 ? 2 : 1);
+ if (frame_data_.output_sample_rate_hz == 8000)
+ frame_data_.output_sample_rate_hz = 16000;
+ else if (frame_data_.output_sample_rate_hz == 16000)
+ frame_data_.output_sample_rate_hz = 32000;
+ else if (frame_data_.output_sample_rate_hz == 32000)
+ frame_data_.output_sample_rate_hz = 48000;
+ else if (frame_data_.output_sample_rate_hz == 48000)
+ frame_data_.output_sample_rate_hz = 8000;
+ }
+ break;
+ case RuntimeParameterSettingScheme::FixedMonoStreamMetadataScheme:
+ if (capture_count_local == 0) {
+ frame_data_.input_sample_rate_hz = 16000;
+ frame_data_.input_number_of_channels = 1;
+ frame_data_.output_sample_rate_hz = 16000;
+ frame_data_.output_number_of_channels = 1;
+ }
+ break;
+ case RuntimeParameterSettingScheme::FixedStereoStreamMetadataScheme:
+ if (capture_count_local == 0) {
+ frame_data_.input_sample_rate_hz = 16000;
+ frame_data_.input_number_of_channels = 2;
+ frame_data_.output_sample_rate_hz = 16000;
+ frame_data_.output_number_of_channels = 2;
+ }
+
+ break;
+ default:
+ assert(false);
+ }
+
+ // Call any specified runtime APM setter and
+ // getter calls.
+ switch (test_config_->runtime_parameter_setting_scheme) {
+ case RuntimeParameterSettingScheme::SparseStreamMetadataChangeScheme:
+ case RuntimeParameterSettingScheme::FixedMonoStreamMetadataScheme:
+ break;
+ case RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme:
+ case RuntimeParameterSettingScheme::FixedStereoStreamMetadataScheme:
+ if (capture_count_local % 2 == 0) {
+ ASSERT_EQ(AudioProcessing::Error::kNoError,
+ apm_->set_stream_delay_ms(30));
+ apm_->set_stream_key_pressed(true);
+ apm_->set_output_will_be_muted(true);
+ apm_->set_delay_offset_ms(15);
+ EXPECT_EQ(apm_->delay_offset_ms(), 15);
+ EXPECT_GE(apm_->num_reverse_channels(), 0);
+ EXPECT_LE(apm_->num_reverse_channels(), 2);
+ } else {
+ ASSERT_EQ(AudioProcessing::Error::kNoError,
+ apm_->set_stream_delay_ms(50));
+ apm_->set_stream_key_pressed(false);
+ apm_->set_output_will_be_muted(false);
+ apm_->set_delay_offset_ms(20);
+ EXPECT_EQ(apm_->delay_offset_ms(), 20);
+ apm_->delay_offset_ms();
+ apm_->num_reverse_channels();
+ EXPECT_GE(apm_->num_reverse_channels(), 0);
+ EXPECT_LE(apm_->num_reverse_channels(), 2);
+ }
+ break;
+ default:
+ FAIL();
+ }
+
+ // Restric the number of output channels not to exceed
+ // the number of input channels.
+ frame_data_.output_number_of_channels =
+ std::min(frame_data_.output_number_of_channels,
+ frame_data_.input_number_of_channels);
+}
+
+// Prepares a frame with relevant audio data and metadata.
+void CaptureProcessor::PrepareFrame() {
+ // Restrict to a common fixed sample rate if the AudioFrame
+ // interface is used.
+ if (test_config_->capture_api_function ==
+ CaptureApiImpl::ProcessStreamImpl1) {
+ frame_data_.input_sample_rate_hz = test_config_->initial_sample_rate_hz;
+ frame_data_.output_sample_rate_hz = test_config_->initial_sample_rate_hz;
+ }
+
+ // Prepare the audioframe data and metadata.
+ frame_data_.input_samples_per_channel =
+ frame_data_.input_sample_rate_hz * AudioProcessing::kChunkSizeMs / 1000;
+ frame_data_.frame.sample_rate_hz_ = frame_data_.input_sample_rate_hz;
+ frame_data_.frame.num_channels_ = frame_data_.input_number_of_channels;
+ frame_data_.frame.samples_per_channel_ =
+ frame_data_.input_samples_per_channel;
+ memset(frame_data_.frame.data_, 0, frame_data_.input_samples_per_channel *
+ sizeof(frame_data_.frame.data_[0]));
+ PopulateAudioFrame(&frame_data_.frame, kCaptureInputFixLevel, rand_gen_);
+
+ // Prepare the float audio input data and metadata.
+ frame_data_.input_stream_config.set_sample_rate_hz(
+ frame_data_.input_sample_rate_hz);
+ frame_data_.input_stream_config.set_num_channels(
+ frame_data_.input_number_of_channels);
+ frame_data_.input_stream_config.set_has_keyboard(false);
+ PopulateAudioFrame(&frame_data_.input_frame[0], kCaptureInputFloatLevel,
+ frame_data_.input_number_of_channels,
+ frame_data_.input_samples_per_channel, rand_gen_);
+ frame_data_.input_channel_layout =
+ (frame_data_.input_number_of_channels == 1
+ ? AudioProcessing::ChannelLayout::kMonoAndKeyboard
+ : AudioProcessing::ChannelLayout::kStereoAndKeyboard);
+
+ // Prepare the float audio output data and metadata.
+ frame_data_.output_samples_per_channel =
+ frame_data_.output_sample_rate_hz * AudioProcessing::kChunkSizeMs / 1000;
+ frame_data_.output_stream_config.set_sample_rate_hz(
+ frame_data_.output_sample_rate_hz);
+ frame_data_.output_stream_config.set_num_channels(
+ frame_data_.output_number_of_channels);
+ frame_data_.output_stream_config.set_has_keyboard(false);
+ frame_data_.output_channel_layout =
+ (frame_data_.output_number_of_channels == 1
+ ? AudioProcessing::ChannelLayout::kMono
+ : AudioProcessing::ChannelLayout::kStereo);
+}
+
+// Implements the callback functionality for the capture thread.
+bool CaptureProcessor::Process() {
+ // Sleep a random time to simulate thread jitter.
+ SleepRandomMs(3, rand_gen_);
+
+ // End the test is complete.
+ test_->CheckTestCompleteness();
+
+ // Ensure that there are not more capture side calls than render side
+ // calls.
+ if (capture_call_checker_->CaptureSideCalled()) {
+ while (kMaxCallDifference < frame_counters_->CaptureMinusRenderCounters()) {
+ SleepMs(1);
+ }
+ }
+
+ // Apply any specified capture side APM non-processing runtime calls.
+ ApplyRuntimeSettingScheme();
+
+ // Apply the capture side processing call.
+ CallApmCaptureSide();
+
+ // Increase the number of capture-side calls.
+ frame_counters_->IncreaseCaptureCounter();
+
+ // Flag that the capture side has been called at least once
+ // (needed to ensure that a capture call has been done
+ // before the first render call is performed (implicitly
+ // required by the APM API).
+ capture_call_checker_->FlagCaptureSideCalled();
+
+ return true;
+}
+
+CaptureProcessor::CaptureProcessor(
+ int max_frame_size,
+ test::Random* rand_gen,
+ FrameCounters* shared_counters_state,
+ CaptureSideCalledChecker* capture_call_checker,
+ AudioProcessingImplLockTest* test_framework,
+ TestConfig* test_config,
+ AudioProcessing* apm)
+ : rand_gen_(rand_gen),
+ frame_counters_(shared_counters_state),
+ capture_call_checker_(capture_call_checker),
+ test_(test_framework),
+ test_config_(test_config),
+ apm_(apm),
+ frame_data_(max_frame_size) {}
+
+const float RenderProcessor::kRenderInputFloatLevel = 0.5f;
+
+RenderProcessor::RenderProcessor(int max_frame_size,
+ test::Random* rand_gen,
+ FrameCounters* shared_counters_state,
+ CaptureSideCalledChecker* capture_call_checker,
+ AudioProcessingImplLockTest* test_framework,
+ TestConfig* test_config,
+ AudioProcessing* apm)
+ : rand_gen_(rand_gen),
+ frame_counters_(shared_counters_state),
+ capture_call_checker_(capture_call_checker),
+ test_(test_framework),
+ test_config_(test_config),
+ apm_(apm),
+ frame_data_(max_frame_size) {}
+
+// Prepares the render side frame and the accompanying metadata
+// with the appropriate information.
+void RenderProcessor::PrepareFrame() {
+ // Restrict to a common fixed sample rate if the AudioFrame interface is
+ // used.
+ if ((test_config_->render_api_function ==
+ RenderApiImpl::AnalyzeReverseStreamImpl1) ||
+ (test_config_->render_api_function ==
+ RenderApiImpl::ProcessReverseStreamImpl1) ||
+ (test_config_->aec_type !=
+ AecType::BasicWebRtcAecSettingsWithAecMobile)) {
+ frame_data_.input_sample_rate_hz = test_config_->initial_sample_rate_hz;
+ frame_data_.output_sample_rate_hz = test_config_->initial_sample_rate_hz;
+ }
+
+ // Prepare the audioframe data and metadata
+ frame_data_.input_samples_per_channel =
+ frame_data_.input_sample_rate_hz * AudioProcessing::kChunkSizeMs / 1000;
+ frame_data_.frame.sample_rate_hz_ = frame_data_.input_sample_rate_hz;
+ frame_data_.frame.num_channels_ = frame_data_.input_number_of_channels;
+ frame_data_.frame.samples_per_channel_ =
+ frame_data_.input_samples_per_channel;
+ memset(frame_data_.frame.data_, 0, frame_data_.input_samples_per_channel *
+ sizeof(frame_data_.frame.data_[0]));
+ PopulateAudioFrame(&frame_data_.frame, kRenderInputFixLevel, rand_gen_);
+
+ // Prepare the float audio input data and metadata.
+ frame_data_.input_stream_config.set_sample_rate_hz(
+ frame_data_.input_sample_rate_hz);
+ frame_data_.input_stream_config.set_num_channels(
+ frame_data_.input_number_of_channels);
+ frame_data_.input_stream_config.set_has_keyboard(false);
+ PopulateAudioFrame(&frame_data_.input_frame[0], kRenderInputFloatLevel,
+ frame_data_.input_number_of_channels,
+ frame_data_.input_samples_per_channel, rand_gen_);
+ frame_data_.input_channel_layout =
+ (frame_data_.input_number_of_channels == 1
+ ? AudioProcessing::ChannelLayout::kMono
+ : AudioProcessing::ChannelLayout::kStereo);
+
+ // Prepare the float audio output data and metadata.
+ frame_data_.output_samples_per_channel =
+ frame_data_.output_sample_rate_hz * AudioProcessing::kChunkSizeMs / 1000;
+ frame_data_.output_stream_config.set_sample_rate_hz(
+ frame_data_.output_sample_rate_hz);
+ frame_data_.output_stream_config.set_num_channels(
+ frame_data_.output_number_of_channels);
+ frame_data_.output_stream_config.set_has_keyboard(false);
+ frame_data_.output_channel_layout =
+ (frame_data_.output_number_of_channels == 1
+ ? AudioProcessing::ChannelLayout::kMono
+ : AudioProcessing::ChannelLayout::kStereo);
+}
+
+// Makes the render side processing API call.
+void RenderProcessor::CallApmRenderSide() {
+ // Prepare a proper render side processing API call input.
+ PrepareFrame();
+
+ // Call the specified render side API processing method.
+ int result = AudioProcessing::kNoError;
+ switch (test_config_->render_api_function) {
+ case RenderApiImpl::ProcessReverseStreamImpl1:
+ result = apm_->ProcessReverseStream(&frame_data_.frame);
+ break;
+ case RenderApiImpl::ProcessReverseStreamImpl2:
+ result = apm_->ProcessReverseStream(
+ &frame_data_.input_frame[0], frame_data_.input_stream_config,
+ frame_data_.output_stream_config, &frame_data_.output_frame[0]);
+ break;
+ case RenderApiImpl::AnalyzeReverseStreamImpl1:
+ result = apm_->AnalyzeReverseStream(&frame_data_.frame);
+ break;
+ case RenderApiImpl::AnalyzeReverseStreamImpl2:
+ result = apm_->AnalyzeReverseStream(
+ &frame_data_.input_frame[0], frame_data_.input_samples_per_channel,
+ frame_data_.input_sample_rate_hz, frame_data_.input_channel_layout);
+ break;
+ default:
+ assert(false);
+ }
+
+ // Check the return code for error.
+ ASSERT_EQ(AudioProcessing::kNoError, result);
+}
+
+// Applies any render capture side APM API calls and audio stream
+// characteristics
+// specified by the scheme for the test.
+void RenderProcessor::ApplyRuntimeSettingScheme() {
+ const int render_count_local = frame_counters_->GetRenderCounter();
+
+ // Update the number of channels and sample rates for the input and output.
+ // Note that the counts frequencies for when to set parameters
+ // are set using prime numbers in order to ensure that the
+ // permutation scheme in the parameter setting changes.
+ switch (test_config_->runtime_parameter_setting_scheme) {
+ case RuntimeParameterSettingScheme::SparseStreamMetadataChangeScheme:
+ if (render_count_local == 0)
+ frame_data_.input_sample_rate_hz = 16000;
+ else if (render_count_local % 47 == 0)
+ frame_data_.input_sample_rate_hz = 32000;
+ else if (render_count_local % 71 == 0)
+ frame_data_.input_sample_rate_hz = 48000;
+ else if (render_count_local % 79 == 0)
+ frame_data_.input_sample_rate_hz = 16000;
+ else if (render_count_local % 83 == 0)
+ frame_data_.input_sample_rate_hz = 8000;
+
+ if (render_count_local == 0)
+ frame_data_.input_number_of_channels = 1;
+ else if (render_count_local % 4 == 0)
+ frame_data_.input_number_of_channels =
+ (frame_data_.input_number_of_channels == 1 ? 2 : 1);
+
+ if (render_count_local == 0)
+ frame_data_.output_sample_rate_hz = 16000;
+ else if (render_count_local % 17 == 0)
+ frame_data_.output_sample_rate_hz = 32000;
+ else if (render_count_local % 19 == 0)
+ frame_data_.output_sample_rate_hz = 48000;
+ else if (render_count_local % 29 == 0)
+ frame_data_.output_sample_rate_hz = 16000;
+ else if (render_count_local % 61 == 0)
+ frame_data_.output_sample_rate_hz = 8000;
+
+ if (render_count_local == 0)
+ frame_data_.output_number_of_channels = 1;
+ else if (render_count_local % 8 == 0)
+ frame_data_.output_number_of_channels =
+ (frame_data_.output_number_of_channels == 1 ? 2 : 1);
+ break;
+ case RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme:
+ if (render_count_local == 0) {
+ frame_data_.input_number_of_channels = 1;
+ frame_data_.input_sample_rate_hz = 16000;
+ frame_data_.output_number_of_channels = 1;
+ frame_data_.output_sample_rate_hz = 16000;
+ } else {
+ frame_data_.input_number_of_channels =
+ (frame_data_.input_number_of_channels == 1 ? 2 : 1);
+ if (frame_data_.input_sample_rate_hz == 8000)
+ frame_data_.input_sample_rate_hz = 16000;
+ else if (frame_data_.input_sample_rate_hz == 16000)
+ frame_data_.input_sample_rate_hz = 32000;
+ else if (frame_data_.input_sample_rate_hz == 32000)
+ frame_data_.input_sample_rate_hz = 48000;
+ else if (frame_data_.input_sample_rate_hz == 48000)
+ frame_data_.input_sample_rate_hz = 8000;
+
+ frame_data_.output_number_of_channels =
+ (frame_data_.output_number_of_channels == 1 ? 2 : 1);
+ if (frame_data_.output_sample_rate_hz == 8000)
+ frame_data_.output_sample_rate_hz = 16000;
+ else if (frame_data_.output_sample_rate_hz == 16000)
+ frame_data_.output_sample_rate_hz = 32000;
+ else if (frame_data_.output_sample_rate_hz == 32000)
+ frame_data_.output_sample_rate_hz = 48000;
+ else if (frame_data_.output_sample_rate_hz == 48000)
+ frame_data_.output_sample_rate_hz = 8000;
+ }
+ break;
+ case RuntimeParameterSettingScheme::FixedMonoStreamMetadataScheme:
+ if (render_count_local == 0) {
+ frame_data_.input_sample_rate_hz = 16000;
+ frame_data_.input_number_of_channels = 1;
+ frame_data_.output_sample_rate_hz = 16000;
+ frame_data_.output_number_of_channels = 1;
+ }
+ break;
+ case RuntimeParameterSettingScheme::FixedStereoStreamMetadataScheme:
+ if (render_count_local == 0) {
+ frame_data_.input_sample_rate_hz = 16000;
+ frame_data_.input_number_of_channels = 2;
+ frame_data_.output_sample_rate_hz = 16000;
+ frame_data_.output_number_of_channels = 2;
+ }
+
+ break;
+ default:
+ assert(false);
+ }
+
+ // Restric the number of output channels not to exceed
+ // the number of input channels.
+ frame_data_.output_number_of_channels =
+ std::min(frame_data_.output_number_of_channels,
+ frame_data_.input_number_of_channels);
+}
+
+// Implements the callback functionality for the render thread.
+bool RenderProcessor::Process() {
+ // Conditional wait to ensure that a capture call has been done
+ // before the first render call is performed (implicitly
+ // required by the APM API).
+ if (first_render_side_call_) {
+ while (!capture_call_checker_->CaptureSideCalled()) {
+ SleepRandomMs(3, rand_gen_);
+ }
+
+ first_render_side_call_ = false;
+ }
+
+ // Sleep a random time to simulate thread jitter.
+ SleepRandomMs(3, rand_gen_);
+
+ // End the test early if a fatal failure (ASSERT_*) has occurred.
+ test_->CheckTestCompleteness();
+
+ // Ensure that the number of render and capture calls do not
+ // differ too much.
+ while (kMaxCallDifference < -frame_counters_->CaptureMinusRenderCounters()) {
+ SleepMs(1);
+ }
+
+ // Apply any specified render side APM non-processing runtime calls.
+ ApplyRuntimeSettingScheme();
+
+ // Apply the render side processing call.
+ CallApmRenderSide();
+
+ // Increase the number of render-side calls.
+ frame_counters_->IncreaseRenderCounter();
+
+ return true;
+}
+
+void AudioProcessingImplLockTest::CheckTestCompleteness() {
+ if (HasFatalFailure() || TestDone()) {
+ test_complete_->Set();
+ }
+}
+
+} // anonymous namespace
+
+TEST_P(AudioProcessingImplLockTest, LockTest) {
+ // Run test and verify that it did not time out.
+ ASSERT_EQ(kEventSignaled, RunTest());
+}
+
+// Instantiate tests from the extreme test configuration set.
+INSTANTIATE_TEST_CASE_P(
+ DISABLED_AudioProcessingImplLockExtensive,
+ AudioProcessingImplLockTest,
+ ::testing::ValuesIn(TestConfig::GenerateExtensiveTestConfigs()));
+
+INSTANTIATE_TEST_CASE_P(
+ AudioProcessingImplLockBrief,
+ AudioProcessingImplLockTest,
+ ::testing::ValuesIn(TestConfig::GenerateBriefTestConfigs()));
+
+} // namespace webrtc
« no previous file with comments | « no previous file | webrtc/modules/modules.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698