OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |