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