Home | History | Annotate | Download | only in video_coding
      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/video_coding/media_opt_util.h"
     12 
     13 #include <float.h>
     14 #include <limits.h>
     15 #include <math.h>
     16 
     17 #include <algorithm>
     18 
     19 #include "webrtc/modules/include/module_common_types.h"
     20 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
     21 #include "webrtc/modules/video_coding/include/video_coding_defines.h"
     22 #include "webrtc/modules/video_coding/fec_tables_xor.h"
     23 #include "webrtc/modules/video_coding/nack_fec_tables.h"
     24 
     25 namespace webrtc {
     26 // Max value of loss rates in off-line model
     27 static const int kPacketLossMax = 129;
     28 
     29 namespace media_optimization {
     30 
     31 VCMProtectionMethod::VCMProtectionMethod()
     32     : _effectivePacketLoss(0),
     33       _protectionFactorK(0),
     34       _protectionFactorD(0),
     35       _scaleProtKey(2.0f),
     36       _maxPayloadSize(1460),
     37       _qmRobustness(new VCMQmRobustness()),
     38       _useUepProtectionK(false),
     39       _useUepProtectionD(true),
     40       _corrFecCost(1.0),
     41       _type(kNone) {}
     42 
     43 VCMProtectionMethod::~VCMProtectionMethod() {
     44   delete _qmRobustness;
     45 }
     46 void VCMProtectionMethod::UpdateContentMetrics(
     47     const VideoContentMetrics* contentMetrics) {
     48   _qmRobustness->UpdateContent(contentMetrics);
     49 }
     50 
     51 VCMNackFecMethod::VCMNackFecMethod(int64_t lowRttNackThresholdMs,
     52                                    int64_t highRttNackThresholdMs)
     53     : VCMFecMethod(),
     54       _lowRttNackMs(lowRttNackThresholdMs),
     55       _highRttNackMs(highRttNackThresholdMs),
     56       _maxFramesFec(1) {
     57   assert(lowRttNackThresholdMs >= -1 && highRttNackThresholdMs >= -1);
     58   assert(highRttNackThresholdMs == -1 ||
     59          lowRttNackThresholdMs <= highRttNackThresholdMs);
     60   assert(lowRttNackThresholdMs > -1 || highRttNackThresholdMs == -1);
     61   _type = kNackFec;
     62 }
     63 
     64 VCMNackFecMethod::~VCMNackFecMethod() {
     65   //
     66 }
     67 bool VCMNackFecMethod::ProtectionFactor(
     68     const VCMProtectionParameters* parameters) {
     69   // Hybrid Nack FEC has three operational modes:
     70   // 1. Low RTT (below kLowRttNackMs) - Nack only: Set FEC rate
     71   //    (_protectionFactorD) to zero. -1 means no FEC.
     72   // 2. High RTT (above _highRttNackMs) - FEC Only: Keep FEC factors.
     73   //    -1 means always allow NACK.
     74   // 3. Medium RTT values - Hybrid mode: We will only nack the
     75   //    residual following the decoding of the FEC (refer to JB logic). FEC
     76   //    delta protection factor will be adjusted based on the RTT.
     77 
     78   // Otherwise: we count on FEC; if the RTT is below a threshold, then we
     79   // nack the residual, based on a decision made in the JB.
     80 
     81   // Compute the protection factors
     82   VCMFecMethod::ProtectionFactor(parameters);
     83   if (_lowRttNackMs == -1 || parameters->rtt < _lowRttNackMs) {
     84     _protectionFactorD = 0;
     85     VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
     86 
     87     // When in Hybrid mode (RTT range), adjust FEC rates based on the
     88     // RTT (NACK effectiveness) - adjustment factor is in the range [0,1].
     89   } else if (_highRttNackMs == -1 || parameters->rtt < _highRttNackMs) {
     90     // TODO(mikhal): Disabling adjustment temporarily.
     91     // uint16_t rttIndex = (uint16_t) parameters->rtt;
     92     float adjustRtt = 1.0f;  // (float)VCMNackFecTable[rttIndex] / 100.0f;
     93 
     94     // Adjust FEC with NACK on (for delta frame only)
     95     // table depends on RTT relative to rttMax (NACK Threshold)
     96     _protectionFactorD = static_cast<uint8_t>(
     97         adjustRtt * static_cast<float>(_protectionFactorD));
     98     // update FEC rates after applying adjustment
     99     VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
    100   }
    101 
    102   return true;
    103 }
    104 
    105 int VCMNackFecMethod::ComputeMaxFramesFec(
    106     const VCMProtectionParameters* parameters) {
    107   if (parameters->numLayers > 2) {
    108     // For more than 2 temporal layers we will only have FEC on the base layer,
    109     // and the base layers will be pretty far apart. Therefore we force one
    110     // frame FEC.
    111     return 1;
    112   }
    113   // We set the max number of frames to base the FEC on so that on average
    114   // we will have complete frames in one RTT. Note that this is an upper
    115   // bound, and that the actual number of frames used for FEC is decided by the
    116   // RTP module based on the actual number of packets and the protection factor.
    117   float base_layer_framerate =
    118       parameters->frameRate /
    119       static_cast<float>(1 << (parameters->numLayers - 1));
    120   int max_frames_fec = std::max(
    121       static_cast<int>(2.0f * base_layer_framerate * parameters->rtt / 1000.0f +
    122                        0.5f),
    123       1);
    124   // |kUpperLimitFramesFec| is the upper limit on how many frames we
    125   // allow any FEC to be based on.
    126   if (max_frames_fec > kUpperLimitFramesFec) {
    127     max_frames_fec = kUpperLimitFramesFec;
    128   }
    129   return max_frames_fec;
    130 }
    131 
    132 int VCMNackFecMethod::MaxFramesFec() const {
    133   return _maxFramesFec;
    134 }
    135 
    136 bool VCMNackFecMethod::BitRateTooLowForFec(
    137     const VCMProtectionParameters* parameters) {
    138   // Bitrate below which we turn off FEC, regardless of reported packet loss.
    139   // The condition should depend on resolution and content. For now, use
    140   // threshold on bytes per frame, with some effect for the frame size.
    141   // The condition for turning off FEC is also based on other factors,
    142   // such as |_numLayers|, |_maxFramesFec|, and |_rtt|.
    143   int estimate_bytes_per_frame = 1000 * BitsPerFrame(parameters) / 8;
    144   int max_bytes_per_frame = kMaxBytesPerFrameForFec;
    145   int num_pixels = parameters->codecWidth * parameters->codecHeight;
    146   if (num_pixels <= 352 * 288) {
    147     max_bytes_per_frame = kMaxBytesPerFrameForFecLow;
    148   } else if (num_pixels > 640 * 480) {
    149     max_bytes_per_frame = kMaxBytesPerFrameForFecHigh;
    150   }
    151   // TODO(marpan): add condition based on maximum frames used for FEC,
    152   // and expand condition based on frame size.
    153   // Max round trip time threshold in ms.
    154   const int64_t kMaxRttTurnOffFec = 200;
    155   if (estimate_bytes_per_frame < max_bytes_per_frame &&
    156       parameters->numLayers < 3 && parameters->rtt < kMaxRttTurnOffFec) {
    157     return true;
    158   }
    159   return false;
    160 }
    161 
    162 bool VCMNackFecMethod::EffectivePacketLoss(
    163     const VCMProtectionParameters* parameters) {
    164   // Set the effective packet loss for encoder (based on FEC code).
    165   // Compute the effective packet loss and residual packet loss due to FEC.
    166   VCMFecMethod::EffectivePacketLoss(parameters);
    167   return true;
    168 }
    169 
    170 bool VCMNackFecMethod::UpdateParameters(
    171     const VCMProtectionParameters* parameters) {
    172   ProtectionFactor(parameters);
    173   EffectivePacketLoss(parameters);
    174   _maxFramesFec = ComputeMaxFramesFec(parameters);
    175   if (BitRateTooLowForFec(parameters)) {
    176     _protectionFactorK = 0;
    177     _protectionFactorD = 0;
    178   }
    179 
    180   // Protection/fec rates obtained above are defined relative to total number
    181   // of packets (total rate: source + fec) FEC in RTP module assumes
    182   // protection factor is defined relative to source number of packets so we
    183   // should convert the factor to reduce mismatch between mediaOpt's rate and
    184   // the actual one
    185   _protectionFactorK = VCMFecMethod::ConvertFECRate(_protectionFactorK);
    186   _protectionFactorD = VCMFecMethod::ConvertFECRate(_protectionFactorD);
    187 
    188   return true;
    189 }
    190 
    191 VCMNackMethod::VCMNackMethod() : VCMProtectionMethod() {
    192   _type = kNack;
    193 }
    194 
    195 VCMNackMethod::~VCMNackMethod() {
    196   //
    197 }
    198 
    199 bool VCMNackMethod::EffectivePacketLoss(
    200     const VCMProtectionParameters* parameter) {
    201   // Effective Packet Loss, NA in current version.
    202   _effectivePacketLoss = 0;
    203   return true;
    204 }
    205 
    206 bool VCMNackMethod::UpdateParameters(
    207     const VCMProtectionParameters* parameters) {
    208   // Compute the effective packet loss
    209   EffectivePacketLoss(parameters);
    210 
    211   // nackCost  = (bitRate - nackCost) * (lossPr)
    212   return true;
    213 }
    214 
    215 VCMFecMethod::VCMFecMethod() : VCMProtectionMethod() {
    216   _type = kFec;
    217 }
    218 VCMFecMethod::~VCMFecMethod() {
    219   //
    220 }
    221 
    222 uint8_t VCMFecMethod::BoostCodeRateKey(uint8_t packetFrameDelta,
    223                                        uint8_t packetFrameKey) const {
    224   uint8_t boostRateKey = 2;
    225   // Default: ratio scales the FEC protection up for I frames
    226   uint8_t ratio = 1;
    227 
    228   if (packetFrameDelta > 0) {
    229     ratio = (int8_t)(packetFrameKey / packetFrameDelta);
    230   }
    231   ratio = VCM_MAX(boostRateKey, ratio);
    232 
    233   return ratio;
    234 }
    235 
    236 uint8_t VCMFecMethod::ConvertFECRate(uint8_t codeRateRTP) const {
    237   return static_cast<uint8_t>(VCM_MIN(
    238       255,
    239       (0.5 + 255.0 * codeRateRTP / static_cast<float>(255 - codeRateRTP))));
    240 }
    241 
    242 // Update FEC with protectionFactorD
    243 void VCMFecMethod::UpdateProtectionFactorD(uint8_t protectionFactorD) {
    244   _protectionFactorD = protectionFactorD;
    245 }
    246 
    247 // Update FEC with protectionFactorK
    248 void VCMFecMethod::UpdateProtectionFactorK(uint8_t protectionFactorK) {
    249   _protectionFactorK = protectionFactorK;
    250 }
    251 
    252 bool VCMFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters) {
    253   // FEC PROTECTION SETTINGS: varies with packet loss and bitrate
    254 
    255   // No protection if (filtered) packetLoss is 0
    256   uint8_t packetLoss = (uint8_t)(255 * parameters->lossPr);
    257   if (packetLoss == 0) {
    258     _protectionFactorK = 0;
    259     _protectionFactorD = 0;
    260     return true;
    261   }
    262 
    263   // Parameters for FEC setting:
    264   // first partition size, thresholds, table pars, spatial resoln fac.
    265 
    266   // First partition protection: ~ 20%
    267   uint8_t firstPartitionProt = (uint8_t)(255 * 0.20);
    268 
    269   // Minimum protection level needed to generate one FEC packet for one
    270   // source packet/frame (in RTP sender)
    271   uint8_t minProtLevelFec = 85;
    272 
    273   // Threshold on packetLoss and bitRrate/frameRate (=average #packets),
    274   // above which we allocate protection to cover at least first partition.
    275   uint8_t lossThr = 0;
    276   uint8_t packetNumThr = 1;
    277 
    278   // Parameters for range of rate index of table.
    279   const uint8_t ratePar1 = 5;
    280   const uint8_t ratePar2 = 49;
    281 
    282   // Spatial resolution size, relative to a reference size.
    283   float spatialSizeToRef =
    284       static_cast<float>(parameters->codecWidth * parameters->codecHeight) /
    285       (static_cast<float>(704 * 576));
    286   // resolnFac: This parameter will generally increase/decrease the FEC rate
    287   // (for fixed bitRate and packetLoss) based on system size.
    288   // Use a smaller exponent (< 1) to control/soften system size effect.
    289   const float resolnFac = 1.0 / powf(spatialSizeToRef, 0.3f);
    290 
    291   const int bitRatePerFrame = BitsPerFrame(parameters);
    292 
    293   // Average number of packets per frame (source and fec):
    294   const uint8_t avgTotPackets =
    295       1 + (uint8_t)(static_cast<float>(bitRatePerFrame) * 1000.0 /
    296                         static_cast<float>(8.0 * _maxPayloadSize) +
    297                     0.5);
    298 
    299   // FEC rate parameters: for P and I frame
    300   uint8_t codeRateDelta = 0;
    301   uint8_t codeRateKey = 0;
    302 
    303   // Get index for table: the FEC protection depends on an effective rate.
    304   // The range on the rate index corresponds to rates (bps)
    305   // from ~200k to ~8000k, for 30fps
    306   const uint16_t effRateFecTable =
    307       static_cast<uint16_t>(resolnFac * bitRatePerFrame);
    308   uint8_t rateIndexTable = (uint8_t)VCM_MAX(
    309       VCM_MIN((effRateFecTable - ratePar1) / ratePar1, ratePar2), 0);
    310 
    311   // Restrict packet loss range to 50:
    312   // current tables defined only up to 50%
    313   if (packetLoss >= kPacketLossMax) {
    314     packetLoss = kPacketLossMax - 1;
    315   }
    316   uint16_t indexTable = rateIndexTable * kPacketLossMax + packetLoss;
    317 
    318   // Check on table index
    319   assert(indexTable < kSizeCodeRateXORTable);
    320 
    321   // Protection factor for P frame
    322   codeRateDelta = kCodeRateXORTable[indexTable];
    323 
    324   if (packetLoss > lossThr && avgTotPackets > packetNumThr) {
    325     // Set a minimum based on first partition size.
    326     if (codeRateDelta < firstPartitionProt) {
    327       codeRateDelta = firstPartitionProt;
    328     }
    329   }
    330 
    331   // Check limit on amount of protection for P frame; 50% is max.
    332   if (codeRateDelta >= kPacketLossMax) {
    333     codeRateDelta = kPacketLossMax - 1;
    334   }
    335 
    336   float adjustFec = 1.0f;
    337   // Avoid additional adjustments when layers are active.
    338   // TODO(mikhal/marco): Update adjusmtent based on layer info.
    339   if (parameters->numLayers == 1) {
    340     adjustFec = _qmRobustness->AdjustFecFactor(
    341         codeRateDelta, parameters->bitRate, parameters->frameRate,
    342         parameters->rtt, packetLoss);
    343   }
    344 
    345   codeRateDelta = static_cast<uint8_t>(codeRateDelta * adjustFec);
    346 
    347   // For Key frame:
    348   // Effectively at a higher rate, so we scale/boost the rate
    349   // The boost factor may depend on several factors: ratio of packet
    350   // number of I to P frames, how much protection placed on P frames, etc.
    351   const uint8_t packetFrameDelta = (uint8_t)(0.5 + parameters->packetsPerFrame);
    352   const uint8_t packetFrameKey =
    353       (uint8_t)(0.5 + parameters->packetsPerFrameKey);
    354   const uint8_t boostKey = BoostCodeRateKey(packetFrameDelta, packetFrameKey);
    355 
    356   rateIndexTable = (uint8_t)VCM_MAX(
    357       VCM_MIN(1 + (boostKey * effRateFecTable - ratePar1) / ratePar1, ratePar2),
    358       0);
    359   uint16_t indexTableKey = rateIndexTable * kPacketLossMax + packetLoss;
    360 
    361   indexTableKey = VCM_MIN(indexTableKey, kSizeCodeRateXORTable);
    362 
    363   // Check on table index
    364   assert(indexTableKey < kSizeCodeRateXORTable);
    365 
    366   // Protection factor for I frame
    367   codeRateKey = kCodeRateXORTable[indexTableKey];
    368 
    369   // Boosting for Key frame.
    370   int boostKeyProt = _scaleProtKey * codeRateDelta;
    371   if (boostKeyProt >= kPacketLossMax) {
    372     boostKeyProt = kPacketLossMax - 1;
    373   }
    374 
    375   // Make sure I frame protection is at least larger than P frame protection,
    376   // and at least as high as filtered packet loss.
    377   codeRateKey = static_cast<uint8_t>(
    378       VCM_MAX(packetLoss, VCM_MAX(boostKeyProt, codeRateKey)));
    379 
    380   // Check limit on amount of protection for I frame: 50% is max.
    381   if (codeRateKey >= kPacketLossMax) {
    382     codeRateKey = kPacketLossMax - 1;
    383   }
    384 
    385   _protectionFactorK = codeRateKey;
    386   _protectionFactorD = codeRateDelta;
    387 
    388   // Generally there is a rate mis-match between the FEC cost estimated
    389   // in mediaOpt and the actual FEC cost sent out in RTP module.
    390   // This is more significant at low rates (small # of source packets), where
    391   // the granularity of the FEC decreases. In this case, non-zero protection
    392   // in mediaOpt may generate 0 FEC packets in RTP sender (since actual #FEC
    393   // is based on rounding off protectionFactor on actual source packet number).
    394   // The correction factor (_corrFecCost) attempts to corrects this, at least
    395   // for cases of low rates (small #packets) and low protection levels.
    396 
    397   float numPacketsFl = 1.0f + (static_cast<float>(bitRatePerFrame) * 1000.0 /
    398                                    static_cast<float>(8.0 * _maxPayloadSize) +
    399                                0.5);
    400 
    401   const float estNumFecGen =
    402       0.5f + static_cast<float>(_protectionFactorD * numPacketsFl / 255.0f);
    403 
    404   // We reduce cost factor (which will reduce overhead for FEC and
    405   // hybrid method) and not the protectionFactor.
    406   _corrFecCost = 1.0f;
    407   if (estNumFecGen < 1.1f && _protectionFactorD < minProtLevelFec) {
    408     _corrFecCost = 0.5f;
    409   }
    410   if (estNumFecGen < 0.9f && _protectionFactorD < minProtLevelFec) {
    411     _corrFecCost = 0.0f;
    412   }
    413 
    414   // TODO(marpan): Set the UEP protection on/off for Key and Delta frames
    415   _useUepProtectionK = _qmRobustness->SetUepProtection(
    416       codeRateKey, parameters->bitRate, packetLoss, 0);
    417 
    418   _useUepProtectionD = _qmRobustness->SetUepProtection(
    419       codeRateDelta, parameters->bitRate, packetLoss, 1);
    420 
    421   // DONE WITH FEC PROTECTION SETTINGS
    422   return true;
    423 }
    424 
    425 int VCMFecMethod::BitsPerFrame(const VCMProtectionParameters* parameters) {
    426   // When temporal layers are available FEC will only be applied on the base
    427   // layer.
    428   const float bitRateRatio =
    429       kVp8LayerRateAlloction[parameters->numLayers - 1][0];
    430   float frameRateRatio = powf(1 / 2.0, parameters->numLayers - 1);
    431   float bitRate = parameters->bitRate * bitRateRatio;
    432   float frameRate = parameters->frameRate * frameRateRatio;
    433 
    434   // TODO(mikhal): Update factor following testing.
    435   float adjustmentFactor = 1;
    436 
    437   // Average bits per frame (units of kbits)
    438   return static_cast<int>(adjustmentFactor * bitRate / frameRate);
    439 }
    440 
    441 bool VCMFecMethod::EffectivePacketLoss(
    442     const VCMProtectionParameters* parameters) {
    443   // Effective packet loss to encoder is based on RPL (residual packet loss)
    444   // this is a soft setting based on degree of FEC protection
    445   // RPL = received/input packet loss - average_FEC_recovery
    446   // note: received/input packet loss may be filtered based on FilteredLoss
    447 
    448   // Effective Packet Loss, NA in current version.
    449   _effectivePacketLoss = 0;
    450 
    451   return true;
    452 }
    453 
    454 bool VCMFecMethod::UpdateParameters(const VCMProtectionParameters* parameters) {
    455   // Compute the protection factor
    456   ProtectionFactor(parameters);
    457 
    458   // Compute the effective packet loss
    459   EffectivePacketLoss(parameters);
    460 
    461   // Protection/fec rates obtained above is defined relative to total number
    462   // of packets (total rate: source+fec) FEC in RTP module assumes protection
    463   // factor is defined relative to source number of packets so we should
    464   // convert the factor to reduce mismatch between mediaOpt suggested rate and
    465   // the actual rate
    466   _protectionFactorK = ConvertFECRate(_protectionFactorK);
    467   _protectionFactorD = ConvertFECRate(_protectionFactorD);
    468 
    469   return true;
    470 }
    471 VCMLossProtectionLogic::VCMLossProtectionLogic(int64_t nowMs)
    472     : _currentParameters(),
    473       _rtt(0),
    474       _lossPr(0.0f),
    475       _bitRate(0.0f),
    476       _frameRate(0.0f),
    477       _keyFrameSize(0.0f),
    478       _fecRateKey(0),
    479       _fecRateDelta(0),
    480       _lastPrUpdateT(0),
    481       _lossPr255(0.9999f),
    482       _lossPrHistory(),
    483       _shortMaxLossPr255(0),
    484       _packetsPerFrame(0.9999f),
    485       _packetsPerFrameKey(0.9999f),
    486       _codecWidth(0),
    487       _codecHeight(0),
    488       _numLayers(1) {
    489   Reset(nowMs);
    490 }
    491 
    492 VCMLossProtectionLogic::~VCMLossProtectionLogic() {
    493   Release();
    494 }
    495 
    496 void VCMLossProtectionLogic::SetMethod(
    497     enum VCMProtectionMethodEnum newMethodType) {
    498   if (_selectedMethod && _selectedMethod->Type() == newMethodType)
    499     return;
    500 
    501   switch (newMethodType) {
    502     case kNack:
    503       _selectedMethod.reset(new VCMNackMethod());
    504       break;
    505     case kFec:
    506       _selectedMethod.reset(new VCMFecMethod());
    507       break;
    508     case kNackFec:
    509       _selectedMethod.reset(new VCMNackFecMethod(kLowRttNackMs, -1));
    510       break;
    511     case kNone:
    512       _selectedMethod.reset();
    513       break;
    514   }
    515   UpdateMethod();
    516 }
    517 
    518 void VCMLossProtectionLogic::UpdateRtt(int64_t rtt) {
    519   _rtt = rtt;
    520 }
    521 
    522 void VCMLossProtectionLogic::UpdateMaxLossHistory(uint8_t lossPr255,
    523                                                   int64_t now) {
    524   if (_lossPrHistory[0].timeMs >= 0 &&
    525       now - _lossPrHistory[0].timeMs < kLossPrShortFilterWinMs) {
    526     if (lossPr255 > _shortMaxLossPr255) {
    527       _shortMaxLossPr255 = lossPr255;
    528     }
    529   } else {
    530     // Only add a new value to the history once a second
    531     if (_lossPrHistory[0].timeMs == -1) {
    532       // First, no shift
    533       _shortMaxLossPr255 = lossPr255;
    534     } else {
    535       // Shift
    536       for (int32_t i = (kLossPrHistorySize - 2); i >= 0; i--) {
    537         _lossPrHistory[i + 1].lossPr255 = _lossPrHistory[i].lossPr255;
    538         _lossPrHistory[i + 1].timeMs = _lossPrHistory[i].timeMs;
    539       }
    540     }
    541     if (_shortMaxLossPr255 == 0) {
    542       _shortMaxLossPr255 = lossPr255;
    543     }
    544 
    545     _lossPrHistory[0].lossPr255 = _shortMaxLossPr255;
    546     _lossPrHistory[0].timeMs = now;
    547     _shortMaxLossPr255 = 0;
    548   }
    549 }
    550 
    551 uint8_t VCMLossProtectionLogic::MaxFilteredLossPr(int64_t nowMs) const {
    552   uint8_t maxFound = _shortMaxLossPr255;
    553   if (_lossPrHistory[0].timeMs == -1) {
    554     return maxFound;
    555   }
    556   for (int32_t i = 0; i < kLossPrHistorySize; i++) {
    557     if (_lossPrHistory[i].timeMs == -1) {
    558       break;
    559     }
    560     if (nowMs - _lossPrHistory[i].timeMs >
    561         kLossPrHistorySize * kLossPrShortFilterWinMs) {
    562       // This sample (and all samples after this) is too old
    563       break;
    564     }
    565     if (_lossPrHistory[i].lossPr255 > maxFound) {
    566       // This sample is the largest one this far into the history
    567       maxFound = _lossPrHistory[i].lossPr255;
    568     }
    569   }
    570   return maxFound;
    571 }
    572 
    573 uint8_t VCMLossProtectionLogic::FilteredLoss(int64_t nowMs,
    574                                              FilterPacketLossMode filter_mode,
    575                                              uint8_t lossPr255) {
    576   // Update the max window filter.
    577   UpdateMaxLossHistory(lossPr255, nowMs);
    578 
    579   // Update the recursive average filter.
    580   _lossPr255.Apply(static_cast<float>(nowMs - _lastPrUpdateT),
    581                    static_cast<float>(lossPr255));
    582   _lastPrUpdateT = nowMs;
    583 
    584   // Filtered loss: default is received loss (no filtering).
    585   uint8_t filtered_loss = lossPr255;
    586 
    587   switch (filter_mode) {
    588     case kNoFilter:
    589       break;
    590     case kAvgFilter:
    591       filtered_loss = static_cast<uint8_t>(_lossPr255.filtered() + 0.5);
    592       break;
    593     case kMaxFilter:
    594       filtered_loss = MaxFilteredLossPr(nowMs);
    595       break;
    596   }
    597 
    598   return filtered_loss;
    599 }
    600 
    601 void VCMLossProtectionLogic::UpdateFilteredLossPr(uint8_t packetLossEnc) {
    602   _lossPr = static_cast<float>(packetLossEnc) / 255.0;
    603 }
    604 
    605 void VCMLossProtectionLogic::UpdateBitRate(float bitRate) {
    606   _bitRate = bitRate;
    607 }
    608 
    609 void VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets,
    610                                                    int64_t nowMs) {
    611   _packetsPerFrame.Apply(static_cast<float>(nowMs - _lastPacketPerFrameUpdateT),
    612                          nPackets);
    613   _lastPacketPerFrameUpdateT = nowMs;
    614 }
    615 
    616 void VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets,
    617                                                       int64_t nowMs) {
    618   _packetsPerFrameKey.Apply(
    619       static_cast<float>(nowMs - _lastPacketPerFrameUpdateTKey), nPackets);
    620   _lastPacketPerFrameUpdateTKey = nowMs;
    621 }
    622 
    623 void VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize) {
    624   _keyFrameSize = keyFrameSize;
    625 }
    626 
    627 void VCMLossProtectionLogic::UpdateFrameSize(uint16_t width, uint16_t height) {
    628   _codecWidth = width;
    629   _codecHeight = height;
    630 }
    631 
    632 void VCMLossProtectionLogic::UpdateNumLayers(int numLayers) {
    633   _numLayers = (numLayers == 0) ? 1 : numLayers;
    634 }
    635 
    636 bool VCMLossProtectionLogic::UpdateMethod() {
    637   if (!_selectedMethod)
    638     return false;
    639   _currentParameters.rtt = _rtt;
    640   _currentParameters.lossPr = _lossPr;
    641   _currentParameters.bitRate = _bitRate;
    642   _currentParameters.frameRate = _frameRate;  // rename actual frame rate?
    643   _currentParameters.keyFrameSize = _keyFrameSize;
    644   _currentParameters.fecRateDelta = _fecRateDelta;
    645   _currentParameters.fecRateKey = _fecRateKey;
    646   _currentParameters.packetsPerFrame = _packetsPerFrame.filtered();
    647   _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.filtered();
    648   _currentParameters.codecWidth = _codecWidth;
    649   _currentParameters.codecHeight = _codecHeight;
    650   _currentParameters.numLayers = _numLayers;
    651   return _selectedMethod->UpdateParameters(&_currentParameters);
    652 }
    653 
    654 VCMProtectionMethod* VCMLossProtectionLogic::SelectedMethod() const {
    655   return _selectedMethod.get();
    656 }
    657 
    658 VCMProtectionMethodEnum VCMLossProtectionLogic::SelectedType() const {
    659   return _selectedMethod ? _selectedMethod->Type() : kNone;
    660 }
    661 
    662 void VCMLossProtectionLogic::Reset(int64_t nowMs) {
    663   _lastPrUpdateT = nowMs;
    664   _lastPacketPerFrameUpdateT = nowMs;
    665   _lastPacketPerFrameUpdateTKey = nowMs;
    666   _lossPr255.Reset(0.9999f);
    667   _packetsPerFrame.Reset(0.9999f);
    668   _fecRateDelta = _fecRateKey = 0;
    669   for (int32_t i = 0; i < kLossPrHistorySize; i++) {
    670     _lossPrHistory[i].lossPr255 = 0;
    671     _lossPrHistory[i].timeMs = -1;
    672   }
    673   _shortMaxLossPr255 = 0;
    674   Release();
    675 }
    676 
    677 void VCMLossProtectionLogic::Release() {
    678   _selectedMethod.reset();
    679 }
    680 
    681 }  // namespace media_optimization
    682 }  // namespace webrtc
    683