• 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 "webrtc/modules/video_coding/main/source/media_opt_util.h"
12 
13 #include <algorithm>
14 #include <float.h>
15 #include <limits.h>
16 #include <math.h>
17 
18 #include "webrtc/modules/interface/module_common_types.h"
19 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
20 #include "webrtc/modules/video_coding/main/interface/video_coding_defines.h"
21 #include "webrtc/modules/video_coding/main/source/er_tables_xor.h"
22 #include "webrtc/modules/video_coding/main/source/fec_tables_xor.h"
23 #include "webrtc/modules/video_coding/main/source/nack_fec_tables.h"
24 
25 namespace webrtc {
26 namespace media_optimization {
27 
VCMProtectionMethod()28 VCMProtectionMethod::VCMProtectionMethod():
29 _effectivePacketLoss(0),
30 _protectionFactorK(0),
31 _protectionFactorD(0),
32 _residualPacketLossFec(0.0f),
33 _scaleProtKey(2.0f),
34 _maxPayloadSize(1460),
35 _qmRobustness(new VCMQmRobustness()),
36 _useUepProtectionK(false),
37 _useUepProtectionD(true),
38 _corrFecCost(1.0),
39 _type(kNone),
40 _efficiency(0)
41 {
42     //
43 }
44 
~VCMProtectionMethod()45 VCMProtectionMethod::~VCMProtectionMethod()
46 {
47     delete _qmRobustness;
48 }
49 void
UpdateContentMetrics(const VideoContentMetrics * contentMetrics)50 VCMProtectionMethod::UpdateContentMetrics(const
51                                           VideoContentMetrics* contentMetrics)
52 {
53     _qmRobustness->UpdateContent(contentMetrics);
54 }
55 
VCMNackFecMethod(int lowRttNackThresholdMs,int highRttNackThresholdMs)56 VCMNackFecMethod::VCMNackFecMethod(int lowRttNackThresholdMs,
57                                    int highRttNackThresholdMs)
58     : VCMFecMethod(),
59       _lowRttNackMs(lowRttNackThresholdMs),
60       _highRttNackMs(highRttNackThresholdMs),
61       _maxFramesFec(1) {
62   assert(lowRttNackThresholdMs >= -1 && highRttNackThresholdMs >= -1);
63   assert(highRttNackThresholdMs == -1 ||
64          lowRttNackThresholdMs <= highRttNackThresholdMs);
65   assert(lowRttNackThresholdMs > -1 || highRttNackThresholdMs == -1);
66   _type = kNackFec;
67 }
68 
~VCMNackFecMethod()69 VCMNackFecMethod::~VCMNackFecMethod()
70 {
71     //
72 }
73 bool
ProtectionFactor(const VCMProtectionParameters * parameters)74 VCMNackFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters)
75 {
76     // Hybrid Nack FEC has three operational modes:
77     // 1. Low RTT (below kLowRttNackMs) - Nack only: Set FEC rate
78     //    (_protectionFactorD) to zero. -1 means no FEC.
79     // 2. High RTT (above _highRttNackMs) - FEC Only: Keep FEC factors.
80     //    -1 means always allow NACK.
81     // 3. Medium RTT values - Hybrid mode: We will only nack the
82     //    residual following the decoding of the FEC (refer to JB logic). FEC
83     //    delta protection factor will be adjusted based on the RTT.
84 
85     // Otherwise: we count on FEC; if the RTT is below a threshold, then we
86     // nack the residual, based on a decision made in the JB.
87 
88     // Compute the protection factors
89     VCMFecMethod::ProtectionFactor(parameters);
90     if (_lowRttNackMs == -1 || parameters->rtt < _lowRttNackMs)
91     {
92         _protectionFactorD = 0;
93         VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
94     }
95 
96     // When in Hybrid mode (RTT range), adjust FEC rates based on the
97     // RTT (NACK effectiveness) - adjustment factor is in the range [0,1].
98     else if (_highRttNackMs == -1 || parameters->rtt < _highRttNackMs)
99     {
100         // TODO(mikhal): Disabling adjustment temporarily.
101         // uint16_t rttIndex = (uint16_t) parameters->rtt;
102         float adjustRtt = 1.0f;// (float)VCMNackFecTable[rttIndex] / 100.0f;
103 
104         // Adjust FEC with NACK on (for delta frame only)
105         // table depends on RTT relative to rttMax (NACK Threshold)
106         _protectionFactorD = static_cast<uint8_t>
107                             (adjustRtt *
108                              static_cast<float>(_protectionFactorD));
109         // update FEC rates after applying adjustment
110         VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
111     }
112 
113     return true;
114 }
115 
ComputeMaxFramesFec(const VCMProtectionParameters * parameters)116 int VCMNackFecMethod::ComputeMaxFramesFec(
117     const VCMProtectionParameters* parameters) {
118   if (parameters->numLayers > 2) {
119     // For more than 2 temporal layers we will only have FEC on the base layer,
120     // and the base layers will be pretty far apart. Therefore we force one
121     // frame FEC.
122     return 1;
123   }
124   // We set the max number of frames to base the FEC on so that on average
125   // we will have complete frames in one RTT. Note that this is an upper
126   // bound, and that the actual number of frames used for FEC is decided by the
127   // RTP module based on the actual number of packets and the protection factor.
128   float base_layer_framerate = parameters->frameRate /
129       static_cast<float>(1 << (parameters->numLayers - 1));
130   int max_frames_fec = std::max(static_cast<int>(
131       2.0f * base_layer_framerate * parameters->rtt /
132       1000.0f + 0.5f), 1);
133   // |kUpperLimitFramesFec| is the upper limit on how many frames we
134   // allow any FEC to be based on.
135   if (max_frames_fec > kUpperLimitFramesFec) {
136     max_frames_fec = kUpperLimitFramesFec;
137   }
138   return max_frames_fec;
139 }
140 
MaxFramesFec() const141 int VCMNackFecMethod::MaxFramesFec() const {
142   return _maxFramesFec;
143 }
144 
BitRateTooLowForFec(const VCMProtectionParameters * parameters)145 bool VCMNackFecMethod::BitRateTooLowForFec(
146     const VCMProtectionParameters* parameters) {
147   // Bitrate below which we turn off FEC, regardless of reported packet loss.
148   // The condition should depend on resolution and content. For now, use
149   // threshold on bytes per frame, with some effect for the frame size.
150   // The condition for turning off FEC is also based on other factors,
151   // such as |_numLayers|, |_maxFramesFec|, and |_rtt|.
152   int estimate_bytes_per_frame = 1000 * BitsPerFrame(parameters) / 8;
153   int max_bytes_per_frame = kMaxBytesPerFrameForFec;
154   int num_pixels = parameters->codecWidth * parameters->codecHeight;
155   if (num_pixels <= 352 * 288) {
156     max_bytes_per_frame = kMaxBytesPerFrameForFecLow;
157   } else if (num_pixels > 640 * 480) {
158     max_bytes_per_frame = kMaxBytesPerFrameForFecHigh;
159   }
160   // TODO (marpan): add condition based on maximum frames used for FEC,
161   // and expand condition based on frame size.
162   if (estimate_bytes_per_frame < max_bytes_per_frame &&
163       parameters->numLayers < 3 &&
164       parameters->rtt < kMaxRttTurnOffFec) {
165     return true;
166   }
167   return false;
168 }
169 
170 bool
EffectivePacketLoss(const VCMProtectionParameters * parameters)171 VCMNackFecMethod::EffectivePacketLoss(const VCMProtectionParameters* parameters)
172 {
173     // Set the effective packet loss for encoder (based on FEC code).
174     // Compute the effective packet loss and residual packet loss due to FEC.
175     VCMFecMethod::EffectivePacketLoss(parameters);
176     return true;
177 }
178 
179 bool
UpdateParameters(const VCMProtectionParameters * parameters)180 VCMNackFecMethod::UpdateParameters(const VCMProtectionParameters* parameters)
181 {
182     ProtectionFactor(parameters);
183     EffectivePacketLoss(parameters);
184     _maxFramesFec = ComputeMaxFramesFec(parameters);
185     if (BitRateTooLowForFec(parameters)) {
186       _protectionFactorK = 0;
187       _protectionFactorD = 0;
188     }
189 
190     // Efficiency computation is based on FEC and NACK
191 
192     // Add FEC cost: ignore I frames for now
193     float fecRate = static_cast<float> (_protectionFactorD) / 255.0f;
194     _efficiency = parameters->bitRate * fecRate * _corrFecCost;
195 
196     // Add NACK cost, when applicable
197     if (_highRttNackMs == -1 || parameters->rtt < _highRttNackMs)
198     {
199         // nackCost  = (bitRate - nackCost) * (lossPr)
200         _efficiency += parameters->bitRate * _residualPacketLossFec /
201                        (1.0f + _residualPacketLossFec);
202     }
203 
204     // Protection/fec rates obtained above are defined relative to total number
205     // of packets (total rate: source + fec) FEC in RTP module assumes
206     // protection factor is defined relative to source number of packets so we
207     // should convert the factor to reduce mismatch between mediaOpt's rate and
208     // the actual one
209     _protectionFactorK = VCMFecMethod::ConvertFECRate(_protectionFactorK);
210     _protectionFactorD = VCMFecMethod::ConvertFECRate(_protectionFactorD);
211 
212     return true;
213 }
214 
VCMNackMethod()215 VCMNackMethod::VCMNackMethod():
216 VCMProtectionMethod()
217 {
218     _type = kNack;
219 }
220 
~VCMNackMethod()221 VCMNackMethod::~VCMNackMethod()
222 {
223     //
224 }
225 
226 bool
EffectivePacketLoss(const VCMProtectionParameters * parameter)227 VCMNackMethod::EffectivePacketLoss(const VCMProtectionParameters* parameter)
228 {
229     // Effective Packet Loss, NA in current version.
230     _effectivePacketLoss = 0;
231     return true;
232 }
233 
234 bool
UpdateParameters(const VCMProtectionParameters * parameters)235 VCMNackMethod::UpdateParameters(const VCMProtectionParameters* parameters)
236 {
237     // Compute the effective packet loss
238     EffectivePacketLoss(parameters);
239 
240     // nackCost  = (bitRate - nackCost) * (lossPr)
241     _efficiency = parameters->bitRate * parameters->lossPr /
242                   (1.0f + parameters->lossPr);
243     return true;
244 }
245 
VCMFecMethod()246 VCMFecMethod::VCMFecMethod():
247 VCMProtectionMethod()
248 {
249     _type = kFec;
250 }
~VCMFecMethod()251 VCMFecMethod::~VCMFecMethod()
252 {
253     //
254 }
255 
256 uint8_t
BoostCodeRateKey(uint8_t packetFrameDelta,uint8_t packetFrameKey) const257 VCMFecMethod::BoostCodeRateKey(uint8_t packetFrameDelta,
258                                uint8_t packetFrameKey) const
259 {
260     uint8_t boostRateKey = 2;
261     // Default: ratio scales the FEC protection up for I frames
262     uint8_t ratio = 1;
263 
264     if (packetFrameDelta > 0)
265     {
266         ratio = (int8_t) (packetFrameKey / packetFrameDelta);
267     }
268     ratio = VCM_MAX(boostRateKey, ratio);
269 
270     return ratio;
271 }
272 
273 uint8_t
ConvertFECRate(uint8_t codeRateRTP) const274 VCMFecMethod::ConvertFECRate(uint8_t codeRateRTP) const
275 {
276     return static_cast<uint8_t> (VCM_MIN(255,(0.5 + 255.0 * codeRateRTP /
277                                       (float)(255 - codeRateRTP))));
278 }
279 
280 // Update FEC with protectionFactorD
281 void
UpdateProtectionFactorD(uint8_t protectionFactorD)282 VCMFecMethod::UpdateProtectionFactorD(uint8_t protectionFactorD)
283 {
284     _protectionFactorD = protectionFactorD;
285 }
286 
287 // Update FEC with protectionFactorK
288 void
UpdateProtectionFactorK(uint8_t protectionFactorK)289 VCMFecMethod::UpdateProtectionFactorK(uint8_t protectionFactorK)
290 {
291     _protectionFactorK = protectionFactorK;
292 }
293 
294 // AvgRecoveryFEC: computes the residual packet loss (RPL) function.
295 // This is the average recovery from the FEC, assuming random packet loss model.
296 // Computed off-line for a range of FEC code parameters and loss rates.
297 float
AvgRecoveryFEC(const VCMProtectionParameters * parameters) const298 VCMFecMethod::AvgRecoveryFEC(const VCMProtectionParameters* parameters) const
299 {
300     // Total (avg) bits available per frame: total rate over actual/sent frame
301     // rate units are kbits/frame
302     const uint16_t bitRatePerFrame = static_cast<uint16_t>
303                         (parameters->bitRate / (parameters->frameRate));
304 
305     // Total (average) number of packets per frame (source and fec):
306     const uint8_t avgTotPackets = 1 + static_cast<uint8_t>
307                         (static_cast<float> (bitRatePerFrame * 1000.0) /
308                          static_cast<float> (8.0 * _maxPayloadSize) + 0.5);
309 
310     const float protectionFactor = static_cast<float>(_protectionFactorD) /
311                                                       255.0;
312 
313     // Round down for estimated #FEC packets/frame, to keep
314     // |fecPacketsPerFrame| <= |sourcePacketsPerFrame|.
315     uint8_t fecPacketsPerFrame = static_cast<uint8_t>
316                                       (protectionFactor * avgTotPackets);
317 
318     uint8_t sourcePacketsPerFrame = avgTotPackets - fecPacketsPerFrame;
319 
320     if ( (fecPacketsPerFrame == 0) || (sourcePacketsPerFrame == 0) )
321     {
322         // No protection, or rate too low: so average recovery from FEC == 0.
323         return 0.0;
324     }
325 
326     // Table defined up to kMaxNumPackets
327     if (sourcePacketsPerFrame > kMaxNumPackets)
328     {
329         sourcePacketsPerFrame = kMaxNumPackets;
330     }
331 
332     // Table defined up to kMaxNumPackets
333     if (fecPacketsPerFrame > kMaxNumPackets)
334     {
335         fecPacketsPerFrame = kMaxNumPackets;
336     }
337 
338     // Code index for tables: up to (kMaxNumPackets * kMaxNumPackets)
339     uint16_t codeIndexTable[kMaxNumPackets * kMaxNumPackets];
340     uint16_t k = 0;
341     for (uint8_t i = 1; i <= kMaxNumPackets; i++)
342     {
343         for (uint8_t j = 1; j <= i; j++)
344         {
345             codeIndexTable[(j - 1) * kMaxNumPackets + i - 1] = k;
346             k += 1;
347         }
348     }
349 
350     uint8_t lossRate = static_cast<uint8_t> (255.0 *
351                              parameters->lossPr + 0.5f);
352 
353     // Constrain lossRate to 50%: tables defined up to 50%
354     if (lossRate >= kPacketLossMax)
355     {
356         lossRate = kPacketLossMax - 1;
357     }
358 
359     const uint16_t codeIndex = (fecPacketsPerFrame - 1) * kMaxNumPackets +
360                                      (sourcePacketsPerFrame - 1);
361 
362     const uint16_t indexTable = codeIndexTable[codeIndex] * kPacketLossMax +
363                                       lossRate;
364 
365     // Check on table index
366     assert(indexTable < kSizeAvgFECRecoveryXOR);
367     float avgFecRecov = static_cast<float>(kAvgFECRecoveryXOR[indexTable]);
368 
369     return avgFecRecov;
370 }
371 
372 bool
ProtectionFactor(const VCMProtectionParameters * parameters)373 VCMFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters)
374 {
375     // FEC PROTECTION SETTINGS: varies with packet loss and bitrate
376 
377     // No protection if (filtered) packetLoss is 0
378     uint8_t packetLoss = (uint8_t) (255 * parameters->lossPr);
379     if (packetLoss == 0)
380     {
381         _protectionFactorK = 0;
382         _protectionFactorD = 0;
383          return true;
384     }
385 
386     // Parameters for FEC setting:
387     // first partition size, thresholds, table pars, spatial resoln fac.
388 
389     // First partition protection: ~ 20%
390     uint8_t firstPartitionProt = (uint8_t) (255 * 0.20);
391 
392     // Minimum protection level needed to generate one FEC packet for one
393     // source packet/frame (in RTP sender)
394     uint8_t minProtLevelFec = 85;
395 
396     // Threshold on packetLoss and bitRrate/frameRate (=average #packets),
397     // above which we allocate protection to cover at least first partition.
398     uint8_t lossThr = 0;
399     uint8_t packetNumThr = 1;
400 
401     // Parameters for range of rate index of table.
402     const uint8_t ratePar1 = 5;
403     const uint8_t ratePar2 = 49;
404 
405     // Spatial resolution size, relative to a reference size.
406     float spatialSizeToRef = static_cast<float>
407                            (parameters->codecWidth * parameters->codecHeight) /
408                            (static_cast<float>(704 * 576));
409     // resolnFac: This parameter will generally increase/decrease the FEC rate
410     // (for fixed bitRate and packetLoss) based on system size.
411     // Use a smaller exponent (< 1) to control/soften system size effect.
412     const float resolnFac = 1.0 / powf(spatialSizeToRef, 0.3f);
413 
414     const int bitRatePerFrame = BitsPerFrame(parameters);
415 
416 
417     // Average number of packets per frame (source and fec):
418     const uint8_t avgTotPackets = 1 + (uint8_t)
419                                         ((float) bitRatePerFrame * 1000.0
420                                        / (float) (8.0 * _maxPayloadSize) + 0.5);
421 
422     // FEC rate parameters: for P and I frame
423     uint8_t codeRateDelta = 0;
424     uint8_t codeRateKey = 0;
425 
426     // Get index for table: the FEC protection depends on an effective rate.
427     // The range on the rate index corresponds to rates (bps)
428     // from ~200k to ~8000k, for 30fps
429     const uint16_t effRateFecTable = static_cast<uint16_t>
430                                            (resolnFac * bitRatePerFrame);
431     uint8_t rateIndexTable =
432         (uint8_t) VCM_MAX(VCM_MIN((effRateFecTable - ratePar1) /
433                                          ratePar1, ratePar2), 0);
434 
435     // Restrict packet loss range to 50:
436     // current tables defined only up to 50%
437     if (packetLoss >= kPacketLossMax)
438     {
439         packetLoss = kPacketLossMax - 1;
440     }
441     uint16_t indexTable = rateIndexTable * kPacketLossMax + packetLoss;
442 
443     // Check on table index
444     assert(indexTable < kSizeCodeRateXORTable);
445 
446     // Protection factor for P frame
447     codeRateDelta = kCodeRateXORTable[indexTable];
448 
449     if (packetLoss > lossThr && avgTotPackets > packetNumThr)
450     {
451         // Set a minimum based on first partition size.
452         if (codeRateDelta < firstPartitionProt)
453         {
454             codeRateDelta = firstPartitionProt;
455         }
456     }
457 
458     // Check limit on amount of protection for P frame; 50% is max.
459     if (codeRateDelta >= kPacketLossMax)
460     {
461         codeRateDelta = kPacketLossMax - 1;
462     }
463 
464     float adjustFec = 1.0f;
465     // Avoid additional adjustments when layers are active.
466     // TODO(mikhal/marco): Update adjusmtent based on layer info.
467     if (parameters->numLayers == 1)
468     {
469         adjustFec = _qmRobustness->AdjustFecFactor(codeRateDelta,
470                                                    parameters->bitRate,
471                                                    parameters->frameRate,
472                                                    parameters->rtt,
473                                                    packetLoss);
474     }
475 
476     codeRateDelta = static_cast<uint8_t>(codeRateDelta * adjustFec);
477 
478     // For Key frame:
479     // Effectively at a higher rate, so we scale/boost the rate
480     // The boost factor may depend on several factors: ratio of packet
481     // number of I to P frames, how much protection placed on P frames, etc.
482     const uint8_t packetFrameDelta = (uint8_t)
483                                            (0.5 + parameters->packetsPerFrame);
484     const uint8_t packetFrameKey = (uint8_t)
485                                          (0.5 + parameters->packetsPerFrameKey);
486     const uint8_t boostKey = BoostCodeRateKey(packetFrameDelta,
487                                                     packetFrameKey);
488 
489     rateIndexTable = (uint8_t) VCM_MAX(VCM_MIN(
490                       1 + (boostKey * effRateFecTable - ratePar1) /
491                       ratePar1,ratePar2),0);
492     uint16_t indexTableKey = rateIndexTable * kPacketLossMax + packetLoss;
493 
494     indexTableKey = VCM_MIN(indexTableKey, kSizeCodeRateXORTable);
495 
496     // Check on table index
497     assert(indexTableKey < kSizeCodeRateXORTable);
498 
499     // Protection factor for I frame
500     codeRateKey = kCodeRateXORTable[indexTableKey];
501 
502     // Boosting for Key frame.
503     int boostKeyProt = _scaleProtKey * codeRateDelta;
504     if (boostKeyProt >= kPacketLossMax)
505     {
506         boostKeyProt = kPacketLossMax - 1;
507     }
508 
509     // Make sure I frame protection is at least larger than P frame protection,
510     // and at least as high as filtered packet loss.
511     codeRateKey = static_cast<uint8_t> (VCM_MAX(packetLoss,
512             VCM_MAX(boostKeyProt, codeRateKey)));
513 
514     // Check limit on amount of protection for I frame: 50% is max.
515     if (codeRateKey >= kPacketLossMax)
516     {
517         codeRateKey = kPacketLossMax - 1;
518     }
519 
520     _protectionFactorK = codeRateKey;
521     _protectionFactorD = codeRateDelta;
522 
523     // Generally there is a rate mis-match between the FEC cost estimated
524     // in mediaOpt and the actual FEC cost sent out in RTP module.
525     // This is more significant at low rates (small # of source packets), where
526     // the granularity of the FEC decreases. In this case, non-zero protection
527     // in mediaOpt may generate 0 FEC packets in RTP sender (since actual #FEC
528     // is based on rounding off protectionFactor on actual source packet number).
529     // The correction factor (_corrFecCost) attempts to corrects this, at least
530     // for cases of low rates (small #packets) and low protection levels.
531 
532     float numPacketsFl = 1.0f + ((float) bitRatePerFrame * 1000.0
533                                 / (float) (8.0 * _maxPayloadSize) + 0.5);
534 
535     const float estNumFecGen = 0.5f + static_cast<float> (_protectionFactorD *
536                                                          numPacketsFl / 255.0f);
537 
538 
539     // We reduce cost factor (which will reduce overhead for FEC and
540     // hybrid method) and not the protectionFactor.
541     _corrFecCost = 1.0f;
542     if (estNumFecGen < 1.1f && _protectionFactorD < minProtLevelFec)
543     {
544         _corrFecCost = 0.5f;
545     }
546     if (estNumFecGen < 0.9f && _protectionFactorD < minProtLevelFec)
547     {
548         _corrFecCost = 0.0f;
549     }
550 
551      // TODO (marpan): Set the UEP protection on/off for Key and Delta frames
552     _useUepProtectionK = _qmRobustness->SetUepProtection(codeRateKey,
553                                                          parameters->bitRate,
554                                                          packetLoss,
555                                                          0);
556 
557     _useUepProtectionD = _qmRobustness->SetUepProtection(codeRateDelta,
558                                                          parameters->bitRate,
559                                                          packetLoss,
560                                                          1);
561 
562     // DONE WITH FEC PROTECTION SETTINGS
563     return true;
564 }
565 
BitsPerFrame(const VCMProtectionParameters * parameters)566 int VCMFecMethod::BitsPerFrame(const VCMProtectionParameters* parameters) {
567   // When temporal layers are available FEC will only be applied on the base
568   // layer.
569   const float bitRateRatio =
570     kVp8LayerRateAlloction[parameters->numLayers - 1][0];
571   float frameRateRatio = powf(1 / 2.0, parameters->numLayers - 1);
572   float bitRate = parameters->bitRate * bitRateRatio;
573   float frameRate = parameters->frameRate * frameRateRatio;
574 
575   // TODO(mikhal): Update factor following testing.
576   float adjustmentFactor = 1;
577 
578   // Average bits per frame (units of kbits)
579   return static_cast<int>(adjustmentFactor * bitRate / frameRate);
580 }
581 
582 bool
EffectivePacketLoss(const VCMProtectionParameters * parameters)583 VCMFecMethod::EffectivePacketLoss(const VCMProtectionParameters* parameters)
584 {
585     // Effective packet loss to encoder is based on RPL (residual packet loss)
586     // this is a soft setting based on degree of FEC protection
587     // RPL = received/input packet loss - average_FEC_recovery
588     // note: received/input packet loss may be filtered based on FilteredLoss
589 
590     // The packet loss:
591     uint8_t packetLoss = (uint8_t) (255 * parameters->lossPr);
592 
593     float avgFecRecov = AvgRecoveryFEC(parameters);
594 
595     // Residual Packet Loss:
596     _residualPacketLossFec = (float) (packetLoss - avgFecRecov) / 255.0f;
597 
598     // Effective Packet Loss, NA in current version.
599     _effectivePacketLoss = 0;
600 
601     return true;
602 }
603 
604 bool
UpdateParameters(const VCMProtectionParameters * parameters)605 VCMFecMethod::UpdateParameters(const VCMProtectionParameters* parameters)
606 {
607     // Compute the protection factor
608     ProtectionFactor(parameters);
609 
610     // Compute the effective packet loss
611     EffectivePacketLoss(parameters);
612 
613     // Compute the bit cost
614     // Ignore key frames for now.
615     float fecRate = static_cast<float> (_protectionFactorD) / 255.0f;
616     if (fecRate >= 0.0f)
617     {
618         // use this formula if the fecRate (protection factor) is defined
619         // relative to number of source packets
620         // this is the case for the previous tables:
621         // _efficiency = parameters->bitRate * ( 1.0 - 1.0 / (1.0 + fecRate));
622 
623         // in the new tables, the fecRate is defined relative to total number of
624         // packets (total rate), so overhead cost is:
625         _efficiency = parameters->bitRate * fecRate * _corrFecCost;
626     }
627     else
628     {
629         _efficiency = 0.0f;
630     }
631 
632     // Protection/fec rates obtained above is defined relative to total number
633     // of packets (total rate: source+fec) FEC in RTP module assumes protection
634     // factor is defined relative to source number of packets so we should
635     // convert the factor to reduce mismatch between mediaOpt suggested rate and
636     // the actual rate
637     _protectionFactorK = ConvertFECRate(_protectionFactorK);
638     _protectionFactorD = ConvertFECRate(_protectionFactorD);
639 
640     return true;
641 }
VCMLossProtectionLogic(int64_t nowMs)642 VCMLossProtectionLogic::VCMLossProtectionLogic(int64_t nowMs):
643 _selectedMethod(NULL),
644 _currentParameters(),
645 _rtt(0),
646 _lossPr(0.0f),
647 _bitRate(0.0f),
648 _frameRate(0.0f),
649 _keyFrameSize(0.0f),
650 _fecRateKey(0),
651 _fecRateDelta(0),
652 _lastPrUpdateT(0),
653 _lossPr255(0.9999f),
654 _lossPrHistory(),
655 _shortMaxLossPr255(0),
656 _packetsPerFrame(0.9999f),
657 _packetsPerFrameKey(0.9999f),
658 _residualPacketLossFec(0),
659 _codecWidth(0),
660 _codecHeight(0),
661 _numLayers(1)
662 {
663     Reset(nowMs);
664 }
665 
~VCMLossProtectionLogic()666 VCMLossProtectionLogic::~VCMLossProtectionLogic()
667 {
668     Release();
669 }
670 
671 bool
SetMethod(enum VCMProtectionMethodEnum newMethodType)672 VCMLossProtectionLogic::SetMethod(enum VCMProtectionMethodEnum newMethodType)
673 {
674     if (_selectedMethod != NULL)
675     {
676         if (_selectedMethod->Type() == newMethodType)
677         {
678             // Nothing to update
679             return false;
680         }
681         // New method - delete existing one
682         delete _selectedMethod;
683     }
684     VCMProtectionMethod *newMethod = NULL;
685     switch (newMethodType)
686     {
687         case kNack:
688         {
689             newMethod = new VCMNackMethod();
690             break;
691         }
692         case kFec:
693         {
694             newMethod  = new VCMFecMethod();
695             break;
696         }
697         case kNackFec:
698         {
699             // Default to always having NACK enabled for the hybrid mode.
700             newMethod =  new VCMNackFecMethod(kLowRttNackMs, -1);
701             break;
702         }
703         default:
704         {
705           return false;
706           break;
707         }
708 
709     }
710     _selectedMethod = newMethod;
711     return true;
712 }
713 bool
RemoveMethod(enum VCMProtectionMethodEnum method)714 VCMLossProtectionLogic::RemoveMethod(enum VCMProtectionMethodEnum method)
715 {
716     if (_selectedMethod == NULL)
717     {
718         return false;
719     }
720     else if (_selectedMethod->Type() == method)
721     {
722         delete _selectedMethod;
723         _selectedMethod = NULL;
724     }
725     return true;
726 }
727 
728 float
RequiredBitRate() const729 VCMLossProtectionLogic::RequiredBitRate() const
730 {
731     float RequiredBitRate = 0.0f;
732     if (_selectedMethod != NULL)
733     {
734         RequiredBitRate = _selectedMethod->RequiredBitRate();
735     }
736     return RequiredBitRate;
737 }
738 
739 void
UpdateRtt(uint32_t rtt)740 VCMLossProtectionLogic::UpdateRtt(uint32_t rtt)
741 {
742     _rtt = rtt;
743 }
744 
745 void
UpdateResidualPacketLoss(float residualPacketLoss)746 VCMLossProtectionLogic::UpdateResidualPacketLoss(float residualPacketLoss)
747 {
748     _residualPacketLossFec = residualPacketLoss;
749 }
750 
751 void
UpdateMaxLossHistory(uint8_t lossPr255,int64_t now)752 VCMLossProtectionLogic::UpdateMaxLossHistory(uint8_t lossPr255,
753                                              int64_t now)
754 {
755     if (_lossPrHistory[0].timeMs >= 0 &&
756         now - _lossPrHistory[0].timeMs < kLossPrShortFilterWinMs)
757     {
758         if (lossPr255 > _shortMaxLossPr255)
759         {
760             _shortMaxLossPr255 = lossPr255;
761         }
762     }
763     else
764     {
765         // Only add a new value to the history once a second
766         if (_lossPrHistory[0].timeMs == -1)
767         {
768             // First, no shift
769             _shortMaxLossPr255 = lossPr255;
770         }
771         else
772         {
773             // Shift
774             for (int32_t i = (kLossPrHistorySize - 2); i >= 0; i--)
775             {
776                 _lossPrHistory[i + 1].lossPr255 = _lossPrHistory[i].lossPr255;
777                 _lossPrHistory[i + 1].timeMs = _lossPrHistory[i].timeMs;
778             }
779         }
780         if (_shortMaxLossPr255 == 0)
781         {
782             _shortMaxLossPr255 = lossPr255;
783         }
784 
785         _lossPrHistory[0].lossPr255 = _shortMaxLossPr255;
786         _lossPrHistory[0].timeMs = now;
787         _shortMaxLossPr255 = 0;
788     }
789 }
790 
791 uint8_t
MaxFilteredLossPr(int64_t nowMs) const792 VCMLossProtectionLogic::MaxFilteredLossPr(int64_t nowMs) const
793 {
794     uint8_t maxFound = _shortMaxLossPr255;
795     if (_lossPrHistory[0].timeMs == -1)
796     {
797         return maxFound;
798     }
799     for (int32_t i = 0; i < kLossPrHistorySize; i++)
800     {
801         if (_lossPrHistory[i].timeMs == -1)
802         {
803             break;
804         }
805         if (nowMs - _lossPrHistory[i].timeMs >
806             kLossPrHistorySize * kLossPrShortFilterWinMs)
807         {
808             // This sample (and all samples after this) is too old
809             break;
810         }
811         if (_lossPrHistory[i].lossPr255 > maxFound)
812         {
813             // This sample is the largest one this far into the history
814             maxFound = _lossPrHistory[i].lossPr255;
815         }
816     }
817     return maxFound;
818 }
819 
FilteredLoss(int64_t nowMs,FilterPacketLossMode filter_mode,uint8_t lossPr255)820 uint8_t VCMLossProtectionLogic::FilteredLoss(
821     int64_t nowMs,
822     FilterPacketLossMode filter_mode,
823     uint8_t lossPr255) {
824 
825   // Update the max window filter.
826   UpdateMaxLossHistory(lossPr255, nowMs);
827 
828   // Update the recursive average filter.
829   _lossPr255.Apply(static_cast<float> (nowMs - _lastPrUpdateT),
830                    static_cast<float> (lossPr255));
831   _lastPrUpdateT = nowMs;
832 
833   // Filtered loss: default is received loss (no filtering).
834   uint8_t filtered_loss = lossPr255;
835 
836   switch (filter_mode) {
837     case kNoFilter:
838       break;
839     case kAvgFilter:
840       filtered_loss = static_cast<uint8_t>(_lossPr255.filtered() + 0.5);
841       break;
842     case kMaxFilter:
843       filtered_loss = MaxFilteredLossPr(nowMs);
844       break;
845   }
846 
847   return filtered_loss;
848 }
849 
850 void
UpdateFilteredLossPr(uint8_t packetLossEnc)851 VCMLossProtectionLogic::UpdateFilteredLossPr(uint8_t packetLossEnc)
852 {
853     _lossPr = (float) packetLossEnc / (float) 255.0;
854 }
855 
856 void
UpdateBitRate(float bitRate)857 VCMLossProtectionLogic::UpdateBitRate(float bitRate)
858 {
859     _bitRate = bitRate;
860 }
861 
862 void
UpdatePacketsPerFrame(float nPackets,int64_t nowMs)863 VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets, int64_t nowMs)
864 {
865     _packetsPerFrame.Apply(static_cast<float>(nowMs - _lastPacketPerFrameUpdateT),
866                            nPackets);
867     _lastPacketPerFrameUpdateT = nowMs;
868 }
869 
870 void
UpdatePacketsPerFrameKey(float nPackets,int64_t nowMs)871 VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets, int64_t nowMs)
872 {
873     _packetsPerFrameKey.Apply(static_cast<float>(nowMs -
874                               _lastPacketPerFrameUpdateTKey), nPackets);
875     _lastPacketPerFrameUpdateTKey = nowMs;
876 }
877 
878 void
UpdateKeyFrameSize(float keyFrameSize)879 VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize)
880 {
881     _keyFrameSize = keyFrameSize;
882 }
883 
884 void
UpdateFrameSize(uint16_t width,uint16_t height)885 VCMLossProtectionLogic::UpdateFrameSize(uint16_t width,
886                                         uint16_t height)
887 {
888     _codecWidth = width;
889     _codecHeight = height;
890 }
891 
UpdateNumLayers(int numLayers)892 void VCMLossProtectionLogic::UpdateNumLayers(int numLayers) {
893   _numLayers = (numLayers == 0) ? 1 : numLayers;
894 }
895 
896 bool
UpdateMethod()897 VCMLossProtectionLogic::UpdateMethod()
898 {
899     if (_selectedMethod == NULL)
900     {
901         return false;
902     }
903     _currentParameters.rtt = _rtt;
904     _currentParameters.lossPr = _lossPr;
905     _currentParameters.bitRate = _bitRate;
906     _currentParameters.frameRate = _frameRate; // rename actual frame rate?
907     _currentParameters.keyFrameSize = _keyFrameSize;
908     _currentParameters.fecRateDelta = _fecRateDelta;
909     _currentParameters.fecRateKey = _fecRateKey;
910     _currentParameters.packetsPerFrame = _packetsPerFrame.filtered();
911     _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.filtered();
912     _currentParameters.residualPacketLossFec = _residualPacketLossFec;
913     _currentParameters.codecWidth = _codecWidth;
914     _currentParameters.codecHeight = _codecHeight;
915     _currentParameters.numLayers = _numLayers;
916     return _selectedMethod->UpdateParameters(&_currentParameters);
917 }
918 
919 VCMProtectionMethod*
SelectedMethod() const920 VCMLossProtectionLogic::SelectedMethod() const
921 {
922     return _selectedMethod;
923 }
924 
925 VCMProtectionMethodEnum
SelectedType() const926 VCMLossProtectionLogic::SelectedType() const
927 {
928     return _selectedMethod->Type();
929 }
930 
931 void
Reset(int64_t nowMs)932 VCMLossProtectionLogic::Reset(int64_t nowMs)
933 {
934     _lastPrUpdateT = nowMs;
935     _lastPacketPerFrameUpdateT = nowMs;
936     _lastPacketPerFrameUpdateTKey = nowMs;
937     _lossPr255.Reset(0.9999f);
938     _packetsPerFrame.Reset(0.9999f);
939     _fecRateDelta = _fecRateKey = 0;
940     for (int32_t i = 0; i < kLossPrHistorySize; i++)
941     {
942         _lossPrHistory[i].lossPr255 = 0;
943         _lossPrHistory[i].timeMs = -1;
944     }
945     _shortMaxLossPr255 = 0;
946     Release();
947 }
948 
949 void
Release()950 VCMLossProtectionLogic::Release()
951 {
952     delete _selectedMethod;
953     _selectedMethod = NULL;
954 }
955 
956 }  // namespace media_optimization
957 }  // namespace webrtc
958