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