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
25namespace webrtc {
26
27struct 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.
42class 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
267class 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
283TEST_P(NetEqStereoTestNoJitter, MAYBE_RunTest) {
284  RunTest(8);
285}
286
287class 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
308TEST_P(NetEqStereoTestPositiveDrift, MAYBE_RunTest) {
309  RunTest(100);
310}
311
312class NetEqStereoTestNegativeDrift : public NetEqStereoTestPositiveDrift {
313 protected:
314  NetEqStereoTestNegativeDrift()
315      : NetEqStereoTestPositiveDrift() {
316    drift_factor = 1.1;
317    last_arrival_time_ = 0;
318  }
319};
320
321TEST_P(NetEqStereoTestNegativeDrift, MAYBE_RunTest) {
322  RunTest(100);
323}
324
325class 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
349TEST_P(NetEqStereoTestDelays, MAYBE_RunTest) {
350  RunTest(1000);
351}
352
353class 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
371TEST_P(NetEqStereoTestLosses, DISABLED_RunTest) {
372  RunTest(100);
373}
374
375
376// Creates a list of parameter sets.
377std::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.
402void 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.
410INSTANTIATE_TEST_CASE_P(MultiChannel,
411                        NetEqStereoTestNoJitter,
412                        ::testing::ValuesIn(GetTestParameters()));
413
414INSTANTIATE_TEST_CASE_P(MultiChannel,
415                        NetEqStereoTestPositiveDrift,
416                        ::testing::ValuesIn(GetTestParameters()));
417
418INSTANTIATE_TEST_CASE_P(MultiChannel,
419                        NetEqStereoTestNegativeDrift,
420                        ::testing::ValuesIn(GetTestParameters()));
421
422INSTANTIATE_TEST_CASE_P(MultiChannel,
423                        NetEqStereoTestDelays,
424                        ::testing::ValuesIn(GetTestParameters()));
425
426INSTANTIATE_TEST_CASE_P(MultiChannel,
427                        NetEqStereoTestLosses,
428                        ::testing::ValuesIn(GetTestParameters()));
429
430}  // namespace webrtc
431