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
25namespace webrtc {
26// Max value of loss rates in off-line model
27static const int kPacketLossMax = 129;
28
29namespace media_optimization {
30
31VCMProtectionMethod::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
43VCMProtectionMethod::~VCMProtectionMethod() {
44  delete _qmRobustness;
45}
46void VCMProtectionMethod::UpdateContentMetrics(
47    const VideoContentMetrics* contentMetrics) {
48  _qmRobustness->UpdateContent(contentMetrics);
49}
50
51VCMNackFecMethod::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
64VCMNackFecMethod::~VCMNackFecMethod() {
65  //
66}
67bool 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
105int 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
132int VCMNackFecMethod::MaxFramesFec() const {
133  return _maxFramesFec;
134}
135
136bool 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
162bool 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
170bool 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
191VCMNackMethod::VCMNackMethod() : VCMProtectionMethod() {
192  _type = kNack;
193}
194
195VCMNackMethod::~VCMNackMethod() {
196  //
197}
198
199bool VCMNackMethod::EffectivePacketLoss(
200    const VCMProtectionParameters* parameter) {
201  // Effective Packet Loss, NA in current version.
202  _effectivePacketLoss = 0;
203  return true;
204}
205
206bool 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
215VCMFecMethod::VCMFecMethod() : VCMProtectionMethod() {
216  _type = kFec;
217}
218VCMFecMethod::~VCMFecMethod() {
219  //
220}
221
222uint8_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
236uint8_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
243void VCMFecMethod::UpdateProtectionFactorD(uint8_t protectionFactorD) {
244  _protectionFactorD = protectionFactorD;
245}
246
247// Update FEC with protectionFactorK
248void VCMFecMethod::UpdateProtectionFactorK(uint8_t protectionFactorK) {
249  _protectionFactorK = protectionFactorK;
250}
251
252bool 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
425int 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
441bool 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
454bool 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}
471VCMLossProtectionLogic::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
492VCMLossProtectionLogic::~VCMLossProtectionLogic() {
493  Release();
494}
495
496void 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
518void VCMLossProtectionLogic::UpdateRtt(int64_t rtt) {
519  _rtt = rtt;
520}
521
522void 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
551uint8_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
573uint8_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
601void VCMLossProtectionLogic::UpdateFilteredLossPr(uint8_t packetLossEnc) {
602  _lossPr = static_cast<float>(packetLossEnc) / 255.0;
603}
604
605void VCMLossProtectionLogic::UpdateBitRate(float bitRate) {
606  _bitRate = bitRate;
607}
608
609void VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets,
610                                                   int64_t nowMs) {
611  _packetsPerFrame.Apply(static_cast<float>(nowMs - _lastPacketPerFrameUpdateT),
612                         nPackets);
613  _lastPacketPerFrameUpdateT = nowMs;
614}
615
616void VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets,
617                                                      int64_t nowMs) {
618  _packetsPerFrameKey.Apply(
619      static_cast<float>(nowMs - _lastPacketPerFrameUpdateTKey), nPackets);
620  _lastPacketPerFrameUpdateTKey = nowMs;
621}
622
623void VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize) {
624  _keyFrameSize = keyFrameSize;
625}
626
627void VCMLossProtectionLogic::UpdateFrameSize(uint16_t width, uint16_t height) {
628  _codecWidth = width;
629  _codecHeight = height;
630}
631
632void VCMLossProtectionLogic::UpdateNumLayers(int numLayers) {
633  _numLayers = (numLayers == 0) ? 1 : numLayers;
634}
635
636bool 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
654VCMProtectionMethod* VCMLossProtectionLogic::SelectedMethod() const {
655  return _selectedMethod.get();
656}
657
658VCMProtectionMethodEnum VCMLossProtectionLogic::SelectedType() const {
659  return _selectedMethod ? _selectedMethod->Type() : kNone;
660}
661
662void 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
677void VCMLossProtectionLogic::Release() {
678  _selectedMethod.reset();
679}
680
681}  // namespace media_optimization
682}  // namespace webrtc
683