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

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: Various fixes in response to reviewer comments Created 5 years, 2 months 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..996675e263301cfd88a5fc5fbea234aa5dd2c7db
--- /dev/null
+++ b/webrtc/modules/audio_processing/audio_processing_impl_locking_unittest.cc
@@ -0,0 +1,1033 @@
+/*
+ * 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 {
+
+// Type of the render thread APM API call to use in the test.
+enum class RenderApiFunctionImplementation {
+ ProcessReverseStreamImplementation1,
+ ProcessReverseStreamImplementation2,
+ AnalyzeReverseStreamImplementation1,
+ AnalyzeReverseStreamImplementation2
+};
the sun 2015/10/29 10:31:30 nit: space between the enum declarations
peah-webrtc 2015/10/29 14:26:51 Done.
+// Type of the capture thread APM API call to use in the test.
+enum class CaptureApiFunctionImplementation {
+ ProcessStreamImplementation1,
+ ProcessStreamImplementation2,
+ ProcessStreamImplementation3
+};
+// The runtime parameter setting scheme to use in the test.
+enum class RuntimeParameterSettingScheme {
+ SparseStreamMetadataChangeScheme,
+ ExtremeStreamMetadataChangeScheme,
+ FixedMonoStreamMetadataScheme,
+ FixedStereoStreamMetadataScheme
+};
+enum class AecType {
+ BasicWebRtcAecSettings,
+ AecTurnedOff,
+ BasicWebRtcAecSettingsWithExtentedFilter,
+ BasicWebRtcAecSettingsWithDelayAgnosticAec,
+ BasicWebRtcAecSettingsWithAecMobile
+};
+
+// The configuration for the test to use.
+struct TestConfig {
+ RenderApiFunctionImplementation render_api_function;
+ CaptureApiFunctionImplementation capture_api_function;
+ RuntimeParameterSettingScheme runtime_parameter_setting_scheme;
+ int initial_sample_rate_hz;
+ AecType aec_type;
+ int min_number_of_calls;
+};
+
+// Class for implementing the tests of the locks in the audio processing module.
+class AudioProcessingImpLockTest : public ::testing::TestWithParam<TestConfig> {
+ public:
+ AudioProcessingImpLockTest()
+ : test_complete_(EventWrapper::Create()),
+ render_thread_(
+ ThreadWrapper::CreateThread(RenderThread, this, "render")),
+ capture_thread_(
+ ThreadWrapper::CreateThread(CaptureThread, this, "capture")),
+ stats_thread_(ThreadWrapper::CreateThread(StatsThread, this, "stats")),
+ rand_gen_(42U) {
+ // Set up the two-dimensional arrays needed for the APM API calls.
the sun 2015/10/29 10:31:30 Why doesn't this setting up go in the c-tors of th
peah-webrtc 2015/10/29 14:26:51 Done.
+ capture_thread_state_.input_framechannels_.resize(2 * 480);
the sun 2015/10/29 10:31:30 nit: Use a constant for 480
peah-webrtc 2015/10/29 14:26:51 Done.
+ capture_thread_state_.input_frame.resize(2);
+ capture_thread_state_.input_frame[0] =
+ &capture_thread_state_.input_framechannels_[0];
+ capture_thread_state_.input_frame[1] =
+ &capture_thread_state_.input_framechannels_[480];
+
+ capture_thread_state_.output_frame_channels.resize(2 * 480);
+ capture_thread_state_.output_frame.resize(2);
+ capture_thread_state_.output_frame[0] =
+ &capture_thread_state_.output_frame_channels[0];
+ capture_thread_state_.output_frame[1] =
+ &capture_thread_state_.output_frame_channels[480];
+
+ render_thread_state_.input_frame_channels.resize(2 * 480);
+ render_thread_state_.input_frame.resize(2);
+ render_thread_state_.input_frame[0] =
+ &render_thread_state_.input_frame_channels[0];
+ render_thread_state_.input_frame[1] =
+ &render_thread_state_.input_frame_channels[480];
+
+ render_thread_state_.output_frame_channels.resize(2 * 480);
+ render_thread_state_.output_frame.resize(2);
+ render_thread_state_.output_frame[0] =
+ &render_thread_state_.output_frame_channels[0];
+ render_thread_state_.output_frame[1] =
+ &render_thread_state_.output_frame_channels[480];
+ }
+
+ // Run the test with a timeout.
+ EventTypeWrapper RunTest() {
+ StartThreads();
+ return test_complete_->Wait(kTestTimeOutLimit);
+ }
+
+ void SetUp() override {
+ 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_->gain_control()->set_mode(GainControl::kFixedDigital));
+
+ 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 TearDown() override {
+ render_thread_->Stop();
+ capture_thread_->Stop();
+ stats_thread_->Stop();
+ }
+
+ // Function for generating the test configurations to use in the brief tests.
+ static std::vector<TestConfig> GenerateBriefTestConfigs() {
the sun 2015/10/29 10:31:30 Move this function out of this class. Maybe put it
peah-webrtc 2015/10/29 14:26:51 Done.
+ 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 =
+ RenderApiFunctionImplementation::ProcessReverseStreamImplementation2;
+ test_config.capture_api_function =
+ CaptureApiFunctionImplementation::ProcessStreamImplementation2;
+
+ // Create test config for the first processing API function set.
+ test_configs.push_back(test_config);
+ test_config.render_api_function =
+ RenderApiFunctionImplementation::AnalyzeReverseStreamImplementation2;
+ test_config.capture_api_function =
+ CaptureApiFunctionImplementation::ProcessStreamImplementation3;
+ test_configs.push_back(test_config);
+ }
+
+ // Return the created test configurations.
+ return test_configs;
+ }
+
+ // Function for generating the test configurations to use in the extensive
+ // tests.
+ static std::vector<TestConfig> GenerateExtensiveTestConfigs() {
the sun 2015/10/29 10:31:30 Move out of this class. Into TestConfig?
peah-webrtc 2015/10/29 14:26:51 Done.
+ std::vector<TestConfig> test_configs;
+ // Loop over all possible test configurations.
+ for (int render = static_cast<int>(RenderApiFunctionImplementation::
+ ProcessReverseStreamImplementation1);
+ render <= static_cast<int>(RenderApiFunctionImplementation::
+ AnalyzeReverseStreamImplementation2);
+ render++)
+ for (int capture = static_cast<int>(
+ CaptureApiFunctionImplementation::ProcessStreamImplementation1);
+ capture <=
+ static_cast<int>(
+ CaptureApiFunctionImplementation::ProcessStreamImplementation3);
+ capture++)
+ for (int aec = static_cast<int>(AecType::BasicWebRtcAecSettings);
+ aec <=
+ static_cast<int>(AecType::BasicWebRtcAecSettingsWithAecMobile);
+ aec++)
+ for (int scheme =
+ static_cast<int>(RuntimeParameterSettingScheme::
+ SparseStreamMetadataChangeScheme);
+ scheme <= static_cast<int>(RuntimeParameterSettingScheme::
+ FixedStereoStreamMetadataScheme);
+ scheme++) {
+ TestConfig test_config;
+ test_config.min_number_of_calls = 10000;
+
+ test_config.render_api_function =
+ static_cast<RenderApiFunctionImplementation>(render);
+ test_config.capture_api_function =
+ static_cast<CaptureApiFunctionImplementation>(capture);
+ test_config.aec_type = static_cast<AecType>(aec);
+
+ // Check that the selected render and capture API calls are
+ // compatible
+ if ((((test_config.render_api_function ==
+ RenderApiFunctionImplementation::
+ ProcessReverseStreamImplementation1) ||
+ (test_config.render_api_function ==
+ RenderApiFunctionImplementation::
+ AnalyzeReverseStreamImplementation1)) &&
+ (test_config.capture_api_function ==
+ CaptureApiFunctionImplementation::
+ ProcessStreamImplementation1)) ||
+ (((test_config.render_api_function !=
+ RenderApiFunctionImplementation::
+ ProcessReverseStreamImplementation1) &&
+ (test_config.render_api_function !=
+ RenderApiFunctionImplementation::
+ AnalyzeReverseStreamImplementation1)) &&
+ (test_config.capture_api_function !=
+ CaptureApiFunctionImplementation::
+ ProcessStreamImplementation1))) {
+ // For the compatible render and capture function combinations
+ // add test configs with different initial sample rates and
+ // parameter setting schemes.
+ test_config.runtime_parameter_setting_scheme =
+ static_cast<RuntimeParameterSettingScheme>(scheme);
+
+ 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);
+ }
+ }
+ }
+ // Return the created test configurations.
+ return test_configs;
+ }
+
+ private:
+ static const int kTestTimeOutLimit = 10 * 60 * 1000;
+ static const int kMaxCallDifference = 10;
+ static const float kRenderInputFloatLevel;
+ static const float kCaptureInputFloatLevel;
+ static const int kRenderInputFixLevel = 16384;
+ static const int kCaptureInputFixLevel = 1024;
+
+ // Generates random number between -(amplitude+1) and amplitude
+ int16_t GenerateRandomInt16(int16_t amplitude) const {
+ return rand_gen_.Rand(-amplitude, amplitude);
hlundin-webrtc 2015/10/29 09:13:22 Does this really generate "random number between -
peah-webrtc 2015/10/29 14:26:51 Good find! Fixed! Done.
+ }
+
+ // Populates a float audio frame with random data.
+ void PopulateAudioFrame(float** frame,
+ float amplitude,
+ size_t num_channels,
+ size_t samples_per_channel) const {
+ 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>(GenerateRandomInt16(32767)) /
+ 32768.0f;
+ }
+ }
+ }
+
+ // Populates an audioframe frame of AudioFrame type with random data.
+ void PopulateAudioFrame(AudioFrame* frame, int16_t amplitude) const {
+ 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 -1 and 1.
+ frame->data_[k * ch] = GenerateRandomInt16(amplitude);
+ }
+ }
+ }
+
+ // Thread callback for the render thread
+ static bool RenderThread(void* context) {
+ return reinterpret_cast<AudioProcessingImpLockTest*>(context)
+ ->RenderThreadImpl();
+ }
+
+ // Thread callback for the capture thread
+ static bool CaptureThread(void* context) {
+ return reinterpret_cast<AudioProcessingImpLockTest*>(context)
+ ->CaptureThreadImpl();
+ }
+
+ // Thread callback for the stats thread
+ static bool StatsThread(void* context) {
+ return reinterpret_cast<AudioProcessingImpLockTest*>(context)
+ ->StatsThreadImpl();
+ }
+
+ // Tests whether all the required render and capture side calls have been
+ // done.
+ bool TestDone() {
+ rtc::CritScope cs(&crit_);
+ return ((shared_thread_counter_state_.render_count >
+ test_config_.min_number_of_calls) &&
+ (shared_thread_counter_state_.capture_count >
+ test_config_.min_number_of_calls));
+ }
+
+ // Sleeps a random time between 0 and max_sleep milliseconds.
+ void SleepRandomMs(int max_sleep) const {
+ int sleeptime = rand_gen_.Rand(0, max_sleep);
+ SleepMs(sleeptime);
+ }
+
+ // Implements the callback functionality for the statistics
+ // collection thread.
+ bool StatsThreadImpl() {
+ SleepRandomMs(100);
+
+ 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;
+ }
+
+ // Implements the callback functionality for the render thread.
+ bool RenderThreadImpl() {
+ // 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 (render_thread_state_.first_render_side_call_) {
+ bool capture_side_called_local;
+ do {
+ {
+ rtc::CritScope cs(&crit_initial_sync_);
+ capture_side_called_local =
+ shared_thread_init_state_.capture_side_called;
+ }
+ SleepRandomMs(3);
+ } while (!capture_side_called_local);
+
+ render_thread_state_.first_render_side_call_ = false;
+ }
+
+ // Sleep a random time to simulate thread jitter.
+ SleepRandomMs(3);
+
+ // End the test early if a fatal failure (ASSERT_*) has occurred.
+ if (HasFatalFailure())
+ test_complete_->Set();
+
+ // Ensure that the number of render and capture calls do not
+ // differ too much.
+ int frame_counter_difference;
+ do {
+ {
+ rtc::CritScope cs(&crit_);
+ frame_counter_difference =
+ (shared_thread_counter_state_.render_count -
+ (shared_thread_counter_state_.capture_count + kMaxCallDifference));
+ }
+ if (frame_counter_difference > 0)
+ SleepMs(1);
+ } while (frame_counter_difference > 0);
+
+ // Apply any specified render side APM non-processing runtime calls.
+ ApplyRenderRuntimeSettingScheme();
+
+ // Apply the render side processing call.
+ CallRenderSide();
+
+ // Increase the number of render-side calls.
+ rtc::CritScope cs(&crit_);
+ shared_thread_counter_state_.render_count++;
+
+ return true;
+ }
+
+ // Makes the capture side processing API call.
+ void CallCaptureSide() {
+ // Prepare a proper capture side processing API call input.
+ PrepareCaptureFrame();
+
+ // 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 CaptureApiFunctionImplementation::ProcessStreamImplementation1:
+ result = apm_->ProcessStream(&capture_thread_state_.frame);
+ break;
+ case CaptureApiFunctionImplementation::ProcessStreamImplementation2:
+ result =
+ apm_->ProcessStream(&capture_thread_state_.input_frame[0],
+ capture_thread_state_.input_samples_per_channel,
+ capture_thread_state_.input_sample_rate_hz,
+ capture_thread_state_.input_channel_layout,
+ capture_thread_state_.output_sample_rate_hz,
+ capture_thread_state_.output_channel_layout,
+ &capture_thread_state_.output_frame[0]);
+ break;
+ case CaptureApiFunctionImplementation::ProcessStreamImplementation3:
+ result = apm_->ProcessStream(&capture_thread_state_.input_frame[0],
+ capture_thread_state_.input_stream_config,
+ capture_thread_state_.output_stream_config,
+ &capture_thread_state_.output_frame[0]);
+ break;
+ default:
+ assert(false);
+ }
+
+ // Check the return code for error.
+ ASSERT_EQ(AudioProcessing::kNoError, result);
+ }
+
+ // Prepares the render side frame and the accompanying metadata
+ // with the appropriate information.
+ void PrepareRenderFrame() {
+ // Restrict to a common fixed sample rate if the AudioFrame interface is
+ // used.
+ if ((test_config_.render_api_function ==
+ RenderApiFunctionImplementation::
+ AnalyzeReverseStreamImplementation1) ||
+ (test_config_.render_api_function ==
+ RenderApiFunctionImplementation::
+ ProcessReverseStreamImplementation1) ||
+ (test_config_.aec_type !=
+ AecType::BasicWebRtcAecSettingsWithAecMobile)) {
+ render_thread_state_.input_sample_rate_hz =
+ test_config_.initial_sample_rate_hz;
+ render_thread_state_.output_sample_rate_hz =
+ test_config_.initial_sample_rate_hz;
+ }
+
+ // Prepare the audioframe data and metadata
+ render_thread_state_.input_samples_per_channel =
+ render_thread_state_.input_sample_rate_hz *
+ AudioProcessing::kChunkSizeMs / 1000;
+ render_thread_state_.frame.sample_rate_hz_ =
+ render_thread_state_.input_sample_rate_hz;
+ render_thread_state_.frame.num_channels_ =
+ render_thread_state_.input_number_of_channels;
+ render_thread_state_.frame.samples_per_channel_ =
+ render_thread_state_.input_samples_per_channel;
+ memset(render_thread_state_.frame.data_, 0,
+ render_thread_state_.input_samples_per_channel *
+ sizeof(render_thread_state_.frame.data_[0]));
+ PopulateAudioFrame(&render_thread_state_.frame, kRenderInputFixLevel);
+
+ // Prepare the float audio input data and metadata.
+ render_thread_state_.input_stream_config.set_sample_rate_hz(
+ render_thread_state_.input_sample_rate_hz);
+ render_thread_state_.input_stream_config.set_num_channels(
+ render_thread_state_.input_number_of_channels);
+ render_thread_state_.input_stream_config.set_has_keyboard(false);
+ PopulateAudioFrame(&render_thread_state_.input_frame[0],
+ kRenderInputFloatLevel,
+ render_thread_state_.input_number_of_channels,
+ render_thread_state_.input_samples_per_channel);
+ render_thread_state_.input_channel_layout =
+ (render_thread_state_.input_number_of_channels == 1
+ ? AudioProcessing::ChannelLayout::kMono
+ : AudioProcessing::ChannelLayout::kStereo);
+
+ // Prepare the float audio output data and metadata.
+ render_thread_state_.output_samples_per_channel =
+ render_thread_state_.output_sample_rate_hz *
+ AudioProcessing::kChunkSizeMs / 1000;
+ render_thread_state_.output_stream_config.set_sample_rate_hz(
+ render_thread_state_.output_sample_rate_hz);
+ render_thread_state_.output_stream_config.set_num_channels(
+ render_thread_state_.output_number_of_channels);
+ render_thread_state_.output_stream_config.set_has_keyboard(false);
+ render_thread_state_.output_channel_layout =
+ (render_thread_state_.output_number_of_channels == 1
+ ? AudioProcessing::ChannelLayout::kMono
+ : AudioProcessing::ChannelLayout::kStereo);
+ }
+
+ void PrepareCaptureFrame() {
+ // Restrict to a common fixed sample rate if the AudioFrame
+ // interface is used.
+ if (test_config_.capture_api_function ==
+ CaptureApiFunctionImplementation::ProcessStreamImplementation1) {
+ capture_thread_state_.input_sample_rate_hz =
+ test_config_.initial_sample_rate_hz;
+ capture_thread_state_.output_sample_rate_hz =
+ test_config_.initial_sample_rate_hz;
+ }
+
+ // Prepare the audioframe data and metadata.
+ capture_thread_state_.input_samples_per_channel =
+ capture_thread_state_.input_sample_rate_hz *
+ AudioProcessing::kChunkSizeMs / 1000;
+ capture_thread_state_.frame.sample_rate_hz_ =
+ capture_thread_state_.input_sample_rate_hz;
+ capture_thread_state_.frame.num_channels_ =
+ capture_thread_state_.input_number_of_channels;
+ capture_thread_state_.frame.samples_per_channel_ =
+ capture_thread_state_.input_samples_per_channel;
+ memset(capture_thread_state_.frame.data_, 0,
+ capture_thread_state_.input_samples_per_channel *
+ sizeof(capture_thread_state_.frame.data_[0]));
+ PopulateAudioFrame(&capture_thread_state_.frame, kCaptureInputFixLevel);
+
+ // Prepare the float audio input data and metadata.
+ capture_thread_state_.input_stream_config.set_sample_rate_hz(
+ capture_thread_state_.input_sample_rate_hz);
+ capture_thread_state_.input_stream_config.set_num_channels(
+ capture_thread_state_.input_number_of_channels);
+ capture_thread_state_.input_stream_config.set_has_keyboard(false);
+ PopulateAudioFrame(&capture_thread_state_.input_frame[0],
+ kCaptureInputFloatLevel,
+ capture_thread_state_.input_number_of_channels,
+ capture_thread_state_.input_samples_per_channel);
+ capture_thread_state_.input_channel_layout =
+ (capture_thread_state_.input_number_of_channels == 1
+ ? AudioProcessing::ChannelLayout::kMonoAndKeyboard
+ : AudioProcessing::ChannelLayout::kStereoAndKeyboard);
+
+ // Prepare the float audio output data and metadata.
+ capture_thread_state_.output_samples_per_channel =
+ capture_thread_state_.output_sample_rate_hz *
+ AudioProcessing::kChunkSizeMs / 1000;
+ capture_thread_state_.output_stream_config.set_sample_rate_hz(
+ capture_thread_state_.output_sample_rate_hz);
+ capture_thread_state_.output_stream_config.set_num_channels(
+ capture_thread_state_.output_number_of_channels);
+ capture_thread_state_.output_stream_config.set_has_keyboard(false);
+ capture_thread_state_.output_channel_layout =
+ (capture_thread_state_.output_number_of_channels == 1
+ ? AudioProcessing::ChannelLayout::kMono
+ : AudioProcessing::ChannelLayout::kStereo);
+ }
+
+ // Applies any render capture APM API calls and audio stream characteristics
+ // specified by the scheme for the test.
+ void ApplyRenderRuntimeSettingScheme() {
+ const int render_count_local = [this] {
+ rtc::CritScope cs(&crit_);
+ return shared_thread_counter_state_.render_count;
+ }();
+
+ // 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)
+ render_thread_state_.input_sample_rate_hz = 16000;
+ else if (render_count_local % 47 == 0)
+ render_thread_state_.input_sample_rate_hz = 32000;
+ else if (render_count_local % 71 == 0)
+ render_thread_state_.input_sample_rate_hz = 48000;
+ else if (render_count_local % 79 == 0)
+ render_thread_state_.input_sample_rate_hz = 16000;
+ else if (render_count_local % 83 == 0)
+ render_thread_state_.input_sample_rate_hz = 8000;
+
+ if (render_count_local == 0)
+ render_thread_state_.input_number_of_channels = 1;
+ else if (render_count_local % 4 == 0)
+ render_thread_state_.input_number_of_channels =
+ (render_thread_state_.input_number_of_channels == 1 ? 2 : 1);
+
+ if (render_count_local == 0)
+ render_thread_state_.output_sample_rate_hz = 16000;
+ else if (render_count_local % 17 == 0)
+ render_thread_state_.output_sample_rate_hz = 32000;
+ else if (render_count_local % 19 == 0)
+ render_thread_state_.output_sample_rate_hz = 48000;
+ else if (render_count_local % 29 == 0)
+ render_thread_state_.output_sample_rate_hz = 16000;
+ else if (render_count_local % 61 == 0)
+ render_thread_state_.output_sample_rate_hz = 8000;
+
+ if (render_count_local == 0)
+ render_thread_state_.output_number_of_channels = 1;
+ else if (render_count_local % 8 == 0)
+ render_thread_state_.output_number_of_channels =
+ (render_thread_state_.output_number_of_channels == 1 ? 2 : 1);
+ break;
+ case RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme:
+ if (render_count_local == 0) {
+ render_thread_state_.input_number_of_channels = 1;
+ render_thread_state_.input_sample_rate_hz = 16000;
+ render_thread_state_.output_number_of_channels = 1;
+ render_thread_state_.output_sample_rate_hz = 16000;
+ } else {
+ render_thread_state_.input_number_of_channels =
+ (render_thread_state_.input_number_of_channels == 1 ? 2 : 1);
+ if (render_thread_state_.input_sample_rate_hz == 8000)
+ render_thread_state_.input_sample_rate_hz = 16000;
+ else if (render_thread_state_.input_sample_rate_hz == 16000)
+ render_thread_state_.input_sample_rate_hz = 32000;
+ else if (render_thread_state_.input_sample_rate_hz == 32000)
+ render_thread_state_.input_sample_rate_hz = 48000;
+ else if (render_thread_state_.input_sample_rate_hz == 48000)
+ render_thread_state_.input_sample_rate_hz = 8000;
+
+ render_thread_state_.output_number_of_channels =
+ (render_thread_state_.output_number_of_channels == 1 ? 2 : 1);
+ if (render_thread_state_.output_sample_rate_hz == 8000)
+ render_thread_state_.output_sample_rate_hz = 16000;
+ else if (render_thread_state_.output_sample_rate_hz == 16000)
+ render_thread_state_.output_sample_rate_hz = 32000;
+ else if (render_thread_state_.output_sample_rate_hz == 32000)
+ render_thread_state_.output_sample_rate_hz = 48000;
+ else if (render_thread_state_.output_sample_rate_hz == 48000)
+ render_thread_state_.output_sample_rate_hz = 8000;
+ }
+ break;
+ case RuntimeParameterSettingScheme::FixedMonoStreamMetadataScheme:
+ if (render_count_local == 0) {
+ render_thread_state_.input_sample_rate_hz = 16000;
+ render_thread_state_.input_number_of_channels = 1;
+ render_thread_state_.output_sample_rate_hz = 16000;
+ render_thread_state_.output_number_of_channels = 1;
+ }
+ break;
+ case RuntimeParameterSettingScheme::FixedStereoStreamMetadataScheme:
+ if (render_count_local == 0) {
+ render_thread_state_.input_sample_rate_hz = 16000;
+ render_thread_state_.input_number_of_channels = 2;
+ render_thread_state_.output_sample_rate_hz = 16000;
+ render_thread_state_.output_number_of_channels = 2;
+ }
+
+ break;
+ default:
+ assert(false);
+ }
+
+ // Restric the number of output channels not to exceed
+ // the number of input channels.
+ render_thread_state_.output_number_of_channels =
+ std::min(render_thread_state_.output_number_of_channels,
+ render_thread_state_.input_number_of_channels);
+ }
+
+ // Applies any runtime capture APM API calls and audio stream characteristics
+ // specified by the scheme for the test.
+ void ApplyCaptureRuntimeSettingScheme() {
+ const int capture_count_local = [this] {
+ rtc::CritScope cs(&crit_);
+ return shared_thread_counter_state_.capture_count;
+ }();
+
+ // 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)
+ capture_thread_state_.input_sample_rate_hz = 16000;
+ else if (capture_count_local % 11 == 0)
+ capture_thread_state_.input_sample_rate_hz = 32000;
+ else if (capture_count_local % 73 == 0)
+ capture_thread_state_.input_sample_rate_hz = 48000;
+ else if (capture_count_local % 89 == 0)
+ capture_thread_state_.input_sample_rate_hz = 16000;
+ else if (capture_count_local % 97 == 0)
+ capture_thread_state_.input_sample_rate_hz = 8000;
+
+ if (capture_count_local == 0)
+ capture_thread_state_.input_number_of_channels = 1;
+ else if (capture_count_local % 4 == 0)
+ capture_thread_state_.input_number_of_channels =
+ (capture_thread_state_.input_number_of_channels == 1 ? 2 : 1);
+
+ if (capture_count_local == 0)
+ capture_thread_state_.output_sample_rate_hz = 16000;
+ else if (capture_count_local % 5 == 0)
+ capture_thread_state_.output_sample_rate_hz = 32000;
+ else if (capture_count_local % 47 == 0)
+ capture_thread_state_.output_sample_rate_hz = 48000;
+ else if (capture_count_local % 53 == 0)
+ capture_thread_state_.output_sample_rate_hz = 16000;
+ else if (capture_count_local % 71 == 0)
+ capture_thread_state_.output_sample_rate_hz = 8000;
+
+ if (capture_count_local == 0)
+ capture_thread_state_.output_number_of_channels = 1;
+ else if (capture_count_local % 8 == 0)
+ capture_thread_state_.output_number_of_channels =
+ (capture_thread_state_.output_number_of_channels == 1 ? 2 : 1);
+ break;
+ case RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme:
+ if (capture_count_local % 2 == 0) {
+ capture_thread_state_.input_number_of_channels = 1;
+ capture_thread_state_.input_sample_rate_hz = 16000;
+ capture_thread_state_.output_number_of_channels = 1;
+ capture_thread_state_.output_sample_rate_hz = 16000;
+ } else {
+ capture_thread_state_.input_number_of_channels =
+ (capture_thread_state_.input_number_of_channels == 1 ? 2 : 1);
+ if (capture_thread_state_.input_sample_rate_hz == 8000)
+ capture_thread_state_.input_sample_rate_hz = 16000;
+ else if (capture_thread_state_.input_sample_rate_hz == 16000)
+ capture_thread_state_.input_sample_rate_hz = 32000;
+ else if (capture_thread_state_.input_sample_rate_hz == 32000)
+ capture_thread_state_.input_sample_rate_hz = 48000;
+ else if (capture_thread_state_.input_sample_rate_hz == 48000)
+ capture_thread_state_.input_sample_rate_hz = 8000;
+
+ capture_thread_state_.output_number_of_channels =
+ (capture_thread_state_.output_number_of_channels == 1 ? 2 : 1);
+ if (capture_thread_state_.output_sample_rate_hz == 8000)
+ capture_thread_state_.output_sample_rate_hz = 16000;
+ else if (capture_thread_state_.output_sample_rate_hz == 16000)
+ capture_thread_state_.output_sample_rate_hz = 32000;
+ else if (capture_thread_state_.output_sample_rate_hz == 32000)
+ capture_thread_state_.output_sample_rate_hz = 48000;
+ else if (capture_thread_state_.output_sample_rate_hz == 48000)
+ capture_thread_state_.output_sample_rate_hz = 8000;
+ }
+ break;
+ case RuntimeParameterSettingScheme::FixedMonoStreamMetadataScheme:
+ if (capture_count_local == 0) {
+ capture_thread_state_.input_sample_rate_hz = 16000;
+ capture_thread_state_.input_number_of_channels = 1;
+ capture_thread_state_.output_sample_rate_hz = 16000;
+ capture_thread_state_.output_number_of_channels = 1;
+ }
+ break;
+ case RuntimeParameterSettingScheme::FixedStereoStreamMetadataScheme:
+ if (capture_count_local == 0) {
+ capture_thread_state_.input_sample_rate_hz = 16000;
+ capture_thread_state_.input_number_of_channels = 2;
+ capture_thread_state_.output_sample_rate_hz = 16000;
+ capture_thread_state_.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.
+ capture_thread_state_.output_number_of_channels =
+ std::min(capture_thread_state_.output_number_of_channels,
+ capture_thread_state_.input_number_of_channels);
+ }
+
+ // Makes the render side processing API call.
+ void CallRenderSide() {
+ // Prepare a proper render side processing API call input.
+ PrepareRenderFrame();
+
+ // Call the specified render side API processing method.
+ int result = AudioProcessing::kNoError;
+ switch (test_config_.render_api_function) {
+ case RenderApiFunctionImplementation::ProcessReverseStreamImplementation1:
+ result = apm_->ProcessReverseStream(&render_thread_state_.frame);
+ break;
+ case RenderApiFunctionImplementation::ProcessReverseStreamImplementation2:
+ result = apm_->ProcessReverseStream(
+ &render_thread_state_.input_frame[0],
+ render_thread_state_.input_stream_config,
+ render_thread_state_.output_stream_config,
+ &render_thread_state_.output_frame[0]);
+ break;
+ case RenderApiFunctionImplementation::AnalyzeReverseStreamImplementation1:
+ result = apm_->AnalyzeReverseStream(&render_thread_state_.frame);
+ break;
+ case RenderApiFunctionImplementation::AnalyzeReverseStreamImplementation2:
+ result = apm_->AnalyzeReverseStream(
+ &render_thread_state_.input_frame[0],
+ render_thread_state_.input_samples_per_channel,
+ render_thread_state_.input_sample_rate_hz,
+ render_thread_state_.input_channel_layout);
+ break;
+ default:
+ assert(false);
+ }
+
+ // Check the return code for error.
+ ASSERT_EQ(AudioProcessing::kNoError, result);
+ }
+
+ // Implements the callback functionality for the capture thread.
+ bool CaptureThreadImpl() {
+ // Sleep a random time to simulate thread jitter.
+ SleepRandomMs(3);
+
+ // End the test early if a fatal failure (ASSERT_*) has occurred.
+ if (HasFatalFailure())
+ test_complete_->Set();
+
+ // Ensure that there are not more capture side calls than render side
+ // calls.
+ int frame_counter_difference;
+ do {
+ {
+ rtc::CritScope cs(&crit_);
+ frame_counter_difference = shared_thread_counter_state_.capture_count -
+ shared_thread_counter_state_.render_count;
+ }
+ if (frame_counter_difference > 0)
+ SleepMs(1);
+ } while (frame_counter_difference > 0);
+
+ // Apply any specified capture side APM non-processing runtime calls.
+ ApplyCaptureRuntimeSettingScheme();
+
+ // Apply the capture side processing call.
+ CallCaptureSide();
+
+ // Increase the number of capture-side calls.
+ {
+ rtc::CritScope cs(&crit_);
+ shared_thread_counter_state_.capture_count++;
+ }
+
+ // Check if the test is done.
+ if (TestDone())
+ test_complete_->Set();
+
+ // 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).
+ {
+ rtc::CritScope cs(&crit_initial_sync_);
+ shared_thread_init_state_.capture_side_called = true;
+ }
+
+ return true;
+ }
+
+ // 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::CriticalSection crit_;
+ rtc::CriticalSection crit_initial_sync_;
+ rtc::scoped_ptr<ThreadWrapper> render_thread_;
+ rtc::scoped_ptr<ThreadWrapper> capture_thread_;
+ rtc::scoped_ptr<ThreadWrapper> stats_thread_;
+ mutable test::Random rand_gen_;
+
+ // The APM object.
+ rtc::scoped_ptr<AudioProcessing> apm_;
+
+ // The test configuration.
+ TestConfig test_config_;
+
+ // Variables shared by the threads during the whole test.
+ struct {
+ int render_count = 0;
+ int capture_count = 0;
+ } shared_thread_counter_state_ GUARDED_BY(crit_);
+
+ // Variable shared by the threads during the initialization phase.
+ struct {
+ bool capture_side_called;
+ } shared_thread_init_state_ GUARDED_BY(crit_initial_sync_);
+
+ // Variables only used on the capture side thread.
+ struct {
+ // Variables related to the capture side audio data and formats.
+ 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;
+ } capture_thread_state_;
+
+ // Variables only used on the render side thread.
+ struct {
+ bool first_render_side_call_ = true;
+
+ // Variables related to the render side audio data and formats.
+ 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_frame_channels;
+ 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;
+ } render_thread_state_;
+};
+
+const float AudioProcessingImpLockTest::kRenderInputFloatLevel = 0.5f;
+const float AudioProcessingImpLockTest::kCaptureInputFloatLevel = 0.03125f;
+
+} // anonymous namespace
+
+TEST_P(AudioProcessingImpLockTest, 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_AudioProcessingImpLockExtensive,
+ AudioProcessingImpLockTest,
+ ::testing::ValuesIn(
+ AudioProcessingImpLockTest::GenerateExtensiveTestConfigs()));
+
+INSTANTIATE_TEST_CASE_P(
+ AudioProcessingImpLockBrief,
+ AudioProcessingImpLockTest,
+ ::testing::ValuesIn(
+ AudioProcessingImpLockTest::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