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