text: /*
   *  Copyright (c) 2011 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 "video_coding_defines.h"
  #include "fec_tables_xor.h"
  #include "er_tables_xor.h"
  #include "nack_fec_tables.h"
  #include "qm_select_data.h"
  #include "media_opt_util.h"
  
  #include <math.h>
  #include <float.h>
  #include <limits.h>
  
  namespace webrtc {
  
  void
  VCMProtectionMethod::UpdateContentMetrics(const
                                            VideoContentMetrics* contentMetrics)
  {
     _qmRobustness->UpdateContent(contentMetrics);
  }
  
  
  VCMNackFecMethod::VCMNackFecMethod() : VCMProtectionMethod(kNackFec)
  {
      _fecMethod = new VCMFecMethod();
  }
  
  VCMNackFecMethod::~VCMNackFecMethod()
  {
      delete _fecMethod;
  }
  bool
  VCMNackFecMethod::ProtectionFactor(const
                                     VCMProtectionParameters* parameters)
  {
      // Hybrid Nack FEC has three operational modes:
      // 1. Low RTT - Nack only (Set FEC rates to zero)
      // 2. High RTT - FEC Only
      // 3. Medium RTT values - Hybrid ; in hybrid mode, we will only nack the
      //    residual following the decoding of the FEC (refer to JB logic)
  
      // Low RTT - NACK only mode
      if (parameters->rtt < kLowRttNackMs)
      {
          // Set the FEC parameters to 0
          _protectionFactorK = 0;
          _protectionFactorD = 0;
          return true;
      }
  
      // Otherwise: we count on FEC; if the RTT is below a threshold, then we
      // nack the residual, based on a decision made in the JB.
  
      // Compute the protection factors
      _fecMethod->ProtectionFactor(parameters);
  
      _protectionFactorK = _fecMethod->_protectionFactorK;
      _protectionFactorD = _fecMethod->_protectionFactorD;
  
      // When in Hybrid mode, correct FEC rates based on the
      // RTT (NACK effectiveness)
      if (parameters->rtt < kHighRttNackMs)
      {
          WebRtc_UWord16 rttIndex = (WebRtc_UWord16) parameters->rtt;
          // TODO(mikhal): update table
          float softnessRtt = (float)VCMNackFecTable[rttIndex] / (float)4096.0;
  
          // Soften FEC with NACK on (for delta frame only)
          // table depends on RTT relative to rttMax (NACK Threshold)
          _protectionFactorD *= static_cast<WebRtc_UWord8> (softnessRtt);
      }
      return true;
  }
  
  bool
  VCMNackFecMethod::EffectivePacketLoss(const
                                        VCMProtectionParameters* parameters)
  {
      // NACK only mode
      if (parameters->rtt < kLowRttNackMs)
      {
          // Effective Packet Loss, NA in current version.
          _effectivePacketLoss = 0;
          // No FEC applied.
          _residualPacketLossFec = 255 * parameters->lossPr;
          return true;
      }
  
      // Set the effective packet loss for encoder (based on FEC code).
      // Compute the effective packet loss and residual packet loss due to FEC.
      _fecMethod->EffectivePacketLoss(parameters);
      _effectivePacketLoss = _fecMethod->_effectivePacketLoss;
      _residualPacketLossFec = _fecMethod->_residualPacketLossFec;
  
      return true;
  }
  
  bool
  VCMNackFecMethod::UpdateParameters(const VCMProtectionParameters* parameters)
  {
      ProtectionFactor(parameters);
      EffectivePacketLoss(parameters);
  
      // Efficiency computation is based on FEC and NACK
  
      // Add FEC cost: ignore I frames for now
      float fecRate = static_cast<float> (_protectionFactorD) / 255.0f;
      _efficiency = parameters->bitRate * fecRate;
  
      // Add NACK cost, when applicable
      if (parameters->rtt < kHighRttNackMs)
      {
          // nackCost  = (bitRate - nackCost) * (lossPr)
          _efficiency += parameters->bitRate * _residualPacketLossFec /
                         (1.0f + _residualPacketLossFec);
      }
  
      // Protection/fec rates obtained above are defined relative to total number
      // of packets (total rate: source + fec) FEC in RTP module assumes
      // protection factor is defined relative to source number of packets so we
      // should convert the factor to reduce mismatch between mediaOpt's rate and
      // the actual one
      _protectionFactorK = _fecMethod->ConvertFECRate(_protectionFactorK);
      _protectionFactorD = _fecMethod->ConvertFECRate(_protectionFactorK);
  
      return true;
  }
  
  bool
  VCMNackMethod::EffectivePacketLoss(WebRtc_UWord8 packetLoss,
                                     WebRtc_UWord16 rttTime)
  {
      WebRtc_UWord16 rttMax = MaxRttNack();
  
      // For large RTT, we should rely on some Error Resilience, so we set
      // packetLossEnc = 0 for RTT less than the NACK threshold
      if (rttTime < rttMax)
      {
          _effectivePacketLoss = 0; // may want a softer transition here
      }
      else
      {
          _effectivePacketLoss = packetLoss;
      }
  
      return true;
  }
  
  bool
  VCMNackMethod::UpdateParameters(const VCMProtectionParameters* parameters)
  {
      // Compute the effective packet loss for ER
      WebRtc_UWord8 effPacketLoss = (WebRtc_UWord8) (255 * parameters->lossPr);
      WebRtc_UWord16 rttTime = (WebRtc_UWord16) parameters->rtt;
      EffectivePacketLoss(effPacketLoss, rttTime);
  
      // Compute the NACK bit cost
      if (rttTime > _NACK_MAX_RTT)
      {
          _efficiency = 0.0f;
          return false;
      }
  
      _efficiency = parameters->bitRate * parameters->lossPr /
                    (1.0f + parameters->lossPr);
      return true;
  }
  
  WebRtc_UWord8
  VCMFecMethod::BoostCodeRateKey(WebRtc_UWord8 packetFrameDelta,
                                 WebRtc_UWord8 packetFrameKey) const
  {
      WebRtc_UWord8 boostRateKey = 2;
      // Default: ratio scales the FEC protection up for I frames
      WebRtc_UWord8 ratio = 1;
  
      if (packetFrameDelta > 0)
      {
          ratio = (WebRtc_Word8) (packetFrameKey / packetFrameDelta);
      }
      ratio = VCM_MAX(boostRateKey, ratio);
  
      return ratio;
  }
  
  WebRtc_UWord8
  VCMFecMethod::ConvertFECRate(WebRtc_UWord8 codeRateRTP) const
  {
      return static_cast<WebRtc_UWord8> (VCM_MIN(255,(0.5 + 255.0 * codeRateRTP /
                                        (float)(255 - codeRateRTP))));
  }
  
  // AvgRecoveryFEC: average recovery from FEC, assuming random packet loss model
  // Computed offline for a range of FEC code parameters and loss rates
  float
  VCMFecMethod::AvgRecoveryFEC(const VCMProtectionParameters* parameters) const
  {
      // Total (avg) bits available per frame: total rate over actual/sent frame
      // rate units are kbits/frame
      const WebRtc_UWord16 bitRatePerFrame = static_cast<WebRtc_UWord16>
                          (parameters->bitRate / (parameters->frameRate));
  
      // Total (avg) number of packets per frame (source and fec):
      const WebRtc_UWord8 avgTotPackets = 1 +
                          (WebRtc_UWord8) ((float) bitRatePerFrame * 1000.0
                          / (float) (8.0 * _maxPayloadSize) + 0.5);
  
      // parameters for tables
      const WebRtc_UWord8 codeSize = 24;
      const WebRtc_UWord8 plossMax = 129;
      const WebRtc_UWord16 maxErTableSize = 38700;
  
      // Get index for table
      const float protectionFactor = (float) _protectionFactorD / (float) 255;
      WebRtc_UWord8 fecPacketsPerFrame = (WebRtc_UWord8) (0.5 + protectionFactor
                                                          * avgTotPackets);
      WebRtc_UWord8 sourcePacketsPerFrame = avgTotPackets - fecPacketsPerFrame;
  
      if (fecPacketsPerFrame == 0)
      {
          return 0.0; // no protection, so avg. recov from FEC == 0
      }
  
      // table defined up to codeSizexcodeSize code
      if (sourcePacketsPerFrame > codeSize)
      {
          sourcePacketsPerFrame = codeSize;
      }
  
      // check: protection factor is maxed at 50%, so this should never happen
      if (sourcePacketsPerFrame < 1)
      {
          assert("average number of source packets below 1\n");
      }
  
      // index for ER tables: up to codeSizexcodeSize mask
      WebRtc_UWord16 codeIndexTable[codeSize * codeSize];
      WebRtc_UWord16 k = 0;
      for (WebRtc_UWord8 i = 1; i <= codeSize; i++)
      {
          for (WebRtc_UWord8 j = 1; j <= i; j++)
          {
              codeIndexTable[(j - 1) * codeSize + i - 1] = k;
              k += 1;
          }
      }
  
      const WebRtc_UWord8 lossRate = (WebRtc_UWord8) (255.0 *
                                      parameters->lossPr + 0.5f);
  
      const WebRtc_UWord16 codeIndex = (fecPacketsPerFrame - 1) * codeSize
                                        + (sourcePacketsPerFrame - 1);
      const WebRtc_UWord16 indexTable = codeIndexTable[codeIndex] * plossMax
                                        + lossRate;
  
      const WebRtc_UWord16 codeIndex2 = (fecPacketsPerFrame) * codeSize
                                        + (sourcePacketsPerFrame);
      WebRtc_UWord16 indexTable2 = codeIndexTable[codeIndex2] * plossMax
                                   + lossRate;
  
      // checks on table index
      if (indexTable >= maxErTableSize)
      {
          assert("ER table index too large\n");
      }
  
      if (indexTable2 >= maxErTableSize)
      {
          indexTable2 = indexTable;
      }
  
      // Get the average effective packet loss recovery from FEC
      // this is from tables, computed using random loss model
      WebRtc_UWord8 avgFecRecov1 = 0;
      WebRtc_UWord8 avgFecRecov2 = 0;
      float avgFecRecov = 0;
  
      if (fecPacketsPerFrame > 0)
      {
          avgFecRecov1 = VCMAvgFECRecoveryXOR[indexTable];
          avgFecRecov2 = VCMAvgFECRecoveryXOR[indexTable2];
      }
  
      // interpolate over two FEC codes
      const float weightRpl = (float) (0.5 + protectionFactor * avgTotPackets)
          - (float) fecPacketsPerFrame;
      avgFecRecov = (float) weightRpl * (float) avgFecRecov2 + (float)
                    (1.0 - weightRpl) * (float) avgFecRecov1;
  
      return avgFecRecov;
  }
  
  bool
  VCMFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters)
  {
      // FEC PROTECTION SETTINGS: varies with packet loss and bitrate
  
      WebRtc_UWord8 packetLoss = (WebRtc_UWord8) (255 * parameters->lossPr);
  
      // No protection if (filtered) packetLoss is 0
      if (packetLoss == 0)
      {
          _protectionFactorK = 0;
          _protectionFactorD = 0;
           return true;
      }
  
      // Size of tables
      const WebRtc_UWord16 maxFecTableSize = 6450;
      // Parameters for range of rate and packet loss for tables
      const WebRtc_UWord8 ratePar1 = 5;
      const WebRtc_UWord8 ratePar2 = 49;
      const WebRtc_UWord8 plossMax = 129;
  
      const float bitRate = parameters->bitRate;
  
      // Total (avg) bits available per frame: total rate over actual/frame_rate.
      // Units are kbits/frame
      const WebRtc_UWord16 bitRatePerFrame = static_cast<WebRtc_UWord16>
                                                       (bitRate /
                                                       (parameters->frameRate));
  
      // TODO (marpan): Incorporate frame size (bpp) into FEC setting
  
      // Total (avg) number of packets per frame (source and fec):
      const WebRtc_UWord8 avgTotPackets = 1 + (WebRtc_UWord8)
                                          ((float) bitRatePerFrame * 1000.0
                                         / (float) (8.0 * _maxPayloadSize) + 0.5);
  
  
      // First partition protection: ~ 20%
      WebRtc_UWord8 firstPartitionProt = (WebRtc_UWord8) (255 * 0.20);
  
      // Threshold on packetLoss and bitRrate/frameRate (=average #packets),
      // above which we allocate protection to cover at least roughly
      // first partition size.
      WebRtc_UWord8 lossThr = 0;
      WebRtc_UWord8 packetNumThr = 1;
  
      // Modulation of protection with available bits/frame (or avgTotpackets)
      float weight1 = 0.5;
      float weight2 = 0.5;
      if (avgTotPackets > 4)
      {
          weight1 = 0.75;
          weight2 = 0.25;
      }
      if (avgTotPackets > 6)
      {
          weight1 = 1.5;
          weight2 = 0.;
      }
  
      // FEC rate parameters: for P and I frame
      WebRtc_UWord8 codeRateDelta = 0;
      WebRtc_UWord8 codeRateKey = 0;
  
      // Get index for table: the FEC protection depends on the (average)
      // available bits/frame. The range on the rate index corresponds to rates
      // (bps) from 200k to 8000k, for 30fps
      WebRtc_UWord8 rateIndexTable =
          (WebRtc_UWord8) VCM_MAX(VCM_MIN((bitRatePerFrame - ratePar1) /
                                          ratePar1, ratePar2), 0);
  
      // Restrict packet loss range to 50:
      // current tables defined only up to 50%
      if (packetLoss >= plossMax)
      {
          packetLoss = plossMax - 1;
      }
      WebRtc_UWord16 indexTable = rateIndexTable * plossMax + packetLoss;
  
      // Check on table index
      assert(indexTable < maxFecTableSize);
  
      // Protection factor for P frame
      codeRateDelta = VCMCodeRateXORTable[indexTable];
  
      if (packetLoss > lossThr && avgTotPackets > packetNumThr)
      {
          // Average with minimum protection level given by (average) total
          // number of packets
          codeRateDelta = static_cast<WebRtc_UWord8>((weight1 *
              (float) codeRateDelta + weight2 * 255.0 / (float) avgTotPackets));
  
          // Set a minimum based on first partition size.
          if (codeRateDelta < firstPartitionProt)
          {
              codeRateDelta = firstPartitionProt;
          }
      }
  
      // Check limit on amount of protection for P frame; 50% is max.
      if (codeRateDelta >= plossMax)
      {
          codeRateDelta = plossMax - 1;
      }
  
      float adjustFec = _qmRobustness->AdjustFecFactor(codeRateDelta, bitRate,
                                                       parameters->frameRate,
                                                       parameters->rtt,
                                                       packetLoss);
  
      codeRateDelta = static_cast<WebRtc_UWord8>(codeRateDelta * adjustFec);
  
      // For Key frame:
      // Effectively at a higher rate, so we scale/boost the rate
      // The boost factor may depend on several factors: ratio of packet
      // number of I to P frames, how much protection placed on P frames, etc.
      const WebRtc_UWord8 packetFrameDelta = (WebRtc_UWord8)
                                             (0.5 + parameters->packetsPerFrame);
      const WebRtc_UWord8 packetFrameKey = (WebRtc_UWord8)
                                           (0.5 + parameters->packetsPerFrameKey);
      const WebRtc_UWord8 boostKey = BoostCodeRateKey(packetFrameDelta,
                                                      packetFrameKey);
  
      rateIndexTable = (WebRtc_UWord8) VCM_MAX(VCM_MIN(
                        1 + (boostKey * bitRatePerFrame - ratePar1) /
                        ratePar1,ratePar2),0);
      WebRtc_UWord16 indexTableKey = rateIndexTable * plossMax + packetLoss;
  
      indexTableKey = VCM_MIN(indexTableKey, maxFecTableSize);
  
      // Check on table index
      assert(indexTableKey < maxFecTableSize);
  
      // Protection factor for I frame
      codeRateKey = VCMCodeRateXORTable[indexTableKey];
  
      // Boosting for Key frame.
      WebRtc_UWord32 boostKeyProt = _scaleProtKey * codeRateDelta;
      if ( boostKeyProt >= plossMax)
      {
          boostKeyProt = plossMax - 1;
      }
  
      // Make sure I frame protection is at least larger than P frame protection,
      // and at least as high as filtered packet loss.
      codeRateKey = static_cast<WebRtc_UWord8> (VCM_MAX(packetLoss,
              VCM_MAX(boostKeyProt, codeRateKey)));
  
      // Check limit on amount of protection for I frame: 50% is max.
      if (codeRateKey >= plossMax)
      {
          codeRateKey = plossMax - 1;
      }
  
      _protectionFactorK = codeRateKey;
      _protectionFactorD = codeRateDelta;
  
  
       // TODO (marpan): Set the UEP protection on/off for Key and Delta frames
      _useUepProtectionK = _qmRobustness->SetUepProtection(codeRateKey, bitRate,
                                                           packetLoss, 0);
  
      _useUepProtectionD = _qmRobustness->SetUepProtection(codeRateDelta, bitRate,
                                                           packetLoss, 1);
  
      // DONE WITH FEC PROTECTION SETTINGS
      return true;
  }
  
  bool
  VCMFecMethod::EffectivePacketLoss(const VCMProtectionParameters* parameters)
  {
      // Effective packet loss to encoder is based on RPL (residual packet loss)
      // this is a soft setting based on degree of FEC protection
      // RPL = received/input packet loss - average_FEC_recovery
      // note: received/input packet loss may be filtered based on FilteredLoss
  
      // The packet loss:
      WebRtc_UWord8 packetLoss = (WebRtc_UWord8) (255 * parameters->lossPr);
  
      float avgFecRecov = AvgRecoveryFEC(parameters);
  
      // Residual Packet Loss:
      _residualPacketLossFec = (float) (packetLoss - avgFecRecov) / 255.0f;
  
      // Effective Packet Loss, NA in current version.
      _effectivePacketLoss = 0;
  
      return true;
  }
  
  bool
  VCMFecMethod::UpdateParameters(const VCMProtectionParameters* parameters)
  {
      // Compute the protection factor
      ProtectionFactor(parameters);
  
      // Compute the effective packet loss
      EffectivePacketLoss(parameters);
  
      // Compute the bit cost
      // Ignore key frames for now.
      float fecRate = static_cast<float> (_protectionFactorD) / 255.0f;
      if (fecRate >= 0.0f)
      {
          // use this formula if the fecRate (protection factor) is defined
          // relative to number of source packets
          // this is the case for the previous tables:
          // _efficiency = parameters->bitRate * ( 1.0 - 1.0 / (1.0 + fecRate));
  
          // in the new tables, the fecRate is defined relative to total number of
          // packets (total rate), so overhead cost is:
          _efficiency = parameters->bitRate * fecRate;
      }
      else
      {
          _efficiency = 0.0f;
      }
  
      // Protection/fec rates obtained above is defined relative to total number
      // of packets (total rate: source+fec) FEC in RTP module assumes protection
      // factor is defined relative to source number of packets so we should
      // convert the factor to reduce mismatch between mediaOpt suggested rate and
      // the actual rate
      _protectionFactorK = ConvertFECRate(_protectionFactorK);
      _protectionFactorD = ConvertFECRate(_protectionFactorD);
  
      return true;
  }
  
  bool
  VCMIntraReqMethod::UpdateParameters(const VCMProtectionParameters* parameters)
  {
      float packetRate = parameters->packetsPerFrame * parameters->frameRate;
      // Assume that all lost packets cohere to different frames
      float lossRate = parameters->lossPr * packetRate;
      if (parameters->keyFrameSize <= 1e-3)
      {
          return false;
      }
      _efficiency = lossRate * parameters->keyFrameSize;
      if (parameters->lossPr >= 1.0f / parameters->keyFrameSize ||
          parameters->rtt > _IREQ_MAX_RTT)
      {
          return false;
      }
      return true;
  }
  
  bool
  VCMPeriodicIntraMethod::UpdateParameters(const
                                          VCMProtectionParameters* /*parameters*/)
  {
      // Periodic I-frames. The last thing we want to use.
      _efficiency = 0.0f;
      return true;
  }
  
  bool
  VCMMbIntraRefreshMethod::UpdateParameters(const
                                            VCMProtectionParameters* parameters)
  {
      // Assume optimal for now.
      _efficiency = parameters->bitRate * parameters->lossPr /
                    (1.0f + parameters->lossPr);
      if (parameters->bitRate < _MBREF_MIN_BITRATE)
      {
          return false;
      }
      return true;
  }
  
  WebRtc_UWord16
  VCMNackMethod::MaxRttNack() const
  {
      return _NACK_MAX_RTT;
  }
  
  VCMLossProtectionLogic::~VCMLossProtectionLogic()
  {
      ClearLossProtections();
  }
  
  void
  VCMLossProtectionLogic::ClearLossProtections()
  {
      ListItem *item;
      while ((item = _availableMethods.First()) != 0) {
        VCMProtectionMethod *method = static_cast<VCMProtectionMethod*>
                                      (item->GetItem());
        if (method != NULL)
        {
            delete method;
        }
        _availableMethods.PopFront();
      }
      _selectedMethod = NULL;
  }
  
  bool
  VCMLossProtectionLogic::AddMethod(VCMProtectionMethod *newMethod)
  {
      VCMProtectionMethod *method;
      ListItem *item;
      if (newMethod == NULL)
      {
          return false;
      }
      for (item = _availableMethods.First(); item != NULL;
          item = _availableMethods.Next(item))
      {
          method = static_cast<VCMProtectionMethod *> (item->GetItem());
          if (method != NULL && method->Type() == newMethod->Type())
          {
              return false;
          }
      }
      _availableMethods.PushBack(newMethod);
      return true;
  }
  bool
  VCMLossProtectionLogic::RemoveMethod(VCMProtectionMethodEnum methodType)
  {
      VCMProtectionMethod *method;
      ListItem *item;
      bool foundAndRemoved = false;
      for (item = _availableMethods.First(); item != NULL;
           item = _availableMethods.Next(item))
      {
          method = static_cast<VCMProtectionMethod *> (item->GetItem());
          if (method != NULL && method->Type() == methodType)
          {
              if (_selectedMethod != NULL &&
                  _selectedMethod->Type() == method->Type())
              {
                  _selectedMethod = NULL;
              }
              _availableMethods.Erase(item);
              item = NULL;
              delete method;
              foundAndRemoved = true;
          }
      }
      return foundAndRemoved;
  }
  
  VCMProtectionMethod*
  VCMLossProtectionLogic::FindMethod(VCMProtectionMethodEnum methodType) const
  {
      VCMProtectionMethod *method;
      ListItem *item;
      for (item = _availableMethods.First(); item != NULL;
           item = _availableMethods.Next(item))
      {
          method = static_cast<VCMProtectionMethod *> (item->GetItem());
          if (method != NULL && method->Type() == methodType)
          {
              return method;
          }
      }
      return NULL;
  }
  
  float
  VCMLossProtectionLogic::HighestOverhead() const
  {
      VCMProtectionMethod *method;
      ListItem *item;
      float highestOverhead = 0.0f;
      for (item = _availableMethods.First(); item != NULL;
           item = _availableMethods.Next(item))
      {
          method = static_cast<VCMProtectionMethod *> (item->GetItem());
          if (method != NULL && method->RequiredBitRate() > highestOverhead)
          {
              highestOverhead = method->RequiredBitRate();
          }
      }
      return highestOverhead;
  }
  
  void
  VCMLossProtectionLogic::UpdateRtt(WebRtc_UWord32 rtt)
  {
      _rtt = rtt;
  }
  
  void
  VCMLossProtectionLogic::UpdateResidualPacketLoss(float residualPacketLoss)
  {
      _residualPacketLossFec = residualPacketLoss;
  }
  
  void
  VCMLossProtectionLogic::UpdateFecType(VCMFecTypes fecType)
  {
      _fecType = fecType;
  }
  
  void
  VCMLossProtectionLogic::UpdateLossPr(WebRtc_UWord8 lossPr255)
  {
      const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
      UpdateMaxLossHistory(lossPr255, now);
      _lossPr255.Apply(static_cast<float> (now - _lastPrUpdateT),
                       static_cast<float> (lossPr255));
      _lastPrUpdateT = now;
      _lossPr = _lossPr255.Value() / 255.0f;
  }
  
  void
  VCMLossProtectionLogic::UpdateMaxLossHistory(WebRtc_UWord8 lossPr255,
                                               WebRtc_Word64 now)
  {
      if (_lossPrHistory[0].timeMs >= 0 &&
          now - _lossPrHistory[0].timeMs < kLossPrShortFilterWinMs)
      {
          if (lossPr255 > _shortMaxLossPr255)
          {
              _shortMaxLossPr255 = lossPr255;
          }
      }
      else
      {
          // Only add a new value to the history once a second
          if (_lossPrHistory[0].timeMs == -1)
          {
              // First, no shift
              _shortMaxLossPr255 = lossPr255;
          }
          else
          {
              // Shift
              for (WebRtc_Word32 i = (kLossPrHistorySize - 2); i >= 0; i--)
              {
                  _lossPrHistory[i + 1].lossPr255 = _lossPrHistory[i].lossPr255;
                  _lossPrHistory[i + 1].timeMs = _lossPrHistory[i].timeMs;
              }
          }
          if (_shortMaxLossPr255 == 0)
          {
              _shortMaxLossPr255 = lossPr255;
          }
  
          _lossPrHistory[0].lossPr255 = _shortMaxLossPr255;
          _lossPrHistory[0].timeMs = now;
          _shortMaxLossPr255 = 0;
      }
  }
  
  WebRtc_UWord8
  VCMLossProtectionLogic::MaxFilteredLossPr(WebRtc_Word64 nowMs) const
  {
      WebRtc_UWord8 maxFound = _shortMaxLossPr255;
      if (_lossPrHistory[0].timeMs == -1)
      {
          return maxFound;
      }
      for (WebRtc_Word32 i = 0; i < kLossPrHistorySize; i++)
      {
          if (_lossPrHistory[i].timeMs == -1)
          {
              break;
          }
          if (nowMs - _lossPrHistory[i].timeMs >
              kLossPrHistorySize * kLossPrShortFilterWinMs)
          {
              // This sample (and all samples after this) is too old
              break;
          }
          if (_lossPrHistory[i].lossPr255 > maxFound)
          {
              // This sample is the largest one this far into the history
              maxFound = _lossPrHistory[i].lossPr255;
          }
      }
      return maxFound;
  }
  
  WebRtc_UWord8
  VCMLossProtectionLogic::FilteredLoss() const
  {
      //take the average received loss
      //return static_cast<WebRtc_UWord8>(_lossPr255.Value() + 0.5f);
  
      //TODO: Update for hybrid
      //take the windowed max of the received loss
      if (_selectedMethod != NULL && _selectedMethod->Type() == kFEC)
      {
          return MaxFilteredLossPr(VCMTickTime::MillisecondTimestamp());
      }
      else
      {
          return static_cast<WebRtc_UWord8> (_lossPr255.Value() + 0.5);
      }
  }
  
  void
  VCMLossProtectionLogic::UpdateFilteredLossPr(WebRtc_UWord8 packetLossEnc)
  {
      _lossPr = (float) packetLossEnc / (float) 255.0;
  }
  
  void
  VCMLossProtectionLogic::UpdateBitRate(float bitRate)
  {
      _bitRate = bitRate;
  }
  
  void
  VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets)
  {
      const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
      _packetsPerFrame.Apply(static_cast<float>(now - _lastPacketPerFrameUpdateT),
                             nPackets);
      _lastPacketPerFrameUpdateT = now;
  }
  
  void
  VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets)
  {
      const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
      _packetsPerFrameKey.Apply(static_cast<float>(now -
                                _lastPacketPerFrameUpdateTKey), nPackets);
      _lastPacketPerFrameUpdateTKey = now;
  }
  
  void
  VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize)
  {
      _keyFrameSize = keyFrameSize;
  }
  
  void
  VCMLossProtectionLogic::UpdateFrameSize(WebRtc_UWord16 width,
                                          WebRtc_UWord16 height)
  {
      _codecWidth = width;
      _codecHeight = height;
  }
  
  bool
  VCMLossProtectionLogic::UpdateMethod(VCMProtectionMethod *newMethod /*=NULL */)
  {
      _currentParameters.rtt = _rtt;
      _currentParameters.lossPr = _lossPr;
      _currentParameters.bitRate = _bitRate;
      _currentParameters.frameRate = _frameRate; // rename actual frame rate?
      _currentParameters.keyFrameSize = _keyFrameSize;
      _currentParameters.fecRateDelta = _fecRateDelta;
      _currentParameters.fecRateKey = _fecRateKey;
      _currentParameters.packetsPerFrame = _packetsPerFrame.Value();
      _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.Value();
      _currentParameters.residualPacketLossFec = _residualPacketLossFec;
      _currentParameters.fecType = _fecType;
      _currentParameters.codecWidth = _codecWidth;
      _currentParameters.codecHeight = _codecHeight;
  
      if (newMethod == NULL)
      {
          //_selectedMethod = _bestNotOkMethod = NULL;
          VCMProtectionMethod *method;
          ListItem *item;
          for (item = _availableMethods.First(); item != NULL;
               item = _availableMethods.Next(item))
          {
              method = static_cast<VCMProtectionMethod *> (item->GetItem());
              if (method != NULL)
              {
                  if (method->Type() == kFEC)
                  {
                        _selectedMethod = method;
                  }
                  if (method->Type() == kNACK)
                  {
                      _selectedMethod = method;
                  }
                  if (method->Type() == kNackFec)
                  {
                      _selectedMethod = method;
                  }
                  method->UpdateParameters(&_currentParameters);
              }
          }
          if (_selectedMethod != NULL && _selectedMethod->Type() != kFEC)
          {
              _selectedMethod = method;
          }
       }
       else
       {
           _selectedMethod = newMethod;
           _selectedMethod->UpdateParameters(&_currentParameters);
       }
      return true;
  }
  
  VCMProtectionMethod*
  VCMLossProtectionLogic::SelectedMethod() const
  {
      return _selectedMethod;
  }
  
  void
  VCMLossProtectionLogic::Reset()
  {
      const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
      _lastPrUpdateT = now;
      _lastPacketPerFrameUpdateT = now;
      _lastPacketPerFrameUpdateTKey = now;
      _lossPr255.Reset(0.9999f);
      _packetsPerFrame.Reset(0.9999f);
      _fecRateDelta = _fecRateKey = 0;
      for (WebRtc_Word32 i = 0; i < kLossPrHistorySize; i++)
      {
          _lossPrHistory[i].lossPr255 = 0;
          _lossPrHistory[i].timeMs = -1;
      }
      _shortMaxLossPr255 = 0;
      ClearLossProtections();
  }
  
  }
  