neteq_stereo_unittest.cc (15720B)
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 <cstddef> 15 #include <cstdint> 16 #include <cstring> 17 #include <list> 18 #include <memory> 19 #include <ostream> 20 #include <string> 21 22 #include "api/array_view.h" 23 #include "api/audio/audio_frame.h" 24 #include "api/audio_codecs/audio_format.h" 25 #include "api/audio_codecs/builtin_audio_decoder_factory.h" 26 #include "api/environment/environment.h" 27 #include "api/environment/environment_factory.h" 28 #include "api/neteq/default_neteq_factory.h" 29 #include "api/neteq/neteq.h" 30 #include "api/rtp_headers.h" 31 #include "api/units/timestamp.h" 32 #include "modules/audio_coding/codecs/pcm16b/pcm16b.h" 33 #include "modules/audio_coding/neteq/tools/input_audio_file.h" 34 #include "modules/audio_coding/neteq/tools/rtp_generator.h" 35 #include "rtc_base/checks.h" 36 #include "rtc_base/strings/string_builder.h" 37 #include "system_wrappers/include/clock.h" 38 #include "test/gtest.h" 39 #include "test/testsupport/file_utils.h" 40 41 namespace webrtc { 42 43 struct TestParameters { 44 int frame_size; 45 int sample_rate; 46 size_t num_channels; 47 }; 48 49 // This is a parameterized test. The test parameters are supplied through a 50 // TestParameters struct, which is obtained through the GetParam() method. 51 // 52 // The objective of the test is to create a mono input signal and a 53 // multi-channel input signal, where each channel is identical to the mono 54 // input channel. The two input signals are processed through their respective 55 // NetEq instances. After that, the output signals are compared. The expected 56 // result is that each channel in the multi-channel output is identical to the 57 // mono output. 58 class NetEqStereoTest : public ::testing::TestWithParam<TestParameters> { 59 protected: 60 static const int kTimeStepMs = 10; 61 static const size_t kMaxBlockSize = 480; // 10 ms @ 48 kHz. 62 static const uint8_t kPayloadTypeMono = 95; 63 static const uint8_t kPayloadTypeMulti = 96; 64 65 NetEqStereoTest() 66 : num_channels_(GetParam().num_channels), 67 sample_rate_hz_(GetParam().sample_rate), 68 samples_per_ms_(sample_rate_hz_ / 1000), 69 frame_size_ms_(GetParam().frame_size), 70 frame_size_samples_( 71 static_cast<size_t>(frame_size_ms_ * samples_per_ms_)), 72 output_size_samples_(10 * samples_per_ms_), 73 clock_(0), 74 env_(CreateEnvironment(&clock_)), 75 rtp_generator_mono_(samples_per_ms_), 76 rtp_generator_(samples_per_ms_), 77 payload_size_bytes_(0), 78 multi_payload_size_bytes_(0), 79 last_send_time_(0), 80 last_arrival_time_(0) { 81 NetEq::Config config; 82 config.sample_rate_hz = sample_rate_hz_; 83 DefaultNetEqFactory neteq_factory; 84 auto decoder_factory = CreateBuiltinAudioDecoderFactory(); 85 neteq_mono_ = neteq_factory.Create(env_, config, decoder_factory); 86 neteq_ = neteq_factory.Create(env_, config, decoder_factory); 87 input_ = new int16_t[frame_size_samples_]; 88 encoded_ = new uint8_t[2 * frame_size_samples_]; 89 input_multi_channel_ = new int16_t[frame_size_samples_ * num_channels_]; 90 encoded_multi_channel_ = 91 new uint8_t[frame_size_samples_ * 2 * num_channels_]; 92 } 93 94 ~NetEqStereoTest() override { 95 delete[] input_; 96 delete[] encoded_; 97 delete[] input_multi_channel_; 98 delete[] encoded_multi_channel_; 99 } 100 101 void SetUp() override { 102 const std::string file_name = 103 test::ResourcePath("audio_coding/testfile32kHz", "pcm"); 104 input_file_.reset(new test::InputAudioFile(file_name)); 105 RTC_CHECK_GE(num_channels_, 2); 106 ASSERT_TRUE(neteq_mono_->RegisterPayloadType( 107 kPayloadTypeMono, SdpAudioFormat("l16", sample_rate_hz_, 1))); 108 ASSERT_TRUE(neteq_->RegisterPayloadType( 109 kPayloadTypeMulti, 110 SdpAudioFormat("l16", sample_rate_hz_, num_channels_))); 111 } 112 113 void TearDown() override {} 114 115 int GetNewPackets() { 116 if (!input_file_->Read(frame_size_samples_, input_)) { 117 return -1; 118 } 119 payload_size_bytes_ = 120 WebRtcPcm16b_Encode(input_, frame_size_samples_, encoded_); 121 if (frame_size_samples_ * 2 != payload_size_bytes_) { 122 return -1; 123 } 124 int next_send_time_ms = rtp_generator_mono_.GetRtpHeader( 125 kPayloadTypeMono, frame_size_samples_, &rtp_header_mono_); 126 MakeMultiChannelInput(); 127 multi_payload_size_bytes_ = WebRtcPcm16b_Encode( 128 input_multi_channel_, frame_size_samples_ * num_channels_, 129 encoded_multi_channel_); 130 if (frame_size_samples_ * 2 * num_channels_ != multi_payload_size_bytes_) { 131 return -1; 132 } 133 rtp_generator_.GetRtpHeader(kPayloadTypeMulti, frame_size_samples_, 134 &rtp_header_); 135 return next_send_time_ms; 136 } 137 138 virtual void MakeMultiChannelInput() { 139 test::InputAudioFile::DuplicateInterleaved( 140 input_, frame_size_samples_, num_channels_, input_multi_channel_); 141 } 142 143 virtual void VerifyOutput(size_t num_samples) { 144 const int16_t* output_data = output_.data(); 145 const int16_t* output_multi_channel_data = output_multi_channel_.data(); 146 for (size_t i = 0; i < num_samples; ++i) { 147 for (size_t j = 0; j < num_channels_; ++j) { 148 ASSERT_EQ(output_data[i], 149 output_multi_channel_data[i * num_channels_ + j]) 150 << "Diff in sample " << i << ", channel " << j << "."; 151 } 152 } 153 } 154 155 virtual int GetArrivalTime(int send_time) { 156 int arrival_time = last_arrival_time_ + (send_time - last_send_time_); 157 last_send_time_ = send_time; 158 last_arrival_time_ = arrival_time; 159 return arrival_time; 160 } 161 162 virtual bool Lost() { return false; } 163 164 void RunTest(int num_loops) { 165 // Get next input packets (mono and multi-channel). 166 int next_send_time_ms; 167 int next_arrival_time_ms; 168 do { 169 next_send_time_ms = GetNewPackets(); 170 ASSERT_NE(-1, next_send_time_ms); 171 next_arrival_time_ms = GetArrivalTime(next_send_time_ms); 172 } while (Lost()); // If lost, immediately read the next packet. 173 174 int time_now_ms = 0; 175 for (int k = 0; k < num_loops; ++k) { 176 while (time_now_ms >= next_arrival_time_ms) { 177 // Insert packet in mono instance. 178 ASSERT_EQ(NetEq::kOK, 179 neteq_mono_->InsertPacket( 180 rtp_header_mono_, 181 ArrayView<const uint8_t>(encoded_, payload_size_bytes_), 182 Timestamp::Millis(time_now_ms))); 183 // Insert packet in multi-channel instance. 184 ASSERT_EQ(NetEq::kOK, 185 neteq_->InsertPacket( 186 rtp_header_, 187 ArrayView<const uint8_t>(encoded_multi_channel_, 188 multi_payload_size_bytes_), 189 Timestamp::Millis(time_now_ms))); 190 // Get next input packets (mono and multi-channel). 191 do { 192 next_send_time_ms = GetNewPackets(); 193 ASSERT_NE(-1, next_send_time_ms); 194 next_arrival_time_ms = GetArrivalTime(next_send_time_ms); 195 } while (Lost()); // If lost, immediately read the next packet. 196 } 197 // Get audio from mono instance. 198 bool muted; 199 EXPECT_EQ(NetEq::kOK, neteq_mono_->GetAudio(&output_, &muted)); 200 ASSERT_FALSE(muted); 201 EXPECT_EQ(1u, output_.num_channels_); 202 EXPECT_EQ(output_size_samples_, output_.samples_per_channel_); 203 // Get audio from multi-channel instance. 204 ASSERT_EQ(NetEq::kOK, neteq_->GetAudio(&output_multi_channel_, &muted)); 205 ASSERT_FALSE(muted); 206 EXPECT_EQ(num_channels_, output_multi_channel_.num_channels_); 207 EXPECT_EQ(output_size_samples_, 208 output_multi_channel_.samples_per_channel_); 209 StringBuilder ss; 210 ss << "Lap number " << k << "."; 211 SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. 212 // Compare mono and multi-channel. 213 ASSERT_NO_FATAL_FAILURE(VerifyOutput(output_size_samples_)); 214 215 time_now_ms += kTimeStepMs; 216 clock_.AdvanceTimeMilliseconds(kTimeStepMs); 217 } 218 } 219 220 const size_t num_channels_; 221 const int sample_rate_hz_; 222 const int samples_per_ms_; 223 const int frame_size_ms_; 224 const size_t frame_size_samples_; 225 const size_t output_size_samples_; 226 SimulatedClock clock_; 227 const Environment env_; 228 std::unique_ptr<NetEq> neteq_mono_; 229 std::unique_ptr<NetEq> neteq_; 230 test::RtpGenerator rtp_generator_mono_; 231 test::RtpGenerator rtp_generator_; 232 int16_t* input_; 233 int16_t* input_multi_channel_; 234 uint8_t* encoded_; 235 uint8_t* encoded_multi_channel_; 236 AudioFrame output_; 237 AudioFrame output_multi_channel_; 238 RTPHeader rtp_header_mono_; 239 RTPHeader rtp_header_; 240 size_t payload_size_bytes_; 241 size_t multi_payload_size_bytes_; 242 int last_send_time_; 243 int last_arrival_time_; 244 std::unique_ptr<test::InputAudioFile> input_file_; 245 }; 246 247 class NetEqStereoTestNoJitter : public NetEqStereoTest { 248 protected: 249 NetEqStereoTestNoJitter() : NetEqStereoTest() { 250 // Start the sender 100 ms before the receiver to pre-fill the buffer. 251 // This is to avoid doing preemptive expand early in the test. 252 // TODO(hlundin): Mock the decision making instead to control the modes. 253 last_arrival_time_ = -100; 254 } 255 }; 256 257 TEST_P(NetEqStereoTestNoJitter, RunTest) { 258 RunTest(8); 259 } 260 261 class NetEqStereoTestPositiveDrift : public NetEqStereoTest { 262 protected: 263 NetEqStereoTestPositiveDrift() : NetEqStereoTest(), drift_factor(0.9) { 264 // Start the sender 100 ms before the receiver to pre-fill the buffer. 265 // This is to avoid doing preemptive expand early in the test. 266 // TODO(hlundin): Mock the decision making instead to control the modes. 267 last_arrival_time_ = -100; 268 } 269 int GetArrivalTime(int send_time) override { 270 int arrival_time = 271 last_arrival_time_ + drift_factor * (send_time - last_send_time_); 272 last_send_time_ = send_time; 273 last_arrival_time_ = arrival_time; 274 return arrival_time; 275 } 276 277 double drift_factor; 278 }; 279 280 TEST_P(NetEqStereoTestPositiveDrift, RunTest) { 281 RunTest(100); 282 } 283 284 class NetEqStereoTestNegativeDrift : public NetEqStereoTestPositiveDrift { 285 protected: 286 NetEqStereoTestNegativeDrift() : NetEqStereoTestPositiveDrift() { 287 drift_factor = 1.1; 288 last_arrival_time_ = 0; 289 } 290 }; 291 292 TEST_P(NetEqStereoTestNegativeDrift, RunTest) { 293 RunTest(100); 294 } 295 296 class NetEqStereoTestDelays : public NetEqStereoTest { 297 protected: 298 static const int kDelayInterval = 10; 299 static const int kDelay = 1000; 300 NetEqStereoTestDelays() : NetEqStereoTest(), frame_index_(0) {} 301 302 int GetArrivalTime(int send_time) override { 303 // Deliver immediately, unless we have a back-log. 304 int arrival_time = std::min(last_arrival_time_, send_time); 305 if (++frame_index_ % kDelayInterval == 0) { 306 // Delay this packet. 307 arrival_time += kDelay; 308 } 309 last_send_time_ = send_time; 310 last_arrival_time_ = arrival_time; 311 return arrival_time; 312 } 313 314 int frame_index_; 315 }; 316 317 TEST_P(NetEqStereoTestDelays, RunTest) { 318 RunTest(1000); 319 } 320 321 class NetEqStereoTestLosses : public NetEqStereoTest { 322 protected: 323 static const int kLossInterval = 10; 324 NetEqStereoTestLosses() : NetEqStereoTest(), frame_index_(0) {} 325 326 bool Lost() override { return (++frame_index_) % kLossInterval == 0; } 327 328 // TODO(hlundin): NetEq is not giving bitexact results for these cases. 329 void VerifyOutput(size_t num_samples) override { 330 for (size_t i = 0; i < num_samples; ++i) { 331 const int16_t* output_data = output_.data(); 332 const int16_t* output_multi_channel_data = output_multi_channel_.data(); 333 auto first_channel_sample = output_multi_channel_data[i * num_channels_]; 334 for (size_t j = 0; j < num_channels_; ++j) { 335 const int kErrorMargin = 200; 336 EXPECT_NEAR(output_data[i], 337 output_multi_channel_data[i * num_channels_ + j], 338 kErrorMargin) 339 << "Diff in sample " << i << ", channel " << j << "."; 340 EXPECT_EQ(first_channel_sample, 341 output_multi_channel_data[i * num_channels_ + j]); 342 } 343 } 344 } 345 346 int frame_index_; 347 }; 348 349 TEST_P(NetEqStereoTestLosses, RunTest) { 350 RunTest(100); 351 } 352 353 class NetEqStereoTestSingleActiveChannelPlc : public NetEqStereoTestLosses { 354 protected: 355 NetEqStereoTestSingleActiveChannelPlc() : NetEqStereoTestLosses() {} 356 357 void MakeMultiChannelInput() override { 358 // Create a multi-channel input by copying the mono channel from file to the 359 // first channel, and setting the others to zero. 360 memset(input_multi_channel_, 0, 361 frame_size_samples_ * num_channels_ * sizeof(int16_t)); 362 for (size_t i = 0; i < frame_size_samples_; ++i) { 363 input_multi_channel_[i * num_channels_] = input_[i]; 364 } 365 } 366 367 void VerifyOutput(size_t num_samples) override { 368 // Simply verify that all samples in channels other than the first are zero. 369 const int16_t* output_multi_channel_data = output_multi_channel_.data(); 370 for (size_t i = 0; i < num_samples; ++i) { 371 for (size_t j = 1; j < num_channels_; ++j) { 372 EXPECT_EQ(0, output_multi_channel_data[i * num_channels_ + j]) 373 << "Sample " << i << ", channel " << j << " is non-zero."; 374 } 375 } 376 } 377 }; 378 379 TEST_P(NetEqStereoTestSingleActiveChannelPlc, RunTest) { 380 RunTest(100); 381 } 382 383 // Creates a list of parameter sets. 384 std::list<TestParameters> GetTestParameters() { 385 std::list<TestParameters> l; 386 const int sample_rates[] = {8000, 16000, 32000}; 387 const int num_rates = sizeof(sample_rates) / sizeof(sample_rates[0]); 388 // Loop through sample rates. 389 for (int rate_index = 0; rate_index < num_rates; ++rate_index) { 390 int sample_rate = sample_rates[rate_index]; 391 // Loop through all frame sizes between 10 and 60 ms. 392 for (int frame_size = 10; frame_size <= 60; frame_size += 10) { 393 TestParameters p; 394 p.frame_size = frame_size; 395 p.sample_rate = sample_rate; 396 p.num_channels = 2; 397 l.push_back(p); 398 if (sample_rate == 8000) { 399 // Add a five-channel test for 8000 Hz. 400 p.num_channels = 5; 401 l.push_back(p); 402 } 403 } 404 } 405 return l; 406 } 407 408 // Pretty-printing the test parameters in case of an error. 409 void PrintTo(const TestParameters& p, ::std::ostream* os) { 410 *os << "{frame_size = " << p.frame_size 411 << ", num_channels = " << p.num_channels 412 << ", sample_rate = " << p.sample_rate << "}"; 413 } 414 415 // Instantiate the tests. Each test is instantiated using the function above, 416 // so that all different parameter combinations are tested. 417 INSTANTIATE_TEST_SUITE_P(MultiChannel, 418 NetEqStereoTestNoJitter, 419 ::testing::ValuesIn(GetTestParameters())); 420 421 INSTANTIATE_TEST_SUITE_P(MultiChannel, 422 NetEqStereoTestPositiveDrift, 423 ::testing::ValuesIn(GetTestParameters())); 424 425 INSTANTIATE_TEST_SUITE_P(MultiChannel, 426 NetEqStereoTestNegativeDrift, 427 ::testing::ValuesIn(GetTestParameters())); 428 429 INSTANTIATE_TEST_SUITE_P(MultiChannel, 430 NetEqStereoTestDelays, 431 ::testing::ValuesIn(GetTestParameters())); 432 433 INSTANTIATE_TEST_SUITE_P(MultiChannel, 434 NetEqStereoTestLosses, 435 ::testing::ValuesIn(GetTestParameters())); 436 437 INSTANTIATE_TEST_SUITE_P(MultiChannel, 438 NetEqStereoTestSingleActiveChannelPlc, 439 ::testing::ValuesIn(GetTestParameters())); 440 } // namespace webrtc