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