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