blob: 5de7526ac2438eca4ce9d56962cb2802d5478ba0 [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
kjellander@webrtc.orgb7ce9642015-11-18 23:04:10 +010011#include "webrtc/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
15#include "webrtc/base/logging.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010016#include "webrtc/system_wrappers/include/trace.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000017
philipel5908c712015-12-21 08:23:20 -080018namespace webrtc {
niklase@google.com470e71d2011-07-07 08:21:25 +000019
isheriff7620be82016-03-06 23:22:33 -080020namespace {
21
22const float kDefaultFrameSizeAlpha = 0.9f;
23const float kDefaultKeyFrameRatioAlpha = 0.99f;
24// 1 key frame every 10th second in 30 fps.
25const float kDefaultKeyFrameRatioValue = 1 / 300.0f;
26
stefan@webrtc.org84cd8e32013-03-07 13:12:32 +000027const float kDefaultDropRatioAlpha = 0.9f;
isheriff7620be82016-03-06 23:22:33 -080028const float kDefaultDropRatioValue = 0.96f;
29// Maximum duration over which frames are continuously dropped.
30const 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 ?
35const float kDefaultTargetBitrateKbps = 300.0f;
36const float kDefaultIncomingFrameRate = 30;
37const 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.
41const int kLargeDeltaFactor = 3;
42
43// Cap on the frame size accumulator to prevent excessive drops.
44const float kAccumulatorCapBufferSizeSecs = 3.0f;
45} // namespace
stefan@webrtc.org84cd8e32013-03-07 13:12:32 +000046
stefan@webrtc.orgeb917922013-02-18 14:40:18 +000047FrameDropper::FrameDropper()
isheriff7620be82016-03-06 23:22:33 -080048 : key_frame_ratio_(kDefaultKeyFrameRatioAlpha),
49 delta_frame_size_avg_kbits_(kDefaultFrameSizeAlpha),
50 drop_ratio_(kDefaultDropRatioAlpha, kDefaultDropRatioValue),
51 enabled_(true),
52 max_drop_duration_secs_(kDefaultMaxDropDurationSecs) {
philipel5908c712015-12-21 08:23:20 -080053 Reset();
stefan@webrtc.org84cd8e32013-03-07 13:12:32 +000054}
55
isheriff7620be82016-03-06 23:22:33 -080056FrameDropper::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 max_drop_duration_secs_(max_drop_duration_secs) {
philipel5908c712015-12-21 08:23:20 -080062 Reset();
niklase@google.com470e71d2011-07-07 08:21:25 +000063}
64
philipel5908c712015-12-21 08:23:20 -080065void FrameDropper::Reset() {
isheriff7620be82016-03-06 23:22:33 -080066 key_frame_ratio_.Reset(kDefaultKeyFrameRatioAlpha);
67 key_frame_ratio_.Apply(1.0f, kDefaultKeyFrameRatioValue);
68 delta_frame_size_avg_kbits_.Reset(kDefaultFrameSizeAlpha);
69
70 accumulator_ = 0.0f;
71 accumulator_max_ = kDefaultTargetBitrateKbps / 2;
72 target_bitrate_ = kDefaultTargetBitrateKbps;
73 incoming_frame_rate_ = kDefaultIncomingFrameRate;
74
75 large_frame_accumulation_count_ = 0;
isheriff4d7bc242016-04-21 16:37:20 -070076 large_frame_accumulation_chunk_size_ = 0;
isheriff7620be82016-03-06 23:22:33 -080077 large_frame_accumulation_spread_ = 0.5 * kDefaultIncomingFrameRate;
78
79 drop_next_ = false;
80 drop_ratio_.Reset(0.9f);
81 drop_ratio_.Apply(0.0f, 0.0f);
82 drop_count_ = 0;
83 was_below_max_ = true;
philipel5908c712015-12-21 08:23:20 -080084}
85
86void FrameDropper::Enable(bool enable) {
isheriff7620be82016-03-06 23:22:33 -080087 enabled_ = enable;
philipel5908c712015-12-21 08:23:20 -080088}
89
isheriff7620be82016-03-06 23:22:33 -080090void FrameDropper::Fill(size_t framesize_bytes, bool delta_frame) {
91 if (!enabled_) {
philipel5908c712015-12-21 08:23:20 -080092 return;
93 }
isheriff7620be82016-03-06 23:22:33 -080094 float framesize_kbits = 8.0f * static_cast<float>(framesize_bytes) / 1000.0f;
95 if (!delta_frame) {
96 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 if (large_frame_accumulation_count_ == 0) {
101 if (key_frame_ratio_.filtered() > 1e-5 &&
102 1 / key_frame_ratio_.filtered() < large_frame_accumulation_spread_) {
103 large_frame_accumulation_count_ =
104 static_cast<int32_t>(1 / key_frame_ratio_.filtered() + 0.5);
105 } else {
106 large_frame_accumulation_count_ =
107 static_cast<int32_t>(large_frame_accumulation_spread_ + 0.5);
108 }
109 large_frame_accumulation_chunk_size_ =
110 framesize_kbits / large_frame_accumulation_count_;
111 framesize_kbits = 0;
philipel5908c712015-12-21 08:23:20 -0800112 }
113 } else {
isheriff7620be82016-03-06 23:22:33 -0800114 // Identify if it is an unusually large delta frame and spread accumulation
115 // if that is the case.
116 if (delta_frame_size_avg_kbits_.filtered() != -1 &&
117 (framesize_kbits >
118 kLargeDeltaFactor * delta_frame_size_avg_kbits_.filtered()) &&
119 large_frame_accumulation_count_ == 0) {
120 large_frame_accumulation_count_ =
121 static_cast<int32_t>(large_frame_accumulation_spread_ + 0.5);
122 large_frame_accumulation_chunk_size_ =
123 framesize_kbits / large_frame_accumulation_count_;
124 framesize_kbits = 0;
125 } else {
126 delta_frame_size_avg_kbits_.Apply(1, framesize_kbits);
127 }
128 key_frame_ratio_.Apply(1.0, 0.0);
philipel5908c712015-12-21 08:23:20 -0800129 }
130 // Change the level of the accumulator (bucket)
isheriff7620be82016-03-06 23:22:33 -0800131 accumulator_ += framesize_kbits;
philipel5908c712015-12-21 08:23:20 -0800132 CapAccumulator();
133}
134
isheriff7620be82016-03-06 23:22:33 -0800135void FrameDropper::Leak(uint32_t input_framerate) {
136 if (!enabled_) {
philipel5908c712015-12-21 08:23:20 -0800137 return;
138 }
isheriff7620be82016-03-06 23:22:33 -0800139 if (input_framerate < 1) {
philipel5908c712015-12-21 08:23:20 -0800140 return;
141 }
isheriff7620be82016-03-06 23:22:33 -0800142 if (target_bitrate_ < 0.0f) {
philipel5908c712015-12-21 08:23:20 -0800143 return;
144 }
isheriff7620be82016-03-06 23:22:33 -0800145 // Add lower bound for large frame accumulation spread.
146 large_frame_accumulation_spread_ = std::max(0.5 * input_framerate, 5.0);
147 // Expected bits per frame based on current input frame rate.
148 float expected_bits_per_frame = target_bitrate_ / input_framerate;
149 if (large_frame_accumulation_count_ > 0) {
150 expected_bits_per_frame -= large_frame_accumulation_chunk_size_;
151 --large_frame_accumulation_count_;
philipel5908c712015-12-21 08:23:20 -0800152 }
isheriff7620be82016-03-06 23:22:33 -0800153 accumulator_ -= expected_bits_per_frame;
154 if (accumulator_ < 0.0f) {
155 accumulator_ = 0.0f;
philipel5908c712015-12-21 08:23:20 -0800156 }
157 UpdateRatio();
niklase@google.com470e71d2011-07-07 08:21:25 +0000158}
159
philipel5908c712015-12-21 08:23:20 -0800160void FrameDropper::UpdateRatio() {
isheriff7620be82016-03-06 23:22:33 -0800161 if (accumulator_ > 1.3f * accumulator_max_) {
philipel5908c712015-12-21 08:23:20 -0800162 // Too far above accumulator max, react faster
isheriff7620be82016-03-06 23:22:33 -0800163 drop_ratio_.UpdateBase(0.8f);
philipel5908c712015-12-21 08:23:20 -0800164 } else {
165 // Go back to normal reaction
isheriff7620be82016-03-06 23:22:33 -0800166 drop_ratio_.UpdateBase(0.9f);
philipel5908c712015-12-21 08:23:20 -0800167 }
isheriff7620be82016-03-06 23:22:33 -0800168 if (accumulator_ > accumulator_max_) {
philipel5908c712015-12-21 08:23:20 -0800169 // We are above accumulator max, and should ideally
170 // drop a frame. Increase the dropRatio and drop
171 // the frame later.
isheriff7620be82016-03-06 23:22:33 -0800172 if (was_below_max_) {
173 drop_next_ = true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000174 }
isheriff7620be82016-03-06 23:22:33 -0800175 drop_ratio_.Apply(1.0f, 1.0f);
176 drop_ratio_.UpdateBase(0.9f);
philipel5908c712015-12-21 08:23:20 -0800177 } else {
isheriff7620be82016-03-06 23:22:33 -0800178 drop_ratio_.Apply(1.0f, 0.0f);
philipel5908c712015-12-21 08:23:20 -0800179 }
isheriff7620be82016-03-06 23:22:33 -0800180 was_below_max_ = accumulator_ < accumulator_max_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000181}
182
philipel5908c712015-12-21 08:23:20 -0800183// This function signals when to drop frames to the caller. It makes use of the
184// dropRatio
niklase@google.com470e71d2011-07-07 08:21:25 +0000185// to smooth out the drops over time.
philipel5908c712015-12-21 08:23:20 -0800186bool FrameDropper::DropFrame() {
isheriff7620be82016-03-06 23:22:33 -0800187 if (!enabled_) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000188 return false;
philipel5908c712015-12-21 08:23:20 -0800189 }
isheriff7620be82016-03-06 23:22:33 -0800190 if (drop_next_) {
191 drop_next_ = false;
192 drop_count_ = 0;
philipel5908c712015-12-21 08:23:20 -0800193 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000194
isheriff7620be82016-03-06 23:22:33 -0800195 if (drop_ratio_.filtered() >= 0.5f) { // Drops per keep
philipel5908c712015-12-21 08:23:20 -0800196 // 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.
isheriff7620be82016-03-06 23:22:33 -0800198 float denom = 1.0f - drop_ratio_.filtered();
philipel5908c712015-12-21 08:23:20 -0800199 if (denom < 1e-5) {
200 denom = 1e-5f;
201 }
202 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).
isheriff7620be82016-03-06 23:22:33 -0800205 int max_limit =
206 static_cast<int>(incoming_frame_rate_ * max_drop_duration_secs_);
philipel5908c712015-12-21 08:23:20 -0800207 if (limit > max_limit) {
208 limit = max_limit;
209 }
isheriff7620be82016-03-06 23:22:33 -0800210 if (drop_count_ < 0) {
211 // Reset the drop_count_ since it was negative and should be positive.
212 drop_count_ = -drop_count_;
philipel5908c712015-12-21 08:23:20 -0800213 }
isheriff7620be82016-03-06 23:22:33 -0800214 if (drop_count_ < limit) {
philipel5908c712015-12-21 08:23:20 -0800215 // As long we are below the limit we should drop frames.
isheriff7620be82016-03-06 23:22:33 -0800216 drop_count_++;
philipel5908c712015-12-21 08:23:20 -0800217 return true;
218 } else {
isheriff7620be82016-03-06 23:22:33 -0800219 // Only when we reset drop_count_ a frame should be kept.
220 drop_count_ = 0;
philipel5908c712015-12-21 08:23:20 -0800221 return false;
222 }
isheriff7620be82016-03-06 23:22:33 -0800223 } else if (drop_ratio_.filtered() > 0.0f &&
224 drop_ratio_.filtered() < 0.5f) { // Keeps per drop
philipel5908c712015-12-21 08:23:20 -0800225 // 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,
isheriff7620be82016-03-06 23:22:33 -0800227 // and the drop_count_ is also negative.
228 float denom = drop_ratio_.filtered();
philipel5908c712015-12-21 08:23:20 -0800229 if (denom < 1e-5) {
230 denom = 1e-5f;
231 }
232 int32_t limit = -static_cast<int32_t>(1.0f / denom - 1.0f + 0.5f);
isheriff7620be82016-03-06 23:22:33 -0800233 if (drop_count_ > 0) {
234 // Reset the drop_count_ since we have a positive
235 // drop_count_, and it should be negative.
236 drop_count_ = -drop_count_;
philipel5908c712015-12-21 08:23:20 -0800237 }
isheriff7620be82016-03-06 23:22:33 -0800238 if (drop_count_ > limit) {
239 if (drop_count_ == 0) {
240 // Drop frames when we reset drop_count_.
241 drop_count_--;
philipel5908c712015-12-21 08:23:20 -0800242 return true;
243 } else {
244 // Keep frames as long as we haven't reached limit.
isheriff7620be82016-03-06 23:22:33 -0800245 drop_count_--;
philipel5908c712015-12-21 08:23:20 -0800246 return false;
247 }
248 } else {
isheriff7620be82016-03-06 23:22:33 -0800249 drop_count_ = 0;
philipel5908c712015-12-21 08:23:20 -0800250 return false;
251 }
252 }
isheriff7620be82016-03-06 23:22:33 -0800253 drop_count_ = 0;
philipel5908c712015-12-21 08:23:20 -0800254 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000255}
256
isheriff7620be82016-03-06 23:22:33 -0800257void FrameDropper::SetRates(float bitrate, float incoming_frame_rate) {
philipel5908c712015-12-21 08:23:20 -0800258 // Bit rate of -1 means infinite bandwidth.
isheriff7620be82016-03-06 23:22:33 -0800259 accumulator_max_ = bitrate * kLeakyBucketSizeSeconds;
260 if (target_bitrate_ > 0.0f && bitrate < target_bitrate_ &&
261 accumulator_ > accumulator_max_) {
philipel5908c712015-12-21 08:23:20 -0800262 // Rescale the accumulator level if the accumulator max decreases
isheriff7620be82016-03-06 23:22:33 -0800263 accumulator_ = bitrate / target_bitrate_ * accumulator_;
philipel5908c712015-12-21 08:23:20 -0800264 }
isheriff7620be82016-03-06 23:22:33 -0800265 target_bitrate_ = bitrate;
philipel5908c712015-12-21 08:23:20 -0800266 CapAccumulator();
isheriff7620be82016-03-06 23:22:33 -0800267 incoming_frame_rate_ = incoming_frame_rate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000268}
269
marpan@webrtc.org1dd8d4b2012-10-09 20:43:56 +0000270// 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).
isheriff7620be82016-03-06 23:22:33 -0800273// TODO(isheriff): Remove this now that large delta frames are also spread out ?
stefan@webrtc.orgeb917922013-02-18 14:40:18 +0000274void FrameDropper::CapAccumulator() {
isheriff7620be82016-03-06 23:22:33 -0800275 float max_accumulator = target_bitrate_ * kAccumulatorCapBufferSizeSecs;
276 if (accumulator_ > max_accumulator) {
277 accumulator_ = max_accumulator;
marpan@webrtc.org1dd8d4b2012-10-09 20:43:56 +0000278 }
279}
philipel5908c712015-12-21 08:23:20 -0800280} // namespace webrtc