/* * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "video_coding_defines.h" #include "fec_tables_xor.h" #include "er_tables_xor.h" #include "nack_fec_tables.h" #include "qm_select_data.h" #include "media_opt_util.h" #include #include #include namespace webrtc { bool VCMProtectionMethod::BetterThan(VCMProtectionMethod *pm) { if (pm == NULL) { return true; } return pm->_score > _score; } bool VCMNackFecMethod::ProtectionFactor(const VCMProtectionParameters* /*parameters*/) { // use FEC model with modification with RTT for now return true; } bool VCMNackFecMethod::EffectivePacketLoss(const VCMProtectionParameters* /*parameters*/) { // use FEC model with modification with RTT for now return true; } bool VCMNackFecMethod::UpdateParameters(const VCMProtectionParameters* parameters) { // Hybrid Nack FEC has three operational modes: // 1. Low RTT - Nack only (Set FEC rates to zero) // 2. High RTT - FEC Only // 3. Medium RTT values - Hybrid ; in hybrid mode, we will only nack the residual // following the decoding of the FEC (and not in all cases, refer to JB logic) // Low RTT - NACK only mode if (parameters->rtt < kLowRttNackMs) { // Set the FEC parameters to 0 _protectionFactorK = 0; _protectionFactorD = 0; // assume packets will be restored via NACK // TODO: relax this assumption? _effectivePacketLoss = 0; _score = _efficiency; return true; } // otherwise: we count on FEC; if the RTT is below a threshold, then we can // nack the residual, based on a decision made in the JB. // TODO(mikhal): adapt the FEC rate based on the RTT, i.e. the the // level on which we will rely on NACK, e.g. less as we approach upper threshold. VCMFecMethod fecMethod; const WebRtc_UWord8 plossMax = 129; // Compute the protection factor fecMethod.ProtectionFactor(parameters); // Compute the effective packet loss fecMethod.EffectivePacketLoss(parameters); WebRtc_UWord8 protFactorK = fecMethod._protectionFactorK; WebRtc_UWord8 protFactorD = fecMethod._protectionFactorD; WebRtc_UWord8 effPacketLoss = fecMethod._effectivePacketLoss; float resPacketLoss = fecMethod._residualPacketLoss; // Correct FEC rates based on the RTT ( NACK effectiveness) WebRtc_Word16 rttIndex= (WebRtc_UWord16) parameters->rtt; float softnessRtt = 1.0; if (parameters->rtt < kHighRttNackMs) { // TODO(mikhal): update table softnessRtt = (float)VCMNackFecTable[rttIndex] / (float)4096.0; // soften ER with NACK on // table depends on RTT relative to rttMax (NACK Threshold) _effectivePacketLoss = (WebRtc_UWord8)(effPacketLoss * softnessRtt); // soften FEC with NACK on // table depends on RTT relative to rttMax (NACK Threshold) _protectionFactorK = (WebRtc_UWord8) (protFactorK * softnessRtt); _protectionFactorD = (WebRtc_UWord8) (protFactorD * softnessRtt); } // else - NACK is disabled, rely on FEC only // make sure I frame protection is at least larger than P frame protection, // and at least as high as received loss WebRtc_UWord8 packetLoss = (WebRtc_UWord8) (255 * parameters->lossPr); _protectionFactorK = static_cast (VCM_MAX(packetLoss, VCM_MAX(_scaleProtKey * protFactorD, protFactorK))); // check limit on amount of protection for I frame: 50% is max if (_protectionFactorK >= plossMax) _protectionFactorK = plossMax - 1; // Bit cost for NackFec // NACK cost: based on residual packet loss (since we should only NACK packets // not recovered by FEC) _efficiency = 0.0f; if (parameters->rtt < kHighRttNackMs) { _efficiency = parameters->bitRate * resPacketLoss / (1.0f + resPacketLoss); } else { // efficiency based on FEC only // add FEC cost: ignore I frames for now float fecRate = static_cast (_protectionFactorD) / 255.0f; if (fecRate >= 0.0f) _efficiency += parameters->bitRate * fecRate; } _score = _efficiency; // Protection/fec rates obtained above are defined relative to total number of // packets (total rate: source + fec) FEC in RTP module assumes protection // factor is defined relative to source number of packets so we should convert // the factor to reduce mismatch between mediaOpt's rate and the actual one WebRtc_UWord8 codeRate = protFactorK; _protectionFactorK = fecMethod.ConvertFECRate(codeRate); codeRate = protFactorD; _protectionFactorD = fecMethod.ConvertFECRate(codeRate); return true; } bool VCMNackMethod::EffectivePacketLoss(WebRtc_UWord8 effPacketLoss, WebRtc_UWord16 rttTime) { WebRtc_UWord16 rttMax = MaxRttNack(); // For large RTT, we should rely on some Error Resilience, so we set // packetLossEnc = 0 for RTT less than the NACK threshold if (rttTime < rttMax) effPacketLoss = 0; //may want a softer transition here _effectivePacketLoss = effPacketLoss; return true; } bool VCMNackMethod::UpdateParameters(const VCMProtectionParameters* parameters) { // Compute the effective packet loss for ER WebRtc_UWord8 effPacketLoss = (WebRtc_UWord8) (255 * parameters->lossPr); WebRtc_UWord16 rttTime = (WebRtc_UWord16) parameters->rtt; EffectivePacketLoss(effPacketLoss, rttTime); // Compute the NACK bit cost _efficiency = parameters->bitRate * parameters->lossPr / (1.0f + parameters->lossPr); _score = _efficiency; if (parameters->rtt > _NACK_MAX_RTT) { _score = 0.0f; return false; } return true; } WebRtc_UWord8 VCMFecMethod::BoostCodeRateKey(WebRtc_UWord8 packetFrameDelta, WebRtc_UWord8 packetFrameKey) const { WebRtc_UWord8 boostRateKey = 2; // default: ratio scales the FEC protection up for I frames WebRtc_UWord8 ratio = 1; if (packetFrameDelta > 0) ratio = (WebRtc_Word8) (packetFrameKey / packetFrameDelta); ratio = VCM_MAX(boostRateKey, ratio); return ratio; } WebRtc_UWord8 VCMFecMethod::ConvertFECRate(WebRtc_UWord8 codeRateRTP) const { return static_cast (VCM_MIN(255,(0.5 + 255.0*codeRateRTP/(float)(255 - codeRateRTP)))); } // AvgRecoveryFEC: average recovery from FEC, assuming random packet loss model // Computed offline for a range of FEC code parameters and loss rates float VCMFecMethod::AvgRecoveryFEC(const VCMProtectionParameters* parameters) const { // Total (avg) bits available per frame: total rate over actual/sent frame rate // units are kbits/frame const WebRtc_UWord16 bitRatePerFrame = static_cast (parameters->bitRate / (parameters->frameRate)); // Total (avg) number of packets per frame (source and fec): const WebRtc_UWord8 avgTotPackets = 1 + (WebRtc_UWord8) ((float) bitRatePerFrame * 1000.0 / (float) (8.0 * _maxPayloadSize) + 0.5); // parameters for tables const WebRtc_UWord8 codeSize = 24; const WebRtc_UWord8 plossMax = 129; const WebRtc_UWord16 maxErTableSize = 38700; // Get index for table const float protectionFactor = (float) _protectionFactorD / (float) 255; WebRtc_UWord8 fecPacketsPerFrame = (WebRtc_UWord8) (0.5 + protectionFactor * avgTotPackets); WebRtc_UWord8 sourcePacketsPerFrame = avgTotPackets - fecPacketsPerFrame; if (fecPacketsPerFrame == 0) { return 0.0; // no protection, so avg. recov from FEC == 0 } // table defined up to codeSizexcodeSize code if (sourcePacketsPerFrame > codeSize) { sourcePacketsPerFrame = codeSize; } // check: protection factor is maxed at 50%, so this should never happen if (sourcePacketsPerFrame < 1) { assert("average number of source packets below 1\n"); } // index for ER tables: up to codeSizexcodeSize mask WebRtc_UWord16 codeIndexTable[codeSize * codeSize]; WebRtc_UWord16 k = -1; for (WebRtc_UWord8 i = 1; i <= codeSize; i++) { for (WebRtc_UWord8 j = 1; j <= i; j++) { k += 1; codeIndexTable[(j - 1) * codeSize + i - 1] = k; } } const WebRtc_UWord8 lossRate = (WebRtc_UWord8) (255.0 * parameters->lossPr + 0.5f); const WebRtc_UWord16 codeIndex = (fecPacketsPerFrame - 1) * codeSize + (sourcePacketsPerFrame - 1); const WebRtc_UWord16 indexTable = codeIndexTable[codeIndex] * plossMax + lossRate; const WebRtc_UWord16 codeIndex2 = (fecPacketsPerFrame) * codeSize + (sourcePacketsPerFrame); WebRtc_UWord16 indexTable2 = codeIndexTable[codeIndex2] * plossMax + lossRate; // checks on table index if (indexTable >= maxErTableSize) { assert("ER table index too large\n"); } if (indexTable2 >= maxErTableSize) { indexTable2 = indexTable; } // // Get the average effective packet loss recovery from FEC // this is from tables, computed using random loss model WebRtc_UWord8 avgFecRecov1 = 0; WebRtc_UWord8 avgFecRecov2 = 0; float avgFecRecov = 0; if (fecPacketsPerFrame > 0) { avgFecRecov1 = VCMAvgFECRecoveryXOR[indexTable]; avgFecRecov2 = VCMAvgFECRecoveryXOR[indexTable2]; } // interpolate over two FEC codes const float weightRpl = (float) (0.5 + protectionFactor * avgTotPackets) - (float) fecPacketsPerFrame; avgFecRecov = (float) weightRpl * (float) avgFecRecov2 + (float) (1.0 - weightRpl) * (float) avgFecRecov1; return avgFecRecov; } bool VCMFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters) { // FEC PROTECTION SETTINGS: varies with packet loss and bitrate const float bitRate = parameters->bitRate; WebRtc_UWord8 packetLoss = (WebRtc_UWord8) (255 * parameters->lossPr); // Size of tables const WebRtc_UWord16 maxFecTableSize = 6450; // Parameters for range of rate and packet loss for tables const WebRtc_UWord8 ratePar1 = 5; const WebRtc_UWord8 ratePar2 = 49; const WebRtc_UWord8 plossMax = 129; // // Just for testing: for the case where we randomly lose slices instead of // RTP packets and use SingleMode packetization in RTP module // const WebRtc_UWord16 slice_size = 3000/6; //corresponds to rate=1000k with 4 cores //float slice_mtu = (float)_maxPayloadSize/(float)slice_size; const float slice_mtu = 1.0; // //Total (avg) bits available per frame: total rate over actual/sent frame rate //units are kbits/frame const WebRtc_UWord16 bitRatePerFrame = static_cast (slice_mtu * bitRate / (parameters->frameRate)); //Total (avg) number of packets per frame (source and fec): const WebRtc_UWord8 avgTotPackets = 1 + (WebRtc_UWord8) ((float) bitRatePerFrame * 1000.0 / (float) (8.0 * _maxPayloadSize) + 0.5); // TODO(marpan): Tune model for FEC Protection. // Better modulation of protection with available bits/frame // (or avgTotpackets) using weight factors // FEC Tables include this effect already, but need to tune model off-line float weight1 = 0.5; float weight2 = 0.5; if (avgTotPackets > 4) { weight1 = 1.0; weight2 = 0.; } if (avgTotPackets > 6) { weight1 = 1.5; weight2 = 0.; } // // Fec rate parameters: for P and I frame WebRtc_UWord8 codeRateDelta = 0; WebRtc_UWord8 codeRateKey = 0; // Get index for new table: the FEC protection depends on the (average) available bits/frame // the range on the rate index corresponds to rates (bps) from 200k to 8000k, for 30fps WebRtc_UWord8 rateIndexTable = (WebRtc_UWord8) VCM_MAX(VCM_MIN((bitRatePerFrame-ratePar1)/ratePar1,ratePar2),0); // Restrict packet loss range to 50 for now%: current tables defined only up to 50% if (packetLoss >= plossMax) { packetLoss = plossMax - 1; } WebRtc_UWord16 indexTable = rateIndexTable * plossMax + packetLoss; // check on table index if (indexTable >= maxFecTableSize) { assert("FEC table index too large\n"); } // // For Key frame: effectively at a higher rate, so we scale/boost the rate // index. The boost factor may depend on several factors: ratio of packet // number of I to P frames, how much protection placed on P frames, etc. // default is 2 const WebRtc_UWord8 packetFrameDelta = (WebRtc_UWord8) (0.5 + parameters->packetsPerFrame); const WebRtc_UWord8 packetFrameKey = (WebRtc_UWord8) (0.5 + parameters->packetsPerFrameKey); const WebRtc_UWord8 boostKey = BoostCodeRateKey(packetFrameDelta, packetFrameKey); rateIndexTable = (WebRtc_UWord8) VCM_MAX(VCM_MIN(1+(boostKey*bitRatePerFrame-ratePar1)/ratePar1,ratePar2),0); WebRtc_UWord16 indexTableKey = rateIndexTable * plossMax + packetLoss; indexTableKey = VCM_MIN(indexTableKey, maxFecTableSize); codeRateDelta = VCMCodeRateXORTable[indexTable]; //protection factor for P frame codeRateKey = VCMCodeRateXORTable[indexTableKey]; //protection factor for I frame //average with minimum protection level given by (average) total number of packets if (packetLoss > 0) { codeRateDelta = static_cast ((weight1 * (float) codeRateDelta + weight2 * 255.0 / (float) avgTotPackets)); } //check limit on amount of protection for P frame; 50% is max if (codeRateDelta >= plossMax) { codeRateDelta = plossMax - 1; } //make sure I frame protection is at least larger than P frame protection, and at least as high as received loss codeRateKey = static_cast (VCM_MAX(packetLoss,VCM_MAX(_scaleProtKey*codeRateDelta, codeRateKey))); //check limit on amount of protection for I frame: 50% is max if (codeRateKey >= plossMax) { codeRateKey = plossMax - 1; } _protectionFactorK = codeRateKey; _protectionFactorD = codeRateDelta; // DONE WITH FEC PROTECTION SETTINGS return true; } bool VCMFecMethod::EffectivePacketLoss(const VCMProtectionParameters* parameters) { // ER SETTINGS: //Effective packet loss to encoder is based on RPL (residual packet loss) //this is a soft setting based on degree of FEC protection //RPL = received/input packet loss - average_FEC_recovery //note: received/input packet loss may be filtered according to FilteredLoss //The input packet loss: WebRtc_UWord8 effPacketLoss = (WebRtc_UWord8) (255 * parameters->lossPr); float scaleErRS = 0.5; float scaleErXOR = 0.5; float minErLevel = (float) 0.025; //float scaleErRS = 1.0; //float scaleErXOR = 1.0; //float minErLevel = (float) 0.0; float avgFecRecov = 0.; //Effective packet loss for ER: float scaleEr = scaleErXOR; avgFecRecov = AvgRecoveryFEC(parameters); //Residual Packet Loss: _residualPacketLoss = (float) (effPacketLoss - avgFecRecov) / (float) 255.0; //Effective Packet Loss for encoder: _effectivePacketLoss = 0; if (effPacketLoss > 0) { _effectivePacketLoss = VCM_MAX((effPacketLoss - (WebRtc_UWord8)(scaleEr * avgFecRecov)), static_cast(minErLevel * 255)); } // DONE WITH ER SETTING return true; } bool VCMFecMethod::UpdateParameters(const VCMProtectionParameters* parameters) { // Compute the protection factor ProtectionFactor(parameters); // Compute the effective packet loss EffectivePacketLoss(parameters); // Compute the bit cost // Ignore key frames for now. float fecRate = static_cast (_protectionFactorD) / 255.0f; if (fecRate >= 0.0f) { // use this formula if the fecRate (protection factor) is defined relative to number of source packets // this is the case for the previous tables: // _efficiency = parameters->bitRate * ( 1.0 - 1.0 / (1.0 + fecRate)); // in the new tables, the fecRate is defined relative to total number of packets (total rate), // so overhead cost is: _efficiency = parameters->bitRate * fecRate; } else { _efficiency = 0.0f; } _score = _efficiency; // Protection/fec rates obtained above is defined relative to total number of packets (total rate: source+fec) // FEC in RTP module assumes protection factor is defined relative to source number of packets // so we should convert the factor to reduce mismatch between mediaOpt suggested rate and the actual rate _protectionFactorK = ConvertFECRate(_protectionFactorK); _protectionFactorD = ConvertFECRate(_protectionFactorD); return true; } bool VCMIntraReqMethod::UpdateParameters(const VCMProtectionParameters* parameters) { float packetRate = parameters->packetsPerFrame * parameters->frameRate; // Assume that all lost packets cohere to different frames float lossRate = parameters->lossPr * packetRate; if (parameters->keyFrameSize <= 1e-3) { _score = FLT_MAX; return false; } _efficiency = lossRate * parameters->keyFrameSize; _score = _efficiency; if (parameters->lossPr >= 1.0f / parameters->keyFrameSize || parameters->rtt > _IREQ_MAX_RTT) { return false; } return true; } bool VCMPeriodicIntraMethod::UpdateParameters(const VCMProtectionParameters* /*parameters*/) { // Periodic I-frames. The last thing we want to use. _efficiency = 0.0f; _score = FLT_MAX; return true; } bool VCMMbIntraRefreshMethod::UpdateParameters(const VCMProtectionParameters* parameters) { // Assume optimal for now. _efficiency = parameters->bitRate * parameters->lossPr / (1.0f + parameters->lossPr); _score = _efficiency; if (parameters->bitRate < _MBREF_MIN_BITRATE) { return false; } return true; } WebRtc_UWord16 VCMNackMethod::MaxRttNack() const { return _NACK_MAX_RTT; } VCMLossProtectionLogic::~VCMLossProtectionLogic() { ClearLossProtections(); } void VCMLossProtectionLogic::ClearLossProtections() { ListItem *item; while ((item = _availableMethods.First()) != 0) { VCMProtectionMethod *method = static_cast (item->GetItem()); if (method != NULL) { delete method; } _availableMethods.PopFront(); } _selectedMethod = NULL; } bool VCMLossProtectionLogic::AddMethod(VCMProtectionMethod *newMethod) { VCMProtectionMethod *method; ListItem *item; if (newMethod == NULL) { return false; } for (item = _availableMethods.First(); item != NULL; item = _availableMethods.Next(item)) { method = static_cast (item->GetItem()); if (method != NULL && method->Type() == newMethod->Type()) { return false; } } _availableMethods.PushBack(newMethod); return true; } bool VCMLossProtectionLogic::RemoveMethod(VCMProtectionMethodEnum methodType) { VCMProtectionMethod *method; ListItem *item; bool foundAndRemoved = false; for (item = _availableMethods.First(); item != NULL; item = _availableMethods.Next(item)) { method = static_cast (item->GetItem()); if (method != NULL && method->Type() == methodType) { if (_selectedMethod != NULL && _selectedMethod->Type() == method->Type()) { _selectedMethod = NULL; } _availableMethods.Erase(item); item = NULL; delete method; foundAndRemoved = true; } } return foundAndRemoved; } VCMProtectionMethod* VCMLossProtectionLogic::FindMethod(VCMProtectionMethodEnum methodType) const { VCMProtectionMethod *method; ListItem *item; for (item = _availableMethods.First(); item != NULL; item = _availableMethods.Next(item)) { method = static_cast (item->GetItem()); if (method != NULL && method->Type() == methodType) { return method; } } return NULL; } float VCMLossProtectionLogic::HighestOverhead() const { VCMProtectionMethod *method; ListItem *item; float highestOverhead = 0.0f; for (item = _availableMethods.First(); item != NULL; item = _availableMethods.Next(item)) { method = static_cast (item->GetItem()); if (method != NULL && method->RequiredBitRate() > highestOverhead) { highestOverhead = method->RequiredBitRate(); } } return highestOverhead; } void VCMLossProtectionLogic::UpdateRtt(WebRtc_UWord32 rtt) { _rtt = rtt; } void VCMLossProtectionLogic::UpdateResidualPacketLoss(float residualPacketLoss) { _residualPacketLoss = residualPacketLoss; } void VCMLossProtectionLogic::UpdateFecType(VCMFecTypes fecType) { _fecType = fecType; } void VCMLossProtectionLogic::UpdateLossPr(WebRtc_UWord8 lossPr255) { WebRtc_UWord32 now = static_cast (VCMTickTime::MillisecondTimestamp()); UpdateMaxLossHistory(lossPr255, now); _lossPr255.Apply(static_cast (now - _lastPrUpdateT), static_cast (lossPr255)); _lastPrUpdateT = now; _lossPr = _lossPr255.Value() / 255.0f; } void VCMLossProtectionLogic::UpdateMaxLossHistory(WebRtc_UWord8 lossPr255, WebRtc_Word64 now) { if (_lossPrHistory[0].timeMs >= 0 && now - _lossPrHistory[0].timeMs < kLossPrShortFilterWinMs) { if (lossPr255 > _shortMaxLossPr255) { _shortMaxLossPr255 = lossPr255; } } else { // Only add a new value to the history once a second if (_lossPrHistory[0].timeMs == -1) { // First, no shift _shortMaxLossPr255 = lossPr255; } else { // Shift for (WebRtc_Word32 i = (kLossPrHistorySize - 2); i >= 0; i--) { _lossPrHistory[i + 1].lossPr255 = _lossPrHistory[i].lossPr255; _lossPrHistory[i + 1].timeMs = _lossPrHistory[i].timeMs; } } if (_shortMaxLossPr255 == 0) { _shortMaxLossPr255 = lossPr255; } _lossPrHistory[0].lossPr255 = _shortMaxLossPr255; _lossPrHistory[0].timeMs = now; _shortMaxLossPr255 = 0; } } WebRtc_UWord8 VCMLossProtectionLogic::MaxFilteredLossPr(WebRtc_Word64 nowMs) const { WebRtc_UWord8 maxFound = _shortMaxLossPr255; if (_lossPrHistory[0].timeMs == -1) { return maxFound; } for (WebRtc_Word32 i = 0; i < kLossPrHistorySize; i++) { if (_lossPrHistory[i].timeMs == -1) { break; } if (nowMs - _lossPrHistory[i].timeMs > kLossPrHistorySize * kLossPrShortFilterWinMs) { // This sample (and all samples after this) is too old break; } if (_lossPrHistory[i].lossPr255 > maxFound) { // This sample is the largest one this far into the history maxFound = _lossPrHistory[i].lossPr255; } } return maxFound; } WebRtc_UWord8 VCMLossProtectionLogic::FilteredLoss() const { //take the average received loss //return static_cast(_lossPr255.Value() + 0.5f); //TODO: Update for hybrid //take the windowed max of the received loss if (_selectedMethod != NULL && _selectedMethod->Type() == kFEC) { return MaxFilteredLossPr(static_cast (VCMTickTime::MillisecondTimestamp())); } else { return static_cast (_lossPr255.Value() + 0.5); } } void VCMLossProtectionLogic::UpdateFilteredLossPr(WebRtc_UWord8 packetLossEnc) { _lossPr = (float) packetLossEnc / (float) 255.0; } void VCMLossProtectionLogic::UpdateBitRate(float bitRate) { _bitRate = bitRate; } void VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets) { WebRtc_UWord32 now = static_cast (VCMTickTime::MillisecondTimestamp()); _packetsPerFrame.Apply(static_cast (now - _lastPacketPerFrameUpdateT), nPackets); _lastPacketPerFrameUpdateT = now; } void VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets) { WebRtc_UWord32 now = static_cast (VCMTickTime::MillisecondTimestamp()); _packetsPerFrameKey.Apply(static_cast (now - _lastPacketPerFrameUpdateTKey), nPackets); _lastPacketPerFrameUpdateTKey = now; } void VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize) { _keyFrameSize = keyFrameSize; } bool VCMLossProtectionLogic::UpdateMethod(VCMProtectionMethod *newMethod /*=NULL */) { _currentParameters.rtt = _rtt; _currentParameters.lossPr = _lossPr; _currentParameters.bitRate = _bitRate; _currentParameters.frameRate = _frameRate; //should this be named actual frame rate? _currentParameters.keyFrameSize = _keyFrameSize; _currentParameters.fecRateDelta = _fecRateDelta; _currentParameters.fecRateKey = _fecRateKey; _currentParameters.packetsPerFrame = _packetsPerFrame.Value(); _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.Value(); _currentParameters.residualPacketLoss = _residualPacketLoss; _currentParameters.fecType = _fecType; if (newMethod == NULL) { //_selectedMethod = _bestNotOkMethod = NULL; VCMProtectionMethod *method; ListItem *item; for (item = _availableMethods.First(); item != NULL; item = _availableMethods.Next(item)) { method = static_cast (item->GetItem()); if (method != NULL) { if (method->Type() == kFEC) { _selectedMethod = method; } if (method->Type() == kNACK) { _selectedMethod = method; } if (method->Type() == kNackFec) { _selectedMethod = method; } method->UpdateParameters(&_currentParameters); } } if (_selectedMethod != NULL && _selectedMethod->Type() != kFEC) { _selectedMethod = method; } } else { _selectedMethod = newMethod; _selectedMethod->UpdateParameters(&_currentParameters); } return true; } VCMProtectionMethod* VCMLossProtectionLogic::SelectedMethod() const { return _selectedMethod; } void VCMLossProtectionLogic::Reset() { _lastPrUpdateT = static_cast (VCMTickTime::MillisecondTimestamp()); _lastPacketPerFrameUpdateT = static_cast (VCMTickTime::MillisecondTimestamp()); _lossPr255.Reset(0.9999f); _packetsPerFrame.Reset(0.9999f); _fecRateDelta = _fecRateKey = 0; for (WebRtc_Word32 i = 0; i < kLossPrHistorySize; i++) { _lossPrHistory[i].lossPr255 = 0; _lossPrHistory[i].timeMs = -1; } _shortMaxLossPr255 = 0; ClearLossProtections(); } }