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