blob: 1bef9c83a2d4aad703d83881a17eb78d065adcb2 [file] [log] [blame]
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +00001/*
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
henrik.lundin@webrtc.orgbe50ab62014-03-04 15:10:03 +000013#include <algorithm>
kwiberg2d0c3322016-02-14 09:28:33 -080014#include <memory>
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000015#include <string>
16#include <list>
17
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "api/audio_codecs/builtin_audio_decoder_factory.h"
Mirko Bonadei71207422017-09-15 13:58:09 +020019#include "common_types.h" // NOLINT(build/include)
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "modules/audio_coding/codecs/pcm16b/pcm16b.h"
21#include "modules/audio_coding/neteq/include/neteq.h"
22#include "modules/audio_coding/neteq/tools/input_audio_file.h"
23#include "modules/audio_coding/neteq/tools/rtp_generator.h"
24#include "modules/include/module_common_types.h"
25#include "test/gtest.h"
26#include "test/testsupport/fileutils.h"
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000027
28namespace webrtc {
29
30struct TestParameters {
31 int frame_size;
32 int sample_rate;
Peter Kasting69558702016-01-12 16:26:35 -080033 size_t num_channels;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000034};
35
36// This is a parameterized test. The test parameters are supplied through a
37// TestParameters struct, which is obtained through the GetParam() method.
38//
39// The objective of the test is to create a mono input signal and a
40// multi-channel input signal, where each channel is identical to the mono
41// input channel. The two input signals are processed through their respective
42// NetEq instances. After that, the output signals are compared. The expected
43// result is that each channel in the multi-channel output is identical to the
44// mono output.
45class NetEqStereoTest : public ::testing::TestWithParam<TestParameters> {
46 protected:
47 static const int kTimeStepMs = 10;
Peter Kastingdce40cf2015-08-24 14:52:23 -070048 static const size_t kMaxBlockSize = 480; // 10 ms @ 48 kHz.
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000049 static const uint8_t kPayloadTypeMono = 95;
50 static const uint8_t kPayloadTypeMulti = 96;
51
52 NetEqStereoTest()
53 : num_channels_(GetParam().num_channels),
54 sample_rate_hz_(GetParam().sample_rate),
55 samples_per_ms_(sample_rate_hz_ / 1000),
56 frame_size_ms_(GetParam().frame_size),
Peter Kastingdce40cf2015-08-24 14:52:23 -070057 frame_size_samples_(
58 static_cast<size_t>(frame_size_ms_ * samples_per_ms_)),
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000059 output_size_samples_(10 * samples_per_ms_),
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000060 rtp_generator_mono_(samples_per_ms_),
61 rtp_generator_(samples_per_ms_),
62 payload_size_bytes_(0),
63 multi_payload_size_bytes_(0),
64 last_send_time_(0),
65 last_arrival_time_(0) {
henrik.lundin@webrtc.org35ead382014-04-14 18:49:17 +000066 NetEq::Config config;
67 config.sample_rate_hz = sample_rate_hz_;
ossue3525782016-05-25 07:37:43 -070068 rtc::scoped_refptr<AudioDecoderFactory> factory =
69 CreateBuiltinAudioDecoderFactory();
70 neteq_mono_ = NetEq::Create(config, factory);
71 neteq_ = NetEq::Create(config, factory);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000072 input_ = new int16_t[frame_size_samples_];
73 encoded_ = new uint8_t[2 * frame_size_samples_];
74 input_multi_channel_ = new int16_t[frame_size_samples_ * num_channels_];
75 encoded_multi_channel_ = new uint8_t[frame_size_samples_ * 2 *
76 num_channels_];
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000077 }
78
79 ~NetEqStereoTest() {
80 delete neteq_mono_;
81 delete neteq_;
82 delete [] input_;
83 delete [] encoded_;
84 delete [] input_multi_channel_;
85 delete [] encoded_multi_channel_;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000086 }
87
88 virtual void SetUp() {
89 const std::string file_name =
90 webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
91 input_file_.reset(new test::InputAudioFile(file_name));
92 NetEqDecoder mono_decoder;
93 NetEqDecoder multi_decoder;
94 switch (sample_rate_hz_) {
95 case 8000:
kwibergee1879c2015-10-29 06:20:28 -070096 mono_decoder = NetEqDecoder::kDecoderPCM16B;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000097 if (num_channels_ == 2) {
kwibergee1879c2015-10-29 06:20:28 -070098 multi_decoder = NetEqDecoder::kDecoderPCM16B_2ch;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000099 } else if (num_channels_ == 5) {
kwibergee1879c2015-10-29 06:20:28 -0700100 multi_decoder = NetEqDecoder::kDecoderPCM16B_5ch;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000101 } else {
102 FAIL() << "Only 2 and 5 channels supported for 8000 Hz.";
103 }
104 break;
105 case 16000:
kwibergee1879c2015-10-29 06:20:28 -0700106 mono_decoder = NetEqDecoder::kDecoderPCM16Bwb;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000107 if (num_channels_ == 2) {
kwibergee1879c2015-10-29 06:20:28 -0700108 multi_decoder = NetEqDecoder::kDecoderPCM16Bwb_2ch;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000109 } else {
110 FAIL() << "More than 2 channels is not supported for 16000 Hz.";
111 }
112 break;
113 case 32000:
kwibergee1879c2015-10-29 06:20:28 -0700114 mono_decoder = NetEqDecoder::kDecoderPCM16Bswb32kHz;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000115 if (num_channels_ == 2) {
kwibergee1879c2015-10-29 06:20:28 -0700116 multi_decoder = NetEqDecoder::kDecoderPCM16Bswb32kHz_2ch;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000117 } else {
118 FAIL() << "More than 2 channels is not supported for 32000 Hz.";
119 }
120 break;
121 case 48000:
kwibergee1879c2015-10-29 06:20:28 -0700122 mono_decoder = NetEqDecoder::kDecoderPCM16Bswb48kHz;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000123 if (num_channels_ == 2) {
kwibergee1879c2015-10-29 06:20:28 -0700124 multi_decoder = NetEqDecoder::kDecoderPCM16Bswb48kHz_2ch;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000125 } else {
126 FAIL() << "More than 2 channels is not supported for 48000 Hz.";
127 }
128 break;
129 default:
130 FAIL() << "We shouldn't get here.";
131 }
henrik.lundin4cf61dd2015-12-09 06:20:58 -0800132 ASSERT_EQ(NetEq::kOK, neteq_mono_->RegisterPayloadType(mono_decoder, "mono",
133 kPayloadTypeMono));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000134 ASSERT_EQ(NetEq::kOK,
henrik.lundin4cf61dd2015-12-09 06:20:58 -0800135 neteq_->RegisterPayloadType(multi_decoder, "multi-channel",
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000136 kPayloadTypeMulti));
137 }
138
139 virtual void TearDown() {}
140
141 int GetNewPackets() {
142 if (!input_file_->Read(frame_size_samples_, input_)) {
143 return -1;
144 }
145 payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_,
146 encoded_);
147 if (frame_size_samples_ * 2 != payload_size_bytes_) {
148 return -1;
149 }
150 int next_send_time = rtp_generator_mono_.GetRtpHeader(kPayloadTypeMono,
151 frame_size_samples_,
152 &rtp_header_mono_);
153 test::InputAudioFile::DuplicateInterleaved(input_, frame_size_samples_,
154 num_channels_,
155 input_multi_channel_);
156 multi_payload_size_bytes_ = WebRtcPcm16b_Encode(
157 input_multi_channel_, frame_size_samples_ * num_channels_,
158 encoded_multi_channel_);
159 if (frame_size_samples_ * 2 * num_channels_ != multi_payload_size_bytes_) {
160 return -1;
161 }
162 rtp_generator_.GetRtpHeader(kPayloadTypeMulti, frame_size_samples_,
163 &rtp_header_);
164 return next_send_time;
165 }
166
ivoc72c08ed2016-01-20 07:26:24 -0800167 virtual void VerifyOutput(size_t num_samples) {
yujo36b1a5f2017-06-12 12:45:32 -0700168 const int16_t* output_data = output_.data();
169 const int16_t* output_multi_channel_data = output_multi_channel_.data();
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000170 for (size_t i = 0; i < num_samples; ++i) {
Peter Kasting69558702016-01-12 16:26:35 -0800171 for (size_t j = 0; j < num_channels_; ++j) {
yujo36b1a5f2017-06-12 12:45:32 -0700172 ASSERT_EQ(output_data[i],
173 output_multi_channel_data[i * num_channels_ + j])
henrik.lundin6d8e0112016-03-04 10:34:21 -0800174 << "Diff in sample " << i << ", channel " << j << ".";
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000175 }
176 }
177 }
178
179 virtual int GetArrivalTime(int send_time) {
180 int arrival_time = last_arrival_time_ + (send_time - last_send_time_);
181 last_send_time_ = send_time;
182 last_arrival_time_ = arrival_time;
183 return arrival_time;
184 }
185
186 virtual bool Lost() { return false; }
187
188 void RunTest(int num_loops) {
189 // Get next input packets (mono and multi-channel).
190 int next_send_time;
191 int next_arrival_time;
192 do {
193 next_send_time = GetNewPackets();
194 ASSERT_NE(-1, next_send_time);
195 next_arrival_time = GetArrivalTime(next_send_time);
196 } while (Lost()); // If lost, immediately read the next packet.
197
198 int time_now = 0;
199 for (int k = 0; k < num_loops; ++k) {
200 while (time_now >= next_arrival_time) {
201 // Insert packet in mono instance.
202 ASSERT_EQ(NetEq::kOK,
henrik.lundin246ef3e2017-04-24 09:14:32 -0700203 neteq_mono_->InsertPacket(rtp_header_mono_,
kwibergee2bac22015-11-11 10:34:00 -0800204 rtc::ArrayView<const uint8_t>(
205 encoded_, payload_size_bytes_),
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000206 next_arrival_time));
207 // Insert packet in multi-channel instance.
Henrik Lundin70c09bd2017-04-24 15:56:56 +0200208 ASSERT_EQ(NetEq::kOK,
209 neteq_->InsertPacket(
henrik.lundin246ef3e2017-04-24 09:14:32 -0700210 rtp_header_,
Henrik Lundin70c09bd2017-04-24 15:56:56 +0200211 rtc::ArrayView<const uint8_t>(encoded_multi_channel_,
212 multi_payload_size_bytes_),
213 next_arrival_time));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000214 // Get next input packets (mono and multi-channel).
215 do {
216 next_send_time = GetNewPackets();
217 ASSERT_NE(-1, next_send_time);
218 next_arrival_time = GetArrivalTime(next_send_time);
219 } while (Lost()); // If lost, immediately read the next packet.
220 }
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000221 // Get audio from mono instance.
henrik.lundin7a926812016-05-12 13:51:28 -0700222 bool muted;
223 EXPECT_EQ(NetEq::kOK, neteq_mono_->GetAudio(&output_, &muted));
224 ASSERT_FALSE(muted);
henrik.lundin6d8e0112016-03-04 10:34:21 -0800225 EXPECT_EQ(1u, output_.num_channels_);
226 EXPECT_EQ(output_size_samples_, output_.samples_per_channel_);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000227 // Get audio from multi-channel instance.
henrik.lundin7a926812016-05-12 13:51:28 -0700228 ASSERT_EQ(NetEq::kOK, neteq_->GetAudio(&output_multi_channel_, &muted));
229 ASSERT_FALSE(muted);
henrik.lundin6d8e0112016-03-04 10:34:21 -0800230 EXPECT_EQ(num_channels_, output_multi_channel_.num_channels_);
231 EXPECT_EQ(output_size_samples_,
232 output_multi_channel_.samples_per_channel_);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000233 std::ostringstream ss;
234 ss << "Lap number " << k << ".";
235 SCOPED_TRACE(ss.str()); // Print out the parameter values on failure.
236 // Compare mono and multi-channel.
237 ASSERT_NO_FATAL_FAILURE(VerifyOutput(output_size_samples_));
238
239 time_now += kTimeStepMs;
240 }
241 }
242
Peter Kasting69558702016-01-12 16:26:35 -0800243 const size_t num_channels_;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000244 const int sample_rate_hz_;
245 const int samples_per_ms_;
246 const int frame_size_ms_;
Peter Kastingdce40cf2015-08-24 14:52:23 -0700247 const size_t frame_size_samples_;
248 const size_t output_size_samples_;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000249 NetEq* neteq_mono_;
250 NetEq* neteq_;
251 test::RtpGenerator rtp_generator_mono_;
252 test::RtpGenerator rtp_generator_;
253 int16_t* input_;
254 int16_t* input_multi_channel_;
255 uint8_t* encoded_;
256 uint8_t* encoded_multi_channel_;
henrik.lundin6d8e0112016-03-04 10:34:21 -0800257 AudioFrame output_;
258 AudioFrame output_multi_channel_;
henrik.lundin246ef3e2017-04-24 09:14:32 -0700259 RTPHeader rtp_header_mono_;
260 RTPHeader rtp_header_;
Peter Kastingdce40cf2015-08-24 14:52:23 -0700261 size_t payload_size_bytes_;
262 size_t multi_payload_size_bytes_;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000263 int last_send_time_;
264 int last_arrival_time_;
kwiberg2d0c3322016-02-14 09:28:33 -0800265 std::unique_ptr<test::InputAudioFile> input_file_;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000266};
267
268class NetEqStereoTestNoJitter : public NetEqStereoTest {
269 protected:
270 NetEqStereoTestNoJitter()
271 : NetEqStereoTest() {
272 // Start the sender 100 ms before the receiver to pre-fill the buffer.
273 // This is to avoid doing preemptive expand early in the test.
274 // TODO(hlundin): Mock the decision making instead to control the modes.
275 last_arrival_time_ = -100;
276 }
277};
278
ivoc72c08ed2016-01-20 07:26:24 -0800279TEST_P(NetEqStereoTestNoJitter, RunTest) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000280 RunTest(8);
281}
282
283class NetEqStereoTestPositiveDrift : public NetEqStereoTest {
284 protected:
285 NetEqStereoTestPositiveDrift()
286 : NetEqStereoTest(),
287 drift_factor(0.9) {
288 // Start the sender 100 ms before the receiver to pre-fill the buffer.
289 // This is to avoid doing preemptive expand early in the test.
290 // TODO(hlundin): Mock the decision making instead to control the modes.
291 last_arrival_time_ = -100;
292 }
293 virtual int GetArrivalTime(int send_time) {
294 int arrival_time = last_arrival_time_ +
295 drift_factor * (send_time - last_send_time_);
296 last_send_time_ = send_time;
297 last_arrival_time_ = arrival_time;
298 return arrival_time;
299 }
300
301 double drift_factor;
302};
303
ivoc72c08ed2016-01-20 07:26:24 -0800304TEST_P(NetEqStereoTestPositiveDrift, RunTest) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000305 RunTest(100);
306}
307
308class NetEqStereoTestNegativeDrift : public NetEqStereoTestPositiveDrift {
309 protected:
310 NetEqStereoTestNegativeDrift()
311 : NetEqStereoTestPositiveDrift() {
312 drift_factor = 1.1;
313 last_arrival_time_ = 0;
314 }
315};
316
ivoc72c08ed2016-01-20 07:26:24 -0800317TEST_P(NetEqStereoTestNegativeDrift, RunTest) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000318 RunTest(100);
319}
320
321class NetEqStereoTestDelays : public NetEqStereoTest {
322 protected:
323 static const int kDelayInterval = 10;
324 static const int kDelay = 1000;
325 NetEqStereoTestDelays()
326 : NetEqStereoTest(),
327 frame_index_(0) {
328 }
329
330 virtual int GetArrivalTime(int send_time) {
331 // Deliver immediately, unless we have a back-log.
332 int arrival_time = std::min(last_arrival_time_, send_time);
333 if (++frame_index_ % kDelayInterval == 0) {
334 // Delay this packet.
335 arrival_time += kDelay;
336 }
337 last_send_time_ = send_time;
338 last_arrival_time_ = arrival_time;
339 return arrival_time;
340 }
341
342 int frame_index_;
343};
344
ivoc72c08ed2016-01-20 07:26:24 -0800345TEST_P(NetEqStereoTestDelays, RunTest) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000346 RunTest(1000);
347}
348
349class NetEqStereoTestLosses : public NetEqStereoTest {
350 protected:
351 static const int kLossInterval = 10;
352 NetEqStereoTestLosses()
353 : NetEqStereoTest(),
354 frame_index_(0) {
355 }
356
357 virtual bool Lost() {
358 return (++frame_index_) % kLossInterval == 0;
359 }
360
ivoc72c08ed2016-01-20 07:26:24 -0800361 // TODO(hlundin): NetEq is not giving bitexact results for these cases.
362 virtual void VerifyOutput(size_t num_samples) {
363 for (size_t i = 0; i < num_samples; ++i) {
yujo36b1a5f2017-06-12 12:45:32 -0700364 const int16_t* output_data = output_.data();
365 const int16_t* output_multi_channel_data = output_multi_channel_.data();
henrik.lundin6d8e0112016-03-04 10:34:21 -0800366 auto first_channel_sample =
yujo36b1a5f2017-06-12 12:45:32 -0700367 output_multi_channel_data[i * num_channels_];
ivoc72c08ed2016-01-20 07:26:24 -0800368 for (size_t j = 0; j < num_channels_; ++j) {
369 const int kErrorMargin = 200;
yujo36b1a5f2017-06-12 12:45:32 -0700370 EXPECT_NEAR(output_data[i],
371 output_multi_channel_data[i * num_channels_ + j],
ivoc72c08ed2016-01-20 07:26:24 -0800372 kErrorMargin)
373 << "Diff in sample " << i << ", channel " << j << ".";
374 EXPECT_EQ(first_channel_sample,
yujo36b1a5f2017-06-12 12:45:32 -0700375 output_multi_channel_data[i * num_channels_ + j]);
ivoc72c08ed2016-01-20 07:26:24 -0800376 }
377 }
378 }
379
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000380 int frame_index_;
381};
382
ivoc72c08ed2016-01-20 07:26:24 -0800383TEST_P(NetEqStereoTestLosses, RunTest) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000384 RunTest(100);
385}
386
387
388// Creates a list of parameter sets.
389std::list<TestParameters> GetTestParameters() {
390 std::list<TestParameters> l;
391 const int sample_rates[] = {8000, 16000, 32000};
392 const int num_rates = sizeof(sample_rates) / sizeof(sample_rates[0]);
393 // Loop through sample rates.
394 for (int rate_index = 0; rate_index < num_rates; ++rate_index) {
395 int sample_rate = sample_rates[rate_index];
396 // Loop through all frame sizes between 10 and 60 ms.
397 for (int frame_size = 10; frame_size <= 60; frame_size += 10) {
398 TestParameters p;
399 p.frame_size = frame_size;
400 p.sample_rate = sample_rate;
401 p.num_channels = 2;
402 l.push_back(p);
403 if (sample_rate == 8000) {
404 // Add a five-channel test for 8000 Hz.
405 p.num_channels = 5;
406 l.push_back(p);
407 }
408 }
409 }
410 return l;
411}
412
413// Pretty-printing the test parameters in case of an error.
414void PrintTo(const TestParameters& p, ::std::ostream* os) {
415 *os << "{frame_size = " << p.frame_size <<
416 ", num_channels = " << p.num_channels <<
417 ", sample_rate = " << p.sample_rate << "}";
418}
419
420// Instantiate the tests. Each test is instantiated using the function above,
421// so that all different parameter combinations are tested.
422INSTANTIATE_TEST_CASE_P(MultiChannel,
423 NetEqStereoTestNoJitter,
424 ::testing::ValuesIn(GetTestParameters()));
425
426INSTANTIATE_TEST_CASE_P(MultiChannel,
427 NetEqStereoTestPositiveDrift,
428 ::testing::ValuesIn(GetTestParameters()));
429
430INSTANTIATE_TEST_CASE_P(MultiChannel,
431 NetEqStereoTestNegativeDrift,
432 ::testing::ValuesIn(GetTestParameters()));
433
434INSTANTIATE_TEST_CASE_P(MultiChannel,
435 NetEqStereoTestDelays,
436 ::testing::ValuesIn(GetTestParameters()));
437
438INSTANTIATE_TEST_CASE_P(MultiChannel,
439 NetEqStereoTestLosses,
440 ::testing::ValuesIn(GetTestParameters()));
441
442} // namespace webrtc