• 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 <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 
NetEqStereoTest()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 
~NetEqStereoTest()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 
SetUp()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 
TearDown()136   virtual void TearDown() {}
137 
GetNewPackets()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 
VerifyOutput(size_t num_samples)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 
GetArrivalTime(int send_time)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 
Lost()180   virtual bool Lost() { return false; }
181 
RunTest(int num_loops)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:
NetEqStereoTestNoJitter()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
TEST_P(NetEqStereoTestNoJitter,MAYBE_RunTest)283 TEST_P(NetEqStereoTestNoJitter, MAYBE_RunTest) {
284   RunTest(8);
285 }
286 
287 class NetEqStereoTestPositiveDrift : public NetEqStereoTest {
288  protected:
NetEqStereoTestPositiveDrift()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   }
GetArrivalTime(int send_time)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 
TEST_P(NetEqStereoTestPositiveDrift,MAYBE_RunTest)308 TEST_P(NetEqStereoTestPositiveDrift, MAYBE_RunTest) {
309   RunTest(100);
310 }
311 
312 class NetEqStereoTestNegativeDrift : public NetEqStereoTestPositiveDrift {
313  protected:
NetEqStereoTestNegativeDrift()314   NetEqStereoTestNegativeDrift()
315       : NetEqStereoTestPositiveDrift() {
316     drift_factor = 1.1;
317     last_arrival_time_ = 0;
318   }
319 };
320 
TEST_P(NetEqStereoTestNegativeDrift,MAYBE_RunTest)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;
NetEqStereoTestDelays()329   NetEqStereoTestDelays()
330       : NetEqStereoTest(),
331         frame_index_(0) {
332   }
333 
GetArrivalTime(int send_time)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 
TEST_P(NetEqStereoTestDelays,MAYBE_RunTest)349 TEST_P(NetEqStereoTestDelays, MAYBE_RunTest) {
350   RunTest(1000);
351 }
352 
353 class NetEqStereoTestLosses : public NetEqStereoTest {
354  protected:
355   static const int kLossInterval = 10;
NetEqStereoTestLosses()356   NetEqStereoTestLosses()
357       : NetEqStereoTest(),
358         frame_index_(0) {
359   }
360 
Lost()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
TEST_P(NetEqStereoTestLosses,DISABLED_RunTest)371 TEST_P(NetEqStereoTestLosses, DISABLED_RunTest) {
372   RunTest(100);
373 }
374 
375 
376 // Creates a list of parameter sets.
GetTestParameters()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.
PrintTo(const TestParameters & p,::std::ostream * os)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