blob: 7b870f22703bfe2ffdee9edde2e0f7996304e722 [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
Yves Gerey3e707812018-11-28 16:47:49 +010013#include <assert.h>
pbos@webrtc.orga4407322013-07-16 12:32:05 +000014#include <math.h>
Jonas Olssona4d87372019-07-05 19:08:33 +020015
philipel9d3ab612015-12-21 04:12:39 -080016#include <algorithm>
17
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "modules/video_coding/fec_rate_table.h"
Yves Gerey3e707812018-11-28 16:47:49 +010019#include "modules/video_coding/internal_defines.h"
Sergio Garcia Murillo43800f92018-06-21 16:16:38 +020020#include "modules/video_coding/utility/simulcast_rate_allocator.h"
Yves Gerey3e707812018-11-28 16:47:49 +010021#include "rtc_base/checks.h"
Emircan Uysaler704a7bd2018-08-06 16:14:10 -070022#include "rtc_base/numerics/safe_conversions.h"
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +000023
niklase@google.com470e71d2011-07-07 08:21:25 +000024namespace webrtc {
Peter Boström9cb1f302015-04-01 11:39:49 +020025// Max value of loss rates in off-line model
26static const int kPacketLossMax = 129;
27
stefan@webrtc.orga64300a2013-03-04 15:24:40 +000028namespace media_optimization {
niklase@google.com470e71d2011-07-07 08:21:25 +000029
Stefan Holmerdbdb3a02018-07-17 16:03:46 +020030VCMProtectionParameters::VCMProtectionParameters()
31 : rtt(0),
32 lossPr(0.0f),
33 bitRate(0.0f),
34 packetsPerFrame(0.0f),
35 packetsPerFrameKey(0.0f),
36 frameRate(0.0f),
37 keyFrameSize(0.0f),
38 fecRateDelta(0),
39 fecRateKey(0),
40 codecWidth(0),
41 codecHeight(0),
42 numLayers(1) {}
43
Peter Boström9cb1f302015-04-01 11:39:49 +020044VCMProtectionMethod::VCMProtectionMethod()
45 : _effectivePacketLoss(0),
46 _protectionFactorK(0),
47 _protectionFactorD(0),
48 _scaleProtKey(2.0f),
49 _maxPayloadSize(1460),
Peter Boström9cb1f302015-04-01 11:39:49 +020050 _corrFecCost(1.0),
philipel9d3ab612015-12-21 04:12:39 -080051 _type(kNone) {}
mikhal@webrtc.orga057a952011-08-26 21:17:34 +000052
pbosc0430522016-05-01 17:19:05 -070053VCMProtectionMethod::~VCMProtectionMethod() {}
marpan@google.com86548c62011-07-12 17:12:57 +000054
Stefan Holmerdbdb3a02018-07-17 16:03:46 +020055enum VCMProtectionMethodEnum VCMProtectionMethod::Type() const {
56 return _type;
57}
58
59uint8_t VCMProtectionMethod::RequiredPacketLossER() {
60 return _effectivePacketLoss;
61}
62
63uint8_t VCMProtectionMethod::RequiredProtectionFactorK() {
64 return _protectionFactorK;
65}
66
67uint8_t VCMProtectionMethod::RequiredProtectionFactorD() {
68 return _protectionFactorD;
69}
70
71bool VCMProtectionMethod::RequiredUepProtectionK() {
72 return _useUepProtectionK;
73}
74
75bool VCMProtectionMethod::RequiredUepProtectionD() {
76 return _useUepProtectionD;
77}
78
79int VCMProtectionMethod::MaxFramesFec() const {
80 return 1;
81}
82
pkasting@chromium.org16825b12015-01-12 21:51:21 +000083VCMNackFecMethod::VCMNackFecMethod(int64_t lowRttNackThresholdMs,
84 int64_t highRttNackThresholdMs)
stefan@webrtc.org932ab182011-11-29 11:33:31 +000085 : VCMFecMethod(),
86 _lowRttNackMs(lowRttNackThresholdMs),
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +000087 _highRttNackMs(highRttNackThresholdMs),
88 _maxFramesFec(1) {
stefan@webrtc.org932ab182011-11-29 11:33:31 +000089 assert(lowRttNackThresholdMs >= -1 && highRttNackThresholdMs >= -1);
90 assert(highRttNackThresholdMs == -1 ||
91 lowRttNackThresholdMs <= highRttNackThresholdMs);
92 assert(lowRttNackThresholdMs > -1 || highRttNackThresholdMs == -1);
93 _type = kNackFec;
mikhal@google.com77408882011-07-22 22:05:25 +000094}
95
philipel9d3ab612015-12-21 04:12:39 -080096VCMNackFecMethod::~VCMNackFecMethod() {
97 //
mikhal@google.com77408882011-07-22 22:05:25 +000098}
philipel9d3ab612015-12-21 04:12:39 -080099bool VCMNackFecMethod::ProtectionFactor(
100 const VCMProtectionParameters* parameters) {
101 // Hybrid Nack FEC has three operational modes:
102 // 1. Low RTT (below kLowRttNackMs) - Nack only: Set FEC rate
103 // (_protectionFactorD) to zero. -1 means no FEC.
104 // 2. High RTT (above _highRttNackMs) - FEC Only: Keep FEC factors.
105 // -1 means always allow NACK.
106 // 3. Medium RTT values - Hybrid mode: We will only nack the
107 // residual following the decoding of the FEC (refer to JB logic). FEC
108 // delta protection factor will be adjusted based on the RTT.
mikhal@google.com022716b2011-07-20 23:12:57 +0000109
philipel9d3ab612015-12-21 04:12:39 -0800110 // Otherwise: we count on FEC; if the RTT is below a threshold, then we
111 // nack the residual, based on a decision made in the JB.
mikhal@google.com022716b2011-07-20 23:12:57 +0000112
philipel9d3ab612015-12-21 04:12:39 -0800113 // Compute the protection factors
114 VCMFecMethod::ProtectionFactor(parameters);
115 if (_lowRttNackMs == -1 || parameters->rtt < _lowRttNackMs) {
116 _protectionFactorD = 0;
117 VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
mikhal@google.com320813c2011-08-03 20:47:50 +0000118
mikhal@google.com679450f2011-08-01 22:14:58 +0000119 // When in Hybrid mode (RTT range), adjust FEC rates based on the
120 // RTT (NACK effectiveness) - adjustment factor is in the range [0,1].
philipel9d3ab612015-12-21 04:12:39 -0800121 } else if (_highRttNackMs == -1 || parameters->rtt < _highRttNackMs) {
122 // TODO(mikhal): Disabling adjustment temporarily.
123 // uint16_t rttIndex = (uint16_t) parameters->rtt;
124 float adjustRtt = 1.0f; // (float)VCMNackFecTable[rttIndex] / 100.0f;
niklase@google.com470e71d2011-07-07 08:21:25 +0000125
philipel9d3ab612015-12-21 04:12:39 -0800126 // Adjust FEC with NACK on (for delta frame only)
127 // table depends on RTT relative to rttMax (NACK Threshold)
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700128 _protectionFactorD = rtc::saturated_cast<uint8_t>(
129 adjustRtt * rtc::saturated_cast<float>(_protectionFactorD));
philipel9d3ab612015-12-21 04:12:39 -0800130 // update FEC rates after applying adjustment
131 VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
132 }
mikhal@google.com679450f2011-08-01 22:14:58 +0000133
philipel9d3ab612015-12-21 04:12:39 -0800134 return true;
mikhal@google.com022716b2011-07-20 23:12:57 +0000135}
niklase@google.com470e71d2011-07-07 08:21:25 +0000136
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000137int VCMNackFecMethod::ComputeMaxFramesFec(
138 const VCMProtectionParameters* parameters) {
139 if (parameters->numLayers > 2) {
140 // For more than 2 temporal layers we will only have FEC on the base layer,
141 // and the base layers will be pretty far apart. Therefore we force one
142 // frame FEC.
143 return 1;
144 }
145 // We set the max number of frames to base the FEC on so that on average
146 // we will have complete frames in one RTT. Note that this is an upper
147 // bound, and that the actual number of frames used for FEC is decided by the
148 // RTP module based on the actual number of packets and the protection factor.
philipel9d3ab612015-12-21 04:12:39 -0800149 float base_layer_framerate =
150 parameters->frameRate /
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700151 rtc::saturated_cast<float>(1 << (parameters->numLayers - 1));
philipel9d3ab612015-12-21 04:12:39 -0800152 int max_frames_fec = std::max(
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700153 rtc::saturated_cast<int>(
154 2.0f * base_layer_framerate * parameters->rtt / 1000.0f + 0.5f),
philipel9d3ab612015-12-21 04:12:39 -0800155 1);
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000156 // |kUpperLimitFramesFec| is the upper limit on how many frames we
157 // allow any FEC to be based on.
158 if (max_frames_fec > kUpperLimitFramesFec) {
159 max_frames_fec = kUpperLimitFramesFec;
160 }
161 return max_frames_fec;
162}
163
164int VCMNackFecMethod::MaxFramesFec() const {
165 return _maxFramesFec;
166}
167
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000168bool VCMNackFecMethod::BitRateTooLowForFec(
169 const VCMProtectionParameters* parameters) {
170 // Bitrate below which we turn off FEC, regardless of reported packet loss.
171 // The condition should depend on resolution and content. For now, use
172 // threshold on bytes per frame, with some effect for the frame size.
173 // The condition for turning off FEC is also based on other factors,
174 // such as |_numLayers|, |_maxFramesFec|, and |_rtt|.
175 int estimate_bytes_per_frame = 1000 * BitsPerFrame(parameters) / 8;
176 int max_bytes_per_frame = kMaxBytesPerFrameForFec;
177 int num_pixels = parameters->codecWidth * parameters->codecHeight;
178 if (num_pixels <= 352 * 288) {
179 max_bytes_per_frame = kMaxBytesPerFrameForFecLow;
180 } else if (num_pixels > 640 * 480) {
181 max_bytes_per_frame = kMaxBytesPerFrameForFecHigh;
182 }
philipel9d3ab612015-12-21 04:12:39 -0800183 // TODO(marpan): add condition based on maximum frames used for FEC,
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000184 // and expand condition based on frame size.
pkasting@chromium.org16825b12015-01-12 21:51:21 +0000185 // Max round trip time threshold in ms.
186 const int64_t kMaxRttTurnOffFec = 200;
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000187 if (estimate_bytes_per_frame < max_bytes_per_frame &&
philipel9d3ab612015-12-21 04:12:39 -0800188 parameters->numLayers < 3 && parameters->rtt < kMaxRttTurnOffFec) {
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000189 return true;
190 }
191 return false;
192}
193
philipel9d3ab612015-12-21 04:12:39 -0800194bool VCMNackFecMethod::EffectivePacketLoss(
195 const VCMProtectionParameters* parameters) {
196 // Set the effective packet loss for encoder (based on FEC code).
197 // Compute the effective packet loss and residual packet loss due to FEC.
198 VCMFecMethod::EffectivePacketLoss(parameters);
199 return true;
mikhal@google.com022716b2011-07-20 23:12:57 +0000200}
niklase@google.com470e71d2011-07-07 08:21:25 +0000201
philipel9d3ab612015-12-21 04:12:39 -0800202bool VCMNackFecMethod::UpdateParameters(
203 const VCMProtectionParameters* parameters) {
204 ProtectionFactor(parameters);
205 EffectivePacketLoss(parameters);
206 _maxFramesFec = ComputeMaxFramesFec(parameters);
207 if (BitRateTooLowForFec(parameters)) {
208 _protectionFactorK = 0;
209 _protectionFactorD = 0;
210 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000211
philipel9d3ab612015-12-21 04:12:39 -0800212 // Protection/fec rates obtained above are defined relative to total number
213 // of packets (total rate: source + fec) FEC in RTP module assumes
214 // protection factor is defined relative to source number of packets so we
215 // should convert the factor to reduce mismatch between mediaOpt's rate and
216 // the actual one
217 _protectionFactorK = VCMFecMethod::ConvertFECRate(_protectionFactorK);
218 _protectionFactorD = VCMFecMethod::ConvertFECRate(_protectionFactorD);
niklase@google.com470e71d2011-07-07 08:21:25 +0000219
philipel9d3ab612015-12-21 04:12:39 -0800220 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000221}
222
philipel9d3ab612015-12-21 04:12:39 -0800223VCMNackMethod::VCMNackMethod() : VCMProtectionMethod() {
224 _type = kNack;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000225}
226
philipel9d3ab612015-12-21 04:12:39 -0800227VCMNackMethod::~VCMNackMethod() {
228 //
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000229}
230
philipel9d3ab612015-12-21 04:12:39 -0800231bool VCMNackMethod::EffectivePacketLoss(
232 const VCMProtectionParameters* parameter) {
233 // Effective Packet Loss, NA in current version.
234 _effectivePacketLoss = 0;
235 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000236}
237
philipel9d3ab612015-12-21 04:12:39 -0800238bool VCMNackMethod::UpdateParameters(
239 const VCMProtectionParameters* parameters) {
240 // Compute the effective packet loss
241 EffectivePacketLoss(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000242
philipel9d3ab612015-12-21 04:12:39 -0800243 // nackCost = (bitRate - nackCost) * (lossPr)
244 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000245}
246
philipel9d3ab612015-12-21 04:12:39 -0800247VCMFecMethod::VCMFecMethod() : VCMProtectionMethod() {
248 _type = kFec;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000249}
philipel9d3ab612015-12-21 04:12:39 -0800250VCMFecMethod::~VCMFecMethod() {
251 //
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000252}
253
philipel9d3ab612015-12-21 04:12:39 -0800254uint8_t VCMFecMethod::BoostCodeRateKey(uint8_t packetFrameDelta,
255 uint8_t packetFrameKey) const {
256 uint8_t boostRateKey = 2;
257 // Default: ratio scales the FEC protection up for I frames
258 uint8_t ratio = 1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000259
philipel9d3ab612015-12-21 04:12:39 -0800260 if (packetFrameDelta > 0) {
261 ratio = (int8_t)(packetFrameKey / packetFrameDelta);
262 }
263 ratio = VCM_MAX(boostRateKey, ratio);
niklase@google.com470e71d2011-07-07 08:21:25 +0000264
philipel9d3ab612015-12-21 04:12:39 -0800265 return ratio;
niklase@google.com470e71d2011-07-07 08:21:25 +0000266}
267
philipel9d3ab612015-12-21 04:12:39 -0800268uint8_t VCMFecMethod::ConvertFECRate(uint8_t codeRateRTP) const {
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700269 return rtc::saturated_cast<uint8_t>(
270 VCM_MIN(255, (0.5 + 255.0 * codeRateRTP /
271 rtc::saturated_cast<float>(255 - codeRateRTP))));
niklase@google.com470e71d2011-07-07 08:21:25 +0000272}
273
mikhal@google.com679450f2011-08-01 22:14:58 +0000274// Update FEC with protectionFactorD
philipel9d3ab612015-12-21 04:12:39 -0800275void VCMFecMethod::UpdateProtectionFactorD(uint8_t protectionFactorD) {
276 _protectionFactorD = protectionFactorD;
mikhal@google.com679450f2011-08-01 22:14:58 +0000277}
278
mikhal@webrtc.orgd0752c32011-10-19 15:48:30 +0000279// Update FEC with protectionFactorK
philipel9d3ab612015-12-21 04:12:39 -0800280void VCMFecMethod::UpdateProtectionFactorK(uint8_t protectionFactorK) {
281 _protectionFactorK = protectionFactorK;
mikhal@webrtc.orgd0752c32011-10-19 15:48:30 +0000282}
283
philipel9d3ab612015-12-21 04:12:39 -0800284bool VCMFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters) {
285 // FEC PROTECTION SETTINGS: varies with packet loss and bitrate
niklase@google.com470e71d2011-07-07 08:21:25 +0000286
philipel9d3ab612015-12-21 04:12:39 -0800287 // No protection if (filtered) packetLoss is 0
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700288 uint8_t packetLoss = rtc::saturated_cast<uint8_t>(255 * parameters->lossPr);
philipel9d3ab612015-12-21 04:12:39 -0800289 if (packetLoss == 0) {
290 _protectionFactorK = 0;
291 _protectionFactorD = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000292 return true;
philipel9d3ab612015-12-21 04:12:39 -0800293 }
294
295 // Parameters for FEC setting:
296 // first partition size, thresholds, table pars, spatial resoln fac.
297
298 // First partition protection: ~ 20%
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700299 uint8_t firstPartitionProt = rtc::saturated_cast<uint8_t>(255 * 0.20);
philipel9d3ab612015-12-21 04:12:39 -0800300
301 // Minimum protection level needed to generate one FEC packet for one
302 // source packet/frame (in RTP sender)
303 uint8_t minProtLevelFec = 85;
304
305 // Threshold on packetLoss and bitRrate/frameRate (=average #packets),
306 // above which we allocate protection to cover at least first partition.
307 uint8_t lossThr = 0;
308 uint8_t packetNumThr = 1;
309
310 // Parameters for range of rate index of table.
311 const uint8_t ratePar1 = 5;
312 const uint8_t ratePar2 = 49;
313
314 // Spatial resolution size, relative to a reference size.
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700315 float spatialSizeToRef = rtc::saturated_cast<float>(parameters->codecWidth *
316 parameters->codecHeight) /
317 (rtc::saturated_cast<float>(704 * 576));
philipel9d3ab612015-12-21 04:12:39 -0800318 // resolnFac: This parameter will generally increase/decrease the FEC rate
319 // (for fixed bitRate and packetLoss) based on system size.
320 // Use a smaller exponent (< 1) to control/soften system size effect.
321 const float resolnFac = 1.0 / powf(spatialSizeToRef, 0.3f);
322
323 const int bitRatePerFrame = BitsPerFrame(parameters);
324
325 // Average number of packets per frame (source and fec):
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700326 const uint8_t avgTotPackets = rtc::saturated_cast<uint8_t>(
327 1.5f + rtc::saturated_cast<float>(bitRatePerFrame) * 1000.0f /
328 rtc::saturated_cast<float>(8.0 * _maxPayloadSize));
philipel9d3ab612015-12-21 04:12:39 -0800329
330 // FEC rate parameters: for P and I frame
331 uint8_t codeRateDelta = 0;
332 uint8_t codeRateKey = 0;
333
334 // Get index for table: the FEC protection depends on an effective rate.
335 // The range on the rate index corresponds to rates (bps)
336 // from ~200k to ~8000k, for 30fps
337 const uint16_t effRateFecTable =
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700338 rtc::saturated_cast<uint16_t>(resolnFac * bitRatePerFrame);
339 uint8_t rateIndexTable = rtc::saturated_cast<uint8_t>(
brandtr05f845d2016-11-16 22:59:39 -0800340 VCM_MAX(VCM_MIN((effRateFecTable - ratePar1) / ratePar1, ratePar2), 0));
philipel9d3ab612015-12-21 04:12:39 -0800341
342 // Restrict packet loss range to 50:
343 // current tables defined only up to 50%
344 if (packetLoss >= kPacketLossMax) {
345 packetLoss = kPacketLossMax - 1;
346 }
347 uint16_t indexTable = rateIndexTable * kPacketLossMax + packetLoss;
348
349 // Check on table index
brandtr71b1c1f2017-01-12 06:16:24 -0800350 RTC_DCHECK_LT(indexTable, kFecRateTableSize);
philipel9d3ab612015-12-21 04:12:39 -0800351
352 // Protection factor for P frame
brandtr71b1c1f2017-01-12 06:16:24 -0800353 codeRateDelta = kFecRateTable[indexTable];
philipel9d3ab612015-12-21 04:12:39 -0800354
355 if (packetLoss > lossThr && avgTotPackets > packetNumThr) {
356 // Set a minimum based on first partition size.
357 if (codeRateDelta < firstPartitionProt) {
358 codeRateDelta = firstPartitionProt;
359 }
360 }
361
362 // Check limit on amount of protection for P frame; 50% is max.
363 if (codeRateDelta >= kPacketLossMax) {
364 codeRateDelta = kPacketLossMax - 1;
365 }
366
philipel9d3ab612015-12-21 04:12:39 -0800367 // For Key frame:
368 // Effectively at a higher rate, so we scale/boost the rate
369 // The boost factor may depend on several factors: ratio of packet
370 // number of I to P frames, how much protection placed on P frames, etc.
brandtr05f845d2016-11-16 22:59:39 -0800371 const uint8_t packetFrameDelta =
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700372 rtc::saturated_cast<uint8_t>(0.5 + parameters->packetsPerFrame);
philipel9d3ab612015-12-21 04:12:39 -0800373 const uint8_t packetFrameKey =
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700374 rtc::saturated_cast<uint8_t>(0.5 + parameters->packetsPerFrameKey);
philipel9d3ab612015-12-21 04:12:39 -0800375 const uint8_t boostKey = BoostCodeRateKey(packetFrameDelta, packetFrameKey);
376
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700377 rateIndexTable = rtc::saturated_cast<uint8_t>(VCM_MAX(
philipel9d3ab612015-12-21 04:12:39 -0800378 VCM_MIN(1 + (boostKey * effRateFecTable - ratePar1) / ratePar1, ratePar2),
brandtr05f845d2016-11-16 22:59:39 -0800379 0));
philipel9d3ab612015-12-21 04:12:39 -0800380 uint16_t indexTableKey = rateIndexTable * kPacketLossMax + packetLoss;
381
brandtr71b1c1f2017-01-12 06:16:24 -0800382 indexTableKey = VCM_MIN(indexTableKey, kFecRateTableSize);
philipel9d3ab612015-12-21 04:12:39 -0800383
384 // Check on table index
brandtr71b1c1f2017-01-12 06:16:24 -0800385 assert(indexTableKey < kFecRateTableSize);
philipel9d3ab612015-12-21 04:12:39 -0800386
387 // Protection factor for I frame
brandtr71b1c1f2017-01-12 06:16:24 -0800388 codeRateKey = kFecRateTable[indexTableKey];
philipel9d3ab612015-12-21 04:12:39 -0800389
390 // Boosting for Key frame.
391 int boostKeyProt = _scaleProtKey * codeRateDelta;
392 if (boostKeyProt >= kPacketLossMax) {
393 boostKeyProt = kPacketLossMax - 1;
394 }
395
396 // Make sure I frame protection is at least larger than P frame protection,
397 // and at least as high as filtered packet loss.
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700398 codeRateKey = rtc::saturated_cast<uint8_t>(
philipel9d3ab612015-12-21 04:12:39 -0800399 VCM_MAX(packetLoss, VCM_MAX(boostKeyProt, codeRateKey)));
400
401 // Check limit on amount of protection for I frame: 50% is max.
402 if (codeRateKey >= kPacketLossMax) {
403 codeRateKey = kPacketLossMax - 1;
404 }
405
406 _protectionFactorK = codeRateKey;
407 _protectionFactorD = codeRateDelta;
408
409 // Generally there is a rate mis-match between the FEC cost estimated
410 // in mediaOpt and the actual FEC cost sent out in RTP module.
411 // This is more significant at low rates (small # of source packets), where
412 // the granularity of the FEC decreases. In this case, non-zero protection
413 // in mediaOpt may generate 0 FEC packets in RTP sender (since actual #FEC
414 // is based on rounding off protectionFactor on actual source packet number).
415 // The correction factor (_corrFecCost) attempts to corrects this, at least
416 // for cases of low rates (small #packets) and low protection levels.
417
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700418 float numPacketsFl =
419 1.0f + (rtc::saturated_cast<float>(bitRatePerFrame) * 1000.0 /
420 rtc::saturated_cast<float>(8.0 * _maxPayloadSize) +
421 0.5);
philipel9d3ab612015-12-21 04:12:39 -0800422
423 const float estNumFecGen =
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700424 0.5f +
425 rtc::saturated_cast<float>(_protectionFactorD * numPacketsFl / 255.0f);
philipel9d3ab612015-12-21 04:12:39 -0800426
427 // We reduce cost factor (which will reduce overhead for FEC and
428 // hybrid method) and not the protectionFactor.
429 _corrFecCost = 1.0f;
430 if (estNumFecGen < 1.1f && _protectionFactorD < minProtLevelFec) {
431 _corrFecCost = 0.5f;
432 }
433 if (estNumFecGen < 0.9f && _protectionFactorD < minProtLevelFec) {
434 _corrFecCost = 0.0f;
435 }
436
philipel9d3ab612015-12-21 04:12:39 -0800437 // DONE WITH FEC PROTECTION SETTINGS
438 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000439}
440
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000441int VCMFecMethod::BitsPerFrame(const VCMProtectionParameters* parameters) {
442 // When temporal layers are available FEC will only be applied on the base
443 // layer.
Erik Språngd92288f2018-07-04 10:07:40 +0200444 const float bitRateRatio =
445 webrtc::SimulcastRateAllocator::GetTemporalRateAllocation(
446 parameters->numLayers, 0);
thakis@chromium.org65bc2542012-08-13 19:26:12 +0000447 float frameRateRatio = powf(1 / 2.0, parameters->numLayers - 1);
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000448 float bitRate = parameters->bitRate * bitRateRatio;
449 float frameRate = parameters->frameRate * frameRateRatio;
450
451 // TODO(mikhal): Update factor following testing.
452 float adjustmentFactor = 1;
453
Peter Boström5e8351b2016-01-28 23:55:29 +0100454 if (frameRate < 1.0f)
455 frameRate = 1.0f;
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000456 // Average bits per frame (units of kbits)
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700457 return rtc::saturated_cast<int>(adjustmentFactor * bitRate / frameRate);
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000458}
459
philipel9d3ab612015-12-21 04:12:39 -0800460bool VCMFecMethod::EffectivePacketLoss(
461 const VCMProtectionParameters* parameters) {
462 // Effective packet loss to encoder is based on RPL (residual packet loss)
463 // this is a soft setting based on degree of FEC protection
464 // RPL = received/input packet loss - average_FEC_recovery
465 // note: received/input packet loss may be filtered based on FilteredLoss
niklase@google.com470e71d2011-07-07 08:21:25 +0000466
philipel9d3ab612015-12-21 04:12:39 -0800467 // Effective Packet Loss, NA in current version.
468 _effectivePacketLoss = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000469
philipel9d3ab612015-12-21 04:12:39 -0800470 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000471}
472
philipel9d3ab612015-12-21 04:12:39 -0800473bool VCMFecMethod::UpdateParameters(const VCMProtectionParameters* parameters) {
474 // Compute the protection factor
475 ProtectionFactor(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000476
philipel9d3ab612015-12-21 04:12:39 -0800477 // Compute the effective packet loss
478 EffectivePacketLoss(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000479
philipel9d3ab612015-12-21 04:12:39 -0800480 // Protection/fec rates obtained above is defined relative to total number
481 // of packets (total rate: source+fec) FEC in RTP module assumes protection
482 // factor is defined relative to source number of packets so we should
483 // convert the factor to reduce mismatch between mediaOpt suggested rate and
484 // the actual rate
485 _protectionFactorK = ConvertFECRate(_protectionFactorK);
486 _protectionFactorD = ConvertFECRate(_protectionFactorD);
niklase@google.com470e71d2011-07-07 08:21:25 +0000487
philipel9d3ab612015-12-21 04:12:39 -0800488 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000489}
philipel9d3ab612015-12-21 04:12:39 -0800490VCMLossProtectionLogic::VCMLossProtectionLogic(int64_t nowMs)
491 : _currentParameters(),
492 _rtt(0),
493 _lossPr(0.0f),
494 _bitRate(0.0f),
495 _frameRate(0.0f),
496 _keyFrameSize(0.0f),
497 _fecRateKey(0),
498 _fecRateDelta(0),
499 _lastPrUpdateT(0),
500 _lossPr255(0.9999f),
501 _lossPrHistory(),
502 _shortMaxLossPr255(0),
503 _packetsPerFrame(0.9999f),
504 _packetsPerFrameKey(0.9999f),
Ying Wang1f262cc2018-08-23 13:54:08 +0200505 _codecWidth(704),
506 _codecHeight(576),
philipel9d3ab612015-12-21 04:12:39 -0800507 _numLayers(1) {
508 Reset(nowMs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000509}
510
philipel9d3ab612015-12-21 04:12:39 -0800511VCMLossProtectionLogic::~VCMLossProtectionLogic() {
512 Release();
niklase@google.com470e71d2011-07-07 08:21:25 +0000513}
514
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000515void VCMLossProtectionLogic::SetMethod(
516 enum VCMProtectionMethodEnum newMethodType) {
pbosba8c15b2015-07-14 09:36:34 -0700517 if (_selectedMethod && _selectedMethod->Type() == newMethodType)
518 return;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000519
philipel9d3ab612015-12-21 04:12:39 -0800520 switch (newMethodType) {
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000521 case kNack:
pbosba8c15b2015-07-14 09:36:34 -0700522 _selectedMethod.reset(new VCMNackMethod());
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000523 break;
524 case kFec:
pbosba8c15b2015-07-14 09:36:34 -0700525 _selectedMethod.reset(new VCMFecMethod());
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000526 break;
527 case kNackFec:
pbosba8c15b2015-07-14 09:36:34 -0700528 _selectedMethod.reset(new VCMNackFecMethod(kLowRttNackMs, -1));
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000529 break;
530 case kNone:
pbosba8c15b2015-07-14 09:36:34 -0700531 _selectedMethod.reset();
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000532 break;
533 }
534 UpdateMethod();
niklase@google.com470e71d2011-07-07 08:21:25 +0000535}
536
philipel9d3ab612015-12-21 04:12:39 -0800537void VCMLossProtectionLogic::UpdateRtt(int64_t rtt) {
538 _rtt = rtt;
niklase@google.com470e71d2011-07-07 08:21:25 +0000539}
540
philipel9d3ab612015-12-21 04:12:39 -0800541void VCMLossProtectionLogic::UpdateMaxLossHistory(uint8_t lossPr255,
542 int64_t now) {
543 if (_lossPrHistory[0].timeMs >= 0 &&
544 now - _lossPrHistory[0].timeMs < kLossPrShortFilterWinMs) {
545 if (lossPr255 > _shortMaxLossPr255) {
546 _shortMaxLossPr255 = lossPr255;
niklase@google.com470e71d2011-07-07 08:21:25 +0000547 }
philipel9d3ab612015-12-21 04:12:39 -0800548 } else {
549 // Only add a new value to the history once a second
550 if (_lossPrHistory[0].timeMs == -1) {
551 // First, no shift
552 _shortMaxLossPr255 = lossPr255;
553 } else {
554 // Shift
555 for (int32_t i = (kLossPrHistorySize - 2); i >= 0; i--) {
556 _lossPrHistory[i + 1].lossPr255 = _lossPrHistory[i].lossPr255;
557 _lossPrHistory[i + 1].timeMs = _lossPrHistory[i].timeMs;
558 }
559 }
560 if (_shortMaxLossPr255 == 0) {
561 _shortMaxLossPr255 = lossPr255;
562 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000563
philipel9d3ab612015-12-21 04:12:39 -0800564 _lossPrHistory[0].lossPr255 = _shortMaxLossPr255;
565 _lossPrHistory[0].timeMs = now;
566 _shortMaxLossPr255 = 0;
567 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000568}
569
philipel9d3ab612015-12-21 04:12:39 -0800570uint8_t VCMLossProtectionLogic::MaxFilteredLossPr(int64_t nowMs) const {
571 uint8_t maxFound = _shortMaxLossPr255;
572 if (_lossPrHistory[0].timeMs == -1) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000573 return maxFound;
philipel9d3ab612015-12-21 04:12:39 -0800574 }
575 for (int32_t i = 0; i < kLossPrHistorySize; i++) {
576 if (_lossPrHistory[i].timeMs == -1) {
577 break;
578 }
579 if (nowMs - _lossPrHistory[i].timeMs >
580 kLossPrHistorySize * kLossPrShortFilterWinMs) {
581 // This sample (and all samples after this) is too old
582 break;
583 }
584 if (_lossPrHistory[i].lossPr255 > maxFound) {
585 // This sample is the largest one this far into the history
586 maxFound = _lossPrHistory[i].lossPr255;
587 }
588 }
589 return maxFound;
niklase@google.com470e71d2011-07-07 08:21:25 +0000590}
591
philipel9d3ab612015-12-21 04:12:39 -0800592uint8_t VCMLossProtectionLogic::FilteredLoss(int64_t nowMs,
593 FilterPacketLossMode filter_mode,
594 uint8_t lossPr255) {
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000595 // Update the max window filter.
596 UpdateMaxLossHistory(lossPr255, nowMs);
597
598 // Update the recursive average filter.
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700599 _lossPr255.Apply(rtc::saturated_cast<float>(nowMs - _lastPrUpdateT),
600 rtc::saturated_cast<float>(lossPr255));
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000601 _lastPrUpdateT = nowMs;
602
603 // Filtered loss: default is received loss (no filtering).
pbos@webrtc.org7b859cc2013-04-02 15:54:38 +0000604 uint8_t filtered_loss = lossPr255;
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000605
606 switch (filter_mode) {
607 case kNoFilter:
608 break;
609 case kAvgFilter:
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700610 filtered_loss = rtc::saturated_cast<uint8_t>(_lossPr255.filtered() + 0.5);
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000611 break;
612 case kMaxFilter:
613 filtered_loss = MaxFilteredLossPr(nowMs);
614 break;
615 }
616
617 return filtered_loss;
niklase@google.com470e71d2011-07-07 08:21:25 +0000618}
619
philipel9d3ab612015-12-21 04:12:39 -0800620void VCMLossProtectionLogic::UpdateFilteredLossPr(uint8_t packetLossEnc) {
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700621 _lossPr = rtc::saturated_cast<float>(packetLossEnc) / 255.0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000622}
623
philipel9d3ab612015-12-21 04:12:39 -0800624void VCMLossProtectionLogic::UpdateBitRate(float bitRate) {
625 _bitRate = bitRate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000626}
627
philipel9d3ab612015-12-21 04:12:39 -0800628void VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets,
629 int64_t nowMs) {
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700630 _packetsPerFrame.Apply(
631 rtc::saturated_cast<float>(nowMs - _lastPacketPerFrameUpdateT), nPackets);
philipel9d3ab612015-12-21 04:12:39 -0800632 _lastPacketPerFrameUpdateT = nowMs;
niklase@google.com470e71d2011-07-07 08:21:25 +0000633}
634
philipel9d3ab612015-12-21 04:12:39 -0800635void VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets,
636 int64_t nowMs) {
637 _packetsPerFrameKey.Apply(
Emircan Uysaler704a7bd2018-08-06 16:14:10 -0700638 rtc::saturated_cast<float>(nowMs - _lastPacketPerFrameUpdateTKey),
639 nPackets);
philipel9d3ab612015-12-21 04:12:39 -0800640 _lastPacketPerFrameUpdateTKey = nowMs;
niklase@google.com470e71d2011-07-07 08:21:25 +0000641}
642
philipel9d3ab612015-12-21 04:12:39 -0800643void VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize) {
644 _keyFrameSize = keyFrameSize;
niklase@google.com470e71d2011-07-07 08:21:25 +0000645}
646
perkjc2c24f72016-07-11 01:47:32 -0700647void VCMLossProtectionLogic::UpdateFrameSize(size_t width, size_t height) {
philipel9d3ab612015-12-21 04:12:39 -0800648 _codecWidth = width;
649 _codecHeight = height;
niklase@google.com470e71d2011-07-07 08:21:25 +0000650}
651
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000652void VCMLossProtectionLogic::UpdateNumLayers(int numLayers) {
653 _numLayers = (numLayers == 0) ? 1 : numLayers;
654}
655
philipel9d3ab612015-12-21 04:12:39 -0800656bool VCMLossProtectionLogic::UpdateMethod() {
657 if (!_selectedMethod)
658 return false;
659 _currentParameters.rtt = _rtt;
660 _currentParameters.lossPr = _lossPr;
661 _currentParameters.bitRate = _bitRate;
662 _currentParameters.frameRate = _frameRate; // rename actual frame rate?
663 _currentParameters.keyFrameSize = _keyFrameSize;
664 _currentParameters.fecRateDelta = _fecRateDelta;
665 _currentParameters.fecRateKey = _fecRateKey;
666 _currentParameters.packetsPerFrame = _packetsPerFrame.filtered();
667 _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.filtered();
668 _currentParameters.codecWidth = _codecWidth;
669 _currentParameters.codecHeight = _codecHeight;
670 _currentParameters.numLayers = _numLayers;
671 return _selectedMethod->UpdateParameters(&_currentParameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000672}
673
philipel9d3ab612015-12-21 04:12:39 -0800674VCMProtectionMethod* VCMLossProtectionLogic::SelectedMethod() const {
675 return _selectedMethod.get();
niklase@google.com470e71d2011-07-07 08:21:25 +0000676}
677
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000678VCMProtectionMethodEnum VCMLossProtectionLogic::SelectedType() const {
pbosba8c15b2015-07-14 09:36:34 -0700679 return _selectedMethod ? _selectedMethod->Type() : kNone;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000680}
681
philipel9d3ab612015-12-21 04:12:39 -0800682void VCMLossProtectionLogic::Reset(int64_t nowMs) {
683 _lastPrUpdateT = nowMs;
684 _lastPacketPerFrameUpdateT = nowMs;
685 _lastPacketPerFrameUpdateTKey = nowMs;
686 _lossPr255.Reset(0.9999f);
687 _packetsPerFrame.Reset(0.9999f);
688 _fecRateDelta = _fecRateKey = 0;
689 for (int32_t i = 0; i < kLossPrHistorySize; i++) {
690 _lossPrHistory[i].lossPr255 = 0;
691 _lossPrHistory[i].timeMs = -1;
692 }
693 _shortMaxLossPr255 = 0;
694 Release();
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000695}
696
pbosba8c15b2015-07-14 09:36:34 -0700697void VCMLossProtectionLogic::Release() {
698 _selectedMethod.reset();
niklase@google.com470e71d2011-07-07 08:21:25 +0000699}
700
stefan@webrtc.orga64300a2013-03-04 15:24:40 +0000701} // namespace media_optimization
702} // namespace webrtc