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/base/scoped_ptr.h" 19 #include "webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.h" 20 #include "webrtc/modules/audio_coding/neteq/include/neteq.h" 21 #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" 22 #include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h" 23 #include "webrtc/test/testsupport/fileutils.h" 24 25 namespace webrtc { 26 27 struct TestParameters { 28 int frame_size; 29 int sample_rate; 30 size_t 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. 42 class NetEqStereoTest : public ::testing::TestWithParam<TestParameters> { 43 protected: 44 static const int kTimeStepMs = 10; 45 static const size_t 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_( 55 static_cast<size_t>(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 = NetEqDecoder::kDecoderPCM16B; 94 if (num_channels_ == 2) { 95 multi_decoder = NetEqDecoder::kDecoderPCM16B_2ch; 96 } else if (num_channels_ == 5) { 97 multi_decoder = NetEqDecoder::kDecoderPCM16B_5ch; 98 } else { 99 FAIL() << "Only 2 and 5 channels supported for 8000 Hz."; 100 } 101 break; 102 case 16000: 103 mono_decoder = NetEqDecoder::kDecoderPCM16Bwb; 104 if (num_channels_ == 2) { 105 multi_decoder = NetEqDecoder::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 = NetEqDecoder::kDecoderPCM16Bswb32kHz; 112 if (num_channels_ == 2) { 113 multi_decoder = NetEqDecoder::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 = NetEqDecoder::kDecoderPCM16Bswb48kHz; 120 if (num_channels_ == 2) { 121 multi_decoder = NetEqDecoder::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, neteq_mono_->RegisterPayloadType(mono_decoder, "mono", 130 kPayloadTypeMono)); 131 ASSERT_EQ(NetEq::kOK, 132 neteq_->RegisterPayloadType(multi_decoder, "multi-channel", 133 kPayloadTypeMulti)); 134 } 135 136 virtual void TearDown() {} 137 138 int GetNewPackets() { 139 if (!input_file_->Read(frame_size_samples_, input_)) { 140 return -1; 141 } 142 payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_, 143 encoded_); 144 if (frame_size_samples_ * 2 != payload_size_bytes_) { 145 return -1; 146 } 147 int next_send_time = rtp_generator_mono_.GetRtpHeader(kPayloadTypeMono, 148 frame_size_samples_, 149 &rtp_header_mono_); 150 test::InputAudioFile::DuplicateInterleaved(input_, frame_size_samples_, 151 num_channels_, 152 input_multi_channel_); 153 multi_payload_size_bytes_ = WebRtcPcm16b_Encode( 154 input_multi_channel_, frame_size_samples_ * num_channels_, 155 encoded_multi_channel_); 156 if (frame_size_samples_ * 2 * num_channels_ != multi_payload_size_bytes_) { 157 return -1; 158 } 159 rtp_generator_.GetRtpHeader(kPayloadTypeMulti, frame_size_samples_, 160 &rtp_header_); 161 return next_send_time; 162 } 163 164 void VerifyOutput(size_t num_samples) { 165 for (size_t i = 0; i < num_samples; ++i) { 166 for (size_t j = 0; j < num_channels_; ++j) { 167 ASSERT_EQ(output_[i], output_multi_channel_[i * num_channels_ + j]) << 168 "Diff in sample " << i << ", channel " << j << "."; 169 } 170 } 171 } 172 173 virtual int GetArrivalTime(int send_time) { 174 int arrival_time = last_arrival_time_ + (send_time - last_send_time_); 175 last_send_time_ = send_time; 176 last_arrival_time_ = arrival_time; 177 return arrival_time; 178 } 179 180 virtual bool Lost() { return false; } 181 182 void RunTest(int num_loops) { 183 // Get next input packets (mono and multi-channel). 184 int next_send_time; 185 int next_arrival_time; 186 do { 187 next_send_time = GetNewPackets(); 188 ASSERT_NE(-1, next_send_time); 189 next_arrival_time = GetArrivalTime(next_send_time); 190 } while (Lost()); // If lost, immediately read the next packet. 191 192 int time_now = 0; 193 for (int k = 0; k < num_loops; ++k) { 194 while (time_now >= next_arrival_time) { 195 // Insert packet in mono instance. 196 ASSERT_EQ(NetEq::kOK, 197 neteq_mono_->InsertPacket(rtp_header_mono_, 198 rtc::ArrayView<const uint8_t>( 199 encoded_, payload_size_bytes_), 200 next_arrival_time)); 201 // Insert packet in multi-channel instance. 202 ASSERT_EQ(NetEq::kOK, neteq_->InsertPacket( 203 rtp_header_, rtc::ArrayView<const uint8_t>( 204 encoded_multi_channel_, 205 multi_payload_size_bytes_), 206 next_arrival_time)); 207 // Get next input packets (mono and multi-channel). 208 do { 209 next_send_time = GetNewPackets(); 210 ASSERT_NE(-1, next_send_time); 211 next_arrival_time = GetArrivalTime(next_send_time); 212 } while (Lost()); // If lost, immediately read the next packet. 213 } 214 NetEqOutputType output_type; 215 // Get audio from mono instance. 216 size_t samples_per_channel; 217 size_t num_channels; 218 EXPECT_EQ(NetEq::kOK, 219 neteq_mono_->GetAudio(kMaxBlockSize, output_, 220 &samples_per_channel, &num_channels, 221 &output_type)); 222 EXPECT_EQ(1u, num_channels); 223 EXPECT_EQ(output_size_samples_, samples_per_channel); 224 // Get audio from multi-channel instance. 225 ASSERT_EQ(NetEq::kOK, 226 neteq_->GetAudio(kMaxBlockSize * num_channels_, 227 output_multi_channel_, 228 &samples_per_channel, &num_channels, 229 &output_type)); 230 EXPECT_EQ(num_channels_, num_channels); 231 EXPECT_EQ(output_size_samples_, samples_per_channel); 232 std::ostringstream ss; 233 ss << "Lap number " << k << "."; 234 SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. 235 // Compare mono and multi-channel. 236 ASSERT_NO_FATAL_FAILURE(VerifyOutput(output_size_samples_)); 237 238 time_now += kTimeStepMs; 239 } 240 } 241 242 const size_t num_channels_; 243 const int sample_rate_hz_; 244 const int samples_per_ms_; 245 const int frame_size_ms_; 246 const size_t frame_size_samples_; 247 const size_t output_size_samples_; 248 NetEq* neteq_mono_; 249 NetEq* neteq_; 250 test::RtpGenerator rtp_generator_mono_; 251 test::RtpGenerator rtp_generator_; 252 int16_t* input_; 253 int16_t* input_multi_channel_; 254 uint8_t* encoded_; 255 uint8_t* encoded_multi_channel_; 256 int16_t output_[kMaxBlockSize]; 257 int16_t* output_multi_channel_; 258 WebRtcRTPHeader rtp_header_mono_; 259 WebRtcRTPHeader rtp_header_; 260 size_t payload_size_bytes_; 261 size_t multi_payload_size_bytes_; 262 int last_send_time_; 263 int last_arrival_time_; 264 rtc::scoped_ptr<test::InputAudioFile> input_file_; 265 }; 266 267 class NetEqStereoTestNoJitter : public NetEqStereoTest { 268 protected: 269 NetEqStereoTestNoJitter() 270 : NetEqStereoTest() { 271 // Start the sender 100 ms before the receiver to pre-fill the buffer. 272 // This is to avoid doing preemptive expand early in the test. 273 // TODO(hlundin): Mock the decision making instead to control the modes. 274 last_arrival_time_ = -100; 275 } 276 }; 277 278 #if defined(WEBRTC_ANDROID) 279 #define MAYBE_RunTest DISABLED_RunTest 280 #else 281 #define MAYBE_RunTest RunTest 282 #endif 283 TEST_P(NetEqStereoTestNoJitter, MAYBE_RunTest) { 284 RunTest(8); 285 } 286 287 class NetEqStereoTestPositiveDrift : public NetEqStereoTest { 288 protected: 289 NetEqStereoTestPositiveDrift() 290 : NetEqStereoTest(), 291 drift_factor(0.9) { 292 // Start the sender 100 ms before the receiver to pre-fill the buffer. 293 // This is to avoid doing preemptive expand early in the test. 294 // TODO(hlundin): Mock the decision making instead to control the modes. 295 last_arrival_time_ = -100; 296 } 297 virtual int GetArrivalTime(int send_time) { 298 int arrival_time = last_arrival_time_ + 299 drift_factor * (send_time - last_send_time_); 300 last_send_time_ = send_time; 301 last_arrival_time_ = arrival_time; 302 return arrival_time; 303 } 304 305 double drift_factor; 306 }; 307 308 TEST_P(NetEqStereoTestPositiveDrift, MAYBE_RunTest) { 309 RunTest(100); 310 } 311 312 class NetEqStereoTestNegativeDrift : public NetEqStereoTestPositiveDrift { 313 protected: 314 NetEqStereoTestNegativeDrift() 315 : NetEqStereoTestPositiveDrift() { 316 drift_factor = 1.1; 317 last_arrival_time_ = 0; 318 } 319 }; 320 321 TEST_P(NetEqStereoTestNegativeDrift, MAYBE_RunTest) { 322 RunTest(100); 323 } 324 325 class NetEqStereoTestDelays : public NetEqStereoTest { 326 protected: 327 static const int kDelayInterval = 10; 328 static const int kDelay = 1000; 329 NetEqStereoTestDelays() 330 : NetEqStereoTest(), 331 frame_index_(0) { 332 } 333 334 virtual int GetArrivalTime(int send_time) { 335 // Deliver immediately, unless we have a back-log. 336 int arrival_time = std::min(last_arrival_time_, send_time); 337 if (++frame_index_ % kDelayInterval == 0) { 338 // Delay this packet. 339 arrival_time += kDelay; 340 } 341 last_send_time_ = send_time; 342 last_arrival_time_ = arrival_time; 343 return arrival_time; 344 } 345 346 int frame_index_; 347 }; 348 349 TEST_P(NetEqStereoTestDelays, MAYBE_RunTest) { 350 RunTest(1000); 351 } 352 353 class NetEqStereoTestLosses : public NetEqStereoTest { 354 protected: 355 static const int kLossInterval = 10; 356 NetEqStereoTestLosses() 357 : NetEqStereoTest(), 358 frame_index_(0) { 359 } 360 361 virtual bool Lost() { 362 return (++frame_index_) % kLossInterval == 0; 363 } 364 365 int frame_index_; 366 }; 367 368 // TODO(pbos): Enable on non-Android, this went failing while being accidentally 369 // disabled on all platforms and not just Android. 370 // https://bugs.chromium.org/p/webrtc/issues/detail?id=5387 371 TEST_P(NetEqStereoTestLosses, DISABLED_RunTest) { 372 RunTest(100); 373 } 374 375 376 // Creates a list of parameter sets. 377 std::list<TestParameters> GetTestParameters() { 378 std::list<TestParameters> l; 379 const int sample_rates[] = {8000, 16000, 32000}; 380 const int num_rates = sizeof(sample_rates) / sizeof(sample_rates[0]); 381 // Loop through sample rates. 382 for (int rate_index = 0; rate_index < num_rates; ++rate_index) { 383 int sample_rate = sample_rates[rate_index]; 384 // Loop through all frame sizes between 10 and 60 ms. 385 for (int frame_size = 10; frame_size <= 60; frame_size += 10) { 386 TestParameters p; 387 p.frame_size = frame_size; 388 p.sample_rate = sample_rate; 389 p.num_channels = 2; 390 l.push_back(p); 391 if (sample_rate == 8000) { 392 // Add a five-channel test for 8000 Hz. 393 p.num_channels = 5; 394 l.push_back(p); 395 } 396 } 397 } 398 return l; 399 } 400 401 // Pretty-printing the test parameters in case of an error. 402 void PrintTo(const TestParameters& p, ::std::ostream* os) { 403 *os << "{frame_size = " << p.frame_size << 404 ", num_channels = " << p.num_channels << 405 ", sample_rate = " << p.sample_rate << "}"; 406 } 407 408 // Instantiate the tests. Each test is instantiated using the function above, 409 // so that all different parameter combinations are tested. 410 INSTANTIATE_TEST_CASE_P(MultiChannel, 411 NetEqStereoTestNoJitter, 412 ::testing::ValuesIn(GetTestParameters())); 413 414 INSTANTIATE_TEST_CASE_P(MultiChannel, 415 NetEqStereoTestPositiveDrift, 416 ::testing::ValuesIn(GetTestParameters())); 417 418 INSTANTIATE_TEST_CASE_P(MultiChannel, 419 NetEqStereoTestNegativeDrift, 420 ::testing::ValuesIn(GetTestParameters())); 421 422 INSTANTIATE_TEST_CASE_P(MultiChannel, 423 NetEqStereoTestDelays, 424 ::testing::ValuesIn(GetTestParameters())); 425 426 INSTANTIATE_TEST_CASE_P(MultiChannel, 427 NetEqStereoTestLosses, 428 ::testing::ValuesIn(GetTestParameters())); 429 430 } // namespace webrtc 431