Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(826)

Side by Side Diff: webrtc/modules/video_coding/utility/frame_dropper.cc

Issue 1750493002: Frame dropper improvements & cleanup (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Addressed comments Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. 2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 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 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 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 #include "webrtc/modules/video_coding/utility/frame_dropper.h" 11 #include "webrtc/modules/video_coding/utility/frame_dropper.h"
12 12
13 #include <algorithm>
14
15 #include "webrtc/base/logging.h"
13 #include "webrtc/system_wrappers/include/trace.h" 16 #include "webrtc/system_wrappers/include/trace.h"
14 17
15 namespace webrtc { 18 namespace webrtc {
16 19
17 const float kDefaultKeyFrameSizeAvgKBits = 0.9f; 20 namespace {
18 const float kDefaultKeyFrameRatio = 0.99f; 21
22 const float kDefaultFrameSizeAlpha = 0.9f;
23 const float kDefaultKeyFrameRatioAlpha = 0.99f;
24 // 1 key frame every 10th second in 30 fps.
25 const float kDefaultKeyFrameRatioValue = 1 / 300.0f;
26
19 const float kDefaultDropRatioAlpha = 0.9f; 27 const float kDefaultDropRatioAlpha = 0.9f;
20 const float kDefaultDropRatioMax = 0.96f; 28 const float kDefaultDropRatioValue = 0.96f;
21 const float kDefaultMaxTimeToDropFrames = 4.0f; // In seconds. 29 // Maximum duration over which frames are continuously dropped.
30 const float kDefaultMaxDropDurationSecs = 4.0f;
31
32 // Default target bitrate.
33 // TODO(isheriff): Should this be higher to avoid dropping too many packets when
34 // the bandwidth is unknown at the start ?
35 const float kDefaultTargetBitrateKbps = 300.0f;
36 const float kDefaultIncomingFrameRate = 30;
37 const float kLeakyBucketSizeSeconds = 0.5f;
38
39 // A delta frame that is bigger than |kLargeDeltaFactor| times the average
40 // delta frame is a large frame that is spread out for accumulation.
41 const int kLargeDeltaFactor = 3;
42
43 // Cap on the frame size accumulator to prevent excessive drops.
44 const float kAccumulatorCapBufferSizeSecs = 3.0f;
45 } // namespace
22 46
23 FrameDropper::FrameDropper() 47 FrameDropper::FrameDropper()
24 : _keyFrameSizeAvgKbits(kDefaultKeyFrameSizeAvgKBits), 48 : key_frame_ratio_(kDefaultKeyFrameRatioAlpha),
25 _keyFrameRatio(kDefaultKeyFrameRatio), 49 delta_frame_size_avg_kbits_(kDefaultFrameSizeAlpha),
26 _dropRatio(kDefaultDropRatioAlpha, kDefaultDropRatioMax), 50 drop_ratio_(kDefaultDropRatioAlpha, kDefaultDropRatioValue),
27 _enabled(true), 51 enabled_(true),
28 _max_time_drops(kDefaultMaxTimeToDropFrames) { 52 max_drop_duration_secs_(kDefaultMaxDropDurationSecs) {
29 Reset(); 53 Reset();
30 } 54 }
31 55
32 FrameDropper::FrameDropper(float max_time_drops) 56 FrameDropper::FrameDropper(float max_drop_duration_secs)
33 : _keyFrameSizeAvgKbits(kDefaultKeyFrameSizeAvgKBits), 57 : key_frame_ratio_(kDefaultKeyFrameRatioAlpha),
34 _keyFrameRatio(kDefaultKeyFrameRatio), 58 delta_frame_size_avg_kbits_(kDefaultFrameSizeAlpha),
35 _dropRatio(kDefaultDropRatioAlpha, kDefaultDropRatioMax), 59 drop_ratio_(kDefaultDropRatioAlpha, kDefaultDropRatioValue),
36 _enabled(true), 60 enabled_(true),
37 _max_time_drops(max_time_drops) { 61 max_drop_duration_secs_(max_drop_duration_secs) {
38 Reset(); 62 Reset();
39 } 63 }
40 64
41 void FrameDropper::Reset() { 65 void FrameDropper::Reset() {
42 _keyFrameRatio.Reset(0.99f); 66 key_frame_ratio_.Reset(kDefaultKeyFrameRatioAlpha);
43 _keyFrameRatio.Apply( 67 key_frame_ratio_.Apply(1.0f, kDefaultKeyFrameRatioValue);
44 1.0f, 1.0f / 300.0f); // 1 key frame every 10th second in 30 fps 68 delta_frame_size_avg_kbits_.Reset(kDefaultFrameSizeAlpha);
45 _keyFrameSizeAvgKbits.Reset(0.9f); 69
46 _keyFrameCount = 0; 70 accumulator_ = 0.0f;
47 _accumulator = 0.0f; 71 accumulator_max_ = kDefaultTargetBitrateKbps / 2;
48 _accumulatorMax = 150.0f; // assume 300 kb/s and 0.5 s window 72 target_bitrate_ = kDefaultTargetBitrateKbps;
49 _targetBitRate = 300.0f; 73 incoming_frame_rate_ = kDefaultIncomingFrameRate;
50 _incoming_frame_rate = 30; 74
51 _keyFrameSpreadFrames = 0.5f * _incoming_frame_rate; 75 large_frame_accumulation_count_ = 0;
52 _dropNext = false; 76 large_frame_accumulation_spread_ = 0.5 * kDefaultIncomingFrameRate;
53 _dropRatio.Reset(0.9f); 77
54 _dropRatio.Apply(0.0f, 0.0f); // Initialize to 0 78 drop_next_ = false;
55 _dropCount = 0; 79 drop_ratio_.Reset(0.9f);
56 _windowSize = 0.5f; 80 drop_ratio_.Apply(0.0f, 0.0f);
57 _wasBelowMax = true; 81 drop_count_ = 0;
58 _fastMode = false; // start with normal (non-aggressive) mode 82 was_below_max_ = true;
59 // Cap for the encoder buffer level/accumulator, in secs.
60 _cap_buffer_size = 3.0f;
61 // Cap on maximum amount of dropped frames between kept frames, in secs.
62 _max_time_drops = 4.0f;
63 } 83 }
64 84
65 void FrameDropper::Enable(bool enable) { 85 void FrameDropper::Enable(bool enable) {
66 _enabled = enable; 86 enabled_ = enable;
67 } 87 }
68 88
69 void FrameDropper::Fill(size_t frameSizeBytes, bool deltaFrame) { 89 void FrameDropper::Fill(size_t framesize_bytes, bool delta_frame) {
70 if (!_enabled) { 90 if (!enabled_) {
71 return; 91 return;
72 } 92 }
73 float frameSizeKbits = 8.0f * static_cast<float>(frameSizeBytes) / 1000.0f; 93 float framesize_kbits = 8.0f * static_cast<float>(framesize_bytes) / 1000.0f;
74 if (!deltaFrame && 94 if (!delta_frame) {
75 !_fastMode) { // fast mode does not treat key-frames any different 95 key_frame_ratio_.Apply(1.0, 1.0);
76 _keyFrameSizeAvgKbits.Apply(1, frameSizeKbits); 96 // Do not spread if we are already doing it (or we risk dropping bits that
77 _keyFrameRatio.Apply(1.0, 1.0); 97 // need accumulation). Given we compute the key
78 if (frameSizeKbits > _keyFrameSizeAvgKbits.filtered()) { 98 // frame ratio and spread based on that, this should not normally happen.
79 // Remove the average key frame size since we 99 if (large_frame_accumulation_count_ == 0) {
80 // compensate for key frames when adding delta 100 if (key_frame_ratio_.filtered() > 1e-5 &&
81 // frames. 101 1 / key_frame_ratio_.filtered() < large_frame_accumulation_spread_) {
82 frameSizeKbits -= _keyFrameSizeAvgKbits.filtered(); 102 large_frame_accumulation_count_ =
103 static_cast<int32_t>(1 / key_frame_ratio_.filtered() + 0.5);
104 } else {
105 large_frame_accumulation_count_ =
106 static_cast<int32_t>(large_frame_accumulation_spread_ + 0.5);
107 }
108 large_frame_accumulation_chunk_size_ =
109 framesize_kbits / large_frame_accumulation_count_;
110 framesize_kbits = 0;
111 }
112 } else {
113 // Identify if it is an unusually large delta frame and spread accumulation
114 // if that is the case.
115 if (delta_frame_size_avg_kbits_.filtered() != -1 &&
116 (framesize_kbits >
117 kLargeDeltaFactor * delta_frame_size_avg_kbits_.filtered()) &&
118 large_frame_accumulation_count_ == 0) {
119 large_frame_accumulation_count_ =
120 static_cast<int32_t>(large_frame_accumulation_spread_ + 0.5);
121 large_frame_accumulation_chunk_size_ =
122 framesize_kbits / large_frame_accumulation_count_;
123 framesize_kbits = 0;
83 } else { 124 } else {
84 // Shouldn't be negative, so zero is the lower bound. 125 delta_frame_size_avg_kbits_.Apply(1, framesize_kbits);
85 frameSizeKbits = 0; 126 }
86 } 127 key_frame_ratio_.Apply(1.0, 0.0);
87 if (_keyFrameRatio.filtered() > 1e-5 &&
88 1 / _keyFrameRatio.filtered() < _keyFrameSpreadFrames) {
89 // We are sending key frames more often than our upper bound for
90 // how much we allow the key frame compensation to be spread
91 // out in time. Therefor we must use the key frame ratio rather
92 // than keyFrameSpreadFrames.
93 _keyFrameCount =
94 static_cast<int32_t>(1 / _keyFrameRatio.filtered() + 0.5);
95 } else {
96 // Compensate for the key frame the following frames
97 _keyFrameCount = static_cast<int32_t>(_keyFrameSpreadFrames + 0.5);
98 }
99 } else {
100 // Decrease the keyFrameRatio
101 _keyFrameRatio.Apply(1.0, 0.0);
102 } 128 }
103 // Change the level of the accumulator (bucket) 129 // Change the level of the accumulator (bucket)
104 _accumulator += frameSizeKbits; 130 accumulator_ += framesize_kbits;
105 CapAccumulator(); 131 CapAccumulator();
106 } 132 LOG(LS_VERBOSE) << "FILL acc " << accumulator_ << " max " << accumulator_max_
107 133 << " count " << large_frame_accumulation_count_ << " chunk "
108 void FrameDropper::Leak(uint32_t inputFrameRate) { 134 << large_frame_accumulation_chunk_size_ << " spread "
109 if (!_enabled) { 135 << large_frame_accumulation_spread_ << " delta avg "
110 return; 136 << delta_frame_size_avg_kbits_.filtered() << " SIZE "
111 } 137 << framesize_kbits << "key frame ratio "
112 if (inputFrameRate < 1) { 138 << key_frame_ratio_.filtered();
113 return; 139 }
114 } 140
115 if (_targetBitRate < 0.0f) { 141 void FrameDropper::Leak(uint32_t input_framerate) {
116 return; 142 if (!enabled_) {
117 } 143 return;
118 _keyFrameSpreadFrames = 0.5f * inputFrameRate; 144 }
119 // T is the expected bits per frame (target). If all frames were the same 145 if (input_framerate < 1) {
120 // size, 146 return;
121 // we would get T bits per frame. Notice that T is also weighted to be able to 147 }
122 // force a lower frame rate if wanted. 148 if (target_bitrate_ < 0.0f) {
123 float T = _targetBitRate / inputFrameRate; 149 return;
124 if (_keyFrameCount > 0) { 150 }
125 // Perform the key frame compensation 151 // Add lower bound for large frame accumulation spread.
126 if (_keyFrameRatio.filtered() > 0 && 152 large_frame_accumulation_spread_ = std::max(0.5 * input_framerate, 5.0);
127 1 / _keyFrameRatio.filtered() < _keyFrameSpreadFrames) { 153 // Expected bits per frame based on current input frame rate.
128 T -= _keyFrameSizeAvgKbits.filtered() * _keyFrameRatio.filtered(); 154 float expected_bits_per_frame = target_bitrate_ / input_framerate;
129 } else { 155 if (large_frame_accumulation_count_ > 0) {
130 T -= _keyFrameSizeAvgKbits.filtered() / _keyFrameSpreadFrames; 156 expected_bits_per_frame -= large_frame_accumulation_chunk_size_;
131 } 157 --large_frame_accumulation_count_;
132 _keyFrameCount--; 158 }
133 } 159 accumulator_ -= expected_bits_per_frame;
134 _accumulator -= T; 160 if (accumulator_ < 0.0f) {
135 if (_accumulator < 0.0f) { 161 accumulator_ = 0.0f;
136 _accumulator = 0.0f; 162 }
137 } 163 LOG(LS_VERBOSE) << "LEAK acc " << accumulator_ << " max " << accumulator_max_
164 << " count " << large_frame_accumulation_count_ << " spread "
165 << large_frame_accumulation_spread_ << " delta avg "
166 << delta_frame_size_avg_kbits_.filtered();
138 UpdateRatio(); 167 UpdateRatio();
139 } 168 }
140 169
141 void FrameDropper::UpdateNack(uint32_t nackBytes) {
142 if (!_enabled) {
143 return;
144 }
145 _accumulator += static_cast<float>(nackBytes) * 8.0f / 1000.0f;
146 }
147
148 void FrameDropper::FillBucket(float inKbits, float outKbits) {
149 _accumulator += (inKbits - outKbits);
150 }
151
152 void FrameDropper::UpdateRatio() { 170 void FrameDropper::UpdateRatio() {
153 if (_accumulator > 1.3f * _accumulatorMax) { 171 if (accumulator_ > 1.3f * accumulator_max_) {
154 // Too far above accumulator max, react faster 172 // Too far above accumulator max, react faster
155 _dropRatio.UpdateBase(0.8f); 173 drop_ratio_.UpdateBase(0.8f);
156 } else { 174 } else {
157 // Go back to normal reaction 175 // Go back to normal reaction
158 _dropRatio.UpdateBase(0.9f); 176 drop_ratio_.UpdateBase(0.9f);
159 } 177 }
160 if (_accumulator > _accumulatorMax) { 178 if (accumulator_ > accumulator_max_) {
161 // We are above accumulator max, and should ideally 179 // We are above accumulator max, and should ideally
162 // drop a frame. Increase the dropRatio and drop 180 // drop a frame. Increase the dropRatio and drop
163 // the frame later. 181 // the frame later.
164 if (_wasBelowMax) { 182 if (was_below_max_) {
165 _dropNext = true; 183 drop_next_ = true;
166 } 184 }
167 if (_fastMode) { 185 drop_ratio_.Apply(1.0f, 1.0f);
168 // always drop in aggressive mode 186 drop_ratio_.UpdateBase(0.9f);
169 _dropNext = true;
170 }
171
172 _dropRatio.Apply(1.0f, 1.0f);
173 _dropRatio.UpdateBase(0.9f);
174 } else { 187 } else {
175 _dropRatio.Apply(1.0f, 0.0f); 188 drop_ratio_.Apply(1.0f, 0.0f);
176 } 189 }
177 _wasBelowMax = _accumulator < _accumulatorMax; 190 was_below_max_ = accumulator_ < accumulator_max_;
178 } 191 }
179 192
180 // This function signals when to drop frames to the caller. It makes use of the 193 // This function signals when to drop frames to the caller. It makes use of the
181 // dropRatio 194 // dropRatio
182 // to smooth out the drops over time. 195 // to smooth out the drops over time.
183 bool FrameDropper::DropFrame() { 196 bool FrameDropper::DropFrame() {
184 if (!_enabled) { 197 if (!enabled_) {
185 return false; 198 return false;
186 } 199 }
187 if (_dropNext) { 200 if (drop_next_) {
188 _dropNext = false; 201 drop_next_ = false;
189 _dropCount = 0; 202 drop_count_ = 0;
190 } 203 }
191 204 LOG(LS_VERBOSE) << " drop_ratio_ " << drop_ratio_.filtered()
192 if (_dropRatio.filtered() >= 0.5f) { // Drops per keep 205 << " drop_count_ " << drop_count_;
206
207 if (drop_ratio_.filtered() >= 0.5f) { // Drops per keep
193 // limit is the number of frames we should drop between each kept frame 208 // limit is the number of frames we should drop between each kept frame
194 // to keep our drop ratio. limit is positive in this case. 209 // to keep our drop ratio. limit is positive in this case.
195 float denom = 1.0f - _dropRatio.filtered(); 210 float denom = 1.0f - drop_ratio_.filtered();
196 if (denom < 1e-5) { 211 if (denom < 1e-5) {
197 denom = 1e-5f; 212 denom = 1e-5f;
198 } 213 }
199 int32_t limit = static_cast<int32_t>(1.0f / denom - 1.0f + 0.5f); 214 int32_t limit = static_cast<int32_t>(1.0f / denom - 1.0f + 0.5f);
200 // Put a bound on the max amount of dropped frames between each kept 215 // Put a bound on the max amount of dropped frames between each kept
201 // frame, in terms of frame rate and window size (secs). 216 // frame, in terms of frame rate and window size (secs).
202 int max_limit = static_cast<int>(_incoming_frame_rate * _max_time_drops); 217 int max_limit =
218 static_cast<int>(incoming_frame_rate_ * max_drop_duration_secs_);
203 if (limit > max_limit) { 219 if (limit > max_limit) {
204 limit = max_limit; 220 limit = max_limit;
205 } 221 }
206 if (_dropCount < 0) { 222 if (drop_count_ < 0) {
207 // Reset the _dropCount since it was negative and should be positive. 223 // Reset the drop_count_ since it was negative and should be positive.
208 if (_dropRatio.filtered() > 0.4f) { 224 drop_count_ = -drop_count_;
209 _dropCount = -_dropCount; 225 }
210 } else { 226 if (drop_count_ < limit) {
211 _dropCount = 0;
212 }
213 }
214 if (_dropCount < limit) {
215 // As long we are below the limit we should drop frames. 227 // As long we are below the limit we should drop frames.
216 _dropCount++; 228 drop_count_++;
217 return true; 229 return true;
218 } else { 230 } else {
219 // Only when we reset _dropCount a frame should be kept. 231 // Only when we reset drop_count_ a frame should be kept.
220 _dropCount = 0; 232 drop_count_ = 0;
221 return false; 233 return false;
222 } 234 }
223 } else if (_dropRatio.filtered() > 0.0f && 235 } else if (drop_ratio_.filtered() > 0.0f &&
224 _dropRatio.filtered() < 0.5f) { // Keeps per drop 236 drop_ratio_.filtered() < 0.5f) { // Keeps per drop
225 // limit is the number of frames we should keep between each drop 237 // limit is the number of frames we should keep between each drop
226 // in order to keep the drop ratio. limit is negative in this case, 238 // in order to keep the drop ratio. limit is negative in this case,
227 // and the _dropCount is also negative. 239 // and the drop_count_ is also negative.
228 float denom = _dropRatio.filtered(); 240 float denom = drop_ratio_.filtered();
229 if (denom < 1e-5) { 241 if (denom < 1e-5) {
230 denom = 1e-5f; 242 denom = 1e-5f;
231 } 243 }
232 int32_t limit = -static_cast<int32_t>(1.0f / denom - 1.0f + 0.5f); 244 int32_t limit = -static_cast<int32_t>(1.0f / denom - 1.0f + 0.5f);
233 if (_dropCount > 0) { 245 if (drop_count_ > 0) {
234 // Reset the _dropCount since we have a positive 246 // Reset the drop_count_ since we have a positive
235 // _dropCount, and it should be negative. 247 // drop_count_, and it should be negative.
236 if (_dropRatio.filtered() < 0.6f) { 248 drop_count_ = -drop_count_;
237 _dropCount = -_dropCount; 249 }
238 } else { 250 if (drop_count_ > limit) {
239 _dropCount = 0; 251 if (drop_count_ == 0) {
240 } 252 // Drop frames when we reset drop_count_.
241 } 253 drop_count_--;
242 if (_dropCount > limit) {
243 if (_dropCount == 0) {
244 // Drop frames when we reset _dropCount.
245 _dropCount--;
246 return true; 254 return true;
247 } else { 255 } else {
248 // Keep frames as long as we haven't reached limit. 256 // Keep frames as long as we haven't reached limit.
249 _dropCount--; 257 drop_count_--;
250 return false; 258 return false;
251 } 259 }
252 } else { 260 } else {
253 _dropCount = 0; 261 drop_count_ = 0;
254 return false; 262 return false;
255 } 263 }
256 } 264 }
257 _dropCount = 0; 265 drop_count_ = 0;
258 return false; 266 return false;
259 267 }
260 // A simpler version, unfiltered and quicker 268
261 // bool dropNext = _dropNext; 269 void FrameDropper::SetRates(float bitrate, float incoming_frame_rate) {
262 // _dropNext = false;
263 // return dropNext;
264 }
265
266 void FrameDropper::SetRates(float bitRate, float incoming_frame_rate) {
267 // Bit rate of -1 means infinite bandwidth. 270 // Bit rate of -1 means infinite bandwidth.
268 _accumulatorMax = bitRate * _windowSize; // bitRate * windowSize (in seconds) 271 accumulator_max_ = bitrate * kLeakyBucketSizeSeconds;
269 if (_targetBitRate > 0.0f && bitRate < _targetBitRate && 272 if (target_bitrate_ > 0.0f && bitrate < target_bitrate_ &&
270 _accumulator > _accumulatorMax) { 273 accumulator_ > accumulator_max_) {
271 // Rescale the accumulator level if the accumulator max decreases 274 // Rescale the accumulator level if the accumulator max decreases
272 _accumulator = bitRate / _targetBitRate * _accumulator; 275 accumulator_ = bitrate / target_bitrate_ * accumulator_;
273 } 276 }
274 _targetBitRate = bitRate; 277 target_bitrate_ = bitrate;
275 CapAccumulator(); 278 CapAccumulator();
276 _incoming_frame_rate = incoming_frame_rate; 279 incoming_frame_rate_ = incoming_frame_rate;
277 }
278
279 float FrameDropper::ActualFrameRate(uint32_t inputFrameRate) const {
280 if (!_enabled) {
281 return static_cast<float>(inputFrameRate);
282 }
283 return inputFrameRate * (1.0f - _dropRatio.filtered());
284 } 280 }
285 281
286 // Put a cap on the accumulator, i.e., don't let it grow beyond some level. 282 // Put a cap on the accumulator, i.e., don't let it grow beyond some level.
287 // This is a temporary fix for screencasting where very large frames from 283 // This is a temporary fix for screencasting where very large frames from
288 // encoder will cause very slow response (too many frame drops). 284 // encoder will cause very slow response (too many frame drops).
285 // TODO(isheriff): Remove this now that large delta frames are also spread out ?
289 void FrameDropper::CapAccumulator() { 286 void FrameDropper::CapAccumulator() {
290 float max_accumulator = _targetBitRate * _cap_buffer_size; 287 float max_accumulator = target_bitrate_ * kAccumulatorCapBufferSizeSecs;
291 if (_accumulator > max_accumulator) { 288 if (accumulator_ > max_accumulator) {
292 _accumulator = max_accumulator; 289 accumulator_ = max_accumulator;
293 } 290 }
294 } 291 }
295 } // namespace webrtc 292 } // namespace webrtc
OLDNEW
« no previous file with comments | « webrtc/modules/video_coding/utility/frame_dropper.h ('k') | webrtc/modules/video_coding/utility/frame_dropper_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698