/* * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "modules/rtp_rtcp/source/forward_error_correction_internal.h" #include <string.h> #include <algorithm> #include "modules/rtp_rtcp/source/fec_private_tables_bursty.h" #include "modules/rtp_rtcp/source/fec_private_tables_random.h" #include "rtc_base/checks.h" namespace { // Allow for different modes of protection for packets in UEP case. enum ProtectionMode { … }; // Fits an input mask (sub_mask) to an output mask. // The mask is a matrix where the rows are the FEC packets, // and the columns are the source packets the FEC is applied to. // Each row of the mask is represented by a number of mask bytes. // // \param[in] num_mask_bytes The number of mask bytes of output mask. // \param[in] num_sub_mask_bytes The number of mask bytes of input mask. // \param[in] num_rows The number of rows of the input mask. // \param[in] sub_mask A pointer to hold the input mask, of size // [0, num_rows * num_sub_mask_bytes] // \param[out] packet_mask A pointer to hold the output mask, of size // [0, x * num_mask_bytes], where x >= num_rows. void FitSubMask(int num_mask_bytes, int num_sub_mask_bytes, int num_rows, const uint8_t* sub_mask, uint8_t* packet_mask) { … } // Shifts a mask by number of columns (bits), and fits it to an output mask. // The mask is a matrix where the rows are the FEC packets, // and the columns are the source packets the FEC is applied to. // Each row of the mask is represented by a number of mask bytes. // // \param[in] num_mask_bytes The number of mask bytes of output mask. // \param[in] num_sub_mask_bytes The number of mask bytes of input mask. // \param[in] num_column_shift The number columns to be shifted, and // the starting row for the output mask. // \param[in] end_row The ending row for the output mask. // \param[in] sub_mask A pointer to hold the input mask, of size // [0, (end_row_fec - start_row_fec) * // num_sub_mask_bytes] // \param[out] packet_mask A pointer to hold the output mask, of size // [0, x * num_mask_bytes], // where x >= end_row_fec. // TODO(marpan): This function is doing three things at the same time: // shift within a byte, byte shift and resizing. // Split up into subroutines. void ShiftFitSubMask(int num_mask_bytes, int res_mask_bytes, int num_column_shift, int end_row, const uint8_t* sub_mask, uint8_t* packet_mask) { … } } // namespace namespace webrtc { namespace internal { PacketMaskTable::PacketMaskTable(FecMaskType fec_mask_type, int num_media_packets) : … { … } PacketMaskTable::~PacketMaskTable() = default; rtc::ArrayView<const uint8_t> PacketMaskTable::LookUp(int num_media_packets, int num_fec_packets) { … } // If `num_media_packets` is larger than the maximum allowed by `fec_mask_type` // for the bursty type, or the random table is explicitly asked for, then the // random type is selected. Otherwise the bursty table callback is returned. const uint8_t* PacketMaskTable::PickTable(FecMaskType fec_mask_type, int num_media_packets) { … } // Remaining protection after important (first partition) packet protection void RemainingPacketProtection(int num_media_packets, int num_fec_remaining, int num_fec_for_imp_packets, int num_mask_bytes, ProtectionMode mode, uint8_t* packet_mask, PacketMaskTable* mask_table) { … } // Protection for important (first partition) packets void ImportantPacketProtection(int num_fec_for_imp_packets, int num_imp_packets, int num_mask_bytes, uint8_t* packet_mask, PacketMaskTable* mask_table) { … } // This function sets the protection allocation: i.e., how many FEC packets // to use for num_imp (1st partition) packets, given the: number of media // packets, number of FEC packets, and number of 1st partition packets. int SetProtectionAllocation(int num_media_packets, int num_fec_packets, int num_imp_packets) { … } // Modification for UEP: reuse the off-line tables for the packet masks. // Note: these masks were designed for equal packet protection case, // assuming random packet loss. // Current version has 3 modes (options) to build UEP mask from existing ones. // Various other combinations may be added in future versions. // Longer-term, we may add another set of tables specifically for UEP cases. // TODO(marpan): also consider modification of masks for bursty loss cases. // Mask is characterized as (#packets_to_protect, #fec_for_protection). // Protection factor defined as: (#fec_for_protection / #packets_to_protect). // Let k=num_media_packets, n=total#packets, (n-k)=num_fec_packets, // m=num_imp_packets. // For ProtectionMode 0 and 1: // one mask (sub_mask1) is used for 1st partition packets, // the other mask (sub_mask21/22, for 0/1) is for the remaining FEC packets. // In both mode 0 and 1, the packets of 1st partition (num_imp_packets) are // treated equally important, and are afforded more protection than the // residual partition packets. // For num_imp_packets: // sub_mask1 = (m, t): protection = t/(m), where t=F(k,n-k,m). // t=F(k,n-k,m) is the number of packets used to protect first partition in // sub_mask1. This is determined from the function SetProtectionAllocation(). // For the left-over protection: // Mode 0: sub_mask21 = (k-m,n-k-t): protection = (n-k-t)/(k-m) // mode 0 has no protection overlap between the two partitions. // For mode 0, we would typically set t = min(m, n-k). // Mode 1: sub_mask22 = (k, n-k-t), with protection (n-k-t)/(k) // mode 1 has protection overlap between the two partitions (preferred). // For ProtectionMode 2: // This gives 1st packet of list (which is 1st packet of 1st partition) more // protection. In mode 2, the equal protection mask (which is obtained from // mode 1 for t=0) is modified (more "1s" added in 1st column of packet mask) // to bias higher protection for the 1st source packet. // Protection Mode 2 may be extended for a sort of sliding protection // (i.e., vary the number/density of "1s" across columns) across packets. void UnequalProtectionMask(int num_media_packets, int num_fec_packets, int num_imp_packets, int num_mask_bytes, uint8_t* packet_mask, PacketMaskTable* mask_table) { … } // This algorithm is tailored to look up data in the `kPacketMaskRandomTbl` and // `kPacketMaskBurstyTbl` tables. These tables only cover fec code for up to 12 // media packets. Starting from 13 media packets, the fec code will be generated // at runtime. The format of those arrays is that they're essentially a 3 // dimensional array with the following dimensions: * media packet // * Size for kPacketMaskRandomTbl: 12 // * Size for kPacketMaskBurstyTbl: 12 // * fec index // * Size for both random and bursty table increases from 1 to number of rows. // (i.e. 1-48, or 1-12 respectively). // * Fec data (what actually gets returned) // * Size for kPacketMaskRandomTbl: 2 bytes. // * For all entries: 2 * fec index (1 based) // * Size for kPacketMaskBurstyTbl: 2 bytes. // * For all entries: 2 * fec index (1 based) rtc::ArrayView<const uint8_t> LookUpInFecTable(const uint8_t* table, int media_packet_index, int fec_index) { … } void GeneratePacketMasks(int num_media_packets, int num_fec_packets, int num_imp_packets, bool use_unequal_protection, PacketMaskTable* mask_table, uint8_t* packet_mask) { … } // End of GetPacketMasks size_t PacketMaskSize(size_t num_sequence_numbers) { … } void InsertZeroColumns(int num_zeros, uint8_t* new_mask, int new_mask_bytes, int num_fec_packets, int new_bit_index) { … } void CopyColumn(uint8_t* new_mask, int new_mask_bytes, uint8_t* old_mask, int old_mask_bytes, int num_fec_packets, int new_bit_index, int old_bit_index) { … } } // namespace internal } // namespace webrtc