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