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 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 45 VCMProtectionMethod::~VCMProtectionMethod() 46 { 47 delete _qmRobustness; 48 } 49 void 50 VCMProtectionMethod::UpdateContentMetrics(const 51 VideoContentMetrics* contentMetrics) 52 { 53 _qmRobustness->UpdateContent(contentMetrics); 54 } 55 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 69 VCMNackFecMethod::~VCMNackFecMethod() 70 { 71 // 72 } 73 bool 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 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 141 int VCMNackFecMethod::MaxFramesFec() const { 142 return _maxFramesFec; 143 } 144 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 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 // 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 215 VCMNackMethod::VCMNackMethod(): 216 VCMProtectionMethod() 217 { 218 _type = kNack; 219 } 220 221 VCMNackMethod::~VCMNackMethod() 222 { 223 // 224 } 225 226 bool 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 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 246 VCMFecMethod::VCMFecMethod(): 247 VCMProtectionMethod() 248 { 249 _type = kFec; 250 } 251 VCMFecMethod::~VCMFecMethod() 252 { 253 // 254 } 255 256 uint8_t 257 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 274 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 282 VCMFecMethod::UpdateProtectionFactorD(uint8_t protectionFactorD) 283 { 284 _protectionFactorD = protectionFactorD; 285 } 286 287 // Update FEC with protectionFactorK 288 void 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 298 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 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 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 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 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 } 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 666 VCMLossProtectionLogic::~VCMLossProtectionLogic() 667 { 668 Release(); 669 } 670 671 bool 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 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 729 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 740 VCMLossProtectionLogic::UpdateRtt(uint32_t rtt) 741 { 742 _rtt = rtt; 743 } 744 745 void 746 VCMLossProtectionLogic::UpdateResidualPacketLoss(float residualPacketLoss) 747 { 748 _residualPacketLossFec = residualPacketLoss; 749 } 750 751 void 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 792 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 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 851 VCMLossProtectionLogic::UpdateFilteredLossPr(uint8_t packetLossEnc) 852 { 853 _lossPr = (float) packetLossEnc / (float) 255.0; 854 } 855 856 void 857 VCMLossProtectionLogic::UpdateBitRate(float bitRate) 858 { 859 _bitRate = bitRate; 860 } 861 862 void 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 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 879 VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize) 880 { 881 _keyFrameSize = keyFrameSize; 882 } 883 884 void 885 VCMLossProtectionLogic::UpdateFrameSize(uint16_t width, 886 uint16_t height) 887 { 888 _codecWidth = width; 889 _codecHeight = height; 890 } 891 892 void VCMLossProtectionLogic::UpdateNumLayers(int numLayers) { 893 _numLayers = (numLayers == 0) ? 1 : numLayers; 894 } 895 896 bool 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* 920 VCMLossProtectionLogic::SelectedMethod() const 921 { 922 return _selectedMethod; 923 } 924 925 VCMProtectionMethodEnum 926 VCMLossProtectionLogic::SelectedType() const 927 { 928 return _selectedMethod->Type(); 929 } 930 931 void 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 950 VCMLossProtectionLogic::Release() 951 { 952 delete _selectedMethod; 953 _selectedMethod = NULL; 954 } 955 956 } // namespace media_optimization 957 } // namespace webrtc 958