blob: e59637ba8d5c758fdc6840d591983f891ca6c2a1 [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>
Yves Gerey665174f2018-06-19 15:03:05 +020014#include <list>
kwiberg2d0c3322016-02-14 09:28:33 -080015#include <memory>
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000016#include <string>
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000017
Fredrik Solenbergbbf21a32018-04-12 22:44:09 +020018#include "api/audio/audio_frame.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "api/audio_codecs/builtin_audio_decoder_factory.h"
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"
Jonas Olsson366a50c2018-09-06 13:41:30 +020024#include "rtc_base/strings/string_builder.h"
Alessio Bazzica8f319a32019-07-24 16:47:02 +000025#include "system_wrappers/include/clock.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026#include "test/gtest.h"
Steve Anton10542f22019-01-11 09:11:00 -080027#include "test/testsupport/file_utils.h"
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000028
29namespace webrtc {
30
31struct TestParameters {
32 int frame_size;
33 int sample_rate;
Peter Kasting69558702016-01-12 16:26:35 -080034 size_t num_channels;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000035};
36
37// This is a parameterized test. The test parameters are supplied through a
38// TestParameters struct, which is obtained through the GetParam() method.
39//
40// The objective of the test is to create a mono input signal and a
41// multi-channel input signal, where each channel is identical to the mono
42// input channel. The two input signals are processed through their respective
43// NetEq instances. After that, the output signals are compared. The expected
44// result is that each channel in the multi-channel output is identical to the
45// mono output.
46class NetEqStereoTest : public ::testing::TestWithParam<TestParameters> {
47 protected:
48 static const int kTimeStepMs = 10;
Peter Kastingdce40cf2015-08-24 14:52:23 -070049 static const size_t kMaxBlockSize = 480; // 10 ms @ 48 kHz.
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000050 static const uint8_t kPayloadTypeMono = 95;
51 static const uint8_t kPayloadTypeMulti = 96;
52
53 NetEqStereoTest()
54 : num_channels_(GetParam().num_channels),
55 sample_rate_hz_(GetParam().sample_rate),
56 samples_per_ms_(sample_rate_hz_ / 1000),
57 frame_size_ms_(GetParam().frame_size),
Peter Kastingdce40cf2015-08-24 14:52:23 -070058 frame_size_samples_(
59 static_cast<size_t>(frame_size_ms_ * samples_per_ms_)),
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000060 output_size_samples_(10 * samples_per_ms_),
Alessio Bazzica8f319a32019-07-24 16:47:02 +000061 clock_(0),
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000062 rtp_generator_mono_(samples_per_ms_),
63 rtp_generator_(samples_per_ms_),
64 payload_size_bytes_(0),
65 multi_payload_size_bytes_(0),
66 last_send_time_(0),
67 last_arrival_time_(0) {
henrik.lundin@webrtc.org35ead382014-04-14 18:49:17 +000068 NetEq::Config config;
69 config.sample_rate_hz = sample_rate_hz_;
ossue3525782016-05-25 07:37:43 -070070 rtc::scoped_refptr<AudioDecoderFactory> factory =
71 CreateBuiltinAudioDecoderFactory();
Alessio Bazzica8f319a32019-07-24 16:47:02 +000072 neteq_mono_ = NetEq::Create(config, &clock_, factory);
73 neteq_ = NetEq::Create(config, &clock_, factory);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000074 input_ = new int16_t[frame_size_samples_];
75 encoded_ = new uint8_t[2 * frame_size_samples_];
76 input_multi_channel_ = new int16_t[frame_size_samples_ * num_channels_];
Yves Gerey665174f2018-06-19 15:03:05 +020077 encoded_multi_channel_ =
78 new uint8_t[frame_size_samples_ * 2 * num_channels_];
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000079 }
80
81 ~NetEqStereoTest() {
82 delete neteq_mono_;
83 delete neteq_;
Yves Gerey665174f2018-06-19 15:03:05 +020084 delete[] input_;
85 delete[] encoded_;
86 delete[] input_multi_channel_;
87 delete[] encoded_multi_channel_;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000088 }
89
90 virtual void SetUp() {
91 const std::string file_name =
92 webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
93 input_file_.reset(new test::InputAudioFile(file_name));
Niels Möller05543682019-01-10 16:55:06 +010094 RTC_CHECK_GE(num_channels_, 2);
95 ASSERT_TRUE(neteq_mono_->RegisterPayloadType(
96 kPayloadTypeMono, SdpAudioFormat("l16", sample_rate_hz_, 1)));
97 ASSERT_TRUE(neteq_->RegisterPayloadType(
98 kPayloadTypeMulti,
99 SdpAudioFormat("l16", sample_rate_hz_, num_channels_)));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000100 }
101
102 virtual void TearDown() {}
103
104 int GetNewPackets() {
105 if (!input_file_->Read(frame_size_samples_, input_)) {
106 return -1;
107 }
Yves Gerey665174f2018-06-19 15:03:05 +0200108 payload_size_bytes_ =
109 WebRtcPcm16b_Encode(input_, frame_size_samples_, encoded_);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000110 if (frame_size_samples_ * 2 != payload_size_bytes_) {
111 return -1;
112 }
Yves Gerey665174f2018-06-19 15:03:05 +0200113 int next_send_time = rtp_generator_mono_.GetRtpHeader(
114 kPayloadTypeMono, frame_size_samples_, &rtp_header_mono_);
115 test::InputAudioFile::DuplicateInterleaved(
116 input_, frame_size_samples_, num_channels_, input_multi_channel_);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000117 multi_payload_size_bytes_ = WebRtcPcm16b_Encode(
118 input_multi_channel_, frame_size_samples_ * num_channels_,
119 encoded_multi_channel_);
120 if (frame_size_samples_ * 2 * num_channels_ != multi_payload_size_bytes_) {
121 return -1;
122 }
123 rtp_generator_.GetRtpHeader(kPayloadTypeMulti, frame_size_samples_,
124 &rtp_header_);
125 return next_send_time;
126 }
127
ivoc72c08ed2016-01-20 07:26:24 -0800128 virtual void VerifyOutput(size_t num_samples) {
yujo36b1a5f2017-06-12 12:45:32 -0700129 const int16_t* output_data = output_.data();
130 const int16_t* output_multi_channel_data = output_multi_channel_.data();
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000131 for (size_t i = 0; i < num_samples; ++i) {
Peter Kasting69558702016-01-12 16:26:35 -0800132 for (size_t j = 0; j < num_channels_; ++j) {
yujo36b1a5f2017-06-12 12:45:32 -0700133 ASSERT_EQ(output_data[i],
134 output_multi_channel_data[i * num_channels_ + j])
henrik.lundin6d8e0112016-03-04 10:34:21 -0800135 << "Diff in sample " << i << ", channel " << j << ".";
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000136 }
137 }
138 }
139
140 virtual int GetArrivalTime(int send_time) {
141 int arrival_time = last_arrival_time_ + (send_time - last_send_time_);
142 last_send_time_ = send_time;
143 last_arrival_time_ = arrival_time;
144 return arrival_time;
145 }
146
147 virtual bool Lost() { return false; }
148
149 void RunTest(int num_loops) {
150 // Get next input packets (mono and multi-channel).
151 int next_send_time;
152 int next_arrival_time;
153 do {
154 next_send_time = GetNewPackets();
155 ASSERT_NE(-1, next_send_time);
156 next_arrival_time = GetArrivalTime(next_send_time);
157 } while (Lost()); // If lost, immediately read the next packet.
158
159 int time_now = 0;
160 for (int k = 0; k < num_loops; ++k) {
161 while (time_now >= next_arrival_time) {
162 // Insert packet in mono instance.
163 ASSERT_EQ(NetEq::kOK,
Karl Wiberg45eb1352019-10-10 14:23:00 +0200164 neteq_mono_->InsertPacket(
165 rtp_header_mono_, rtc::ArrayView<const uint8_t>(
166 encoded_, payload_size_bytes_)));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000167 // Insert packet in multi-channel instance.
Karl Wiberg45eb1352019-10-10 14:23:00 +0200168 ASSERT_EQ(NetEq::kOK, neteq_->InsertPacket(
169 rtp_header_, rtc::ArrayView<const uint8_t>(
170 encoded_multi_channel_,
171 multi_payload_size_bytes_)));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000172 // Get next input packets (mono and multi-channel).
173 do {
174 next_send_time = GetNewPackets();
175 ASSERT_NE(-1, next_send_time);
176 next_arrival_time = GetArrivalTime(next_send_time);
177 } while (Lost()); // If lost, immediately read the next packet.
178 }
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000179 // Get audio from mono instance.
henrik.lundin7a926812016-05-12 13:51:28 -0700180 bool muted;
181 EXPECT_EQ(NetEq::kOK, neteq_mono_->GetAudio(&output_, &muted));
182 ASSERT_FALSE(muted);
henrik.lundin6d8e0112016-03-04 10:34:21 -0800183 EXPECT_EQ(1u, output_.num_channels_);
184 EXPECT_EQ(output_size_samples_, output_.samples_per_channel_);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000185 // Get audio from multi-channel instance.
henrik.lundin7a926812016-05-12 13:51:28 -0700186 ASSERT_EQ(NetEq::kOK, neteq_->GetAudio(&output_multi_channel_, &muted));
187 ASSERT_FALSE(muted);
henrik.lundin6d8e0112016-03-04 10:34:21 -0800188 EXPECT_EQ(num_channels_, output_multi_channel_.num_channels_);
189 EXPECT_EQ(output_size_samples_,
190 output_multi_channel_.samples_per_channel_);
Jonas Olsson366a50c2018-09-06 13:41:30 +0200191 rtc::StringBuilder ss;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000192 ss << "Lap number " << k << ".";
193 SCOPED_TRACE(ss.str()); // Print out the parameter values on failure.
194 // Compare mono and multi-channel.
195 ASSERT_NO_FATAL_FAILURE(VerifyOutput(output_size_samples_));
196
197 time_now += kTimeStepMs;
Alessio Bazzica8f319a32019-07-24 16:47:02 +0000198 clock_.AdvanceTimeMilliseconds(kTimeStepMs);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000199 }
200 }
201
Peter Kasting69558702016-01-12 16:26:35 -0800202 const size_t num_channels_;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000203 const int sample_rate_hz_;
204 const int samples_per_ms_;
205 const int frame_size_ms_;
Peter Kastingdce40cf2015-08-24 14:52:23 -0700206 const size_t frame_size_samples_;
207 const size_t output_size_samples_;
Alessio Bazzica8f319a32019-07-24 16:47:02 +0000208 SimulatedClock clock_;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000209 NetEq* neteq_mono_;
210 NetEq* neteq_;
211 test::RtpGenerator rtp_generator_mono_;
212 test::RtpGenerator rtp_generator_;
213 int16_t* input_;
214 int16_t* input_multi_channel_;
215 uint8_t* encoded_;
216 uint8_t* encoded_multi_channel_;
henrik.lundin6d8e0112016-03-04 10:34:21 -0800217 AudioFrame output_;
218 AudioFrame output_multi_channel_;
henrik.lundin246ef3e2017-04-24 09:14:32 -0700219 RTPHeader rtp_header_mono_;
220 RTPHeader rtp_header_;
Peter Kastingdce40cf2015-08-24 14:52:23 -0700221 size_t payload_size_bytes_;
222 size_t multi_payload_size_bytes_;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000223 int last_send_time_;
224 int last_arrival_time_;
kwiberg2d0c3322016-02-14 09:28:33 -0800225 std::unique_ptr<test::InputAudioFile> input_file_;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000226};
227
228class NetEqStereoTestNoJitter : public NetEqStereoTest {
229 protected:
Yves Gerey665174f2018-06-19 15:03:05 +0200230 NetEqStereoTestNoJitter() : NetEqStereoTest() {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000231 // Start the sender 100 ms before the receiver to pre-fill the buffer.
232 // This is to avoid doing preemptive expand early in the test.
233 // TODO(hlundin): Mock the decision making instead to control the modes.
234 last_arrival_time_ = -100;
235 }
236};
237
ivoc72c08ed2016-01-20 07:26:24 -0800238TEST_P(NetEqStereoTestNoJitter, RunTest) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000239 RunTest(8);
240}
241
242class NetEqStereoTestPositiveDrift : public NetEqStereoTest {
243 protected:
Yves Gerey665174f2018-06-19 15:03:05 +0200244 NetEqStereoTestPositiveDrift() : NetEqStereoTest(), drift_factor(0.9) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000245 // Start the sender 100 ms before the receiver to pre-fill the buffer.
246 // This is to avoid doing preemptive expand early in the test.
247 // TODO(hlundin): Mock the decision making instead to control the modes.
248 last_arrival_time_ = -100;
249 }
250 virtual int GetArrivalTime(int send_time) {
Yves Gerey665174f2018-06-19 15:03:05 +0200251 int arrival_time =
252 last_arrival_time_ + drift_factor * (send_time - last_send_time_);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000253 last_send_time_ = send_time;
254 last_arrival_time_ = arrival_time;
255 return arrival_time;
256 }
257
258 double drift_factor;
259};
260
ivoc72c08ed2016-01-20 07:26:24 -0800261TEST_P(NetEqStereoTestPositiveDrift, RunTest) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000262 RunTest(100);
263}
264
265class NetEqStereoTestNegativeDrift : public NetEqStereoTestPositiveDrift {
266 protected:
Yves Gerey665174f2018-06-19 15:03:05 +0200267 NetEqStereoTestNegativeDrift() : NetEqStereoTestPositiveDrift() {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000268 drift_factor = 1.1;
269 last_arrival_time_ = 0;
270 }
271};
272
ivoc72c08ed2016-01-20 07:26:24 -0800273TEST_P(NetEqStereoTestNegativeDrift, RunTest) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000274 RunTest(100);
275}
276
277class NetEqStereoTestDelays : public NetEqStereoTest {
278 protected:
279 static const int kDelayInterval = 10;
280 static const int kDelay = 1000;
Yves Gerey665174f2018-06-19 15:03:05 +0200281 NetEqStereoTestDelays() : NetEqStereoTest(), frame_index_(0) {}
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000282
283 virtual int GetArrivalTime(int send_time) {
284 // Deliver immediately, unless we have a back-log.
285 int arrival_time = std::min(last_arrival_time_, send_time);
286 if (++frame_index_ % kDelayInterval == 0) {
287 // Delay this packet.
288 arrival_time += kDelay;
289 }
290 last_send_time_ = send_time;
291 last_arrival_time_ = arrival_time;
292 return arrival_time;
293 }
294
295 int frame_index_;
296};
297
ivoc72c08ed2016-01-20 07:26:24 -0800298TEST_P(NetEqStereoTestDelays, RunTest) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000299 RunTest(1000);
300}
301
302class NetEqStereoTestLosses : public NetEqStereoTest {
303 protected:
304 static const int kLossInterval = 10;
Yves Gerey665174f2018-06-19 15:03:05 +0200305 NetEqStereoTestLosses() : NetEqStereoTest(), frame_index_(0) {}
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000306
Yves Gerey665174f2018-06-19 15:03:05 +0200307 virtual bool Lost() { return (++frame_index_) % kLossInterval == 0; }
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000308
ivoc72c08ed2016-01-20 07:26:24 -0800309 // TODO(hlundin): NetEq is not giving bitexact results for these cases.
310 virtual void VerifyOutput(size_t num_samples) {
311 for (size_t i = 0; i < num_samples; ++i) {
yujo36b1a5f2017-06-12 12:45:32 -0700312 const int16_t* output_data = output_.data();
313 const int16_t* output_multi_channel_data = output_multi_channel_.data();
Yves Gerey665174f2018-06-19 15:03:05 +0200314 auto first_channel_sample = output_multi_channel_data[i * num_channels_];
ivoc72c08ed2016-01-20 07:26:24 -0800315 for (size_t j = 0; j < num_channels_; ++j) {
316 const int kErrorMargin = 200;
yujo36b1a5f2017-06-12 12:45:32 -0700317 EXPECT_NEAR(output_data[i],
318 output_multi_channel_data[i * num_channels_ + j],
ivoc72c08ed2016-01-20 07:26:24 -0800319 kErrorMargin)
320 << "Diff in sample " << i << ", channel " << j << ".";
321 EXPECT_EQ(first_channel_sample,
yujo36b1a5f2017-06-12 12:45:32 -0700322 output_multi_channel_data[i * num_channels_ + j]);
ivoc72c08ed2016-01-20 07:26:24 -0800323 }
324 }
325 }
326
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000327 int frame_index_;
328};
329
ivoc72c08ed2016-01-20 07:26:24 -0800330TEST_P(NetEqStereoTestLosses, RunTest) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000331 RunTest(100);
332}
333
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000334// Creates a list of parameter sets.
335std::list<TestParameters> GetTestParameters() {
336 std::list<TestParameters> l;
337 const int sample_rates[] = {8000, 16000, 32000};
338 const int num_rates = sizeof(sample_rates) / sizeof(sample_rates[0]);
339 // Loop through sample rates.
340 for (int rate_index = 0; rate_index < num_rates; ++rate_index) {
341 int sample_rate = sample_rates[rate_index];
342 // Loop through all frame sizes between 10 and 60 ms.
343 for (int frame_size = 10; frame_size <= 60; frame_size += 10) {
344 TestParameters p;
345 p.frame_size = frame_size;
346 p.sample_rate = sample_rate;
347 p.num_channels = 2;
348 l.push_back(p);
349 if (sample_rate == 8000) {
350 // Add a five-channel test for 8000 Hz.
351 p.num_channels = 5;
352 l.push_back(p);
353 }
354 }
355 }
356 return l;
357}
358
359// Pretty-printing the test parameters in case of an error.
360void PrintTo(const TestParameters& p, ::std::ostream* os) {
Yves Gerey665174f2018-06-19 15:03:05 +0200361 *os << "{frame_size = " << p.frame_size
362 << ", num_channels = " << p.num_channels
363 << ", sample_rate = " << p.sample_rate << "}";
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000364}
365
366// Instantiate the tests. Each test is instantiated using the function above,
367// so that all different parameter combinations are tested.
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100368INSTANTIATE_TEST_SUITE_P(MultiChannel,
369 NetEqStereoTestNoJitter,
370 ::testing::ValuesIn(GetTestParameters()));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000371
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100372INSTANTIATE_TEST_SUITE_P(MultiChannel,
373 NetEqStereoTestPositiveDrift,
374 ::testing::ValuesIn(GetTestParameters()));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000375
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100376INSTANTIATE_TEST_SUITE_P(MultiChannel,
377 NetEqStereoTestNegativeDrift,
378 ::testing::ValuesIn(GetTestParameters()));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000379
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100380INSTANTIATE_TEST_SUITE_P(MultiChannel,
381 NetEqStereoTestDelays,
382 ::testing::ValuesIn(GetTestParameters()));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000383
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100384INSTANTIATE_TEST_SUITE_P(MultiChannel,
385 NetEqStereoTestLosses,
386 ::testing::ValuesIn(GetTestParameters()));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000387
388} // namespace webrtc