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 #include "webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h" 12 13 #include <assert.h> 14 #include <string.h> 15 16 #include "webrtc/modules/rtp_rtcp/source/fec_private_tables_bursty.h" 17 #include "webrtc/modules/rtp_rtcp/source/fec_private_tables_random.h" 18 19 namespace { 20 using webrtc::fec_private_tables::kPacketMaskBurstyTbl; 21 using webrtc::fec_private_tables::kPacketMaskRandomTbl; 22 23 // Allow for different modes of protection for packets in UEP case. 24 enum ProtectionMode { 25 kModeNoOverlap, 26 kModeOverlap, 27 kModeBiasFirstPacket, 28 }; 29 30 // Fits an input mask (sub_mask) to an output mask. 31 // The mask is a matrix where the rows are the FEC packets, 32 // and the columns are the source packets the FEC is applied to. 33 // Each row of the mask is represented by a number of mask bytes. 34 // 35 // \param[in] num_mask_bytes The number of mask bytes of output mask. 36 // \param[in] num_sub_mask_bytes The number of mask bytes of input mask. 37 // \param[in] num_rows The number of rows of the input mask. 38 // \param[in] sub_mask A pointer to hold the input mask, of size 39 // [0, num_rows * num_sub_mask_bytes] 40 // \param[out] packet_mask A pointer to hold the output mask, of size 41 // [0, x * num_mask_bytes], where x >= num_rows. 42 void FitSubMask(int num_mask_bytes, 43 int num_sub_mask_bytes, 44 int num_rows, 45 const uint8_t* sub_mask, 46 uint8_t* packet_mask) { 47 if (num_mask_bytes == num_sub_mask_bytes) { 48 memcpy(packet_mask, sub_mask, num_rows * num_sub_mask_bytes); 49 } else { 50 for (int i = 0; i < num_rows; ++i) { 51 int pkt_mask_idx = i * num_mask_bytes; 52 int pkt_mask_idx2 = i * num_sub_mask_bytes; 53 for (int j = 0; j < num_sub_mask_bytes; ++j) { 54 packet_mask[pkt_mask_idx] = sub_mask[pkt_mask_idx2]; 55 pkt_mask_idx++; 56 pkt_mask_idx2++; 57 } 58 } 59 } 60 } 61 62 // Shifts a mask by number of columns (bits), and fits it to an output mask. 63 // The mask is a matrix where the rows are the FEC packets, 64 // and the columns are the source packets the FEC is applied to. 65 // Each row of the mask is represented by a number of mask bytes. 66 // 67 // \param[in] num_mask_bytes The number of mask bytes of output mask. 68 // \param[in] num_sub_mask_bytes The number of mask bytes of input mask. 69 // \param[in] num_column_shift The number columns to be shifted, and 70 // the starting row for the output mask. 71 // \param[in] end_row The ending row for the output mask. 72 // \param[in] sub_mask A pointer to hold the input mask, of size 73 // [0, (end_row_fec - start_row_fec) * 74 // num_sub_mask_bytes] 75 // \param[out] packet_mask A pointer to hold the output mask, of size 76 // [0, x * num_mask_bytes], 77 // where x >= end_row_fec. 78 // TODO(marpan): This function is doing three things at the same time: 79 // shift within a byte, byte shift and resizing. 80 // Split up into subroutines. 81 void ShiftFitSubMask(int num_mask_bytes, 82 int res_mask_bytes, 83 int num_column_shift, 84 int end_row, 85 const uint8_t* sub_mask, 86 uint8_t* packet_mask) { 87 // Number of bit shifts within a byte 88 const int num_bit_shifts = (num_column_shift % 8); 89 const int num_byte_shifts = num_column_shift >> 3; 90 91 // Modify new mask with sub-mask21. 92 93 // Loop over the remaining FEC packets. 94 for (int i = num_column_shift; i < end_row; ++i) { 95 // Byte index of new mask, for row i and column res_mask_bytes, 96 // offset by the number of bytes shifts 97 int pkt_mask_idx = 98 i * num_mask_bytes + res_mask_bytes - 1 + num_byte_shifts; 99 // Byte index of sub_mask, for row i and column res_mask_bytes 100 int pkt_mask_idx2 = 101 (i - num_column_shift) * res_mask_bytes + res_mask_bytes - 1; 102 103 uint8_t shift_right_curr_byte = 0; 104 uint8_t shift_left_prev_byte = 0; 105 uint8_t comb_new_byte = 0; 106 107 // Handle case of num_mask_bytes > res_mask_bytes: 108 // For a given row, copy the rightmost "numBitShifts" bits 109 // of the last byte of sub_mask into output mask. 110 if (num_mask_bytes > res_mask_bytes) { 111 shift_left_prev_byte = (sub_mask[pkt_mask_idx2] << (8 - num_bit_shifts)); 112 packet_mask[pkt_mask_idx + 1] = shift_left_prev_byte; 113 } 114 115 // For each row i (FEC packet), shift the bit-mask of the sub_mask. 116 // Each row of the mask contains "resMaskBytes" of bytes. 117 // We start from the last byte of the sub_mask and move to first one. 118 for (int j = res_mask_bytes - 1; j > 0; j--) { 119 // Shift current byte of sub21 to the right by "numBitShifts". 120 shift_right_curr_byte = sub_mask[pkt_mask_idx2] >> num_bit_shifts; 121 122 // Fill in shifted bits with bits from the previous (left) byte: 123 // First shift the previous byte to the left by "8-numBitShifts". 124 shift_left_prev_byte = 125 (sub_mask[pkt_mask_idx2 - 1] << (8 - num_bit_shifts)); 126 127 // Then combine both shifted bytes into new mask byte. 128 comb_new_byte = shift_right_curr_byte | shift_left_prev_byte; 129 130 // Assign to new mask. 131 packet_mask[pkt_mask_idx] = comb_new_byte; 132 pkt_mask_idx--; 133 pkt_mask_idx2--; 134 } 135 // For the first byte in the row (j=0 case). 136 shift_right_curr_byte = sub_mask[pkt_mask_idx2] >> num_bit_shifts; 137 packet_mask[pkt_mask_idx] = shift_right_curr_byte; 138 } 139 } 140 } // namespace 141 142 namespace webrtc { 143 namespace internal { 144 145 PacketMaskTable::PacketMaskTable(FecMaskType fec_mask_type, 146 int num_media_packets) 147 : fec_mask_type_(InitMaskType(fec_mask_type, num_media_packets)), 148 fec_packet_mask_table_(InitMaskTable(fec_mask_type_)) {} 149 150 // Sets |fec_mask_type_| to the type of packet mask selected. The type of 151 // packet mask selected is based on |fec_mask_type| and |num_media_packets|. 152 // If |num_media_packets| is larger than the maximum allowed by |fec_mask_type| 153 // for the bursty type, then the random type is selected. 154 FecMaskType PacketMaskTable::InitMaskType(FecMaskType fec_mask_type, 155 int num_media_packets) { 156 // The mask should not be bigger than |packetMaskTbl|. 157 assert(num_media_packets <= static_cast<int>(sizeof(kPacketMaskRandomTbl) / 158 sizeof(*kPacketMaskRandomTbl))); 159 switch (fec_mask_type) { 160 case kFecMaskRandom: { 161 return kFecMaskRandom; 162 } 163 case kFecMaskBursty: { 164 int max_media_packets = static_cast<int>(sizeof(kPacketMaskBurstyTbl) / 165 sizeof(*kPacketMaskBurstyTbl)); 166 if (num_media_packets > max_media_packets) { 167 return kFecMaskRandom; 168 } else { 169 return kFecMaskBursty; 170 } 171 } 172 } 173 assert(false); 174 return kFecMaskRandom; 175 } 176 177 // Returns the pointer to the packet mask tables corresponding to type 178 // |fec_mask_type|. 179 const uint8_t*** PacketMaskTable::InitMaskTable(FecMaskType fec_mask_type) { 180 switch (fec_mask_type) { 181 case kFecMaskRandom: { 182 return kPacketMaskRandomTbl; 183 } 184 case kFecMaskBursty: { 185 return kPacketMaskBurstyTbl; 186 } 187 } 188 assert(false); 189 return kPacketMaskRandomTbl; 190 } 191 192 // Remaining protection after important (first partition) packet protection 193 void RemainingPacketProtection(int num_media_packets, 194 int num_fec_remaining, 195 int num_fec_for_imp_packets, 196 int num_mask_bytes, 197 ProtectionMode mode, 198 uint8_t* packet_mask, 199 const PacketMaskTable& mask_table) { 200 if (mode == kModeNoOverlap) { 201 // sub_mask21 202 203 const int l_bit = 204 (num_media_packets - num_fec_for_imp_packets) > 16 ? 1 : 0; 205 206 const int res_mask_bytes = 207 (l_bit == 1) ? kMaskSizeLBitSet : kMaskSizeLBitClear; 208 209 const uint8_t* packet_mask_sub_21 = 210 mask_table.fec_packet_mask_table()[num_media_packets - 211 num_fec_for_imp_packets - 212 1][num_fec_remaining - 1]; 213 214 ShiftFitSubMask(num_mask_bytes, res_mask_bytes, num_fec_for_imp_packets, 215 (num_fec_for_imp_packets + num_fec_remaining), 216 packet_mask_sub_21, packet_mask); 217 218 } else if (mode == kModeOverlap || mode == kModeBiasFirstPacket) { 219 // sub_mask22 220 221 const uint8_t* packet_mask_sub_22 = 222 mask_table.fec_packet_mask_table()[num_media_packets - 223 1][num_fec_remaining - 1]; 224 225 FitSubMask(num_mask_bytes, num_mask_bytes, num_fec_remaining, 226 packet_mask_sub_22, 227 &packet_mask[num_fec_for_imp_packets * num_mask_bytes]); 228 229 if (mode == kModeBiasFirstPacket) { 230 for (int i = 0; i < num_fec_remaining; ++i) { 231 int pkt_mask_idx = i * num_mask_bytes; 232 packet_mask[pkt_mask_idx] = packet_mask[pkt_mask_idx] | (1 << 7); 233 } 234 } 235 } else { 236 assert(false); 237 } 238 } 239 240 // Protection for important (first partition) packets 241 void ImportantPacketProtection(int num_fec_for_imp_packets, 242 int num_imp_packets, 243 int num_mask_bytes, 244 uint8_t* packet_mask, 245 const PacketMaskTable& mask_table) { 246 const int l_bit = num_imp_packets > 16 ? 1 : 0; 247 const int num_imp_mask_bytes = 248 (l_bit == 1) ? kMaskSizeLBitSet : kMaskSizeLBitClear; 249 250 // Get sub_mask1 from table 251 const uint8_t* packet_mask_sub_1 = 252 mask_table.fec_packet_mask_table()[num_imp_packets - 253 1][num_fec_for_imp_packets - 1]; 254 255 FitSubMask(num_mask_bytes, num_imp_mask_bytes, num_fec_for_imp_packets, 256 packet_mask_sub_1, packet_mask); 257 } 258 259 // This function sets the protection allocation: i.e., how many FEC packets 260 // to use for num_imp (1st partition) packets, given the: number of media 261 // packets, number of FEC packets, and number of 1st partition packets. 262 int SetProtectionAllocation(int num_media_packets, 263 int num_fec_packets, 264 int num_imp_packets) { 265 // TODO(marpan): test different cases for protection allocation: 266 267 // Use at most (alloc_par * num_fec_packets) for important packets. 268 float alloc_par = 0.5; 269 int max_num_fec_for_imp = alloc_par * num_fec_packets; 270 271 int num_fec_for_imp_packets = (num_imp_packets < max_num_fec_for_imp) 272 ? num_imp_packets 273 : max_num_fec_for_imp; 274 275 // Fall back to equal protection in this case 276 if (num_fec_packets == 1 && (num_media_packets > 2 * num_imp_packets)) { 277 num_fec_for_imp_packets = 0; 278 } 279 280 return num_fec_for_imp_packets; 281 } 282 283 // Modification for UEP: reuse the off-line tables for the packet masks. 284 // Note: these masks were designed for equal packet protection case, 285 // assuming random packet loss. 286 287 // Current version has 3 modes (options) to build UEP mask from existing ones. 288 // Various other combinations may be added in future versions. 289 // Longer-term, we may add another set of tables specifically for UEP cases. 290 // TODO(marpan): also consider modification of masks for bursty loss cases. 291 292 // Mask is characterized as (#packets_to_protect, #fec_for_protection). 293 // Protection factor defined as: (#fec_for_protection / #packets_to_protect). 294 295 // Let k=num_media_packets, n=total#packets, (n-k)=num_fec_packets, 296 // m=num_imp_packets. 297 298 // For ProtectionMode 0 and 1: 299 // one mask (sub_mask1) is used for 1st partition packets, 300 // the other mask (sub_mask21/22, for 0/1) is for the remaining FEC packets. 301 302 // In both mode 0 and 1, the packets of 1st partition (num_imp_packets) are 303 // treated equally important, and are afforded more protection than the 304 // residual partition packets. 305 306 // For num_imp_packets: 307 // sub_mask1 = (m, t): protection = t/(m), where t=F(k,n-k,m). 308 // t=F(k,n-k,m) is the number of packets used to protect first partition in 309 // sub_mask1. This is determined from the function SetProtectionAllocation(). 310 311 // For the left-over protection: 312 // Mode 0: sub_mask21 = (k-m,n-k-t): protection = (n-k-t)/(k-m) 313 // mode 0 has no protection overlap between the two partitions. 314 // For mode 0, we would typically set t = min(m, n-k). 315 316 // Mode 1: sub_mask22 = (k, n-k-t), with protection (n-k-t)/(k) 317 // mode 1 has protection overlap between the two partitions (preferred). 318 319 // For ProtectionMode 2: 320 // This gives 1st packet of list (which is 1st packet of 1st partition) more 321 // protection. In mode 2, the equal protection mask (which is obtained from 322 // mode 1 for t=0) is modified (more "1s" added in 1st column of packet mask) 323 // to bias higher protection for the 1st source packet. 324 325 // Protection Mode 2 may be extended for a sort of sliding protection 326 // (i.e., vary the number/density of "1s" across columns) across packets. 327 328 void UnequalProtectionMask(int num_media_packets, 329 int num_fec_packets, 330 int num_imp_packets, 331 int num_mask_bytes, 332 uint8_t* packet_mask, 333 const PacketMaskTable& mask_table) { 334 // Set Protection type and allocation 335 // TODO(marpan): test/update for best mode and some combinations thereof. 336 337 ProtectionMode mode = kModeOverlap; 338 int num_fec_for_imp_packets = 0; 339 340 if (mode != kModeBiasFirstPacket) { 341 num_fec_for_imp_packets = SetProtectionAllocation( 342 num_media_packets, num_fec_packets, num_imp_packets); 343 } 344 345 int num_fec_remaining = num_fec_packets - num_fec_for_imp_packets; 346 // Done with setting protection type and allocation 347 348 // 349 // Generate sub_mask1 350 // 351 if (num_fec_for_imp_packets > 0) { 352 ImportantPacketProtection(num_fec_for_imp_packets, num_imp_packets, 353 num_mask_bytes, packet_mask, mask_table); 354 } 355 356 // 357 // Generate sub_mask2 358 // 359 if (num_fec_remaining > 0) { 360 RemainingPacketProtection(num_media_packets, num_fec_remaining, 361 num_fec_for_imp_packets, num_mask_bytes, mode, 362 packet_mask, mask_table); 363 } 364 } 365 366 void GeneratePacketMasks(int num_media_packets, 367 int num_fec_packets, 368 int num_imp_packets, 369 bool use_unequal_protection, 370 const PacketMaskTable& mask_table, 371 uint8_t* packet_mask) { 372 assert(num_media_packets > 0); 373 assert(num_fec_packets <= num_media_packets && num_fec_packets > 0); 374 assert(num_imp_packets <= num_media_packets && num_imp_packets >= 0); 375 376 int l_bit = num_media_packets > 16 ? 1 : 0; 377 const int num_mask_bytes = 378 (l_bit == 1) ? kMaskSizeLBitSet : kMaskSizeLBitClear; 379 380 // Equal-protection for these cases. 381 if (!use_unequal_protection || num_imp_packets == 0) { 382 // Retrieve corresponding mask table directly:for equal-protection case. 383 // Mask = (k,n-k), with protection factor = (n-k)/k, 384 // where k = num_media_packets, n=total#packets, (n-k)=num_fec_packets. 385 memcpy(packet_mask, 386 mask_table.fec_packet_mask_table()[num_media_packets - 387 1][num_fec_packets - 1], 388 num_fec_packets * num_mask_bytes); 389 } else { // UEP case 390 UnequalProtectionMask(num_media_packets, num_fec_packets, num_imp_packets, 391 num_mask_bytes, packet_mask, mask_table); 392 } // End of UEP modification 393 } // End of GetPacketMasks 394 395 } // namespace internal 396 } // namespace webrtc 397