2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
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.
11 // Test to verify correct stereo and multi-channel operation.
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h"
19 #include "webrtc/modules/audio_coding/neteq/interface/neteq.h"
20 #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h"
21 #include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h"
22 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
23 #include "webrtc/test/testsupport/fileutils.h"
24 #include "webrtc/test/testsupport/gtest_disable.h"
28 struct TestParameters {
34 // This is a parameterized test. The test parameters are supplied through a
35 // TestParameters struct, which is obtained through the GetParam() method.
37 // The objective of the test is to create a mono input signal and a
38 // multi-channel input signal, where each channel is identical to the mono
39 // input channel. The two input signals are processed through their respective
40 // NetEq instances. After that, the output signals are compared. The expected
41 // result is that each channel in the multi-channel output is identical to the
43 class NetEqStereoTest : public ::testing::TestWithParam<TestParameters> {
45 static const int kTimeStepMs = 10;
46 static const int kMaxBlockSize = 480; // 10 ms @ 48 kHz.
47 static const uint8_t kPayloadTypeMono = 95;
48 static const uint8_t kPayloadTypeMulti = 96;
51 : num_channels_(GetParam().num_channels),
52 sample_rate_hz_(GetParam().sample_rate),
53 samples_per_ms_(sample_rate_hz_ / 1000),
54 frame_size_ms_(GetParam().frame_size),
55 frame_size_samples_(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),
62 last_arrival_time_(0) {
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 *
72 output_multi_channel_ = new int16_t[kMaxBlockSize * num_channels_];
80 delete [] input_multi_channel_;
81 delete [] encoded_multi_channel_;
82 delete [] output_multi_channel_;
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_) {
93 mono_decoder = kDecoderPCM16B;
94 if (num_channels_ == 2) {
95 multi_decoder = kDecoderPCM16B_2ch;
96 } else if (num_channels_ == 5) {
97 multi_decoder = kDecoderPCM16B_5ch;
99 FAIL() << "Only 2 and 5 channels supported for 8000 Hz.";
103 mono_decoder = kDecoderPCM16Bwb;
104 if (num_channels_ == 2) {
105 multi_decoder = kDecoderPCM16Bwb_2ch;
107 FAIL() << "More than 2 channels is not supported for 16000 Hz.";
111 mono_decoder = kDecoderPCM16Bswb32kHz;
112 if (num_channels_ == 2) {
113 multi_decoder = kDecoderPCM16Bswb32kHz_2ch;
115 FAIL() << "More than 2 channels is not supported for 32000 Hz.";
119 mono_decoder = kDecoderPCM16Bswb48kHz;
120 if (num_channels_ == 2) {
121 multi_decoder = kDecoderPCM16Bswb48kHz_2ch;
123 FAIL() << "More than 2 channels is not supported for 48000 Hz.";
127 FAIL() << "We shouldn't get here.";
129 ASSERT_EQ(NetEq::kOK,
130 neteq_mono_->RegisterPayloadType(mono_decoder,
132 ASSERT_EQ(NetEq::kOK,
133 neteq_->RegisterPayloadType(multi_decoder,
137 virtual void TearDown() {}
139 int GetNewPackets() {
140 if (!input_file_->Read(frame_size_samples_, input_)) {
143 payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_,
145 if (frame_size_samples_ * 2 != payload_size_bytes_) {
148 int next_send_time = rtp_generator_mono_.GetRtpHeader(kPayloadTypeMono,
151 test::InputAudioFile::DuplicateInterleaved(input_, frame_size_samples_,
153 input_multi_channel_);
154 multi_payload_size_bytes_ = WebRtcPcm16b_Encode(
155 input_multi_channel_, frame_size_samples_ * num_channels_,
156 encoded_multi_channel_);
157 if (frame_size_samples_ * 2 * num_channels_ != multi_payload_size_bytes_) {
160 rtp_generator_.GetRtpHeader(kPayloadTypeMulti, frame_size_samples_,
162 return next_send_time;
165 void VerifyOutput(size_t num_samples) {
166 for (size_t i = 0; i < num_samples; ++i) {
167 for (int j = 0; j < num_channels_; ++j) {
168 ASSERT_EQ(output_[i], output_multi_channel_[i * num_channels_ + j]) <<
169 "Diff in sample " << i << ", channel " << j << ".";
174 virtual int GetArrivalTime(int send_time) {
175 int arrival_time = last_arrival_time_ + (send_time - last_send_time_);
176 last_send_time_ = send_time;
177 last_arrival_time_ = arrival_time;
181 virtual bool Lost() { return false; }
183 void RunTest(int num_loops) {
184 // Get next input packets (mono and multi-channel).
186 int next_arrival_time;
188 next_send_time = GetNewPackets();
189 ASSERT_NE(-1, next_send_time);
190 next_arrival_time = GetArrivalTime(next_send_time);
191 } while (Lost()); // If lost, immediately read the next packet.
194 for (int k = 0; k < num_loops; ++k) {
195 while (time_now >= next_arrival_time) {
196 // Insert packet in mono instance.
197 ASSERT_EQ(NetEq::kOK,
198 neteq_mono_->InsertPacket(rtp_header_mono_, encoded_,
201 // Insert packet in multi-channel instance.
202 ASSERT_EQ(NetEq::kOK,
203 neteq_->InsertPacket(rtp_header_, encoded_multi_channel_,
204 multi_payload_size_bytes_,
206 // Get next input packets (mono and multi-channel).
208 next_send_time = GetNewPackets();
209 ASSERT_NE(-1, next_send_time);
210 next_arrival_time = GetArrivalTime(next_send_time);
211 } while (Lost()); // If lost, immediately read the next packet.
213 NetEqOutputType output_type;
214 // Get audio from mono instance.
215 int samples_per_channel;
217 EXPECT_EQ(NetEq::kOK,
218 neteq_mono_->GetAudio(kMaxBlockSize, output_,
219 &samples_per_channel, &num_channels,
221 EXPECT_EQ(1, num_channels);
222 EXPECT_EQ(output_size_samples_, samples_per_channel);
223 // Get audio from multi-channel instance.
224 ASSERT_EQ(NetEq::kOK,
225 neteq_->GetAudio(kMaxBlockSize * num_channels_,
226 output_multi_channel_,
227 &samples_per_channel, &num_channels,
229 EXPECT_EQ(num_channels_, num_channels);
230 EXPECT_EQ(output_size_samples_, samples_per_channel);
231 std::ostringstream ss;
232 ss << "Lap number " << k << ".";
233 SCOPED_TRACE(ss.str()); // Print out the parameter values on failure.
234 // Compare mono and multi-channel.
235 ASSERT_NO_FATAL_FAILURE(VerifyOutput(output_size_samples_));
237 time_now += kTimeStepMs;
241 const int num_channels_;
242 const int sample_rate_hz_;
243 const int samples_per_ms_;
244 const int frame_size_ms_;
245 const int frame_size_samples_;
246 const int output_size_samples_;
249 test::RtpGenerator rtp_generator_mono_;
250 test::RtpGenerator rtp_generator_;
252 int16_t* input_multi_channel_;
254 uint8_t* encoded_multi_channel_;
255 int16_t output_[kMaxBlockSize];
256 int16_t* output_multi_channel_;
257 WebRtcRTPHeader rtp_header_mono_;
258 WebRtcRTPHeader rtp_header_;
259 int payload_size_bytes_;
260 int multi_payload_size_bytes_;
262 int last_arrival_time_;
263 scoped_ptr<test::InputAudioFile> input_file_;
266 class NetEqStereoTestNoJitter : public NetEqStereoTest {
268 NetEqStereoTestNoJitter()
269 : NetEqStereoTest() {
270 // Start the sender 100 ms before the receiver to pre-fill the buffer.
271 // This is to avoid doing preemptive expand early in the test.
272 // TODO(hlundin): Mock the decision making instead to control the modes.
273 last_arrival_time_ = -100;
277 TEST_P(NetEqStereoTestNoJitter, DISABLED_ON_ANDROID(RunTest)) {
281 class NetEqStereoTestPositiveDrift : public NetEqStereoTest {
283 NetEqStereoTestPositiveDrift()
286 // Start the sender 100 ms before the receiver to pre-fill the buffer.
287 // This is to avoid doing preemptive expand early in the test.
288 // TODO(hlundin): Mock the decision making instead to control the modes.
289 last_arrival_time_ = -100;
291 virtual int GetArrivalTime(int send_time) {
292 int arrival_time = last_arrival_time_ +
293 drift_factor * (send_time - last_send_time_);
294 last_send_time_ = send_time;
295 last_arrival_time_ = arrival_time;
302 TEST_P(NetEqStereoTestPositiveDrift, DISABLED_ON_ANDROID(RunTest)) {
306 class NetEqStereoTestNegativeDrift : public NetEqStereoTestPositiveDrift {
308 NetEqStereoTestNegativeDrift()
309 : NetEqStereoTestPositiveDrift() {
311 last_arrival_time_ = 0;
315 TEST_P(NetEqStereoTestNegativeDrift, DISABLED_ON_ANDROID(RunTest)) {
319 class NetEqStereoTestDelays : public NetEqStereoTest {
321 static const int kDelayInterval = 10;
322 static const int kDelay = 1000;
323 NetEqStereoTestDelays()
328 virtual int GetArrivalTime(int send_time) {
329 // Deliver immediately, unless we have a back-log.
330 int arrival_time = std::min(last_arrival_time_, send_time);
331 if (++frame_index_ % kDelayInterval == 0) {
332 // Delay this packet.
333 arrival_time += kDelay;
335 last_send_time_ = send_time;
336 last_arrival_time_ = arrival_time;
343 TEST_P(NetEqStereoTestDelays, DISABLED_ON_ANDROID(RunTest)) {
347 class NetEqStereoTestLosses : public NetEqStereoTest {
349 static const int kLossInterval = 10;
350 NetEqStereoTestLosses()
355 virtual bool Lost() {
356 return (++frame_index_) % kLossInterval == 0;
362 TEST_P(NetEqStereoTestLosses, DISABLED_ON_ANDROID(RunTest)) {
367 // Creates a list of parameter sets.
368 std::list<TestParameters> GetTestParameters() {
369 std::list<TestParameters> l;
370 const int sample_rates[] = {8000, 16000, 32000};
371 const int num_rates = sizeof(sample_rates) / sizeof(sample_rates[0]);
372 // Loop through sample rates.
373 for (int rate_index = 0; rate_index < num_rates; ++rate_index) {
374 int sample_rate = sample_rates[rate_index];
375 // Loop through all frame sizes between 10 and 60 ms.
376 for (int frame_size = 10; frame_size <= 60; frame_size += 10) {
378 p.frame_size = frame_size;
379 p.sample_rate = sample_rate;
382 if (sample_rate == 8000) {
383 // Add a five-channel test for 8000 Hz.
392 // Pretty-printing the test parameters in case of an error.
393 void PrintTo(const TestParameters& p, ::std::ostream* os) {
394 *os << "{frame_size = " << p.frame_size <<
395 ", num_channels = " << p.num_channels <<
396 ", sample_rate = " << p.sample_rate << "}";
399 // Instantiate the tests. Each test is instantiated using the function above,
400 // so that all different parameter combinations are tested.
401 INSTANTIATE_TEST_CASE_P(MultiChannel,
402 NetEqStereoTestNoJitter,
403 ::testing::ValuesIn(GetTestParameters()));
405 INSTANTIATE_TEST_CASE_P(MultiChannel,
406 NetEqStereoTestPositiveDrift,
407 ::testing::ValuesIn(GetTestParameters()));
409 INSTANTIATE_TEST_CASE_P(MultiChannel,
410 NetEqStereoTestNegativeDrift,
411 ::testing::ValuesIn(GetTestParameters()));
413 INSTANTIATE_TEST_CASE_P(MultiChannel,
414 NetEqStereoTestDelays,
415 ::testing::ValuesIn(GetTestParameters()));
417 INSTANTIATE_TEST_CASE_P(MultiChannel,
418 NetEqStereoTestLosses,
419 ::testing::ValuesIn(GetTestParameters()));
421 } // namespace webrtc