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 operation for externally created decoders.
12
13#include "testing/gmock/include/gmock/gmock.h"
14#include "webrtc/base/scoped_ptr.h"
15#include "webrtc/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h"
16#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h"
17#include "webrtc/modules/audio_coding/neteq/tools/neteq_external_decoder_test.h"
18#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h"
19#include "webrtc/test/testsupport/fileutils.h"
20
21namespace webrtc {
22
23using ::testing::_;
24using ::testing::Return;
25
26class NetEqExternalDecoderUnitTest : public test::NetEqExternalDecoderTest {
27 protected:
28  static const int kFrameSizeMs = 10;  // Frame size of Pcm16B.
29
30  NetEqExternalDecoderUnitTest(NetEqDecoder codec,
31                               MockExternalPcm16B* decoder)
32      : NetEqExternalDecoderTest(codec, decoder),
33        external_decoder_(decoder),
34        samples_per_ms_(CodecSampleRateHz(codec) / 1000),
35        frame_size_samples_(kFrameSizeMs * samples_per_ms_),
36        rtp_generator_(new test::RtpGenerator(samples_per_ms_)),
37        input_(new int16_t[frame_size_samples_]),
38        // Payload should be no larger than input.
39        encoded_(new uint8_t[2 * frame_size_samples_]),
40        payload_size_bytes_(0),
41        last_send_time_(0),
42        last_arrival_time_(0) {
43    // NetEq is not allowed to delete the external decoder (hence Times(0)).
44    EXPECT_CALL(*external_decoder_, Die()).Times(0);
45    Init();
46
47    const std::string file_name =
48        webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
49    input_file_.reset(new test::InputAudioFile(file_name));
50  }
51
52  virtual ~NetEqExternalDecoderUnitTest() {
53    delete [] input_;
54    delete [] encoded_;
55    // ~NetEqExternalDecoderTest() will delete |external_decoder_|, so expecting
56    // Die() to be called.
57    EXPECT_CALL(*external_decoder_, Die()).Times(1);
58  }
59
60  // Method to draw kFrameSizeMs audio and verify the output.
61  // Use gTest methods. e.g. ASSERT_EQ() inside to trigger errors.
62  virtual void GetAndVerifyOutput() = 0;
63
64  // Method to get the number of calls to the Decode() method of the external
65  // decoder.
66  virtual int NumExpectedDecodeCalls(int num_loops) = 0;
67
68  // Method to generate packets and return the send time of the packet.
69  int GetNewPacket() {
70    if (!input_file_->Read(frame_size_samples_, input_)) {
71      return -1;
72    }
73    payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_,
74                                              encoded_);
75
76    int next_send_time = rtp_generator_->GetRtpHeader(
77        kPayloadType, frame_size_samples_, &rtp_header_);
78    return next_send_time;
79  }
80
81  // Method to decide packet losses.
82  virtual bool Lost() { return false; }
83
84  // Method to calculate packet arrival time.
85  int GetArrivalTime(int send_time) {
86    int arrival_time = last_arrival_time_ + (send_time - last_send_time_);
87    last_send_time_ = send_time;
88    last_arrival_time_ = arrival_time;
89    return arrival_time;
90  }
91
92  void RunTest(int num_loops) {
93    // Get next input packets (mono and multi-channel).
94    uint32_t next_send_time;
95    uint32_t next_arrival_time;
96    do {
97      next_send_time = GetNewPacket();
98      next_arrival_time = GetArrivalTime(next_send_time);
99    } while (Lost());  // If lost, immediately read the next packet.
100
101    EXPECT_CALL(
102        *external_decoder_,
103        DecodeInternal(_, payload_size_bytes_, 1000 * samples_per_ms_, _, _))
104        .Times(NumExpectedDecodeCalls(num_loops));
105
106    uint32_t time_now = 0;
107    for (int k = 0; k < num_loops; ++k) {
108      while (time_now >= next_arrival_time) {
109        InsertPacket(rtp_header_, rtc::ArrayView<const uint8_t>(
110                                      encoded_, payload_size_bytes_),
111                     next_arrival_time);
112        // Get next input packet.
113        do {
114          next_send_time = GetNewPacket();
115          next_arrival_time = GetArrivalTime(next_send_time);
116        } while (Lost());  // If lost, immediately read the next packet.
117      }
118
119      std::ostringstream ss;
120      ss << "Lap number " << k << ".";
121      SCOPED_TRACE(ss.str());  // Print out the parameter values on failure.
122      // Compare mono and multi-channel.
123      ASSERT_NO_FATAL_FAILURE(GetAndVerifyOutput());
124
125      time_now += kOutputLengthMs;
126    }
127  }
128
129  void InsertPacket(WebRtcRTPHeader rtp_header,
130                    rtc::ArrayView<const uint8_t> payload,
131                    uint32_t receive_timestamp) override {
132    EXPECT_CALL(
133        *external_decoder_,
134        IncomingPacket(_, payload.size(), rtp_header.header.sequenceNumber,
135                       rtp_header.header.timestamp, receive_timestamp));
136    NetEqExternalDecoderTest::InsertPacket(rtp_header, payload,
137                                           receive_timestamp);
138  }
139
140  MockExternalPcm16B* external_decoder() { return external_decoder_.get(); }
141
142  void ResetRtpGenerator(test::RtpGenerator* rtp_generator) {
143    rtp_generator_.reset(rtp_generator);
144  }
145
146  int samples_per_ms() const { return samples_per_ms_; }
147 private:
148  rtc::scoped_ptr<MockExternalPcm16B> external_decoder_;
149  int samples_per_ms_;
150  size_t frame_size_samples_;
151  rtc::scoped_ptr<test::RtpGenerator> rtp_generator_;
152  int16_t* input_;
153  uint8_t* encoded_;
154  size_t payload_size_bytes_;
155  uint32_t last_send_time_;
156  uint32_t last_arrival_time_;
157  rtc::scoped_ptr<test::InputAudioFile> input_file_;
158  WebRtcRTPHeader rtp_header_;
159};
160
161// This test encodes a few packets of PCM16b 32 kHz data and inserts it into two
162// different NetEq instances. The first instance uses the internal version of
163// the decoder object, while the second one uses an externally created decoder
164// object (ExternalPcm16B wrapped in MockExternalPcm16B, both defined above).
165// The test verifies that the output from both instances match.
166class NetEqExternalVsInternalDecoderTest : public NetEqExternalDecoderUnitTest,
167                                           public ::testing::Test {
168 protected:
169  static const size_t kMaxBlockSize = 480;  // 10 ms @ 48 kHz.
170
171  NetEqExternalVsInternalDecoderTest()
172      : NetEqExternalDecoderUnitTest(NetEqDecoder::kDecoderPCM16Bswb32kHz,
173                                     new MockExternalPcm16B),
174        sample_rate_hz_(
175            CodecSampleRateHz(NetEqDecoder::kDecoderPCM16Bswb32kHz)) {
176    NetEq::Config config;
177    config.sample_rate_hz =
178        CodecSampleRateHz(NetEqDecoder::kDecoderPCM16Bswb32kHz);
179    neteq_internal_.reset(NetEq::Create(config));
180  }
181
182  void SetUp() override {
183    ASSERT_EQ(NetEq::kOK, neteq_internal_->RegisterPayloadType(
184                              NetEqDecoder::kDecoderPCM16Bswb32kHz,
185                              "pcm16-swb32", kPayloadType));
186  }
187
188  void GetAndVerifyOutput() override {
189    NetEqOutputType output_type;
190    size_t samples_per_channel;
191    size_t num_channels;
192    // Get audio from internal decoder instance.
193    EXPECT_EQ(NetEq::kOK,
194              neteq_internal_->GetAudio(kMaxBlockSize,
195                                        output_internal_,
196                                        &samples_per_channel,
197                                        &num_channels,
198                                        &output_type));
199    EXPECT_EQ(1u, num_channels);
200    EXPECT_EQ(static_cast<size_t>(kOutputLengthMs * sample_rate_hz_ / 1000),
201              samples_per_channel);
202
203    // Get audio from external decoder instance.
204    samples_per_channel = GetOutputAudio(kMaxBlockSize, output_, &output_type);
205
206    for (size_t i = 0; i < samples_per_channel; ++i) {
207      ASSERT_EQ(output_[i], output_internal_[i]) <<
208          "Diff in sample " << i << ".";
209    }
210  }
211
212  void InsertPacket(WebRtcRTPHeader rtp_header,
213                    rtc::ArrayView<const uint8_t> payload,
214                    uint32_t receive_timestamp) override {
215    // Insert packet in internal decoder.
216    ASSERT_EQ(NetEq::kOK, neteq_internal_->InsertPacket(rtp_header, payload,
217                                                        receive_timestamp));
218
219    // Insert packet in external decoder instance.
220    NetEqExternalDecoderUnitTest::InsertPacket(rtp_header, payload,
221                                               receive_timestamp);
222  }
223
224  int NumExpectedDecodeCalls(int num_loops) override { return num_loops; }
225
226 private:
227  int sample_rate_hz_;
228  rtc::scoped_ptr<NetEq> neteq_internal_;
229  int16_t output_internal_[kMaxBlockSize];
230  int16_t output_[kMaxBlockSize];
231};
232
233TEST_F(NetEqExternalVsInternalDecoderTest, RunTest) {
234  RunTest(100);  // Run 100 laps @ 10 ms each in the test loop.
235}
236
237class LargeTimestampJumpTest : public NetEqExternalDecoderUnitTest,
238                               public ::testing::Test {
239 protected:
240  static const size_t kMaxBlockSize = 480;  // 10 ms @ 48 kHz.
241
242  enum TestStates {
243    kInitialPhase,
244    kNormalPhase,
245    kExpandPhase,
246    kFadedExpandPhase,
247    kRecovered
248  };
249
250  LargeTimestampJumpTest()
251      : NetEqExternalDecoderUnitTest(NetEqDecoder::kDecoderPCM16B,
252                                     new MockExternalPcm16B),
253        test_state_(kInitialPhase) {
254    EXPECT_CALL(*external_decoder(), HasDecodePlc())
255        .WillRepeatedly(Return(false));
256  }
257
258  virtual void UpdateState(NetEqOutputType output_type) {
259    switch (test_state_) {
260      case kInitialPhase: {
261        if (output_type == kOutputNormal) {
262          test_state_ = kNormalPhase;
263        }
264        break;
265      }
266      case kNormalPhase: {
267        if (output_type == kOutputPLC) {
268          test_state_ = kExpandPhase;
269        }
270        break;
271      }
272      case kExpandPhase: {
273        if (output_type == kOutputPLCtoCNG) {
274          test_state_ = kFadedExpandPhase;
275        } else if (output_type == kOutputNormal) {
276          test_state_ = kRecovered;
277        }
278        break;
279      }
280      case kFadedExpandPhase: {
281        if (output_type == kOutputNormal) {
282          test_state_ = kRecovered;
283        }
284        break;
285      }
286      case kRecovered: {
287        break;
288      }
289    }
290  }
291
292  void GetAndVerifyOutput() override {
293    size_t num_samples;
294    NetEqOutputType output_type;
295    num_samples = GetOutputAudio(kMaxBlockSize, output_, &output_type);
296    UpdateState(output_type);
297
298    if (test_state_ == kExpandPhase || test_state_ == kFadedExpandPhase) {
299      // Don't verify the output in this phase of the test.
300      return;
301    }
302
303    for (size_t i = 0; i < num_samples; ++i) {
304      if (output_[i] != 0)
305        return;
306    }
307    EXPECT_TRUE(false)
308        << "Expected at least one non-zero sample in each output block.";
309  }
310
311  int NumExpectedDecodeCalls(int num_loops) override {
312    // Some packets at the end of the stream won't be decoded. When the jump in
313    // timestamp happens, NetEq will do Expand during one GetAudio call. In the
314    // next call it will decode the packet after the jump, but the net result is
315    // that the delay increased by 1 packet. In another call, a Pre-emptive
316    // Expand operation is performed, leading to delay increase by 1 packet. In
317    // total, the test will end with a 2-packet delay, which results in the 2
318    // last packets not being decoded.
319    return num_loops - 2;
320  }
321
322  TestStates test_state_;
323
324 private:
325  int16_t output_[kMaxBlockSize];
326};
327
328TEST_F(LargeTimestampJumpTest, JumpLongerThanHalfRange) {
329  // Set the timestamp series to start at 2880, increase to 7200, then jump to
330  // 2869342376. The sequence numbers start at 42076 and increase by 1 for each
331  // packet, also when the timestamp jumps.
332  static const uint16_t kStartSeqeunceNumber = 42076;
333  static const uint32_t kStartTimestamp = 2880;
334  static const uint32_t kJumpFromTimestamp = 7200;
335  static const uint32_t kJumpToTimestamp = 2869342376;
336  static_assert(kJumpFromTimestamp < kJumpToTimestamp,
337                "timestamp jump should not result in wrap");
338  static_assert(
339      static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) > 0x7FFFFFFF,
340      "jump should be larger than half range");
341  // Replace the default RTP generator with one that jumps in timestamp.
342  ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(),
343                                                        kStartSeqeunceNumber,
344                                                        kStartTimestamp,
345                                                        kJumpFromTimestamp,
346                                                        kJumpToTimestamp));
347
348  RunTest(130);  // Run 130 laps @ 10 ms each in the test loop.
349  EXPECT_EQ(kRecovered, test_state_);
350}
351
352TEST_F(LargeTimestampJumpTest, JumpLongerThanHalfRangeAndWrap) {
353  // Make a jump larger than half the 32-bit timestamp range. Set the start
354  // timestamp such that the jump will result in a wrap around.
355  static const uint16_t kStartSeqeunceNumber = 42076;
356  // Set the jump length slightly larger than 2^31.
357  static const uint32_t kStartTimestamp = 3221223116;
358  static const uint32_t kJumpFromTimestamp = 3221223216;
359  static const uint32_t kJumpToTimestamp = 1073744278;
360  static_assert(kJumpToTimestamp < kJumpFromTimestamp,
361                "timestamp jump should result in wrap");
362  static_assert(
363      static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) > 0x7FFFFFFF,
364      "jump should be larger than half range");
365  // Replace the default RTP generator with one that jumps in timestamp.
366  ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(),
367                                                        kStartSeqeunceNumber,
368                                                        kStartTimestamp,
369                                                        kJumpFromTimestamp,
370                                                        kJumpToTimestamp));
371
372  RunTest(130);  // Run 130 laps @ 10 ms each in the test loop.
373  EXPECT_EQ(kRecovered, test_state_);
374}
375
376class ShortTimestampJumpTest : public LargeTimestampJumpTest {
377 protected:
378  void UpdateState(NetEqOutputType output_type) override {
379    switch (test_state_) {
380      case kInitialPhase: {
381        if (output_type == kOutputNormal) {
382          test_state_ = kNormalPhase;
383        }
384        break;
385      }
386      case kNormalPhase: {
387        if (output_type == kOutputPLC) {
388          test_state_ = kExpandPhase;
389        }
390        break;
391      }
392      case kExpandPhase: {
393        if (output_type == kOutputNormal) {
394          test_state_ = kRecovered;
395        }
396        break;
397      }
398      case kRecovered: {
399        break;
400      }
401      default: { FAIL(); }
402    }
403  }
404
405  int NumExpectedDecodeCalls(int num_loops) override {
406    // Some packets won't be decoded because of the timestamp jump.
407    return num_loops - 2;
408  }
409};
410
411TEST_F(ShortTimestampJumpTest, JumpShorterThanHalfRange) {
412  // Make a jump shorter than half the 32-bit timestamp range. Set the start
413  // timestamp such that the jump will not result in a wrap around.
414  static const uint16_t kStartSeqeunceNumber = 42076;
415  // Set the jump length slightly smaller than 2^31.
416  static const uint32_t kStartTimestamp = 4711;
417  static const uint32_t kJumpFromTimestamp = 4811;
418  static const uint32_t kJumpToTimestamp = 2147483747;
419  static_assert(kJumpFromTimestamp < kJumpToTimestamp,
420                "timestamp jump should not result in wrap");
421  static_assert(
422      static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) < 0x7FFFFFFF,
423      "jump should be smaller than half range");
424  // Replace the default RTP generator with one that jumps in timestamp.
425  ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(),
426                                                        kStartSeqeunceNumber,
427                                                        kStartTimestamp,
428                                                        kJumpFromTimestamp,
429                                                        kJumpToTimestamp));
430
431  RunTest(130);  // Run 130 laps @ 10 ms each in the test loop.
432  EXPECT_EQ(kRecovered, test_state_);
433}
434
435TEST_F(ShortTimestampJumpTest, JumpShorterThanHalfRangeAndWrap) {
436  // Make a jump shorter than half the 32-bit timestamp range. Set the start
437  // timestamp such that the jump will result in a wrap around.
438  static const uint16_t kStartSeqeunceNumber = 42076;
439  // Set the jump length slightly smaller than 2^31.
440  static const uint32_t kStartTimestamp = 3221227827;
441  static const uint32_t kJumpFromTimestamp = 3221227927;
442  static const uint32_t kJumpToTimestamp = 1073739567;
443  static_assert(kJumpToTimestamp < kJumpFromTimestamp,
444                "timestamp jump should result in wrap");
445  static_assert(
446      static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) < 0x7FFFFFFF,
447      "jump should be smaller than half range");
448  // Replace the default RTP generator with one that jumps in timestamp.
449  ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(),
450                                                        kStartSeqeunceNumber,
451                                                        kStartTimestamp,
452                                                        kJumpFromTimestamp,
453                                                        kJumpToTimestamp));
454
455  RunTest(130);  // Run 130 laps @ 10 ms each in the test loop.
456  EXPECT_EQ(kRecovered, test_state_);
457}
458
459}  // namespace webrtc
460