1 /*
2 * Copyright (c) 2013 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 // Test to verify correct stereo and multi-channel operation.
12
13 #include <algorithm>
14 #include <string>
15 #include <list>
16
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "webrtc/base/scoped_ptr.h"
19 #include "webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.h"
20 #include "webrtc/modules/audio_coding/neteq/include/neteq.h"
21 #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h"
22 #include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h"
23 #include "webrtc/test/testsupport/fileutils.h"
24
25 namespace webrtc {
26
27 struct TestParameters {
28 int frame_size;
29 int sample_rate;
30 size_t num_channels;
31 };
32
33 // This is a parameterized test. The test parameters are supplied through a
34 // TestParameters struct, which is obtained through the GetParam() method.
35 //
36 // The objective of the test is to create a mono input signal and a
37 // multi-channel input signal, where each channel is identical to the mono
38 // input channel. The two input signals are processed through their respective
39 // NetEq instances. After that, the output signals are compared. The expected
40 // result is that each channel in the multi-channel output is identical to the
41 // mono output.
42 class NetEqStereoTest : public ::testing::TestWithParam<TestParameters> {
43 protected:
44 static const int kTimeStepMs = 10;
45 static const size_t kMaxBlockSize = 480; // 10 ms @ 48 kHz.
46 static const uint8_t kPayloadTypeMono = 95;
47 static const uint8_t kPayloadTypeMulti = 96;
48
NetEqStereoTest()49 NetEqStereoTest()
50 : num_channels_(GetParam().num_channels),
51 sample_rate_hz_(GetParam().sample_rate),
52 samples_per_ms_(sample_rate_hz_ / 1000),
53 frame_size_ms_(GetParam().frame_size),
54 frame_size_samples_(
55 static_cast<size_t>(frame_size_ms_ * samples_per_ms_)),
56 output_size_samples_(10 * samples_per_ms_),
57 rtp_generator_mono_(samples_per_ms_),
58 rtp_generator_(samples_per_ms_),
59 payload_size_bytes_(0),
60 multi_payload_size_bytes_(0),
61 last_send_time_(0),
62 last_arrival_time_(0) {
63 NetEq::Config config;
64 config.sample_rate_hz = sample_rate_hz_;
65 neteq_mono_ = NetEq::Create(config);
66 neteq_ = NetEq::Create(config);
67 input_ = new int16_t[frame_size_samples_];
68 encoded_ = new uint8_t[2 * frame_size_samples_];
69 input_multi_channel_ = new int16_t[frame_size_samples_ * num_channels_];
70 encoded_multi_channel_ = new uint8_t[frame_size_samples_ * 2 *
71 num_channels_];
72 output_multi_channel_ = new int16_t[kMaxBlockSize * num_channels_];
73 }
74
~NetEqStereoTest()75 ~NetEqStereoTest() {
76 delete neteq_mono_;
77 delete neteq_;
78 delete [] input_;
79 delete [] encoded_;
80 delete [] input_multi_channel_;
81 delete [] encoded_multi_channel_;
82 delete [] output_multi_channel_;
83 }
84
SetUp()85 virtual void SetUp() {
86 const std::string file_name =
87 webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
88 input_file_.reset(new test::InputAudioFile(file_name));
89 NetEqDecoder mono_decoder;
90 NetEqDecoder multi_decoder;
91 switch (sample_rate_hz_) {
92 case 8000:
93 mono_decoder = NetEqDecoder::kDecoderPCM16B;
94 if (num_channels_ == 2) {
95 multi_decoder = NetEqDecoder::kDecoderPCM16B_2ch;
96 } else if (num_channels_ == 5) {
97 multi_decoder = NetEqDecoder::kDecoderPCM16B_5ch;
98 } else {
99 FAIL() << "Only 2 and 5 channels supported for 8000 Hz.";
100 }
101 break;
102 case 16000:
103 mono_decoder = NetEqDecoder::kDecoderPCM16Bwb;
104 if (num_channels_ == 2) {
105 multi_decoder = NetEqDecoder::kDecoderPCM16Bwb_2ch;
106 } else {
107 FAIL() << "More than 2 channels is not supported for 16000 Hz.";
108 }
109 break;
110 case 32000:
111 mono_decoder = NetEqDecoder::kDecoderPCM16Bswb32kHz;
112 if (num_channels_ == 2) {
113 multi_decoder = NetEqDecoder::kDecoderPCM16Bswb32kHz_2ch;
114 } else {
115 FAIL() << "More than 2 channels is not supported for 32000 Hz.";
116 }
117 break;
118 case 48000:
119 mono_decoder = NetEqDecoder::kDecoderPCM16Bswb48kHz;
120 if (num_channels_ == 2) {
121 multi_decoder = NetEqDecoder::kDecoderPCM16Bswb48kHz_2ch;
122 } else {
123 FAIL() << "More than 2 channels is not supported for 48000 Hz.";
124 }
125 break;
126 default:
127 FAIL() << "We shouldn't get here.";
128 }
129 ASSERT_EQ(NetEq::kOK, neteq_mono_->RegisterPayloadType(mono_decoder, "mono",
130 kPayloadTypeMono));
131 ASSERT_EQ(NetEq::kOK,
132 neteq_->RegisterPayloadType(multi_decoder, "multi-channel",
133 kPayloadTypeMulti));
134 }
135
TearDown()136 virtual void TearDown() {}
137
GetNewPackets()138 int GetNewPackets() {
139 if (!input_file_->Read(frame_size_samples_, input_)) {
140 return -1;
141 }
142 payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_,
143 encoded_);
144 if (frame_size_samples_ * 2 != payload_size_bytes_) {
145 return -1;
146 }
147 int next_send_time = rtp_generator_mono_.GetRtpHeader(kPayloadTypeMono,
148 frame_size_samples_,
149 &rtp_header_mono_);
150 test::InputAudioFile::DuplicateInterleaved(input_, frame_size_samples_,
151 num_channels_,
152 input_multi_channel_);
153 multi_payload_size_bytes_ = WebRtcPcm16b_Encode(
154 input_multi_channel_, frame_size_samples_ * num_channels_,
155 encoded_multi_channel_);
156 if (frame_size_samples_ * 2 * num_channels_ != multi_payload_size_bytes_) {
157 return -1;
158 }
159 rtp_generator_.GetRtpHeader(kPayloadTypeMulti, frame_size_samples_,
160 &rtp_header_);
161 return next_send_time;
162 }
163
VerifyOutput(size_t num_samples)164 void VerifyOutput(size_t num_samples) {
165 for (size_t i = 0; i < num_samples; ++i) {
166 for (size_t j = 0; j < num_channels_; ++j) {
167 ASSERT_EQ(output_[i], output_multi_channel_[i * num_channels_ + j]) <<
168 "Diff in sample " << i << ", channel " << j << ".";
169 }
170 }
171 }
172
GetArrivalTime(int send_time)173 virtual int GetArrivalTime(int send_time) {
174 int arrival_time = last_arrival_time_ + (send_time - last_send_time_);
175 last_send_time_ = send_time;
176 last_arrival_time_ = arrival_time;
177 return arrival_time;
178 }
179
Lost()180 virtual bool Lost() { return false; }
181
RunTest(int num_loops)182 void RunTest(int num_loops) {
183 // Get next input packets (mono and multi-channel).
184 int next_send_time;
185 int next_arrival_time;
186 do {
187 next_send_time = GetNewPackets();
188 ASSERT_NE(-1, next_send_time);
189 next_arrival_time = GetArrivalTime(next_send_time);
190 } while (Lost()); // If lost, immediately read the next packet.
191
192 int time_now = 0;
193 for (int k = 0; k < num_loops; ++k) {
194 while (time_now >= next_arrival_time) {
195 // Insert packet in mono instance.
196 ASSERT_EQ(NetEq::kOK,
197 neteq_mono_->InsertPacket(rtp_header_mono_,
198 rtc::ArrayView<const uint8_t>(
199 encoded_, payload_size_bytes_),
200 next_arrival_time));
201 // Insert packet in multi-channel instance.
202 ASSERT_EQ(NetEq::kOK, neteq_->InsertPacket(
203 rtp_header_, rtc::ArrayView<const uint8_t>(
204 encoded_multi_channel_,
205 multi_payload_size_bytes_),
206 next_arrival_time));
207 // Get next input packets (mono and multi-channel).
208 do {
209 next_send_time = GetNewPackets();
210 ASSERT_NE(-1, next_send_time);
211 next_arrival_time = GetArrivalTime(next_send_time);
212 } while (Lost()); // If lost, immediately read the next packet.
213 }
214 NetEqOutputType output_type;
215 // Get audio from mono instance.
216 size_t samples_per_channel;
217 size_t num_channels;
218 EXPECT_EQ(NetEq::kOK,
219 neteq_mono_->GetAudio(kMaxBlockSize, output_,
220 &samples_per_channel, &num_channels,
221 &output_type));
222 EXPECT_EQ(1u, num_channels);
223 EXPECT_EQ(output_size_samples_, samples_per_channel);
224 // Get audio from multi-channel instance.
225 ASSERT_EQ(NetEq::kOK,
226 neteq_->GetAudio(kMaxBlockSize * num_channels_,
227 output_multi_channel_,
228 &samples_per_channel, &num_channels,
229 &output_type));
230 EXPECT_EQ(num_channels_, num_channels);
231 EXPECT_EQ(output_size_samples_, samples_per_channel);
232 std::ostringstream ss;
233 ss << "Lap number " << k << ".";
234 SCOPED_TRACE(ss.str()); // Print out the parameter values on failure.
235 // Compare mono and multi-channel.
236 ASSERT_NO_FATAL_FAILURE(VerifyOutput(output_size_samples_));
237
238 time_now += kTimeStepMs;
239 }
240 }
241
242 const size_t num_channels_;
243 const int sample_rate_hz_;
244 const int samples_per_ms_;
245 const int frame_size_ms_;
246 const size_t frame_size_samples_;
247 const size_t output_size_samples_;
248 NetEq* neteq_mono_;
249 NetEq* neteq_;
250 test::RtpGenerator rtp_generator_mono_;
251 test::RtpGenerator rtp_generator_;
252 int16_t* input_;
253 int16_t* input_multi_channel_;
254 uint8_t* encoded_;
255 uint8_t* encoded_multi_channel_;
256 int16_t output_[kMaxBlockSize];
257 int16_t* output_multi_channel_;
258 WebRtcRTPHeader rtp_header_mono_;
259 WebRtcRTPHeader rtp_header_;
260 size_t payload_size_bytes_;
261 size_t multi_payload_size_bytes_;
262 int last_send_time_;
263 int last_arrival_time_;
264 rtc::scoped_ptr<test::InputAudioFile> input_file_;
265 };
266
267 class NetEqStereoTestNoJitter : public NetEqStereoTest {
268 protected:
NetEqStereoTestNoJitter()269 NetEqStereoTestNoJitter()
270 : NetEqStereoTest() {
271 // Start the sender 100 ms before the receiver to pre-fill the buffer.
272 // This is to avoid doing preemptive expand early in the test.
273 // TODO(hlundin): Mock the decision making instead to control the modes.
274 last_arrival_time_ = -100;
275 }
276 };
277
278 #if defined(WEBRTC_ANDROID)
279 #define MAYBE_RunTest DISABLED_RunTest
280 #else
281 #define MAYBE_RunTest RunTest
282 #endif
TEST_P(NetEqStereoTestNoJitter,MAYBE_RunTest)283 TEST_P(NetEqStereoTestNoJitter, MAYBE_RunTest) {
284 RunTest(8);
285 }
286
287 class NetEqStereoTestPositiveDrift : public NetEqStereoTest {
288 protected:
NetEqStereoTestPositiveDrift()289 NetEqStereoTestPositiveDrift()
290 : NetEqStereoTest(),
291 drift_factor(0.9) {
292 // Start the sender 100 ms before the receiver to pre-fill the buffer.
293 // This is to avoid doing preemptive expand early in the test.
294 // TODO(hlundin): Mock the decision making instead to control the modes.
295 last_arrival_time_ = -100;
296 }
GetArrivalTime(int send_time)297 virtual int GetArrivalTime(int send_time) {
298 int arrival_time = last_arrival_time_ +
299 drift_factor * (send_time - last_send_time_);
300 last_send_time_ = send_time;
301 last_arrival_time_ = arrival_time;
302 return arrival_time;
303 }
304
305 double drift_factor;
306 };
307
TEST_P(NetEqStereoTestPositiveDrift,MAYBE_RunTest)308 TEST_P(NetEqStereoTestPositiveDrift, MAYBE_RunTest) {
309 RunTest(100);
310 }
311
312 class NetEqStereoTestNegativeDrift : public NetEqStereoTestPositiveDrift {
313 protected:
NetEqStereoTestNegativeDrift()314 NetEqStereoTestNegativeDrift()
315 : NetEqStereoTestPositiveDrift() {
316 drift_factor = 1.1;
317 last_arrival_time_ = 0;
318 }
319 };
320
TEST_P(NetEqStereoTestNegativeDrift,MAYBE_RunTest)321 TEST_P(NetEqStereoTestNegativeDrift, MAYBE_RunTest) {
322 RunTest(100);
323 }
324
325 class NetEqStereoTestDelays : public NetEqStereoTest {
326 protected:
327 static const int kDelayInterval = 10;
328 static const int kDelay = 1000;
NetEqStereoTestDelays()329 NetEqStereoTestDelays()
330 : NetEqStereoTest(),
331 frame_index_(0) {
332 }
333
GetArrivalTime(int send_time)334 virtual int GetArrivalTime(int send_time) {
335 // Deliver immediately, unless we have a back-log.
336 int arrival_time = std::min(last_arrival_time_, send_time);
337 if (++frame_index_ % kDelayInterval == 0) {
338 // Delay this packet.
339 arrival_time += kDelay;
340 }
341 last_send_time_ = send_time;
342 last_arrival_time_ = arrival_time;
343 return arrival_time;
344 }
345
346 int frame_index_;
347 };
348
TEST_P(NetEqStereoTestDelays,MAYBE_RunTest)349 TEST_P(NetEqStereoTestDelays, MAYBE_RunTest) {
350 RunTest(1000);
351 }
352
353 class NetEqStereoTestLosses : public NetEqStereoTest {
354 protected:
355 static const int kLossInterval = 10;
NetEqStereoTestLosses()356 NetEqStereoTestLosses()
357 : NetEqStereoTest(),
358 frame_index_(0) {
359 }
360
Lost()361 virtual bool Lost() {
362 return (++frame_index_) % kLossInterval == 0;
363 }
364
365 int frame_index_;
366 };
367
368 // TODO(pbos): Enable on non-Android, this went failing while being accidentally
369 // disabled on all platforms and not just Android.
370 // https://bugs.chromium.org/p/webrtc/issues/detail?id=5387
TEST_P(NetEqStereoTestLosses,DISABLED_RunTest)371 TEST_P(NetEqStereoTestLosses, DISABLED_RunTest) {
372 RunTest(100);
373 }
374
375
376 // Creates a list of parameter sets.
GetTestParameters()377 std::list<TestParameters> GetTestParameters() {
378 std::list<TestParameters> l;
379 const int sample_rates[] = {8000, 16000, 32000};
380 const int num_rates = sizeof(sample_rates) / sizeof(sample_rates[0]);
381 // Loop through sample rates.
382 for (int rate_index = 0; rate_index < num_rates; ++rate_index) {
383 int sample_rate = sample_rates[rate_index];
384 // Loop through all frame sizes between 10 and 60 ms.
385 for (int frame_size = 10; frame_size <= 60; frame_size += 10) {
386 TestParameters p;
387 p.frame_size = frame_size;
388 p.sample_rate = sample_rate;
389 p.num_channels = 2;
390 l.push_back(p);
391 if (sample_rate == 8000) {
392 // Add a five-channel test for 8000 Hz.
393 p.num_channels = 5;
394 l.push_back(p);
395 }
396 }
397 }
398 return l;
399 }
400
401 // Pretty-printing the test parameters in case of an error.
PrintTo(const TestParameters & p,::std::ostream * os)402 void PrintTo(const TestParameters& p, ::std::ostream* os) {
403 *os << "{frame_size = " << p.frame_size <<
404 ", num_channels = " << p.num_channels <<
405 ", sample_rate = " << p.sample_rate << "}";
406 }
407
408 // Instantiate the tests. Each test is instantiated using the function above,
409 // so that all different parameter combinations are tested.
410 INSTANTIATE_TEST_CASE_P(MultiChannel,
411 NetEqStereoTestNoJitter,
412 ::testing::ValuesIn(GetTestParameters()));
413
414 INSTANTIATE_TEST_CASE_P(MultiChannel,
415 NetEqStereoTestPositiveDrift,
416 ::testing::ValuesIn(GetTestParameters()));
417
418 INSTANTIATE_TEST_CASE_P(MultiChannel,
419 NetEqStereoTestNegativeDrift,
420 ::testing::ValuesIn(GetTestParameters()));
421
422 INSTANTIATE_TEST_CASE_P(MultiChannel,
423 NetEqStereoTestDelays,
424 ::testing::ValuesIn(GetTestParameters()));
425
426 INSTANTIATE_TEST_CASE_P(MultiChannel,
427 NetEqStereoTestLosses,
428 ::testing::ValuesIn(GetTestParameters()));
429
430 } // namespace webrtc
431