1 /*
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
11 #include "modules/video_coding/utility/frame_dropper.h"
12
13 #include <algorithm>
14
15 namespace webrtc {
16
17 namespace {
18
19 const float kDefaultFrameSizeAlpha = 0.9f;
20 const float kDefaultKeyFrameRatioAlpha = 0.99f;
21 // 1 key frame every 10th second in 30 fps.
22 const float kDefaultKeyFrameRatioValue = 1 / 300.0f;
23
24 const float kDefaultDropRatioAlpha = 0.9f;
25 const float kDefaultDropRatioValue = 0.96f;
26 // Maximum duration over which frames are continuously dropped.
27 const float kDefaultMaxDropDurationSecs = 4.0f;
28
29 // Default target bitrate.
30 // TODO(isheriff): Should this be higher to avoid dropping too many packets when
31 // the bandwidth is unknown at the start ?
32 const float kDefaultTargetBitrateKbps = 300.0f;
33 const float kDefaultIncomingFrameRate = 30;
34 const float kLeakyBucketSizeSeconds = 0.5f;
35
36 // A delta frame that is bigger than |kLargeDeltaFactor| times the average
37 // delta frame is a large frame that is spread out for accumulation.
38 const int kLargeDeltaFactor = 3;
39
40 // Cap on the frame size accumulator to prevent excessive drops.
41 const float kAccumulatorCapBufferSizeSecs = 3.0f;
42 } // namespace
43
FrameDropper()44 FrameDropper::FrameDropper()
45 : key_frame_ratio_(kDefaultKeyFrameRatioAlpha),
46 delta_frame_size_avg_kbits_(kDefaultFrameSizeAlpha),
47 drop_ratio_(kDefaultDropRatioAlpha, kDefaultDropRatioValue),
48 enabled_(true),
49 max_drop_duration_secs_(kDefaultMaxDropDurationSecs) {
50 Reset();
51 }
52
Reset()53 void FrameDropper::Reset() {
54 key_frame_ratio_.Reset(kDefaultKeyFrameRatioAlpha);
55 key_frame_ratio_.Apply(1.0f, kDefaultKeyFrameRatioValue);
56 delta_frame_size_avg_kbits_.Reset(kDefaultFrameSizeAlpha);
57
58 accumulator_ = 0.0f;
59 accumulator_max_ = kDefaultTargetBitrateKbps / 2;
60 target_bitrate_ = kDefaultTargetBitrateKbps;
61 incoming_frame_rate_ = kDefaultIncomingFrameRate;
62
63 large_frame_accumulation_count_ = 0;
64 large_frame_accumulation_chunk_size_ = 0;
65 large_frame_accumulation_spread_ = 0.5 * kDefaultIncomingFrameRate;
66
67 drop_next_ = false;
68 drop_ratio_.Reset(0.9f);
69 drop_ratio_.Apply(0.0f, 0.0f);
70 drop_count_ = 0;
71 was_below_max_ = true;
72 }
73
Enable(bool enable)74 void FrameDropper::Enable(bool enable) {
75 enabled_ = enable;
76 }
77
Fill(size_t framesize_bytes,bool delta_frame)78 void FrameDropper::Fill(size_t framesize_bytes, bool delta_frame) {
79 if (!enabled_) {
80 return;
81 }
82 float framesize_kbits = 8.0f * static_cast<float>(framesize_bytes) / 1000.0f;
83 if (!delta_frame) {
84 key_frame_ratio_.Apply(1.0, 1.0);
85 // Do not spread if we are already doing it (or we risk dropping bits that
86 // need accumulation). Given we compute the key frame ratio and spread
87 // based on that, this should not normally happen.
88 if (large_frame_accumulation_count_ == 0) {
89 if (key_frame_ratio_.filtered() > 1e-5 &&
90 1 / key_frame_ratio_.filtered() < large_frame_accumulation_spread_) {
91 large_frame_accumulation_count_ =
92 static_cast<int32_t>(1 / key_frame_ratio_.filtered() + 0.5);
93 } else {
94 large_frame_accumulation_count_ =
95 static_cast<int32_t>(large_frame_accumulation_spread_ + 0.5);
96 }
97 large_frame_accumulation_chunk_size_ =
98 framesize_kbits / large_frame_accumulation_count_;
99 framesize_kbits = 0;
100 }
101 } else {
102 // Identify if it is an unusually large delta frame and spread accumulation
103 // if that is the case.
104 if (delta_frame_size_avg_kbits_.filtered() != -1 &&
105 (framesize_kbits >
106 kLargeDeltaFactor * delta_frame_size_avg_kbits_.filtered()) &&
107 large_frame_accumulation_count_ == 0) {
108 large_frame_accumulation_count_ =
109 static_cast<int32_t>(large_frame_accumulation_spread_ + 0.5);
110 large_frame_accumulation_chunk_size_ =
111 framesize_kbits / large_frame_accumulation_count_;
112 framesize_kbits = 0;
113 } else {
114 delta_frame_size_avg_kbits_.Apply(1, framesize_kbits);
115 }
116 key_frame_ratio_.Apply(1.0, 0.0);
117 }
118 // Change the level of the accumulator (bucket)
119 accumulator_ += framesize_kbits;
120 CapAccumulator();
121 }
122
Leak(uint32_t input_framerate)123 void FrameDropper::Leak(uint32_t input_framerate) {
124 if (!enabled_) {
125 return;
126 }
127 if (input_framerate < 1) {
128 return;
129 }
130 if (target_bitrate_ < 0.0f) {
131 return;
132 }
133 // Add lower bound for large frame accumulation spread.
134 large_frame_accumulation_spread_ = std::max(0.5 * input_framerate, 5.0);
135 // Expected bits per frame based on current input frame rate.
136 float expected_bits_per_frame = target_bitrate_ / input_framerate;
137 if (large_frame_accumulation_count_ > 0) {
138 expected_bits_per_frame -= large_frame_accumulation_chunk_size_;
139 --large_frame_accumulation_count_;
140 }
141 accumulator_ -= expected_bits_per_frame;
142 if (accumulator_ < 0.0f) {
143 accumulator_ = 0.0f;
144 }
145 UpdateRatio();
146 }
147
UpdateRatio()148 void FrameDropper::UpdateRatio() {
149 if (accumulator_ > 1.3f * accumulator_max_) {
150 // Too far above accumulator max, react faster.
151 drop_ratio_.UpdateBase(0.8f);
152 } else {
153 // Go back to normal reaction.
154 drop_ratio_.UpdateBase(0.9f);
155 }
156 if (accumulator_ > accumulator_max_) {
157 // We are above accumulator max, and should ideally drop a frame. Increase
158 // the drop_ratio_ and drop the frame later.
159 if (was_below_max_) {
160 drop_next_ = true;
161 }
162 drop_ratio_.Apply(1.0f, 1.0f);
163 drop_ratio_.UpdateBase(0.9f);
164 } else {
165 drop_ratio_.Apply(1.0f, 0.0f);
166 }
167 was_below_max_ = accumulator_ < accumulator_max_;
168 }
169
170 // This function signals when to drop frames to the caller. It makes use of the
171 // drop_ratio_ to smooth out the drops over time.
DropFrame()172 bool FrameDropper::DropFrame() {
173 if (!enabled_) {
174 return false;
175 }
176 if (drop_next_) {
177 drop_next_ = false;
178 drop_count_ = 0;
179 }
180
181 if (drop_ratio_.filtered() >= 0.5f) { // Drops per keep
182 // Limit is the number of frames we should drop between each kept frame
183 // to keep our drop ratio. limit is positive in this case.
184 float denom = 1.0f - drop_ratio_.filtered();
185 if (denom < 1e-5) {
186 denom = 1e-5f;
187 }
188 int32_t limit = static_cast<int32_t>(1.0f / denom - 1.0f + 0.5f);
189 // Put a bound on the max amount of dropped frames between each kept
190 // frame, in terms of frame rate and window size (secs).
191 int max_limit =
192 static_cast<int>(incoming_frame_rate_ * max_drop_duration_secs_);
193 if (limit > max_limit) {
194 limit = max_limit;
195 }
196 if (drop_count_ < 0) {
197 // Reset the drop_count_ since it was negative and should be positive.
198 drop_count_ = -drop_count_;
199 }
200 if (drop_count_ < limit) {
201 // As long we are below the limit we should drop frames.
202 drop_count_++;
203 return true;
204 } else {
205 // Only when we reset drop_count_ a frame should be kept.
206 drop_count_ = 0;
207 return false;
208 }
209 } else if (drop_ratio_.filtered() > 0.0f &&
210 drop_ratio_.filtered() < 0.5f) { // Keeps per drop
211 // Limit is the number of frames we should keep between each drop
212 // in order to keep the drop ratio. limit is negative in this case,
213 // and the drop_count_ is also negative.
214 float denom = drop_ratio_.filtered();
215 if (denom < 1e-5) {
216 denom = 1e-5f;
217 }
218 int32_t limit = -static_cast<int32_t>(1.0f / denom - 1.0f + 0.5f);
219 if (drop_count_ > 0) {
220 // Reset the drop_count_ since we have a positive
221 // drop_count_, and it should be negative.
222 drop_count_ = -drop_count_;
223 }
224 if (drop_count_ > limit) {
225 if (drop_count_ == 0) {
226 // Drop frames when we reset drop_count_.
227 drop_count_--;
228 return true;
229 } else {
230 // Keep frames as long as we haven't reached limit.
231 drop_count_--;
232 return false;
233 }
234 } else {
235 drop_count_ = 0;
236 return false;
237 }
238 }
239 drop_count_ = 0;
240 return false;
241 }
242
SetRates(float bitrate,float incoming_frame_rate)243 void FrameDropper::SetRates(float bitrate, float incoming_frame_rate) {
244 // Bit rate of -1 means infinite bandwidth.
245 accumulator_max_ = bitrate * kLeakyBucketSizeSeconds;
246 if (target_bitrate_ > 0.0f && bitrate < target_bitrate_ &&
247 accumulator_ > accumulator_max_) {
248 // Rescale the accumulator level if the accumulator max decreases
249 accumulator_ = bitrate / target_bitrate_ * accumulator_;
250 }
251 target_bitrate_ = bitrate;
252 CapAccumulator();
253 incoming_frame_rate_ = incoming_frame_rate;
254 }
255
256 // Put a cap on the accumulator, i.e., don't let it grow beyond some level.
257 // This is a temporary fix for screencasting where very large frames from
258 // encoder will cause very slow response (too many frame drops).
259 // TODO(isheriff): Remove this now that large delta frames are also spread out ?
CapAccumulator()260 void FrameDropper::CapAccumulator() {
261 float max_accumulator = target_bitrate_ * kAccumulatorCapBufferSizeSecs;
262 if (accumulator_ > max_accumulator) {
263 accumulator_ = max_accumulator;
264 }
265 }
266 } // namespace webrtc
267