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

Side by Side Diff: webrtc/modules/audio_device/audio_device_unittest.cc

Issue 2826073002: Adds AudioDeviceTest.MeasureLoopbackLatency unittest (Closed)
Patch Set: Fixed comment Created 3 years, 8 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 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 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 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 #include <algorithm>
11 #include <cstring> 12 #include <cstring>
12 13
13 #include "webrtc/base/array_view.h" 14 #include "webrtc/base/array_view.h"
14 #include "webrtc/base/buffer.h" 15 #include "webrtc/base/buffer.h"
15 #include "webrtc/base/criticalsection.h" 16 #include "webrtc/base/criticalsection.h"
16 #include "webrtc/base/event.h" 17 #include "webrtc/base/event.h"
17 #include "webrtc/base/logging.h" 18 #include "webrtc/base/logging.h"
18 #include "webrtc/base/race_checker.h" 19 #include "webrtc/base/race_checker.h"
19 #include "webrtc/base/scoped_ref_ptr.h" 20 #include "webrtc/base/scoped_ref_ptr.h"
20 #include "webrtc/base/thread_annotations.h" 21 #include "webrtc/base/thread_annotations.h"
22 #include "webrtc/base/timeutils.h"
21 #include "webrtc/modules/audio_device/audio_device_impl.h" 23 #include "webrtc/modules/audio_device/audio_device_impl.h"
22 #include "webrtc/modules/audio_device/include/audio_device.h" 24 #include "webrtc/modules/audio_device/include/audio_device.h"
23 #include "webrtc/modules/audio_device/include/mock_audio_transport.h" 25 #include "webrtc/modules/audio_device/include/mock_audio_transport.h"
24 #include "webrtc/system_wrappers/include/sleep.h" 26 #include "webrtc/system_wrappers/include/sleep.h"
25 #include "webrtc/test/gmock.h" 27 #include "webrtc/test/gmock.h"
26 #include "webrtc/test/gtest.h" 28 #include "webrtc/test/gtest.h"
27 29
28 using ::testing::_; 30 using ::testing::_;
29 using ::testing::AtLeast; 31 using ::testing::AtLeast;
30 using ::testing::Ge; 32 using ::testing::Ge;
31 using ::testing::Invoke; 33 using ::testing::Invoke;
32 using ::testing::NiceMock; 34 using ::testing::NiceMock;
33 using ::testing::NotNull; 35 using ::testing::NotNull;
34 36
35 namespace webrtc { 37 namespace webrtc {
36 namespace { 38 namespace {
37 39
38 // #define ENABLE_DEBUG_PRINTF 40 #define ENABLE_DEBUG_PRINTF
39 #ifdef ENABLE_DEBUG_PRINTF 41 #ifdef ENABLE_DEBUG_PRINTF
40 #define PRINTD(...) fprintf(stderr, __VA_ARGS__); 42 #define PRINTD(...) fprintf(stderr, __VA_ARGS__);
41 #else 43 #else
42 #define PRINTD(...) ((void)0) 44 #define PRINTD(...) ((void)0)
43 #endif 45 #endif
44 #define PRINT(...) fprintf(stderr, __VA_ARGS__); 46 #define PRINT(...) fprintf(stderr, __VA_ARGS__);
45 47
46 // Don't run these tests in combination with sanitizers. 48 // Don't run these tests in combination with sanitizers.
47 #if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) 49 #if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER)
48 #define SKIP_TEST_IF_NOT(requirements_satisfied) \ 50 #define SKIP_TEST_IF_NOT(requirements_satisfied) \
49 do { \ 51 do { \
50 if (!requirements_satisfied) { \ 52 if (!requirements_satisfied) { \
51 return; \ 53 return; \
52 } \ 54 } \
53 } while (false) 55 } while (false)
54 #else 56 #else
55 // Or if other audio-related requirements are not met. 57 // Or if other audio-related requirements are not met.
56 #define SKIP_TEST_IF_NOT(requirements_satisfied) \ 58 #define SKIP_TEST_IF_NOT(requirements_satisfied) \
57 do { \ 59 do { \
58 return; \ 60 return; \
59 } while (false) 61 } while (false)
60 #endif 62 #endif
61 63
62 // Number of callbacks (input or output) the tests waits for before we set 64 // Number of callbacks (input or output) the tests waits for before we set
63 // an event indicating that the test was OK. 65 // an event indicating that the test was OK.
64 static constexpr size_t kNumCallbacks = 10; 66 static constexpr size_t kNumCallbacks = 10;
65 // Max amount of time we wait for an event to be set while counting callbacks. 67 // Max amount of time we wait for an event to be set while counting callbacks.
66 static constexpr int kTestTimeOutInMilliseconds = 10 * 1000; 68 static constexpr size_t kTestTimeOutInMilliseconds = 10 * 1000;
67 // Average number of audio callbacks per second assuming 10ms packet size. 69 // Average number of audio callbacks per second assuming 10ms packet size.
68 static constexpr size_t kNumCallbacksPerSecond = 100; 70 static constexpr size_t kNumCallbacksPerSecond = 100;
69 // Run the full-duplex test during this time (unit is in seconds). 71 // Run the full-duplex test during this time (unit is in seconds).
70 static constexpr int kFullDuplexTimeInSec = 5; 72 static constexpr size_t kFullDuplexTimeInSec = 5;
73 // Length of round-trip latency measurements. Number of deteced impulses
74 // shall be kImpulseFrequencyInHz * kMeasureLatencyTimeInSec - 1 since the
75 // last transmitted pulse is not used.
76 static constexpr size_t kMeasureLatencyTimeInSec = 11;
77 // Sets the number of impulses per second in the latency test.
78 static constexpr size_t kImpulseFrequencyInHz = 1;
79 // Utilized in round-trip latency measurements to avoid capturing noise samples.
80 static constexpr int kImpulseThreshold = 1000;
81 static constexpr char kTag[] = "[..........] ";
kwiberg-webrtc 2017/04/20 09:26:37 Can you move all or some of these closer to where
henrika_webrtc 2017/04/20 12:22:30 Thanks. Moving and inlining kTag ;-)
71 82
72 enum class TransportType { 83 enum class TransportType {
73 kInvalid, 84 kInvalid,
74 kPlay, 85 kPlay,
75 kRecord, 86 kRecord,
76 kPlayAndRecord, 87 kPlayAndRecord,
77 }; 88 };
78 89
79 // Interface for processing the audio stream. Real implementations can e.g. 90 // Interface for processing the audio stream. Real implementations can e.g.
80 // run audio in loopback, read audio from a file or perform latency 91 // run audio in loopback, read audio from a file or perform latency
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 162
152 rtc::CriticalSection lock_; 163 rtc::CriticalSection lock_;
153 rtc::RaceChecker race_checker_; 164 rtc::RaceChecker race_checker_;
154 165
155 std::list<Buffer16> fifo_ GUARDED_BY(lock_); 166 std::list<Buffer16> fifo_ GUARDED_BY(lock_);
156 size_t write_count_ GUARDED_BY(race_checker_) = 0; 167 size_t write_count_ GUARDED_BY(race_checker_) = 0;
157 size_t max_size_ GUARDED_BY(race_checker_) = 0; 168 size_t max_size_ GUARDED_BY(race_checker_) = 0;
158 size_t written_elements_ GUARDED_BY(race_checker_) = 0; 169 size_t written_elements_ GUARDED_BY(race_checker_) = 0;
159 }; 170 };
160 171
172 // Inserts periodic impulses and measures the latency between the time of
173 // transmission and time of receiving the same impulse.
174 class LatencyAudioStream : public AudioStream {
175 public:
176 // Insert periodic impulses in first two samples of |destination|.
177 void Read(rtc::ArrayView<int16_t> destination, size_t channels) override {
178 EXPECT_EQ(channels, 1u);
179 if (read_count_ == 0) {
180 PRINT("[");
181 }
182 read_count_++;
183 std::fill(destination.begin(), destination.end(), 0);
184 if (read_count_ % (kNumCallbacksPerSecond / kImpulseFrequencyInHz) == 0) {
185 PRINT(".");
186 {
187 rtc::CritScope lock(&lock_);
188 if (pulse_time_ == 0) {
189 pulse_time_ = rtc::TimeMillis();
190 }
191 }
192 const int16_t impulse = std::numeric_limits<int16_t>::max();
kwiberg-webrtc 2017/04/20 09:26:37 constexpr?
henrika_webrtc 2017/04/20 12:22:30 works as well ;-)
193 std::fill_n(destination.begin(), 2, impulse);
194 }
195 }
196
197 // Detect received impulses in |source|, derive time between transmission and
198 // detection and add the calculated delay to list of latencies.
199 void Write(rtc::ArrayView<const int16_t> source, size_t channels) override {
200 EXPECT_EQ(channels, 1u);
201 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
202 rtc::CritScope lock(&lock_);
203 write_count_++;
204 if (pulse_time_ == 0) {
205 // Avoid detection of new impulse response until a new impulse has
206 // been transmitted (sets |pulse_time_| to value larger than zero).
207 return;
208 }
209 // Find max value in the audio buffer.
210 size_t max = *std::max_element(source.begin(), source.end());
211 // Find index (element position in vector) of the max element.
212 size_t index_of_max = std::distance(
213 source.begin(), std::find(source.begin(), source.end(), max));
kwiberg-webrtc 2017/04/20 09:26:37 Why are you using std::find here?---you already fo
kwiberg-webrtc 2017/04/20 11:14:30 const size_t index_of_max = std::max_element(sourc
henrika_webrtc 2017/04/20 12:22:30 Thanks!
214 // Derive time between transmitted pulse and received pulse if the level
215 // is high enough (removes noise).
216 if (max > kImpulseThreshold) {
217 PRINTD("(%zu, %zu)", max, index_of_max);
218 int64_t now_time = rtc::TimeMillis();
219 int extra_delay = IndexToMilliseconds(index_of_max, source.size());
220 PRINTD("[%d]", static_cast<int>(now_time - pulse_time_));
kwiberg-webrtc 2017/04/20 09:26:37 checked_cast?
henrika_webrtc 2017/04/20 12:22:30 Done.
221 PRINTD("[%d]", extra_delay);
222 // Total latency is the difference between transmit time and detection
223 // tome plus the extra delay within the buffer in which we detected the
224 // received impulse. It is transmitted at sample 0 but can be received
225 // at sample N where N > 0. The term |extra_delay| accounts for N and it
226 // is a value between 0 and 10ms.
227 latencies_.push_back(now_time - pulse_time_ + extra_delay);
228 pulse_time_ = 0;
229 } else {
230 PRINTD("-");
231 }
232 }
233
234 // Converts index corresponding to position within a 10ms buffer into a
235 // delay value in milliseconds.
236 // Example: index=240, frames_per_10ms_buffer=480 => 5ms as output.
237 int IndexToMilliseconds(size_t index, size_t frames_per_10ms_buffer) const {
238 return static_cast<int>(
239 10.0 * (static_cast<double>(index) / frames_per_10ms_buffer) + 0.5);
240 }
kwiberg-webrtc 2017/04/20 09:26:37 This doesn't need to be a member function. Put it
henrika_webrtc 2017/04/20 12:22:30 Done.
241
242 size_t num_latency_values() const {
243 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
244 return latencies_.size();
245 }
246
247 int min_latency() const {
248 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
249 if (latencies_.empty())
250 return 0;
251 return *std::min_element(latencies_.begin(), latencies_.end());
252 }
253
254 int max_latency() const {
255 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
256 if (latencies_.empty())
257 return 0;
258 return *std::max_element(latencies_.begin(), latencies_.end());
259 }
260
261 int average_latency() const {
262 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
263 if (latencies_.empty())
264 return 0;
265 return 0.5 + static_cast<double>(
266 std::accumulate(latencies_.begin(), latencies_.end(), 0)) /
267 latencies_.size();
268 }
269
270 void PrintResults() const {
271 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
272 PRINT("] ");
273 for (auto it = latencies_.begin(); it != latencies_.end(); ++it) {
274 PRINTD("%d ", *it);
275 }
276 PRINT("\n");
277 PRINT("%s[min, max, avg]=[%d, %d, %d] ms\n", kTag, min_latency(),
278 max_latency(), average_latency());
279 }
280
281 rtc::CriticalSection lock_;
282 rtc::RaceChecker race_checker_;
283
284 int64_t pulse_time_ GUARDED_BY(lock_) = 0;
kwiberg-webrtc 2017/04/20 09:26:37 You're using pulse_time_ == 0 to mean that it's un
henrika_webrtc 2017/04/20 12:22:30 Done (I think since I have not used Optional befor
285 std::vector<int> latencies_ GUARDED_BY(race_checker_);
286 size_t read_count_ = 0;
287 size_t write_count_ = 0;
kwiberg-webrtc 2017/04/20 09:26:37 Why are these two not protected by anything?
henrika_webrtc 2017/04/20 12:22:30 I know by design that read_count_ is only access o
288 };
289
161 // Mocks the AudioTransport object and proxies actions for the two callbacks 290 // Mocks the AudioTransport object and proxies actions for the two callbacks
162 // (RecordedDataIsAvailable and NeedMorePlayData) to different implementations 291 // (RecordedDataIsAvailable and NeedMorePlayData) to different implementations
163 // of AudioStreamInterface. 292 // of AudioStreamInterface.
164 class MockAudioTransport : public test::MockAudioTransport { 293 class MockAudioTransport : public test::MockAudioTransport {
165 public: 294 public:
166 explicit MockAudioTransport(TransportType type) : type_(type) {} 295 explicit MockAudioTransport(TransportType type) : type_(type) {}
167 ~MockAudioTransport() {} 296 ~MockAudioTransport() {}
168 297
169 // Set default actions of the mock object. We are delegating to fake 298 // Set default actions of the mock object. We are delegating to fake
170 // implementation where the number of callbacks is counted and an event 299 // implementation where the number of callbacks is counted and an event
(...skipping 343 matching lines...) Expand 10 before | Expand all | Expand 10 after
514 std::max(kTestTimeOutInMilliseconds, 1000 * kFullDuplexTimeInSec)); 643 std::max(kTestTimeOutInMilliseconds, 1000 * kFullDuplexTimeInSec));
515 StopRecording(); 644 StopRecording();
516 StopPlayout(); 645 StopPlayout();
517 // This thresholds is set rather high to accommodate differences in hardware 646 // This thresholds is set rather high to accommodate differences in hardware
518 // in several devices. The main idea is to capture cases where a very large 647 // in several devices. The main idea is to capture cases where a very large
519 // latency is built up. 648 // latency is built up.
520 EXPECT_LE(audio_stream.average_size(), 5u); 649 EXPECT_LE(audio_stream.average_size(), 5u);
521 PRINT("\n"); 650 PRINT("\n");
522 } 651 }
523 652
653 // Measures loopback latency and reports the min, max and average values for
654 // a full duplex audio session.
655 // The latency is measured like so:
656 // - Insert impulses periodically on the output side.
657 // - Detect the impulses on the input side.
658 // - Measure the time difference between the transmit time and receive time.
659 // - Store time differences in a vector and calculate min, max and average.
660 // This test needs the '--gtest_also_run_disabled_tests' flag to run and also
661 // some sort of audio feedback loop. E.g. a headset where the mic is placed
662 // close to the speaker to ensure highest possible echo. It is also recommended
663 // to run the test at highest possible output volume.
664 TEST_F(AudioDeviceTest, DISABLED_MeasureLoopbackLatency) {
665 SKIP_TEST_IF_NOT(requirements_satisfied());
666 NiceMock<MockAudioTransport> mock(TransportType::kPlayAndRecord);
667 LatencyAudioStream audio_stream;
668 mock.HandleCallbacks(event(), &audio_stream,
669 kMeasureLatencyTimeInSec * kNumCallbacksPerSecond);
670 EXPECT_EQ(0, audio_device()->RegisterAudioCallback(&mock));
671 EXPECT_EQ(0, audio_device()->SetStereoPlayout(false));
672 EXPECT_EQ(0, audio_device()->SetStereoRecording(false));
673 StartPlayout();
674 StartRecording();
675 event()->Wait(
676 std::max(kTestTimeOutInMilliseconds, 1000 * kMeasureLatencyTimeInSec));
677 StopRecording();
678 StopPlayout();
679 // Verify that the correct number of transmitted impulses are detected.
680 EXPECT_EQ(audio_stream.num_latency_values(),
681 static_cast<size_t>(
682 kImpulseFrequencyInHz * kMeasureLatencyTimeInSec - 1));
683 // Print out min, max and average delay values for debugging purposes.
684 audio_stream.PrintResults();
685 }
686
524 } // namespace webrtc 687 } // namespace webrtc
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698