| Index: webrtc/modules/audio_processing/audio_processing_performance_unittest.cc
|
| diff --git a/webrtc/modules/audio_processing/audio_processing_performance_unittest.cc b/webrtc/modules/audio_processing/audio_processing_performance_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0c8c060ea3b82bc30a2c79be664f7fe67716c61e
|
| --- /dev/null
|
| +++ b/webrtc/modules/audio_processing/audio_processing_performance_unittest.cc
|
| @@ -0,0 +1,724 @@
|
| +/*
|
| + * 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 <math.h>
|
| +
|
| +#include <algorithm>
|
| +#include <vector>
|
| +
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "webrtc/base/array_view.h"
|
| +#include "webrtc/base/criticalsection.h"
|
| +#include "webrtc/base/platform_thread.h"
|
| +#include "webrtc/base/random.h"
|
| +#include "webrtc/base/safe_conversions.h"
|
| +#include "webrtc/config.h"
|
| +#include "webrtc/modules/audio_processing/test/test_utils.h"
|
| +#include "webrtc/modules/include/module_common_types.h"
|
| +#include "webrtc/system_wrappers/include/clock.h"
|
| +#include "webrtc/system_wrappers/include/event_wrapper.h"
|
| +#include "webrtc/system_wrappers/include/sleep.h"
|
| +#include "webrtc/test/testsupport/perf_test.h"
|
| +
|
| +namespace webrtc {
|
| +
|
| +namespace {
|
| +
|
| +static const bool kPrintAllDurations = false;
|
| +
|
| +class CallSimulator;
|
| +
|
| +// Type of the render thread APM API call to use in the test.
|
| +enum class ProcessorType { kRender, kCapture };
|
| +
|
| +// Variant of APM processing settings to use in the test.
|
| +enum class SettingsType {
|
| + kDefaultApmDesktop,
|
| + kDefaultApmMobile,
|
| + kDefaultApmDesktopAndBeamformer,
|
| + kDefaultApmDesktopAndIntelligibilityEnhancer,
|
| + kAllSubmodulesTurnedOff,
|
| + kDefaultDesktopApmWithoutDelayAgnostic,
|
| + kDefaultDesktopApmWithoutExtendedFilter
|
| +};
|
| +
|
| +// Variables related to the audio data and formats.
|
| +struct AudioFrameData {
|
| + explicit AudioFrameData(size_t 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];
|
| + }
|
| +
|
| + std::vector<float> output_frame_channels;
|
| + std::vector<float*> output_frame;
|
| + std::vector<float> input_framechannels;
|
| + std::vector<float*> input_frame;
|
| + StreamConfig input_stream_config;
|
| + StreamConfig output_stream_config;
|
| +};
|
| +
|
| +// The configuration for the test.
|
| +struct SimulationConfig {
|
| + SimulationConfig(int sample_rate_hz, SettingsType simulation_settings)
|
| + : sample_rate_hz(sample_rate_hz),
|
| + simulation_settings(simulation_settings) {}
|
| +
|
| + static std::vector<SimulationConfig> GenerateSimulationConfigs() {
|
| + std::vector<SimulationConfig> simulation_configs;
|
| +#ifndef WEBRTC_ANDROID
|
| + const SettingsType desktop_settings[] = {
|
| + SettingsType::kDefaultApmDesktop, SettingsType::kAllSubmodulesTurnedOff,
|
| + SettingsType::kDefaultDesktopApmWithoutDelayAgnostic,
|
| + SettingsType::kDefaultDesktopApmWithoutExtendedFilter};
|
| +
|
| + const int desktop_sample_rates[] = {8000, 16000, 32000, 48000};
|
| +
|
| + for (auto sample_rate : desktop_sample_rates) {
|
| + for (auto settings : desktop_settings) {
|
| + simulation_configs.push_back(SimulationConfig(sample_rate, settings));
|
| + }
|
| + }
|
| +
|
| + const SettingsType intelligibility_enhancer_settings[] = {
|
| + SettingsType::kDefaultApmDesktopAndIntelligibilityEnhancer};
|
| +
|
| + const int intelligibility_enhancer_sample_rates[] = {8000, 16000, 32000,
|
| + 48000};
|
| +
|
| + for (auto sample_rate : intelligibility_enhancer_sample_rates) {
|
| + for (auto settings : intelligibility_enhancer_settings) {
|
| + simulation_configs.push_back(SimulationConfig(sample_rate, settings));
|
| + }
|
| + }
|
| +
|
| + const SettingsType beamformer_settings[] = {
|
| + SettingsType::kDefaultApmDesktopAndBeamformer};
|
| +
|
| + const int beamformer_sample_rates[] = {8000, 16000, 32000, 48000};
|
| +
|
| + for (auto sample_rate : beamformer_sample_rates) {
|
| + for (auto settings : beamformer_settings) {
|
| + simulation_configs.push_back(SimulationConfig(sample_rate, settings));
|
| + }
|
| + }
|
| +#endif
|
| +
|
| + const SettingsType mobile_settings[] = {SettingsType::kDefaultApmMobile};
|
| +
|
| + const int mobile_sample_rates[] = {8000, 16000};
|
| +
|
| + for (auto sample_rate : mobile_sample_rates) {
|
| + for (auto settings : mobile_settings) {
|
| + simulation_configs.push_back(SimulationConfig(sample_rate, settings));
|
| + }
|
| + }
|
| +
|
| + return simulation_configs;
|
| + }
|
| +
|
| + std::string SettingsDescription() const {
|
| + std::string description;
|
| + switch (simulation_settings) {
|
| + case SettingsType::kDefaultApmMobile:
|
| + description = "DefaultApmMobile";
|
| + break;
|
| + case SettingsType::kDefaultApmDesktop:
|
| + description = "DefaultApmDesktop";
|
| + break;
|
| + case SettingsType::kDefaultApmDesktopAndBeamformer:
|
| + description = "DefaultApmDesktopAndBeamformer";
|
| + break;
|
| + case SettingsType::kDefaultApmDesktopAndIntelligibilityEnhancer:
|
| + description = "DefaultApmDesktopAndIntelligibilityEnhancer";
|
| + break;
|
| + case SettingsType::kAllSubmodulesTurnedOff:
|
| + description = "AllSubmodulesOff";
|
| + break;
|
| + case SettingsType::kDefaultDesktopApmWithoutDelayAgnostic:
|
| + description = "DefaultDesktopApmWithoutDelayAgnostic";
|
| + break;
|
| + case SettingsType::kDefaultDesktopApmWithoutExtendedFilter:
|
| + description = "DefaultDesktopApmWithoutExtendedFilter";
|
| + break;
|
| + }
|
| + return description;
|
| + }
|
| +
|
| + int sample_rate_hz = 16000;
|
| + SettingsType simulation_settings = SettingsType::kDefaultApmDesktop;
|
| +};
|
| +
|
| +// 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() const {
|
| + rtc::CritScope cs(&crit_);
|
| + return capture_count_;
|
| + }
|
| +
|
| + int GetRenderCounter() const {
|
| + rtc::CritScope cs(&crit_);
|
| + return render_count_;
|
| + }
|
| +
|
| + int CaptureMinusRenderCounters() const {
|
| + rtc::CritScope cs(&crit_);
|
| + return capture_count_ - render_count_;
|
| + }
|
| +
|
| + int RenderMinusCaptureCounters() const {
|
| + return -CaptureMinusRenderCounters();
|
| + }
|
| +
|
| + bool BothCountersExceedeThreshold(int threshold) const {
|
| + rtc::CritScope cs(&crit_);
|
| + return (render_count_ > threshold && capture_count_ > threshold);
|
| + }
|
| +
|
| + private:
|
| + mutable rtc::CriticalSection crit_;
|
| + int render_count_ GUARDED_BY(crit_) = 0;
|
| + int capture_count_ GUARDED_BY(crit_) = 0;
|
| +};
|
| +
|
| +// Class that protects a flag using a lock.
|
| +class LockedFlag {
|
| + public:
|
| + bool get_flag() const {
|
| + rtc::CritScope cs(&crit_);
|
| + return flag_;
|
| + }
|
| +
|
| + void set_flag() {
|
| + rtc::CritScope cs(&crit_);
|
| + flag_ = true;
|
| + }
|
| +
|
| + private:
|
| + mutable rtc::CriticalSection crit_;
|
| + bool flag_ GUARDED_BY(crit_) = false;
|
| +};
|
| +
|
| +// Parent class for the thread processors.
|
| +class TimedThreadApiProcessor {
|
| + public:
|
| + TimedThreadApiProcessor(ProcessorType processor_type,
|
| + Random* rand_gen,
|
| + FrameCounters* shared_counters_state,
|
| + LockedFlag* capture_call_checker,
|
| + CallSimulator* test_framework,
|
| + const SimulationConfig* simulation_config,
|
| + AudioProcessing* apm,
|
| + int num_durations_to_store,
|
| + float input_level,
|
| + int num_channels)
|
| + : rand_gen_(rand_gen),
|
| + frame_counters_(shared_counters_state),
|
| + capture_call_checker_(capture_call_checker),
|
| + test_(test_framework),
|
| + simulation_config_(simulation_config),
|
| + apm_(apm),
|
| + frame_data_(kMaxFrameSize),
|
| + clock_(webrtc::Clock::GetRealTimeClock()),
|
| + num_durations_to_store_(num_durations_to_store),
|
| + input_level_(input_level),
|
| + processor_type_(processor_type),
|
| + num_channels_(num_channels) {
|
| + api_call_durations_.reserve(num_durations_to_store_);
|
| + }
|
| +
|
| + // Implements the callback functionality for the threads.
|
| + bool Process();
|
| +
|
| + // Method for printing out the simulation statistics.
|
| + void print_processor_statistics(std::string processor_name) const {
|
| + const std::string modifier = "_api_call_duration";
|
| +
|
| + // Lambda function for creating a test printout string.
|
| + auto create_mean_and_std_string = [](int64_t average,
|
| + int64_t standard_dev) {
|
| + std::string s = std::to_string(average);
|
| + s += ", ";
|
| + s += std::to_string(standard_dev);
|
| + return s;
|
| + };
|
| +
|
| + const std::string sample_rate_name =
|
| + "_" + std::to_string(simulation_config_->sample_rate_hz) + "Hz";
|
| +
|
| + webrtc::test::PrintResultMeanAndError(
|
| + "apm_timing", sample_rate_name, processor_name,
|
| + create_mean_and_std_string(GetDurationAverage(),
|
| + GetDurationStandardDeviation()),
|
| + "us", false);
|
| +
|
| + if (kPrintAllDurations) {
|
| + std::string value_string = "";
|
| + for (int64_t duration : api_call_durations_) {
|
| + value_string += std::to_string(duration) + ",";
|
| + }
|
| + webrtc::test::PrintResultList("apm_call_durations", sample_rate_name,
|
| + processor_name, value_string, "us", false);
|
| + }
|
| + }
|
| +
|
| + void AddDuration(int64_t duration) {
|
| + if (api_call_durations_.size() < num_durations_to_store_) {
|
| + api_call_durations_.push_back(duration);
|
| + }
|
| + }
|
| +
|
| + private:
|
| + static const int kMaxCallDifference = 10;
|
| + static const int kMaxFrameSize = 480;
|
| + static const int kNumInitializationFrames = 5;
|
| +
|
| + int64_t GetDurationStandardDeviation() const {
|
| + double variance = 0;
|
| + const int64_t average_duration = GetDurationAverage();
|
| + for (size_t k = kNumInitializationFrames; k < api_call_durations_.size();
|
| + k++) {
|
| + int64_t tmp = api_call_durations_[k] - average_duration;
|
| + variance += static_cast<double>(tmp * tmp);
|
| + }
|
| + const int denominator = rtc::checked_cast<int>(api_call_durations_.size()) -
|
| + kNumInitializationFrames;
|
| + return (denominator > 0
|
| + ? rtc::checked_cast<int64_t>(sqrt(variance / denominator))
|
| + : -1);
|
| + }
|
| +
|
| + int64_t GetDurationAverage() const {
|
| + int64_t average_duration = 0;
|
| + for (size_t k = kNumInitializationFrames; k < api_call_durations_.size();
|
| + k++) {
|
| + average_duration += api_call_durations_[k];
|
| + }
|
| + const int denominator = rtc::checked_cast<int>(api_call_durations_.size()) -
|
| + kNumInitializationFrames;
|
| + return (denominator > 0 ? average_duration / denominator : -1);
|
| + }
|
| +
|
| + int ProcessCapture() {
|
| + // Set the stream delay.
|
| + apm_->set_stream_delay_ms(30);
|
| +
|
| + // Call and time the specified capture side API processing method.
|
| + const int64_t start_time = clock_->TimeInMicroseconds();
|
| + const int result = apm_->ProcessStream(
|
| + &frame_data_.input_frame[0], frame_data_.input_stream_config,
|
| + frame_data_.output_stream_config, &frame_data_.output_frame[0]);
|
| + const int64_t end_time = clock_->TimeInMicroseconds();
|
| +
|
| + frame_counters_->IncreaseCaptureCounter();
|
| +
|
| + AddDuration(end_time - start_time);
|
| +
|
| + if (first_process_call_) {
|
| + // 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_->set_flag();
|
| + first_process_call_ = false;
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + bool ReadyToProcessCapture() {
|
| + return (frame_counters_->CaptureMinusRenderCounters() <=
|
| + kMaxCallDifference);
|
| + }
|
| +
|
| + int ProcessRender() {
|
| + // Call and time the specified render side API processing method.
|
| + const int64_t start_time = clock_->TimeInMicroseconds();
|
| + const int result = apm_->ProcessReverseStream(
|
| + &frame_data_.input_frame[0], frame_data_.input_stream_config,
|
| + frame_data_.output_stream_config, &frame_data_.output_frame[0]);
|
| + const int64_t end_time = clock_->TimeInMicroseconds();
|
| + frame_counters_->IncreaseRenderCounter();
|
| +
|
| + AddDuration(end_time - start_time);
|
| +
|
| + return result;
|
| + }
|
| +
|
| + bool ReadyToProcessRender() {
|
| + // Do not process until at least one capture call has been done.
|
| + // (implicitly required by the APM API).
|
| + if (first_process_call_ && !capture_call_checker_->get_flag()) {
|
| + return false;
|
| + }
|
| +
|
| + // Ensure that the number of render and capture calls do not differ too
|
| + // much.
|
| + if (frame_counters_->RenderMinusCaptureCounters() > kMaxCallDifference) {
|
| + return false;
|
| + }
|
| +
|
| + first_process_call_ = false;
|
| + return true;
|
| + }
|
| +
|
| + void PrepareFrame() {
|
| + // Lambda function for populating a float multichannel audio frame
|
| + // with random data.
|
| + auto populate_audio_frame = [](float amplitude, size_t num_channels,
|
| + size_t samples_per_channel, Random* rand_gen,
|
| + float** frame) {
|
| + for (size_t ch = 0; ch < num_channels; ch++) {
|
| + for (size_t k = 0; k < samples_per_channel; k++) {
|
| + // Store random float number with a value between +-amplitude.
|
| + frame[ch][k] = amplitude * (2 * rand_gen->Rand<float>() - 1);
|
| + }
|
| + }
|
| + };
|
| +
|
| + // Prepare the audio input data and metadata.
|
| + frame_data_.input_stream_config.set_sample_rate_hz(
|
| + simulation_config_->sample_rate_hz);
|
| + frame_data_.input_stream_config.set_num_channels(num_channels_);
|
| + frame_data_.input_stream_config.set_has_keyboard(false);
|
| + populate_audio_frame(input_level_, num_channels_,
|
| + (simulation_config_->sample_rate_hz *
|
| + AudioProcessing::kChunkSizeMs / 1000),
|
| + rand_gen_, &frame_data_.input_frame[0]);
|
| +
|
| + // Prepare the float audio output data and metadata.
|
| + frame_data_.output_stream_config.set_sample_rate_hz(
|
| + simulation_config_->sample_rate_hz);
|
| + frame_data_.output_stream_config.set_num_channels(1);
|
| + frame_data_.output_stream_config.set_has_keyboard(false);
|
| + }
|
| +
|
| + bool ReadyToProcess() {
|
| + switch (processor_type_) {
|
| + case ProcessorType::kRender:
|
| + return ReadyToProcessRender();
|
| + break;
|
| + case ProcessorType::kCapture:
|
| + return ReadyToProcessCapture();
|
| + break;
|
| + }
|
| +
|
| + // Should not be reached, but the return statement is needed for the code to
|
| + // build successfully on Android.
|
| + RTC_NOTREACHED();
|
| + return false;
|
| + }
|
| +
|
| + Random* rand_gen_ = nullptr;
|
| + FrameCounters* frame_counters_ = nullptr;
|
| + LockedFlag* capture_call_checker_ = nullptr;
|
| + CallSimulator* test_ = nullptr;
|
| + const SimulationConfig* const simulation_config_ = nullptr;
|
| + AudioProcessing* apm_ = nullptr;
|
| + AudioFrameData frame_data_;
|
| + webrtc::Clock* clock_;
|
| + const size_t num_durations_to_store_;
|
| + std::vector<int64_t> api_call_durations_;
|
| + const float input_level_;
|
| + bool first_process_call_ = true;
|
| + const ProcessorType processor_type_;
|
| + const int num_channels_ = 1;
|
| +};
|
| +
|
| +// Class for managing the test simulation.
|
| +class CallSimulator : public ::testing::TestWithParam<SimulationConfig> {
|
| + public:
|
| + CallSimulator()
|
| + : test_complete_(EventWrapper::Create()),
|
| + render_thread_(
|
| + new rtc::PlatformThread(RenderProcessorThreadFunc, this, "render")),
|
| + capture_thread_(new rtc::PlatformThread(CaptureProcessorThreadFunc,
|
| + this,
|
| + "capture")),
|
| + rand_gen_(42U),
|
| + simulation_config_(static_cast<SimulationConfig>(GetParam())) {}
|
| +
|
| + // Run the call simulation with a timeout.
|
| + EventTypeWrapper Run() {
|
| + StartThreads();
|
| +
|
| + EventTypeWrapper result = test_complete_->Wait(kTestTimeout);
|
| +
|
| + StopThreads();
|
| +
|
| + render_thread_state_->print_processor_statistics(
|
| + simulation_config_.SettingsDescription() + "_render");
|
| + capture_thread_state_->print_processor_statistics(
|
| + simulation_config_.SettingsDescription() + "_capture");
|
| +
|
| + return result;
|
| + }
|
| +
|
| + // Tests whether all the required render and capture side calls have been
|
| + // done.
|
| + bool MaybeEndTest() {
|
| + if (frame_counters_.BothCountersExceedeThreshold(kMinNumFramesToProcess)) {
|
| + test_complete_->Set();
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + private:
|
| + static const float kCaptureInputFloatLevel;
|
| + static const float kRenderInputFloatLevel;
|
| + static const int kMinNumFramesToProcess = 150;
|
| + static const int32_t kTestTimeout = 3 * 10 * kMinNumFramesToProcess;
|
| +
|
| + // ::testing::TestWithParam<> implementation.
|
| + void TearDown() override { StopThreads(); }
|
| +
|
| + // Stop all running threads.
|
| + void StopThreads() {
|
| + render_thread_->Stop();
|
| + capture_thread_->Stop();
|
| + }
|
| +
|
| + // Simulator and APM setup.
|
| + void SetUp() override {
|
| + // Lambda function for setting the default APM runtime settings for desktop.
|
| + auto set_default_desktop_apm_runtime_settings = [](AudioProcessing* apm) {
|
| + 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::kAdaptiveDigital));
|
| + 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));
|
| + 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));
|
| + };
|
| +
|
| + // Lambda function for setting the default APM runtime settings for mobile.
|
| + auto set_default_mobile_apm_runtime_settings = [](AudioProcessing* apm) {
|
| + 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::kAdaptiveDigital));
|
| + 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));
|
| + ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
|
| + ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(false));
|
| + };
|
| +
|
| + // Lambda function for turning off all of the APM runtime settings
|
| + // submodules.
|
| + auto turn_off_default_apm_runtime_settings = [](AudioProcessing* apm) {
|
| + ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(false));
|
| + ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(false));
|
| + ASSERT_EQ(apm->kNoError,
|
| + apm->gain_control()->set_mode(GainControl::kAdaptiveDigital));
|
| + ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(false));
|
| + ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(false));
|
| + ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(false));
|
| + ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(false));
|
| + ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(false));
|
| + ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->enable_metrics(false));
|
| + ASSERT_EQ(apm->kNoError,
|
| + apm->echo_cancellation()->enable_delay_logging(false));
|
| + };
|
| +
|
| + // Lambda function for adding default desktop APM settings to a config.
|
| + auto add_default_desktop_config = [](Config* config) {
|
| + config->Set<ExtendedFilter>(new ExtendedFilter(true));
|
| + config->Set<DelayAgnostic>(new DelayAgnostic(true));
|
| + };
|
| +
|
| + // Lambda function for adding beamformer settings to a config.
|
| + auto add_beamformer_config = [](Config* config) {
|
| + const size_t num_mics = 2;
|
| + const std::vector<Point> array_geometry =
|
| + ParseArrayGeometry("0 0 0 0.05 0 0", num_mics);
|
| + RTC_CHECK_EQ(array_geometry.size(), num_mics);
|
| +
|
| + config->Set<Beamforming>(
|
| + new Beamforming(true, array_geometry,
|
| + SphericalPointf(DegreesToRadians(90), 0.f, 1.f)));
|
| + };
|
| +
|
| + int num_capture_channels = 1;
|
| + switch (simulation_config_.simulation_settings) {
|
| + case SettingsType::kDefaultApmMobile: {
|
| + apm_.reset(AudioProcessingImpl::Create());
|
| + ASSERT_TRUE(!!apm_);
|
| + set_default_mobile_apm_runtime_settings(apm_.get());
|
| + break;
|
| + }
|
| + case SettingsType::kDefaultApmDesktop: {
|
| + Config config;
|
| + add_default_desktop_config(&config);
|
| + apm_.reset(AudioProcessingImpl::Create(config));
|
| + ASSERT_TRUE(!!apm_);
|
| + set_default_desktop_apm_runtime_settings(apm_.get());
|
| + apm_->SetExtraOptions(config);
|
| + break;
|
| + }
|
| + case SettingsType::kDefaultApmDesktopAndBeamformer: {
|
| + Config config;
|
| + add_beamformer_config(&config);
|
| + add_default_desktop_config(&config);
|
| + apm_.reset(AudioProcessingImpl::Create(config));
|
| + ASSERT_TRUE(!!apm_);
|
| + set_default_desktop_apm_runtime_settings(apm_.get());
|
| + apm_->SetExtraOptions(config);
|
| + num_capture_channels = 2;
|
| + break;
|
| + }
|
| + case SettingsType::kDefaultApmDesktopAndIntelligibilityEnhancer: {
|
| + Config config;
|
| + config.Set<Intelligibility>(new Intelligibility(true));
|
| + add_default_desktop_config(&config);
|
| + apm_.reset(AudioProcessingImpl::Create(config));
|
| + ASSERT_TRUE(!!apm_);
|
| + set_default_desktop_apm_runtime_settings(apm_.get());
|
| + apm_->SetExtraOptions(config);
|
| + break;
|
| + }
|
| + case SettingsType::kAllSubmodulesTurnedOff: {
|
| + apm_.reset(AudioProcessingImpl::Create());
|
| + ASSERT_TRUE(!!apm_);
|
| + turn_off_default_apm_runtime_settings(apm_.get());
|
| + break;
|
| + }
|
| + case SettingsType::kDefaultDesktopApmWithoutDelayAgnostic: {
|
| + Config config;
|
| + config.Set<ExtendedFilter>(new ExtendedFilter(true));
|
| + config.Set<DelayAgnostic>(new DelayAgnostic(false));
|
| + apm_.reset(AudioProcessingImpl::Create(config));
|
| + ASSERT_TRUE(!!apm_);
|
| + set_default_desktop_apm_runtime_settings(apm_.get());
|
| + apm_->SetExtraOptions(config);
|
| + break;
|
| + }
|
| + case SettingsType::kDefaultDesktopApmWithoutExtendedFilter: {
|
| + Config config;
|
| + config.Set<ExtendedFilter>(new ExtendedFilter(false));
|
| + config.Set<DelayAgnostic>(new DelayAgnostic(true));
|
| + apm_.reset(AudioProcessingImpl::Create(config));
|
| + ASSERT_TRUE(!!apm_);
|
| + set_default_desktop_apm_runtime_settings(apm_.get());
|
| + apm_->SetExtraOptions(config);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + render_thread_state_.reset(new TimedThreadApiProcessor(
|
| + ProcessorType::kRender, &rand_gen_, &frame_counters_,
|
| + &capture_call_checker_, this, &simulation_config_, apm_.get(),
|
| + kMinNumFramesToProcess, kRenderInputFloatLevel, 1));
|
| + capture_thread_state_.reset(new TimedThreadApiProcessor(
|
| + ProcessorType::kCapture, &rand_gen_, &frame_counters_,
|
| + &capture_call_checker_, this, &simulation_config_, apm_.get(),
|
| + kMinNumFramesToProcess, kCaptureInputFloatLevel, num_capture_channels));
|
| + }
|
| +
|
| + // Thread callback for the render thread.
|
| + static bool RenderProcessorThreadFunc(void* context) {
|
| + return reinterpret_cast<CallSimulator*>(context)
|
| + ->render_thread_state_->Process();
|
| + }
|
| +
|
| + // Thread callback for the capture thread.
|
| + static bool CaptureProcessorThreadFunc(void* context) {
|
| + return reinterpret_cast<CallSimulator*>(context)
|
| + ->capture_thread_state_->Process();
|
| + }
|
| +
|
| + // Start the threads used in the test.
|
| + void StartThreads() {
|
| + ASSERT_NO_FATAL_FAILURE(render_thread_->Start());
|
| + render_thread_->SetPriority(rtc::kRealtimePriority);
|
| + ASSERT_NO_FATAL_FAILURE(capture_thread_->Start());
|
| + capture_thread_->SetPriority(rtc::kRealtimePriority);
|
| + }
|
| +
|
| + // Event handler for the test.
|
| + const rtc::scoped_ptr<EventWrapper> test_complete_;
|
| +
|
| + // Thread related variables.
|
| + rtc::scoped_ptr<rtc::PlatformThread> render_thread_;
|
| + rtc::scoped_ptr<rtc::PlatformThread> capture_thread_;
|
| + Random rand_gen_;
|
| +
|
| + rtc::scoped_ptr<AudioProcessing> apm_;
|
| + const SimulationConfig simulation_config_;
|
| + FrameCounters frame_counters_;
|
| + LockedFlag capture_call_checker_;
|
| + rtc::scoped_ptr<TimedThreadApiProcessor> render_thread_state_;
|
| + rtc::scoped_ptr<TimedThreadApiProcessor> capture_thread_state_;
|
| +};
|
| +
|
| +// Implements the callback functionality for the threads.
|
| +bool TimedThreadApiProcessor::Process() {
|
| + PrepareFrame();
|
| +
|
| + // Wait in a spinlock manner until it is ok to start processing.
|
| + // Note that SleepMs is not applicable since it only allows sleeping
|
| + // on a millisecond basis which is too long.
|
| + while (!ReadyToProcess()) {
|
| + }
|
| +
|
| + int result = AudioProcessing::kNoError;
|
| + switch (processor_type_) {
|
| + case ProcessorType::kRender:
|
| + result = ProcessRender();
|
| + break;
|
| + case ProcessorType::kCapture:
|
| + result = ProcessCapture();
|
| + break;
|
| + }
|
| +
|
| + EXPECT_EQ(result, AudioProcessing::kNoError);
|
| +
|
| + return !test_->MaybeEndTest();
|
| +}
|
| +
|
| +const float CallSimulator::kRenderInputFloatLevel = 0.5f;
|
| +const float CallSimulator::kCaptureInputFloatLevel = 0.03125f;
|
| +} // anonymous namespace
|
| +
|
| +TEST_P(CallSimulator, ApiCallDurationTest) {
|
| + // Run test and verify that it did not time out.
|
| + EXPECT_EQ(kEventSignaled, Run());
|
| +}
|
| +
|
| +INSTANTIATE_TEST_CASE_P(
|
| + AudioProcessingPerformanceTest,
|
| + CallSimulator,
|
| + ::testing::ValuesIn(SimulationConfig::GenerateSimulationConfigs()));
|
| +
|
| +} // namespace webrtc
|
|
|