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