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" |
19 #include "webrtc/base/optional.h" | |
18 #include "webrtc/base/race_checker.h" | 20 #include "webrtc/base/race_checker.h" |
21 #include "webrtc/base/safe_conversions.h" | |
19 #include "webrtc/base/scoped_ref_ptr.h" | 22 #include "webrtc/base/scoped_ref_ptr.h" |
20 #include "webrtc/base/thread_annotations.h" | 23 #include "webrtc/base/thread_annotations.h" |
24 #include "webrtc/base/thread_checker.h" | |
25 #include "webrtc/base/timeutils.h" | |
21 #include "webrtc/modules/audio_device/audio_device_impl.h" | 26 #include "webrtc/modules/audio_device/audio_device_impl.h" |
22 #include "webrtc/modules/audio_device/include/audio_device.h" | 27 #include "webrtc/modules/audio_device/include/audio_device.h" |
23 #include "webrtc/modules/audio_device/include/mock_audio_transport.h" | 28 #include "webrtc/modules/audio_device/include/mock_audio_transport.h" |
24 #include "webrtc/system_wrappers/include/sleep.h" | 29 #include "webrtc/system_wrappers/include/sleep.h" |
25 #include "webrtc/test/gmock.h" | 30 #include "webrtc/test/gmock.h" |
26 #include "webrtc/test/gtest.h" | 31 #include "webrtc/test/gtest.h" |
27 | 32 |
28 using ::testing::_; | 33 using ::testing::_; |
29 using ::testing::AtLeast; | 34 using ::testing::AtLeast; |
30 using ::testing::Ge; | 35 using ::testing::Ge; |
31 using ::testing::Invoke; | 36 using ::testing::Invoke; |
32 using ::testing::NiceMock; | 37 using ::testing::NiceMock; |
33 using ::testing::NotNull; | 38 using ::testing::NotNull; |
34 | 39 |
35 namespace webrtc { | 40 namespace webrtc { |
36 namespace { | 41 namespace { |
37 | 42 |
38 // #define ENABLE_DEBUG_PRINTF | 43 // #define ENABLE_DEBUG_PRINTF |
kwiberg-webrtc
2017/04/20 12:33:27
Did you mean to do this?
henrika_webrtc
2017/04/20 12:39:26
Yes I did. Only used to add extra verbose debug lo
| |
39 #ifdef ENABLE_DEBUG_PRINTF | 44 #ifdef ENABLE_DEBUG_PRINTF |
40 #define PRINTD(...) fprintf(stderr, __VA_ARGS__); | 45 #define PRINTD(...) fprintf(stderr, __VA_ARGS__); |
41 #else | 46 #else |
42 #define PRINTD(...) ((void)0) | 47 #define PRINTD(...) ((void)0) |
43 #endif | 48 #endif |
44 #define PRINT(...) fprintf(stderr, __VA_ARGS__); | 49 #define PRINT(...) fprintf(stderr, __VA_ARGS__); |
45 | 50 |
46 // Don't run these tests in combination with sanitizers. | 51 // Don't run these tests in combination with sanitizers. |
47 #if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) | 52 #if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) |
48 #define SKIP_TEST_IF_NOT(requirements_satisfied) \ | 53 #define SKIP_TEST_IF_NOT(requirements_satisfied) \ |
49 do { \ | 54 do { \ |
50 if (!requirements_satisfied) { \ | 55 if (!requirements_satisfied) { \ |
51 return; \ | 56 return; \ |
52 } \ | 57 } \ |
53 } while (false) | 58 } while (false) |
54 #else | 59 #else |
55 // Or if other audio-related requirements are not met. | 60 // Or if other audio-related requirements are not met. |
56 #define SKIP_TEST_IF_NOT(requirements_satisfied) \ | 61 #define SKIP_TEST_IF_NOT(requirements_satisfied) \ |
57 do { \ | 62 do { \ |
58 return; \ | 63 return; \ |
59 } while (false) | 64 } while (false) |
60 #endif | 65 #endif |
61 | 66 |
62 // Number of callbacks (input or output) the tests waits for before we set | 67 // Number of callbacks (input or output) the tests waits for before we set |
63 // an event indicating that the test was OK. | 68 // an event indicating that the test was OK. |
64 static constexpr size_t kNumCallbacks = 10; | 69 static constexpr size_t kNumCallbacks = 10; |
65 // Max amount of time we wait for an event to be set while counting callbacks. | 70 // Max amount of time we wait for an event to be set while counting callbacks. |
66 static constexpr int kTestTimeOutInMilliseconds = 10 * 1000; | 71 static constexpr size_t kTestTimeOutInMilliseconds = 10 * 1000; |
67 // Average number of audio callbacks per second assuming 10ms packet size. | 72 // Average number of audio callbacks per second assuming 10ms packet size. |
68 static constexpr size_t kNumCallbacksPerSecond = 100; | 73 static constexpr size_t kNumCallbacksPerSecond = 100; |
69 // Run the full-duplex test during this time (unit is in seconds). | 74 // Run the full-duplex test during this time (unit is in seconds). |
70 static constexpr int kFullDuplexTimeInSec = 5; | 75 static constexpr size_t kFullDuplexTimeInSec = 5; |
76 // Length of round-trip latency measurements. Number of deteced impulses | |
77 // shall be kImpulseFrequencyInHz * kMeasureLatencyTimeInSec - 1 since the | |
78 // last transmitted pulse is not used. | |
79 static constexpr size_t kMeasureLatencyTimeInSec = 10; | |
80 // Sets the number of impulses per second in the latency test. | |
81 static constexpr size_t kImpulseFrequencyInHz = 1; | |
82 // Utilized in round-trip latency measurements to avoid capturing noise samples. | |
83 static constexpr int kImpulseThreshold = 1000; | |
71 | 84 |
72 enum class TransportType { | 85 enum class TransportType { |
73 kInvalid, | 86 kInvalid, |
74 kPlay, | 87 kPlay, |
75 kRecord, | 88 kRecord, |
76 kPlayAndRecord, | 89 kPlayAndRecord, |
77 }; | 90 }; |
78 | 91 |
79 // Interface for processing the audio stream. Real implementations can e.g. | 92 // Interface for processing the audio stream. Real implementations can e.g. |
80 // run audio in loopback, read audio from a file or perform latency | 93 // run audio in loopback, read audio from a file or perform latency |
81 // measurements. | 94 // measurements. |
82 class AudioStream { | 95 class AudioStream { |
83 public: | 96 public: |
84 virtual void Write(rtc::ArrayView<const int16_t> source, size_t channels) = 0; | 97 virtual void Write(rtc::ArrayView<const int16_t> source, size_t channels) = 0; |
85 virtual void Read(rtc::ArrayView<int16_t> destination, size_t channels) = 0; | 98 virtual void Read(rtc::ArrayView<int16_t> destination, size_t channels) = 0; |
86 | 99 |
87 virtual ~AudioStream() = default; | 100 virtual ~AudioStream() = default; |
88 }; | 101 }; |
89 | 102 |
103 // Converts index corresponding to position within a 10ms buffer into a | |
104 // delay value in milliseconds. | |
105 // Example: index=240, frames_per_10ms_buffer=480 => 5ms as output. | |
106 int IndexToMilliseconds(size_t index, size_t frames_per_10ms_buffer) { | |
107 return rtc::checked_cast<int>( | |
108 10.0 * (static_cast<double>(index) / frames_per_10ms_buffer) + 0.5); | |
109 } | |
110 | |
90 } // namespace | 111 } // namespace |
91 | 112 |
92 // Simple first in first out (FIFO) class that wraps a list of 16-bit audio | 113 // Simple first in first out (FIFO) class that wraps a list of 16-bit audio |
93 // buffers of fixed size and allows Write and Read operations. The idea is to | 114 // buffers of fixed size and allows Write and Read operations. The idea is to |
94 // store recorded audio buffers (using Write) and then read (using Read) these | 115 // store recorded audio buffers (using Write) and then read (using Read) these |
95 // stored buffers with as short delay as possible when the audio layer needs | 116 // stored buffers with as short delay as possible when the audio layer needs |
96 // data to play out. The number of buffers in the FIFO will stabilize under | 117 // data to play out. The number of buffers in the FIFO will stabilize under |
97 // normal conditions since there will be a balance between Write and Read calls. | 118 // normal conditions since there will be a balance between Write and Read calls. |
98 // The container is a std::list container and access is protected with a lock | 119 // The container is a std::list container and access is protected with a lock |
99 // since both sides (playout and recording) are driven by its own thread. | 120 // since both sides (playout and recording) are driven by its own thread. |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
151 | 172 |
152 rtc::CriticalSection lock_; | 173 rtc::CriticalSection lock_; |
153 rtc::RaceChecker race_checker_; | 174 rtc::RaceChecker race_checker_; |
154 | 175 |
155 std::list<Buffer16> fifo_ GUARDED_BY(lock_); | 176 std::list<Buffer16> fifo_ GUARDED_BY(lock_); |
156 size_t write_count_ GUARDED_BY(race_checker_) = 0; | 177 size_t write_count_ GUARDED_BY(race_checker_) = 0; |
157 size_t max_size_ GUARDED_BY(race_checker_) = 0; | 178 size_t max_size_ GUARDED_BY(race_checker_) = 0; |
158 size_t written_elements_ GUARDED_BY(race_checker_) = 0; | 179 size_t written_elements_ GUARDED_BY(race_checker_) = 0; |
159 }; | 180 }; |
160 | 181 |
182 // Inserts periodic impulses and measures the latency between the time of | |
183 // transmission and time of receiving the same impulse. | |
184 class LatencyAudioStream : public AudioStream { | |
185 public: | |
186 LatencyAudioStream() { | |
187 // Delay thread checkers from being initialized until first callback from | |
188 // respective thread. | |
189 read_thread_checker_.DetachFromThread(); | |
190 write_thread_checker_.DetachFromThread(); | |
191 } | |
192 | |
193 // Insert periodic impulses in first two samples of |destination|. | |
194 void Read(rtc::ArrayView<int16_t> destination, size_t channels) override { | |
195 RTC_DCHECK_RUN_ON(&read_thread_checker_); | |
196 EXPECT_EQ(channels, 1u); | |
197 if (read_count_ == 0) { | |
198 PRINT("["); | |
199 } | |
200 read_count_++; | |
201 std::fill(destination.begin(), destination.end(), 0); | |
202 if (read_count_ % (kNumCallbacksPerSecond / kImpulseFrequencyInHz) == 0) { | |
203 PRINT("."); | |
204 { | |
205 rtc::CritScope lock(&lock_); | |
206 if (!pulse_time_) { | |
207 pulse_time_ = rtc::Optional<int64_t>(rtc::TimeMillis()); | |
208 } | |
209 } | |
210 constexpr int16_t impulse = std::numeric_limits<int16_t>::max(); | |
211 std::fill_n(destination.begin(), 2, impulse); | |
212 } | |
213 } | |
214 | |
215 // Detect received impulses in |source|, derive time between transmission and | |
216 // detection and add the calculated delay to list of latencies. | |
217 void Write(rtc::ArrayView<const int16_t> source, size_t channels) override { | |
218 EXPECT_EQ(channels, 1u); | |
219 RTC_DCHECK_RUN_ON(&write_thread_checker_); | |
220 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
221 rtc::CritScope lock(&lock_); | |
222 write_count_++; | |
223 if (!pulse_time_) { | |
224 // Avoid detection of new impulse response until a new impulse has | |
225 // been transmitted (sets |pulse_time_| to value larger than zero). | |
226 return; | |
227 } | |
228 // Find index (element position in vector) of the max element. | |
229 const size_t index_of_max = | |
230 std::max_element(source.begin(), source.end()) - source.begin(); | |
231 // Derive time between transmitted pulse and received pulse if the level | |
232 // is high enough (removes noise). | |
233 const size_t max = source[index_of_max]; | |
234 if (max > kImpulseThreshold) { | |
235 PRINTD("(%zu, %zu)", max, index_of_max); | |
236 int64_t now_time = rtc::TimeMillis(); | |
237 int extra_delay = IndexToMilliseconds(index_of_max, source.size()); | |
238 PRINTD("[%d]", rtc::checked_cast<int>(now_time - pulse_time_)); | |
239 PRINTD("[%d]", extra_delay); | |
240 // Total latency is the difference between transmit time and detection | |
241 // tome plus the extra delay within the buffer in which we detected the | |
242 // received impulse. It is transmitted at sample 0 but can be received | |
243 // at sample N where N > 0. The term |extra_delay| accounts for N and it | |
244 // is a value between 0 and 10ms. | |
245 latencies_.push_back(now_time - *pulse_time_ + extra_delay); | |
246 pulse_time_.reset(); | |
247 } else { | |
248 PRINTD("-"); | |
249 } | |
250 } | |
251 | |
252 size_t num_latency_values() const { | |
253 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
254 return latencies_.size(); | |
255 } | |
256 | |
257 int min_latency() const { | |
258 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
259 if (latencies_.empty()) | |
260 return 0; | |
261 return *std::min_element(latencies_.begin(), latencies_.end()); | |
262 } | |
263 | |
264 int max_latency() const { | |
265 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
266 if (latencies_.empty()) | |
267 return 0; | |
268 return *std::max_element(latencies_.begin(), latencies_.end()); | |
269 } | |
270 | |
271 int average_latency() const { | |
272 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
273 if (latencies_.empty()) | |
274 return 0; | |
275 return 0.5 + static_cast<double>( | |
276 std::accumulate(latencies_.begin(), latencies_.end(), 0)) / | |
277 latencies_.size(); | |
278 } | |
279 | |
280 void PrintResults() const { | |
281 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
282 PRINT("] "); | |
283 for (auto it = latencies_.begin(); it != latencies_.end(); ++it) { | |
284 PRINTD("%d ", *it); | |
285 } | |
286 PRINT("\n"); | |
287 PRINT("[..........] [min, max, avg]=[%d, %d, %d] ms\n", min_latency(), | |
288 max_latency(), average_latency()); | |
289 } | |
290 | |
291 rtc::CriticalSection lock_; | |
292 rtc::RaceChecker race_checker_; | |
293 rtc::ThreadChecker read_thread_checker_; | |
294 rtc::ThreadChecker write_thread_checker_; | |
295 | |
296 rtc::Optional<int64_t> pulse_time_ GUARDED_BY(lock_); | |
297 std::vector<int> latencies_ GUARDED_BY(race_checker_); | |
298 size_t read_count_ ACCESS_ON(read_thread_checker_) = 0; | |
299 size_t write_count_ ACCESS_ON(write_thread_checker_)= 0; | |
300 }; | |
301 | |
161 // Mocks the AudioTransport object and proxies actions for the two callbacks | 302 // Mocks the AudioTransport object and proxies actions for the two callbacks |
162 // (RecordedDataIsAvailable and NeedMorePlayData) to different implementations | 303 // (RecordedDataIsAvailable and NeedMorePlayData) to different implementations |
163 // of AudioStreamInterface. | 304 // of AudioStreamInterface. |
164 class MockAudioTransport : public test::MockAudioTransport { | 305 class MockAudioTransport : public test::MockAudioTransport { |
165 public: | 306 public: |
166 explicit MockAudioTransport(TransportType type) : type_(type) {} | 307 explicit MockAudioTransport(TransportType type) : type_(type) {} |
167 ~MockAudioTransport() {} | 308 ~MockAudioTransport() {} |
168 | 309 |
169 // Set default actions of the mock object. We are delegating to fake | 310 // Set default actions of the mock object. We are delegating to fake |
170 // implementation where the number of callbacks is counted and an event | 311 // 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)); | 655 std::max(kTestTimeOutInMilliseconds, 1000 * kFullDuplexTimeInSec)); |
515 StopRecording(); | 656 StopRecording(); |
516 StopPlayout(); | 657 StopPlayout(); |
517 // This thresholds is set rather high to accommodate differences in hardware | 658 // 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 | 659 // in several devices. The main idea is to capture cases where a very large |
519 // latency is built up. | 660 // latency is built up. |
520 EXPECT_LE(audio_stream.average_size(), 5u); | 661 EXPECT_LE(audio_stream.average_size(), 5u); |
521 PRINT("\n"); | 662 PRINT("\n"); |
522 } | 663 } |
523 | 664 |
665 // Measures loopback latency and reports the min, max and average values for | |
666 // a full duplex audio session. | |
667 // The latency is measured like so: | |
668 // - Insert impulses periodically on the output side. | |
669 // - Detect the impulses on the input side. | |
670 // - Measure the time difference between the transmit time and receive time. | |
671 // - Store time differences in a vector and calculate min, max and average. | |
672 // This test needs the '--gtest_also_run_disabled_tests' flag to run and also | |
673 // some sort of audio feedback loop. E.g. a headset where the mic is placed | |
674 // close to the speaker to ensure highest possible echo. It is also recommended | |
675 // to run the test at highest possible output volume. | |
676 TEST_F(AudioDeviceTest, DISABLED_MeasureLoopbackLatency) { | |
677 SKIP_TEST_IF_NOT(requirements_satisfied()); | |
678 NiceMock<MockAudioTransport> mock(TransportType::kPlayAndRecord); | |
679 LatencyAudioStream audio_stream; | |
680 mock.HandleCallbacks(event(), &audio_stream, | |
681 kMeasureLatencyTimeInSec * kNumCallbacksPerSecond); | |
682 EXPECT_EQ(0, audio_device()->RegisterAudioCallback(&mock)); | |
683 EXPECT_EQ(0, audio_device()->SetStereoPlayout(false)); | |
684 EXPECT_EQ(0, audio_device()->SetStereoRecording(false)); | |
685 StartPlayout(); | |
686 StartRecording(); | |
687 event()->Wait( | |
688 std::max(kTestTimeOutInMilliseconds, 1000 * kMeasureLatencyTimeInSec)); | |
689 StopRecording(); | |
690 StopPlayout(); | |
691 // Verify that the correct number of transmitted impulses are detected. | |
692 EXPECT_EQ(audio_stream.num_latency_values(), | |
693 static_cast<size_t>( | |
694 kImpulseFrequencyInHz * kMeasureLatencyTimeInSec - 1)); | |
695 // Print out min, max and average delay values for debugging purposes. | |
696 audio_stream.PrintResults(); | |
697 } | |
698 | |
524 } // namespace webrtc | 699 } // namespace webrtc |
OLD | NEW |