blob: 42db2facf1cf4252775cb5d7f4c6510635041e50 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
Henrik Kjellander2557b862015-11-18 22:00:21 +010011#include "webrtc/modules/video_coding/media_opt_util.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
niklase@google.com470e71d2011-07-07 08:21:25 +000013#include <float.h>
14#include <limits.h>
pbos@webrtc.orga4407322013-07-16 12:32:05 +000015#include <math.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000016
philipel9d3ab612015-12-21 04:12:39 -080017#include <algorithm>
18
Henrik Kjellanderff761fb2015-11-04 08:31:52 +010019#include "webrtc/modules/include/module_common_types.h"
pbos@webrtc.orga4407322013-07-16 12:32:05 +000020#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
Henrik Kjellander2557b862015-11-18 22:00:21 +010021#include "webrtc/modules/video_coding/include/video_coding_defines.h"
22#include "webrtc/modules/video_coding/fec_tables_xor.h"
23#include "webrtc/modules/video_coding/nack_fec_tables.h"
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +000024
niklase@google.com470e71d2011-07-07 08:21:25 +000025namespace webrtc {
Peter Boström9cb1f302015-04-01 11:39:49 +020026// Max value of loss rates in off-line model
27static const int kPacketLossMax = 129;
28
stefan@webrtc.orga64300a2013-03-04 15:24:40 +000029namespace media_optimization {
niklase@google.com470e71d2011-07-07 08:21:25 +000030
Peter Boström9cb1f302015-04-01 11:39:49 +020031VCMProtectionMethod::VCMProtectionMethod()
32 : _effectivePacketLoss(0),
33 _protectionFactorK(0),
34 _protectionFactorD(0),
35 _scaleProtKey(2.0f),
36 _maxPayloadSize(1460),
Peter Boström9cb1f302015-04-01 11:39:49 +020037 _corrFecCost(1.0),
philipel9d3ab612015-12-21 04:12:39 -080038 _type(kNone) {}
mikhal@webrtc.orga057a952011-08-26 21:17:34 +000039
pbosc0430522016-05-01 17:19:05 -070040VCMProtectionMethod::~VCMProtectionMethod() {}
marpan@google.com86548c62011-07-12 17:12:57 +000041
pkasting@chromium.org16825b12015-01-12 21:51:21 +000042VCMNackFecMethod::VCMNackFecMethod(int64_t lowRttNackThresholdMs,
43 int64_t highRttNackThresholdMs)
stefan@webrtc.org932ab182011-11-29 11:33:31 +000044 : VCMFecMethod(),
45 _lowRttNackMs(lowRttNackThresholdMs),
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +000046 _highRttNackMs(highRttNackThresholdMs),
47 _maxFramesFec(1) {
stefan@webrtc.org932ab182011-11-29 11:33:31 +000048 assert(lowRttNackThresholdMs >= -1 && highRttNackThresholdMs >= -1);
49 assert(highRttNackThresholdMs == -1 ||
50 lowRttNackThresholdMs <= highRttNackThresholdMs);
51 assert(lowRttNackThresholdMs > -1 || highRttNackThresholdMs == -1);
52 _type = kNackFec;
mikhal@google.com77408882011-07-22 22:05:25 +000053}
54
philipel9d3ab612015-12-21 04:12:39 -080055VCMNackFecMethod::~VCMNackFecMethod() {
56 //
mikhal@google.com77408882011-07-22 22:05:25 +000057}
philipel9d3ab612015-12-21 04:12:39 -080058bool VCMNackFecMethod::ProtectionFactor(
59 const VCMProtectionParameters* parameters) {
60 // Hybrid Nack FEC has three operational modes:
61 // 1. Low RTT (below kLowRttNackMs) - Nack only: Set FEC rate
62 // (_protectionFactorD) to zero. -1 means no FEC.
63 // 2. High RTT (above _highRttNackMs) - FEC Only: Keep FEC factors.
64 // -1 means always allow NACK.
65 // 3. Medium RTT values - Hybrid mode: We will only nack the
66 // residual following the decoding of the FEC (refer to JB logic). FEC
67 // delta protection factor will be adjusted based on the RTT.
mikhal@google.com022716b2011-07-20 23:12:57 +000068
philipel9d3ab612015-12-21 04:12:39 -080069 // Otherwise: we count on FEC; if the RTT is below a threshold, then we
70 // nack the residual, based on a decision made in the JB.
mikhal@google.com022716b2011-07-20 23:12:57 +000071
philipel9d3ab612015-12-21 04:12:39 -080072 // Compute the protection factors
73 VCMFecMethod::ProtectionFactor(parameters);
74 if (_lowRttNackMs == -1 || parameters->rtt < _lowRttNackMs) {
75 _protectionFactorD = 0;
76 VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
mikhal@google.com320813c2011-08-03 20:47:50 +000077
mikhal@google.com679450f2011-08-01 22:14:58 +000078 // When in Hybrid mode (RTT range), adjust FEC rates based on the
79 // RTT (NACK effectiveness) - adjustment factor is in the range [0,1].
philipel9d3ab612015-12-21 04:12:39 -080080 } else if (_highRttNackMs == -1 || parameters->rtt < _highRttNackMs) {
81 // TODO(mikhal): Disabling adjustment temporarily.
82 // uint16_t rttIndex = (uint16_t) parameters->rtt;
83 float adjustRtt = 1.0f; // (float)VCMNackFecTable[rttIndex] / 100.0f;
niklase@google.com470e71d2011-07-07 08:21:25 +000084
philipel9d3ab612015-12-21 04:12:39 -080085 // Adjust FEC with NACK on (for delta frame only)
86 // table depends on RTT relative to rttMax (NACK Threshold)
87 _protectionFactorD = static_cast<uint8_t>(
88 adjustRtt * static_cast<float>(_protectionFactorD));
89 // update FEC rates after applying adjustment
90 VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
91 }
mikhal@google.com679450f2011-08-01 22:14:58 +000092
philipel9d3ab612015-12-21 04:12:39 -080093 return true;
mikhal@google.com022716b2011-07-20 23:12:57 +000094}
niklase@google.com470e71d2011-07-07 08:21:25 +000095
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +000096int VCMNackFecMethod::ComputeMaxFramesFec(
97 const VCMProtectionParameters* parameters) {
98 if (parameters->numLayers > 2) {
99 // For more than 2 temporal layers we will only have FEC on the base layer,
100 // and the base layers will be pretty far apart. Therefore we force one
101 // frame FEC.
102 return 1;
103 }
104 // We set the max number of frames to base the FEC on so that on average
105 // we will have complete frames in one RTT. Note that this is an upper
106 // bound, and that the actual number of frames used for FEC is decided by the
107 // RTP module based on the actual number of packets and the protection factor.
philipel9d3ab612015-12-21 04:12:39 -0800108 float base_layer_framerate =
109 parameters->frameRate /
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000110 static_cast<float>(1 << (parameters->numLayers - 1));
philipel9d3ab612015-12-21 04:12:39 -0800111 int max_frames_fec = std::max(
112 static_cast<int>(2.0f * base_layer_framerate * parameters->rtt / 1000.0f +
113 0.5f),
114 1);
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000115 // |kUpperLimitFramesFec| is the upper limit on how many frames we
116 // allow any FEC to be based on.
117 if (max_frames_fec > kUpperLimitFramesFec) {
118 max_frames_fec = kUpperLimitFramesFec;
119 }
120 return max_frames_fec;
121}
122
123int VCMNackFecMethod::MaxFramesFec() const {
124 return _maxFramesFec;
125}
126
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000127bool VCMNackFecMethod::BitRateTooLowForFec(
128 const VCMProtectionParameters* parameters) {
129 // Bitrate below which we turn off FEC, regardless of reported packet loss.
130 // The condition should depend on resolution and content. For now, use
131 // threshold on bytes per frame, with some effect for the frame size.
132 // The condition for turning off FEC is also based on other factors,
133 // such as |_numLayers|, |_maxFramesFec|, and |_rtt|.
134 int estimate_bytes_per_frame = 1000 * BitsPerFrame(parameters) / 8;
135 int max_bytes_per_frame = kMaxBytesPerFrameForFec;
136 int num_pixels = parameters->codecWidth * parameters->codecHeight;
137 if (num_pixels <= 352 * 288) {
138 max_bytes_per_frame = kMaxBytesPerFrameForFecLow;
139 } else if (num_pixels > 640 * 480) {
140 max_bytes_per_frame = kMaxBytesPerFrameForFecHigh;
141 }
philipel9d3ab612015-12-21 04:12:39 -0800142 // TODO(marpan): add condition based on maximum frames used for FEC,
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000143 // and expand condition based on frame size.
pkasting@chromium.org16825b12015-01-12 21:51:21 +0000144 // Max round trip time threshold in ms.
145 const int64_t kMaxRttTurnOffFec = 200;
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000146 if (estimate_bytes_per_frame < max_bytes_per_frame &&
philipel9d3ab612015-12-21 04:12:39 -0800147 parameters->numLayers < 3 && parameters->rtt < kMaxRttTurnOffFec) {
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000148 return true;
149 }
150 return false;
151}
152
philipel9d3ab612015-12-21 04:12:39 -0800153bool VCMNackFecMethod::EffectivePacketLoss(
154 const VCMProtectionParameters* parameters) {
155 // Set the effective packet loss for encoder (based on FEC code).
156 // Compute the effective packet loss and residual packet loss due to FEC.
157 VCMFecMethod::EffectivePacketLoss(parameters);
158 return true;
mikhal@google.com022716b2011-07-20 23:12:57 +0000159}
niklase@google.com470e71d2011-07-07 08:21:25 +0000160
philipel9d3ab612015-12-21 04:12:39 -0800161bool VCMNackFecMethod::UpdateParameters(
162 const VCMProtectionParameters* parameters) {
163 ProtectionFactor(parameters);
164 EffectivePacketLoss(parameters);
165 _maxFramesFec = ComputeMaxFramesFec(parameters);
166 if (BitRateTooLowForFec(parameters)) {
167 _protectionFactorK = 0;
168 _protectionFactorD = 0;
169 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000170
philipel9d3ab612015-12-21 04:12:39 -0800171 // Protection/fec rates obtained above are defined relative to total number
172 // of packets (total rate: source + fec) FEC in RTP module assumes
173 // protection factor is defined relative to source number of packets so we
174 // should convert the factor to reduce mismatch between mediaOpt's rate and
175 // the actual one
176 _protectionFactorK = VCMFecMethod::ConvertFECRate(_protectionFactorK);
177 _protectionFactorD = VCMFecMethod::ConvertFECRate(_protectionFactorD);
niklase@google.com470e71d2011-07-07 08:21:25 +0000178
philipel9d3ab612015-12-21 04:12:39 -0800179 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000180}
181
philipel9d3ab612015-12-21 04:12:39 -0800182VCMNackMethod::VCMNackMethod() : VCMProtectionMethod() {
183 _type = kNack;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000184}
185
philipel9d3ab612015-12-21 04:12:39 -0800186VCMNackMethod::~VCMNackMethod() {
187 //
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000188}
189
philipel9d3ab612015-12-21 04:12:39 -0800190bool VCMNackMethod::EffectivePacketLoss(
191 const VCMProtectionParameters* parameter) {
192 // Effective Packet Loss, NA in current version.
193 _effectivePacketLoss = 0;
194 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000195}
196
philipel9d3ab612015-12-21 04:12:39 -0800197bool VCMNackMethod::UpdateParameters(
198 const VCMProtectionParameters* parameters) {
199 // Compute the effective packet loss
200 EffectivePacketLoss(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000201
philipel9d3ab612015-12-21 04:12:39 -0800202 // nackCost = (bitRate - nackCost) * (lossPr)
203 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000204}
205
philipel9d3ab612015-12-21 04:12:39 -0800206VCMFecMethod::VCMFecMethod() : VCMProtectionMethod() {
207 _type = kFec;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000208}
philipel9d3ab612015-12-21 04:12:39 -0800209VCMFecMethod::~VCMFecMethod() {
210 //
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000211}
212
philipel9d3ab612015-12-21 04:12:39 -0800213uint8_t VCMFecMethod::BoostCodeRateKey(uint8_t packetFrameDelta,
214 uint8_t packetFrameKey) const {
215 uint8_t boostRateKey = 2;
216 // Default: ratio scales the FEC protection up for I frames
217 uint8_t ratio = 1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000218
philipel9d3ab612015-12-21 04:12:39 -0800219 if (packetFrameDelta > 0) {
220 ratio = (int8_t)(packetFrameKey / packetFrameDelta);
221 }
222 ratio = VCM_MAX(boostRateKey, ratio);
niklase@google.com470e71d2011-07-07 08:21:25 +0000223
philipel9d3ab612015-12-21 04:12:39 -0800224 return ratio;
niklase@google.com470e71d2011-07-07 08:21:25 +0000225}
226
philipel9d3ab612015-12-21 04:12:39 -0800227uint8_t VCMFecMethod::ConvertFECRate(uint8_t codeRateRTP) const {
228 return static_cast<uint8_t>(VCM_MIN(
229 255,
230 (0.5 + 255.0 * codeRateRTP / static_cast<float>(255 - codeRateRTP))));
niklase@google.com470e71d2011-07-07 08:21:25 +0000231}
232
mikhal@google.com679450f2011-08-01 22:14:58 +0000233// Update FEC with protectionFactorD
philipel9d3ab612015-12-21 04:12:39 -0800234void VCMFecMethod::UpdateProtectionFactorD(uint8_t protectionFactorD) {
235 _protectionFactorD = protectionFactorD;
mikhal@google.com679450f2011-08-01 22:14:58 +0000236}
237
mikhal@webrtc.orgd0752c32011-10-19 15:48:30 +0000238// Update FEC with protectionFactorK
philipel9d3ab612015-12-21 04:12:39 -0800239void VCMFecMethod::UpdateProtectionFactorK(uint8_t protectionFactorK) {
240 _protectionFactorK = protectionFactorK;
mikhal@webrtc.orgd0752c32011-10-19 15:48:30 +0000241}
242
philipel9d3ab612015-12-21 04:12:39 -0800243bool VCMFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters) {
244 // FEC PROTECTION SETTINGS: varies with packet loss and bitrate
niklase@google.com470e71d2011-07-07 08:21:25 +0000245
philipel9d3ab612015-12-21 04:12:39 -0800246 // No protection if (filtered) packetLoss is 0
247 uint8_t packetLoss = (uint8_t)(255 * parameters->lossPr);
248 if (packetLoss == 0) {
249 _protectionFactorK = 0;
250 _protectionFactorD = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000251 return true;
philipel9d3ab612015-12-21 04:12:39 -0800252 }
253
254 // Parameters for FEC setting:
255 // first partition size, thresholds, table pars, spatial resoln fac.
256
257 // First partition protection: ~ 20%
258 uint8_t firstPartitionProt = (uint8_t)(255 * 0.20);
259
260 // Minimum protection level needed to generate one FEC packet for one
261 // source packet/frame (in RTP sender)
262 uint8_t minProtLevelFec = 85;
263
264 // Threshold on packetLoss and bitRrate/frameRate (=average #packets),
265 // above which we allocate protection to cover at least first partition.
266 uint8_t lossThr = 0;
267 uint8_t packetNumThr = 1;
268
269 // Parameters for range of rate index of table.
270 const uint8_t ratePar1 = 5;
271 const uint8_t ratePar2 = 49;
272
273 // Spatial resolution size, relative to a reference size.
274 float spatialSizeToRef =
275 static_cast<float>(parameters->codecWidth * parameters->codecHeight) /
276 (static_cast<float>(704 * 576));
277 // resolnFac: This parameter will generally increase/decrease the FEC rate
278 // (for fixed bitRate and packetLoss) based on system size.
279 // Use a smaller exponent (< 1) to control/soften system size effect.
280 const float resolnFac = 1.0 / powf(spatialSizeToRef, 0.3f);
281
282 const int bitRatePerFrame = BitsPerFrame(parameters);
283
284 // Average number of packets per frame (source and fec):
285 const uint8_t avgTotPackets =
286 1 + (uint8_t)(static_cast<float>(bitRatePerFrame) * 1000.0 /
287 static_cast<float>(8.0 * _maxPayloadSize) +
288 0.5);
289
290 // FEC rate parameters: for P and I frame
291 uint8_t codeRateDelta = 0;
292 uint8_t codeRateKey = 0;
293
294 // Get index for table: the FEC protection depends on an effective rate.
295 // The range on the rate index corresponds to rates (bps)
296 // from ~200k to ~8000k, for 30fps
297 const uint16_t effRateFecTable =
298 static_cast<uint16_t>(resolnFac * bitRatePerFrame);
299 uint8_t rateIndexTable = (uint8_t)VCM_MAX(
300 VCM_MIN((effRateFecTable - ratePar1) / ratePar1, ratePar2), 0);
301
302 // Restrict packet loss range to 50:
303 // current tables defined only up to 50%
304 if (packetLoss >= kPacketLossMax) {
305 packetLoss = kPacketLossMax - 1;
306 }
307 uint16_t indexTable = rateIndexTable * kPacketLossMax + packetLoss;
308
309 // Check on table index
310 assert(indexTable < kSizeCodeRateXORTable);
311
312 // Protection factor for P frame
313 codeRateDelta = kCodeRateXORTable[indexTable];
314
315 if (packetLoss > lossThr && avgTotPackets > packetNumThr) {
316 // Set a minimum based on first partition size.
317 if (codeRateDelta < firstPartitionProt) {
318 codeRateDelta = firstPartitionProt;
319 }
320 }
321
322 // Check limit on amount of protection for P frame; 50% is max.
323 if (codeRateDelta >= kPacketLossMax) {
324 codeRateDelta = kPacketLossMax - 1;
325 }
326
philipel9d3ab612015-12-21 04:12:39 -0800327 // For Key frame:
328 // Effectively at a higher rate, so we scale/boost the rate
329 // The boost factor may depend on several factors: ratio of packet
330 // number of I to P frames, how much protection placed on P frames, etc.
331 const uint8_t packetFrameDelta = (uint8_t)(0.5 + parameters->packetsPerFrame);
332 const uint8_t packetFrameKey =
333 (uint8_t)(0.5 + parameters->packetsPerFrameKey);
334 const uint8_t boostKey = BoostCodeRateKey(packetFrameDelta, packetFrameKey);
335
336 rateIndexTable = (uint8_t)VCM_MAX(
337 VCM_MIN(1 + (boostKey * effRateFecTable - ratePar1) / ratePar1, ratePar2),
338 0);
339 uint16_t indexTableKey = rateIndexTable * kPacketLossMax + packetLoss;
340
341 indexTableKey = VCM_MIN(indexTableKey, kSizeCodeRateXORTable);
342
343 // Check on table index
344 assert(indexTableKey < kSizeCodeRateXORTable);
345
346 // Protection factor for I frame
347 codeRateKey = kCodeRateXORTable[indexTableKey];
348
349 // Boosting for Key frame.
350 int boostKeyProt = _scaleProtKey * codeRateDelta;
351 if (boostKeyProt >= kPacketLossMax) {
352 boostKeyProt = kPacketLossMax - 1;
353 }
354
355 // Make sure I frame protection is at least larger than P frame protection,
356 // and at least as high as filtered packet loss.
357 codeRateKey = static_cast<uint8_t>(
358 VCM_MAX(packetLoss, VCM_MAX(boostKeyProt, codeRateKey)));
359
360 // Check limit on amount of protection for I frame: 50% is max.
361 if (codeRateKey >= kPacketLossMax) {
362 codeRateKey = kPacketLossMax - 1;
363 }
364
365 _protectionFactorK = codeRateKey;
366 _protectionFactorD = codeRateDelta;
367
368 // Generally there is a rate mis-match between the FEC cost estimated
369 // in mediaOpt and the actual FEC cost sent out in RTP module.
370 // This is more significant at low rates (small # of source packets), where
371 // the granularity of the FEC decreases. In this case, non-zero protection
372 // in mediaOpt may generate 0 FEC packets in RTP sender (since actual #FEC
373 // is based on rounding off protectionFactor on actual source packet number).
374 // The correction factor (_corrFecCost) attempts to corrects this, at least
375 // for cases of low rates (small #packets) and low protection levels.
376
377 float numPacketsFl = 1.0f + (static_cast<float>(bitRatePerFrame) * 1000.0 /
378 static_cast<float>(8.0 * _maxPayloadSize) +
379 0.5);
380
381 const float estNumFecGen =
382 0.5f + static_cast<float>(_protectionFactorD * numPacketsFl / 255.0f);
383
384 // We reduce cost factor (which will reduce overhead for FEC and
385 // hybrid method) and not the protectionFactor.
386 _corrFecCost = 1.0f;
387 if (estNumFecGen < 1.1f && _protectionFactorD < minProtLevelFec) {
388 _corrFecCost = 0.5f;
389 }
390 if (estNumFecGen < 0.9f && _protectionFactorD < minProtLevelFec) {
391 _corrFecCost = 0.0f;
392 }
393
philipel9d3ab612015-12-21 04:12:39 -0800394 // DONE WITH FEC PROTECTION SETTINGS
395 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000396}
397
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000398int VCMFecMethod::BitsPerFrame(const VCMProtectionParameters* parameters) {
399 // When temporal layers are available FEC will only be applied on the base
400 // layer.
401 const float bitRateRatio =
philipel9d3ab612015-12-21 04:12:39 -0800402 kVp8LayerRateAlloction[parameters->numLayers - 1][0];
thakis@chromium.org65bc2542012-08-13 19:26:12 +0000403 float frameRateRatio = powf(1 / 2.0, parameters->numLayers - 1);
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000404 float bitRate = parameters->bitRate * bitRateRatio;
405 float frameRate = parameters->frameRate * frameRateRatio;
406
407 // TODO(mikhal): Update factor following testing.
408 float adjustmentFactor = 1;
409
Peter Boström5e8351b2016-01-28 23:55:29 +0100410 if (frameRate < 1.0f)
411 frameRate = 1.0f;
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000412 // Average bits per frame (units of kbits)
413 return static_cast<int>(adjustmentFactor * bitRate / frameRate);
414}
415
philipel9d3ab612015-12-21 04:12:39 -0800416bool VCMFecMethod::EffectivePacketLoss(
417 const VCMProtectionParameters* parameters) {
418 // Effective packet loss to encoder is based on RPL (residual packet loss)
419 // this is a soft setting based on degree of FEC protection
420 // RPL = received/input packet loss - average_FEC_recovery
421 // note: received/input packet loss may be filtered based on FilteredLoss
niklase@google.com470e71d2011-07-07 08:21:25 +0000422
philipel9d3ab612015-12-21 04:12:39 -0800423 // Effective Packet Loss, NA in current version.
424 _effectivePacketLoss = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000425
philipel9d3ab612015-12-21 04:12:39 -0800426 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000427}
428
philipel9d3ab612015-12-21 04:12:39 -0800429bool VCMFecMethod::UpdateParameters(const VCMProtectionParameters* parameters) {
430 // Compute the protection factor
431 ProtectionFactor(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000432
philipel9d3ab612015-12-21 04:12:39 -0800433 // Compute the effective packet loss
434 EffectivePacketLoss(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000435
philipel9d3ab612015-12-21 04:12:39 -0800436 // Protection/fec rates obtained above is defined relative to total number
437 // of packets (total rate: source+fec) FEC in RTP module assumes protection
438 // factor is defined relative to source number of packets so we should
439 // convert the factor to reduce mismatch between mediaOpt suggested rate and
440 // the actual rate
441 _protectionFactorK = ConvertFECRate(_protectionFactorK);
442 _protectionFactorD = ConvertFECRate(_protectionFactorD);
niklase@google.com470e71d2011-07-07 08:21:25 +0000443
philipel9d3ab612015-12-21 04:12:39 -0800444 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000445}
philipel9d3ab612015-12-21 04:12:39 -0800446VCMLossProtectionLogic::VCMLossProtectionLogic(int64_t nowMs)
447 : _currentParameters(),
448 _rtt(0),
449 _lossPr(0.0f),
450 _bitRate(0.0f),
451 _frameRate(0.0f),
452 _keyFrameSize(0.0f),
453 _fecRateKey(0),
454 _fecRateDelta(0),
455 _lastPrUpdateT(0),
456 _lossPr255(0.9999f),
457 _lossPrHistory(),
458 _shortMaxLossPr255(0),
459 _packetsPerFrame(0.9999f),
460 _packetsPerFrameKey(0.9999f),
461 _codecWidth(0),
462 _codecHeight(0),
463 _numLayers(1) {
464 Reset(nowMs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000465}
466
philipel9d3ab612015-12-21 04:12:39 -0800467VCMLossProtectionLogic::~VCMLossProtectionLogic() {
468 Release();
niklase@google.com470e71d2011-07-07 08:21:25 +0000469}
470
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000471void VCMLossProtectionLogic::SetMethod(
472 enum VCMProtectionMethodEnum newMethodType) {
pbosba8c15b2015-07-14 09:36:34 -0700473 if (_selectedMethod && _selectedMethod->Type() == newMethodType)
474 return;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000475
philipel9d3ab612015-12-21 04:12:39 -0800476 switch (newMethodType) {
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000477 case kNack:
pbosba8c15b2015-07-14 09:36:34 -0700478 _selectedMethod.reset(new VCMNackMethod());
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000479 break;
480 case kFec:
pbosba8c15b2015-07-14 09:36:34 -0700481 _selectedMethod.reset(new VCMFecMethod());
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000482 break;
483 case kNackFec:
pbosba8c15b2015-07-14 09:36:34 -0700484 _selectedMethod.reset(new VCMNackFecMethod(kLowRttNackMs, -1));
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000485 break;
486 case kNone:
pbosba8c15b2015-07-14 09:36:34 -0700487 _selectedMethod.reset();
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000488 break;
489 }
490 UpdateMethod();
niklase@google.com470e71d2011-07-07 08:21:25 +0000491}
492
philipel9d3ab612015-12-21 04:12:39 -0800493void VCMLossProtectionLogic::UpdateRtt(int64_t rtt) {
494 _rtt = rtt;
niklase@google.com470e71d2011-07-07 08:21:25 +0000495}
496
philipel9d3ab612015-12-21 04:12:39 -0800497void VCMLossProtectionLogic::UpdateMaxLossHistory(uint8_t lossPr255,
498 int64_t now) {
499 if (_lossPrHistory[0].timeMs >= 0 &&
500 now - _lossPrHistory[0].timeMs < kLossPrShortFilterWinMs) {
501 if (lossPr255 > _shortMaxLossPr255) {
502 _shortMaxLossPr255 = lossPr255;
niklase@google.com470e71d2011-07-07 08:21:25 +0000503 }
philipel9d3ab612015-12-21 04:12:39 -0800504 } else {
505 // Only add a new value to the history once a second
506 if (_lossPrHistory[0].timeMs == -1) {
507 // First, no shift
508 _shortMaxLossPr255 = lossPr255;
509 } else {
510 // Shift
511 for (int32_t i = (kLossPrHistorySize - 2); i >= 0; i--) {
512 _lossPrHistory[i + 1].lossPr255 = _lossPrHistory[i].lossPr255;
513 _lossPrHistory[i + 1].timeMs = _lossPrHistory[i].timeMs;
514 }
515 }
516 if (_shortMaxLossPr255 == 0) {
517 _shortMaxLossPr255 = lossPr255;
518 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000519
philipel9d3ab612015-12-21 04:12:39 -0800520 _lossPrHistory[0].lossPr255 = _shortMaxLossPr255;
521 _lossPrHistory[0].timeMs = now;
522 _shortMaxLossPr255 = 0;
523 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000524}
525
philipel9d3ab612015-12-21 04:12:39 -0800526uint8_t VCMLossProtectionLogic::MaxFilteredLossPr(int64_t nowMs) const {
527 uint8_t maxFound = _shortMaxLossPr255;
528 if (_lossPrHistory[0].timeMs == -1) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000529 return maxFound;
philipel9d3ab612015-12-21 04:12:39 -0800530 }
531 for (int32_t i = 0; i < kLossPrHistorySize; i++) {
532 if (_lossPrHistory[i].timeMs == -1) {
533 break;
534 }
535 if (nowMs - _lossPrHistory[i].timeMs >
536 kLossPrHistorySize * kLossPrShortFilterWinMs) {
537 // This sample (and all samples after this) is too old
538 break;
539 }
540 if (_lossPrHistory[i].lossPr255 > maxFound) {
541 // This sample is the largest one this far into the history
542 maxFound = _lossPrHistory[i].lossPr255;
543 }
544 }
545 return maxFound;
niklase@google.com470e71d2011-07-07 08:21:25 +0000546}
547
philipel9d3ab612015-12-21 04:12:39 -0800548uint8_t VCMLossProtectionLogic::FilteredLoss(int64_t nowMs,
549 FilterPacketLossMode filter_mode,
550 uint8_t lossPr255) {
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000551 // Update the max window filter.
552 UpdateMaxLossHistory(lossPr255, nowMs);
553
554 // Update the recursive average filter.
philipel9d3ab612015-12-21 04:12:39 -0800555 _lossPr255.Apply(static_cast<float>(nowMs - _lastPrUpdateT),
556 static_cast<float>(lossPr255));
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000557 _lastPrUpdateT = nowMs;
558
559 // Filtered loss: default is received loss (no filtering).
pbos@webrtc.org7b859cc2013-04-02 15:54:38 +0000560 uint8_t filtered_loss = lossPr255;
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000561
562 switch (filter_mode) {
563 case kNoFilter:
564 break;
565 case kAvgFilter:
minyue@webrtc.org74aaf292014-07-16 21:28:26 +0000566 filtered_loss = static_cast<uint8_t>(_lossPr255.filtered() + 0.5);
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000567 break;
568 case kMaxFilter:
569 filtered_loss = MaxFilteredLossPr(nowMs);
570 break;
571 }
572
573 return filtered_loss;
niklase@google.com470e71d2011-07-07 08:21:25 +0000574}
575
philipel9d3ab612015-12-21 04:12:39 -0800576void VCMLossProtectionLogic::UpdateFilteredLossPr(uint8_t packetLossEnc) {
577 _lossPr = static_cast<float>(packetLossEnc) / 255.0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000578}
579
philipel9d3ab612015-12-21 04:12:39 -0800580void VCMLossProtectionLogic::UpdateBitRate(float bitRate) {
581 _bitRate = bitRate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000582}
583
philipel9d3ab612015-12-21 04:12:39 -0800584void VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets,
585 int64_t nowMs) {
586 _packetsPerFrame.Apply(static_cast<float>(nowMs - _lastPacketPerFrameUpdateT),
587 nPackets);
588 _lastPacketPerFrameUpdateT = nowMs;
niklase@google.com470e71d2011-07-07 08:21:25 +0000589}
590
philipel9d3ab612015-12-21 04:12:39 -0800591void VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets,
592 int64_t nowMs) {
593 _packetsPerFrameKey.Apply(
594 static_cast<float>(nowMs - _lastPacketPerFrameUpdateTKey), nPackets);
595 _lastPacketPerFrameUpdateTKey = nowMs;
niklase@google.com470e71d2011-07-07 08:21:25 +0000596}
597
philipel9d3ab612015-12-21 04:12:39 -0800598void VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize) {
599 _keyFrameSize = keyFrameSize;
niklase@google.com470e71d2011-07-07 08:21:25 +0000600}
601
philipel9d3ab612015-12-21 04:12:39 -0800602void VCMLossProtectionLogic::UpdateFrameSize(uint16_t width, uint16_t height) {
603 _codecWidth = width;
604 _codecHeight = height;
niklase@google.com470e71d2011-07-07 08:21:25 +0000605}
606
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000607void VCMLossProtectionLogic::UpdateNumLayers(int numLayers) {
608 _numLayers = (numLayers == 0) ? 1 : numLayers;
609}
610
philipel9d3ab612015-12-21 04:12:39 -0800611bool VCMLossProtectionLogic::UpdateMethod() {
612 if (!_selectedMethod)
613 return false;
614 _currentParameters.rtt = _rtt;
615 _currentParameters.lossPr = _lossPr;
616 _currentParameters.bitRate = _bitRate;
617 _currentParameters.frameRate = _frameRate; // rename actual frame rate?
618 _currentParameters.keyFrameSize = _keyFrameSize;
619 _currentParameters.fecRateDelta = _fecRateDelta;
620 _currentParameters.fecRateKey = _fecRateKey;
621 _currentParameters.packetsPerFrame = _packetsPerFrame.filtered();
622 _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.filtered();
623 _currentParameters.codecWidth = _codecWidth;
624 _currentParameters.codecHeight = _codecHeight;
625 _currentParameters.numLayers = _numLayers;
626 return _selectedMethod->UpdateParameters(&_currentParameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000627}
628
philipel9d3ab612015-12-21 04:12:39 -0800629VCMProtectionMethod* VCMLossProtectionLogic::SelectedMethod() const {
630 return _selectedMethod.get();
niklase@google.com470e71d2011-07-07 08:21:25 +0000631}
632
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000633VCMProtectionMethodEnum VCMLossProtectionLogic::SelectedType() const {
pbosba8c15b2015-07-14 09:36:34 -0700634 return _selectedMethod ? _selectedMethod->Type() : kNone;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000635}
636
philipel9d3ab612015-12-21 04:12:39 -0800637void VCMLossProtectionLogic::Reset(int64_t nowMs) {
638 _lastPrUpdateT = nowMs;
639 _lastPacketPerFrameUpdateT = nowMs;
640 _lastPacketPerFrameUpdateTKey = nowMs;
641 _lossPr255.Reset(0.9999f);
642 _packetsPerFrame.Reset(0.9999f);
643 _fecRateDelta = _fecRateKey = 0;
644 for (int32_t i = 0; i < kLossPrHistorySize; i++) {
645 _lossPrHistory[i].lossPr255 = 0;
646 _lossPrHistory[i].timeMs = -1;
647 }
648 _shortMaxLossPr255 = 0;
649 Release();
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000650}
651
pbosba8c15b2015-07-14 09:36:34 -0700652void VCMLossProtectionLogic::Release() {
653 _selectedMethod.reset();
niklase@google.com470e71d2011-07-07 08:21:25 +0000654}
655
stefan@webrtc.orga64300a2013-03-04 15:24:40 +0000656} // namespace media_optimization
657} // namespace webrtc