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/base/checks.h" 16#include "webrtc/base/md5digest.h" 17#include "webrtc/base/thread_annotations.h" 18#include "webrtc/modules/audio_coding/main/acm2/acm_receive_test.h" 19#include "webrtc/modules/audio_coding/main/acm2/acm_send_test.h" 20#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" 21#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" 22#include "webrtc/modules/audio_coding/neteq/tools/audio_checksum.h" 23#include "webrtc/modules/audio_coding/neteq/tools/audio_loop.h" 24#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" 25#include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" 26#include "webrtc/modules/audio_coding/neteq/tools/packet.h" 27#include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" 28#include "webrtc/modules/interface/module_common_types.h" 29#include "webrtc/system_wrappers/interface/clock.h" 30#include "webrtc/system_wrappers/interface/compile_assert.h" 31#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" 32#include "webrtc/system_wrappers/interface/event_wrapper.h" 33#include "webrtc/system_wrappers/interface/scoped_ptr.h" 34#include "webrtc/system_wrappers/interface/sleep.h" 35#include "webrtc/system_wrappers/interface/thread_wrapper.h" 36#include "webrtc/test/testsupport/fileutils.h" 37#include "webrtc/test/testsupport/gtest_disable.h" 38 39namespace webrtc { 40 41const int kSampleRateHz = 16000; 42const int kNumSamples10ms = kSampleRateHz / 100; 43const int kFrameSizeMs = 10; // Multiple of 10. 44const int kFrameSizeSamples = kFrameSizeMs / 10 * kNumSamples10ms; 45const int kPayloadSizeBytes = kFrameSizeSamples * sizeof(int16_t); 46const uint8_t kPayloadType = 111; 47 48class RtpUtility { 49 public: 50 RtpUtility(int samples_per_packet, uint8_t payload_type) 51 : samples_per_packet_(samples_per_packet), payload_type_(payload_type) {} 52 53 virtual ~RtpUtility() {} 54 55 void Populate(WebRtcRTPHeader* rtp_header) { 56 rtp_header->header.sequenceNumber = 0xABCD; 57 rtp_header->header.timestamp = 0xABCDEF01; 58 rtp_header->header.payloadType = payload_type_; 59 rtp_header->header.markerBit = false; 60 rtp_header->header.ssrc = 0x1234; 61 rtp_header->header.numCSRCs = 0; 62 rtp_header->frameType = kAudioFrameSpeech; 63 64 rtp_header->header.payload_type_frequency = kSampleRateHz; 65 rtp_header->type.Audio.channel = 1; 66 rtp_header->type.Audio.isCNG = false; 67 } 68 69 void Forward(WebRtcRTPHeader* rtp_header) { 70 ++rtp_header->header.sequenceNumber; 71 rtp_header->header.timestamp += samples_per_packet_; 72 } 73 74 private: 75 int samples_per_packet_; 76 uint8_t payload_type_; 77}; 78 79class PacketizationCallbackStub : public AudioPacketizationCallback { 80 public: 81 PacketizationCallbackStub() 82 : num_calls_(0), 83 crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) {} 84 85 virtual int32_t SendData( 86 FrameType frame_type, 87 uint8_t payload_type, 88 uint32_t timestamp, 89 const uint8_t* payload_data, 90 uint16_t payload_len_bytes, 91 const RTPFragmentationHeader* fragmentation) OVERRIDE { 92 CriticalSectionScoped lock(crit_sect_.get()); 93 ++num_calls_; 94 last_payload_vec_.assign(payload_data, payload_data + payload_len_bytes); 95 return 0; 96 } 97 98 int num_calls() const { 99 CriticalSectionScoped lock(crit_sect_.get()); 100 return num_calls_; 101 } 102 103 int last_payload_len_bytes() const { 104 CriticalSectionScoped lock(crit_sect_.get()); 105 return last_payload_vec_.size(); 106 } 107 108 void SwapBuffers(std::vector<uint8_t>* payload) { 109 CriticalSectionScoped lock(crit_sect_.get()); 110 last_payload_vec_.swap(*payload); 111 } 112 113 private: 114 int num_calls_ GUARDED_BY(crit_sect_); 115 std::vector<uint8_t> last_payload_vec_ GUARDED_BY(crit_sect_); 116 const scoped_ptr<CriticalSectionWrapper> crit_sect_; 117}; 118 119class AudioCodingModuleTest : public ::testing::Test { 120 protected: 121 AudioCodingModuleTest() 122 : rtp_utility_(new RtpUtility(kFrameSizeSamples, kPayloadType)) { 123 config_.transport = &packet_cb_; 124 } 125 126 ~AudioCodingModuleTest() {} 127 128 void TearDown() OVERRIDE {} 129 130 void SetUp() OVERRIDE { 131 rtp_utility_->Populate(&rtp_header_); 132 133 input_frame_.sample_rate_hz_ = kSampleRateHz; 134 input_frame_.num_channels_ = 1; 135 input_frame_.samples_per_channel_ = kSampleRateHz * 10 / 1000; // 10 ms. 136 COMPILE_ASSERT(kSampleRateHz * 10 / 1000 <= AudioFrame::kMaxDataSizeSamples, 137 audio_frame_too_small); 138 memset(input_frame_.data_, 139 0, 140 input_frame_.samples_per_channel_ * sizeof(input_frame_.data_[0])); 141 } 142 143 void CreateAcm() { 144 acm_.reset(AudioCoding::Create(config_)); 145 ASSERT_TRUE(acm_.get() != NULL); 146 RegisterCodec(); 147 } 148 149 virtual void RegisterCodec() { 150 // Register L16 codec in ACM. 151 int codec_type = acm2::ACMCodecDB::kNone; 152 switch (kSampleRateHz) { 153 case 8000: 154 codec_type = acm2::ACMCodecDB::kPCM16B; 155 break; 156 case 16000: 157 codec_type = acm2::ACMCodecDB::kPCM16Bwb; 158 break; 159 case 32000: 160 codec_type = acm2::ACMCodecDB::kPCM16Bswb32kHz; 161 break; 162 default: 163 FATAL() << "Sample rate not supported in this test."; 164 } 165 ASSERT_TRUE(acm_->RegisterSendCodec(codec_type, kPayloadType)); 166 ASSERT_TRUE(acm_->RegisterReceiveCodec(codec_type, kPayloadType)); 167 } 168 169 virtual void InsertPacketAndPullAudio() { 170 InsertPacket(); 171 PullAudio(); 172 } 173 174 virtual void InsertPacket() { 175 const uint8_t kPayload[kPayloadSizeBytes] = {0}; 176 ASSERT_TRUE(acm_->InsertPacket(kPayload, kPayloadSizeBytes, rtp_header_)); 177 rtp_utility_->Forward(&rtp_header_); 178 } 179 180 virtual void PullAudio() { 181 AudioFrame audio_frame; 182 ASSERT_TRUE(acm_->Get10MsAudio(&audio_frame)); 183 } 184 185 virtual void InsertAudio() { 186 int encoded_bytes = acm_->Add10MsAudio(input_frame_); 187 ASSERT_GE(encoded_bytes, 0); 188 input_frame_.timestamp_ += kNumSamples10ms; 189 } 190 191 AudioCoding::Config config_; 192 scoped_ptr<RtpUtility> rtp_utility_; 193 scoped_ptr<AudioCoding> acm_; 194 PacketizationCallbackStub packet_cb_; 195 WebRtcRTPHeader rtp_header_; 196 AudioFrame input_frame_; 197}; 198 199// Check if the statistics are initialized correctly. Before any call to ACM 200// all fields have to be zero. 201TEST_F(AudioCodingModuleTest, DISABLED_ON_ANDROID(InitializedToZero)) { 202 CreateAcm(); 203 AudioDecodingCallStats stats; 204 acm_->GetDecodingCallStatistics(&stats); 205 EXPECT_EQ(0, stats.calls_to_neteq); 206 EXPECT_EQ(0, stats.calls_to_silence_generator); 207 EXPECT_EQ(0, stats.decoded_normal); 208 EXPECT_EQ(0, stats.decoded_cng); 209 EXPECT_EQ(0, stats.decoded_plc); 210 EXPECT_EQ(0, stats.decoded_plc_cng); 211} 212 213// Apply an initial playout delay. Calls to AudioCodingModule::PlayoutData10ms() 214// should result in generating silence, check the associated field. 215TEST_F(AudioCodingModuleTest, DISABLED_ON_ANDROID(SilenceGeneratorCalled)) { 216 const int kInitialDelay = 100; 217 config_.initial_playout_delay_ms = kInitialDelay; 218 CreateAcm(); 219 AudioDecodingCallStats stats; 220 221 int num_calls = 0; 222 for (int time_ms = 0; time_ms < kInitialDelay; 223 time_ms += kFrameSizeMs, ++num_calls) { 224 InsertPacketAndPullAudio(); 225 } 226 acm_->GetDecodingCallStatistics(&stats); 227 EXPECT_EQ(0, stats.calls_to_neteq); 228 EXPECT_EQ(num_calls, stats.calls_to_silence_generator); 229 EXPECT_EQ(0, stats.decoded_normal); 230 EXPECT_EQ(0, stats.decoded_cng); 231 EXPECT_EQ(0, stats.decoded_plc); 232 EXPECT_EQ(0, stats.decoded_plc_cng); 233} 234 235// Insert some packets and pull audio. Check statistics are valid. Then, 236// simulate packet loss and check if PLC and PLC-to-CNG statistics are 237// correctly updated. 238TEST_F(AudioCodingModuleTest, DISABLED_ON_ANDROID(NetEqCalls)) { 239 CreateAcm(); 240 AudioDecodingCallStats stats; 241 const int kNumNormalCalls = 10; 242 243 for (int num_calls = 0; num_calls < kNumNormalCalls; ++num_calls) { 244 InsertPacketAndPullAudio(); 245 } 246 acm_->GetDecodingCallStatistics(&stats); 247 EXPECT_EQ(kNumNormalCalls, stats.calls_to_neteq); 248 EXPECT_EQ(0, stats.calls_to_silence_generator); 249 EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); 250 EXPECT_EQ(0, stats.decoded_cng); 251 EXPECT_EQ(0, stats.decoded_plc); 252 EXPECT_EQ(0, stats.decoded_plc_cng); 253 254 const int kNumPlc = 3; 255 const int kNumPlcCng = 5; 256 257 // Simulate packet-loss. NetEq first performs PLC then PLC fades to CNG. 258 for (int n = 0; n < kNumPlc + kNumPlcCng; ++n) { 259 PullAudio(); 260 } 261 acm_->GetDecodingCallStatistics(&stats); 262 EXPECT_EQ(kNumNormalCalls + kNumPlc + kNumPlcCng, stats.calls_to_neteq); 263 EXPECT_EQ(0, stats.calls_to_silence_generator); 264 EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); 265 EXPECT_EQ(0, stats.decoded_cng); 266 EXPECT_EQ(kNumPlc, stats.decoded_plc); 267 EXPECT_EQ(kNumPlcCng, stats.decoded_plc_cng); 268} 269 270TEST_F(AudioCodingModuleTest, VerifyOutputFrame) { 271 CreateAcm(); 272 AudioFrame audio_frame; 273 const int kSampleRateHz = 32000; 274 EXPECT_TRUE(acm_->Get10MsAudio(&audio_frame)); 275 EXPECT_EQ(0u, audio_frame.timestamp_); 276 EXPECT_GT(audio_frame.num_channels_, 0); 277 EXPECT_EQ(kSampleRateHz / 100, audio_frame.samples_per_channel_); 278 EXPECT_EQ(kSampleRateHz, audio_frame.sample_rate_hz_); 279} 280 281// A multi-threaded test for ACM. This base class is using the PCM16b 16 kHz 282// codec, while the derive class AcmIsacMtTest is using iSAC. 283class AudioCodingModuleMtTest : public AudioCodingModuleTest { 284 protected: 285 static const int kNumPackets = 500; 286 static const int kNumPullCalls = 500; 287 288 AudioCodingModuleMtTest() 289 : AudioCodingModuleTest(), 290 send_thread_(ThreadWrapper::CreateThread(CbSendThread, 291 this, 292 kRealtimePriority, 293 "send")), 294 insert_packet_thread_(ThreadWrapper::CreateThread(CbInsertPacketThread, 295 this, 296 kRealtimePriority, 297 "insert_packet")), 298 pull_audio_thread_(ThreadWrapper::CreateThread(CbPullAudioThread, 299 this, 300 kRealtimePriority, 301 "pull_audio")), 302 test_complete_(EventWrapper::Create()), 303 send_count_(0), 304 insert_packet_count_(0), 305 pull_audio_count_(0), 306 crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), 307 next_insert_packet_time_ms_(0), 308 fake_clock_(new SimulatedClock(0)) { 309 config_.clock = fake_clock_.get(); 310 } 311 312 virtual void SetUp() OVERRIDE { 313 AudioCodingModuleTest::SetUp(); 314 CreateAcm(); 315 StartThreads(); 316 } 317 318 void StartThreads() { 319 unsigned int thread_id = 0; 320 ASSERT_TRUE(send_thread_->Start(thread_id)); 321 ASSERT_TRUE(insert_packet_thread_->Start(thread_id)); 322 ASSERT_TRUE(pull_audio_thread_->Start(thread_id)); 323 } 324 325 virtual void TearDown() OVERRIDE { 326 AudioCodingModuleTest::TearDown(); 327 pull_audio_thread_->Stop(); 328 send_thread_->Stop(); 329 insert_packet_thread_->Stop(); 330 } 331 332 EventTypeWrapper RunTest() { 333 return test_complete_->Wait(10 * 60 * 1000); // 10 minutes' timeout. 334 } 335 336 virtual bool TestDone() { 337 if (packet_cb_.num_calls() > kNumPackets) { 338 CriticalSectionScoped lock(crit_sect_.get()); 339 if (pull_audio_count_ > kNumPullCalls) { 340 // Both conditions for completion are met. End the test. 341 return true; 342 } 343 } 344 return false; 345 } 346 347 static bool CbSendThread(void* context) { 348 return reinterpret_cast<AudioCodingModuleMtTest*>(context)->CbSendImpl(); 349 } 350 351 // The send thread doesn't have to care about the current simulated time, 352 // since only the AcmReceiver is using the clock. 353 bool CbSendImpl() { 354 SleepMs(1); 355 if (HasFatalFailure()) { 356 // End the test early if a fatal failure (ASSERT_*) has occurred. 357 test_complete_->Set(); 358 } 359 ++send_count_; 360 InsertAudio(); 361 if (TestDone()) { 362 test_complete_->Set(); 363 } 364 return true; 365 } 366 367 static bool CbInsertPacketThread(void* context) { 368 return reinterpret_cast<AudioCodingModuleMtTest*>(context) 369 ->CbInsertPacketImpl(); 370 } 371 372 bool CbInsertPacketImpl() { 373 SleepMs(1); 374 { 375 CriticalSectionScoped lock(crit_sect_.get()); 376 if (fake_clock_->TimeInMilliseconds() < next_insert_packet_time_ms_) { 377 return true; 378 } 379 next_insert_packet_time_ms_ += 10; 380 } 381 // Now we're not holding the crit sect when calling ACM. 382 ++insert_packet_count_; 383 InsertPacket(); 384 return true; 385 } 386 387 static bool CbPullAudioThread(void* context) { 388 return reinterpret_cast<AudioCodingModuleMtTest*>(context) 389 ->CbPullAudioImpl(); 390 } 391 392 bool CbPullAudioImpl() { 393 SleepMs(1); 394 { 395 CriticalSectionScoped lock(crit_sect_.get()); 396 // Don't let the insert thread fall behind. 397 if (next_insert_packet_time_ms_ < fake_clock_->TimeInMilliseconds()) { 398 return true; 399 } 400 ++pull_audio_count_; 401 } 402 // Now we're not holding the crit sect when calling ACM. 403 PullAudio(); 404 fake_clock_->AdvanceTimeMilliseconds(10); 405 return true; 406 } 407 408 scoped_ptr<ThreadWrapper> send_thread_; 409 scoped_ptr<ThreadWrapper> insert_packet_thread_; 410 scoped_ptr<ThreadWrapper> pull_audio_thread_; 411 const scoped_ptr<EventWrapper> test_complete_; 412 int send_count_; 413 int insert_packet_count_; 414 int pull_audio_count_ GUARDED_BY(crit_sect_); 415 const scoped_ptr<CriticalSectionWrapper> crit_sect_; 416 int64_t next_insert_packet_time_ms_ GUARDED_BY(crit_sect_); 417 scoped_ptr<SimulatedClock> fake_clock_; 418}; 419 420TEST_F(AudioCodingModuleMtTest, DoTest) { 421 EXPECT_EQ(kEventSignaled, RunTest()); 422} 423 424// This is a multi-threaded ACM test using iSAC. The test encodes audio 425// from a PCM file. The most recent encoded frame is used as input to the 426// receiving part. Depending on timing, it may happen that the same RTP packet 427// is inserted into the receiver multiple times, but this is a valid use-case, 428// and simplifies the test code a lot. 429class AcmIsacMtTest : public AudioCodingModuleMtTest { 430 protected: 431 static const int kNumPackets = 500; 432 static const int kNumPullCalls = 500; 433 434 AcmIsacMtTest() 435 : AudioCodingModuleMtTest(), 436 last_packet_number_(0) {} 437 438 ~AcmIsacMtTest() {} 439 440 virtual void SetUp() OVERRIDE { 441 AudioCodingModuleTest::SetUp(); 442 CreateAcm(); 443 444 // Set up input audio source to read from specified file, loop after 5 445 // seconds, and deliver blocks of 10 ms. 446 const std::string input_file_name = 447 webrtc::test::ResourcePath("audio_coding/speech_mono_16kHz", "pcm"); 448 audio_loop_.Init(input_file_name, 5 * kSampleRateHz, kNumSamples10ms); 449 450 // Generate one packet to have something to insert. 451 int loop_counter = 0; 452 while (packet_cb_.last_payload_len_bytes() == 0) { 453 InsertAudio(); 454 ASSERT_LT(loop_counter++, 10); 455 } 456 // Set |last_packet_number_| to one less that |num_calls| so that the packet 457 // will be fetched in the next InsertPacket() call. 458 last_packet_number_ = packet_cb_.num_calls() - 1; 459 460 StartThreads(); 461 } 462 463 virtual void RegisterCodec() OVERRIDE { 464 COMPILE_ASSERT(kSampleRateHz == 16000, test_designed_for_isac_16khz); 465 466 // Register iSAC codec in ACM, effectively unregistering the PCM16B codec 467 // registered in AudioCodingModuleTest::SetUp(); 468 ASSERT_TRUE(acm_->RegisterSendCodec(acm2::ACMCodecDB::kISAC, kPayloadType)); 469 ASSERT_TRUE( 470 acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kISAC, kPayloadType)); 471 } 472 473 virtual void InsertPacket() OVERRIDE { 474 int num_calls = packet_cb_.num_calls(); // Store locally for thread safety. 475 if (num_calls > last_packet_number_) { 476 // Get the new payload out from the callback handler. 477 // Note that since we swap buffers here instead of directly inserting 478 // a pointer to the data in |packet_cb_|, we avoid locking the callback 479 // for the duration of the IncomingPacket() call. 480 packet_cb_.SwapBuffers(&last_payload_vec_); 481 ASSERT_GT(last_payload_vec_.size(), 0u); 482 rtp_utility_->Forward(&rtp_header_); 483 last_packet_number_ = num_calls; 484 } 485 ASSERT_GT(last_payload_vec_.size(), 0u); 486 ASSERT_TRUE(acm_->InsertPacket( 487 &last_payload_vec_[0], last_payload_vec_.size(), rtp_header_)); 488 } 489 490 virtual void InsertAudio() OVERRIDE { 491 memcpy(input_frame_.data_, audio_loop_.GetNextBlock(), kNumSamples10ms); 492 AudioCodingModuleTest::InsertAudio(); 493 } 494 495 // This method is the same as AudioCodingModuleMtTest::TestDone(), but here 496 // it is using the constants defined in this class (i.e., shorter test run). 497 virtual bool TestDone() OVERRIDE { 498 if (packet_cb_.num_calls() > kNumPackets) { 499 CriticalSectionScoped lock(crit_sect_.get()); 500 if (pull_audio_count_ > kNumPullCalls) { 501 // Both conditions for completion are met. End the test. 502 return true; 503 } 504 } 505 return false; 506 } 507 508 int last_packet_number_; 509 std::vector<uint8_t> last_payload_vec_; 510 test::AudioLoop audio_loop_; 511}; 512 513TEST_F(AcmIsacMtTest, DoTest) { 514 EXPECT_EQ(kEventSignaled, RunTest()); 515} 516 517class AcmReceiverBitExactness : public ::testing::Test { 518 public: 519 static std::string PlatformChecksum(std::string win64, 520 std::string android, 521 std::string others) { 522#if defined(_WIN32) && defined(WEBRTC_ARCH_64_BITS) 523 return win64; 524#elif defined(WEBRTC_ANDROID) 525 return android; 526#else 527 return others; 528#endif 529 } 530 531 protected: 532 void Run(int output_freq_hz, const std::string& checksum_ref) { 533 const std::string input_file_name = 534 webrtc::test::ResourcePath("audio_coding/neteq_universal_new", "rtp"); 535 scoped_ptr<test::RtpFileSource> packet_source( 536 test::RtpFileSource::Create(input_file_name)); 537#ifdef WEBRTC_ANDROID 538 // Filter out iLBC and iSAC-swb since they are not supported on Android. 539 packet_source->FilterOutPayloadType(102); // iLBC. 540 packet_source->FilterOutPayloadType(104); // iSAC-swb. 541#endif 542 543 test::AudioChecksum checksum; 544 const std::string output_file_name = 545 webrtc::test::OutputPath() + 546 ::testing::UnitTest::GetInstance() 547 ->current_test_info() 548 ->test_case_name() + 549 "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name() + 550 "_output.pcm"; 551 test::OutputAudioFile output_file(output_file_name); 552 test::AudioSinkFork output(&checksum, &output_file); 553 554 test::AcmReceiveTest test(packet_source.get(), &output, output_freq_hz, 555 test::AcmReceiveTest::kArbitraryChannels); 556 ASSERT_NO_FATAL_FAILURE(test.RegisterNetEqTestCodecs()); 557 test.Run(); 558 559 std::string checksum_string = checksum.Finish(); 560 EXPECT_EQ(checksum_ref, checksum_string); 561 } 562}; 563 564TEST_F(AcmReceiverBitExactness, 8kHzOutput) { 565 Run(8000, 566 PlatformChecksum("bd6f8d9602cd82444ea2539e674df747", 567 "6ac89c7145072c26bfeba602cd661afb", 568 "8a8440f5511eb729221b9aac25cda3a0")); 569} 570 571TEST_F(AcmReceiverBitExactness, 16kHzOutput) { 572 Run(16000, 573 PlatformChecksum("a39bc6ee0c4eb15f3ad2f43cebcc571d", 574 "3e888eb04f57db2c6ef952fe64f17fe6", 575 "7be583092c5adbcb0f6cd66eca20ea63")); 576} 577 578TEST_F(AcmReceiverBitExactness, 32kHzOutput) { 579 Run(32000, 580 PlatformChecksum("80964572aaa2dc92f9e34896dd3802b3", 581 "aeca37e963310f5b6552b7edea23c2f1", 582 "3a84188abe9fca25fedd6034760f3e22")); 583} 584 585TEST_F(AcmReceiverBitExactness, 48kHzOutput) { 586 Run(48000, 587 PlatformChecksum("8aacde91f390e0d5a9c2ed571a25fd37", 588 "76b9e99e0a3998aa28355e7a2bd836f7", 589 "89b4b19bdb4de40f1d88302ef8cb9f9b")); 590} 591 592// This test verifies bit exactness for the send-side of ACM. The test setup is 593// a chain of three different test classes: 594// 595// test::AcmSendTest -> AcmSenderBitExactness -> test::AcmReceiveTest 596// 597// The receiver side is driving the test by requesting new packets from 598// AcmSenderBitExactness::NextPacket(). This method, in turn, asks for the 599// packet from test::AcmSendTest::NextPacket, which inserts audio from the 600// input file until one packet is produced. (The input file loops indefinitely.) 601// Before passing the packet to the receiver, this test class verifies the 602// packet header and updates a payload checksum with the new payload. The 603// decoded output from the receiver is also verified with a (separate) checksum. 604class AcmSenderBitExactness : public ::testing::Test, 605 public test::PacketSource { 606 protected: 607 static const int kTestDurationMs = 1000; 608 609 AcmSenderBitExactness() 610 : frame_size_rtp_timestamps_(0), 611 packet_count_(0), 612 payload_type_(0), 613 last_sequence_number_(0), 614 last_timestamp_(0) {} 615 616 // Sets up the test::AcmSendTest object. Returns true on success, otherwise 617 // false. 618 bool SetUpSender() { 619 const std::string input_file_name = 620 webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); 621 // Note that |audio_source_| will loop forever. The test duration is set 622 // explicitly by |kTestDurationMs|. 623 audio_source_.reset(new test::InputAudioFile(input_file_name)); 624 static const int kSourceRateHz = 32000; 625 send_test_.reset(new test::AcmSendTest( 626 audio_source_.get(), kSourceRateHz, kTestDurationMs)); 627 return send_test_.get() != NULL; 628 } 629 630 // Registers a send codec in the test::AcmSendTest object. Returns true on 631 // success, false on failure. 632 bool RegisterSendCodec(int codec_type, 633 int channels, 634 int payload_type, 635 int frame_size_samples, 636 int frame_size_rtp_timestamps) { 637 payload_type_ = payload_type; 638 frame_size_rtp_timestamps_ = frame_size_rtp_timestamps; 639 return send_test_->RegisterCodec( 640 codec_type, channels, payload_type, frame_size_samples); 641 } 642 643 // Runs the test. SetUpSender() and RegisterSendCodec() must have been called 644 // before calling this method. 645 void Run(const std::string& audio_checksum_ref, 646 const std::string& payload_checksum_ref, 647 int expected_packets, 648 test::AcmReceiveTest::NumOutputChannels expected_channels) { 649 // Set up the receiver used to decode the packets and verify the decoded 650 // output. 651 test::AudioChecksum audio_checksum; 652 const std::string output_file_name = 653 webrtc::test::OutputPath() + 654 ::testing::UnitTest::GetInstance() 655 ->current_test_info() 656 ->test_case_name() + 657 "_" + 658 ::testing::UnitTest::GetInstance()->current_test_info()->name() + 659 "_output.pcm"; 660 test::OutputAudioFile output_file(output_file_name); 661 // Have the output audio sent both to file and to the checksum calculator. 662 test::AudioSinkFork output(&audio_checksum, &output_file); 663 const int kOutputFreqHz = 8000; 664 test::AcmReceiveTest receive_test( 665 this, &output, kOutputFreqHz, expected_channels); 666 ASSERT_NO_FATAL_FAILURE(receive_test.RegisterDefaultCodecs()); 667 668 // This is where the actual test is executed. 669 receive_test.Run(); 670 671 // Extract and verify the audio checksum. 672 std::string checksum_string = audio_checksum.Finish(); 673 EXPECT_EQ(audio_checksum_ref, checksum_string); 674 675 // Extract and verify the payload checksum. 676 char checksum_result[rtc::Md5Digest::kSize]; 677 payload_checksum_.Finish(checksum_result, rtc::Md5Digest::kSize); 678 checksum_string = rtc::hex_encode(checksum_result, rtc::Md5Digest::kSize); 679 EXPECT_EQ(payload_checksum_ref, checksum_string); 680 681 // Verify number of packets produced. 682 EXPECT_EQ(expected_packets, packet_count_); 683 } 684 685 // Returns a pointer to the next packet. Returns NULL if the source is 686 // depleted (i.e., the test duration is exceeded), or if an error occurred. 687 // Inherited from test::PacketSource. 688 virtual test::Packet* NextPacket() OVERRIDE { 689 // Get the next packet from AcmSendTest. Ownership of |packet| is 690 // transferred to this method. 691 test::Packet* packet = send_test_->NextPacket(); 692 if (!packet) 693 return NULL; 694 695 VerifyPacket(packet); 696 // TODO(henrik.lundin) Save the packet to file as well. 697 698 // Pass it on to the caller. The caller becomes the owner of |packet|. 699 return packet; 700 } 701 702 // Verifies the packet. 703 void VerifyPacket(const test::Packet* packet) { 704 EXPECT_TRUE(packet->valid_header()); 705 // (We can check the header fields even if valid_header() is false.) 706 EXPECT_EQ(payload_type_, packet->header().payloadType); 707 if (packet_count_ > 0) { 708 // This is not the first packet. 709 uint16_t sequence_number_diff = 710 packet->header().sequenceNumber - last_sequence_number_; 711 EXPECT_EQ(1, sequence_number_diff); 712 uint32_t timestamp_diff = packet->header().timestamp - last_timestamp_; 713 EXPECT_EQ(frame_size_rtp_timestamps_, timestamp_diff); 714 } 715 ++packet_count_; 716 last_sequence_number_ = packet->header().sequenceNumber; 717 last_timestamp_ = packet->header().timestamp; 718 // Update the checksum. 719 payload_checksum_.Update(packet->payload(), packet->payload_length_bytes()); 720 } 721 722 void SetUpTest(int codec_type, 723 int channels, 724 int payload_type, 725 int codec_frame_size_samples, 726 int codec_frame_size_rtp_timestamps) { 727 ASSERT_TRUE(SetUpSender()); 728 ASSERT_TRUE(RegisterSendCodec(codec_type, 729 channels, 730 payload_type, 731 codec_frame_size_samples, 732 codec_frame_size_rtp_timestamps)); 733 } 734 735 scoped_ptr<test::AcmSendTest> send_test_; 736 scoped_ptr<test::InputAudioFile> audio_source_; 737 uint32_t frame_size_rtp_timestamps_; 738 int packet_count_; 739 uint8_t payload_type_; 740 uint16_t last_sequence_number_; 741 uint32_t last_timestamp_; 742 rtc::Md5Digest payload_checksum_; 743}; 744 745TEST_F(AcmSenderBitExactness, IsacWb30ms) { 746 ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kISAC, 1, 103, 480, 480)); 747 Run(AcmReceiverBitExactness::PlatformChecksum( 748 "c7e5bdadfa2871df95639fcc297cf23d", 749 "0499ca260390769b3172136faad925b9", 750 "0b58f9eeee43d5891f5f6c75e77984a3"), 751 AcmReceiverBitExactness::PlatformChecksum( 752 "d42cb5195463da26c8129bbfe73a22e6", 753 "83de248aea9c3c2bd680b6952401b4ca", 754 "3c79f16f34218271f3dca4e2b1dfe1bb"), 755 33, 756 test::AcmReceiveTest::kMonoOutput); 757} 758 759TEST_F(AcmSenderBitExactness, IsacWb60ms) { 760 ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kISAC, 1, 103, 960, 960)); 761 Run(AcmReceiverBitExactness::PlatformChecksum( 762 "14d63c5f08127d280e722e3191b73bdd", 763 "8da003e16c5371af2dc2be79a50f9076", 764 "1ad29139a04782a33daad8c2b9b35875"), 765 AcmReceiverBitExactness::PlatformChecksum( 766 "ebe04a819d3a9d83a83a17f271e1139a", 767 "97aeef98553b5a4b5a68f8b716e8eaf0", 768 "9e0a0ab743ad987b55b8e14802769c56"), 769 16, 770 test::AcmReceiveTest::kMonoOutput); 771} 772 773TEST_F(AcmSenderBitExactness, DISABLED_ON_ANDROID(IsacSwb30ms)) { 774 ASSERT_NO_FATAL_FAILURE( 775 SetUpTest(acm2::ACMCodecDB::kISACSWB, 1, 104, 960, 960)); 776 Run(AcmReceiverBitExactness::PlatformChecksum( 777 "98d960600eb4ddb3fcbe11f5057ddfd7", 778 "", 779 "2f6dfe142f735f1d96f6bd86d2526f42"), 780 AcmReceiverBitExactness::PlatformChecksum( 781 "cc9d2d86a71d6f99f97680a5c27e2762", 782 "", 783 "7b214fc3a5e33d68bf30e77969371f31"), 784 33, 785 test::AcmReceiveTest::kMonoOutput); 786} 787 788TEST_F(AcmSenderBitExactness, Pcm16_8000khz_10ms) { 789 ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kPCM16B, 1, 107, 80, 80)); 790 Run("de4a98e1406f8b798d99cd0704e862e2", 791 "c1edd36339ce0326cc4550041ad719a0", 792 100, 793 test::AcmReceiveTest::kMonoOutput); 794} 795 796TEST_F(AcmSenderBitExactness, Pcm16_16000khz_10ms) { 797 ASSERT_NO_FATAL_FAILURE( 798 SetUpTest(acm2::ACMCodecDB::kPCM16Bwb, 1, 108, 160, 160)); 799 Run("ae646d7b68384a1269cc080dd4501916", 800 "ad786526383178b08d80d6eee06e9bad", 801 100, 802 test::AcmReceiveTest::kMonoOutput); 803} 804 805TEST_F(AcmSenderBitExactness, Pcm16_32000khz_10ms) { 806 ASSERT_NO_FATAL_FAILURE( 807 SetUpTest(acm2::ACMCodecDB::kPCM16Bswb32kHz, 1, 109, 320, 320)); 808 Run("7fe325e8fbaf755e3c5df0b11a4774fb", 809 "5ef82ea885e922263606c6fdbc49f651", 810 100, 811 test::AcmReceiveTest::kMonoOutput); 812} 813 814TEST_F(AcmSenderBitExactness, Pcm16_stereo_8000khz_10ms) { 815 ASSERT_NO_FATAL_FAILURE( 816 SetUpTest(acm2::ACMCodecDB::kPCM16B_2ch, 2, 111, 80, 80)); 817 Run("fb263b74e7ac3de915474d77e4744ceb", 818 "62ce5adb0d4965d0a52ec98ae7f98974", 819 100, 820 test::AcmReceiveTest::kStereoOutput); 821} 822 823TEST_F(AcmSenderBitExactness, Pcm16_stereo_16000khz_10ms) { 824 ASSERT_NO_FATAL_FAILURE( 825 SetUpTest(acm2::ACMCodecDB::kPCM16Bwb_2ch, 2, 112, 160, 160)); 826 Run("d09e9239553649d7ac93e19d304281fd", 827 "41ca8edac4b8c71cd54fd9f25ec14870", 828 100, 829 test::AcmReceiveTest::kStereoOutput); 830} 831 832TEST_F(AcmSenderBitExactness, Pcm16_stereo_32000khz_10ms) { 833 ASSERT_NO_FATAL_FAILURE( 834 SetUpTest(acm2::ACMCodecDB::kPCM16Bswb32kHz_2ch, 2, 113, 320, 320)); 835 Run("5f025d4f390982cc26b3d92fe02e3044", 836 "50e58502fb04421bf5b857dda4c96879", 837 100, 838 test::AcmReceiveTest::kStereoOutput); 839} 840 841TEST_F(AcmSenderBitExactness, Pcmu_20ms) { 842 ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kPCMU, 1, 0, 160, 160)); 843 Run("81a9d4c0bb72e9becc43aef124c981e9", 844 "8f9b8750bd80fe26b6cbf6659b89f0f9", 845 50, 846 test::AcmReceiveTest::kMonoOutput); 847} 848 849TEST_F(AcmSenderBitExactness, Pcma_20ms) { 850 ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kPCMA, 1, 8, 160, 160)); 851 Run("39611f798969053925a49dc06d08de29", 852 "6ad745e55aa48981bfc790d0eeef2dd1", 853 50, 854 test::AcmReceiveTest::kMonoOutput); 855} 856 857TEST_F(AcmSenderBitExactness, Pcmu_stereo_20ms) { 858 ASSERT_NO_FATAL_FAILURE( 859 SetUpTest(acm2::ACMCodecDB::kPCMU_2ch, 2, 110, 160, 160)); 860 Run("437bec032fdc5cbaa0d5175430af7b18", 861 "60b6f25e8d1e74cb679cfe756dd9bca5", 862 50, 863 test::AcmReceiveTest::kStereoOutput); 864} 865 866TEST_F(AcmSenderBitExactness, Pcma_stereo_20ms) { 867 ASSERT_NO_FATAL_FAILURE( 868 SetUpTest(acm2::ACMCodecDB::kPCMA_2ch, 2, 118, 160, 160)); 869 Run("a5c6d83c5b7cedbeff734238220a4b0c", 870 "92b282c83efd20e7eeef52ba40842cf7", 871 50, 872 test::AcmReceiveTest::kStereoOutput); 873} 874 875TEST_F(AcmSenderBitExactness, DISABLED_ON_ANDROID(Ilbc_30ms)) { 876 ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kILBC, 1, 102, 240, 240)); 877 Run(AcmReceiverBitExactness::PlatformChecksum( 878 "7b6ec10910debd9af08011d3ed5249f7", 879 "android_audio", 880 "7b6ec10910debd9af08011d3ed5249f7"), 881 AcmReceiverBitExactness::PlatformChecksum( 882 "cfae2e9f6aba96e145f2bcdd5050ce78", 883 "android_payload", 884 "cfae2e9f6aba96e145f2bcdd5050ce78"), 885 33, 886 test::AcmReceiveTest::kMonoOutput); 887} 888 889TEST_F(AcmSenderBitExactness, DISABLED_ON_ANDROID(G722_20ms)) { 890 ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kG722, 1, 9, 320, 160)); 891 Run(AcmReceiverBitExactness::PlatformChecksum( 892 "7d759436f2533582950d148b5161a36c", 893 "android_audio", 894 "7d759436f2533582950d148b5161a36c"), 895 AcmReceiverBitExactness::PlatformChecksum( 896 "fc68a87e1380614e658087cb35d5ca10", 897 "android_payload", 898 "fc68a87e1380614e658087cb35d5ca10"), 899 50, 900 test::AcmReceiveTest::kMonoOutput); 901} 902 903TEST_F(AcmSenderBitExactness, DISABLED_ON_ANDROID(G722_stereo_20ms)) { 904 ASSERT_NO_FATAL_FAILURE( 905 SetUpTest(acm2::ACMCodecDB::kG722_2ch, 2, 119, 320, 160)); 906 Run(AcmReceiverBitExactness::PlatformChecksum( 907 "7190ee718ab3d80eca181e5f7140c210", 908 "android_audio", 909 "7190ee718ab3d80eca181e5f7140c210"), 910 AcmReceiverBitExactness::PlatformChecksum( 911 "66516152eeaa1e650ad94ff85f668dac", 912 "android_payload", 913 "66516152eeaa1e650ad94ff85f668dac"), 914 50, 915 test::AcmReceiveTest::kStereoOutput); 916} 917 918TEST_F(AcmSenderBitExactness, Opus_stereo_20ms) { 919 ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kOpus, 2, 120, 960, 960)); 920 Run(AcmReceiverBitExactness::PlatformChecksum( 921 "855041f2490b887302bce9d544731849", 922 "1e1a0fce893fef2d66886a7f09e2ebce", 923 "855041f2490b887302bce9d544731849"), 924 AcmReceiverBitExactness::PlatformChecksum( 925 "d781cce1ab986b618d0da87226cdde30", 926 "1a1fe04dd12e755949987c8d729fb3e0", 927 "d781cce1ab986b618d0da87226cdde30"), 928 50, 929 test::AcmReceiveTest::kStereoOutput); 930} 931 932} // namespace webrtc 933