blob: aea35b04dda5bc53d74745eed3916352ce798a29 [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "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>
brandtr05f845d2016-11-16 22:59:39 -080018#include <limits>
philipel9d3ab612015-12-21 04:12:39 -080019
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "modules/include/module_common_types.h"
Mirko Bonadei6f440ed2018-06-21 13:41:01 +000021#include "modules/video_coding/codecs/vp8/include/vp8_common_types.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "modules/video_coding/fec_rate_table.h"
Yves Gerey665174f2018-06-19 15:03:05 +020023#include "modules/video_coding/include/video_coding_defines.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "modules/video_coding/nack_fec_tables.h"
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +000025
niklase@google.com470e71d2011-07-07 08:21:25 +000026namespace webrtc {
Peter Boström9cb1f302015-04-01 11:39:49 +020027// Max value of loss rates in off-line model
28static const int kPacketLossMax = 129;
29
stefan@webrtc.orga64300a2013-03-04 15:24:40 +000030namespace media_optimization {
niklase@google.com470e71d2011-07-07 08:21:25 +000031
Peter Boström9cb1f302015-04-01 11:39:49 +020032VCMProtectionMethod::VCMProtectionMethod()
33 : _effectivePacketLoss(0),
34 _protectionFactorK(0),
35 _protectionFactorD(0),
36 _scaleProtKey(2.0f),
37 _maxPayloadSize(1460),
Peter Boström9cb1f302015-04-01 11:39:49 +020038 _corrFecCost(1.0),
philipel9d3ab612015-12-21 04:12:39 -080039 _type(kNone) {}
mikhal@webrtc.orga057a952011-08-26 21:17:34 +000040
pbosc0430522016-05-01 17:19:05 -070041VCMProtectionMethod::~VCMProtectionMethod() {}
marpan@google.com86548c62011-07-12 17:12:57 +000042
pkasting@chromium.org16825b12015-01-12 21:51:21 +000043VCMNackFecMethod::VCMNackFecMethod(int64_t lowRttNackThresholdMs,
44 int64_t highRttNackThresholdMs)
stefan@webrtc.org932ab182011-11-29 11:33:31 +000045 : VCMFecMethod(),
46 _lowRttNackMs(lowRttNackThresholdMs),
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +000047 _highRttNackMs(highRttNackThresholdMs),
48 _maxFramesFec(1) {
stefan@webrtc.org932ab182011-11-29 11:33:31 +000049 assert(lowRttNackThresholdMs >= -1 && highRttNackThresholdMs >= -1);
50 assert(highRttNackThresholdMs == -1 ||
51 lowRttNackThresholdMs <= highRttNackThresholdMs);
52 assert(lowRttNackThresholdMs > -1 || highRttNackThresholdMs == -1);
53 _type = kNackFec;
mikhal@google.com77408882011-07-22 22:05:25 +000054}
55
philipel9d3ab612015-12-21 04:12:39 -080056VCMNackFecMethod::~VCMNackFecMethod() {
57 //
mikhal@google.com77408882011-07-22 22:05:25 +000058}
philipel9d3ab612015-12-21 04:12:39 -080059bool VCMNackFecMethod::ProtectionFactor(
60 const VCMProtectionParameters* parameters) {
61 // Hybrid Nack FEC has three operational modes:
62 // 1. Low RTT (below kLowRttNackMs) - Nack only: Set FEC rate
63 // (_protectionFactorD) to zero. -1 means no FEC.
64 // 2. High RTT (above _highRttNackMs) - FEC Only: Keep FEC factors.
65 // -1 means always allow NACK.
66 // 3. Medium RTT values - Hybrid mode: We will only nack the
67 // residual following the decoding of the FEC (refer to JB logic). FEC
68 // delta protection factor will be adjusted based on the RTT.
mikhal@google.com022716b2011-07-20 23:12:57 +000069
philipel9d3ab612015-12-21 04:12:39 -080070 // Otherwise: we count on FEC; if the RTT is below a threshold, then we
71 // nack the residual, based on a decision made in the JB.
mikhal@google.com022716b2011-07-20 23:12:57 +000072
philipel9d3ab612015-12-21 04:12:39 -080073 // Compute the protection factors
74 VCMFecMethod::ProtectionFactor(parameters);
75 if (_lowRttNackMs == -1 || parameters->rtt < _lowRttNackMs) {
76 _protectionFactorD = 0;
77 VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
mikhal@google.com320813c2011-08-03 20:47:50 +000078
mikhal@google.com679450f2011-08-01 22:14:58 +000079 // When in Hybrid mode (RTT range), adjust FEC rates based on the
80 // RTT (NACK effectiveness) - adjustment factor is in the range [0,1].
philipel9d3ab612015-12-21 04:12:39 -080081 } else if (_highRttNackMs == -1 || parameters->rtt < _highRttNackMs) {
82 // TODO(mikhal): Disabling adjustment temporarily.
83 // uint16_t rttIndex = (uint16_t) parameters->rtt;
84 float adjustRtt = 1.0f; // (float)VCMNackFecTable[rttIndex] / 100.0f;
niklase@google.com470e71d2011-07-07 08:21:25 +000085
philipel9d3ab612015-12-21 04:12:39 -080086 // Adjust FEC with NACK on (for delta frame only)
87 // table depends on RTT relative to rttMax (NACK Threshold)
88 _protectionFactorD = static_cast<uint8_t>(
89 adjustRtt * static_cast<float>(_protectionFactorD));
90 // update FEC rates after applying adjustment
91 VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
92 }
mikhal@google.com679450f2011-08-01 22:14:58 +000093
philipel9d3ab612015-12-21 04:12:39 -080094 return true;
mikhal@google.com022716b2011-07-20 23:12:57 +000095}
niklase@google.com470e71d2011-07-07 08:21:25 +000096
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +000097int VCMNackFecMethod::ComputeMaxFramesFec(
98 const VCMProtectionParameters* parameters) {
99 if (parameters->numLayers > 2) {
100 // For more than 2 temporal layers we will only have FEC on the base layer,
101 // and the base layers will be pretty far apart. Therefore we force one
102 // frame FEC.
103 return 1;
104 }
105 // We set the max number of frames to base the FEC on so that on average
106 // we will have complete frames in one RTT. Note that this is an upper
107 // bound, and that the actual number of frames used for FEC is decided by the
108 // RTP module based on the actual number of packets and the protection factor.
philipel9d3ab612015-12-21 04:12:39 -0800109 float base_layer_framerate =
110 parameters->frameRate /
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000111 static_cast<float>(1 << (parameters->numLayers - 1));
philipel9d3ab612015-12-21 04:12:39 -0800112 int max_frames_fec = std::max(
113 static_cast<int>(2.0f * base_layer_framerate * parameters->rtt / 1000.0f +
114 0.5f),
115 1);
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000116 // |kUpperLimitFramesFec| is the upper limit on how many frames we
117 // allow any FEC to be based on.
118 if (max_frames_fec > kUpperLimitFramesFec) {
119 max_frames_fec = kUpperLimitFramesFec;
120 }
121 return max_frames_fec;
122}
123
124int VCMNackFecMethod::MaxFramesFec() const {
125 return _maxFramesFec;
126}
127
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000128bool VCMNackFecMethod::BitRateTooLowForFec(
129 const VCMProtectionParameters* parameters) {
130 // Bitrate below which we turn off FEC, regardless of reported packet loss.
131 // The condition should depend on resolution and content. For now, use
132 // threshold on bytes per frame, with some effect for the frame size.
133 // The condition for turning off FEC is also based on other factors,
134 // such as |_numLayers|, |_maxFramesFec|, and |_rtt|.
135 int estimate_bytes_per_frame = 1000 * BitsPerFrame(parameters) / 8;
136 int max_bytes_per_frame = kMaxBytesPerFrameForFec;
137 int num_pixels = parameters->codecWidth * parameters->codecHeight;
138 if (num_pixels <= 352 * 288) {
139 max_bytes_per_frame = kMaxBytesPerFrameForFecLow;
140 } else if (num_pixels > 640 * 480) {
141 max_bytes_per_frame = kMaxBytesPerFrameForFecHigh;
142 }
philipel9d3ab612015-12-21 04:12:39 -0800143 // TODO(marpan): add condition based on maximum frames used for FEC,
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000144 // and expand condition based on frame size.
pkasting@chromium.org16825b12015-01-12 21:51:21 +0000145 // Max round trip time threshold in ms.
146 const int64_t kMaxRttTurnOffFec = 200;
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000147 if (estimate_bytes_per_frame < max_bytes_per_frame &&
philipel9d3ab612015-12-21 04:12:39 -0800148 parameters->numLayers < 3 && parameters->rtt < kMaxRttTurnOffFec) {
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000149 return true;
150 }
151 return false;
152}
153
philipel9d3ab612015-12-21 04:12:39 -0800154bool VCMNackFecMethod::EffectivePacketLoss(
155 const VCMProtectionParameters* parameters) {
156 // Set the effective packet loss for encoder (based on FEC code).
157 // Compute the effective packet loss and residual packet loss due to FEC.
158 VCMFecMethod::EffectivePacketLoss(parameters);
159 return true;
mikhal@google.com022716b2011-07-20 23:12:57 +0000160}
niklase@google.com470e71d2011-07-07 08:21:25 +0000161
philipel9d3ab612015-12-21 04:12:39 -0800162bool VCMNackFecMethod::UpdateParameters(
163 const VCMProtectionParameters* parameters) {
164 ProtectionFactor(parameters);
165 EffectivePacketLoss(parameters);
166 _maxFramesFec = ComputeMaxFramesFec(parameters);
167 if (BitRateTooLowForFec(parameters)) {
168 _protectionFactorK = 0;
169 _protectionFactorD = 0;
170 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000171
philipel9d3ab612015-12-21 04:12:39 -0800172 // Protection/fec rates obtained above are defined relative to total number
173 // of packets (total rate: source + fec) FEC in RTP module assumes
174 // protection factor is defined relative to source number of packets so we
175 // should convert the factor to reduce mismatch between mediaOpt's rate and
176 // the actual one
177 _protectionFactorK = VCMFecMethod::ConvertFECRate(_protectionFactorK);
178 _protectionFactorD = VCMFecMethod::ConvertFECRate(_protectionFactorD);
niklase@google.com470e71d2011-07-07 08:21:25 +0000179
philipel9d3ab612015-12-21 04:12:39 -0800180 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000181}
182
philipel9d3ab612015-12-21 04:12:39 -0800183VCMNackMethod::VCMNackMethod() : VCMProtectionMethod() {
184 _type = kNack;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000185}
186
philipel9d3ab612015-12-21 04:12:39 -0800187VCMNackMethod::~VCMNackMethod() {
188 //
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000189}
190
philipel9d3ab612015-12-21 04:12:39 -0800191bool VCMNackMethod::EffectivePacketLoss(
192 const VCMProtectionParameters* parameter) {
193 // Effective Packet Loss, NA in current version.
194 _effectivePacketLoss = 0;
195 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000196}
197
philipel9d3ab612015-12-21 04:12:39 -0800198bool VCMNackMethod::UpdateParameters(
199 const VCMProtectionParameters* parameters) {
200 // Compute the effective packet loss
201 EffectivePacketLoss(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000202
philipel9d3ab612015-12-21 04:12:39 -0800203 // nackCost = (bitRate - nackCost) * (lossPr)
204 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000205}
206
philipel9d3ab612015-12-21 04:12:39 -0800207VCMFecMethod::VCMFecMethod() : VCMProtectionMethod() {
208 _type = kFec;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000209}
philipel9d3ab612015-12-21 04:12:39 -0800210VCMFecMethod::~VCMFecMethod() {
211 //
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000212}
213
philipel9d3ab612015-12-21 04:12:39 -0800214uint8_t VCMFecMethod::BoostCodeRateKey(uint8_t packetFrameDelta,
215 uint8_t packetFrameKey) const {
216 uint8_t boostRateKey = 2;
217 // Default: ratio scales the FEC protection up for I frames
218 uint8_t ratio = 1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000219
philipel9d3ab612015-12-21 04:12:39 -0800220 if (packetFrameDelta > 0) {
221 ratio = (int8_t)(packetFrameKey / packetFrameDelta);
222 }
223 ratio = VCM_MAX(boostRateKey, ratio);
niklase@google.com470e71d2011-07-07 08:21:25 +0000224
philipel9d3ab612015-12-21 04:12:39 -0800225 return ratio;
niklase@google.com470e71d2011-07-07 08:21:25 +0000226}
227
philipel9d3ab612015-12-21 04:12:39 -0800228uint8_t VCMFecMethod::ConvertFECRate(uint8_t codeRateRTP) const {
229 return static_cast<uint8_t>(VCM_MIN(
230 255,
231 (0.5 + 255.0 * codeRateRTP / static_cast<float>(255 - codeRateRTP))));
niklase@google.com470e71d2011-07-07 08:21:25 +0000232}
233
mikhal@google.com679450f2011-08-01 22:14:58 +0000234// Update FEC with protectionFactorD
philipel9d3ab612015-12-21 04:12:39 -0800235void VCMFecMethod::UpdateProtectionFactorD(uint8_t protectionFactorD) {
236 _protectionFactorD = protectionFactorD;
mikhal@google.com679450f2011-08-01 22:14:58 +0000237}
238
mikhal@webrtc.orgd0752c32011-10-19 15:48:30 +0000239// Update FEC with protectionFactorK
philipel9d3ab612015-12-21 04:12:39 -0800240void VCMFecMethod::UpdateProtectionFactorK(uint8_t protectionFactorK) {
241 _protectionFactorK = protectionFactorK;
mikhal@webrtc.orgd0752c32011-10-19 15:48:30 +0000242}
243
philipel9d3ab612015-12-21 04:12:39 -0800244bool VCMFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters) {
245 // FEC PROTECTION SETTINGS: varies with packet loss and bitrate
niklase@google.com470e71d2011-07-07 08:21:25 +0000246
philipel9d3ab612015-12-21 04:12:39 -0800247 // No protection if (filtered) packetLoss is 0
brandtr05f845d2016-11-16 22:59:39 -0800248 uint8_t packetLoss = static_cast<uint8_t>(255 * parameters->lossPr);
philipel9d3ab612015-12-21 04:12:39 -0800249 if (packetLoss == 0) {
250 _protectionFactorK = 0;
251 _protectionFactorD = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000252 return true;
philipel9d3ab612015-12-21 04:12:39 -0800253 }
254
255 // Parameters for FEC setting:
256 // first partition size, thresholds, table pars, spatial resoln fac.
257
258 // First partition protection: ~ 20%
brandtr05f845d2016-11-16 22:59:39 -0800259 uint8_t firstPartitionProt = static_cast<uint8_t>(255 * 0.20);
philipel9d3ab612015-12-21 04:12:39 -0800260
261 // Minimum protection level needed to generate one FEC packet for one
262 // source packet/frame (in RTP sender)
263 uint8_t minProtLevelFec = 85;
264
265 // Threshold on packetLoss and bitRrate/frameRate (=average #packets),
266 // above which we allocate protection to cover at least first partition.
267 uint8_t lossThr = 0;
268 uint8_t packetNumThr = 1;
269
270 // Parameters for range of rate index of table.
271 const uint8_t ratePar1 = 5;
272 const uint8_t ratePar2 = 49;
273
274 // Spatial resolution size, relative to a reference size.
275 float spatialSizeToRef =
276 static_cast<float>(parameters->codecWidth * parameters->codecHeight) /
277 (static_cast<float>(704 * 576));
278 // resolnFac: This parameter will generally increase/decrease the FEC rate
279 // (for fixed bitRate and packetLoss) based on system size.
280 // Use a smaller exponent (< 1) to control/soften system size effect.
281 const float resolnFac = 1.0 / powf(spatialSizeToRef, 0.3f);
282
283 const int bitRatePerFrame = BitsPerFrame(parameters);
284
285 // Average number of packets per frame (source and fec):
brandtr05f845d2016-11-16 22:59:39 -0800286 const uint8_t avgTotPackets = static_cast<uint8_t>(
287 std::min(static_cast<float>(std::numeric_limits<uint8_t>::max()),
Yves Gerey665174f2018-06-19 15:03:05 +0200288 1.5f + static_cast<float>(bitRatePerFrame) * 1000.0f /
289 static_cast<float>(8.0 * _maxPayloadSize)));
philipel9d3ab612015-12-21 04:12:39 -0800290
291 // FEC rate parameters: for P and I frame
292 uint8_t codeRateDelta = 0;
293 uint8_t codeRateKey = 0;
294
295 // Get index for table: the FEC protection depends on an effective rate.
296 // The range on the rate index corresponds to rates (bps)
297 // from ~200k to ~8000k, for 30fps
298 const uint16_t effRateFecTable =
299 static_cast<uint16_t>(resolnFac * bitRatePerFrame);
brandtr05f845d2016-11-16 22:59:39 -0800300 uint8_t rateIndexTable = static_cast<uint8_t>(
301 VCM_MAX(VCM_MIN((effRateFecTable - ratePar1) / ratePar1, ratePar2), 0));
philipel9d3ab612015-12-21 04:12:39 -0800302
303 // Restrict packet loss range to 50:
304 // current tables defined only up to 50%
305 if (packetLoss >= kPacketLossMax) {
306 packetLoss = kPacketLossMax - 1;
307 }
308 uint16_t indexTable = rateIndexTable * kPacketLossMax + packetLoss;
309
310 // Check on table index
brandtr71b1c1f2017-01-12 06:16:24 -0800311 RTC_DCHECK_LT(indexTable, kFecRateTableSize);
philipel9d3ab612015-12-21 04:12:39 -0800312
313 // Protection factor for P frame
brandtr71b1c1f2017-01-12 06:16:24 -0800314 codeRateDelta = kFecRateTable[indexTable];
philipel9d3ab612015-12-21 04:12:39 -0800315
316 if (packetLoss > lossThr && avgTotPackets > packetNumThr) {
317 // Set a minimum based on first partition size.
318 if (codeRateDelta < firstPartitionProt) {
319 codeRateDelta = firstPartitionProt;
320 }
321 }
322
323 // Check limit on amount of protection for P frame; 50% is max.
324 if (codeRateDelta >= kPacketLossMax) {
325 codeRateDelta = kPacketLossMax - 1;
326 }
327
philipel9d3ab612015-12-21 04:12:39 -0800328 // For Key frame:
329 // Effectively at a higher rate, so we scale/boost the rate
330 // The boost factor may depend on several factors: ratio of packet
331 // number of I to P frames, how much protection placed on P frames, etc.
brandtr05f845d2016-11-16 22:59:39 -0800332 const uint8_t packetFrameDelta =
333 static_cast<uint8_t>(0.5 + parameters->packetsPerFrame);
philipel9d3ab612015-12-21 04:12:39 -0800334 const uint8_t packetFrameKey =
brandtr05f845d2016-11-16 22:59:39 -0800335 static_cast<uint8_t>(0.5 + parameters->packetsPerFrameKey);
philipel9d3ab612015-12-21 04:12:39 -0800336 const uint8_t boostKey = BoostCodeRateKey(packetFrameDelta, packetFrameKey);
337
brandtr05f845d2016-11-16 22:59:39 -0800338 rateIndexTable = static_cast<uint8_t>(VCM_MAX(
philipel9d3ab612015-12-21 04:12:39 -0800339 VCM_MIN(1 + (boostKey * effRateFecTable - ratePar1) / ratePar1, ratePar2),
brandtr05f845d2016-11-16 22:59:39 -0800340 0));
philipel9d3ab612015-12-21 04:12:39 -0800341 uint16_t indexTableKey = rateIndexTable * kPacketLossMax + packetLoss;
342
brandtr71b1c1f2017-01-12 06:16:24 -0800343 indexTableKey = VCM_MIN(indexTableKey, kFecRateTableSize);
philipel9d3ab612015-12-21 04:12:39 -0800344
345 // Check on table index
brandtr71b1c1f2017-01-12 06:16:24 -0800346 assert(indexTableKey < kFecRateTableSize);
philipel9d3ab612015-12-21 04:12:39 -0800347
348 // Protection factor for I frame
brandtr71b1c1f2017-01-12 06:16:24 -0800349 codeRateKey = kFecRateTable[indexTableKey];
philipel9d3ab612015-12-21 04:12:39 -0800350
351 // Boosting for Key frame.
352 int boostKeyProt = _scaleProtKey * codeRateDelta;
353 if (boostKeyProt >= kPacketLossMax) {
354 boostKeyProt = kPacketLossMax - 1;
355 }
356
357 // Make sure I frame protection is at least larger than P frame protection,
358 // and at least as high as filtered packet loss.
359 codeRateKey = static_cast<uint8_t>(
360 VCM_MAX(packetLoss, VCM_MAX(boostKeyProt, codeRateKey)));
361
362 // Check limit on amount of protection for I frame: 50% is max.
363 if (codeRateKey >= kPacketLossMax) {
364 codeRateKey = kPacketLossMax - 1;
365 }
366
367 _protectionFactorK = codeRateKey;
368 _protectionFactorD = codeRateDelta;
369
370 // Generally there is a rate mis-match between the FEC cost estimated
371 // in mediaOpt and the actual FEC cost sent out in RTP module.
372 // This is more significant at low rates (small # of source packets), where
373 // the granularity of the FEC decreases. In this case, non-zero protection
374 // in mediaOpt may generate 0 FEC packets in RTP sender (since actual #FEC
375 // is based on rounding off protectionFactor on actual source packet number).
376 // The correction factor (_corrFecCost) attempts to corrects this, at least
377 // for cases of low rates (small #packets) and low protection levels.
378
379 float numPacketsFl = 1.0f + (static_cast<float>(bitRatePerFrame) * 1000.0 /
380 static_cast<float>(8.0 * _maxPayloadSize) +
381 0.5);
382
383 const float estNumFecGen =
384 0.5f + static_cast<float>(_protectionFactorD * numPacketsFl / 255.0f);
385
386 // We reduce cost factor (which will reduce overhead for FEC and
387 // hybrid method) and not the protectionFactor.
388 _corrFecCost = 1.0f;
389 if (estNumFecGen < 1.1f && _protectionFactorD < minProtLevelFec) {
390 _corrFecCost = 0.5f;
391 }
392 if (estNumFecGen < 0.9f && _protectionFactorD < minProtLevelFec) {
393 _corrFecCost = 0.0f;
394 }
395
philipel9d3ab612015-12-21 04:12:39 -0800396 // DONE WITH FEC PROTECTION SETTINGS
397 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000398}
399
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000400int VCMFecMethod::BitsPerFrame(const VCMProtectionParameters* parameters) {
401 // When temporal layers are available FEC will only be applied on the base
402 // layer.
Mirko Bonadei6f440ed2018-06-21 13:41:01 +0000403 const float bitRateRatio =
404 kVp8LayerRateAlloction[parameters->numLayers - 1][0];
thakis@chromium.org65bc2542012-08-13 19:26:12 +0000405 float frameRateRatio = powf(1 / 2.0, parameters->numLayers - 1);
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000406 float bitRate = parameters->bitRate * bitRateRatio;
407 float frameRate = parameters->frameRate * frameRateRatio;
408
409 // TODO(mikhal): Update factor following testing.
410 float adjustmentFactor = 1;
411
Peter Boström5e8351b2016-01-28 23:55:29 +0100412 if (frameRate < 1.0f)
413 frameRate = 1.0f;
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000414 // Average bits per frame (units of kbits)
415 return static_cast<int>(adjustmentFactor * bitRate / frameRate);
416}
417
philipel9d3ab612015-12-21 04:12:39 -0800418bool VCMFecMethod::EffectivePacketLoss(
419 const VCMProtectionParameters* parameters) {
420 // Effective packet loss to encoder is based on RPL (residual packet loss)
421 // this is a soft setting based on degree of FEC protection
422 // RPL = received/input packet loss - average_FEC_recovery
423 // note: received/input packet loss may be filtered based on FilteredLoss
niklase@google.com470e71d2011-07-07 08:21:25 +0000424
philipel9d3ab612015-12-21 04:12:39 -0800425 // Effective Packet Loss, NA in current version.
426 _effectivePacketLoss = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000427
philipel9d3ab612015-12-21 04:12:39 -0800428 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000429}
430
philipel9d3ab612015-12-21 04:12:39 -0800431bool VCMFecMethod::UpdateParameters(const VCMProtectionParameters* parameters) {
432 // Compute the protection factor
433 ProtectionFactor(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000434
philipel9d3ab612015-12-21 04:12:39 -0800435 // Compute the effective packet loss
436 EffectivePacketLoss(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000437
philipel9d3ab612015-12-21 04:12:39 -0800438 // Protection/fec rates obtained above is defined relative to total number
439 // of packets (total rate: source+fec) FEC in RTP module assumes protection
440 // factor is defined relative to source number of packets so we should
441 // convert the factor to reduce mismatch between mediaOpt suggested rate and
442 // the actual rate
443 _protectionFactorK = ConvertFECRate(_protectionFactorK);
444 _protectionFactorD = ConvertFECRate(_protectionFactorD);
niklase@google.com470e71d2011-07-07 08:21:25 +0000445
philipel9d3ab612015-12-21 04:12:39 -0800446 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000447}
philipel9d3ab612015-12-21 04:12:39 -0800448VCMLossProtectionLogic::VCMLossProtectionLogic(int64_t nowMs)
449 : _currentParameters(),
450 _rtt(0),
451 _lossPr(0.0f),
452 _bitRate(0.0f),
453 _frameRate(0.0f),
454 _keyFrameSize(0.0f),
455 _fecRateKey(0),
456 _fecRateDelta(0),
457 _lastPrUpdateT(0),
458 _lossPr255(0.9999f),
459 _lossPrHistory(),
460 _shortMaxLossPr255(0),
461 _packetsPerFrame(0.9999f),
462 _packetsPerFrameKey(0.9999f),
463 _codecWidth(0),
464 _codecHeight(0),
465 _numLayers(1) {
466 Reset(nowMs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000467}
468
philipel9d3ab612015-12-21 04:12:39 -0800469VCMLossProtectionLogic::~VCMLossProtectionLogic() {
470 Release();
niklase@google.com470e71d2011-07-07 08:21:25 +0000471}
472
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000473void VCMLossProtectionLogic::SetMethod(
474 enum VCMProtectionMethodEnum newMethodType) {
pbosba8c15b2015-07-14 09:36:34 -0700475 if (_selectedMethod && _selectedMethod->Type() == newMethodType)
476 return;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000477
philipel9d3ab612015-12-21 04:12:39 -0800478 switch (newMethodType) {
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000479 case kNack:
pbosba8c15b2015-07-14 09:36:34 -0700480 _selectedMethod.reset(new VCMNackMethod());
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000481 break;
482 case kFec:
pbosba8c15b2015-07-14 09:36:34 -0700483 _selectedMethod.reset(new VCMFecMethod());
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000484 break;
485 case kNackFec:
pbosba8c15b2015-07-14 09:36:34 -0700486 _selectedMethod.reset(new VCMNackFecMethod(kLowRttNackMs, -1));
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000487 break;
488 case kNone:
pbosba8c15b2015-07-14 09:36:34 -0700489 _selectedMethod.reset();
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000490 break;
491 }
492 UpdateMethod();
niklase@google.com470e71d2011-07-07 08:21:25 +0000493}
494
philipel9d3ab612015-12-21 04:12:39 -0800495void VCMLossProtectionLogic::UpdateRtt(int64_t rtt) {
496 _rtt = rtt;
niklase@google.com470e71d2011-07-07 08:21:25 +0000497}
498
philipel9d3ab612015-12-21 04:12:39 -0800499void VCMLossProtectionLogic::UpdateMaxLossHistory(uint8_t lossPr255,
500 int64_t now) {
501 if (_lossPrHistory[0].timeMs >= 0 &&
502 now - _lossPrHistory[0].timeMs < kLossPrShortFilterWinMs) {
503 if (lossPr255 > _shortMaxLossPr255) {
504 _shortMaxLossPr255 = lossPr255;
niklase@google.com470e71d2011-07-07 08:21:25 +0000505 }
philipel9d3ab612015-12-21 04:12:39 -0800506 } else {
507 // Only add a new value to the history once a second
508 if (_lossPrHistory[0].timeMs == -1) {
509 // First, no shift
510 _shortMaxLossPr255 = lossPr255;
511 } else {
512 // Shift
513 for (int32_t i = (kLossPrHistorySize - 2); i >= 0; i--) {
514 _lossPrHistory[i + 1].lossPr255 = _lossPrHistory[i].lossPr255;
515 _lossPrHistory[i + 1].timeMs = _lossPrHistory[i].timeMs;
516 }
517 }
518 if (_shortMaxLossPr255 == 0) {
519 _shortMaxLossPr255 = lossPr255;
520 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000521
philipel9d3ab612015-12-21 04:12:39 -0800522 _lossPrHistory[0].lossPr255 = _shortMaxLossPr255;
523 _lossPrHistory[0].timeMs = now;
524 _shortMaxLossPr255 = 0;
525 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000526}
527
philipel9d3ab612015-12-21 04:12:39 -0800528uint8_t VCMLossProtectionLogic::MaxFilteredLossPr(int64_t nowMs) const {
529 uint8_t maxFound = _shortMaxLossPr255;
530 if (_lossPrHistory[0].timeMs == -1) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000531 return maxFound;
philipel9d3ab612015-12-21 04:12:39 -0800532 }
533 for (int32_t i = 0; i < kLossPrHistorySize; i++) {
534 if (_lossPrHistory[i].timeMs == -1) {
535 break;
536 }
537 if (nowMs - _lossPrHistory[i].timeMs >
538 kLossPrHistorySize * kLossPrShortFilterWinMs) {
539 // This sample (and all samples after this) is too old
540 break;
541 }
542 if (_lossPrHistory[i].lossPr255 > maxFound) {
543 // This sample is the largest one this far into the history
544 maxFound = _lossPrHistory[i].lossPr255;
545 }
546 }
547 return maxFound;
niklase@google.com470e71d2011-07-07 08:21:25 +0000548}
549
philipel9d3ab612015-12-21 04:12:39 -0800550uint8_t VCMLossProtectionLogic::FilteredLoss(int64_t nowMs,
551 FilterPacketLossMode filter_mode,
552 uint8_t lossPr255) {
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000553 // Update the max window filter.
554 UpdateMaxLossHistory(lossPr255, nowMs);
555
556 // Update the recursive average filter.
philipel9d3ab612015-12-21 04:12:39 -0800557 _lossPr255.Apply(static_cast<float>(nowMs - _lastPrUpdateT),
558 static_cast<float>(lossPr255));
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000559 _lastPrUpdateT = nowMs;
560
561 // Filtered loss: default is received loss (no filtering).
pbos@webrtc.org7b859cc2013-04-02 15:54:38 +0000562 uint8_t filtered_loss = lossPr255;
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000563
564 switch (filter_mode) {
565 case kNoFilter:
566 break;
567 case kAvgFilter:
minyue@webrtc.org74aaf292014-07-16 21:28:26 +0000568 filtered_loss = static_cast<uint8_t>(_lossPr255.filtered() + 0.5);
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000569 break;
570 case kMaxFilter:
571 filtered_loss = MaxFilteredLossPr(nowMs);
572 break;
573 }
574
575 return filtered_loss;
niklase@google.com470e71d2011-07-07 08:21:25 +0000576}
577
philipel9d3ab612015-12-21 04:12:39 -0800578void VCMLossProtectionLogic::UpdateFilteredLossPr(uint8_t packetLossEnc) {
579 _lossPr = static_cast<float>(packetLossEnc) / 255.0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000580}
581
philipel9d3ab612015-12-21 04:12:39 -0800582void VCMLossProtectionLogic::UpdateBitRate(float bitRate) {
583 _bitRate = bitRate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000584}
585
philipel9d3ab612015-12-21 04:12:39 -0800586void VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets,
587 int64_t nowMs) {
588 _packetsPerFrame.Apply(static_cast<float>(nowMs - _lastPacketPerFrameUpdateT),
589 nPackets);
590 _lastPacketPerFrameUpdateT = nowMs;
niklase@google.com470e71d2011-07-07 08:21:25 +0000591}
592
philipel9d3ab612015-12-21 04:12:39 -0800593void VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets,
594 int64_t nowMs) {
595 _packetsPerFrameKey.Apply(
596 static_cast<float>(nowMs - _lastPacketPerFrameUpdateTKey), nPackets);
597 _lastPacketPerFrameUpdateTKey = nowMs;
niklase@google.com470e71d2011-07-07 08:21:25 +0000598}
599
philipel9d3ab612015-12-21 04:12:39 -0800600void VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize) {
601 _keyFrameSize = keyFrameSize;
niklase@google.com470e71d2011-07-07 08:21:25 +0000602}
603
perkjc2c24f72016-07-11 01:47:32 -0700604void VCMLossProtectionLogic::UpdateFrameSize(size_t width, size_t height) {
philipel9d3ab612015-12-21 04:12:39 -0800605 _codecWidth = width;
606 _codecHeight = height;
niklase@google.com470e71d2011-07-07 08:21:25 +0000607}
608
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000609void VCMLossProtectionLogic::UpdateNumLayers(int numLayers) {
610 _numLayers = (numLayers == 0) ? 1 : numLayers;
611}
612
philipel9d3ab612015-12-21 04:12:39 -0800613bool VCMLossProtectionLogic::UpdateMethod() {
614 if (!_selectedMethod)
615 return false;
616 _currentParameters.rtt = _rtt;
617 _currentParameters.lossPr = _lossPr;
618 _currentParameters.bitRate = _bitRate;
619 _currentParameters.frameRate = _frameRate; // rename actual frame rate?
620 _currentParameters.keyFrameSize = _keyFrameSize;
621 _currentParameters.fecRateDelta = _fecRateDelta;
622 _currentParameters.fecRateKey = _fecRateKey;
623 _currentParameters.packetsPerFrame = _packetsPerFrame.filtered();
624 _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.filtered();
625 _currentParameters.codecWidth = _codecWidth;
626 _currentParameters.codecHeight = _codecHeight;
627 _currentParameters.numLayers = _numLayers;
628 return _selectedMethod->UpdateParameters(&_currentParameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000629}
630
philipel9d3ab612015-12-21 04:12:39 -0800631VCMProtectionMethod* VCMLossProtectionLogic::SelectedMethod() const {
632 return _selectedMethod.get();
niklase@google.com470e71d2011-07-07 08:21:25 +0000633}
634
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000635VCMProtectionMethodEnum VCMLossProtectionLogic::SelectedType() const {
pbosba8c15b2015-07-14 09:36:34 -0700636 return _selectedMethod ? _selectedMethod->Type() : kNone;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000637}
638
philipel9d3ab612015-12-21 04:12:39 -0800639void VCMLossProtectionLogic::Reset(int64_t nowMs) {
640 _lastPrUpdateT = nowMs;
641 _lastPacketPerFrameUpdateT = nowMs;
642 _lastPacketPerFrameUpdateTKey = nowMs;
643 _lossPr255.Reset(0.9999f);
644 _packetsPerFrame.Reset(0.9999f);
645 _fecRateDelta = _fecRateKey = 0;
646 for (int32_t i = 0; i < kLossPrHistorySize; i++) {
647 _lossPrHistory[i].lossPr255 = 0;
648 _lossPrHistory[i].timeMs = -1;
649 }
650 _shortMaxLossPr255 = 0;
651 Release();
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000652}
653
pbosba8c15b2015-07-14 09:36:34 -0700654void VCMLossProtectionLogic::Release() {
655 _selectedMethod.reset();
niklase@google.com470e71d2011-07-07 08:21:25 +0000656}
657
stefan@webrtc.orga64300a2013-03-04 15:24:40 +0000658} // namespace media_optimization
659} // namespace webrtc