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