blob: 69cf757f2b45adeb8804e2c9b726ed0f0e61d0c2 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Henrik Kjellander2557b862015-11-18 22:00:21 +010011#include "webrtc/modules/video_coding/media_opt_util.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
niklase@google.com470e71d2011-07-07 08:21:25 +000013#include <float.h>
14#include <limits.h>
pbos@webrtc.orga4407322013-07-16 12:32:05 +000015#include <math.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000016
philipel9d3ab612015-12-21 04:12:39 -080017#include <algorithm>
18
Henrik Kjellanderff761fb2015-11-04 08:31:52 +010019#include "webrtc/modules/include/module_common_types.h"
pbos@webrtc.orga4407322013-07-16 12:32:05 +000020#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
Henrik Kjellander2557b862015-11-18 22:00:21 +010021#include "webrtc/modules/video_coding/include/video_coding_defines.h"
22#include "webrtc/modules/video_coding/fec_tables_xor.h"
23#include "webrtc/modules/video_coding/nack_fec_tables.h"
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +000024
niklase@google.com470e71d2011-07-07 08:21:25 +000025namespace webrtc {
Peter Boström9cb1f302015-04-01 11:39:49 +020026// Max value of loss rates in off-line model
27static const int kPacketLossMax = 129;
28
stefan@webrtc.orga64300a2013-03-04 15:24:40 +000029namespace media_optimization {
niklase@google.com470e71d2011-07-07 08:21:25 +000030
Peter Boström9cb1f302015-04-01 11:39:49 +020031VCMProtectionMethod::VCMProtectionMethod()
32 : _effectivePacketLoss(0),
33 _protectionFactorK(0),
34 _protectionFactorD(0),
35 _scaleProtKey(2.0f),
36 _maxPayloadSize(1460),
pbos602316c2016-04-29 16:10:27 -070037 _qmRobustness(new VCMQmRobustness()),
38 _useUepProtectionK(false),
39 _useUepProtectionD(true),
Peter Boström9cb1f302015-04-01 11:39:49 +020040 _corrFecCost(1.0),
philipel9d3ab612015-12-21 04:12:39 -080041 _type(kNone) {}
mikhal@webrtc.orga057a952011-08-26 21:17:34 +000042
pbos602316c2016-04-29 16:10:27 -070043VCMProtectionMethod::~VCMProtectionMethod() {
44 delete _qmRobustness;
45}
46void VCMProtectionMethod::UpdateContentMetrics(
47 const VideoContentMetrics* contentMetrics) {
48 _qmRobustness->UpdateContent(contentMetrics);
49}
marpan@google.com86548c62011-07-12 17:12:57 +000050
pkasting@chromium.org16825b12015-01-12 21:51:21 +000051VCMNackFecMethod::VCMNackFecMethod(int64_t lowRttNackThresholdMs,
52 int64_t highRttNackThresholdMs)
stefan@webrtc.org932ab182011-11-29 11:33:31 +000053 : VCMFecMethod(),
54 _lowRttNackMs(lowRttNackThresholdMs),
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +000055 _highRttNackMs(highRttNackThresholdMs),
56 _maxFramesFec(1) {
stefan@webrtc.org932ab182011-11-29 11:33:31 +000057 assert(lowRttNackThresholdMs >= -1 && highRttNackThresholdMs >= -1);
58 assert(highRttNackThresholdMs == -1 ||
59 lowRttNackThresholdMs <= highRttNackThresholdMs);
60 assert(lowRttNackThresholdMs > -1 || highRttNackThresholdMs == -1);
61 _type = kNackFec;
mikhal@google.com77408882011-07-22 22:05:25 +000062}
63
philipel9d3ab612015-12-21 04:12:39 -080064VCMNackFecMethod::~VCMNackFecMethod() {
65 //
mikhal@google.com77408882011-07-22 22:05:25 +000066}
philipel9d3ab612015-12-21 04:12:39 -080067bool VCMNackFecMethod::ProtectionFactor(
68 const VCMProtectionParameters* parameters) {
69 // Hybrid Nack FEC has three operational modes:
70 // 1. Low RTT (below kLowRttNackMs) - Nack only: Set FEC rate
71 // (_protectionFactorD) to zero. -1 means no FEC.
72 // 2. High RTT (above _highRttNackMs) - FEC Only: Keep FEC factors.
73 // -1 means always allow NACK.
74 // 3. Medium RTT values - Hybrid mode: We will only nack the
75 // residual following the decoding of the FEC (refer to JB logic). FEC
76 // delta protection factor will be adjusted based on the RTT.
mikhal@google.com022716b2011-07-20 23:12:57 +000077
philipel9d3ab612015-12-21 04:12:39 -080078 // Otherwise: we count on FEC; if the RTT is below a threshold, then we
79 // nack the residual, based on a decision made in the JB.
mikhal@google.com022716b2011-07-20 23:12:57 +000080
philipel9d3ab612015-12-21 04:12:39 -080081 // Compute the protection factors
82 VCMFecMethod::ProtectionFactor(parameters);
83 if (_lowRttNackMs == -1 || parameters->rtt < _lowRttNackMs) {
84 _protectionFactorD = 0;
85 VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
mikhal@google.com320813c2011-08-03 20:47:50 +000086
mikhal@google.com679450f2011-08-01 22:14:58 +000087 // When in Hybrid mode (RTT range), adjust FEC rates based on the
88 // RTT (NACK effectiveness) - adjustment factor is in the range [0,1].
philipel9d3ab612015-12-21 04:12:39 -080089 } else if (_highRttNackMs == -1 || parameters->rtt < _highRttNackMs) {
90 // TODO(mikhal): Disabling adjustment temporarily.
91 // uint16_t rttIndex = (uint16_t) parameters->rtt;
92 float adjustRtt = 1.0f; // (float)VCMNackFecTable[rttIndex] / 100.0f;
niklase@google.com470e71d2011-07-07 08:21:25 +000093
philipel9d3ab612015-12-21 04:12:39 -080094 // Adjust FEC with NACK on (for delta frame only)
95 // table depends on RTT relative to rttMax (NACK Threshold)
96 _protectionFactorD = static_cast<uint8_t>(
97 adjustRtt * static_cast<float>(_protectionFactorD));
98 // update FEC rates after applying adjustment
99 VCMFecMethod::UpdateProtectionFactorD(_protectionFactorD);
100 }
mikhal@google.com679450f2011-08-01 22:14:58 +0000101
philipel9d3ab612015-12-21 04:12:39 -0800102 return true;
mikhal@google.com022716b2011-07-20 23:12:57 +0000103}
niklase@google.com470e71d2011-07-07 08:21:25 +0000104
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000105int VCMNackFecMethod::ComputeMaxFramesFec(
106 const VCMProtectionParameters* parameters) {
107 if (parameters->numLayers > 2) {
108 // For more than 2 temporal layers we will only have FEC on the base layer,
109 // and the base layers will be pretty far apart. Therefore we force one
110 // frame FEC.
111 return 1;
112 }
113 // We set the max number of frames to base the FEC on so that on average
114 // we will have complete frames in one RTT. Note that this is an upper
115 // bound, and that the actual number of frames used for FEC is decided by the
116 // RTP module based on the actual number of packets and the protection factor.
philipel9d3ab612015-12-21 04:12:39 -0800117 float base_layer_framerate =
118 parameters->frameRate /
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000119 static_cast<float>(1 << (parameters->numLayers - 1));
philipel9d3ab612015-12-21 04:12:39 -0800120 int max_frames_fec = std::max(
121 static_cast<int>(2.0f * base_layer_framerate * parameters->rtt / 1000.0f +
122 0.5f),
123 1);
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000124 // |kUpperLimitFramesFec| is the upper limit on how many frames we
125 // allow any FEC to be based on.
126 if (max_frames_fec > kUpperLimitFramesFec) {
127 max_frames_fec = kUpperLimitFramesFec;
128 }
129 return max_frames_fec;
130}
131
132int VCMNackFecMethod::MaxFramesFec() const {
133 return _maxFramesFec;
134}
135
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000136bool VCMNackFecMethod::BitRateTooLowForFec(
137 const VCMProtectionParameters* parameters) {
138 // Bitrate below which we turn off FEC, regardless of reported packet loss.
139 // The condition should depend on resolution and content. For now, use
140 // threshold on bytes per frame, with some effect for the frame size.
141 // The condition for turning off FEC is also based on other factors,
142 // such as |_numLayers|, |_maxFramesFec|, and |_rtt|.
143 int estimate_bytes_per_frame = 1000 * BitsPerFrame(parameters) / 8;
144 int max_bytes_per_frame = kMaxBytesPerFrameForFec;
145 int num_pixels = parameters->codecWidth * parameters->codecHeight;
146 if (num_pixels <= 352 * 288) {
147 max_bytes_per_frame = kMaxBytesPerFrameForFecLow;
148 } else if (num_pixels > 640 * 480) {
149 max_bytes_per_frame = kMaxBytesPerFrameForFecHigh;
150 }
philipel9d3ab612015-12-21 04:12:39 -0800151 // TODO(marpan): add condition based on maximum frames used for FEC,
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000152 // and expand condition based on frame size.
pkasting@chromium.org16825b12015-01-12 21:51:21 +0000153 // Max round trip time threshold in ms.
154 const int64_t kMaxRttTurnOffFec = 200;
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000155 if (estimate_bytes_per_frame < max_bytes_per_frame &&
philipel9d3ab612015-12-21 04:12:39 -0800156 parameters->numLayers < 3 && parameters->rtt < kMaxRttTurnOffFec) {
marpan@webrtc.org88ad06b2012-04-20 16:05:24 +0000157 return true;
158 }
159 return false;
160}
161
philipel9d3ab612015-12-21 04:12:39 -0800162bool VCMNackFecMethod::EffectivePacketLoss(
163 const VCMProtectionParameters* parameters) {
164 // Set the effective packet loss for encoder (based on FEC code).
165 // Compute the effective packet loss and residual packet loss due to FEC.
166 VCMFecMethod::EffectivePacketLoss(parameters);
167 return true;
mikhal@google.com022716b2011-07-20 23:12:57 +0000168}
niklase@google.com470e71d2011-07-07 08:21:25 +0000169
philipel9d3ab612015-12-21 04:12:39 -0800170bool VCMNackFecMethod::UpdateParameters(
171 const VCMProtectionParameters* parameters) {
172 ProtectionFactor(parameters);
173 EffectivePacketLoss(parameters);
174 _maxFramesFec = ComputeMaxFramesFec(parameters);
175 if (BitRateTooLowForFec(parameters)) {
176 _protectionFactorK = 0;
177 _protectionFactorD = 0;
178 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000179
philipel9d3ab612015-12-21 04:12:39 -0800180 // Protection/fec rates obtained above are defined relative to total number
181 // of packets (total rate: source + fec) FEC in RTP module assumes
182 // protection factor is defined relative to source number of packets so we
183 // should convert the factor to reduce mismatch between mediaOpt's rate and
184 // the actual one
185 _protectionFactorK = VCMFecMethod::ConvertFECRate(_protectionFactorK);
186 _protectionFactorD = VCMFecMethod::ConvertFECRate(_protectionFactorD);
niklase@google.com470e71d2011-07-07 08:21:25 +0000187
philipel9d3ab612015-12-21 04:12:39 -0800188 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000189}
190
philipel9d3ab612015-12-21 04:12:39 -0800191VCMNackMethod::VCMNackMethod() : VCMProtectionMethod() {
192 _type = kNack;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000193}
194
philipel9d3ab612015-12-21 04:12:39 -0800195VCMNackMethod::~VCMNackMethod() {
196 //
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000197}
198
philipel9d3ab612015-12-21 04:12:39 -0800199bool VCMNackMethod::EffectivePacketLoss(
200 const VCMProtectionParameters* parameter) {
201 // Effective Packet Loss, NA in current version.
202 _effectivePacketLoss = 0;
203 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000204}
205
philipel9d3ab612015-12-21 04:12:39 -0800206bool VCMNackMethod::UpdateParameters(
207 const VCMProtectionParameters* parameters) {
208 // Compute the effective packet loss
209 EffectivePacketLoss(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000210
philipel9d3ab612015-12-21 04:12:39 -0800211 // nackCost = (bitRate - nackCost) * (lossPr)
212 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000213}
214
philipel9d3ab612015-12-21 04:12:39 -0800215VCMFecMethod::VCMFecMethod() : VCMProtectionMethod() {
216 _type = kFec;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000217}
philipel9d3ab612015-12-21 04:12:39 -0800218VCMFecMethod::~VCMFecMethod() {
219 //
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000220}
221
philipel9d3ab612015-12-21 04:12:39 -0800222uint8_t VCMFecMethod::BoostCodeRateKey(uint8_t packetFrameDelta,
223 uint8_t packetFrameKey) const {
224 uint8_t boostRateKey = 2;
225 // Default: ratio scales the FEC protection up for I frames
226 uint8_t ratio = 1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000227
philipel9d3ab612015-12-21 04:12:39 -0800228 if (packetFrameDelta > 0) {
229 ratio = (int8_t)(packetFrameKey / packetFrameDelta);
230 }
231 ratio = VCM_MAX(boostRateKey, ratio);
niklase@google.com470e71d2011-07-07 08:21:25 +0000232
philipel9d3ab612015-12-21 04:12:39 -0800233 return ratio;
niklase@google.com470e71d2011-07-07 08:21:25 +0000234}
235
philipel9d3ab612015-12-21 04:12:39 -0800236uint8_t VCMFecMethod::ConvertFECRate(uint8_t codeRateRTP) const {
237 return static_cast<uint8_t>(VCM_MIN(
238 255,
239 (0.5 + 255.0 * codeRateRTP / static_cast<float>(255 - codeRateRTP))));
niklase@google.com470e71d2011-07-07 08:21:25 +0000240}
241
mikhal@google.com679450f2011-08-01 22:14:58 +0000242// Update FEC with protectionFactorD
philipel9d3ab612015-12-21 04:12:39 -0800243void VCMFecMethod::UpdateProtectionFactorD(uint8_t protectionFactorD) {
244 _protectionFactorD = protectionFactorD;
mikhal@google.com679450f2011-08-01 22:14:58 +0000245}
246
mikhal@webrtc.orgd0752c32011-10-19 15:48:30 +0000247// Update FEC with protectionFactorK
philipel9d3ab612015-12-21 04:12:39 -0800248void VCMFecMethod::UpdateProtectionFactorK(uint8_t protectionFactorK) {
249 _protectionFactorK = protectionFactorK;
mikhal@webrtc.orgd0752c32011-10-19 15:48:30 +0000250}
251
philipel9d3ab612015-12-21 04:12:39 -0800252bool VCMFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters) {
253 // FEC PROTECTION SETTINGS: varies with packet loss and bitrate
niklase@google.com470e71d2011-07-07 08:21:25 +0000254
philipel9d3ab612015-12-21 04:12:39 -0800255 // No protection if (filtered) packetLoss is 0
256 uint8_t packetLoss = (uint8_t)(255 * parameters->lossPr);
257 if (packetLoss == 0) {
258 _protectionFactorK = 0;
259 _protectionFactorD = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000260 return true;
philipel9d3ab612015-12-21 04:12:39 -0800261 }
262
263 // Parameters for FEC setting:
264 // first partition size, thresholds, table pars, spatial resoln fac.
265
266 // First partition protection: ~ 20%
267 uint8_t firstPartitionProt = (uint8_t)(255 * 0.20);
268
269 // Minimum protection level needed to generate one FEC packet for one
270 // source packet/frame (in RTP sender)
271 uint8_t minProtLevelFec = 85;
272
273 // Threshold on packetLoss and bitRrate/frameRate (=average #packets),
274 // above which we allocate protection to cover at least first partition.
275 uint8_t lossThr = 0;
276 uint8_t packetNumThr = 1;
277
278 // Parameters for range of rate index of table.
279 const uint8_t ratePar1 = 5;
280 const uint8_t ratePar2 = 49;
281
282 // Spatial resolution size, relative to a reference size.
283 float spatialSizeToRef =
284 static_cast<float>(parameters->codecWidth * parameters->codecHeight) /
285 (static_cast<float>(704 * 576));
286 // resolnFac: This parameter will generally increase/decrease the FEC rate
287 // (for fixed bitRate and packetLoss) based on system size.
288 // Use a smaller exponent (< 1) to control/soften system size effect.
289 const float resolnFac = 1.0 / powf(spatialSizeToRef, 0.3f);
290
291 const int bitRatePerFrame = BitsPerFrame(parameters);
292
293 // Average number of packets per frame (source and fec):
294 const uint8_t avgTotPackets =
295 1 + (uint8_t)(static_cast<float>(bitRatePerFrame) * 1000.0 /
296 static_cast<float>(8.0 * _maxPayloadSize) +
297 0.5);
298
299 // FEC rate parameters: for P and I frame
300 uint8_t codeRateDelta = 0;
301 uint8_t codeRateKey = 0;
302
303 // Get index for table: the FEC protection depends on an effective rate.
304 // The range on the rate index corresponds to rates (bps)
305 // from ~200k to ~8000k, for 30fps
306 const uint16_t effRateFecTable =
307 static_cast<uint16_t>(resolnFac * bitRatePerFrame);
308 uint8_t rateIndexTable = (uint8_t)VCM_MAX(
309 VCM_MIN((effRateFecTable - ratePar1) / ratePar1, ratePar2), 0);
310
311 // Restrict packet loss range to 50:
312 // current tables defined only up to 50%
313 if (packetLoss >= kPacketLossMax) {
314 packetLoss = kPacketLossMax - 1;
315 }
316 uint16_t indexTable = rateIndexTable * kPacketLossMax + packetLoss;
317
318 // Check on table index
319 assert(indexTable < kSizeCodeRateXORTable);
320
321 // Protection factor for P frame
322 codeRateDelta = kCodeRateXORTable[indexTable];
323
324 if (packetLoss > lossThr && avgTotPackets > packetNumThr) {
325 // Set a minimum based on first partition size.
326 if (codeRateDelta < firstPartitionProt) {
327 codeRateDelta = firstPartitionProt;
328 }
329 }
330
331 // Check limit on amount of protection for P frame; 50% is max.
332 if (codeRateDelta >= kPacketLossMax) {
333 codeRateDelta = kPacketLossMax - 1;
334 }
335
pbos602316c2016-04-29 16:10:27 -0700336 float adjustFec = 1.0f;
337 // Avoid additional adjustments when layers are active.
338 // TODO(mikhal/marco): Update adjusmtent based on layer info.
339 if (parameters->numLayers == 1) {
340 adjustFec = _qmRobustness->AdjustFecFactor(
341 codeRateDelta, parameters->bitRate, parameters->frameRate,
342 parameters->rtt, packetLoss);
343 }
344
345 codeRateDelta = static_cast<uint8_t>(codeRateDelta * adjustFec);
346
philipel9d3ab612015-12-21 04:12:39 -0800347 // For Key frame:
348 // Effectively at a higher rate, so we scale/boost the rate
349 // The boost factor may depend on several factors: ratio of packet
350 // number of I to P frames, how much protection placed on P frames, etc.
351 const uint8_t packetFrameDelta = (uint8_t)(0.5 + parameters->packetsPerFrame);
352 const uint8_t packetFrameKey =
353 (uint8_t)(0.5 + parameters->packetsPerFrameKey);
354 const uint8_t boostKey = BoostCodeRateKey(packetFrameDelta, packetFrameKey);
355
356 rateIndexTable = (uint8_t)VCM_MAX(
357 VCM_MIN(1 + (boostKey * effRateFecTable - ratePar1) / ratePar1, ratePar2),
358 0);
359 uint16_t indexTableKey = rateIndexTable * kPacketLossMax + packetLoss;
360
361 indexTableKey = VCM_MIN(indexTableKey, kSizeCodeRateXORTable);
362
363 // Check on table index
364 assert(indexTableKey < kSizeCodeRateXORTable);
365
366 // Protection factor for I frame
367 codeRateKey = kCodeRateXORTable[indexTableKey];
368
369 // Boosting for Key frame.
370 int boostKeyProt = _scaleProtKey * codeRateDelta;
371 if (boostKeyProt >= kPacketLossMax) {
372 boostKeyProt = kPacketLossMax - 1;
373 }
374
375 // Make sure I frame protection is at least larger than P frame protection,
376 // and at least as high as filtered packet loss.
377 codeRateKey = static_cast<uint8_t>(
378 VCM_MAX(packetLoss, VCM_MAX(boostKeyProt, codeRateKey)));
379
380 // Check limit on amount of protection for I frame: 50% is max.
381 if (codeRateKey >= kPacketLossMax) {
382 codeRateKey = kPacketLossMax - 1;
383 }
384
385 _protectionFactorK = codeRateKey;
386 _protectionFactorD = codeRateDelta;
387
388 // Generally there is a rate mis-match between the FEC cost estimated
389 // in mediaOpt and the actual FEC cost sent out in RTP module.
390 // This is more significant at low rates (small # of source packets), where
391 // the granularity of the FEC decreases. In this case, non-zero protection
392 // in mediaOpt may generate 0 FEC packets in RTP sender (since actual #FEC
393 // is based on rounding off protectionFactor on actual source packet number).
394 // The correction factor (_corrFecCost) attempts to corrects this, at least
395 // for cases of low rates (small #packets) and low protection levels.
396
397 float numPacketsFl = 1.0f + (static_cast<float>(bitRatePerFrame) * 1000.0 /
398 static_cast<float>(8.0 * _maxPayloadSize) +
399 0.5);
400
401 const float estNumFecGen =
402 0.5f + static_cast<float>(_protectionFactorD * numPacketsFl / 255.0f);
403
404 // We reduce cost factor (which will reduce overhead for FEC and
405 // hybrid method) and not the protectionFactor.
406 _corrFecCost = 1.0f;
407 if (estNumFecGen < 1.1f && _protectionFactorD < minProtLevelFec) {
408 _corrFecCost = 0.5f;
409 }
410 if (estNumFecGen < 0.9f && _protectionFactorD < minProtLevelFec) {
411 _corrFecCost = 0.0f;
412 }
413
pbos602316c2016-04-29 16:10:27 -0700414 // TODO(marpan): Set the UEP protection on/off for Key and Delta frames
415 _useUepProtectionK = _qmRobustness->SetUepProtection(
416 codeRateKey, parameters->bitRate, packetLoss, 0);
417
418 _useUepProtectionD = _qmRobustness->SetUepProtection(
419 codeRateDelta, parameters->bitRate, packetLoss, 1);
420
philipel9d3ab612015-12-21 04:12:39 -0800421 // DONE WITH FEC PROTECTION SETTINGS
422 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000423}
424
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000425int VCMFecMethod::BitsPerFrame(const VCMProtectionParameters* parameters) {
426 // When temporal layers are available FEC will only be applied on the base
427 // layer.
428 const float bitRateRatio =
philipel9d3ab612015-12-21 04:12:39 -0800429 kVp8LayerRateAlloction[parameters->numLayers - 1][0];
thakis@chromium.org65bc2542012-08-13 19:26:12 +0000430 float frameRateRatio = powf(1 / 2.0, parameters->numLayers - 1);
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000431 float bitRate = parameters->bitRate * bitRateRatio;
432 float frameRate = parameters->frameRate * frameRateRatio;
433
434 // TODO(mikhal): Update factor following testing.
435 float adjustmentFactor = 1;
436
Peter Boström5e8351b2016-01-28 23:55:29 +0100437 if (frameRate < 1.0f)
438 frameRate = 1.0f;
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000439 // Average bits per frame (units of kbits)
440 return static_cast<int>(adjustmentFactor * bitRate / frameRate);
441}
442
philipel9d3ab612015-12-21 04:12:39 -0800443bool VCMFecMethod::EffectivePacketLoss(
444 const VCMProtectionParameters* parameters) {
445 // Effective packet loss to encoder is based on RPL (residual packet loss)
446 // this is a soft setting based on degree of FEC protection
447 // RPL = received/input packet loss - average_FEC_recovery
448 // note: received/input packet loss may be filtered based on FilteredLoss
niklase@google.com470e71d2011-07-07 08:21:25 +0000449
philipel9d3ab612015-12-21 04:12:39 -0800450 // Effective Packet Loss, NA in current version.
451 _effectivePacketLoss = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000452
philipel9d3ab612015-12-21 04:12:39 -0800453 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000454}
455
philipel9d3ab612015-12-21 04:12:39 -0800456bool VCMFecMethod::UpdateParameters(const VCMProtectionParameters* parameters) {
457 // Compute the protection factor
458 ProtectionFactor(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000459
philipel9d3ab612015-12-21 04:12:39 -0800460 // Compute the effective packet loss
461 EffectivePacketLoss(parameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000462
philipel9d3ab612015-12-21 04:12:39 -0800463 // Protection/fec rates obtained above is defined relative to total number
464 // of packets (total rate: source+fec) FEC in RTP module assumes protection
465 // factor is defined relative to source number of packets so we should
466 // convert the factor to reduce mismatch between mediaOpt suggested rate and
467 // the actual rate
468 _protectionFactorK = ConvertFECRate(_protectionFactorK);
469 _protectionFactorD = ConvertFECRate(_protectionFactorD);
niklase@google.com470e71d2011-07-07 08:21:25 +0000470
philipel9d3ab612015-12-21 04:12:39 -0800471 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000472}
philipel9d3ab612015-12-21 04:12:39 -0800473VCMLossProtectionLogic::VCMLossProtectionLogic(int64_t nowMs)
474 : _currentParameters(),
475 _rtt(0),
476 _lossPr(0.0f),
477 _bitRate(0.0f),
478 _frameRate(0.0f),
479 _keyFrameSize(0.0f),
480 _fecRateKey(0),
481 _fecRateDelta(0),
482 _lastPrUpdateT(0),
483 _lossPr255(0.9999f),
484 _lossPrHistory(),
485 _shortMaxLossPr255(0),
486 _packetsPerFrame(0.9999f),
487 _packetsPerFrameKey(0.9999f),
488 _codecWidth(0),
489 _codecHeight(0),
490 _numLayers(1) {
491 Reset(nowMs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000492}
493
philipel9d3ab612015-12-21 04:12:39 -0800494VCMLossProtectionLogic::~VCMLossProtectionLogic() {
495 Release();
niklase@google.com470e71d2011-07-07 08:21:25 +0000496}
497
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000498void VCMLossProtectionLogic::SetMethod(
499 enum VCMProtectionMethodEnum newMethodType) {
pbosba8c15b2015-07-14 09:36:34 -0700500 if (_selectedMethod && _selectedMethod->Type() == newMethodType)
501 return;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000502
philipel9d3ab612015-12-21 04:12:39 -0800503 switch (newMethodType) {
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000504 case kNack:
pbosba8c15b2015-07-14 09:36:34 -0700505 _selectedMethod.reset(new VCMNackMethod());
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000506 break;
507 case kFec:
pbosba8c15b2015-07-14 09:36:34 -0700508 _selectedMethod.reset(new VCMFecMethod());
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000509 break;
510 case kNackFec:
pbosba8c15b2015-07-14 09:36:34 -0700511 _selectedMethod.reset(new VCMNackFecMethod(kLowRttNackMs, -1));
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000512 break;
513 case kNone:
pbosba8c15b2015-07-14 09:36:34 -0700514 _selectedMethod.reset();
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000515 break;
516 }
517 UpdateMethod();
niklase@google.com470e71d2011-07-07 08:21:25 +0000518}
519
philipel9d3ab612015-12-21 04:12:39 -0800520void VCMLossProtectionLogic::UpdateRtt(int64_t rtt) {
521 _rtt = rtt;
niklase@google.com470e71d2011-07-07 08:21:25 +0000522}
523
philipel9d3ab612015-12-21 04:12:39 -0800524void VCMLossProtectionLogic::UpdateMaxLossHistory(uint8_t lossPr255,
525 int64_t now) {
526 if (_lossPrHistory[0].timeMs >= 0 &&
527 now - _lossPrHistory[0].timeMs < kLossPrShortFilterWinMs) {
528 if (lossPr255 > _shortMaxLossPr255) {
529 _shortMaxLossPr255 = lossPr255;
niklase@google.com470e71d2011-07-07 08:21:25 +0000530 }
philipel9d3ab612015-12-21 04:12:39 -0800531 } else {
532 // Only add a new value to the history once a second
533 if (_lossPrHistory[0].timeMs == -1) {
534 // First, no shift
535 _shortMaxLossPr255 = lossPr255;
536 } else {
537 // Shift
538 for (int32_t i = (kLossPrHistorySize - 2); i >= 0; i--) {
539 _lossPrHistory[i + 1].lossPr255 = _lossPrHistory[i].lossPr255;
540 _lossPrHistory[i + 1].timeMs = _lossPrHistory[i].timeMs;
541 }
542 }
543 if (_shortMaxLossPr255 == 0) {
544 _shortMaxLossPr255 = lossPr255;
545 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000546
philipel9d3ab612015-12-21 04:12:39 -0800547 _lossPrHistory[0].lossPr255 = _shortMaxLossPr255;
548 _lossPrHistory[0].timeMs = now;
549 _shortMaxLossPr255 = 0;
550 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000551}
552
philipel9d3ab612015-12-21 04:12:39 -0800553uint8_t VCMLossProtectionLogic::MaxFilteredLossPr(int64_t nowMs) const {
554 uint8_t maxFound = _shortMaxLossPr255;
555 if (_lossPrHistory[0].timeMs == -1) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000556 return maxFound;
philipel9d3ab612015-12-21 04:12:39 -0800557 }
558 for (int32_t i = 0; i < kLossPrHistorySize; i++) {
559 if (_lossPrHistory[i].timeMs == -1) {
560 break;
561 }
562 if (nowMs - _lossPrHistory[i].timeMs >
563 kLossPrHistorySize * kLossPrShortFilterWinMs) {
564 // This sample (and all samples after this) is too old
565 break;
566 }
567 if (_lossPrHistory[i].lossPr255 > maxFound) {
568 // This sample is the largest one this far into the history
569 maxFound = _lossPrHistory[i].lossPr255;
570 }
571 }
572 return maxFound;
niklase@google.com470e71d2011-07-07 08:21:25 +0000573}
574
philipel9d3ab612015-12-21 04:12:39 -0800575uint8_t VCMLossProtectionLogic::FilteredLoss(int64_t nowMs,
576 FilterPacketLossMode filter_mode,
577 uint8_t lossPr255) {
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000578 // Update the max window filter.
579 UpdateMaxLossHistory(lossPr255, nowMs);
580
581 // Update the recursive average filter.
philipel9d3ab612015-12-21 04:12:39 -0800582 _lossPr255.Apply(static_cast<float>(nowMs - _lastPrUpdateT),
583 static_cast<float>(lossPr255));
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000584 _lastPrUpdateT = nowMs;
585
586 // Filtered loss: default is received loss (no filtering).
pbos@webrtc.org7b859cc2013-04-02 15:54:38 +0000587 uint8_t filtered_loss = lossPr255;
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000588
589 switch (filter_mode) {
590 case kNoFilter:
591 break;
592 case kAvgFilter:
minyue@webrtc.org74aaf292014-07-16 21:28:26 +0000593 filtered_loss = static_cast<uint8_t>(_lossPr255.filtered() + 0.5);
marpan@webrtc.org2dad3fb2012-01-09 18:18:36 +0000594 break;
595 case kMaxFilter:
596 filtered_loss = MaxFilteredLossPr(nowMs);
597 break;
598 }
599
600 return filtered_loss;
niklase@google.com470e71d2011-07-07 08:21:25 +0000601}
602
philipel9d3ab612015-12-21 04:12:39 -0800603void VCMLossProtectionLogic::UpdateFilteredLossPr(uint8_t packetLossEnc) {
604 _lossPr = static_cast<float>(packetLossEnc) / 255.0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000605}
606
philipel9d3ab612015-12-21 04:12:39 -0800607void VCMLossProtectionLogic::UpdateBitRate(float bitRate) {
608 _bitRate = bitRate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000609}
610
philipel9d3ab612015-12-21 04:12:39 -0800611void VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets,
612 int64_t nowMs) {
613 _packetsPerFrame.Apply(static_cast<float>(nowMs - _lastPacketPerFrameUpdateT),
614 nPackets);
615 _lastPacketPerFrameUpdateT = nowMs;
niklase@google.com470e71d2011-07-07 08:21:25 +0000616}
617
philipel9d3ab612015-12-21 04:12:39 -0800618void VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets,
619 int64_t nowMs) {
620 _packetsPerFrameKey.Apply(
621 static_cast<float>(nowMs - _lastPacketPerFrameUpdateTKey), nPackets);
622 _lastPacketPerFrameUpdateTKey = nowMs;
niklase@google.com470e71d2011-07-07 08:21:25 +0000623}
624
philipel9d3ab612015-12-21 04:12:39 -0800625void VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize) {
626 _keyFrameSize = keyFrameSize;
niklase@google.com470e71d2011-07-07 08:21:25 +0000627}
628
philipel9d3ab612015-12-21 04:12:39 -0800629void VCMLossProtectionLogic::UpdateFrameSize(uint16_t width, uint16_t height) {
630 _codecWidth = width;
631 _codecHeight = height;
niklase@google.com470e71d2011-07-07 08:21:25 +0000632}
633
mikhal@webrtc.org0e7d9d82011-12-19 19:04:49 +0000634void VCMLossProtectionLogic::UpdateNumLayers(int numLayers) {
635 _numLayers = (numLayers == 0) ? 1 : numLayers;
636}
637
philipel9d3ab612015-12-21 04:12:39 -0800638bool VCMLossProtectionLogic::UpdateMethod() {
639 if (!_selectedMethod)
640 return false;
641 _currentParameters.rtt = _rtt;
642 _currentParameters.lossPr = _lossPr;
643 _currentParameters.bitRate = _bitRate;
644 _currentParameters.frameRate = _frameRate; // rename actual frame rate?
645 _currentParameters.keyFrameSize = _keyFrameSize;
646 _currentParameters.fecRateDelta = _fecRateDelta;
647 _currentParameters.fecRateKey = _fecRateKey;
648 _currentParameters.packetsPerFrame = _packetsPerFrame.filtered();
649 _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.filtered();
650 _currentParameters.codecWidth = _codecWidth;
651 _currentParameters.codecHeight = _codecHeight;
652 _currentParameters.numLayers = _numLayers;
653 return _selectedMethod->UpdateParameters(&_currentParameters);
niklase@google.com470e71d2011-07-07 08:21:25 +0000654}
655
philipel9d3ab612015-12-21 04:12:39 -0800656VCMProtectionMethod* VCMLossProtectionLogic::SelectedMethod() const {
657 return _selectedMethod.get();
niklase@google.com470e71d2011-07-07 08:21:25 +0000658}
659
pbos@webrtc.orgcade82c2015-03-12 10:39:24 +0000660VCMProtectionMethodEnum VCMLossProtectionLogic::SelectedType() const {
pbosba8c15b2015-07-14 09:36:34 -0700661 return _selectedMethod ? _selectedMethod->Type() : kNone;
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000662}
663
philipel9d3ab612015-12-21 04:12:39 -0800664void VCMLossProtectionLogic::Reset(int64_t nowMs) {
665 _lastPrUpdateT = nowMs;
666 _lastPacketPerFrameUpdateT = nowMs;
667 _lastPacketPerFrameUpdateTKey = nowMs;
668 _lossPr255.Reset(0.9999f);
669 _packetsPerFrame.Reset(0.9999f);
670 _fecRateDelta = _fecRateKey = 0;
671 for (int32_t i = 0; i < kLossPrHistorySize; i++) {
672 _lossPrHistory[i].lossPr255 = 0;
673 _lossPrHistory[i].timeMs = -1;
674 }
675 _shortMaxLossPr255 = 0;
676 Release();
mikhal@webrtc.orga057a952011-08-26 21:17:34 +0000677}
678
pbosba8c15b2015-07-14 09:36:34 -0700679void VCMLossProtectionLogic::Release() {
680 _selectedMethod.reset();
niklase@google.com470e71d2011-07-07 08:21:25 +0000681}
682
stefan@webrtc.orga64300a2013-03-04 15:24:40 +0000683} // namespace media_optimization
684} // namespace webrtc