blob: 5c518fa57ac0f34e0ceca2e204ceade3784a1385 [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/video_coding/utility/frame_dropper.h"
stefan@webrtc.orgeb917922013-02-18 14:40:18 +000012
isheriff7620be82016-03-06 23:22:33 -080013#include <algorithm>
14
philipel5908c712015-12-21 08:23:20 -080015namespace webrtc {
niklase@google.com470e71d2011-07-07 08:21:25 +000016
isheriff7620be82016-03-06 23:22:33 -080017namespace {
18
19const float kDefaultFrameSizeAlpha = 0.9f;
20const float kDefaultKeyFrameRatioAlpha = 0.99f;
21// 1 key frame every 10th second in 30 fps.
22const float kDefaultKeyFrameRatioValue = 1 / 300.0f;
23
stefan@webrtc.org84cd8e32013-03-07 13:12:32 +000024const float kDefaultDropRatioAlpha = 0.9f;
isheriff7620be82016-03-06 23:22:33 -080025const float kDefaultDropRatioValue = 0.96f;
26// Maximum duration over which frames are continuously dropped.
27const 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 ?
32const float kDefaultTargetBitrateKbps = 300.0f;
33const float kDefaultIncomingFrameRate = 30;
34const 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.
38const int kLargeDeltaFactor = 3;
39
40// Cap on the frame size accumulator to prevent excessive drops.
41const float kAccumulatorCapBufferSizeSecs = 3.0f;
42} // namespace
stefan@webrtc.org84cd8e32013-03-07 13:12:32 +000043
stefan@webrtc.orgeb917922013-02-18 14:40:18 +000044FrameDropper::FrameDropper()
isheriff7620be82016-03-06 23:22:33 -080045 : 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) {
philipel5908c712015-12-21 08:23:20 -080050 Reset();
stefan@webrtc.org84cd8e32013-03-07 13:12:32 +000051}
52
Rasmus Brandt260182d2018-09-03 12:59:51 +020053FrameDropper::~FrameDropper() = default;
54
philipel5908c712015-12-21 08:23:20 -080055void FrameDropper::Reset() {
isheriff7620be82016-03-06 23:22:33 -080056 key_frame_ratio_.Reset(kDefaultKeyFrameRatioAlpha);
57 key_frame_ratio_.Apply(1.0f, kDefaultKeyFrameRatioValue);
58 delta_frame_size_avg_kbits_.Reset(kDefaultFrameSizeAlpha);
59
60 accumulator_ = 0.0f;
61 accumulator_max_ = kDefaultTargetBitrateKbps / 2;
62 target_bitrate_ = kDefaultTargetBitrateKbps;
63 incoming_frame_rate_ = kDefaultIncomingFrameRate;
64
65 large_frame_accumulation_count_ = 0;
isheriff4d7bc242016-04-21 16:37:20 -070066 large_frame_accumulation_chunk_size_ = 0;
isheriff7620be82016-03-06 23:22:33 -080067 large_frame_accumulation_spread_ = 0.5 * kDefaultIncomingFrameRate;
68
69 drop_next_ = false;
70 drop_ratio_.Reset(0.9f);
71 drop_ratio_.Apply(0.0f, 0.0f);
72 drop_count_ = 0;
73 was_below_max_ = true;
philipel5908c712015-12-21 08:23:20 -080074}
75
76void FrameDropper::Enable(bool enable) {
isheriff7620be82016-03-06 23:22:33 -080077 enabled_ = enable;
philipel5908c712015-12-21 08:23:20 -080078}
79
isheriff7620be82016-03-06 23:22:33 -080080void FrameDropper::Fill(size_t framesize_bytes, bool delta_frame) {
81 if (!enabled_) {
philipel5908c712015-12-21 08:23:20 -080082 return;
83 }
isheriff7620be82016-03-06 23:22:33 -080084 float framesize_kbits = 8.0f * static_cast<float>(framesize_bytes) / 1000.0f;
85 if (!delta_frame) {
86 key_frame_ratio_.Apply(1.0, 1.0);
87 // Do not spread if we are already doing it (or we risk dropping bits that
Åsa Perssonb52a4d92017-11-08 11:33:37 +010088 // need accumulation). Given we compute the key frame ratio and spread
89 // based on that, this should not normally happen.
isheriff7620be82016-03-06 23:22:33 -080090 if (large_frame_accumulation_count_ == 0) {
91 if (key_frame_ratio_.filtered() > 1e-5 &&
92 1 / key_frame_ratio_.filtered() < large_frame_accumulation_spread_) {
93 large_frame_accumulation_count_ =
94 static_cast<int32_t>(1 / key_frame_ratio_.filtered() + 0.5);
95 } else {
96 large_frame_accumulation_count_ =
97 static_cast<int32_t>(large_frame_accumulation_spread_ + 0.5);
98 }
99 large_frame_accumulation_chunk_size_ =
100 framesize_kbits / large_frame_accumulation_count_;
101 framesize_kbits = 0;
philipel5908c712015-12-21 08:23:20 -0800102 }
103 } else {
isheriff7620be82016-03-06 23:22:33 -0800104 // Identify if it is an unusually large delta frame and spread accumulation
105 // if that is the case.
106 if (delta_frame_size_avg_kbits_.filtered() != -1 &&
107 (framesize_kbits >
108 kLargeDeltaFactor * delta_frame_size_avg_kbits_.filtered()) &&
109 large_frame_accumulation_count_ == 0) {
110 large_frame_accumulation_count_ =
111 static_cast<int32_t>(large_frame_accumulation_spread_ + 0.5);
112 large_frame_accumulation_chunk_size_ =
113 framesize_kbits / large_frame_accumulation_count_;
114 framesize_kbits = 0;
115 } else {
116 delta_frame_size_avg_kbits_.Apply(1, framesize_kbits);
117 }
118 key_frame_ratio_.Apply(1.0, 0.0);
philipel5908c712015-12-21 08:23:20 -0800119 }
120 // Change the level of the accumulator (bucket)
isheriff7620be82016-03-06 23:22:33 -0800121 accumulator_ += framesize_kbits;
philipel5908c712015-12-21 08:23:20 -0800122 CapAccumulator();
123}
124
isheriff7620be82016-03-06 23:22:33 -0800125void FrameDropper::Leak(uint32_t input_framerate) {
126 if (!enabled_) {
philipel5908c712015-12-21 08:23:20 -0800127 return;
128 }
isheriff7620be82016-03-06 23:22:33 -0800129 if (input_framerate < 1) {
philipel5908c712015-12-21 08:23:20 -0800130 return;
131 }
isheriff7620be82016-03-06 23:22:33 -0800132 if (target_bitrate_ < 0.0f) {
philipel5908c712015-12-21 08:23:20 -0800133 return;
134 }
isheriff7620be82016-03-06 23:22:33 -0800135 // Add lower bound for large frame accumulation spread.
136 large_frame_accumulation_spread_ = std::max(0.5 * input_framerate, 5.0);
137 // Expected bits per frame based on current input frame rate.
138 float expected_bits_per_frame = target_bitrate_ / input_framerate;
139 if (large_frame_accumulation_count_ > 0) {
140 expected_bits_per_frame -= large_frame_accumulation_chunk_size_;
141 --large_frame_accumulation_count_;
philipel5908c712015-12-21 08:23:20 -0800142 }
isheriff7620be82016-03-06 23:22:33 -0800143 accumulator_ -= expected_bits_per_frame;
144 if (accumulator_ < 0.0f) {
145 accumulator_ = 0.0f;
philipel5908c712015-12-21 08:23:20 -0800146 }
147 UpdateRatio();
niklase@google.com470e71d2011-07-07 08:21:25 +0000148}
149
philipel5908c712015-12-21 08:23:20 -0800150void FrameDropper::UpdateRatio() {
isheriff7620be82016-03-06 23:22:33 -0800151 if (accumulator_ > 1.3f * accumulator_max_) {
Åsa Perssonb52a4d92017-11-08 11:33:37 +0100152 // Too far above accumulator max, react faster.
isheriff7620be82016-03-06 23:22:33 -0800153 drop_ratio_.UpdateBase(0.8f);
philipel5908c712015-12-21 08:23:20 -0800154 } else {
Åsa Perssonb52a4d92017-11-08 11:33:37 +0100155 // Go back to normal reaction.
isheriff7620be82016-03-06 23:22:33 -0800156 drop_ratio_.UpdateBase(0.9f);
philipel5908c712015-12-21 08:23:20 -0800157 }
isheriff7620be82016-03-06 23:22:33 -0800158 if (accumulator_ > accumulator_max_) {
Åsa Perssonb52a4d92017-11-08 11:33:37 +0100159 // We are above accumulator max, and should ideally drop a frame. Increase
160 // the drop_ratio_ and drop the frame later.
isheriff7620be82016-03-06 23:22:33 -0800161 if (was_below_max_) {
162 drop_next_ = true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000163 }
isheriff7620be82016-03-06 23:22:33 -0800164 drop_ratio_.Apply(1.0f, 1.0f);
165 drop_ratio_.UpdateBase(0.9f);
philipel5908c712015-12-21 08:23:20 -0800166 } else {
isheriff7620be82016-03-06 23:22:33 -0800167 drop_ratio_.Apply(1.0f, 0.0f);
philipel5908c712015-12-21 08:23:20 -0800168 }
isheriff7620be82016-03-06 23:22:33 -0800169 was_below_max_ = accumulator_ < accumulator_max_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000170}
171
philipel5908c712015-12-21 08:23:20 -0800172// This function signals when to drop frames to the caller. It makes use of the
Åsa Perssonb52a4d92017-11-08 11:33:37 +0100173// drop_ratio_ to smooth out the drops over time.
philipel5908c712015-12-21 08:23:20 -0800174bool FrameDropper::DropFrame() {
isheriff7620be82016-03-06 23:22:33 -0800175 if (!enabled_) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000176 return false;
philipel5908c712015-12-21 08:23:20 -0800177 }
isheriff7620be82016-03-06 23:22:33 -0800178 if (drop_next_) {
179 drop_next_ = false;
180 drop_count_ = 0;
philipel5908c712015-12-21 08:23:20 -0800181 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000182
isheriff7620be82016-03-06 23:22:33 -0800183 if (drop_ratio_.filtered() >= 0.5f) { // Drops per keep
Åsa Perssonb52a4d92017-11-08 11:33:37 +0100184 // Limit is the number of frames we should drop between each kept frame
philipel5908c712015-12-21 08:23:20 -0800185 // to keep our drop ratio. limit is positive in this case.
isheriff7620be82016-03-06 23:22:33 -0800186 float denom = 1.0f - drop_ratio_.filtered();
philipel5908c712015-12-21 08:23:20 -0800187 if (denom < 1e-5) {
188 denom = 1e-5f;
189 }
190 int32_t limit = static_cast<int32_t>(1.0f / denom - 1.0f + 0.5f);
191 // Put a bound on the max amount of dropped frames between each kept
192 // frame, in terms of frame rate and window size (secs).
isheriff7620be82016-03-06 23:22:33 -0800193 int max_limit =
194 static_cast<int>(incoming_frame_rate_ * max_drop_duration_secs_);
philipel5908c712015-12-21 08:23:20 -0800195 if (limit > max_limit) {
196 limit = max_limit;
197 }
isheriff7620be82016-03-06 23:22:33 -0800198 if (drop_count_ < 0) {
199 // Reset the drop_count_ since it was negative and should be positive.
200 drop_count_ = -drop_count_;
philipel5908c712015-12-21 08:23:20 -0800201 }
isheriff7620be82016-03-06 23:22:33 -0800202 if (drop_count_ < limit) {
philipel5908c712015-12-21 08:23:20 -0800203 // As long we are below the limit we should drop frames.
isheriff7620be82016-03-06 23:22:33 -0800204 drop_count_++;
philipel5908c712015-12-21 08:23:20 -0800205 return true;
206 } else {
isheriff7620be82016-03-06 23:22:33 -0800207 // Only when we reset drop_count_ a frame should be kept.
208 drop_count_ = 0;
philipel5908c712015-12-21 08:23:20 -0800209 return false;
210 }
isheriff7620be82016-03-06 23:22:33 -0800211 } else if (drop_ratio_.filtered() > 0.0f &&
212 drop_ratio_.filtered() < 0.5f) { // Keeps per drop
Åsa Perssonb52a4d92017-11-08 11:33:37 +0100213 // Limit is the number of frames we should keep between each drop
philipel5908c712015-12-21 08:23:20 -0800214 // in order to keep the drop ratio. limit is negative in this case,
isheriff7620be82016-03-06 23:22:33 -0800215 // and the drop_count_ is also negative.
216 float denom = drop_ratio_.filtered();
philipel5908c712015-12-21 08:23:20 -0800217 if (denom < 1e-5) {
218 denom = 1e-5f;
219 }
220 int32_t limit = -static_cast<int32_t>(1.0f / denom - 1.0f + 0.5f);
isheriff7620be82016-03-06 23:22:33 -0800221 if (drop_count_ > 0) {
222 // Reset the drop_count_ since we have a positive
223 // drop_count_, and it should be negative.
224 drop_count_ = -drop_count_;
philipel5908c712015-12-21 08:23:20 -0800225 }
isheriff7620be82016-03-06 23:22:33 -0800226 if (drop_count_ > limit) {
227 if (drop_count_ == 0) {
228 // Drop frames when we reset drop_count_.
229 drop_count_--;
philipel5908c712015-12-21 08:23:20 -0800230 return true;
231 } else {
232 // Keep frames as long as we haven't reached limit.
isheriff7620be82016-03-06 23:22:33 -0800233 drop_count_--;
philipel5908c712015-12-21 08:23:20 -0800234 return false;
235 }
236 } else {
isheriff7620be82016-03-06 23:22:33 -0800237 drop_count_ = 0;
philipel5908c712015-12-21 08:23:20 -0800238 return false;
239 }
240 }
isheriff7620be82016-03-06 23:22:33 -0800241 drop_count_ = 0;
philipel5908c712015-12-21 08:23:20 -0800242 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000243}
244
isheriff7620be82016-03-06 23:22:33 -0800245void FrameDropper::SetRates(float bitrate, float incoming_frame_rate) {
philipel5908c712015-12-21 08:23:20 -0800246 // Bit rate of -1 means infinite bandwidth.
isheriff7620be82016-03-06 23:22:33 -0800247 accumulator_max_ = bitrate * kLeakyBucketSizeSeconds;
248 if (target_bitrate_ > 0.0f && bitrate < target_bitrate_ &&
249 accumulator_ > accumulator_max_) {
philipel5908c712015-12-21 08:23:20 -0800250 // Rescale the accumulator level if the accumulator max decreases
isheriff7620be82016-03-06 23:22:33 -0800251 accumulator_ = bitrate / target_bitrate_ * accumulator_;
philipel5908c712015-12-21 08:23:20 -0800252 }
isheriff7620be82016-03-06 23:22:33 -0800253 target_bitrate_ = bitrate;
philipel5908c712015-12-21 08:23:20 -0800254 CapAccumulator();
isheriff7620be82016-03-06 23:22:33 -0800255 incoming_frame_rate_ = incoming_frame_rate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000256}
257
marpan@webrtc.org1dd8d4b2012-10-09 20:43:56 +0000258// Put a cap on the accumulator, i.e., don't let it grow beyond some level.
259// This is a temporary fix for screencasting where very large frames from
260// encoder will cause very slow response (too many frame drops).
isheriff7620be82016-03-06 23:22:33 -0800261// TODO(isheriff): Remove this now that large delta frames are also spread out ?
stefan@webrtc.orgeb917922013-02-18 14:40:18 +0000262void FrameDropper::CapAccumulator() {
isheriff7620be82016-03-06 23:22:33 -0800263 float max_accumulator = target_bitrate_ * kAccumulatorCapBufferSizeSecs;
264 if (accumulator_ > max_accumulator) {
265 accumulator_ = max_accumulator;
marpan@webrtc.org1dd8d4b2012-10-09 20:43:56 +0000266 }
267}
philipel5908c712015-12-21 08:23:20 -0800268} // namespace webrtc