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 operation for externally created decoders. 12 13#include "testing/gmock/include/gmock/gmock.h" 14#include "webrtc/base/scoped_ptr.h" 15#include "webrtc/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h" 16#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" 17#include "webrtc/modules/audio_coding/neteq/tools/neteq_external_decoder_test.h" 18#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h" 19#include "webrtc/test/testsupport/fileutils.h" 20 21namespace webrtc { 22 23using ::testing::_; 24using ::testing::Return; 25 26class NetEqExternalDecoderUnitTest : public test::NetEqExternalDecoderTest { 27 protected: 28 static const int kFrameSizeMs = 10; // Frame size of Pcm16B. 29 30 NetEqExternalDecoderUnitTest(NetEqDecoder codec, 31 MockExternalPcm16B* decoder) 32 : NetEqExternalDecoderTest(codec, decoder), 33 external_decoder_(decoder), 34 samples_per_ms_(CodecSampleRateHz(codec) / 1000), 35 frame_size_samples_(kFrameSizeMs * samples_per_ms_), 36 rtp_generator_(new test::RtpGenerator(samples_per_ms_)), 37 input_(new int16_t[frame_size_samples_]), 38 // Payload should be no larger than input. 39 encoded_(new uint8_t[2 * frame_size_samples_]), 40 payload_size_bytes_(0), 41 last_send_time_(0), 42 last_arrival_time_(0) { 43 // NetEq is not allowed to delete the external decoder (hence Times(0)). 44 EXPECT_CALL(*external_decoder_, Die()).Times(0); 45 Init(); 46 47 const std::string file_name = 48 webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); 49 input_file_.reset(new test::InputAudioFile(file_name)); 50 } 51 52 virtual ~NetEqExternalDecoderUnitTest() { 53 delete [] input_; 54 delete [] encoded_; 55 // ~NetEqExternalDecoderTest() will delete |external_decoder_|, so expecting 56 // Die() to be called. 57 EXPECT_CALL(*external_decoder_, Die()).Times(1); 58 } 59 60 // Method to draw kFrameSizeMs audio and verify the output. 61 // Use gTest methods. e.g. ASSERT_EQ() inside to trigger errors. 62 virtual void GetAndVerifyOutput() = 0; 63 64 // Method to get the number of calls to the Decode() method of the external 65 // decoder. 66 virtual int NumExpectedDecodeCalls(int num_loops) = 0; 67 68 // Method to generate packets and return the send time of the packet. 69 int GetNewPacket() { 70 if (!input_file_->Read(frame_size_samples_, input_)) { 71 return -1; 72 } 73 payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_, 74 encoded_); 75 76 int next_send_time = rtp_generator_->GetRtpHeader( 77 kPayloadType, frame_size_samples_, &rtp_header_); 78 return next_send_time; 79 } 80 81 // Method to decide packet losses. 82 virtual bool Lost() { return false; } 83 84 // Method to calculate packet arrival time. 85 int GetArrivalTime(int send_time) { 86 int arrival_time = last_arrival_time_ + (send_time - last_send_time_); 87 last_send_time_ = send_time; 88 last_arrival_time_ = arrival_time; 89 return arrival_time; 90 } 91 92 void RunTest(int num_loops) { 93 // Get next input packets (mono and multi-channel). 94 uint32_t next_send_time; 95 uint32_t next_arrival_time; 96 do { 97 next_send_time = GetNewPacket(); 98 next_arrival_time = GetArrivalTime(next_send_time); 99 } while (Lost()); // If lost, immediately read the next packet. 100 101 EXPECT_CALL( 102 *external_decoder_, 103 DecodeInternal(_, payload_size_bytes_, 1000 * samples_per_ms_, _, _)) 104 .Times(NumExpectedDecodeCalls(num_loops)); 105 106 uint32_t time_now = 0; 107 for (int k = 0; k < num_loops; ++k) { 108 while (time_now >= next_arrival_time) { 109 InsertPacket(rtp_header_, rtc::ArrayView<const uint8_t>( 110 encoded_, payload_size_bytes_), 111 next_arrival_time); 112 // Get next input packet. 113 do { 114 next_send_time = GetNewPacket(); 115 next_arrival_time = GetArrivalTime(next_send_time); 116 } while (Lost()); // If lost, immediately read the next packet. 117 } 118 119 std::ostringstream ss; 120 ss << "Lap number " << k << "."; 121 SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. 122 // Compare mono and multi-channel. 123 ASSERT_NO_FATAL_FAILURE(GetAndVerifyOutput()); 124 125 time_now += kOutputLengthMs; 126 } 127 } 128 129 void InsertPacket(WebRtcRTPHeader rtp_header, 130 rtc::ArrayView<const uint8_t> payload, 131 uint32_t receive_timestamp) override { 132 EXPECT_CALL( 133 *external_decoder_, 134 IncomingPacket(_, payload.size(), rtp_header.header.sequenceNumber, 135 rtp_header.header.timestamp, receive_timestamp)); 136 NetEqExternalDecoderTest::InsertPacket(rtp_header, payload, 137 receive_timestamp); 138 } 139 140 MockExternalPcm16B* external_decoder() { return external_decoder_.get(); } 141 142 void ResetRtpGenerator(test::RtpGenerator* rtp_generator) { 143 rtp_generator_.reset(rtp_generator); 144 } 145 146 int samples_per_ms() const { return samples_per_ms_; } 147 private: 148 rtc::scoped_ptr<MockExternalPcm16B> external_decoder_; 149 int samples_per_ms_; 150 size_t frame_size_samples_; 151 rtc::scoped_ptr<test::RtpGenerator> rtp_generator_; 152 int16_t* input_; 153 uint8_t* encoded_; 154 size_t payload_size_bytes_; 155 uint32_t last_send_time_; 156 uint32_t last_arrival_time_; 157 rtc::scoped_ptr<test::InputAudioFile> input_file_; 158 WebRtcRTPHeader rtp_header_; 159}; 160 161// This test encodes a few packets of PCM16b 32 kHz data and inserts it into two 162// different NetEq instances. The first instance uses the internal version of 163// the decoder object, while the second one uses an externally created decoder 164// object (ExternalPcm16B wrapped in MockExternalPcm16B, both defined above). 165// The test verifies that the output from both instances match. 166class NetEqExternalVsInternalDecoderTest : public NetEqExternalDecoderUnitTest, 167 public ::testing::Test { 168 protected: 169 static const size_t kMaxBlockSize = 480; // 10 ms @ 48 kHz. 170 171 NetEqExternalVsInternalDecoderTest() 172 : NetEqExternalDecoderUnitTest(NetEqDecoder::kDecoderPCM16Bswb32kHz, 173 new MockExternalPcm16B), 174 sample_rate_hz_( 175 CodecSampleRateHz(NetEqDecoder::kDecoderPCM16Bswb32kHz)) { 176 NetEq::Config config; 177 config.sample_rate_hz = 178 CodecSampleRateHz(NetEqDecoder::kDecoderPCM16Bswb32kHz); 179 neteq_internal_.reset(NetEq::Create(config)); 180 } 181 182 void SetUp() override { 183 ASSERT_EQ(NetEq::kOK, neteq_internal_->RegisterPayloadType( 184 NetEqDecoder::kDecoderPCM16Bswb32kHz, 185 "pcm16-swb32", kPayloadType)); 186 } 187 188 void GetAndVerifyOutput() override { 189 NetEqOutputType output_type; 190 size_t samples_per_channel; 191 size_t num_channels; 192 // Get audio from internal decoder instance. 193 EXPECT_EQ(NetEq::kOK, 194 neteq_internal_->GetAudio(kMaxBlockSize, 195 output_internal_, 196 &samples_per_channel, 197 &num_channels, 198 &output_type)); 199 EXPECT_EQ(1u, num_channels); 200 EXPECT_EQ(static_cast<size_t>(kOutputLengthMs * sample_rate_hz_ / 1000), 201 samples_per_channel); 202 203 // Get audio from external decoder instance. 204 samples_per_channel = GetOutputAudio(kMaxBlockSize, output_, &output_type); 205 206 for (size_t i = 0; i < samples_per_channel; ++i) { 207 ASSERT_EQ(output_[i], output_internal_[i]) << 208 "Diff in sample " << i << "."; 209 } 210 } 211 212 void InsertPacket(WebRtcRTPHeader rtp_header, 213 rtc::ArrayView<const uint8_t> payload, 214 uint32_t receive_timestamp) override { 215 // Insert packet in internal decoder. 216 ASSERT_EQ(NetEq::kOK, neteq_internal_->InsertPacket(rtp_header, payload, 217 receive_timestamp)); 218 219 // Insert packet in external decoder instance. 220 NetEqExternalDecoderUnitTest::InsertPacket(rtp_header, payload, 221 receive_timestamp); 222 } 223 224 int NumExpectedDecodeCalls(int num_loops) override { return num_loops; } 225 226 private: 227 int sample_rate_hz_; 228 rtc::scoped_ptr<NetEq> neteq_internal_; 229 int16_t output_internal_[kMaxBlockSize]; 230 int16_t output_[kMaxBlockSize]; 231}; 232 233TEST_F(NetEqExternalVsInternalDecoderTest, RunTest) { 234 RunTest(100); // Run 100 laps @ 10 ms each in the test loop. 235} 236 237class LargeTimestampJumpTest : public NetEqExternalDecoderUnitTest, 238 public ::testing::Test { 239 protected: 240 static const size_t kMaxBlockSize = 480; // 10 ms @ 48 kHz. 241 242 enum TestStates { 243 kInitialPhase, 244 kNormalPhase, 245 kExpandPhase, 246 kFadedExpandPhase, 247 kRecovered 248 }; 249 250 LargeTimestampJumpTest() 251 : NetEqExternalDecoderUnitTest(NetEqDecoder::kDecoderPCM16B, 252 new MockExternalPcm16B), 253 test_state_(kInitialPhase) { 254 EXPECT_CALL(*external_decoder(), HasDecodePlc()) 255 .WillRepeatedly(Return(false)); 256 } 257 258 virtual void UpdateState(NetEqOutputType output_type) { 259 switch (test_state_) { 260 case kInitialPhase: { 261 if (output_type == kOutputNormal) { 262 test_state_ = kNormalPhase; 263 } 264 break; 265 } 266 case kNormalPhase: { 267 if (output_type == kOutputPLC) { 268 test_state_ = kExpandPhase; 269 } 270 break; 271 } 272 case kExpandPhase: { 273 if (output_type == kOutputPLCtoCNG) { 274 test_state_ = kFadedExpandPhase; 275 } else if (output_type == kOutputNormal) { 276 test_state_ = kRecovered; 277 } 278 break; 279 } 280 case kFadedExpandPhase: { 281 if (output_type == kOutputNormal) { 282 test_state_ = kRecovered; 283 } 284 break; 285 } 286 case kRecovered: { 287 break; 288 } 289 } 290 } 291 292 void GetAndVerifyOutput() override { 293 size_t num_samples; 294 NetEqOutputType output_type; 295 num_samples = GetOutputAudio(kMaxBlockSize, output_, &output_type); 296 UpdateState(output_type); 297 298 if (test_state_ == kExpandPhase || test_state_ == kFadedExpandPhase) { 299 // Don't verify the output in this phase of the test. 300 return; 301 } 302 303 for (size_t i = 0; i < num_samples; ++i) { 304 if (output_[i] != 0) 305 return; 306 } 307 EXPECT_TRUE(false) 308 << "Expected at least one non-zero sample in each output block."; 309 } 310 311 int NumExpectedDecodeCalls(int num_loops) override { 312 // Some packets at the end of the stream won't be decoded. When the jump in 313 // timestamp happens, NetEq will do Expand during one GetAudio call. In the 314 // next call it will decode the packet after the jump, but the net result is 315 // that the delay increased by 1 packet. In another call, a Pre-emptive 316 // Expand operation is performed, leading to delay increase by 1 packet. In 317 // total, the test will end with a 2-packet delay, which results in the 2 318 // last packets not being decoded. 319 return num_loops - 2; 320 } 321 322 TestStates test_state_; 323 324 private: 325 int16_t output_[kMaxBlockSize]; 326}; 327 328TEST_F(LargeTimestampJumpTest, JumpLongerThanHalfRange) { 329 // Set the timestamp series to start at 2880, increase to 7200, then jump to 330 // 2869342376. The sequence numbers start at 42076 and increase by 1 for each 331 // packet, also when the timestamp jumps. 332 static const uint16_t kStartSeqeunceNumber = 42076; 333 static const uint32_t kStartTimestamp = 2880; 334 static const uint32_t kJumpFromTimestamp = 7200; 335 static const uint32_t kJumpToTimestamp = 2869342376; 336 static_assert(kJumpFromTimestamp < kJumpToTimestamp, 337 "timestamp jump should not result in wrap"); 338 static_assert( 339 static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) > 0x7FFFFFFF, 340 "jump should be larger than half range"); 341 // Replace the default RTP generator with one that jumps in timestamp. 342 ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(), 343 kStartSeqeunceNumber, 344 kStartTimestamp, 345 kJumpFromTimestamp, 346 kJumpToTimestamp)); 347 348 RunTest(130); // Run 130 laps @ 10 ms each in the test loop. 349 EXPECT_EQ(kRecovered, test_state_); 350} 351 352TEST_F(LargeTimestampJumpTest, JumpLongerThanHalfRangeAndWrap) { 353 // Make a jump larger than half the 32-bit timestamp range. Set the start 354 // timestamp such that the jump will result in a wrap around. 355 static const uint16_t kStartSeqeunceNumber = 42076; 356 // Set the jump length slightly larger than 2^31. 357 static const uint32_t kStartTimestamp = 3221223116; 358 static const uint32_t kJumpFromTimestamp = 3221223216; 359 static const uint32_t kJumpToTimestamp = 1073744278; 360 static_assert(kJumpToTimestamp < kJumpFromTimestamp, 361 "timestamp jump should result in wrap"); 362 static_assert( 363 static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) > 0x7FFFFFFF, 364 "jump should be larger than half range"); 365 // Replace the default RTP generator with one that jumps in timestamp. 366 ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(), 367 kStartSeqeunceNumber, 368 kStartTimestamp, 369 kJumpFromTimestamp, 370 kJumpToTimestamp)); 371 372 RunTest(130); // Run 130 laps @ 10 ms each in the test loop. 373 EXPECT_EQ(kRecovered, test_state_); 374} 375 376class ShortTimestampJumpTest : public LargeTimestampJumpTest { 377 protected: 378 void UpdateState(NetEqOutputType output_type) override { 379 switch (test_state_) { 380 case kInitialPhase: { 381 if (output_type == kOutputNormal) { 382 test_state_ = kNormalPhase; 383 } 384 break; 385 } 386 case kNormalPhase: { 387 if (output_type == kOutputPLC) { 388 test_state_ = kExpandPhase; 389 } 390 break; 391 } 392 case kExpandPhase: { 393 if (output_type == kOutputNormal) { 394 test_state_ = kRecovered; 395 } 396 break; 397 } 398 case kRecovered: { 399 break; 400 } 401 default: { FAIL(); } 402 } 403 } 404 405 int NumExpectedDecodeCalls(int num_loops) override { 406 // Some packets won't be decoded because of the timestamp jump. 407 return num_loops - 2; 408 } 409}; 410 411TEST_F(ShortTimestampJumpTest, JumpShorterThanHalfRange) { 412 // Make a jump shorter than half the 32-bit timestamp range. Set the start 413 // timestamp such that the jump will not result in a wrap around. 414 static const uint16_t kStartSeqeunceNumber = 42076; 415 // Set the jump length slightly smaller than 2^31. 416 static const uint32_t kStartTimestamp = 4711; 417 static const uint32_t kJumpFromTimestamp = 4811; 418 static const uint32_t kJumpToTimestamp = 2147483747; 419 static_assert(kJumpFromTimestamp < kJumpToTimestamp, 420 "timestamp jump should not result in wrap"); 421 static_assert( 422 static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) < 0x7FFFFFFF, 423 "jump should be smaller than half range"); 424 // Replace the default RTP generator with one that jumps in timestamp. 425 ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(), 426 kStartSeqeunceNumber, 427 kStartTimestamp, 428 kJumpFromTimestamp, 429 kJumpToTimestamp)); 430 431 RunTest(130); // Run 130 laps @ 10 ms each in the test loop. 432 EXPECT_EQ(kRecovered, test_state_); 433} 434 435TEST_F(ShortTimestampJumpTest, JumpShorterThanHalfRangeAndWrap) { 436 // Make a jump shorter than half the 32-bit timestamp range. Set the start 437 // timestamp such that the jump will result in a wrap around. 438 static const uint16_t kStartSeqeunceNumber = 42076; 439 // Set the jump length slightly smaller than 2^31. 440 static const uint32_t kStartTimestamp = 3221227827; 441 static const uint32_t kJumpFromTimestamp = 3221227927; 442 static const uint32_t kJumpToTimestamp = 1073739567; 443 static_assert(kJumpToTimestamp < kJumpFromTimestamp, 444 "timestamp jump should result in wrap"); 445 static_assert( 446 static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) < 0x7FFFFFFF, 447 "jump should be smaller than half range"); 448 // Replace the default RTP generator with one that jumps in timestamp. 449 ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(), 450 kStartSeqeunceNumber, 451 kStartTimestamp, 452 kJumpFromTimestamp, 453 kJumpToTimestamp)); 454 455 RunTest(130); // Run 130 laps @ 10 ms each in the test loop. 456 EXPECT_EQ(kRecovered, test_state_); 457} 458 459} // namespace webrtc 460