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