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