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