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 <string> 15 #include <list> 16 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" 25 26 namespace webrtc { 27 28 struct TestParameters { 29 int frame_size; 30 int sample_rate; 31 int num_channels; 32 }; 33 34 // This is a parameterized test. The test parameters are supplied through a 35 // TestParameters struct, which is obtained through the GetParam() method. 36 // 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 42 // mono output. 43 class NetEqStereoTest : public ::testing::TestWithParam<TestParameters> { 44 protected: 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; 49 50 NetEqStereoTest() 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), 61 last_send_time_(0), 62 last_arrival_time_(0) { 63 NetEq::Config config; 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 * 71 num_channels_]; 72 output_multi_channel_ = new int16_t[kMaxBlockSize * num_channels_]; 73 } 74 75 ~NetEqStereoTest() { 76 delete neteq_mono_; 77 delete neteq_; 78 delete [] input_; 79 delete [] encoded_; 80 delete [] input_multi_channel_; 81 delete [] encoded_multi_channel_; 82 delete [] output_multi_channel_; 83 } 84 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_) { 92 case 8000: 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; 98 } else { 99 FAIL() << "Only 2 and 5 channels supported for 8000 Hz."; 100 } 101 break; 102 case 16000: 103 mono_decoder = kDecoderPCM16Bwb; 104 if (num_channels_ == 2) { 105 multi_decoder = kDecoderPCM16Bwb_2ch; 106 } else { 107 FAIL() << "More than 2 channels is not supported for 16000 Hz."; 108 } 109 break; 110 case 32000: 111 mono_decoder = kDecoderPCM16Bswb32kHz; 112 if (num_channels_ == 2) { 113 multi_decoder = kDecoderPCM16Bswb32kHz_2ch; 114 } else { 115 FAIL() << "More than 2 channels is not supported for 32000 Hz."; 116 } 117 break; 118 case 48000: 119 mono_decoder = kDecoderPCM16Bswb48kHz; 120 if (num_channels_ == 2) { 121 multi_decoder = kDecoderPCM16Bswb48kHz_2ch; 122 } else { 123 FAIL() << "More than 2 channels is not supported for 48000 Hz."; 124 } 125 break; 126 default: 127 FAIL() << "We shouldn't get here."; 128 } 129 ASSERT_EQ(NetEq::kOK, 130 neteq_mono_->RegisterPayloadType(mono_decoder, 131 kPayloadTypeMono)); 132 ASSERT_EQ(NetEq::kOK, 133 neteq_->RegisterPayloadType(multi_decoder, 134 kPayloadTypeMulti)); 135 } 136 137 virtual void TearDown() {} 138 139 int GetNewPackets() { 140 if (!input_file_->Read(frame_size_samples_, input_)) { 141 return -1; 142 } 143 payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_, 144 encoded_); 145 if (frame_size_samples_ * 2 != payload_size_bytes_) { 146 return -1; 147 } 148 int next_send_time = rtp_generator_mono_.GetRtpHeader(kPayloadTypeMono, 149 frame_size_samples_, 150 &rtp_header_mono_); 151 test::InputAudioFile::DuplicateInterleaved(input_, frame_size_samples_, 152 num_channels_, 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_) { 158 return -1; 159 } 160 rtp_generator_.GetRtpHeader(kPayloadTypeMulti, frame_size_samples_, 161 &rtp_header_); 162 return next_send_time; 163 } 164 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 << "."; 170 } 171 } 172 } 173 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; 178 return arrival_time; 179 } 180 181 virtual bool Lost() { return false; } 182 183 void RunTest(int num_loops) { 184 // Get next input packets (mono and multi-channel). 185 int next_send_time; 186 int next_arrival_time; 187 do { 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. 192 193 int time_now = 0; 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_, 199 payload_size_bytes_, 200 next_arrival_time)); 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_, 205 next_arrival_time)); 206 // Get next input packets (mono and multi-channel). 207 do { 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. 212 } 213 NetEqOutputType output_type; 214 // Get audio from mono instance. 215 int samples_per_channel; 216 int num_channels; 217 EXPECT_EQ(NetEq::kOK, 218 neteq_mono_->GetAudio(kMaxBlockSize, output_, 219 &samples_per_channel, &num_channels, 220 &output_type)); 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, 228 &output_type)); 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_)); 236 237 time_now += kTimeStepMs; 238 } 239 } 240 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_; 247 NetEq* neteq_mono_; 248 NetEq* neteq_; 249 test::RtpGenerator rtp_generator_mono_; 250 test::RtpGenerator rtp_generator_; 251 int16_t* input_; 252 int16_t* input_multi_channel_; 253 uint8_t* encoded_; 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_; 261 int last_send_time_; 262 int last_arrival_time_; 263 scoped_ptr<test::InputAudioFile> input_file_; 264 }; 265 266 class NetEqStereoTestNoJitter : public NetEqStereoTest { 267 protected: 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; 274 } 275 }; 276 277 TEST_P(NetEqStereoTestNoJitter, DISABLED_ON_ANDROID(RunTest)) { 278 RunTest(8); 279 } 280 281 class NetEqStereoTestPositiveDrift : public NetEqStereoTest { 282 protected: 283 NetEqStereoTestPositiveDrift() 284 : NetEqStereoTest(), 285 drift_factor(0.9) { 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; 290 } 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; 296 return arrival_time; 297 } 298 299 double drift_factor; 300 }; 301 302 TEST_P(NetEqStereoTestPositiveDrift, DISABLED_ON_ANDROID(RunTest)) { 303 RunTest(100); 304 } 305 306 class NetEqStereoTestNegativeDrift : public NetEqStereoTestPositiveDrift { 307 protected: 308 NetEqStereoTestNegativeDrift() 309 : NetEqStereoTestPositiveDrift() { 310 drift_factor = 1.1; 311 last_arrival_time_ = 0; 312 } 313 }; 314 315 TEST_P(NetEqStereoTestNegativeDrift, DISABLED_ON_ANDROID(RunTest)) { 316 RunTest(100); 317 } 318 319 class NetEqStereoTestDelays : public NetEqStereoTest { 320 protected: 321 static const int kDelayInterval = 10; 322 static const int kDelay = 1000; 323 NetEqStereoTestDelays() 324 : NetEqStereoTest(), 325 frame_index_(0) { 326 } 327 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; 334 } 335 last_send_time_ = send_time; 336 last_arrival_time_ = arrival_time; 337 return arrival_time; 338 } 339 340 int frame_index_; 341 }; 342 343 TEST_P(NetEqStereoTestDelays, DISABLED_ON_ANDROID(RunTest)) { 344 RunTest(1000); 345 } 346 347 class NetEqStereoTestLosses : public NetEqStereoTest { 348 protected: 349 static const int kLossInterval = 10; 350 NetEqStereoTestLosses() 351 : NetEqStereoTest(), 352 frame_index_(0) { 353 } 354 355 virtual bool Lost() { 356 return (++frame_index_) % kLossInterval == 0; 357 } 358 359 int frame_index_; 360 }; 361 362 TEST_P(NetEqStereoTestLosses, DISABLED_ON_ANDROID(RunTest)) { 363 RunTest(100); 364 } 365 366 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) { 377 TestParameters p; 378 p.frame_size = frame_size; 379 p.sample_rate = sample_rate; 380 p.num_channels = 2; 381 l.push_back(p); 382 if (sample_rate == 8000) { 383 // Add a five-channel test for 8000 Hz. 384 p.num_channels = 5; 385 l.push_back(p); 386 } 387 } 388 } 389 return l; 390 } 391 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 << "}"; 397 } 398 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())); 404 405 INSTANTIATE_TEST_CASE_P(MultiChannel, 406 NetEqStereoTestPositiveDrift, 407 ::testing::ValuesIn(GetTestParameters())); 408 409 INSTANTIATE_TEST_CASE_P(MultiChannel, 410 NetEqStereoTestNegativeDrift, 411 ::testing::ValuesIn(GetTestParameters())); 412 413 INSTANTIATE_TEST_CASE_P(MultiChannel, 414 NetEqStereoTestDelays, 415 ::testing::ValuesIn(GetTestParameters())); 416 417 INSTANTIATE_TEST_CASE_P(MultiChannel, 418 NetEqStereoTestLosses, 419 ::testing::ValuesIn(GetTestParameters())); 420 421 } // namespace webrtc 422