Home | History | Annotate | Download | only in testFec
      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  * The purpose of this test is to compute metrics to characterize the properties
     13  * and efficiency of the packets masks used in the generic XOR FEC code.
     14  *
     15  * The metrics measure the efficiency (recovery potential or residual loss) of
     16  * the FEC code, under various statistical loss models for the packet/symbol
     17  * loss events. Various constraints on the behavior of these metrics are
     18  * verified, and compared to the reference RS (Reed-Solomon) code. This serves
     19  * in some way as a basic check/benchmark for the packet masks.
     20  *
     21  * By an FEC code, we mean an erasure packet/symbol code, characterized by:
     22  * (1) The code size parameters (k,m), where k = number of source/media packets,
     23  * and m = number of FEC packets,
     24  * (2) The code type: XOR or RS.
     25  * In the case of XOR, the residual loss is determined via the set of packet
     26  * masks (generator matrix). In the case of RS, the residual loss is determined
     27  * directly from the MDS (maximum distance separable) property of RS.
     28  *
     29  * Currently two classes of packets masks are available (random type and bursty
     30  * type), so three codes are considered below: RS, XOR-random, and XOR-bursty.
     31  * The bursty class is defined up to k=12, so (k=12,m=12) is largest code size
     32  * considered in this test.
     33  *
     34  * The XOR codes are defined via the RFC 5109 and correspond to the class of
     35  * LDGM (low density generator matrix) codes, which is a subset of the LDPC
     36  * (low density parity check) codes. Future implementation will consider
     37  * extending our XOR codes to include LDPC codes, which explicitly include
     38  * protection of FEC packets.
     39  *
     40  * The type of packet/symbol loss models considered in this test are:
     41  * (1) Random loss: Bernoulli process, characterized by the average loss rate.
     42  * (2) Bursty loss: Markov chain (Gilbert-Elliot model), characterized by two
     43  * parameters: average loss rate and average burst length.
     44 */
     45 
     46 #include <math.h>
     47 
     48 #include "testing/gtest/include/gtest/gtest.h"
     49 #include "webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h"
     50 #include "webrtc/modules/rtp_rtcp/test/testFec/average_residual_loss_xor_codes.h"
     51 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
     52 #include "webrtc/test/testsupport/fileutils.h"
     53 
     54 namespace webrtc {
     55 
     56 // Maximum number of media packets allows for XOR (RFC 5109) code.
     57 enum { kMaxNumberMediaPackets = 48 };
     58 
     59 // Maximum number of media packets allowed for each mask type.
     60 const uint16_t kMaxMediaPackets[] = {kMaxNumberMediaPackets, 12};
     61 
     62 // Maximum number of media packets allowed in this test. The burst mask types
     63 // are currently defined up to (k=12,m=12).
     64 const int kMaxMediaPacketsTest = 12;
     65 
     66 // Maximum number of FEC codes considered in this test.
     67 const int kNumberCodes = kMaxMediaPacketsTest * (kMaxMediaPacketsTest + 1) / 2;
     68 
     69 // Maximum gap size for characterizing the consecutiveness of the loss.
     70 const int kMaxGapSize = 2 * kMaxMediaPacketsTest;
     71 
     72 // Number of gap levels written to file/output.
     73 const int kGapSizeOutput = 5;
     74 
     75 // Maximum number of states for characterizing the residual loss distribution.
     76 const int kNumStatesDistribution = 2 * kMaxMediaPacketsTest * kMaxGapSize + 1;
     77 
     78 // The code type.
     79 enum CodeType {
     80   xor_random_code,    // XOR with random mask type.
     81   xor_bursty_code,    // XOR with bursty mask type.
     82   rs_code             // Reed_solomon.
     83 };
     84 
     85 // The code size parameters.
     86 struct CodeSizeParams {
     87   int num_media_packets;
     88   int num_fec_packets;
     89   // Protection level: num_fec_packets / (num_media_packets + num_fec_packets).
     90   float protection_level;
     91   // Number of loss configurations, for a given loss number and gap number.
     92   // The gap number refers to the maximum gap/hole of a loss configuration
     93   // (used to measure the "consecutiveness" of the loss).
     94   int configuration_density[kNumStatesDistribution];
     95 };
     96 
     97 // The type of loss models.
     98 enum LossModelType {
     99   kRandomLossModel,
    100   kBurstyLossModel
    101 };
    102 
    103 struct LossModel {
    104   LossModelType loss_type;
    105   float average_loss_rate;
    106   float average_burst_length;
    107 };
    108 
    109 // Average loss rates.
    110 const float kAverageLossRate[] = { 0.025f, 0.05f, 0.1f, 0.25f };
    111 
    112 // Average burst lengths. The case of |kAverageBurstLength = 1.0| refers to
    113 // the random model. Note that for the random (Bernoulli) model, the average
    114 // burst length is determined by the average loss rate, i.e.,
    115 // AverageBurstLength = 1 / (1 - AverageLossRate) for random model.
    116 const float kAverageBurstLength[] = { 1.0f, 2.0f, 4.0f };
    117 
    118 // Total number of loss models: For each burst length case, there are
    119 // a number of models corresponding to the loss rates.
    120 const int kNumLossModels =  (sizeof(kAverageBurstLength) /
    121     sizeof(*kAverageBurstLength)) * (sizeof(kAverageLossRate) /
    122         sizeof(*kAverageLossRate));
    123 
    124 // Thresholds on the average loss rate of the packet loss model, below which
    125 // certain properties of the codes are expected.
    126 float loss_rate_upper_threshold = 0.20f;
    127 float loss_rate_lower_threshold = 0.025f;
    128 
    129 // Set of thresholds on the expected average recovery rate, for each code type.
    130 // These are global thresholds for now; in future version we may condition them
    131 // on the code length/size and protection level.
    132 const float kRecoveryRateXorRandom[3] = { 0.94f, 0.50f, 0.19f };
    133 const float kRecoveryRateXorBursty[3] = { 0.90f, 0.54f, 0.22f };
    134 
    135 // Metrics for a given FEC code; each code is defined by the code type
    136 // (RS, XOR-random/bursty), and the code size parameters (k,m), where
    137 // k = num_media_packets, m = num_fec_packets.
    138 struct MetricsFecCode {
    139   // The average and variance of the residual loss, as a function of the
    140   // packet/symbol loss model. The average/variance is computed by averaging
    141   // over all loss configurations wrt the loss probability given by the
    142   // underlying loss model.
    143   double average_residual_loss[kNumLossModels];
    144   double variance_residual_loss[kNumLossModels];
    145   // The residual loss, as a function of the loss number and the gap number of
    146   // the loss configurations. The gap number refers to the maximum gap/hole of
    147   // a loss configuration (used to measure the "consecutiveness" of the loss).
    148   double residual_loss_per_loss_gap[kNumStatesDistribution];
    149   // The recovery rate as a function of the loss number.
    150   double recovery_rate_per_loss[2 * kMaxMediaPacketsTest + 1];
    151 };
    152 
    153 MetricsFecCode kMetricsXorRandom[kNumberCodes];
    154 MetricsFecCode kMetricsXorBursty[kNumberCodes];
    155 MetricsFecCode kMetricsReedSolomon[kNumberCodes];
    156 
    157 class FecPacketMaskMetricsTest : public ::testing::Test {
    158  protected:
    159   FecPacketMaskMetricsTest() { }
    160 
    161   int max_num_codes_;
    162   LossModel loss_model_[kNumLossModels];
    163   CodeSizeParams code_params_[kNumberCodes];
    164 
    165   uint8_t fec_packet_masks_[kMaxNumberMediaPackets][kMaxNumberMediaPackets];
    166   FILE* fp_mask_;
    167 
    168   // Measure of the gap of the loss for configuration given by |state|.
    169   // This is to measure degree of consecutiveness for the loss configuration.
    170   // Useful if the packets are sent out in order of sequence numbers and there
    171   // is little/no re-ordering during transmission.
    172   int GapLoss(int tot_num_packets, uint8_t* state) {
    173     int max_gap_loss = 0;
    174     // Find the first loss.
    175     int first_loss = 0;
    176     for (int i = 0; i < tot_num_packets; i++) {
    177       if (state[i] == 1) {
    178         first_loss = i;
    179         break;
    180       }
    181     }
    182     int prev_loss = first_loss;
    183     for (int i = first_loss + 1; i < tot_num_packets; i++) {
    184       if (state[i] == 1) {  // Lost state.
    185         int gap_loss = (i - prev_loss) - 1;
    186         if (gap_loss > max_gap_loss) {
    187           max_gap_loss = gap_loss;
    188         }
    189         prev_loss = i;
    190       }
    191     }
    192     return max_gap_loss;
    193   }
    194 
    195   // Returns the number of recovered media packets for the XOR code, given the
    196   // packet mask |fec_packet_masks_|, for the loss state/configuration given by
    197   // |state|.
    198   int RecoveredMediaPackets(int num_media_packets,
    199                             int num_fec_packets,
    200                             uint8_t* state) {
    201     scoped_ptr<uint8_t[]> state_tmp(
    202         new uint8_t[num_media_packets + num_fec_packets]);
    203     memcpy(state_tmp.get(), state, num_media_packets + num_fec_packets);
    204     int num_recovered_packets = 0;
    205     bool loop_again = true;
    206     while (loop_again) {
    207       loop_again = false;
    208       bool recovered_new_packet = false;
    209       // Check if we can recover anything: loop over all possible FEC packets.
    210       for (int i = 0; i < num_fec_packets; i++) {
    211         if (state_tmp[i + num_media_packets] == 0) {
    212           // We have this FEC packet.
    213           int num_packets_in_mask = 0;
    214           int num_received_packets_in_mask = 0;
    215           for (int j = 0; j < num_media_packets; j++) {
    216             if (fec_packet_masks_[i][j] == 1) {
    217               num_packets_in_mask++;
    218               if (state_tmp[j] == 0) {
    219                 num_received_packets_in_mask++;
    220               }
    221             }
    222           }
    223           if ((num_packets_in_mask - 1) == num_received_packets_in_mask) {
    224             // We can recover the missing media packet for this FEC packet.
    225             num_recovered_packets++;
    226             recovered_new_packet = true;
    227             int jsel = -1;
    228             int check_num_recovered = 0;
    229             // Update the state with newly recovered media packet.
    230             for (int j = 0; j < num_media_packets; j++) {
    231               if (fec_packet_masks_[i][j] == 1 && state_tmp[j] == 1) {
    232                 // This is the lost media packet we will recover.
    233                 jsel = j;
    234                 check_num_recovered++;
    235               }
    236             }
    237             // Check that we can only recover 1 packet.
    238             assert(check_num_recovered == 1);
    239             // Update the state with the newly recovered media packet.
    240             state_tmp[jsel] = 0;
    241           }
    242         }
    243       }  // Go to the next FEC packet in the loop.
    244       // If we have recovered at least one new packet in this FEC loop,
    245       // go through loop again, otherwise we leave loop.
    246       if (recovered_new_packet) {
    247         loop_again = true;
    248       }
    249     }
    250     return num_recovered_packets;
    251   }
    252 
    253   // Compute the probability of occurence of the loss state/configuration,
    254   // given by |state|, for all the loss models considered in this test.
    255   void ComputeProbabilityWeight(double* prob_weight,
    256                                 uint8_t* state,
    257                                 int tot_num_packets) {
    258     // Loop over the loss models.
    259     for (int k = 0; k < kNumLossModels; k++) {
    260       double loss_rate = static_cast<double>(
    261           loss_model_[k].average_loss_rate);
    262       double burst_length = static_cast<double>(
    263           loss_model_[k].average_burst_length);
    264       double result = 1.0;
    265       if (loss_model_[k].loss_type == kRandomLossModel) {
    266         for (int i = 0; i < tot_num_packets; i++) {
    267           if (state[i] == 0) {
    268             result *= (1.0 - loss_rate);
    269           } else {
    270             result *= loss_rate;
    271           }
    272         }
    273       } else {  // Gilbert-Elliot model for burst model.
    274         assert(loss_model_[k].loss_type == kBurstyLossModel);
    275         // Transition probabilities: from previous to current state.
    276         // Prob. of previous = lost --> current = received.
    277         double prob10 = 1.0 / burst_length;
    278         // Prob. of previous = lost --> currrent = lost.
    279         double prob11 = 1.0 - prob10;
    280         // Prob. of previous = received --> current = lost.
    281         double prob01 = prob10 * (loss_rate / (1.0 - loss_rate));
    282         // Prob. of previous = received --> current = received.
    283         double prob00 = 1.0 - prob01;
    284 
    285         // Use stationary probability for first state/packet.
    286         if (state[0] == 0) {  // Received
    287           result = (1.0 - loss_rate);
    288         } else {   // Lost
    289           result = loss_rate;
    290         }
    291 
    292         // Subsequent states: use transition probabilities.
    293         for (int i = 1; i < tot_num_packets; i++) {
    294           // Current state is received
    295           if (state[i] == 0) {
    296             if (state[i-1] == 0) {
    297               result *= prob00;   // Previous received, current received.
    298               } else {
    299                 result *= prob10;  // Previous lost, current received.
    300               }
    301           } else {  // Current state is lost
    302             if (state[i-1] == 0) {
    303               result *= prob01;  // Previous received, current lost.
    304             } else {
    305               result *= prob11;  // Previous lost, current lost.
    306             }
    307           }
    308         }
    309       }
    310       prob_weight[k] = result;
    311     }
    312   }
    313 
    314   void CopyMetrics(MetricsFecCode* metrics_output,
    315                    MetricsFecCode metrics_input) {
    316     memcpy(metrics_output->average_residual_loss,
    317            metrics_input.average_residual_loss,
    318            sizeof(double) * kNumLossModels);
    319     memcpy(metrics_output->variance_residual_loss,
    320            metrics_input.variance_residual_loss,
    321            sizeof(double) * kNumLossModels);
    322     memcpy(metrics_output->residual_loss_per_loss_gap,
    323            metrics_input.residual_loss_per_loss_gap,
    324            sizeof(double) * kNumStatesDistribution);
    325     memcpy(metrics_output->recovery_rate_per_loss,
    326            metrics_input.recovery_rate_per_loss,
    327            sizeof(double) * 2 * kMaxMediaPacketsTest);
    328   }
    329 
    330   // Compute the residual loss per gap, by summing the
    331   // |residual_loss_per_loss_gap| over all loss configurations up to loss number
    332   // = |num_fec_packets|.
    333   double ComputeResidualLossPerGap(MetricsFecCode metrics,
    334                                    int gap_number,
    335                                    int num_fec_packets,
    336                                    int code_index) {
    337     double residual_loss_gap = 0.0;
    338     int tot_num_configs = 0;
    339     for (int loss = 1; loss <= num_fec_packets; loss++) {
    340       int index = gap_number * (2 * kMaxMediaPacketsTest) + loss;
    341       residual_loss_gap += metrics.residual_loss_per_loss_gap[index];
    342       tot_num_configs +=
    343           code_params_[code_index].configuration_density[index];
    344     }
    345     // Normalize, to compare across code sizes.
    346     if (tot_num_configs > 0) {
    347       residual_loss_gap = residual_loss_gap /
    348           static_cast<double>(tot_num_configs);
    349     }
    350     return residual_loss_gap;
    351   }
    352 
    353   // Compute the recovery rate per loss number, by summing the
    354   // |residual_loss_per_loss_gap| over all gap configurations.
    355   void ComputeRecoveryRatePerLoss(MetricsFecCode* metrics,
    356                                   int num_media_packets,
    357                                   int num_fec_packets,
    358                                   int code_index) {
    359     for (int loss = 1; loss <= num_media_packets + num_fec_packets; loss++) {
    360       metrics->recovery_rate_per_loss[loss] = 0.0;
    361       int tot_num_configs = 0;
    362       double arl = 0.0;
    363       for (int gap = 0; gap < kMaxGapSize; gap ++) {
    364         int index = gap * (2 * kMaxMediaPacketsTest) + loss;
    365         arl += metrics->residual_loss_per_loss_gap[index];
    366         tot_num_configs +=
    367             code_params_[code_index].configuration_density[index];
    368       }
    369       // Normalize, to compare across code sizes.
    370       if (tot_num_configs > 0) {
    371         arl = arl / static_cast<double>(tot_num_configs);
    372       }
    373       // Recovery rate for a given loss |loss| is 1 minus the scaled |arl|,
    374       // where the scale factor is relative to code size/parameters.
    375       double scaled_loss = static_cast<double>(loss * num_media_packets) /
    376           static_cast<double>(num_media_packets + num_fec_packets);
    377       metrics->recovery_rate_per_loss[loss] = 1.0 - arl / scaled_loss;
    378     }
    379   }
    380 
    381   void SetMetricsZero(MetricsFecCode* metrics) {
    382     memset(metrics->average_residual_loss, 0, sizeof(double) * kNumLossModels);
    383     memset(metrics->variance_residual_loss, 0, sizeof(double) * kNumLossModels);
    384     memset(metrics->residual_loss_per_loss_gap, 0,
    385            sizeof(double) * kNumStatesDistribution);
    386     memset(metrics->recovery_rate_per_loss, 0,
    387            sizeof(double) * 2 * kMaxMediaPacketsTest + 1);
    388   }
    389 
    390   // Compute the metrics for an FEC code, given by the code type |code_type|
    391   // (XOR-random/ bursty or RS), and by the code index |code_index|
    392   // (which containes the code size parameters/protection length).
    393   void ComputeMetricsForCode(CodeType code_type,
    394                              int code_index) {
    395     scoped_ptr<double[]> prob_weight(new double[kNumLossModels]);
    396     memset(prob_weight.get() , 0, sizeof(double) * kNumLossModels);
    397     MetricsFecCode metrics_code;
    398     SetMetricsZero(&metrics_code);
    399 
    400     int num_media_packets = code_params_[code_index].num_media_packets;
    401     int num_fec_packets = code_params_[code_index].num_fec_packets;
    402     int tot_num_packets = num_media_packets + num_fec_packets;
    403     scoped_ptr<uint8_t[]> state(new uint8_t[tot_num_packets]);
    404     memset(state.get() , 0, tot_num_packets);
    405 
    406     int num_loss_configurations = static_cast<int>(pow(2.0f, tot_num_packets));
    407     // Loop over all loss configurations for the symbol sequence of length
    408     // |tot_num_packets|. In this version we process up to (k=12, m=12) codes,
    409     // and get exact expressions for the residual loss.
    410     // TODO (marpan): For larger codes, loop over some random sample of loss
    411     // configurations, sampling driven by the underlying statistical loss model
    412     // (importance sampling).
    413 
    414     // The symbols/packets are arranged as a sequence of source/media packets
    415     // followed by FEC packets. This is the sequence ordering used in the RTP.
    416     // A configuration refers to a sequence of received/lost (0/1 bit) states
    417     // for the string of packets/symbols. For example, for a (k=4,m=3) code
    418     // (4 media packets, 3 FEC packets), with 2 losses (one media and one FEC),
    419     // the loss configurations is:
    420     // Media1   Media2   Media3   Media4   FEC1   FEC2   FEC3
    421     //   0         0        1       0        0      1     0
    422     for (int i = 1; i < num_loss_configurations; i++) {
    423       // Counter for number of packets lost.
    424       int num_packets_lost = 0;
    425       // Counters for the number of media packets lost.
    426       int num_media_packets_lost = 0;
    427 
    428       // Map configuration number to a loss state.
    429       for (int j = 0; j < tot_num_packets; j++) {
    430         state[j]=0;  // Received state.
    431         int bit_value = i >> (tot_num_packets - j - 1) & 1;
    432         if (bit_value == 1) {
    433           state[j] = 1;  // Lost state.
    434           num_packets_lost++;
    435            if (j < num_media_packets) {
    436              num_media_packets_lost++;
    437            }
    438         }
    439       }  // Done with loop over total number of packets.
    440       assert(num_media_packets_lost <= num_media_packets);
    441       assert(num_packets_lost <= tot_num_packets && num_packets_lost > 0);
    442       double residual_loss = 0.0;
    443       // Only need to compute residual loss (number of recovered packets) for
    444       // configurations that have at least one media packet lost.
    445       if (num_media_packets_lost >= 1) {
    446         // Compute the number of recovered packets.
    447         int num_recovered_packets = 0;
    448         if (code_type == xor_random_code || code_type == xor_bursty_code) {
    449           num_recovered_packets = RecoveredMediaPackets(num_media_packets,
    450                                                         num_fec_packets,
    451                                                         state.get());
    452         } else {
    453           // For the RS code, we can either completely recover all the packets
    454           // if the loss is less than or equal to the number of FEC packets,
    455           // otherwise we can recover none of the missing packets. This is the
    456           // all or nothing (MDS) property of the RS code.
    457           if (num_packets_lost <= num_fec_packets) {
    458             num_recovered_packets = num_media_packets_lost;
    459           }
    460         }
    461         assert(num_recovered_packets <= num_media_packets);
    462         // Compute the residual loss. We only care about recovering media/source
    463         // packets, so residual loss is based on lost/recovered media packets.
    464         residual_loss = static_cast<double>(num_media_packets_lost -
    465                                             num_recovered_packets);
    466         // Compute the probability weights for this configuration.
    467         ComputeProbabilityWeight(prob_weight.get(),
    468                                  state.get(),
    469                                  tot_num_packets);
    470         // Update the average and variance of the residual loss.
    471         for (int k = 0; k < kNumLossModels; k++) {
    472           metrics_code.average_residual_loss[k] += residual_loss *
    473               prob_weight[k];
    474           metrics_code.variance_residual_loss[k] += residual_loss *
    475               residual_loss * prob_weight[k];
    476         }
    477       }  // Done with processing for num_media_packets_lost >= 1.
    478       // Update the distribution statistics.
    479       // Compute the gap of the loss (the "consecutiveness" of the loss).
    480       int gap_loss = GapLoss(tot_num_packets, state.get());
    481       assert(gap_loss < kMaxGapSize);
    482       int index = gap_loss * (2 * kMaxMediaPacketsTest) + num_packets_lost;
    483       assert(index < kNumStatesDistribution);
    484       metrics_code.residual_loss_per_loss_gap[index] += residual_loss;
    485       if (code_type == xor_random_code) {
    486         // The configuration density is only a function of the code length and
    487         // only needs to computed for the first |code_type| passed here.
    488         code_params_[code_index].configuration_density[index]++;
    489       }
    490     }  // Done with loop over configurations.
    491     // Normalize the average residual loss and compute/normalize the variance.
    492     for (int k = 0; k < kNumLossModels; k++) {
    493       // Normalize the average residual loss by the total number of packets
    494       // |tot_num_packets| (i.e., the code length). For a code with no (zero)
    495       // recovery, the average residual loss for that code would be reduced like
    496       // ~|average_loss_rate| * |num_media_packets| / |tot_num_packets|. This is
    497       // the expected reduction in the average residual loss just from adding
    498       // FEC packets to the symbol sequence.
    499       metrics_code.average_residual_loss[k] =
    500           metrics_code.average_residual_loss[k] /
    501           static_cast<double>(tot_num_packets);
    502       metrics_code.variance_residual_loss[k] =
    503                metrics_code.variance_residual_loss[k] /
    504                static_cast<double>(num_media_packets * num_media_packets);
    505       metrics_code.variance_residual_loss[k] =
    506           metrics_code.variance_residual_loss[k] -
    507           (metrics_code.average_residual_loss[k] *
    508               metrics_code.average_residual_loss[k]);
    509       assert(metrics_code.variance_residual_loss[k] >= 0.0);
    510       assert(metrics_code.average_residual_loss[k] > 0.0);
    511       metrics_code.variance_residual_loss[k] =
    512           sqrt(metrics_code.variance_residual_loss[k]) /
    513           metrics_code.average_residual_loss[k];
    514     }
    515 
    516     // Compute marginal distribution as a function of loss parameter.
    517     ComputeRecoveryRatePerLoss(&metrics_code,
    518                                num_media_packets,
    519                                num_fec_packets,
    520                                code_index);
    521     if (code_type == rs_code) {
    522       CopyMetrics(&kMetricsReedSolomon[code_index], metrics_code);
    523     } else if (code_type == xor_random_code) {
    524       CopyMetrics(&kMetricsXorRandom[code_index], metrics_code);
    525     } else if (code_type == xor_bursty_code) {
    526       CopyMetrics(&kMetricsXorBursty[code_index], metrics_code);
    527     } else {
    528       assert(false);
    529     }
    530   }
    531 
    532   void WriteOutMetricsAllFecCodes()  {
    533     std::string filename = test::OutputPath() + "data_metrics_all_codes";
    534     FILE* fp = fopen(filename.c_str(), "wb");
    535     // Loop through codes up to |kMaxMediaPacketsTest|.
    536     int code_index = 0;
    537     for (int num_media_packets = 1; num_media_packets <= kMaxMediaPacketsTest;
    538         num_media_packets++) {
    539       for (int num_fec_packets = 1; num_fec_packets <= num_media_packets;
    540           num_fec_packets++) {
    541         fprintf(fp, "FOR CODE: (%d, %d) \n", num_media_packets,
    542                 num_fec_packets);
    543         for (int k = 0; k < kNumLossModels; k++) {
    544           float loss_rate = loss_model_[k].average_loss_rate;
    545           float burst_length = loss_model_[k].average_burst_length;
    546           fprintf(fp, "Loss rate = %.2f, Burst length = %.2f:  %.4f  %.4f  %.4f"
    547               " **** %.4f %.4f %.4f \n",
    548               loss_rate,
    549               burst_length,
    550               100 * kMetricsReedSolomon[code_index].average_residual_loss[k],
    551               100 * kMetricsXorRandom[code_index].average_residual_loss[k],
    552               100 * kMetricsXorBursty[code_index].average_residual_loss[k],
    553               kMetricsReedSolomon[code_index].variance_residual_loss[k],
    554               kMetricsXorRandom[code_index].variance_residual_loss[k],
    555               kMetricsXorBursty[code_index].variance_residual_loss[k]);
    556         }
    557         for (int gap = 0; gap < kGapSizeOutput; gap ++) {
    558           double rs_residual_loss = ComputeResidualLossPerGap(
    559               kMetricsReedSolomon[code_index],
    560               gap,
    561               num_fec_packets,
    562               code_index);
    563           double xor_random_residual_loss = ComputeResidualLossPerGap(
    564               kMetricsXorRandom[code_index],
    565               gap,
    566               num_fec_packets,
    567               code_index);
    568           double xor_bursty_residual_loss = ComputeResidualLossPerGap(
    569               kMetricsXorBursty[code_index],
    570               gap,
    571               num_fec_packets,
    572               code_index);
    573           fprintf(fp, "Residual loss as a function of gap "
    574               "%d: %.4f %.4f %.4f \n",
    575               gap,
    576               rs_residual_loss,
    577               xor_random_residual_loss,
    578               xor_bursty_residual_loss);
    579         }
    580         fprintf(fp, "Recovery rate as a function of loss number \n");
    581         for (int loss = 1; loss <= num_media_packets + num_fec_packets;
    582                      loss ++) {
    583           fprintf(fp, "For loss number %d: %.4f %.4f %.4f \n",
    584                   loss,
    585                   kMetricsReedSolomon[code_index].
    586                   recovery_rate_per_loss[loss],
    587                   kMetricsXorRandom[code_index].
    588                   recovery_rate_per_loss[loss],
    589                   kMetricsXorBursty[code_index].
    590                   recovery_rate_per_loss[loss]);
    591         }
    592         fprintf(fp, "******************\n");
    593         fprintf(fp, "\n");
    594         code_index++;
    595       }
    596     }
    597     fclose(fp);
    598   }
    599 
    600   void SetLossModels() {
    601     int num_loss_rates = sizeof(kAverageLossRate) /
    602         sizeof(*kAverageLossRate);
    603     int num_burst_lengths = sizeof(kAverageBurstLength) /
    604         sizeof(*kAverageBurstLength);
    605     int num_loss_models = 0;
    606     for (int k = 0; k < num_burst_lengths; k++) {
    607       for (int k2 = 0; k2 < num_loss_rates; k2++) {
    608         loss_model_[num_loss_models].average_loss_rate = kAverageLossRate[k2];
    609         loss_model_[num_loss_models].average_burst_length =
    610             kAverageBurstLength[k];
    611         // First set of loss models are of random type.
    612         if (k == 0) {
    613           loss_model_[num_loss_models].loss_type = kRandomLossModel;
    614         } else {
    615           loss_model_[num_loss_models].loss_type = kBurstyLossModel;
    616         }
    617         num_loss_models++;
    618       }
    619     }
    620     assert(num_loss_models == kNumLossModels);
    621   }
    622 
    623   void SetCodeParams() {
    624     int code_index = 0;
    625     for (int num_media_packets = 1; num_media_packets <= kMaxMediaPacketsTest;
    626         num_media_packets++) {
    627       for (int num_fec_packets = 1; num_fec_packets <= num_media_packets;
    628           num_fec_packets++) {
    629         code_params_[code_index].num_media_packets = num_media_packets;
    630         code_params_[code_index].num_fec_packets = num_fec_packets;
    631         code_params_[code_index].protection_level =
    632             static_cast<float>(num_fec_packets) /
    633             static_cast<float>(num_media_packets + num_fec_packets);
    634         for (int k = 0; k < kNumStatesDistribution; k++) {
    635           code_params_[code_index].configuration_density[k] = 0;
    636         }
    637         code_index++;
    638       }
    639     }
    640     max_num_codes_ = code_index;
    641   }
    642 
    643   // Make some basic checks on the packet masks. Return -1 if any of these
    644   // checks fail.
    645   int RejectInvalidMasks(int num_media_packets, int num_fec_packets) {
    646     // Make sure every FEC packet protects something.
    647     for (int i = 0; i < num_fec_packets; i++) {
    648       int row_degree = 0;
    649       for (int j = 0; j < num_media_packets; j++) {
    650         if (fec_packet_masks_[i][j] == 1) {
    651           row_degree++;
    652         }
    653       }
    654       if (row_degree == 0) {
    655         printf("Invalid mask: FEC packet has empty mask (does not protect "
    656             "anything) %d %d %d \n", i, num_media_packets, num_fec_packets);
    657         return -1;
    658       }
    659     }
    660     // Mask sure every media packet has some protection.
    661     for (int j = 0; j < num_media_packets; j++) {
    662       int column_degree = 0;
    663       for (int i = 0; i < num_fec_packets; i++) {
    664         if (fec_packet_masks_[i][j] == 1) {
    665           column_degree++;
    666         }
    667       }
    668       if (column_degree == 0) {
    669         printf("Invalid mask: Media packet has no protection at all %d %d %d "
    670             "\n", j, num_media_packets, num_fec_packets);
    671         return -1;
    672       }
    673     }
    674     // Make sure we do not have two identical FEC packets.
    675     for (int i = 0; i < num_fec_packets; i++) {
    676       for (int i2 = i + 1; i2 < num_fec_packets; i2++) {
    677         int overlap = 0;
    678         for (int j = 0; j < num_media_packets; j++) {
    679           if (fec_packet_masks_[i][j] == fec_packet_masks_[i2][j]) {
    680             overlap++;
    681           }
    682         }
    683         if (overlap == num_media_packets) {
    684           printf("Invalid mask: Two FEC packets are identical %d %d %d %d \n",
    685                  i, i2, num_media_packets, num_fec_packets);
    686           return -1;
    687         }
    688       }
    689     }
    690     // Avoid codes that have two media packets with full protection (all 1s in
    691     // their corresponding columns). This would mean that if we lose those
    692     // two packets, we can never recover them even if we receive all the other
    693     // packets. Exclude the special cases of 1 or 2 FEC packets.
    694     if (num_fec_packets > 2) {
    695       for (int j = 0; j < num_media_packets; j++) {
    696         for (int j2 = j + 1; j2 < num_media_packets; j2++) {
    697           int degree = 0;
    698           for (int i = 0; i < num_fec_packets; i++) {
    699             if (fec_packet_masks_[i][j] == fec_packet_masks_[i][j2] &&
    700                 fec_packet_masks_[i][j] == 1) {
    701               degree++;
    702             }
    703           }
    704           if (degree == num_fec_packets) {
    705             printf("Invalid mask: Two media packets are have full degree "
    706                 "%d %d %d %d \n", j, j2, num_media_packets, num_fec_packets);
    707             return -1;
    708           }
    709         }
    710       }
    711     }
    712     return 0;
    713   }
    714 
    715   void GetPacketMaskConvertToBitMask(uint8_t* packet_mask,
    716                                      int num_media_packets,
    717                                      int num_fec_packets,
    718                                      int mask_bytes_fec_packet,
    719                                      CodeType code_type) {
    720     for (int i = 0; i < num_fec_packets; i++) {
    721       for (int j = 0; j < num_media_packets; j++) {
    722         const uint8_t byte_mask =
    723             packet_mask[i * mask_bytes_fec_packet + j / 8];
    724         const int bit_position = (7 - j % 8);
    725         fec_packet_masks_[i][j] =
    726             (byte_mask & (1 << bit_position)) >> bit_position;
    727         fprintf(fp_mask_, "%d ", fec_packet_masks_[i][j]);
    728       }
    729       fprintf(fp_mask_, "\n");
    730     }
    731     fprintf(fp_mask_, "\n");
    732   }
    733 
    734   int ProcessXORPacketMasks(CodeType code_type,
    735                           FecMaskType fec_mask_type) {
    736     int code_index = 0;
    737     // Maximum number of media packets allowed for the mask type.
    738     const int packet_mask_max = kMaxMediaPackets[fec_mask_type];
    739     uint8_t* packet_mask = new uint8_t[packet_mask_max * kMaskSizeLBitSet];
    740     // Loop through codes up to |kMaxMediaPacketsTest|.
    741     for (int num_media_packets = 1; num_media_packets <= kMaxMediaPacketsTest;
    742         num_media_packets++) {
    743       const int mask_bytes_fec_packet =
    744           (num_media_packets > 16) ? kMaskSizeLBitSet : kMaskSizeLBitClear;
    745       internal::PacketMaskTable mask_table(fec_mask_type, num_media_packets);
    746       for (int num_fec_packets = 1; num_fec_packets <= num_media_packets;
    747           num_fec_packets++) {
    748         memset(packet_mask, 0, num_media_packets * mask_bytes_fec_packet);
    749         memcpy(packet_mask, mask_table.fec_packet_mask_table()
    750                [num_media_packets - 1][num_fec_packets - 1],
    751                num_fec_packets * mask_bytes_fec_packet);
    752         // Convert to bit mask.
    753         GetPacketMaskConvertToBitMask(packet_mask,
    754                                       num_media_packets,
    755                                       num_fec_packets,
    756                                       mask_bytes_fec_packet,
    757                                       code_type);
    758         if (RejectInvalidMasks(num_media_packets, num_fec_packets) < 0) {
    759           return -1;
    760         }
    761         // Compute the metrics for this code/mask.
    762         ComputeMetricsForCode(code_type,
    763                               code_index);
    764         code_index++;
    765       }
    766     }
    767     assert(code_index == kNumberCodes);
    768     delete [] packet_mask;
    769     return 0;
    770   }
    771 
    772   void ProcessRS(CodeType code_type) {
    773     int code_index = 0;
    774     for (int num_media_packets = 1; num_media_packets <= kMaxMediaPacketsTest;
    775         num_media_packets++) {
    776       for (int num_fec_packets = 1; num_fec_packets <= num_media_packets;
    777           num_fec_packets++) {
    778         // Compute the metrics for this code type.
    779         ComputeMetricsForCode(code_type,
    780                               code_index);
    781         code_index++;
    782       }
    783     }
    784   }
    785 
    786   // Compute metrics for all code types and sizes.
    787   void ComputeMetricsAllCodes() {
    788     SetLossModels();
    789     SetCodeParams();
    790     // Get metrics for XOR code with packet masks of random type.
    791     std::string filename = test::OutputPath() + "data_packet_masks";
    792     fp_mask_ = fopen(filename.c_str(), "wb");
    793     fprintf(fp_mask_, "MASK OF TYPE RANDOM: \n");
    794     EXPECT_EQ(ProcessXORPacketMasks(xor_random_code, kFecMaskRandom), 0);
    795     // Get metrics for XOR code with packet masks of bursty type.
    796     fprintf(fp_mask_, "MASK OF TYPE BURSTY: \n");
    797     EXPECT_EQ(ProcessXORPacketMasks(xor_bursty_code, kFecMaskBursty), 0);
    798     fclose(fp_mask_);
    799     // Get metrics for Reed-Solomon code.
    800     ProcessRS(rs_code);
    801   }
    802 };
    803 
    804 // Verify that the average residual loss, averaged over loss models
    805 // appropriate to each mask type, is below some maximum acceptable level. The
    806 // acceptable levels are read in from a file, and correspond to a current set
    807 // of packet masks. The levels for each code may be updated over time.
    808 TEST_F(FecPacketMaskMetricsTest, FecXorMaxResidualLoss) {
    809   SetLossModels();
    810   SetCodeParams();
    811   ComputeMetricsAllCodes();
    812   WriteOutMetricsAllFecCodes();
    813   int num_loss_rates = sizeof(kAverageLossRate) /
    814       sizeof(*kAverageLossRate);
    815   int num_burst_lengths = sizeof(kAverageBurstLength) /
    816       sizeof(*kAverageBurstLength);
    817   for (int code_index = 0; code_index < max_num_codes_; code_index++) {
    818     double sum_residual_loss_random_mask_random_loss = 0.0;
    819     double sum_residual_loss_bursty_mask_bursty_loss = 0.0;
    820     // Compute the sum residual loss across the models, for each mask type.
    821     for (int k = 0; k < kNumLossModels; k++) {
    822       if (loss_model_[k].loss_type == kRandomLossModel) {
    823         sum_residual_loss_random_mask_random_loss +=
    824             kMetricsXorRandom[code_index].average_residual_loss[k];
    825       } else if (loss_model_[k].loss_type == kBurstyLossModel) {
    826         sum_residual_loss_bursty_mask_bursty_loss +=
    827             kMetricsXorBursty[code_index].average_residual_loss[k];
    828       }
    829     }
    830     float average_residual_loss_random_mask_random_loss =
    831         sum_residual_loss_random_mask_random_loss / num_loss_rates;
    832     float average_residual_loss_bursty_mask_bursty_loss =
    833         sum_residual_loss_bursty_mask_bursty_loss /
    834         (num_loss_rates * (num_burst_lengths  - 1));
    835     const float ref_random_mask = kMaxResidualLossRandomMask[code_index];
    836     const float ref_bursty_mask = kMaxResidualLossBurstyMask[code_index];
    837     EXPECT_LE(average_residual_loss_random_mask_random_loss, ref_random_mask);
    838     EXPECT_LE(average_residual_loss_bursty_mask_bursty_loss, ref_bursty_mask);
    839   }
    840 }
    841 
    842 // Verify the behavior of the XOR codes vs the RS codes.
    843 // For random loss model with average loss rates <= the code protection level,
    844 // the RS code (optimal MDS code) is more efficient than XOR codes.
    845 // However, for larger loss rates (above protection level) and/or bursty
    846 // loss models, the RS is not always more efficient than XOR (though in most
    847 // cases it still is).
    848 TEST_F(FecPacketMaskMetricsTest, FecXorVsRS) {
    849   SetLossModels();
    850   SetCodeParams();
    851   for (int code_index = 0; code_index < max_num_codes_; code_index++) {
    852     for (int k = 0; k < kNumLossModels; k++) {
    853       float loss_rate = loss_model_[k].average_loss_rate;
    854       float protection_level = code_params_[code_index].protection_level;
    855       // Under these conditions we expect XOR to not be better than RS.
    856        if (loss_model_[k].loss_type == kRandomLossModel &&
    857            loss_rate <= protection_level) {
    858         EXPECT_GE(kMetricsXorRandom[code_index].average_residual_loss[k],
    859                   kMetricsReedSolomon[code_index].average_residual_loss[k]);
    860         EXPECT_GE(kMetricsXorBursty[code_index].average_residual_loss[k],
    861                   kMetricsReedSolomon[code_index].average_residual_loss[k]);
    862        }
    863       // TODO (marpan): There are some cases (for high loss rates and/or
    864       // burst loss models) where XOR is better than RS. Is there some pattern
    865       // we can identify and enforce as a constraint?
    866     }
    867   }
    868 }
    869 
    870 // Verify the trend (change) in the average residual loss, as a function of
    871 // loss rate, of the XOR code relative to the RS code.
    872 // The difference between XOR and RS should not get worse as we increase
    873 // the average loss rate.
    874 TEST_F(FecPacketMaskMetricsTest, FecTrendXorVsRsLossRate) {
    875   SetLossModels();
    876   SetCodeParams();
    877   // TODO (marpan): Examine this further to see if the condition can be strictly
    878   // satisfied (i.e., scale = 1.0) for all codes with different/better masks.
    879   double scale = 0.90;
    880   int num_loss_rates = sizeof(kAverageLossRate) /
    881       sizeof(*kAverageLossRate);
    882   int num_burst_lengths = sizeof(kAverageBurstLength) /
    883       sizeof(*kAverageBurstLength);
    884   for (int code_index = 0; code_index < max_num_codes_; code_index++) {
    885     for (int i = 0; i < num_burst_lengths; i++) {
    886       for (int j = 0; j < num_loss_rates - 1; j++) {
    887         int k = num_loss_rates * i + j;
    888         // For XOR random.
    889         if (kMetricsXorRandom[code_index].average_residual_loss[k] >
    890         kMetricsReedSolomon[code_index].average_residual_loss[k]) {
    891           double diff_rs_xor_random_loss1 =
    892               (kMetricsXorRandom[code_index].average_residual_loss[k] -
    893                kMetricsReedSolomon[code_index].average_residual_loss[k]) /
    894                kMetricsXorRandom[code_index].average_residual_loss[k];
    895           double diff_rs_xor_random_loss2 =
    896               (kMetricsXorRandom[code_index].average_residual_loss[k+1] -
    897                kMetricsReedSolomon[code_index].average_residual_loss[k+1]) /
    898                kMetricsXorRandom[code_index].average_residual_loss[k+1];
    899           EXPECT_GE(diff_rs_xor_random_loss1, scale * diff_rs_xor_random_loss2);
    900         }
    901         // TODO (marpan): Investigate the cases for the bursty mask where
    902         // this trend is not strictly satisfied.
    903       }
    904     }
    905   }
    906 }
    907 
    908 // Verify the average residual loss behavior via the protection level and
    909 // the code length. The average residual loss for a given (k1,m1) code
    910 // should generally be higher than that of another code (k2,m2), which has
    911 // either of the two conditions satisfied:
    912 // 1) higher protection & code length at least as large: (k2+m2) >= (k1+m1),
    913 // 2) equal protection and larger code length: (k2+m2) > (k1+m1).
    914 // Currently does not hold for some cases of the XOR code with random mask.
    915 TEST_F(FecPacketMaskMetricsTest, FecBehaviorViaProtectionLevelAndLength) {
    916   SetLossModels();
    917   SetCodeParams();
    918   for (int code_index1 = 0; code_index1 < max_num_codes_; code_index1++) {
    919     float protection_level1 = code_params_[code_index1].protection_level;
    920     int length1 = code_params_[code_index1].num_media_packets +
    921         code_params_[code_index1].num_fec_packets;
    922     for (int code_index2 = 0; code_index2 < max_num_codes_; code_index2++) {
    923       float protection_level2 = code_params_[code_index2].protection_level;
    924       int length2 = code_params_[code_index2].num_media_packets +
    925           code_params_[code_index2].num_fec_packets;
    926       // Codes with higher protection are more efficient, conditioned on the
    927       // length of the code (higher protection but shorter length codes are
    928       // generally not more efficient). For two codes with equal protection,
    929       // the longer code is generally more efficient. For high loss rate
    930       // models, this condition may be violated for some codes with equal or
    931       // very close protection levels. High loss rate case is excluded below.
    932       if ((protection_level2 > protection_level1 && length2 >= length1) ||
    933           (protection_level2 == protection_level1 && length2 > length1)) {
    934         for (int k = 0; k < kNumLossModels; k++) {
    935           float loss_rate = loss_model_[k].average_loss_rate;
    936           if (loss_rate < loss_rate_upper_threshold) {
    937             EXPECT_LT(
    938                 kMetricsReedSolomon[code_index2].average_residual_loss[k],
    939                 kMetricsReedSolomon[code_index1].average_residual_loss[k]);
    940             // TODO (marpan): There are some corner cases where this is not
    941             // satisfied with the current packet masks. Look into updating
    942             // these cases to see if this behavior should/can be satisfied,
    943             // with overall lower residual loss for those XOR codes.
    944             // EXPECT_LT(
    945             //    kMetricsXorBursty[code_index2].average_residual_loss[k],
    946             //    kMetricsXorBursty[code_index1].average_residual_loss[k]);
    947             // EXPECT_LT(
    948             //   kMetricsXorRandom[code_index2].average_residual_loss[k],
    949             //   kMetricsXorRandom[code_index1].average_residual_loss[k]);
    950           }
    951         }
    952       }
    953     }
    954   }
    955 }
    956 
    957 // Verify the beheavior of the variance of the XOR codes.
    958 // The partial recovery of the XOR versus the all or nothing behavior of the RS
    959 // code means that the variance of the residual loss for XOR should generally
    960 // not be worse than RS.
    961 TEST_F(FecPacketMaskMetricsTest, FecVarianceBehaviorXorVsRs) {
    962   SetLossModels();
    963   SetCodeParams();
    964   // The condition is not strictly satisfied with the current masks,
    965   // i.e., for some codes, the variance of XOR may be slightly higher than RS.
    966   // TODO (marpan): Examine this further to see if the condition can be strictly
    967   // satisfied (i.e., scale = 1.0) for all codes with different/better masks.
    968   double scale = 0.95;
    969   for (int code_index = 0; code_index < max_num_codes_; code_index++) {
    970     for (int k = 0; k < kNumLossModels; k++) {
    971       EXPECT_LE(scale *
    972                 kMetricsXorRandom[code_index].variance_residual_loss[k],
    973                 kMetricsReedSolomon[code_index].variance_residual_loss[k]);
    974       EXPECT_LE(scale *
    975                 kMetricsXorBursty[code_index].variance_residual_loss[k],
    976                 kMetricsReedSolomon[code_index].variance_residual_loss[k]);
    977     }
    978   }
    979 }
    980 
    981 // For the bursty mask type, the residual loss must be strictly zero for all
    982 // consecutive losses (i.e, gap = 0) with number of losses <= num_fec_packets.
    983 // This is a design property of the bursty mask type.
    984 TEST_F(FecPacketMaskMetricsTest, FecXorBurstyPerfectRecoveryConsecutiveLoss) {
    985   SetLossModels();
    986   SetCodeParams();
    987   for (int code_index = 0; code_index < max_num_codes_; code_index++) {
    988     int num_fec_packets = code_params_[code_index].num_fec_packets;
    989     for (int loss = 1; loss <= num_fec_packets; loss++) {
    990       int index = loss;  // |gap| is zero.
    991       EXPECT_EQ(kMetricsXorBursty[code_index].
    992                 residual_loss_per_loss_gap[index], 0.0);
    993     }
    994   }
    995 }
    996 
    997 // The XOR codes with random mask type are generally better than the ones with
    998 // bursty mask type, for random loss models at low loss rates.
    999 // The XOR codes with bursty mask types are generally better than the one with
   1000 // random mask type, for bursty loss models and/or high loss rates.
   1001 // TODO (marpan): Enable this test when some of the packet masks are updated.
   1002 // Some isolated cases of the codes don't pass this currently.
   1003 /*
   1004 TEST_F(FecPacketMaskMetricsTest, FecXorRandomVsBursty) {
   1005   SetLossModels();
   1006   SetCodeParams();
   1007   for (int code_index = 0; code_index < max_num_codes_; code_index++) {
   1008     double sum_residual_loss_random_mask_random_loss = 0.0;
   1009     double sum_residual_loss_bursty_mask_random_loss = 0.0;
   1010     double sum_residual_loss_random_mask_bursty_loss = 0.0;
   1011     double sum_residual_loss_bursty_mask_bursty_loss = 0.0;
   1012     // Compute the sum residual loss across the models, for each mask type.
   1013     for (int k = 0; k < kNumLossModels; k++) {
   1014       float loss_rate = loss_model_[k].average_loss_rate;
   1015       if (loss_model_[k].loss_type == kRandomLossModel &&
   1016           loss_rate < loss_rate_upper_threshold) {
   1017         sum_residual_loss_random_mask_random_loss +=
   1018             kMetricsXorRandom[code_index].average_residual_loss[k];
   1019         sum_residual_loss_bursty_mask_random_loss +=
   1020             kMetricsXorBursty[code_index].average_residual_loss[k];
   1021       } else if (loss_model_[k].loss_type == kBurstyLossModel &&
   1022           loss_rate > loss_rate_lower_threshold) {
   1023         sum_residual_loss_random_mask_bursty_loss +=
   1024             kMetricsXorRandom[code_index].average_residual_loss[k];
   1025         sum_residual_loss_bursty_mask_bursty_loss +=
   1026             kMetricsXorBursty[code_index].average_residual_loss[k];
   1027       }
   1028     }
   1029     EXPECT_LE(sum_residual_loss_random_mask_random_loss,
   1030               sum_residual_loss_bursty_mask_random_loss);
   1031     EXPECT_LE(sum_residual_loss_bursty_mask_bursty_loss,
   1032               sum_residual_loss_random_mask_bursty_loss);
   1033   }
   1034 }
   1035 */
   1036 
   1037 // Verify that the average recovery rate for each code is equal or above some
   1038 // threshold, for certain loss number conditions.
   1039 TEST_F(FecPacketMaskMetricsTest, FecRecoveryRateUnderLossConditions) {
   1040   SetLossModels();
   1041   SetCodeParams();
   1042   for (int code_index = 0; code_index < max_num_codes_; code_index++) {
   1043     int num_media_packets = code_params_[code_index].num_media_packets;
   1044     int num_fec_packets = code_params_[code_index].num_fec_packets;
   1045     // Perfect recovery (|recovery_rate_per_loss| == 1) is expected for
   1046     // |loss_number| = 1, for all codes.
   1047     int loss_number = 1;
   1048     EXPECT_EQ(kMetricsReedSolomon[code_index].
   1049               recovery_rate_per_loss[loss_number], 1.0);
   1050     EXPECT_EQ(kMetricsXorRandom[code_index].
   1051               recovery_rate_per_loss[loss_number], 1.0);
   1052     EXPECT_EQ(kMetricsXorBursty[code_index].
   1053               recovery_rate_per_loss[loss_number], 1.0);
   1054     // For |loss_number| = |num_fec_packets| / 2, we expect the following:
   1055     // Perfect recovery for RS, and recovery for XOR above the threshold.
   1056     loss_number = num_fec_packets / 2 > 0 ? num_fec_packets / 2 : 1;
   1057     EXPECT_EQ(kMetricsReedSolomon[code_index].
   1058               recovery_rate_per_loss[loss_number], 1.0);
   1059     EXPECT_GE(kMetricsXorRandom[code_index].
   1060               recovery_rate_per_loss[loss_number], kRecoveryRateXorRandom[0]);
   1061     EXPECT_GE(kMetricsXorBursty[code_index].
   1062               recovery_rate_per_loss[loss_number], kRecoveryRateXorBursty[0]);
   1063     // For |loss_number| = |num_fec_packets|, we expect the following:
   1064     // Perfect recovery for RS, and recovery for XOR above the threshold.
   1065     loss_number = num_fec_packets;
   1066     EXPECT_EQ(kMetricsReedSolomon[code_index].
   1067               recovery_rate_per_loss[loss_number], 1.0);
   1068     EXPECT_GE(kMetricsXorRandom[code_index].
   1069               recovery_rate_per_loss[loss_number], kRecoveryRateXorRandom[1]);
   1070     EXPECT_GE(kMetricsXorBursty[code_index].
   1071               recovery_rate_per_loss[loss_number], kRecoveryRateXorBursty[1]);
   1072     // For |loss_number| = |num_fec_packets| + 1, we expect the following:
   1073     // Zero recovery for RS, but non-zero recovery for XOR.
   1074     if (num_fec_packets > 1 && num_media_packets > 2) {
   1075       loss_number =  num_fec_packets + 1;
   1076       EXPECT_EQ(kMetricsReedSolomon[code_index].
   1077                 recovery_rate_per_loss[loss_number], 0.0);
   1078       EXPECT_GE(kMetricsXorRandom[code_index].
   1079                 recovery_rate_per_loss[loss_number],
   1080                 kRecoveryRateXorRandom[2]);
   1081       EXPECT_GE(kMetricsXorBursty[code_index].
   1082                 recovery_rate_per_loss[loss_number],
   1083                 kRecoveryRateXorBursty[2]);
   1084     }
   1085   }
   1086 }
   1087 
   1088 }  // namespace webrtc
   1089