1 /* 2 * Copyright (c) 2014 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 #include <string.h> 12 #include <vector> 13 14 #include "testing/gtest/include/gtest/gtest.h" 15 #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" 16 #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" 17 #include "webrtc/modules/audio_coding/neteq/tools/audio_loop.h" 18 #include "webrtc/modules/interface/module_common_types.h" 19 #include "webrtc/system_wrappers/interface/clock.h" 20 #include "webrtc/system_wrappers/interface/compile_assert.h" 21 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" 22 #include "webrtc/system_wrappers/interface/event_wrapper.h" 23 #include "webrtc/system_wrappers/interface/scoped_ptr.h" 24 #include "webrtc/system_wrappers/interface/sleep.h" 25 #include "webrtc/system_wrappers/interface/thread_annotations.h" 26 #include "webrtc/system_wrappers/interface/thread_wrapper.h" 27 #include "webrtc/test/testsupport/fileutils.h" 28 #include "webrtc/test/testsupport/gtest_disable.h" 29 30 namespace webrtc { 31 32 const int kSampleRateHz = 16000; 33 const int kNumSamples10ms = kSampleRateHz / 100; 34 const int kFrameSizeMs = 10; // Multiple of 10. 35 const int kFrameSizeSamples = kFrameSizeMs / 10 * kNumSamples10ms; 36 const int kPayloadSizeBytes = kFrameSizeSamples * sizeof(int16_t); 37 const uint8_t kPayloadType = 111; 38 39 class RtpUtility { 40 public: 41 RtpUtility(int samples_per_packet, uint8_t payload_type) 42 : samples_per_packet_(samples_per_packet), payload_type_(payload_type) {} 43 44 virtual ~RtpUtility() {} 45 46 void Populate(WebRtcRTPHeader* rtp_header) { 47 rtp_header->header.sequenceNumber = 0xABCD; 48 rtp_header->header.timestamp = 0xABCDEF01; 49 rtp_header->header.payloadType = payload_type_; 50 rtp_header->header.markerBit = false; 51 rtp_header->header.ssrc = 0x1234; 52 rtp_header->header.numCSRCs = 0; 53 rtp_header->frameType = kAudioFrameSpeech; 54 55 rtp_header->header.payload_type_frequency = kSampleRateHz; 56 rtp_header->type.Audio.channel = 1; 57 rtp_header->type.Audio.isCNG = false; 58 } 59 60 void Forward(WebRtcRTPHeader* rtp_header) { 61 ++rtp_header->header.sequenceNumber; 62 rtp_header->header.timestamp += samples_per_packet_; 63 } 64 65 private: 66 int samples_per_packet_; 67 uint8_t payload_type_; 68 }; 69 70 class PacketizationCallbackStub : public AudioPacketizationCallback { 71 public: 72 PacketizationCallbackStub() 73 : num_calls_(0), 74 crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) {} 75 76 virtual int32_t SendData( 77 FrameType frame_type, 78 uint8_t payload_type, 79 uint32_t timestamp, 80 const uint8_t* payload_data, 81 uint16_t payload_len_bytes, 82 const RTPFragmentationHeader* fragmentation) OVERRIDE { 83 CriticalSectionScoped lock(crit_sect_.get()); 84 ++num_calls_; 85 last_payload_vec_.assign(payload_data, payload_data + payload_len_bytes); 86 return 0; 87 } 88 89 int num_calls() const { 90 CriticalSectionScoped lock(crit_sect_.get()); 91 return num_calls_; 92 } 93 94 int last_payload_len_bytes() const { 95 CriticalSectionScoped lock(crit_sect_.get()); 96 return last_payload_vec_.size(); 97 } 98 99 void SwapBuffers(std::vector<uint8_t>* payload) { 100 CriticalSectionScoped lock(crit_sect_.get()); 101 last_payload_vec_.swap(*payload); 102 } 103 104 private: 105 int num_calls_ GUARDED_BY(crit_sect_); 106 std::vector<uint8_t> last_payload_vec_ GUARDED_BY(crit_sect_); 107 const scoped_ptr<CriticalSectionWrapper> crit_sect_; 108 }; 109 110 class AudioCodingModuleTest : public ::testing::Test { 111 protected: 112 AudioCodingModuleTest() 113 : id_(1), 114 rtp_utility_(new RtpUtility(kFrameSizeSamples, kPayloadType)), 115 clock_(Clock::GetRealTimeClock()) {} 116 117 ~AudioCodingModuleTest() {} 118 119 void TearDown() {} 120 121 void SetUp() { 122 acm_.reset(AudioCodingModule::Create(id_, clock_)); 123 124 RegisterCodec(); 125 126 rtp_utility_->Populate(&rtp_header_); 127 128 input_frame_.sample_rate_hz_ = kSampleRateHz; 129 input_frame_.num_channels_ = 1; 130 input_frame_.samples_per_channel_ = kSampleRateHz * 10 / 1000; // 10 ms. 131 COMPILE_ASSERT(kSampleRateHz * 10 / 1000 <= AudioFrame::kMaxDataSizeSamples, 132 audio_frame_too_small); 133 memset(input_frame_.data_, 134 0, 135 input_frame_.samples_per_channel_ * sizeof(input_frame_.data_[0])); 136 137 ASSERT_EQ(0, acm_->RegisterTransportCallback(&packet_cb_)); 138 } 139 140 virtual void RegisterCodec() { 141 AudioCodingModule::Codec("L16", &codec_, kSampleRateHz, 1); 142 codec_.pltype = kPayloadType; 143 144 // Register L16 codec in ACM. 145 ASSERT_EQ(0, acm_->RegisterReceiveCodec(codec_)); 146 ASSERT_EQ(0, acm_->RegisterSendCodec(codec_)); 147 } 148 149 virtual void InsertPacketAndPullAudio() { 150 InsertPacket(); 151 PullAudio(); 152 } 153 154 virtual void InsertPacket() { 155 const uint8_t kPayload[kPayloadSizeBytes] = {0}; 156 ASSERT_EQ(0, 157 acm_->IncomingPacket(kPayload, kPayloadSizeBytes, rtp_header_)); 158 rtp_utility_->Forward(&rtp_header_); 159 } 160 161 virtual void PullAudio() { 162 AudioFrame audio_frame; 163 ASSERT_EQ(0, acm_->PlayoutData10Ms(-1, &audio_frame)); 164 } 165 166 virtual void InsertAudio() { 167 ASSERT_EQ(0, acm_->Add10MsData(input_frame_)); 168 input_frame_.timestamp_ += kNumSamples10ms; 169 } 170 171 virtual void Encode() { 172 int32_t encoded_bytes = acm_->Process(); 173 // Expect to get one packet with two bytes per sample, or no packet at all, 174 // depending on how many 10 ms blocks go into |codec_.pacsize|. 175 EXPECT_TRUE(encoded_bytes == 2 * codec_.pacsize || encoded_bytes == 0); 176 } 177 178 const int id_; 179 scoped_ptr<RtpUtility> rtp_utility_; 180 scoped_ptr<AudioCodingModule> acm_; 181 PacketizationCallbackStub packet_cb_; 182 WebRtcRTPHeader rtp_header_; 183 AudioFrame input_frame_; 184 CodecInst codec_; 185 Clock* clock_; 186 }; 187 188 // Check if the statistics are initialized correctly. Before any call to ACM 189 // all fields have to be zero. 190 TEST_F(AudioCodingModuleTest, DISABLED_ON_ANDROID(InitializedToZero)) { 191 AudioDecodingCallStats stats; 192 acm_->GetDecodingCallStatistics(&stats); 193 EXPECT_EQ(0, stats.calls_to_neteq); 194 EXPECT_EQ(0, stats.calls_to_silence_generator); 195 EXPECT_EQ(0, stats.decoded_normal); 196 EXPECT_EQ(0, stats.decoded_cng); 197 EXPECT_EQ(0, stats.decoded_plc); 198 EXPECT_EQ(0, stats.decoded_plc_cng); 199 } 200 201 // Apply an initial playout delay. Calls to AudioCodingModule::PlayoutData10ms() 202 // should result in generating silence, check the associated field. 203 TEST_F(AudioCodingModuleTest, DISABLED_ON_ANDROID(SilenceGeneratorCalled)) { 204 AudioDecodingCallStats stats; 205 const int kInitialDelay = 100; 206 207 acm_->SetInitialPlayoutDelay(kInitialDelay); 208 209 int num_calls = 0; 210 for (int time_ms = 0; time_ms < kInitialDelay; 211 time_ms += kFrameSizeMs, ++num_calls) { 212 InsertPacketAndPullAudio(); 213 } 214 acm_->GetDecodingCallStatistics(&stats); 215 EXPECT_EQ(0, stats.calls_to_neteq); 216 EXPECT_EQ(num_calls, stats.calls_to_silence_generator); 217 EXPECT_EQ(0, stats.decoded_normal); 218 EXPECT_EQ(0, stats.decoded_cng); 219 EXPECT_EQ(0, stats.decoded_plc); 220 EXPECT_EQ(0, stats.decoded_plc_cng); 221 } 222 223 // Insert some packets and pull audio. Check statistics are valid. Then, 224 // simulate packet loss and check if PLC and PLC-to-CNG statistics are 225 // correctly updated. 226 TEST_F(AudioCodingModuleTest, DISABLED_ON_ANDROID(NetEqCalls)) { 227 AudioDecodingCallStats stats; 228 const int kNumNormalCalls = 10; 229 230 for (int num_calls = 0; num_calls < kNumNormalCalls; ++num_calls) { 231 InsertPacketAndPullAudio(); 232 } 233 acm_->GetDecodingCallStatistics(&stats); 234 EXPECT_EQ(kNumNormalCalls, stats.calls_to_neteq); 235 EXPECT_EQ(0, stats.calls_to_silence_generator); 236 EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); 237 EXPECT_EQ(0, stats.decoded_cng); 238 EXPECT_EQ(0, stats.decoded_plc); 239 EXPECT_EQ(0, stats.decoded_plc_cng); 240 241 const int kNumPlc = 3; 242 const int kNumPlcCng = 5; 243 244 // Simulate packet-loss. NetEq first performs PLC then PLC fades to CNG. 245 for (int n = 0; n < kNumPlc + kNumPlcCng; ++n) { 246 PullAudio(); 247 } 248 acm_->GetDecodingCallStatistics(&stats); 249 EXPECT_EQ(kNumNormalCalls + kNumPlc + kNumPlcCng, stats.calls_to_neteq); 250 EXPECT_EQ(0, stats.calls_to_silence_generator); 251 EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); 252 EXPECT_EQ(0, stats.decoded_cng); 253 EXPECT_EQ(kNumPlc, stats.decoded_plc); 254 EXPECT_EQ(kNumPlcCng, stats.decoded_plc_cng); 255 } 256 257 TEST_F(AudioCodingModuleTest, VerifyOutputFrame) { 258 AudioFrame audio_frame; 259 const int kSampleRateHz = 32000; 260 EXPECT_EQ(0, acm_->PlayoutData10Ms(kSampleRateHz, &audio_frame)); 261 EXPECT_EQ(id_, audio_frame.id_); 262 EXPECT_EQ(0u, audio_frame.timestamp_); 263 EXPECT_GT(audio_frame.num_channels_, 0); 264 EXPECT_EQ(kSampleRateHz / 100, audio_frame.samples_per_channel_); 265 EXPECT_EQ(kSampleRateHz, audio_frame.sample_rate_hz_); 266 } 267 268 TEST_F(AudioCodingModuleTest, FailOnZeroDesiredFrequency) { 269 AudioFrame audio_frame; 270 EXPECT_EQ(-1, acm_->PlayoutData10Ms(0, &audio_frame)); 271 } 272 273 // A multi-threaded test for ACM. This base class is using the PCM16b 16 kHz 274 // codec, while the derive class AcmIsacMtTest is using iSAC. 275 class AudioCodingModuleMtTest : public AudioCodingModuleTest { 276 protected: 277 static const int kNumPackets = 500; 278 static const int kNumPullCalls = 500; 279 280 AudioCodingModuleMtTest() 281 : AudioCodingModuleTest(), 282 send_thread_(ThreadWrapper::CreateThread(CbSendThread, 283 this, 284 kRealtimePriority, 285 "send")), 286 insert_packet_thread_(ThreadWrapper::CreateThread(CbInsertPacketThread, 287 this, 288 kRealtimePriority, 289 "insert_packet")), 290 pull_audio_thread_(ThreadWrapper::CreateThread(CbPullAudioThread, 291 this, 292 kRealtimePriority, 293 "pull_audio")), 294 test_complete_(EventWrapper::Create()), 295 send_count_(0), 296 insert_packet_count_(0), 297 pull_audio_count_(0), 298 crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), 299 next_insert_packet_time_ms_(0), 300 fake_clock_(new SimulatedClock(0)) { 301 clock_ = fake_clock_.get(); 302 } 303 304 void SetUp() { 305 AudioCodingModuleTest::SetUp(); 306 StartThreads(); 307 } 308 309 void StartThreads() { 310 unsigned int thread_id = 0; 311 ASSERT_TRUE(send_thread_->Start(thread_id)); 312 ASSERT_TRUE(insert_packet_thread_->Start(thread_id)); 313 ASSERT_TRUE(pull_audio_thread_->Start(thread_id)); 314 } 315 316 void TearDown() { 317 AudioCodingModuleTest::TearDown(); 318 pull_audio_thread_->Stop(); 319 send_thread_->Stop(); 320 insert_packet_thread_->Stop(); 321 } 322 323 EventTypeWrapper RunTest() { 324 return test_complete_->Wait(10 * 60 * 1000); // 10 minutes' timeout. 325 } 326 327 virtual bool TestDone() { 328 if (packet_cb_.num_calls() > kNumPackets) { 329 CriticalSectionScoped lock(crit_sect_.get()); 330 if (pull_audio_count_ > kNumPullCalls) { 331 // Both conditions for completion are met. End the test. 332 return true; 333 } 334 } 335 return false; 336 } 337 338 static bool CbSendThread(void* context) { 339 return reinterpret_cast<AudioCodingModuleMtTest*>(context)->CbSendImpl(); 340 } 341 342 // The send thread doesn't have to care about the current simulated time, 343 // since only the AcmReceiver is using the clock. 344 bool CbSendImpl() { 345 SleepMs(1); 346 if (HasFatalFailure()) { 347 // End the test early if a fatal failure (ASSERT_*) has occurred. 348 test_complete_->Set(); 349 } 350 ++send_count_; 351 InsertAudio(); 352 Encode(); 353 if (TestDone()) { 354 test_complete_->Set(); 355 } 356 return true; 357 } 358 359 static bool CbInsertPacketThread(void* context) { 360 return reinterpret_cast<AudioCodingModuleMtTest*>(context) 361 ->CbInsertPacketImpl(); 362 } 363 364 bool CbInsertPacketImpl() { 365 SleepMs(1); 366 { 367 CriticalSectionScoped lock(crit_sect_.get()); 368 if (clock_->TimeInMilliseconds() < next_insert_packet_time_ms_) { 369 return true; 370 } 371 next_insert_packet_time_ms_ += 10; 372 } 373 // Now we're not holding the crit sect when calling ACM. 374 ++insert_packet_count_; 375 InsertPacket(); 376 return true; 377 } 378 379 static bool CbPullAudioThread(void* context) { 380 return reinterpret_cast<AudioCodingModuleMtTest*>(context) 381 ->CbPullAudioImpl(); 382 } 383 384 bool CbPullAudioImpl() { 385 SleepMs(1); 386 { 387 CriticalSectionScoped lock(crit_sect_.get()); 388 // Don't let the insert thread fall behind. 389 if (next_insert_packet_time_ms_ < clock_->TimeInMilliseconds()) { 390 return true; 391 } 392 ++pull_audio_count_; 393 } 394 // Now we're not holding the crit sect when calling ACM. 395 PullAudio(); 396 fake_clock_->AdvanceTimeMilliseconds(10); 397 return true; 398 } 399 400 scoped_ptr<ThreadWrapper> send_thread_; 401 scoped_ptr<ThreadWrapper> insert_packet_thread_; 402 scoped_ptr<ThreadWrapper> pull_audio_thread_; 403 const scoped_ptr<EventWrapper> test_complete_; 404 int send_count_; 405 int insert_packet_count_; 406 int pull_audio_count_ GUARDED_BY(crit_sect_); 407 const scoped_ptr<CriticalSectionWrapper> crit_sect_; 408 int64_t next_insert_packet_time_ms_ GUARDED_BY(crit_sect_); 409 scoped_ptr<SimulatedClock> fake_clock_; 410 }; 411 412 TEST_F(AudioCodingModuleMtTest, DoTest) { 413 EXPECT_EQ(kEventSignaled, RunTest()); 414 } 415 416 // This is a multi-threaded ACM test using iSAC. The test encodes audio 417 // from a PCM file. The most recent encoded frame is used as input to the 418 // receiving part. Depending on timing, it may happen that the same RTP packet 419 // is inserted into the receiver multiple times, but this is a valid use-case, 420 // and simplifies the test code a lot. 421 class AcmIsacMtTest : public AudioCodingModuleMtTest { 422 protected: 423 static const int kNumPackets = 500; 424 static const int kNumPullCalls = 500; 425 426 AcmIsacMtTest() 427 : AudioCodingModuleMtTest(), 428 last_packet_number_(0) {} 429 430 ~AcmIsacMtTest() {} 431 432 void SetUp() { 433 AudioCodingModuleTest::SetUp(); 434 435 // Set up input audio source to read from specified file, loop after 5 436 // seconds, and deliver blocks of 10 ms. 437 const std::string input_file_name = 438 webrtc::test::ResourcePath("audio_coding/speech_mono_16kHz", "pcm"); 439 audio_loop_.Init(input_file_name, 5 * kSampleRateHz, kNumSamples10ms); 440 441 // Generate one packet to have something to insert. 442 int loop_counter = 0; 443 while (packet_cb_.last_payload_len_bytes() == 0) { 444 InsertAudio(); 445 Encode(); 446 ASSERT_LT(loop_counter++, 10); 447 } 448 // Set |last_packet_number_| to one less that |num_calls| so that the packet 449 // will be fetched in the next InsertPacket() call. 450 last_packet_number_ = packet_cb_.num_calls() - 1; 451 452 StartThreads(); 453 } 454 455 virtual void RegisterCodec() { 456 COMPILE_ASSERT(kSampleRateHz == 16000, test_designed_for_isac_16khz); 457 AudioCodingModule::Codec("ISAC", &codec_, kSampleRateHz, 1); 458 codec_.pltype = kPayloadType; 459 460 // Register iSAC codec in ACM, effectively unregistering the PCM16B codec 461 // registered in AudioCodingModuleTest::SetUp(); 462 ASSERT_EQ(0, acm_->RegisterReceiveCodec(codec_)); 463 ASSERT_EQ(0, acm_->RegisterSendCodec(codec_)); 464 } 465 466 void InsertPacket() { 467 int num_calls = packet_cb_.num_calls(); // Store locally for thread safety. 468 if (num_calls > last_packet_number_) { 469 // Get the new payload out from the callback handler. 470 // Note that since we swap buffers here instead of directly inserting 471 // a pointer to the data in |packet_cb_|, we avoid locking the callback 472 // for the duration of the IncomingPacket() call. 473 packet_cb_.SwapBuffers(&last_payload_vec_); 474 ASSERT_GT(last_payload_vec_.size(), 0u); 475 rtp_utility_->Forward(&rtp_header_); 476 last_packet_number_ = num_calls; 477 } 478 ASSERT_GT(last_payload_vec_.size(), 0u); 479 ASSERT_EQ( 480 0, 481 acm_->IncomingPacket( 482 &last_payload_vec_[0], last_payload_vec_.size(), rtp_header_)); 483 } 484 485 void InsertAudio() { 486 memcpy(input_frame_.data_, audio_loop_.GetNextBlock(), kNumSamples10ms); 487 AudioCodingModuleTest::InsertAudio(); 488 } 489 490 void Encode() { ASSERT_GE(acm_->Process(), 0); } 491 492 // This method is the same as AudioCodingModuleMtTest::TestDone(), but here 493 // it is using the constants defined in this class (i.e., shorter test run). 494 virtual bool TestDone() { 495 if (packet_cb_.num_calls() > kNumPackets) { 496 CriticalSectionScoped lock(crit_sect_.get()); 497 if (pull_audio_count_ > kNumPullCalls) { 498 // Both conditions for completion are met. End the test. 499 return true; 500 } 501 } 502 return false; 503 } 504 505 int last_packet_number_; 506 std::vector<uint8_t> last_payload_vec_; 507 test::AudioLoop audio_loop_; 508 }; 509 510 TEST_F(AcmIsacMtTest, DoTest) { 511 EXPECT_EQ(kEventSignaled, RunTest()); 512 } 513 514 } // namespace webrtc 515