tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

neteq_stereo_unittest.cc (15720B)


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