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