xref: /aosp_15_r20/external/webrtc/modules/rtp_rtcp/test/testFec/test_fec.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 /*
12  * Test application for core FEC algorithm. Calls encoding and decoding
13  * functions in ForwardErrorCorrection directly.
14  */
15 
16 #include <string.h>
17 #include <time.h>
18 
19 #include <list>
20 
21 #include "modules/rtp_rtcp/source/byte_io.h"
22 #include "modules/rtp_rtcp/source/forward_error_correction.h"
23 #include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
24 #include "rtc_base/random.h"
25 #include "test/gtest.h"
26 #include "test/testsupport/file_utils.h"
27 
28 // #define VERBOSE_OUTPUT
29 
30 namespace webrtc {
31 namespace fec_private_tables {
32 extern const uint8_t** kPacketMaskBurstyTbl[12];
33 }
34 namespace test {
35 using fec_private_tables::kPacketMaskBurstyTbl;
36 
ReceivePackets(std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>> * to_decode_list,std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>> * received_packet_list,size_t num_packets_to_decode,float reorder_rate,float duplicate_rate,Random * random)37 void ReceivePackets(
38     std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>*
39         to_decode_list,
40     std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>*
41         received_packet_list,
42     size_t num_packets_to_decode,
43     float reorder_rate,
44     float duplicate_rate,
45     Random* random) {
46   RTC_DCHECK(to_decode_list->empty());
47   RTC_DCHECK_LE(num_packets_to_decode, received_packet_list->size());
48 
49   for (size_t i = 0; i < num_packets_to_decode; i++) {
50     auto it = received_packet_list->begin();
51     // Reorder packets.
52     float random_variable = random->Rand<float>();
53     while (random_variable < reorder_rate) {
54       ++it;
55       if (it == received_packet_list->end()) {
56         --it;
57         break;
58       }
59       random_variable = random->Rand<float>();
60     }
61     to_decode_list->push_back(std::move(*it));
62     received_packet_list->erase(it);
63 
64     // Duplicate packets.
65     ForwardErrorCorrection::ReceivedPacket* received_packet =
66         to_decode_list->back().get();
67     random_variable = random->Rand<float>();
68     while (random_variable < duplicate_rate) {
69       std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> duplicate_packet(
70           new ForwardErrorCorrection::ReceivedPacket());
71       *duplicate_packet = *received_packet;
72       duplicate_packet->pkt = new ForwardErrorCorrection::Packet();
73       duplicate_packet->pkt->data = received_packet->pkt->data;
74 
75       to_decode_list->push_back(std::move(duplicate_packet));
76       random_variable = random->Rand<float>();
77     }
78   }
79 }
80 
RunTest(bool use_flexfec)81 void RunTest(bool use_flexfec) {
82   // TODO(marpan): Split this function into subroutines/helper functions.
83   enum { kMaxNumberMediaPackets = 48 };
84   enum { kMaxNumberFecPackets = 48 };
85 
86   const uint32_t kNumMaskBytesL0 = 2;
87   const uint32_t kNumMaskBytesL1 = 6;
88 
89   // FOR UEP
90   const bool kUseUnequalProtection = true;
91 
92   // FEC mask types.
93   const FecMaskType kMaskTypes[] = {kFecMaskRandom, kFecMaskBursty};
94   const int kNumFecMaskTypes = sizeof(kMaskTypes) / sizeof(*kMaskTypes);
95 
96   // Maximum number of media packets allowed for the mask type.
97   const uint16_t kMaxMediaPackets[] = {
98       kMaxNumberMediaPackets,
99       sizeof(kPacketMaskBurstyTbl) / sizeof(*kPacketMaskBurstyTbl)};
100 
101   ASSERT_EQ(12, kMaxMediaPackets[1]) << "Max media packets for bursty mode not "
102                                         "equal to 12.";
103 
104   ForwardErrorCorrection::PacketList media_packet_list;
105   std::list<ForwardErrorCorrection::Packet*> fec_packet_list;
106   std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>
107       to_decode_list;
108   std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>
109       received_packet_list;
110   ForwardErrorCorrection::RecoveredPacketList recovered_packet_list;
111   std::list<uint8_t*> fec_mask_list;
112 
113   // Running over only two loss rates to limit execution time.
114   const float loss_rate[] = {0.05f, 0.01f};
115   const uint32_t loss_rate_size = sizeof(loss_rate) / sizeof(*loss_rate);
116   const float reorder_rate = 0.1f;
117   const float duplicate_rate = 0.1f;
118 
119   uint8_t media_loss_mask[kMaxNumberMediaPackets];
120   uint8_t fec_loss_mask[kMaxNumberFecPackets];
121   uint8_t fec_packet_masks[kMaxNumberFecPackets][kMaxNumberMediaPackets];
122 
123   // Seed the random number generator, storing the seed to file in order to
124   // reproduce past results.
125   const unsigned int random_seed = static_cast<unsigned int>(time(nullptr));
126   Random random(random_seed);
127   std::string filename = webrtc::test::OutputPath() + "randomSeedLog.txt";
128   FILE* random_seed_file = fopen(filename.c_str(), "a");
129   fprintf(random_seed_file, "%u\n", random_seed);
130   fclose(random_seed_file);
131   random_seed_file = nullptr;
132 
133   uint16_t seq_num = 0;
134   uint32_t timestamp = random.Rand<uint32_t>();
135   const uint32_t media_ssrc = random.Rand(1u, 0xfffffffe);
136   uint32_t fec_ssrc;
137   uint16_t fec_seq_num_offset;
138   if (use_flexfec) {
139     fec_ssrc = random.Rand(1u, 0xfffffffe);
140     fec_seq_num_offset = random.Rand(0, 1 << 15);
141   } else {
142     fec_ssrc = media_ssrc;
143     fec_seq_num_offset = 0;
144   }
145 
146   std::unique_ptr<ForwardErrorCorrection> fec;
147   if (use_flexfec) {
148     fec = ForwardErrorCorrection::CreateFlexfec(fec_ssrc, media_ssrc);
149   } else {
150     RTC_DCHECK_EQ(media_ssrc, fec_ssrc);
151     fec = ForwardErrorCorrection::CreateUlpfec(fec_ssrc);
152   }
153 
154   // Loop over the mask types: random and bursty.
155   for (int mask_type_idx = 0; mask_type_idx < kNumFecMaskTypes;
156        ++mask_type_idx) {
157     for (uint32_t loss_rate_idx = 0; loss_rate_idx < loss_rate_size;
158          ++loss_rate_idx) {
159       printf("Loss rate: %.2f, Mask type %d \n", loss_rate[loss_rate_idx],
160              mask_type_idx);
161 
162       const uint32_t packet_mask_max = kMaxMediaPackets[mask_type_idx];
163       std::unique_ptr<uint8_t[]> packet_mask(
164           new uint8_t[packet_mask_max * kNumMaskBytesL1]);
165 
166       FecMaskType fec_mask_type = kMaskTypes[mask_type_idx];
167 
168       for (uint32_t num_media_packets = 1; num_media_packets <= packet_mask_max;
169            num_media_packets++) {
170         internal::PacketMaskTable mask_table(fec_mask_type, num_media_packets);
171 
172         for (uint32_t num_fec_packets = 1;
173              num_fec_packets <= num_media_packets &&
174              num_fec_packets <= packet_mask_max;
175              num_fec_packets++) {
176           // Loop over num_imp_packets: usually <= (0.3*num_media_packets).
177           // For this test we check up to ~ (num_media_packets / 4).
178           uint32_t max_num_imp_packets = num_media_packets / 4 + 1;
179           for (uint32_t num_imp_packets = 0;
180                num_imp_packets <= max_num_imp_packets &&
181                num_imp_packets <= packet_mask_max;
182                num_imp_packets++) {
183             uint8_t protection_factor =
184                 static_cast<uint8_t>(num_fec_packets * 255 / num_media_packets);
185 
186             const uint32_t mask_bytes_per_fec_packet =
187                 (num_media_packets > 16) ? kNumMaskBytesL1 : kNumMaskBytesL0;
188 
189             memset(packet_mask.get(), 0,
190                    num_media_packets * mask_bytes_per_fec_packet);
191 
192             // Transfer packet masks from bit-mask to byte-mask.
193             internal::GeneratePacketMasks(
194                 num_media_packets, num_fec_packets, num_imp_packets,
195                 kUseUnequalProtection, &mask_table, packet_mask.get());
196 
197 #ifdef VERBOSE_OUTPUT
198             printf(
199                 "%u media packets, %u FEC packets, %u num_imp_packets, "
200                 "loss rate = %.2f \n",
201                 num_media_packets, num_fec_packets, num_imp_packets,
202                 loss_rate[loss_rate_idx]);
203             printf("Packet mask matrix \n");
204 #endif
205 
206             for (uint32_t i = 0; i < num_fec_packets; i++) {
207               for (uint32_t j = 0; j < num_media_packets; j++) {
208                 const uint8_t byte_mask =
209                     packet_mask[i * mask_bytes_per_fec_packet + j / 8];
210                 const uint32_t bit_position = (7 - j % 8);
211                 fec_packet_masks[i][j] =
212                     (byte_mask & (1 << bit_position)) >> bit_position;
213 #ifdef VERBOSE_OUTPUT
214                 printf("%u ", fec_packet_masks[i][j]);
215 #endif
216               }
217 #ifdef VERBOSE_OUTPUT
218               printf("\n");
219 #endif
220             }
221 #ifdef VERBOSE_OUTPUT
222             printf("\n");
223 #endif
224             // Check for all zero rows or columns: indicates incorrect mask.
225             uint32_t row_limit = num_media_packets;
226             for (uint32_t i = 0; i < num_fec_packets; ++i) {
227               uint32_t row_sum = 0;
228               for (uint32_t j = 0; j < row_limit; ++j) {
229                 row_sum += fec_packet_masks[i][j];
230               }
231               ASSERT_NE(0u, row_sum) << "Row is all zero " << i;
232             }
233             for (uint32_t j = 0; j < row_limit; ++j) {
234               uint32_t column_sum = 0;
235               for (uint32_t i = 0; i < num_fec_packets; ++i) {
236                 column_sum += fec_packet_masks[i][j];
237               }
238               ASSERT_NE(0u, column_sum) << "Column is all zero " << j;
239             }
240 
241             // Construct media packets.
242             // Reset the sequence number here for each FEC code/mask tested
243             // below, to avoid sequence number wrap-around. In actual decoding,
244             // old FEC packets in list are dropped if sequence number wrap
245             // around is detected. This case is currently not handled below.
246             seq_num = 0;
247             for (uint32_t i = 0; i < num_media_packets; ++i) {
248               std::unique_ptr<ForwardErrorCorrection::Packet> media_packet(
249                   new ForwardErrorCorrection::Packet());
250               const uint32_t kMinPacketSize = 12;
251               const uint32_t kMaxPacketSize = static_cast<uint32_t>(
252                   IP_PACKET_SIZE - 12 - 28 - fec->MaxPacketOverhead());
253               size_t packet_length =
254                   random.Rand(kMinPacketSize, kMaxPacketSize);
255               media_packet->data.SetSize(packet_length);
256 
257               uint8_t* data = media_packet->data.MutableData();
258               // Generate random values for the first 2 bytes.
259               data[0] = random.Rand<uint8_t>();
260               data[1] = random.Rand<uint8_t>();
261 
262               // The first two bits are assumed to be 10 by the
263               // FEC encoder. In fact the FEC decoder will set the
264               // two first bits to 10 regardless of what they
265               // actually were. Set the first two bits to 10
266               // so that a memcmp can be performed for the
267               // whole restored packet.
268               data[0] |= 0x80;
269               data[0] &= 0xbf;
270 
271               // FEC is applied to a whole frame.
272               // A frame is signaled by multiple packets without
273               // the marker bit set followed by the last packet of
274               // the frame for which the marker bit is set.
275               // Only push one (fake) frame to the FEC.
276               data[1] &= 0x7f;
277 
278               ByteWriter<uint16_t>::WriteBigEndian(&data[2], seq_num);
279               ByteWriter<uint32_t>::WriteBigEndian(&data[4], timestamp);
280               ByteWriter<uint32_t>::WriteBigEndian(&data[8], media_ssrc);
281               // Generate random values for payload
282               for (size_t j = 12; j < packet_length; ++j) {
283                 data[j] = random.Rand<uint8_t>();
284               }
285               media_packet_list.push_back(std::move(media_packet));
286               seq_num++;
287             }
288             media_packet_list.back()->data.MutableData()[1] |= 0x80;
289 
290             ASSERT_EQ(0, fec->EncodeFec(media_packet_list, protection_factor,
291                                         num_imp_packets, kUseUnequalProtection,
292                                         fec_mask_type, &fec_packet_list))
293                 << "EncodeFec() failed";
294 
295             ASSERT_EQ(num_fec_packets, fec_packet_list.size())
296                 << "We requested " << num_fec_packets
297                 << " FEC packets, but "
298                    "EncodeFec() produced "
299                 << fec_packet_list.size();
300 
301             memset(media_loss_mask, 0, sizeof(media_loss_mask));
302             uint32_t media_packet_idx = 0;
303             for (const auto& media_packet : media_packet_list) {
304               // We want a value between 0 and 1.
305               const float loss_random_variable = random.Rand<float>();
306 
307               if (loss_random_variable >= loss_rate[loss_rate_idx]) {
308                 media_loss_mask[media_packet_idx] = 1;
309                 std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>
310                     received_packet(
311                         new ForwardErrorCorrection::ReceivedPacket());
312                 received_packet->pkt = new ForwardErrorCorrection::Packet();
313                 received_packet->pkt->data = media_packet->data;
314                 received_packet->ssrc = media_ssrc;
315                 received_packet->seq_num = ByteReader<uint16_t>::ReadBigEndian(
316                     media_packet->data.data() + 2);
317                 received_packet->is_fec = false;
318                 received_packet_list.push_back(std::move(received_packet));
319               }
320               media_packet_idx++;
321             }
322 
323             memset(fec_loss_mask, 0, sizeof(fec_loss_mask));
324             uint32_t fec_packet_idx = 0;
325             for (auto* fec_packet : fec_packet_list) {
326               const float loss_random_variable = random.Rand<float>();
327               if (loss_random_variable >= loss_rate[loss_rate_idx]) {
328                 fec_loss_mask[fec_packet_idx] = 1;
329                 std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>
330                     received_packet(
331                         new ForwardErrorCorrection::ReceivedPacket());
332                 received_packet->pkt = new ForwardErrorCorrection::Packet();
333                 received_packet->pkt->data = fec_packet->data;
334                 received_packet->seq_num = fec_seq_num_offset + seq_num;
335                 received_packet->is_fec = true;
336                 received_packet->ssrc = fec_ssrc;
337                 received_packet_list.push_back(std::move(received_packet));
338 
339                 fec_mask_list.push_back(fec_packet_masks[fec_packet_idx]);
340               }
341               ++fec_packet_idx;
342               ++seq_num;
343             }
344 
345 #ifdef VERBOSE_OUTPUT
346             printf("Media loss mask:\n");
347             for (uint32_t i = 0; i < num_media_packets; i++) {
348               printf("%u ", media_loss_mask[i]);
349             }
350             printf("\n\n");
351 
352             printf("FEC loss mask:\n");
353             for (uint32_t i = 0; i < num_fec_packets; i++) {
354               printf("%u ", fec_loss_mask[i]);
355             }
356             printf("\n\n");
357 #endif
358 
359             auto fec_mask_it = fec_mask_list.begin();
360             while (fec_mask_it != fec_mask_list.end()) {
361               uint32_t hamming_dist = 0;
362               uint32_t recovery_position = 0;
363               for (uint32_t i = 0; i < num_media_packets; i++) {
364                 if (media_loss_mask[i] == 0 && (*fec_mask_it)[i] == 1) {
365                   recovery_position = i;
366                   ++hamming_dist;
367                 }
368               }
369               auto item_to_delete = fec_mask_it;
370               ++fec_mask_it;
371 
372               if (hamming_dist == 1) {
373                 // Recovery possible. Restart search.
374                 media_loss_mask[recovery_position] = 1;
375                 fec_mask_it = fec_mask_list.begin();
376               } else if (hamming_dist == 0) {
377                 // FEC packet cannot provide further recovery.
378                 fec_mask_list.erase(item_to_delete);
379               }
380             }
381 #ifdef VERBOSE_OUTPUT
382             printf("Recovery mask:\n");
383             for (uint32_t i = 0; i < num_media_packets; ++i) {
384               printf("%u ", media_loss_mask[i]);
385             }
386             printf("\n\n");
387 #endif
388             // For error-checking frame completion.
389             bool fec_packet_received = false;
390             while (!received_packet_list.empty()) {
391               size_t num_packets_to_decode = random.Rand(
392                   1u, static_cast<uint32_t>(received_packet_list.size()));
393               ReceivePackets(&to_decode_list, &received_packet_list,
394                              num_packets_to_decode, reorder_rate,
395                              duplicate_rate, &random);
396 
397               if (fec_packet_received == false) {
398                 for (const auto& received_packet : to_decode_list) {
399                   if (received_packet->is_fec) {
400                     fec_packet_received = true;
401                   }
402                 }
403               }
404               for (const auto& received_packet : to_decode_list) {
405                 fec->DecodeFec(*received_packet, &recovered_packet_list);
406               }
407               to_decode_list.clear();
408             }
409             media_packet_idx = 0;
410             for (const auto& media_packet : media_packet_list) {
411               if (media_loss_mask[media_packet_idx] == 1) {
412                 // Should have recovered this packet.
413                 auto recovered_packet_list_it = recovered_packet_list.cbegin();
414 
415                 ASSERT_FALSE(recovered_packet_list_it ==
416                              recovered_packet_list.end())
417                     << "Insufficient number of recovered packets.";
418                 ForwardErrorCorrection::RecoveredPacket* recovered_packet =
419                     recovered_packet_list_it->get();
420 
421                 ASSERT_EQ(recovered_packet->pkt->data.size(),
422                           media_packet->data.size())
423                     << "Recovered packet length not identical to original "
424                        "media packet";
425                 ASSERT_EQ(0, memcmp(recovered_packet->pkt->data.cdata(),
426                                     media_packet->data.cdata(),
427                                     media_packet->data.size()))
428                     << "Recovered packet payload not identical to original "
429                        "media packet";
430                 recovered_packet_list.pop_front();
431               }
432               ++media_packet_idx;
433             }
434             fec->ResetState(&recovered_packet_list);
435             ASSERT_TRUE(recovered_packet_list.empty())
436                 << "Excessive number of recovered packets.\t size is: "
437                 << recovered_packet_list.size();
438             // -- Teardown --
439             media_packet_list.clear();
440 
441             // Clear FEC packet list, so we don't pass in a non-empty
442             // list in the next call to DecodeFec().
443             fec_packet_list.clear();
444 
445             // Delete received packets we didn't pass to DecodeFec(), due to
446             // early frame completion.
447             received_packet_list.clear();
448 
449             while (!fec_mask_list.empty()) {
450               fec_mask_list.pop_front();
451             }
452             timestamp += 90000 / 30;
453           }  // loop over num_imp_packets
454         }    // loop over FecPackets
455       }      // loop over num_media_packets
456     }        // loop over loss rates
457   }          // loop over mask types
458 
459   // Have DecodeFec clear the recovered packet list.
460   fec->ResetState(&recovered_packet_list);
461   ASSERT_TRUE(recovered_packet_list.empty())
462       << "Recovered packet list is not empty";
463 }
464 
TEST(FecTest,UlpfecTest)465 TEST(FecTest, UlpfecTest) {
466   RunTest(false);
467 }
468 
TEST(FecTest,FlexfecTest)469 TEST(FecTest, FlexfecTest) {
470   RunTest(true);
471 }
472 
473 }  // namespace test
474 }  // namespace webrtc
475