1 /*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include <numeric>
12 #include <vector>
13
14 #include "webrtc/base/array_view.h"
15 #include "webrtc/base/random.h"
16 #include "webrtc/modules/audio_processing/audio_buffer.h"
17 #include "webrtc/modules/audio_processing/include/audio_processing.h"
18 #include "webrtc/modules/audio_processing/residual_echo_detector.h"
19 #include "webrtc/modules/audio_processing/test/audio_buffer_tools.h"
20 #include "webrtc/modules/audio_processing/test/performance_timer.h"
21 #include "webrtc/modules/audio_processing/test/simulator_buffers.h"
22 #include "webrtc/system_wrappers/include/clock.h"
23 #include "webrtc/test/gtest.h"
24 #include "webrtc/test/testsupport/perf_test.h"
25
26 namespace webrtc {
27 namespace {
28
29 const size_t kNumFramesToProcess = 500;
30 const size_t kProcessingBatchSize = 20;
31 const size_t kWarmupBatchSize = 2 * kProcessingBatchSize;
32 const int kSampleRate = AudioProcessing::kSampleRate48kHz;
33 const int kNumberOfChannels = 1;
34
FormPerformanceMeasureString(const test::PerformanceTimer & timer)35 std::string FormPerformanceMeasureString(const test::PerformanceTimer& timer) {
36 std::string s = std::to_string(timer.GetDurationAverage());
37 s += ", ";
38 s += std::to_string(timer.GetDurationStandardDeviation());
39 return s;
40 }
41
RunStandaloneSubmodule()42 void RunStandaloneSubmodule() {
43 test::SimulatorBuffers buffers(
44 kSampleRate, kSampleRate, kSampleRate, kSampleRate, kNumberOfChannels,
45 kNumberOfChannels, kNumberOfChannels, kNumberOfChannels);
46 test::PerformanceTimer timer(kNumFramesToProcess);
47
48 ResidualEchoDetector echo_detector;
49 echo_detector.Initialize();
50
51 for (size_t frame_no = 0; frame_no < kNumFramesToProcess; ++frame_no) {
52 // The first batch of frames are for warming up, and are not part of the
53 // benchmark. After that the processing time is measured in chunks of
54 // kProcessingBatchSize frames.
55 if (frame_no >= kWarmupBatchSize && frame_no % kProcessingBatchSize == 0) {
56 timer.StartTimer();
57 }
58
59 buffers.UpdateInputBuffers();
60 echo_detector.AnalyzeRenderAudio(rtc::ArrayView<const float>(
61 buffers.render_input_buffer->split_bands_const_f(0)[kBand0To8kHz],
62 buffers.render_input_buffer->num_frames_per_band()));
63 echo_detector.AnalyzeCaptureAudio(rtc::ArrayView<const float>(
64 buffers.capture_input_buffer->split_bands_const_f(0)[kBand0To8kHz],
65 buffers.capture_input_buffer->num_frames_per_band()));
66
67 if (frame_no >= kWarmupBatchSize &&
68 frame_no % kProcessingBatchSize == kProcessingBatchSize - 1) {
69 timer.StopTimer();
70 }
71 }
72 webrtc::test::PrintResultMeanAndError(
73 "echo_detector_call_durations", "", "StandaloneEchoDetector",
74 FormPerformanceMeasureString(timer), "us", false);
75 }
76
RunTogetherWithApm(std::string test_description,bool use_mobile_aec,bool include_default_apm_processing)77 void RunTogetherWithApm(std::string test_description,
78 bool use_mobile_aec,
79 bool include_default_apm_processing) {
80 test::SimulatorBuffers buffers(
81 kSampleRate, kSampleRate, kSampleRate, kSampleRate, kNumberOfChannels,
82 kNumberOfChannels, kNumberOfChannels, kNumberOfChannels);
83 test::PerformanceTimer timer(kNumFramesToProcess);
84
85 webrtc::Config config;
86 AudioProcessing::Config apm_config;
87 if (include_default_apm_processing) {
88 config.Set<DelayAgnostic>(new DelayAgnostic(true));
89 config.Set<ExtendedFilter>(new ExtendedFilter(true));
90 }
91 apm_config.level_controller.enabled = include_default_apm_processing;
92 apm_config.residual_echo_detector.enabled = true;
93
94 std::unique_ptr<AudioProcessing> apm;
95 apm.reset(AudioProcessing::Create(config));
96 ASSERT_TRUE(apm.get());
97 apm->ApplyConfig(apm_config);
98
99 ASSERT_EQ(AudioProcessing::kNoError,
100 apm->gain_control()->Enable(include_default_apm_processing));
101 if (use_mobile_aec) {
102 ASSERT_EQ(AudioProcessing::kNoError,
103 apm->echo_cancellation()->Enable(false));
104 ASSERT_EQ(AudioProcessing::kNoError, apm->echo_control_mobile()->Enable(
105 include_default_apm_processing));
106 } else {
107 ASSERT_EQ(AudioProcessing::kNoError,
108 apm->echo_cancellation()->Enable(include_default_apm_processing));
109 ASSERT_EQ(AudioProcessing::kNoError,
110 apm->echo_control_mobile()->Enable(false));
111 }
112 ASSERT_EQ(AudioProcessing::kNoError,
113 apm->high_pass_filter()->Enable(include_default_apm_processing));
114 ASSERT_EQ(AudioProcessing::kNoError,
115 apm->noise_suppression()->Enable(include_default_apm_processing));
116 ASSERT_EQ(AudioProcessing::kNoError,
117 apm->voice_detection()->Enable(include_default_apm_processing));
118 ASSERT_EQ(AudioProcessing::kNoError,
119 apm->level_estimator()->Enable(include_default_apm_processing));
120
121 StreamConfig stream_config(kSampleRate, kNumberOfChannels, false);
122
123 for (size_t frame_no = 0; frame_no < kNumFramesToProcess; ++frame_no) {
124 // The first batch of frames are for warming up, and are not part of the
125 // benchmark. After that the processing time is measured in chunks of
126 // kProcessingBatchSize frames.
127 if (frame_no >= kWarmupBatchSize && frame_no % kProcessingBatchSize == 0) {
128 timer.StartTimer();
129 }
130
131 buffers.UpdateInputBuffers();
132
133 ASSERT_EQ(
134 AudioProcessing::kNoError,
135 apm->ProcessReverseStream(&buffers.render_input[0], stream_config,
136 stream_config, &buffers.render_output[0]));
137
138 ASSERT_EQ(AudioProcessing::kNoError, apm->set_stream_delay_ms(0));
139 if (include_default_apm_processing) {
140 apm->gain_control()->set_stream_analog_level(0);
141 if (!use_mobile_aec) {
142 apm->echo_cancellation()->set_stream_drift_samples(0);
143 }
144 }
145 ASSERT_EQ(AudioProcessing::kNoError,
146 apm->ProcessStream(&buffers.capture_input[0], stream_config,
147 stream_config, &buffers.capture_output[0]));
148
149 if (frame_no >= kWarmupBatchSize &&
150 frame_no % kProcessingBatchSize == kProcessingBatchSize - 1) {
151 timer.StopTimer();
152 }
153 }
154
155 webrtc::test::PrintResultMeanAndError(
156 "echo_detector_call_durations", "_total", test_description,
157 FormPerformanceMeasureString(timer), "us", false);
158 }
159
160 } // namespace
161
TEST(EchoDetectorPerformanceTest,StandaloneProcessing)162 TEST(EchoDetectorPerformanceTest, StandaloneProcessing) {
163 RunStandaloneSubmodule();
164 }
165
TEST(EchoDetectorPerformanceTest,ProcessingViaApm)166 TEST(EchoDetectorPerformanceTest, ProcessingViaApm) {
167 RunTogetherWithApm("SimpleEchoDetectorViaApm", false, false);
168 }
169
TEST(EchoDetectorPerformanceTest,InteractionWithDefaultApm)170 TEST(EchoDetectorPerformanceTest, InteractionWithDefaultApm) {
171 RunTogetherWithApm("EchoDetectorAndDefaultDesktopApm", false, true);
172 RunTogetherWithApm("EchoDetectorAndDefaultMobileApm", true, true);
173 }
174
175 } // namespace webrtc
176