tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

media_opt_util.cc (24206B)


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