blob: b871022d3b0d0e5e9b07a25002d1c7aaedabe64e [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3 *
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
stefan@webrtc.orgeb917922013-02-18 14:40:18 +000011#include "webrtc/modules/video_coding/utility/include/frame_dropper.h"
12
13#include "webrtc/system_wrappers/interface/trace.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000014
15namespace webrtc
16{
17
stefan@webrtc.orgeb917922013-02-18 14:40:18 +000018FrameDropper::FrameDropper()
niklase@google.com470e71d2011-07-07 08:21:25 +000019:
niklase@google.com470e71d2011-07-07 08:21:25 +000020_keyFrameSizeAvgKbits(0.9f),
21_keyFrameRatio(0.99f),
mikhal@webrtc.org57a00492013-02-11 21:23:23 +000022_dropRatio(0.9f, 0.96f),
23_enabled(true)
niklase@google.com470e71d2011-07-07 08:21:25 +000024{
25 Reset();
26}
27
28void
stefan@webrtc.orgeb917922013-02-18 14:40:18 +000029FrameDropper::Reset()
niklase@google.com470e71d2011-07-07 08:21:25 +000030{
31 _keyFrameRatio.Reset(0.99f);
32 _keyFrameRatio.Apply(1.0f, 1.0f/300.0f); // 1 key frame every 10th second in 30 fps
33 _keyFrameSizeAvgKbits.Reset(0.9f);
34 _keyFrameCount = 0;
35 _accumulator = 0.0f;
36 _accumulatorMax = 150.0f; // assume 300 kb/s and 0.5 s window
37 _targetBitRate = 300.0f;
marpan@webrtc.org1dd8d4b2012-10-09 20:43:56 +000038 _incoming_frame_rate = 30;
39 _keyFrameSpreadFrames = 0.5f * _incoming_frame_rate;
niklase@google.com470e71d2011-07-07 08:21:25 +000040 _dropNext = false;
41 _dropRatio.Reset(0.9f);
42 _dropRatio.Apply(0.0f, 0.0f); // Initialize to 0
43 _dropCount = 0;
44 _windowSize = 0.5f;
45 _wasBelowMax = true;
niklase@google.com470e71d2011-07-07 08:21:25 +000046 _fastMode = false; // start with normal (non-aggressive) mode
marpan@webrtc.org1dd8d4b2012-10-09 20:43:56 +000047 // Cap for the encoder buffer level/accumulator, in secs.
48 _cap_buffer_size = 3.0f;
49 // Cap on maximum amount of dropped frames between kept frames, in secs.
50 _max_time_drops = 4.0f;
niklase@google.com470e71d2011-07-07 08:21:25 +000051}
52
53void
stefan@webrtc.orgeb917922013-02-18 14:40:18 +000054FrameDropper::Enable(bool enable)
niklase@google.com470e71d2011-07-07 08:21:25 +000055{
56 _enabled = enable;
57}
58
59void
stefan@webrtc.orgeb917922013-02-18 14:40:18 +000060FrameDropper::Fill(WebRtc_UWord32 frameSizeBytes, bool deltaFrame)
niklase@google.com470e71d2011-07-07 08:21:25 +000061{
62 if (!_enabled)
63 {
64 return;
65 }
66 float frameSizeKbits = 8.0f * static_cast<float>(frameSizeBytes) / 1000.0f;
67 if (!deltaFrame && !_fastMode) // fast mode does not treat key-frames any different
68 {
69 _keyFrameSizeAvgKbits.Apply(1, frameSizeKbits);
70 _keyFrameRatio.Apply(1.0, 1.0);
71 if (frameSizeKbits > _keyFrameSizeAvgKbits.Value())
72 {
73 // Remove the average key frame size since we
74 // compensate for key frames when adding delta
75 // frames.
76 frameSizeKbits -= _keyFrameSizeAvgKbits.Value();
77 }
78 else
79 {
80 // Shouldn't be negative, so zero is the lower bound.
81 frameSizeKbits = 0;
82 }
83 if (_keyFrameRatio.Value() > 1e-5 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames)
84 {
85 // We are sending key frames more often than our upper bound for
86 // how much we allow the key frame compensation to be spread
87 // out in time. Therefor we must use the key frame ratio rather
88 // than keyFrameSpreadFrames.
89 _keyFrameCount = static_cast<WebRtc_Word32>(1 / _keyFrameRatio.Value() + 0.5);
90 }
91 else
92 {
93 // Compensate for the key frame the following frames
94 _keyFrameCount = static_cast<WebRtc_Word32>(_keyFrameSpreadFrames + 0.5);
95 }
96 }
97 else
98 {
99 // Decrease the keyFrameRatio
100 _keyFrameRatio.Apply(1.0, 0.0);
101 }
102 // Change the level of the accumulator (bucket)
103 _accumulator += frameSizeKbits;
marpan@webrtc.org1dd8d4b2012-10-09 20:43:56 +0000104 CapAccumulator();
niklase@google.com470e71d2011-07-07 08:21:25 +0000105}
106
107void
stefan@webrtc.orgeb917922013-02-18 14:40:18 +0000108FrameDropper::Leak(WebRtc_UWord32 inputFrameRate)
niklase@google.com470e71d2011-07-07 08:21:25 +0000109{
110 if (!_enabled)
111 {
112 return;
113 }
114 if (inputFrameRate < 1)
115 {
116 return;
117 }
118 if (_targetBitRate < 0.0f)
119 {
120 return;
121 }
122 _keyFrameSpreadFrames = 0.5f * inputFrameRate;
123 // T is the expected bits per frame (target). If all frames were the same size,
124 // we would get T bits per frame. Notice that T is also weighted to be able to
125 // force a lower frame rate if wanted.
126 float T = _targetBitRate / inputFrameRate;
127 if (_keyFrameCount > 0)
128 {
129 // Perform the key frame compensation
130 if (_keyFrameRatio.Value() > 0 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames)
131 {
132 T -= _keyFrameSizeAvgKbits.Value() * _keyFrameRatio.Value();
133 }
134 else
135 {
136 T -= _keyFrameSizeAvgKbits.Value() / _keyFrameSpreadFrames;
137 }
138 _keyFrameCount--;
139 }
140 _accumulator -= T;
141 UpdateRatio();
niklase@google.com470e71d2011-07-07 08:21:25 +0000142}
143
144void
stefan@webrtc.orgeb917922013-02-18 14:40:18 +0000145FrameDropper::UpdateNack(WebRtc_UWord32 nackBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000146{
147 if (!_enabled)
148 {
149 return;
150 }
151 _accumulator += static_cast<float>(nackBytes) * 8.0f / 1000.0f;
152}
153
154void
stefan@webrtc.orgeb917922013-02-18 14:40:18 +0000155FrameDropper::FillBucket(float inKbits, float outKbits)
niklase@google.com470e71d2011-07-07 08:21:25 +0000156{
157 _accumulator += (inKbits - outKbits);
158}
159
160void
stefan@webrtc.orgeb917922013-02-18 14:40:18 +0000161FrameDropper::UpdateRatio()
niklase@google.com470e71d2011-07-07 08:21:25 +0000162{
163 if (_accumulator > 1.3f * _accumulatorMax)
164 {
165 // Too far above accumulator max, react faster
166 _dropRatio.UpdateBase(0.8f);
167 }
168 else
169 {
170 // Go back to normal reaction
171 _dropRatio.UpdateBase(0.9f);
172 }
173 if (_accumulator > _accumulatorMax)
174 {
175 // We are above accumulator max, and should ideally
176 // drop a frame. Increase the dropRatio and drop
177 // the frame later.
178 if (_wasBelowMax)
179 {
180 _dropNext = true;
181 }
182 if (_fastMode)
183 {
184 // always drop in aggressive mode
185 _dropNext = true;
186 }
187
188 _dropRatio.Apply(1.0f, 1.0f);
189 _dropRatio.UpdateBase(0.9f);
190 }
191 else
192 {
193 _dropRatio.Apply(1.0f, 0.0f);
194 }
195 if (_accumulator < 0.0f)
196 {
197 _accumulator = 0.0f;
198 }
199 _wasBelowMax = _accumulator < _accumulatorMax;
niklase@google.com470e71d2011-07-07 08:21:25 +0000200}
201
202// This function signals when to drop frames to the caller. It makes use of the dropRatio
203// to smooth out the drops over time.
204bool
stefan@webrtc.orgeb917922013-02-18 14:40:18 +0000205FrameDropper::DropFrame()
niklase@google.com470e71d2011-07-07 08:21:25 +0000206{
207 if (!_enabled)
208 {
209 return false;
210 }
211 if (_dropNext)
212 {
213 _dropNext = false;
214 _dropCount = 0;
215 }
216
217 if (_dropRatio.Value() >= 0.5f) // Drops per keep
218 {
219 // limit is the number of frames we should drop between each kept frame
220 // to keep our drop ratio. limit is positive in this case.
221 float denom = 1.0f - _dropRatio.Value();
222 if (denom < 1e-5)
223 {
224 denom = (float)1e-5;
225 }
226 WebRtc_Word32 limit = static_cast<WebRtc_Word32>(1.0f / denom - 1.0f + 0.5f);
marpan@webrtc.org1dd8d4b2012-10-09 20:43:56 +0000227 // Put a bound on the max amount of dropped frames between each kept
228 // frame, in terms of frame rate and window size (secs).
229 int max_limit = static_cast<int>(_incoming_frame_rate *
230 _max_time_drops);
231 if (limit > max_limit) {
232 limit = max_limit;
233 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000234 if (_dropCount < 0)
235 {
236 // Reset the _dropCount since it was negative and should be positive.
237 if (_dropRatio.Value() > 0.4f)
238 {
239 _dropCount = -_dropCount;
240 }
241 else
242 {
243 _dropCount = 0;
244 }
245 }
246 if (_dropCount < limit)
247 {
248 // As long we are below the limit we should drop frames.
249 _dropCount++;
250 return true;
251 }
252 else
253 {
254 // Only when we reset _dropCount a frame should be kept.
255 _dropCount = 0;
256 return false;
257 }
258 }
259 else if (_dropRatio.Value() > 0.0f && _dropRatio.Value() < 0.5f) // Keeps per drop
260 {
261 // limit is the number of frames we should keep between each drop
262 // in order to keep the drop ratio. limit is negative in this case,
263 // and the _dropCount is also negative.
264 float denom = _dropRatio.Value();
265 if (denom < 1e-5)
266 {
267 denom = (float)1e-5;
268 }
269 WebRtc_Word32 limit = -static_cast<WebRtc_Word32>(1.0f / denom - 1.0f + 0.5f);
270 if (_dropCount > 0)
271 {
272 // Reset the _dropCount since we have a positive
273 // _dropCount, and it should be negative.
274 if (_dropRatio.Value() < 0.6f)
275 {
276 _dropCount = -_dropCount;
277 }
278 else
279 {
280 _dropCount = 0;
281 }
282 }
283 if (_dropCount > limit)
284 {
285 if (_dropCount == 0)
286 {
287 // Drop frames when we reset _dropCount.
288 _dropCount--;
289 return true;
290 }
291 else
292 {
293 // Keep frames as long as we haven't reached limit.
294 _dropCount--;
295 return false;
296 }
297 }
298 else
299 {
300 _dropCount = 0;
301 return false;
302 }
303 }
304 _dropCount = 0;
305 return false;
306
307 // A simpler version, unfiltered and quicker
308 //bool dropNext = _dropNext;
309 //_dropNext = false;
310 //return dropNext;
311}
312
313void
stefan@webrtc.orgeb917922013-02-18 14:40:18 +0000314FrameDropper::SetRates(float bitRate, float incoming_frame_rate)
niklase@google.com470e71d2011-07-07 08:21:25 +0000315{
316 // Bit rate of -1 means infinite bandwidth.
317 _accumulatorMax = bitRate * _windowSize; // bitRate * windowSize (in seconds)
318 if (_targetBitRate > 0.0f && bitRate < _targetBitRate && _accumulator > _accumulatorMax)
319 {
320 // Rescale the accumulator level if the accumulator max decreases
321 _accumulator = bitRate / _targetBitRate * _accumulator;
322 }
323 _targetBitRate = bitRate;
marpan@webrtc.org1dd8d4b2012-10-09 20:43:56 +0000324 CapAccumulator();
325 _incoming_frame_rate = incoming_frame_rate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000326}
327
328float
stefan@webrtc.orgeb917922013-02-18 14:40:18 +0000329FrameDropper::ActualFrameRate(WebRtc_UWord32 inputFrameRate) const
niklase@google.com470e71d2011-07-07 08:21:25 +0000330{
331 if (!_enabled)
332 {
333 return static_cast<float>(inputFrameRate);
334 }
335 return inputFrameRate * (1.0f - _dropRatio.Value());
336}
337
marpan@webrtc.org1dd8d4b2012-10-09 20:43:56 +0000338// Put a cap on the accumulator, i.e., don't let it grow beyond some level.
339// This is a temporary fix for screencasting where very large frames from
340// encoder will cause very slow response (too many frame drops).
stefan@webrtc.orgeb917922013-02-18 14:40:18 +0000341void FrameDropper::CapAccumulator() {
marpan@webrtc.org1dd8d4b2012-10-09 20:43:56 +0000342 float max_accumulator = _targetBitRate * _cap_buffer_size;
343 if (_accumulator > max_accumulator) {
344 _accumulator = max_accumulator;
345 }
346}
347
niklase@google.com470e71d2011-07-07 08:21:25 +0000348}