Home | History | Annotate | Download | only in acm2
      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