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