xref: /aosp_15_r20/external/webrtc/modules/audio_coding/neteq/red_payload_splitter_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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