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