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 
21 namespace webrtc {
22 
23 using ::testing::_;
24 using ::testing::Return;
25 
26 class NetEqExternalDecoderUnitTest : public test::NetEqExternalDecoderTest {
27  protected:
28   static const int kFrameSizeMs = 10;  // Frame size of Pcm16B.
29 
NetEqExternalDecoderUnitTest(NetEqDecoder codec,MockExternalPcm16B * decoder)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 
~NetEqExternalDecoderUnitTest()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.
GetNewPacket()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.
Lost()82   virtual bool Lost() { return false; }
83 
84   // Method to calculate packet arrival time.
GetArrivalTime(int send_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 
RunTest(int num_loops)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 
InsertPacket(WebRtcRTPHeader rtp_header,rtc::ArrayView<const uint8_t> payload,uint32_t receive_timestamp)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 
external_decoder()140   MockExternalPcm16B* external_decoder() { return external_decoder_.get(); }
141 
ResetRtpGenerator(test::RtpGenerator * rtp_generator)142   void ResetRtpGenerator(test::RtpGenerator* rtp_generator) {
143     rtp_generator_.reset(rtp_generator);
144   }
145 
samples_per_ms() const146   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.
166 class NetEqExternalVsInternalDecoderTest : public NetEqExternalDecoderUnitTest,
167                                            public ::testing::Test {
168  protected:
169   static const size_t kMaxBlockSize = 480;  // 10 ms @ 48 kHz.
170 
NetEqExternalVsInternalDecoderTest()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 
SetUp()182   void SetUp() override {
183     ASSERT_EQ(NetEq::kOK, neteq_internal_->RegisterPayloadType(
184                               NetEqDecoder::kDecoderPCM16Bswb32kHz,
185                               "pcm16-swb32", kPayloadType));
186   }
187 
GetAndVerifyOutput()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 
InsertPacket(WebRtcRTPHeader rtp_header,rtc::ArrayView<const uint8_t> payload,uint32_t receive_timestamp)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 
NumExpectedDecodeCalls(int num_loops)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 
TEST_F(NetEqExternalVsInternalDecoderTest,RunTest)233 TEST_F(NetEqExternalVsInternalDecoderTest, RunTest) {
234   RunTest(100);  // Run 100 laps @ 10 ms each in the test loop.
235 }
236 
237 class 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 
LargeTimestampJumpTest()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 
UpdateState(NetEqOutputType output_type)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 
GetAndVerifyOutput()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 
NumExpectedDecodeCalls(int num_loops)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 
TEST_F(LargeTimestampJumpTest,JumpLongerThanHalfRange)328 TEST_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 
TEST_F(LargeTimestampJumpTest,JumpLongerThanHalfRangeAndWrap)352 TEST_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 
376 class ShortTimestampJumpTest : public LargeTimestampJumpTest {
377  protected:
UpdateState(NetEqOutputType output_type)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 
NumExpectedDecodeCalls(int num_loops)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 
TEST_F(ShortTimestampJumpTest,JumpShorterThanHalfRange)411 TEST_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 
TEST_F(ShortTimestampJumpTest,JumpShorterThanHalfRangeAndWrap)435 TEST_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