1 /*
2 * Copyright (c) 2012 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 // Unit tests for RedPayloadSplitter class.
12
13 #include "modules/audio_coding/neteq/red_payload_splitter.h"
14
15
16 #include <memory>
17 #include <utility> // pair
18
19 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
20 #include "modules/audio_coding/neteq/decoder_database.h"
21 #include "modules/audio_coding/neteq/packet.h"
22 #include "rtc_base/numerics/safe_conversions.h"
23 #include "test/gtest.h"
24 #include "test/mock_audio_decoder_factory.h"
25
26 using ::testing::Return;
27 using ::testing::ReturnNull;
28
29 namespace webrtc {
30
31 static const int kRedPayloadType = 100;
32 static const size_t kPayloadLength = 10;
33 static const uint16_t kSequenceNumber = 0;
34 static const uint32_t kBaseTimestamp = 0x12345678;
35
36 // A possible Opus packet that contains FEC is the following.
37 // The frame is 20 ms in duration.
38 //
39 // 0 1 2 3
40 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
41 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42 // |0|0|0|0|1|0|0|0|x|1|x|x|x|x|x|x|x| |
43 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
44 // | Compressed frame 1 (N-2 bytes)... :
45 // : |
46 // | |
47 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
CreateOpusFecPayload(uint8_t * payload,size_t payload_length,uint8_t payload_value)48 void CreateOpusFecPayload(uint8_t* payload,
49 size_t payload_length,
50 uint8_t payload_value) {
51 if (payload_length < 2) {
52 return;
53 }
54 payload[0] = 0x08;
55 payload[1] = 0x40;
56 memset(&payload[2], payload_value, payload_length - 2);
57 }
58
59 // RED headers (according to RFC 2198):
60 //
61 // 0 1 2 3
62 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
63 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64 // |F| block PT | timestamp offset | block length |
65 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
66 //
67 // Last RED header:
68 // 0 1 2 3 4 5 6 7
69 // +-+-+-+-+-+-+-+-+
70 // |0| Block PT |
71 // +-+-+-+-+-+-+-+-+
72
73 // Creates a RED packet, with `num_payloads` payloads, with payload types given
74 // by the values in array `payload_types` (which must be of length
75 // `num_payloads`). Each redundant payload is `timestamp_offset` samples
76 // "behind" the the previous payload.
CreateRedPayload(size_t num_payloads,uint8_t * payload_types,int timestamp_offset,bool embed_opus_fec=false)77 Packet CreateRedPayload(size_t num_payloads,
78 uint8_t* payload_types,
79 int timestamp_offset,
80 bool embed_opus_fec = false) {
81 Packet packet;
82 packet.payload_type = kRedPayloadType;
83 packet.timestamp = kBaseTimestamp;
84 packet.sequence_number = kSequenceNumber;
85 packet.payload.SetSize((kPayloadLength + 1) +
86 (num_payloads - 1) *
87 (kPayloadLength + kRedHeaderLength));
88 uint8_t* payload_ptr = packet.payload.data();
89 for (size_t i = 0; i < num_payloads; ++i) {
90 // Write the RED headers.
91 if (i == num_payloads - 1) {
92 // Special case for last payload.
93 *payload_ptr = payload_types[i] & 0x7F; // F = 0;
94 ++payload_ptr;
95 break;
96 }
97 *payload_ptr = payload_types[i] & 0x7F;
98 // Not the last block; set F = 1.
99 *payload_ptr |= 0x80;
100 ++payload_ptr;
101 int this_offset =
102 rtc::checked_cast<int>((num_payloads - i - 1) * timestamp_offset);
103 *payload_ptr = this_offset >> 6;
104 ++payload_ptr;
105 RTC_DCHECK_LE(kPayloadLength, 1023); // Max length described by 10 bits.
106 *payload_ptr = ((this_offset & 0x3F) << 2) | (kPayloadLength >> 8);
107 ++payload_ptr;
108 *payload_ptr = kPayloadLength & 0xFF;
109 ++payload_ptr;
110 }
111 for (size_t i = 0; i < num_payloads; ++i) {
112 // Write `i` to all bytes in each payload.
113 if (embed_opus_fec) {
114 CreateOpusFecPayload(payload_ptr, kPayloadLength,
115 static_cast<uint8_t>(i));
116 } else {
117 memset(payload_ptr, static_cast<int>(i), kPayloadLength);
118 }
119 payload_ptr += kPayloadLength;
120 }
121 return packet;
122 }
123
124 // Create a packet with all payload bytes set to `payload_value`.
CreatePacket(uint8_t payload_type,size_t payload_length,uint8_t payload_value,bool opus_fec=false)125 Packet CreatePacket(uint8_t payload_type,
126 size_t payload_length,
127 uint8_t payload_value,
128 bool opus_fec = false) {
129 Packet packet;
130 packet.payload_type = payload_type;
131 packet.timestamp = kBaseTimestamp;
132 packet.sequence_number = kSequenceNumber;
133 packet.payload.SetSize(payload_length);
134 if (opus_fec) {
135 CreateOpusFecPayload(packet.payload.data(), packet.payload.size(),
136 payload_value);
137 } else {
138 memset(packet.payload.data(), payload_value, packet.payload.size());
139 }
140 return packet;
141 }
142
143 // Checks that `packet` has the attributes given in the remaining parameters.
VerifyPacket(const Packet & packet,size_t payload_length,uint8_t payload_type,uint16_t sequence_number,uint32_t timestamp,uint8_t payload_value,Packet::Priority priority)144 void VerifyPacket(const Packet& packet,
145 size_t payload_length,
146 uint8_t payload_type,
147 uint16_t sequence_number,
148 uint32_t timestamp,
149 uint8_t payload_value,
150 Packet::Priority priority) {
151 EXPECT_EQ(payload_length, packet.payload.size());
152 EXPECT_EQ(payload_type, packet.payload_type);
153 EXPECT_EQ(sequence_number, packet.sequence_number);
154 EXPECT_EQ(timestamp, packet.timestamp);
155 EXPECT_EQ(priority, packet.priority);
156 ASSERT_FALSE(packet.payload.empty());
157 for (size_t i = 0; i < packet.payload.size(); ++i) {
158 ASSERT_EQ(payload_value, packet.payload.data()[i]);
159 }
160 }
161
VerifyPacket(const Packet & packet,size_t payload_length,uint8_t payload_type,uint16_t sequence_number,uint32_t timestamp,uint8_t payload_value,bool primary)162 void VerifyPacket(const Packet& packet,
163 size_t payload_length,
164 uint8_t payload_type,
165 uint16_t sequence_number,
166 uint32_t timestamp,
167 uint8_t payload_value,
168 bool primary) {
169 return VerifyPacket(packet, payload_length, payload_type, sequence_number,
170 timestamp, payload_value,
171 Packet::Priority{0, primary ? 0 : 1});
172 }
173
174 // Start of test definitions.
175
TEST(RedPayloadSplitter,CreateAndDestroy)176 TEST(RedPayloadSplitter, CreateAndDestroy) {
177 RedPayloadSplitter* splitter = new RedPayloadSplitter;
178 delete splitter;
179 }
180
181 // Packet A is split into A1 and A2.
TEST(RedPayloadSplitter,OnePacketTwoPayloads)182 TEST(RedPayloadSplitter, OnePacketTwoPayloads) {
183 uint8_t payload_types[] = {0, 0};
184 const int kTimestampOffset = 160;
185 PacketList packet_list;
186 packet_list.push_back(CreateRedPayload(2, payload_types, kTimestampOffset));
187 RedPayloadSplitter splitter;
188 EXPECT_TRUE(splitter.SplitRed(&packet_list));
189 ASSERT_EQ(2u, packet_list.size());
190 // Check first packet. The first in list should always be the primary payload.
191 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[1],
192 kSequenceNumber, kBaseTimestamp, 1, true);
193 packet_list.pop_front();
194 // Check second packet.
195 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0],
196 kSequenceNumber, kBaseTimestamp - kTimestampOffset, 0, false);
197 }
198
199 // Packets A and B are not split at all. Only the RED header in each packet is
200 // removed.
TEST(RedPayloadSplitter,TwoPacketsOnePayload)201 TEST(RedPayloadSplitter, TwoPacketsOnePayload) {
202 uint8_t payload_types[] = {0};
203 const int kTimestampOffset = 160;
204 // Create first packet, with a single RED payload.
205 PacketList packet_list;
206 packet_list.push_back(CreateRedPayload(1, payload_types, kTimestampOffset));
207 // Create second packet, with a single RED payload.
208 {
209 Packet packet = CreateRedPayload(1, payload_types, kTimestampOffset);
210 // Manually change timestamp and sequence number of second packet.
211 packet.timestamp += kTimestampOffset;
212 packet.sequence_number++;
213 packet_list.push_back(std::move(packet));
214 }
215 RedPayloadSplitter splitter;
216 EXPECT_TRUE(splitter.SplitRed(&packet_list));
217 ASSERT_EQ(2u, packet_list.size());
218 // Check first packet.
219 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0],
220 kSequenceNumber, kBaseTimestamp, 0, true);
221 packet_list.pop_front();
222 // Check second packet.
223 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0],
224 kSequenceNumber + 1, kBaseTimestamp + kTimestampOffset, 0, true);
225 }
226
227 // Packets A and B are split into packets A1, A2, A3, B1, B2, B3, with
228 // attributes as follows:
229 //
230 // A1* A2 A3 B1* B2 B3
231 // Payload type 0 1 2 0 1 2
232 // Timestamp b b-o b-2o b+o b b-o
233 // Sequence number 0 0 0 1 1 1
234 //
235 // b = kBaseTimestamp, o = kTimestampOffset, * = primary.
TEST(RedPayloadSplitter,TwoPacketsThreePayloads)236 TEST(RedPayloadSplitter, TwoPacketsThreePayloads) {
237 uint8_t payload_types[] = {2, 1, 0}; // Primary is the last one.
238 const int kTimestampOffset = 160;
239 // Create first packet, with 3 RED payloads.
240 PacketList packet_list;
241 packet_list.push_back(CreateRedPayload(3, payload_types, kTimestampOffset));
242 // Create first packet, with 3 RED payloads.
243 {
244 Packet packet = CreateRedPayload(3, payload_types, kTimestampOffset);
245 // Manually change timestamp and sequence number of second packet.
246 packet.timestamp += kTimestampOffset;
247 packet.sequence_number++;
248 packet_list.push_back(std::move(packet));
249 }
250 RedPayloadSplitter splitter;
251 EXPECT_TRUE(splitter.SplitRed(&packet_list));
252 ASSERT_EQ(6u, packet_list.size());
253 // Check first packet, A1.
254 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[2],
255 kSequenceNumber, kBaseTimestamp, 2, {0, 0});
256 packet_list.pop_front();
257 // Check second packet, A2.
258 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[1],
259 kSequenceNumber, kBaseTimestamp - kTimestampOffset, 1, {0, 1});
260 packet_list.pop_front();
261 // Check third packet, A3.
262 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0],
263 kSequenceNumber, kBaseTimestamp - 2 * kTimestampOffset, 0,
264 {0, 2});
265 packet_list.pop_front();
266 // Check fourth packet, B1.
267 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[2],
268 kSequenceNumber + 1, kBaseTimestamp + kTimestampOffset, 2,
269 {0, 0});
270 packet_list.pop_front();
271 // Check fifth packet, B2.
272 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[1],
273 kSequenceNumber + 1, kBaseTimestamp, 1, {0, 1});
274 packet_list.pop_front();
275 // Check sixth packet, B3.
276 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0],
277 kSequenceNumber + 1, kBaseTimestamp - kTimestampOffset, 0,
278 {0, 2});
279 }
280
281 // Creates a list with 4 packets with these payload types:
282 // 0 = CNGnb
283 // 1 = PCMu
284 // 2 = DTMF (AVT)
285 // 3 = iLBC
286 // We expect the method CheckRedPayloads to discard the iLBC packet, since it
287 // is a non-CNG, non-DTMF payload of another type than the first speech payload
288 // found in the list (which is PCMu).
TEST(RedPayloadSplitter,CheckRedPayloads)289 TEST(RedPayloadSplitter, CheckRedPayloads) {
290 PacketList packet_list;
291 for (uint8_t i = 0; i <= 3; ++i) {
292 // Create packet with payload type `i`, payload length 10 bytes, all 0.
293 packet_list.push_back(CreatePacket(i, 10, 0));
294 }
295
296 // Use a real DecoderDatabase object here instead of a mock, since it is
297 // easier to just register the payload types and let the actual implementation
298 // do its job.
299 DecoderDatabase decoder_database(
300 rtc::make_ref_counted<MockAudioDecoderFactory>(), absl::nullopt);
301 decoder_database.RegisterPayload(0, SdpAudioFormat("cn", 8000, 1));
302 decoder_database.RegisterPayload(1, SdpAudioFormat("pcmu", 8000, 1));
303 decoder_database.RegisterPayload(2,
304 SdpAudioFormat("telephone-event", 8000, 1));
305 decoder_database.RegisterPayload(3, SdpAudioFormat("ilbc", 8000, 1));
306
307 RedPayloadSplitter splitter;
308 splitter.CheckRedPayloads(&packet_list, decoder_database);
309
310 ASSERT_EQ(3u, packet_list.size()); // Should have dropped the last packet.
311 // Verify packets. The loop verifies that payload types 0, 1, and 2 are in the
312 // list.
313 for (int i = 0; i <= 2; ++i) {
314 VerifyPacket(packet_list.front(), 10, i, kSequenceNumber, kBaseTimestamp, 0,
315 true);
316 packet_list.pop_front();
317 }
318 EXPECT_TRUE(packet_list.empty());
319 }
320
321 // This test creates a RED packet where the payloads also have the payload type
322 // for RED. That is, some kind of weird nested RED packet. This is not supported
323 // and the splitter should discard all packets.
TEST(RedPayloadSplitter,CheckRedPayloadsRecursiveRed)324 TEST(RedPayloadSplitter, CheckRedPayloadsRecursiveRed) {
325 PacketList packet_list;
326 for (uint8_t i = 0; i <= 3; ++i) {
327 // Create packet with RED payload type, payload length 10 bytes, all 0.
328 packet_list.push_back(CreatePacket(kRedPayloadType, 10, 0));
329 }
330
331 // Use a real DecoderDatabase object here instead of a mock, since it is
332 // easier to just register the payload types and let the actual implementation
333 // do its job.
334 DecoderDatabase decoder_database(
335 rtc::make_ref_counted<MockAudioDecoderFactory>(), absl::nullopt);
336 decoder_database.RegisterPayload(kRedPayloadType,
337 SdpAudioFormat("red", 8000, 1));
338
339 RedPayloadSplitter splitter;
340 splitter.CheckRedPayloads(&packet_list, decoder_database);
341
342 EXPECT_TRUE(packet_list.empty()); // Should have dropped all packets.
343 }
344
345 // Packet A is split into A1, A2 and A3. But the length parameter is off, so
346 // the last payloads should be discarded.
TEST(RedPayloadSplitter,WrongPayloadLength)347 TEST(RedPayloadSplitter, WrongPayloadLength) {
348 uint8_t payload_types[] = {0, 0, 0};
349 const int kTimestampOffset = 160;
350 PacketList packet_list;
351 {
352 Packet packet = CreateRedPayload(3, payload_types, kTimestampOffset);
353 // Manually tamper with the payload length of the packet.
354 // This is one byte too short for the second payload (out of three).
355 // We expect only the first payload to be returned.
356 packet.payload.SetSize(packet.payload.size() - (kPayloadLength + 1));
357 packet_list.push_back(std::move(packet));
358 }
359 RedPayloadSplitter splitter;
360 EXPECT_FALSE(splitter.SplitRed(&packet_list));
361 ASSERT_EQ(1u, packet_list.size());
362 // Check first packet.
363 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0],
364 kSequenceNumber, kBaseTimestamp - 2 * kTimestampOffset, 0,
365 {0, 2});
366 packet_list.pop_front();
367 }
368
369 // Test that we reject packets too short to contain a RED header.
TEST(RedPayloadSplitter,RejectsIncompleteHeaders)370 TEST(RedPayloadSplitter, RejectsIncompleteHeaders) {
371 RedPayloadSplitter splitter;
372
373 uint8_t payload_types[] = {0, 0};
374 const int kTimestampOffset = 160;
375
376 PacketList packet_list;
377
378 // Truncate the packet such that the first block can not be parsed.
379 packet_list.push_back(CreateRedPayload(2, payload_types, kTimestampOffset));
380 packet_list.front().payload.SetSize(4);
381 EXPECT_FALSE(splitter.SplitRed(&packet_list));
382 EXPECT_FALSE(packet_list.empty());
383
384 // Truncate the packet such that the first block can not be parsed.
385 packet_list.front().payload.SetSize(3);
386 EXPECT_FALSE(splitter.SplitRed(&packet_list));
387 EXPECT_FALSE(packet_list.empty());
388 }
389
390 } // namespace webrtc
391