blob: cdcc6e7652520d66b4f63aa473af9259e2236e64 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:251/*
stefan@webrtc.orgc35f5ce2012-04-11 07:42:252 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:253 *
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 04:47:3111#include "modules/video_coding/media_opt_util.h"
niklase@google.com470e71d2011-07-07 08:21:2512
pbos@webrtc.orga4407322013-07-16 12:32:0513#include <math.h>
Jonas Olssona4d87372019-07-05 17:08:3314
philipel9d3ab612015-12-21 12:12:3915#include <algorithm>
Danil Chapovalov67ba9142024-05-10 13:10:2416#include <memory>
philipel9d3ab612015-12-21 12:12:3917
Danil Chapovalov67ba9142024-05-10 13:10:2418#include "api/field_trials_view.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3119#include "modules/video_coding/fec_rate_table.h"
Yves Gerey3e707812018-11-28 15:47:4920#include "modules/video_coding/internal_defines.h"
Sergio Garcia Murillo43800f92018-06-21 14:16:3821#include "modules/video_coding/utility/simulcast_rate_allocator.h"
Yves Gerey3e707812018-11-28 15:47:4922#include "rtc_base/checks.h"
Rasmus Brandt2b9317a2019-10-30 12:01:4623#include "rtc_base/experiments/rate_control_settings.h"
Emircan Uysaler704a7bd2018-08-06 23:14:1024#include "rtc_base/numerics/safe_conversions.h"
Danil Chapovalov67ba9142024-05-10 13:10:2425#include "system_wrappers/include/clock.h"
mikhal@webrtc.org0e7d9d82011-12-19 19:04:4926
niklase@google.com470e71d2011-07-07 08:21:2527namespace webrtc {
Peter Boström9cb1f302015-04-01 09:39:4928// Max value of loss rates in off-line model
29static const int kPacketLossMax = 129;
30
stefan@webrtc.orga64300a2013-03-04 15:24:4031namespace media_optimization {
niklase@google.com470e71d2011-07-07 08:21:2532
Stefan Holmerdbdb3a02018-07-17 14:03:4633VCMProtectionParameters::VCMProtectionParameters()
34 : rtt(0),
35 lossPr(0.0f),
36 bitRate(0.0f),
37 packetsPerFrame(0.0f),
38 packetsPerFrameKey(0.0f),
39 frameRate(0.0f),
40 keyFrameSize(0.0f),
41 fecRateDelta(0),
42 fecRateKey(0),
43 codecWidth(0),
44 codecHeight(0),
45 numLayers(1) {}
46
Peter Boström9cb1f302015-04-01 09:39:4947VCMProtectionMethod::VCMProtectionMethod()
48 : _effectivePacketLoss(0),
49 _protectionFactorK(0),
50 _protectionFactorD(0),
51 _scaleProtKey(2.0f),
52 _maxPayloadSize(1460),
Peter Boström9cb1f302015-04-01 09:39:4953 _corrFecCost(1.0),
philipel9d3ab612015-12-21 12:12:3954 _type(kNone) {}
mikhal@webrtc.orga057a952011-08-26 21:17:3455
pbosc0430522016-05-02 00:19:0556VCMProtectionMethod::~VCMProtectionMethod() {}
marpan@google.com86548c62011-07-12 17:12:5757
Stefan Holmerdbdb3a02018-07-17 14:03:4658enum VCMProtectionMethodEnum VCMProtectionMethod::Type() const {
59 return _type;
60}
61
62uint8_t VCMProtectionMethod::RequiredPacketLossER() {
63 return _effectivePacketLoss;
64}
65
66uint8_t VCMProtectionMethod::RequiredProtectionFactorK() {
67 return _protectionFactorK;
68}
69
70uint8_t VCMProtectionMethod::RequiredProtectionFactorD() {
71 return _protectionFactorD;
72}
73
74bool VCMProtectionMethod::RequiredUepProtectionK() {
75 return _useUepProtectionK;
76}
77
78bool VCMProtectionMethod::RequiredUepProtectionD() {
79 return _useUepProtectionD;
80}
81
82int VCMProtectionMethod::MaxFramesFec() const {
83 return 1;
84}
85
Danil Chapovalov67ba9142024-05-10 13:10:2486VCMNackFecMethod::VCMNackFecMethod(const FieldTrialsView& field_trials,
87 int64_t lowRttNackThresholdMs,
pkasting@chromium.org16825b12015-01-12 21:51:2188 int64_t highRttNackThresholdMs)
Danil Chapovalov67ba9142024-05-10 13:10:2489 : VCMFecMethod(field_trials),
stefan@webrtc.org932ab182011-11-29 11:33:3190 _lowRttNackMs(lowRttNackThresholdMs),
stefan@webrtc.orgc35f5ce2012-04-11 07:42:2591 _highRttNackMs(highRttNackThresholdMs),
92 _maxFramesFec(1) {
Mirko Bonadei25ab3222021-07-08 18:08:2093 RTC_DCHECK(lowRttNackThresholdMs >= -1 && highRttNackThresholdMs >= -1);
94 RTC_DCHECK(highRttNackThresholdMs == -1 ||
95 lowRttNackThresholdMs <= highRttNackThresholdMs);
96 RTC_DCHECK(lowRttNackThresholdMs > -1 || highRttNackThresholdMs == -1);
stefan@webrtc.org932ab182011-11-29 11:33:3197 _type = kNackFec;
mikhal@google.com77408882011-07-22 22:05:2598}
99
philipel9d3ab612015-12-21 12:12:39100VCMNackFecMethod::~VCMNackFecMethod() {
101 //
mikhal@google.com77408882011-07-22 22:05:25102}
philipel9d3ab612015-12-21 12:12:39103bool VCMNackFecMethod::ProtectionFactor(
104 const VCMProtectionParameters* parameters) {
105 // Hybrid Nack FEC has three operational modes:
106 // 1. Low RTT (below kLowRttNackMs) - Nack only: Set FEC rate
107 // (_protectionFactorD) to zero. -1 means no FEC.
108 // 2. High RTT (above _highRttNackMs) - FEC Only: Keep FEC factors.
109 // -1 means always allow NACK.
110 // 3. Medium RTT values - Hybrid mode: We will only nack the
111 // residual following the decoding of the FEC (refer to JB logic). FEC
112 // delta protection factor will be adjusted based on the RTT.
mikhal@google.com022716b2011-07-20 23:12:57113
philipel9d3ab612015-12-21 12:12:39114 // Otherwise: we count on FEC; if the RTT is below a threshold, then we
115 // nack the residual, based on a decision made in the JB.
mikhal@google.com022716b2011-07-20 23:12:57116
philipel9d3ab612015-12-21 12:12:39117 // Compute the protection factors
118 VCMFecMethod::ProtectionFactor(parameters);
119 if (_lowRttNackMs == -1 || parameters->rtt < _lowRttNackMs) {
120 _protectionFactorD = 0;
121 VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
mikhal@google.com320813c2011-08-03 20:47:50122
mikhal@google.com679450f2011-08-01 22:14:58123 // When in Hybrid mode (RTT range), adjust FEC rates based on the
124 // RTT (NACK effectiveness) - adjustment factor is in the range [0,1].
philipel9d3ab612015-12-21 12:12:39125 } else if (_highRttNackMs == -1 || parameters->rtt < _highRttNackMs) {
126 // TODO(mikhal): Disabling adjustment temporarily.
127 // uint16_t rttIndex = (uint16_t) parameters->rtt;
128 float adjustRtt = 1.0f; // (float)VCMNackFecTable[rttIndex] / 100.0f;
niklase@google.com470e71d2011-07-07 08:21:25129
philipel9d3ab612015-12-21 12:12:39130 // Adjust FEC with NACK on (for delta frame only)
131 // table depends on RTT relative to rttMax (NACK Threshold)
Emircan Uysaler704a7bd2018-08-06 23:14:10132 _protectionFactorD = rtc::saturated_cast<uint8_t>(
133 adjustRtt * rtc::saturated_cast<float>(_protectionFactorD));
philipel9d3ab612015-12-21 12:12:39134 // update FEC rates after applying adjustment
135 VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
136 }
mikhal@google.com679450f2011-08-01 22:14:58137
philipel9d3ab612015-12-21 12:12:39138 return true;
mikhal@google.com022716b2011-07-20 23:12:57139}
niklase@google.com470e71d2011-07-07 08:21:25140
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25141int VCMNackFecMethod::ComputeMaxFramesFec(
142 const VCMProtectionParameters* parameters) {
143 if (parameters->numLayers > 2) {
144 // For more than 2 temporal layers we will only have FEC on the base layer,
145 // and the base layers will be pretty far apart. Therefore we force one
146 // frame FEC.
147 return 1;
148 }
149 // We set the max number of frames to base the FEC on so that on average
150 // we will have complete frames in one RTT. Note that this is an upper
151 // bound, and that the actual number of frames used for FEC is decided by the
152 // RTP module based on the actual number of packets and the protection factor.
philipel9d3ab612015-12-21 12:12:39153 float base_layer_framerate =
154 parameters->frameRate /
Emircan Uysaler704a7bd2018-08-06 23:14:10155 rtc::saturated_cast<float>(1 << (parameters->numLayers - 1));
philipel9d3ab612015-12-21 12:12:39156 int max_frames_fec = std::max(
Emircan Uysaler704a7bd2018-08-06 23:14:10157 rtc::saturated_cast<int>(
158 2.0f * base_layer_framerate * parameters->rtt / 1000.0f + 0.5f),
philipel9d3ab612015-12-21 12:12:39159 1);
Artem Titovdcd7fc72021-08-09 11:02:57160 // `kUpperLimitFramesFec` is the upper limit on how many frames we
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25161 // allow any FEC to be based on.
162 if (max_frames_fec > kUpperLimitFramesFec) {
163 max_frames_fec = kUpperLimitFramesFec;
164 }
165 return max_frames_fec;
166}
167
168int VCMNackFecMethod::MaxFramesFec() const {
169 return _maxFramesFec;
170}
171
marpan@webrtc.org88ad06b2012-04-20 16:05:24172bool VCMNackFecMethod::BitRateTooLowForFec(
173 const VCMProtectionParameters* parameters) {
174 // Bitrate below which we turn off FEC, regardless of reported packet loss.
175 // The condition should depend on resolution and content. For now, use
176 // threshold on bytes per frame, with some effect for the frame size.
177 // The condition for turning off FEC is also based on other factors,
Artem Titovdcd7fc72021-08-09 11:02:57178 // such as `_numLayers`, `_maxFramesFec`, and `_rtt`.
marpan@webrtc.org88ad06b2012-04-20 16:05:24179 int estimate_bytes_per_frame = 1000 * BitsPerFrame(parameters) / 8;
180 int max_bytes_per_frame = kMaxBytesPerFrameForFec;
181 int num_pixels = parameters->codecWidth * parameters->codecHeight;
182 if (num_pixels <= 352 * 288) {
183 max_bytes_per_frame = kMaxBytesPerFrameForFecLow;
184 } else if (num_pixels > 640 * 480) {
185 max_bytes_per_frame = kMaxBytesPerFrameForFecHigh;
186 }
philipel9d3ab612015-12-21 12:12:39187 // TODO(marpan): add condition based on maximum frames used for FEC,
marpan@webrtc.org88ad06b2012-04-20 16:05:24188 // and expand condition based on frame size.
pkasting@chromium.org16825b12015-01-12 21:51:21189 // Max round trip time threshold in ms.
190 const int64_t kMaxRttTurnOffFec = 200;
marpan@webrtc.org88ad06b2012-04-20 16:05:24191 if (estimate_bytes_per_frame < max_bytes_per_frame &&
philipel9d3ab612015-12-21 12:12:39192 parameters->numLayers < 3 && parameters->rtt < kMaxRttTurnOffFec) {
marpan@webrtc.org88ad06b2012-04-20 16:05:24193 return true;
194 }
195 return false;
196}
197
philipel9d3ab612015-12-21 12:12:39198bool VCMNackFecMethod::EffectivePacketLoss(
199 const VCMProtectionParameters* parameters) {
200 // Set the effective packet loss for encoder (based on FEC code).
201 // Compute the effective packet loss and residual packet loss due to FEC.
202 VCMFecMethod::EffectivePacketLoss(parameters);
203 return true;
mikhal@google.com022716b2011-07-20 23:12:57204}
niklase@google.com470e71d2011-07-07 08:21:25205
philipel9d3ab612015-12-21 12:12:39206bool VCMNackFecMethod::UpdateParameters(
207 const VCMProtectionParameters* parameters) {
208 ProtectionFactor(parameters);
209 EffectivePacketLoss(parameters);
210 _maxFramesFec = ComputeMaxFramesFec(parameters);
211 if (BitRateTooLowForFec(parameters)) {
212 _protectionFactorK = 0;
213 _protectionFactorD = 0;
214 }
niklase@google.com470e71d2011-07-07 08:21:25215
philipel9d3ab612015-12-21 12:12:39216 // Protection/fec rates obtained above are defined relative to total number
217 // of packets (total rate: source + fec) FEC in RTP module assumes
218 // protection factor is defined relative to source number of packets so we
219 // should convert the factor to reduce mismatch between mediaOpt's rate and
220 // the actual one
221 _protectionFactorK = VCMFecMethod::ConvertFECRate(_protectionFactorK);
222 _protectionFactorD = VCMFecMethod::ConvertFECRate(_protectionFactorD);
niklase@google.com470e71d2011-07-07 08:21:25223
philipel9d3ab612015-12-21 12:12:39224 return true;
niklase@google.com470e71d2011-07-07 08:21:25225}
226
philipel9d3ab612015-12-21 12:12:39227VCMNackMethod::VCMNackMethod() : VCMProtectionMethod() {
228 _type = kNack;
mikhal@webrtc.orga057a952011-08-26 21:17:34229}
230
philipel9d3ab612015-12-21 12:12:39231VCMNackMethod::~VCMNackMethod() {
232 //
mikhal@webrtc.orga057a952011-08-26 21:17:34233}
234
philipel9d3ab612015-12-21 12:12:39235bool VCMNackMethod::EffectivePacketLoss(
236 const VCMProtectionParameters* parameter) {
237 // Effective Packet Loss, NA in current version.
238 _effectivePacketLoss = 0;
239 return true;
niklase@google.com470e71d2011-07-07 08:21:25240}
241
philipel9d3ab612015-12-21 12:12:39242bool VCMNackMethod::UpdateParameters(
243 const VCMProtectionParameters* parameters) {
244 // Compute the effective packet loss
245 EffectivePacketLoss(parameters);
niklase@google.com470e71d2011-07-07 08:21:25246
philipel9d3ab612015-12-21 12:12:39247 // nackCost = (bitRate - nackCost) * (lossPr)
248 return true;
niklase@google.com470e71d2011-07-07 08:21:25249}
250
Danil Chapovalov67ba9142024-05-10 13:10:24251VCMFecMethod::VCMFecMethod(const FieldTrialsView& field_trials)
252 : rate_control_settings_(field_trials) {
philipel9d3ab612015-12-21 12:12:39253 _type = kFec;
mikhal@webrtc.orga057a952011-08-26 21:17:34254}
Rasmus Brandt2b9317a2019-10-30 12:01:46255
256VCMFecMethod::~VCMFecMethod() = default;
mikhal@webrtc.orga057a952011-08-26 21:17:34257
philipel9d3ab612015-12-21 12:12:39258uint8_t VCMFecMethod::BoostCodeRateKey(uint8_t packetFrameDelta,
259 uint8_t packetFrameKey) const {
260 uint8_t boostRateKey = 2;
261 // Default: ratio scales the FEC protection up for I frames
262 uint8_t ratio = 1;
niklase@google.com470e71d2011-07-07 08:21:25263
philipel9d3ab612015-12-21 12:12:39264 if (packetFrameDelta > 0) {
265 ratio = (int8_t)(packetFrameKey / packetFrameDelta);
266 }
267 ratio = VCM_MAX(boostRateKey, ratio);
niklase@google.com470e71d2011-07-07 08:21:25268
philipel9d3ab612015-12-21 12:12:39269 return ratio;
niklase@google.com470e71d2011-07-07 08:21:25270}
271
philipel9d3ab612015-12-21 12:12:39272uint8_t VCMFecMethod::ConvertFECRate(uint8_t codeRateRTP) const {
Emircan Uysaler704a7bd2018-08-06 23:14:10273 return rtc::saturated_cast<uint8_t>(
274 VCM_MIN(255, (0.5 + 255.0 * codeRateRTP /
275 rtc::saturated_cast<float>(255 - codeRateRTP))));
niklase@google.com470e71d2011-07-07 08:21:25276}
277
mikhal@google.com679450f2011-08-01 22:14:58278// Update FEC with protectionFactorD
philipel9d3ab612015-12-21 12:12:39279void VCMFecMethod::UpdateProtectionFactorD(uint8_t protectionFactorD) {
280 _protectionFactorD = protectionFactorD;
mikhal@google.com679450f2011-08-01 22:14:58281}
282
mikhal@webrtc.orgd0752c32011-10-19 15:48:30283// Update FEC with protectionFactorK
philipel9d3ab612015-12-21 12:12:39284void VCMFecMethod::UpdateProtectionFactorK(uint8_t protectionFactorK) {
285 _protectionFactorK = protectionFactorK;
mikhal@webrtc.orgd0752c32011-10-19 15:48:30286}
287
philipel9d3ab612015-12-21 12:12:39288bool VCMFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters) {
289 // FEC PROTECTION SETTINGS: varies with packet loss and bitrate
niklase@google.com470e71d2011-07-07 08:21:25290
philipel9d3ab612015-12-21 12:12:39291 // No protection if (filtered) packetLoss is 0
Emircan Uysaler704a7bd2018-08-06 23:14:10292 uint8_t packetLoss = rtc::saturated_cast<uint8_t>(255 * parameters->lossPr);
philipel9d3ab612015-12-21 12:12:39293 if (packetLoss == 0) {
294 _protectionFactorK = 0;
295 _protectionFactorD = 0;
niklase@google.com470e71d2011-07-07 08:21:25296 return true;
philipel9d3ab612015-12-21 12:12:39297 }
298
299 // Parameters for FEC setting:
300 // first partition size, thresholds, table pars, spatial resoln fac.
301
302 // First partition protection: ~ 20%
Emircan Uysaler704a7bd2018-08-06 23:14:10303 uint8_t firstPartitionProt = rtc::saturated_cast<uint8_t>(255 * 0.20);
philipel9d3ab612015-12-21 12:12:39304
305 // Minimum protection level needed to generate one FEC packet for one
306 // source packet/frame (in RTP sender)
307 uint8_t minProtLevelFec = 85;
308
309 // Threshold on packetLoss and bitRrate/frameRate (=average #packets),
310 // above which we allocate protection to cover at least first partition.
311 uint8_t lossThr = 0;
312 uint8_t packetNumThr = 1;
313
314 // Parameters for range of rate index of table.
315 const uint8_t ratePar1 = 5;
316 const uint8_t ratePar2 = 49;
317
318 // Spatial resolution size, relative to a reference size.
Emircan Uysaler704a7bd2018-08-06 23:14:10319 float spatialSizeToRef = rtc::saturated_cast<float>(parameters->codecWidth *
320 parameters->codecHeight) /
321 (rtc::saturated_cast<float>(704 * 576));
philipel9d3ab612015-12-21 12:12:39322 // resolnFac: This parameter will generally increase/decrease the FEC rate
323 // (for fixed bitRate and packetLoss) based on system size.
324 // Use a smaller exponent (< 1) to control/soften system size effect.
325 const float resolnFac = 1.0 / powf(spatialSizeToRef, 0.3f);
326
327 const int bitRatePerFrame = BitsPerFrame(parameters);
328
329 // Average number of packets per frame (source and fec):
Emircan Uysaler704a7bd2018-08-06 23:14:10330 const uint8_t avgTotPackets = rtc::saturated_cast<uint8_t>(
331 1.5f + rtc::saturated_cast<float>(bitRatePerFrame) * 1000.0f /
332 rtc::saturated_cast<float>(8.0 * _maxPayloadSize));
philipel9d3ab612015-12-21 12:12:39333
334 // FEC rate parameters: for P and I frame
335 uint8_t codeRateDelta = 0;
336 uint8_t codeRateKey = 0;
337
338 // Get index for table: the FEC protection depends on an effective rate.
339 // The range on the rate index corresponds to rates (bps)
340 // from ~200k to ~8000k, for 30fps
341 const uint16_t effRateFecTable =
Emircan Uysaler704a7bd2018-08-06 23:14:10342 rtc::saturated_cast<uint16_t>(resolnFac * bitRatePerFrame);
343 uint8_t rateIndexTable = rtc::saturated_cast<uint8_t>(
brandtr05f845d2016-11-17 06:59:39344 VCM_MAX(VCM_MIN((effRateFecTable - ratePar1) / ratePar1, ratePar2), 0));
philipel9d3ab612015-12-21 12:12:39345
346 // Restrict packet loss range to 50:
347 // current tables defined only up to 50%
348 if (packetLoss >= kPacketLossMax) {
349 packetLoss = kPacketLossMax - 1;
350 }
351 uint16_t indexTable = rateIndexTable * kPacketLossMax + packetLoss;
352
353 // Check on table index
brandtr71b1c1f2017-01-12 14:16:24354 RTC_DCHECK_LT(indexTable, kFecRateTableSize);
philipel9d3ab612015-12-21 12:12:39355
356 // Protection factor for P frame
brandtr71b1c1f2017-01-12 14:16:24357 codeRateDelta = kFecRateTable[indexTable];
philipel9d3ab612015-12-21 12:12:39358
359 if (packetLoss > lossThr && avgTotPackets > packetNumThr) {
360 // Set a minimum based on first partition size.
361 if (codeRateDelta < firstPartitionProt) {
362 codeRateDelta = firstPartitionProt;
363 }
364 }
365
366 // Check limit on amount of protection for P frame; 50% is max.
367 if (codeRateDelta >= kPacketLossMax) {
368 codeRateDelta = kPacketLossMax - 1;
369 }
370
philipel9d3ab612015-12-21 12:12:39371 // For Key frame:
372 // Effectively at a higher rate, so we scale/boost the rate
373 // The boost factor may depend on several factors: ratio of packet
374 // number of I to P frames, how much protection placed on P frames, etc.
brandtr05f845d2016-11-17 06:59:39375 const uint8_t packetFrameDelta =
Emircan Uysaler704a7bd2018-08-06 23:14:10376 rtc::saturated_cast<uint8_t>(0.5 + parameters->packetsPerFrame);
philipel9d3ab612015-12-21 12:12:39377 const uint8_t packetFrameKey =
Emircan Uysaler704a7bd2018-08-06 23:14:10378 rtc::saturated_cast<uint8_t>(0.5 + parameters->packetsPerFrameKey);
philipel9d3ab612015-12-21 12:12:39379 const uint8_t boostKey = BoostCodeRateKey(packetFrameDelta, packetFrameKey);
380
Emircan Uysaler704a7bd2018-08-06 23:14:10381 rateIndexTable = rtc::saturated_cast<uint8_t>(VCM_MAX(
philipel9d3ab612015-12-21 12:12:39382 VCM_MIN(1 + (boostKey * effRateFecTable - ratePar1) / ratePar1, ratePar2),
brandtr05f845d2016-11-17 06:59:39383 0));
philipel9d3ab612015-12-21 12:12:39384 uint16_t indexTableKey = rateIndexTable * kPacketLossMax + packetLoss;
385
brandtr71b1c1f2017-01-12 14:16:24386 indexTableKey = VCM_MIN(indexTableKey, kFecRateTableSize);
philipel9d3ab612015-12-21 12:12:39387
388 // Check on table index
Mirko Bonadei25ab3222021-07-08 18:08:20389 RTC_DCHECK_LT(indexTableKey, kFecRateTableSize);
philipel9d3ab612015-12-21 12:12:39390
391 // Protection factor for I frame
brandtr71b1c1f2017-01-12 14:16:24392 codeRateKey = kFecRateTable[indexTableKey];
philipel9d3ab612015-12-21 12:12:39393
394 // Boosting for Key frame.
395 int boostKeyProt = _scaleProtKey * codeRateDelta;
396 if (boostKeyProt >= kPacketLossMax) {
397 boostKeyProt = kPacketLossMax - 1;
398 }
399
400 // Make sure I frame protection is at least larger than P frame protection,
401 // and at least as high as filtered packet loss.
Emircan Uysaler704a7bd2018-08-06 23:14:10402 codeRateKey = rtc::saturated_cast<uint8_t>(
philipel9d3ab612015-12-21 12:12:39403 VCM_MAX(packetLoss, VCM_MAX(boostKeyProt, codeRateKey)));
404
405 // Check limit on amount of protection for I frame: 50% is max.
406 if (codeRateKey >= kPacketLossMax) {
407 codeRateKey = kPacketLossMax - 1;
408 }
409
410 _protectionFactorK = codeRateKey;
411 _protectionFactorD = codeRateDelta;
412
413 // Generally there is a rate mis-match between the FEC cost estimated
414 // in mediaOpt and the actual FEC cost sent out in RTP module.
415 // This is more significant at low rates (small # of source packets), where
416 // the granularity of the FEC decreases. In this case, non-zero protection
417 // in mediaOpt may generate 0 FEC packets in RTP sender (since actual #FEC
418 // is based on rounding off protectionFactor on actual source packet number).
419 // The correction factor (_corrFecCost) attempts to corrects this, at least
420 // for cases of low rates (small #packets) and low protection levels.
421
Emircan Uysaler704a7bd2018-08-06 23:14:10422 float numPacketsFl =
423 1.0f + (rtc::saturated_cast<float>(bitRatePerFrame) * 1000.0 /
424 rtc::saturated_cast<float>(8.0 * _maxPayloadSize) +
425 0.5);
philipel9d3ab612015-12-21 12:12:39426
427 const float estNumFecGen =
Emircan Uysaler704a7bd2018-08-06 23:14:10428 0.5f +
429 rtc::saturated_cast<float>(_protectionFactorD * numPacketsFl / 255.0f);
philipel9d3ab612015-12-21 12:12:39430
431 // We reduce cost factor (which will reduce overhead for FEC and
432 // hybrid method) and not the protectionFactor.
433 _corrFecCost = 1.0f;
434 if (estNumFecGen < 1.1f && _protectionFactorD < minProtLevelFec) {
435 _corrFecCost = 0.5f;
436 }
437 if (estNumFecGen < 0.9f && _protectionFactorD < minProtLevelFec) {
438 _corrFecCost = 0.0f;
439 }
440
philipel9d3ab612015-12-21 12:12:39441 // DONE WITH FEC PROTECTION SETTINGS
442 return true;
niklase@google.com470e71d2011-07-07 08:21:25443}
444
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49445int VCMFecMethod::BitsPerFrame(const VCMProtectionParameters* parameters) {
446 // When temporal layers are available FEC will only be applied on the base
447 // layer.
Erik Språngd92288f2018-07-04 08:07:40448 const float bitRateRatio =
449 webrtc::SimulcastRateAllocator::GetTemporalRateAllocation(
Rasmus Brandt2b9317a2019-10-30 12:01:46450 parameters->numLayers, 0,
451 rate_control_settings_.Vp8BaseHeavyTl3RateAllocation());
thakis@chromium.org65bc2542012-08-13 19:26:12452 float frameRateRatio = powf(1 / 2.0, parameters->numLayers - 1);
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49453 float bitRate = parameters->bitRate * bitRateRatio;
454 float frameRate = parameters->frameRate * frameRateRatio;
455
456 // TODO(mikhal): Update factor following testing.
457 float adjustmentFactor = 1;
458
Peter Boström5e8351b2016-01-28 22:55:29459 if (frameRate < 1.0f)
460 frameRate = 1.0f;
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49461 // Average bits per frame (units of kbits)
Emircan Uysaler704a7bd2018-08-06 23:14:10462 return rtc::saturated_cast<int>(adjustmentFactor * bitRate / frameRate);
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49463}
464
philipel9d3ab612015-12-21 12:12:39465bool VCMFecMethod::EffectivePacketLoss(
466 const VCMProtectionParameters* parameters) {
467 // Effective packet loss to encoder is based on RPL (residual packet loss)
468 // this is a soft setting based on degree of FEC protection
469 // RPL = received/input packet loss - average_FEC_recovery
470 // note: received/input packet loss may be filtered based on FilteredLoss
niklase@google.com470e71d2011-07-07 08:21:25471
philipel9d3ab612015-12-21 12:12:39472 // Effective Packet Loss, NA in current version.
473 _effectivePacketLoss = 0;
niklase@google.com470e71d2011-07-07 08:21:25474
philipel9d3ab612015-12-21 12:12:39475 return true;
niklase@google.com470e71d2011-07-07 08:21:25476}
477
philipel9d3ab612015-12-21 12:12:39478bool VCMFecMethod::UpdateParameters(const VCMProtectionParameters* parameters) {
479 // Compute the protection factor
480 ProtectionFactor(parameters);
niklase@google.com470e71d2011-07-07 08:21:25481
philipel9d3ab612015-12-21 12:12:39482 // Compute the effective packet loss
483 EffectivePacketLoss(parameters);
niklase@google.com470e71d2011-07-07 08:21:25484
philipel9d3ab612015-12-21 12:12:39485 // Protection/fec rates obtained above is defined relative to total number
486 // of packets (total rate: source+fec) FEC in RTP module assumes protection
487 // factor is defined relative to source number of packets so we should
488 // convert the factor to reduce mismatch between mediaOpt suggested rate and
489 // the actual rate
490 _protectionFactorK = ConvertFECRate(_protectionFactorK);
491 _protectionFactorD = ConvertFECRate(_protectionFactorD);
niklase@google.com470e71d2011-07-07 08:21:25492
philipel9d3ab612015-12-21 12:12:39493 return true;
niklase@google.com470e71d2011-07-07 08:21:25494}
Danil Chapovalov67ba9142024-05-10 13:10:24495VCMLossProtectionLogic::VCMLossProtectionLogic(const Environment& env)
496 : env_(env),
497 _currentParameters(),
philipel9d3ab612015-12-21 12:12:39498 _rtt(0),
499 _lossPr(0.0f),
500 _bitRate(0.0f),
501 _frameRate(0.0f),
502 _keyFrameSize(0.0f),
503 _fecRateKey(0),
504 _fecRateDelta(0),
505 _lastPrUpdateT(0),
506 _lossPr255(0.9999f),
507 _lossPrHistory(),
508 _shortMaxLossPr255(0),
509 _packetsPerFrame(0.9999f),
510 _packetsPerFrameKey(0.9999f),
Ying Wang1f262cc2018-08-23 11:54:08511 _codecWidth(704),
512 _codecHeight(576),
philipel9d3ab612015-12-21 12:12:39513 _numLayers(1) {
Danil Chapovalov67ba9142024-05-10 13:10:24514 Reset(env_.clock().CurrentTime().ms());
niklase@google.com470e71d2011-07-07 08:21:25515}
516
philipel9d3ab612015-12-21 12:12:39517VCMLossProtectionLogic::~VCMLossProtectionLogic() {
518 Release();
niklase@google.com470e71d2011-07-07 08:21:25519}
520
pbos@webrtc.orgcade82c2015-03-12 10:39:24521void VCMLossProtectionLogic::SetMethod(
522 enum VCMProtectionMethodEnum newMethodType) {
pbosba8c15b2015-07-14 16:36:34523 if (_selectedMethod && _selectedMethod->Type() == newMethodType)
524 return;
mikhal@webrtc.orga057a952011-08-26 21:17:34525
philipel9d3ab612015-12-21 12:12:39526 switch (newMethodType) {
pbos@webrtc.orgcade82c2015-03-12 10:39:24527 case kNack:
pbosba8c15b2015-07-14 16:36:34528 _selectedMethod.reset(new VCMNackMethod());
pbos@webrtc.orgcade82c2015-03-12 10:39:24529 break;
530 case kFec:
Danil Chapovalov67ba9142024-05-10 13:10:24531 _selectedMethod = std::make_unique<VCMFecMethod>(env_.field_trials());
pbos@webrtc.orgcade82c2015-03-12 10:39:24532 break;
533 case kNackFec:
Danil Chapovalov67ba9142024-05-10 13:10:24534 _selectedMethod = std::make_unique<VCMNackFecMethod>(env_.field_trials(),
535 kLowRttNackMs, -1);
pbos@webrtc.orgcade82c2015-03-12 10:39:24536 break;
537 case kNone:
pbosba8c15b2015-07-14 16:36:34538 _selectedMethod.reset();
pbos@webrtc.orgcade82c2015-03-12 10:39:24539 break;
540 }
541 UpdateMethod();
niklase@google.com470e71d2011-07-07 08:21:25542}
543
philipel9d3ab612015-12-21 12:12:39544void VCMLossProtectionLogic::UpdateRtt(int64_t rtt) {
545 _rtt = rtt;
niklase@google.com470e71d2011-07-07 08:21:25546}
547
philipel9d3ab612015-12-21 12:12:39548void VCMLossProtectionLogic::UpdateMaxLossHistory(uint8_t lossPr255,
549 int64_t now) {
550 if (_lossPrHistory[0].timeMs >= 0 &&
551 now - _lossPrHistory[0].timeMs < kLossPrShortFilterWinMs) {
552 if (lossPr255 > _shortMaxLossPr255) {
553 _shortMaxLossPr255 = lossPr255;
niklase@google.com470e71d2011-07-07 08:21:25554 }
philipel9d3ab612015-12-21 12:12:39555 } else {
556 // Only add a new value to the history once a second
557 if (_lossPrHistory[0].timeMs == -1) {
558 // First, no shift
559 _shortMaxLossPr255 = lossPr255;
560 } else {
561 // Shift
562 for (int32_t i = (kLossPrHistorySize - 2); i >= 0; i--) {
563 _lossPrHistory[i + 1].lossPr255 = _lossPrHistory[i].lossPr255;
564 _lossPrHistory[i + 1].timeMs = _lossPrHistory[i].timeMs;
565 }
566 }
567 if (_shortMaxLossPr255 == 0) {
568 _shortMaxLossPr255 = lossPr255;
569 }
niklase@google.com470e71d2011-07-07 08:21:25570
philipel9d3ab612015-12-21 12:12:39571 _lossPrHistory[0].lossPr255 = _shortMaxLossPr255;
572 _lossPrHistory[0].timeMs = now;
573 _shortMaxLossPr255 = 0;
574 }
niklase@google.com470e71d2011-07-07 08:21:25575}
576
philipel9d3ab612015-12-21 12:12:39577uint8_t VCMLossProtectionLogic::MaxFilteredLossPr(int64_t nowMs) const {
578 uint8_t maxFound = _shortMaxLossPr255;
579 if (_lossPrHistory[0].timeMs == -1) {
niklase@google.com470e71d2011-07-07 08:21:25580 return maxFound;
philipel9d3ab612015-12-21 12:12:39581 }
582 for (int32_t i = 0; i < kLossPrHistorySize; i++) {
583 if (_lossPrHistory[i].timeMs == -1) {
584 break;
585 }
586 if (nowMs - _lossPrHistory[i].timeMs >
587 kLossPrHistorySize * kLossPrShortFilterWinMs) {
588 // This sample (and all samples after this) is too old
589 break;
590 }
591 if (_lossPrHistory[i].lossPr255 > maxFound) {
592 // This sample is the largest one this far into the history
593 maxFound = _lossPrHistory[i].lossPr255;
594 }
595 }
596 return maxFound;
niklase@google.com470e71d2011-07-07 08:21:25597}
598
philipel9d3ab612015-12-21 12:12:39599uint8_t VCMLossProtectionLogic::FilteredLoss(int64_t nowMs,
600 FilterPacketLossMode filter_mode,
601 uint8_t lossPr255) {
marpan@webrtc.org2dad3fb2012-01-09 18:18:36602 // Update the max window filter.
603 UpdateMaxLossHistory(lossPr255, nowMs);
604
605 // Update the recursive average filter.
Emircan Uysaler704a7bd2018-08-06 23:14:10606 _lossPr255.Apply(rtc::saturated_cast<float>(nowMs - _lastPrUpdateT),
607 rtc::saturated_cast<float>(lossPr255));
marpan@webrtc.org2dad3fb2012-01-09 18:18:36608 _lastPrUpdateT = nowMs;
609
610 // Filtered loss: default is received loss (no filtering).
pbos@webrtc.org7b859cc2013-04-02 15:54:38611 uint8_t filtered_loss = lossPr255;
marpan@webrtc.org2dad3fb2012-01-09 18:18:36612
613 switch (filter_mode) {
614 case kNoFilter:
615 break;
616 case kAvgFilter:
Emircan Uysaler704a7bd2018-08-06 23:14:10617 filtered_loss = rtc::saturated_cast<uint8_t>(_lossPr255.filtered() + 0.5);
marpan@webrtc.org2dad3fb2012-01-09 18:18:36618 break;
619 case kMaxFilter:
620 filtered_loss = MaxFilteredLossPr(nowMs);
621 break;
622 }
623
624 return filtered_loss;
niklase@google.com470e71d2011-07-07 08:21:25625}
626
philipel9d3ab612015-12-21 12:12:39627void VCMLossProtectionLogic::UpdateFilteredLossPr(uint8_t packetLossEnc) {
Emircan Uysaler704a7bd2018-08-06 23:14:10628 _lossPr = rtc::saturated_cast<float>(packetLossEnc) / 255.0;
niklase@google.com470e71d2011-07-07 08:21:25629}
630
philipel9d3ab612015-12-21 12:12:39631void VCMLossProtectionLogic::UpdateBitRate(float bitRate) {
632 _bitRate = bitRate;
niklase@google.com470e71d2011-07-07 08:21:25633}
634
philipel9d3ab612015-12-21 12:12:39635void VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets,
636 int64_t nowMs) {
Emircan Uysaler704a7bd2018-08-06 23:14:10637 _packetsPerFrame.Apply(
638 rtc::saturated_cast<float>(nowMs - _lastPacketPerFrameUpdateT), nPackets);
philipel9d3ab612015-12-21 12:12:39639 _lastPacketPerFrameUpdateT = nowMs;
niklase@google.com470e71d2011-07-07 08:21:25640}
641
philipel9d3ab612015-12-21 12:12:39642void VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets,
643 int64_t nowMs) {
644 _packetsPerFrameKey.Apply(
Emircan Uysaler704a7bd2018-08-06 23:14:10645 rtc::saturated_cast<float>(nowMs - _lastPacketPerFrameUpdateTKey),
646 nPackets);
philipel9d3ab612015-12-21 12:12:39647 _lastPacketPerFrameUpdateTKey = nowMs;
niklase@google.com470e71d2011-07-07 08:21:25648}
649
philipel9d3ab612015-12-21 12:12:39650void VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize) {
651 _keyFrameSize = keyFrameSize;
niklase@google.com470e71d2011-07-07 08:21:25652}
653
perkjc2c24f72016-07-11 08:47:32654void VCMLossProtectionLogic::UpdateFrameSize(size_t width, size_t height) {
philipel9d3ab612015-12-21 12:12:39655 _codecWidth = width;
656 _codecHeight = height;
niklase@google.com470e71d2011-07-07 08:21:25657}
658
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49659void VCMLossProtectionLogic::UpdateNumLayers(int numLayers) {
660 _numLayers = (numLayers == 0) ? 1 : numLayers;
661}
662
philipel9d3ab612015-12-21 12:12:39663bool VCMLossProtectionLogic::UpdateMethod() {
664 if (!_selectedMethod)
665 return false;
666 _currentParameters.rtt = _rtt;
667 _currentParameters.lossPr = _lossPr;
668 _currentParameters.bitRate = _bitRate;
669 _currentParameters.frameRate = _frameRate; // rename actual frame rate?
670 _currentParameters.keyFrameSize = _keyFrameSize;
671 _currentParameters.fecRateDelta = _fecRateDelta;
672 _currentParameters.fecRateKey = _fecRateKey;
673 _currentParameters.packetsPerFrame = _packetsPerFrame.filtered();
674 _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.filtered();
675 _currentParameters.codecWidth = _codecWidth;
676 _currentParameters.codecHeight = _codecHeight;
677 _currentParameters.numLayers = _numLayers;
678 return _selectedMethod->UpdateParameters(&_currentParameters);
niklase@google.com470e71d2011-07-07 08:21:25679}
680
philipel9d3ab612015-12-21 12:12:39681VCMProtectionMethod* VCMLossProtectionLogic::SelectedMethod() const {
682 return _selectedMethod.get();
niklase@google.com470e71d2011-07-07 08:21:25683}
684
pbos@webrtc.orgcade82c2015-03-12 10:39:24685VCMProtectionMethodEnum VCMLossProtectionLogic::SelectedType() const {
pbosba8c15b2015-07-14 16:36:34686 return _selectedMethod ? _selectedMethod->Type() : kNone;
mikhal@webrtc.orga057a952011-08-26 21:17:34687}
688
philipel9d3ab612015-12-21 12:12:39689void VCMLossProtectionLogic::Reset(int64_t nowMs) {
690 _lastPrUpdateT = nowMs;
691 _lastPacketPerFrameUpdateT = nowMs;
692 _lastPacketPerFrameUpdateTKey = nowMs;
693 _lossPr255.Reset(0.9999f);
694 _packetsPerFrame.Reset(0.9999f);
695 _fecRateDelta = _fecRateKey = 0;
696 for (int32_t i = 0; i < kLossPrHistorySize; i++) {
697 _lossPrHistory[i].lossPr255 = 0;
698 _lossPrHistory[i].timeMs = -1;
699 }
700 _shortMaxLossPr255 = 0;
701 Release();
mikhal@webrtc.orga057a952011-08-26 21:17:34702}
703
pbosba8c15b2015-07-14 16:36:34704void VCMLossProtectionLogic::Release() {
705 _selectedMethod.reset();
niklase@google.com470e71d2011-07-07 08:21:25706}
707
stefan@webrtc.orga64300a2013-03-04 15:24:40708} // namespace media_optimization
709} // namespace webrtc