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