1 /*
2 * Copyright (c) 2016 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/simulcast_rate_allocator.h"
12
13 #include <stdio.h>
14
15 #include <algorithm>
16 #include <cmath>
17 #include <cstdint>
18 #include <numeric>
19 #include <string>
20 #include <tuple>
21 #include <vector>
22
23 #include "rtc_base/checks.h"
24 #include "rtc_base/experiments/rate_control_settings.h"
25 #include "system_wrappers/include/field_trial.h"
26
27 namespace webrtc {
28 namespace {
29 // Ratio allocation between temporal streams:
30 // Values as required for the VP8 codec (accumulating).
31 static const float
32 kLayerRateAllocation[kMaxTemporalStreams][kMaxTemporalStreams] = {
33 {1.0f, 1.0f, 1.0f, 1.0f}, // 1 layer
34 {0.6f, 1.0f, 1.0f, 1.0f}, // 2 layers {60%, 40%}
35 {0.4f, 0.6f, 1.0f, 1.0f}, // 3 layers {40%, 20%, 40%}
36 {0.25f, 0.4f, 0.6f, 1.0f} // 4 layers {25%, 15%, 20%, 40%}
37 };
38
39 static const float kBaseHeavy3TlRateAllocation[kMaxTemporalStreams] = {
40 0.6f, 0.8f, 1.0f, 1.0f // 3 layers {60%, 20%, 20%}
41 };
42
43 const uint32_t kLegacyScreenshareTl0BitrateKbps = 200;
44 const uint32_t kLegacyScreenshareTl1BitrateKbps = 1000;
45 } // namespace
46
GetTemporalRateAllocation(int num_layers,int temporal_id,bool base_heavy_tl3_alloc)47 float SimulcastRateAllocator::GetTemporalRateAllocation(
48 int num_layers,
49 int temporal_id,
50 bool base_heavy_tl3_alloc) {
51 RTC_CHECK_GT(num_layers, 0);
52 RTC_CHECK_LE(num_layers, kMaxTemporalStreams);
53 RTC_CHECK_GE(temporal_id, 0);
54 RTC_CHECK_LT(temporal_id, num_layers);
55 if (num_layers == 3 && base_heavy_tl3_alloc) {
56 return kBaseHeavy3TlRateAllocation[temporal_id];
57 }
58 return kLayerRateAllocation[num_layers - 1][temporal_id];
59 }
60
SimulcastRateAllocator(const VideoCodec & codec)61 SimulcastRateAllocator::SimulcastRateAllocator(const VideoCodec& codec)
62 : codec_(codec),
63 stable_rate_settings_(StableTargetRateExperiment::ParseFromFieldTrials()),
64 rate_control_settings_(RateControlSettings::ParseFromFieldTrials()) {}
65
66 SimulcastRateAllocator::~SimulcastRateAllocator() = default;
67
Allocate(VideoBitrateAllocationParameters parameters)68 VideoBitrateAllocation SimulcastRateAllocator::Allocate(
69 VideoBitrateAllocationParameters parameters) {
70 VideoBitrateAllocation allocated_bitrates;
71 DataRate stable_rate = parameters.total_bitrate;
72 if (stable_rate_settings_.IsEnabled() &&
73 parameters.stable_bitrate > DataRate::Zero()) {
74 stable_rate = std::min(parameters.stable_bitrate, parameters.total_bitrate);
75 }
76 DistributeAllocationToSimulcastLayers(parameters.total_bitrate, stable_rate,
77 &allocated_bitrates);
78 DistributeAllocationToTemporalLayers(&allocated_bitrates);
79 return allocated_bitrates;
80 }
81
DistributeAllocationToSimulcastLayers(DataRate total_bitrate,DataRate stable_bitrate,VideoBitrateAllocation * allocated_bitrates)82 void SimulcastRateAllocator::DistributeAllocationToSimulcastLayers(
83 DataRate total_bitrate,
84 DataRate stable_bitrate,
85 VideoBitrateAllocation* allocated_bitrates) {
86 DataRate left_in_total_allocation = total_bitrate;
87 DataRate left_in_stable_allocation = stable_bitrate;
88
89 if (codec_.maxBitrate) {
90 DataRate max_rate = DataRate::KilobitsPerSec(codec_.maxBitrate);
91 left_in_total_allocation = std::min(left_in_total_allocation, max_rate);
92 left_in_stable_allocation = std::min(left_in_stable_allocation, max_rate);
93 }
94
95 if (codec_.numberOfSimulcastStreams == 0) {
96 // No simulcast, just set the target as this has been capped already.
97 if (codec_.active) {
98 allocated_bitrates->SetBitrate(
99 0, 0,
100 std::max(DataRate::KilobitsPerSec(codec_.minBitrate),
101 left_in_total_allocation)
102 .bps());
103 }
104 return;
105 }
106
107 // Sort the layers by maxFramerate, they might not always be from smallest
108 // to biggest
109 std::vector<size_t> layer_index(codec_.numberOfSimulcastStreams);
110 std::iota(layer_index.begin(), layer_index.end(), 0);
111 std::stable_sort(layer_index.begin(), layer_index.end(),
112 [this](size_t a, size_t b) {
113 return std::tie(codec_.simulcastStream[a].maxBitrate) <
114 std::tie(codec_.simulcastStream[b].maxBitrate);
115 });
116
117 // Find the first active layer. We don't allocate to inactive layers.
118 size_t active_layer = 0;
119 for (; active_layer < codec_.numberOfSimulcastStreams; ++active_layer) {
120 if (codec_.simulcastStream[layer_index[active_layer]].active) {
121 // Found the first active layer.
122 break;
123 }
124 }
125 // All streams could be inactive, and nothing more to do.
126 if (active_layer == codec_.numberOfSimulcastStreams) {
127 return;
128 }
129
130 // Always allocate enough bitrate for the minimum bitrate of the first
131 // active layer. Suspending below min bitrate is controlled outside the
132 // codec implementation and is not overridden by this.
133 DataRate min_rate = DataRate::KilobitsPerSec(
134 codec_.simulcastStream[layer_index[active_layer]].minBitrate);
135 left_in_total_allocation = std::max(left_in_total_allocation, min_rate);
136 left_in_stable_allocation = std::max(left_in_stable_allocation, min_rate);
137
138 // Begin by allocating bitrate to simulcast streams, putting all bitrate in
139 // temporal layer 0. We'll then distribute this bitrate, across potential
140 // temporal layers, when stream allocation is done.
141
142 bool first_allocation = false;
143 if (stream_enabled_.empty()) {
144 // First time allocating, this means we should not include hysteresis in
145 // case this is a reconfiguration of an existing enabled stream.
146 first_allocation = true;
147 stream_enabled_.resize(codec_.numberOfSimulcastStreams, false);
148 }
149
150 size_t top_active_layer = active_layer;
151 // Allocate up to the target bitrate for each active simulcast layer.
152 for (; active_layer < codec_.numberOfSimulcastStreams; ++active_layer) {
153 const SimulcastStream& stream =
154 codec_.simulcastStream[layer_index[active_layer]];
155 if (!stream.active) {
156 stream_enabled_[layer_index[active_layer]] = false;
157 continue;
158 }
159 // If we can't allocate to the current layer we can't allocate to higher
160 // layers because they require a higher minimum bitrate.
161 DataRate min_bitrate = DataRate::KilobitsPerSec(stream.minBitrate);
162 DataRate target_bitrate = DataRate::KilobitsPerSec(stream.targetBitrate);
163 double hysteresis_factor =
164 codec_.mode == VideoCodecMode::kRealtimeVideo
165 ? stable_rate_settings_.GetVideoHysteresisFactor()
166 : stable_rate_settings_.GetScreenshareHysteresisFactor();
167 if (!first_allocation && !stream_enabled_[layer_index[active_layer]]) {
168 min_bitrate = std::min(hysteresis_factor * min_bitrate, target_bitrate);
169 }
170 if (left_in_stable_allocation < min_bitrate) {
171 allocated_bitrates->set_bw_limited(true);
172 break;
173 }
174
175 // We are allocating to this layer so it is the current active allocation.
176 top_active_layer = layer_index[active_layer];
177 stream_enabled_[layer_index[active_layer]] = true;
178 DataRate layer_rate = std::min(left_in_total_allocation, target_bitrate);
179 allocated_bitrates->SetBitrate(layer_index[active_layer], 0,
180 layer_rate.bps());
181 left_in_total_allocation -= layer_rate;
182 left_in_stable_allocation -=
183 std::min(left_in_stable_allocation, target_bitrate);
184 }
185
186 // All layers above this one are not active.
187 for (; active_layer < codec_.numberOfSimulcastStreams; ++active_layer) {
188 stream_enabled_[layer_index[active_layer]] = false;
189 }
190
191 // Next, try allocate remaining bitrate, up to max bitrate, in top active
192 // stream.
193 // TODO(sprang): Allocate up to max bitrate for all layers once we have a
194 // better idea of possible performance implications.
195 if (left_in_total_allocation > DataRate::Zero()) {
196 const SimulcastStream& stream = codec_.simulcastStream[top_active_layer];
197 DataRate initial_layer_rate = DataRate::BitsPerSec(
198 allocated_bitrates->GetSpatialLayerSum(top_active_layer));
199 DataRate additional_allocation = std::min(
200 left_in_total_allocation,
201 DataRate::KilobitsPerSec(stream.maxBitrate) - initial_layer_rate);
202 allocated_bitrates->SetBitrate(
203 top_active_layer, 0,
204 (initial_layer_rate + additional_allocation).bps());
205 }
206 }
207
DistributeAllocationToTemporalLayers(VideoBitrateAllocation * allocated_bitrates_bps) const208 void SimulcastRateAllocator::DistributeAllocationToTemporalLayers(
209 VideoBitrateAllocation* allocated_bitrates_bps) const {
210 const int num_spatial_streams =
211 std::max(1, static_cast<int>(codec_.numberOfSimulcastStreams));
212
213 // Finally, distribute the bitrate for the simulcast streams across the
214 // available temporal layers.
215 for (int simulcast_id = 0; simulcast_id < num_spatial_streams;
216 ++simulcast_id) {
217 uint32_t target_bitrate_kbps =
218 allocated_bitrates_bps->GetBitrate(simulcast_id, 0) / 1000;
219 if (target_bitrate_kbps == 0) {
220 continue;
221 }
222
223 const uint32_t expected_allocated_bitrate_kbps = target_bitrate_kbps;
224 RTC_DCHECK_EQ(
225 target_bitrate_kbps,
226 allocated_bitrates_bps->GetSpatialLayerSum(simulcast_id) / 1000);
227 const int num_temporal_streams = NumTemporalStreams(simulcast_id);
228 uint32_t max_bitrate_kbps;
229 // Legacy temporal-layered only screenshare, or simulcast screenshare
230 // with legacy mode for simulcast stream 0.
231 const bool conference_screenshare_mode =
232 codec_.mode == VideoCodecMode::kScreensharing &&
233 ((num_spatial_streams == 1 && num_temporal_streams == 2) || // Legacy.
234 (num_spatial_streams > 1 && simulcast_id == 0 &&
235 num_temporal_streams == 2)); // Simulcast.
236 if (conference_screenshare_mode) {
237 // TODO(holmer): This is a "temporary" hack for screensharing, where we
238 // interpret the startBitrate as the encoder target bitrate. This is
239 // to allow for a different max bitrate, so if the codec can't meet
240 // the target we still allow it to overshoot up to the max before dropping
241 // frames. This hack should be improved.
242 max_bitrate_kbps =
243 std::min(kLegacyScreenshareTl1BitrateKbps, target_bitrate_kbps);
244 target_bitrate_kbps =
245 std::min(kLegacyScreenshareTl0BitrateKbps, target_bitrate_kbps);
246 } else if (num_spatial_streams == 1) {
247 max_bitrate_kbps = codec_.maxBitrate;
248 } else {
249 max_bitrate_kbps = codec_.simulcastStream[simulcast_id].maxBitrate;
250 }
251
252 std::vector<uint32_t> tl_allocation;
253 if (num_temporal_streams == 1) {
254 tl_allocation.push_back(target_bitrate_kbps);
255 } else {
256 if (conference_screenshare_mode) {
257 tl_allocation = ScreenshareTemporalLayerAllocation(
258 target_bitrate_kbps, max_bitrate_kbps, simulcast_id);
259 } else {
260 tl_allocation = DefaultTemporalLayerAllocation(
261 target_bitrate_kbps, max_bitrate_kbps, simulcast_id);
262 }
263 }
264 RTC_DCHECK_GT(tl_allocation.size(), 0);
265 RTC_DCHECK_LE(tl_allocation.size(), num_temporal_streams);
266
267 uint64_t tl_allocation_sum_kbps = 0;
268 for (size_t tl_index = 0; tl_index < tl_allocation.size(); ++tl_index) {
269 uint32_t layer_rate_kbps = tl_allocation[tl_index];
270 if (layer_rate_kbps > 0) {
271 allocated_bitrates_bps->SetBitrate(simulcast_id, tl_index,
272 layer_rate_kbps * 1000);
273 }
274 tl_allocation_sum_kbps += layer_rate_kbps;
275 }
276 RTC_DCHECK_LE(tl_allocation_sum_kbps, expected_allocated_bitrate_kbps);
277 }
278 }
279
DefaultTemporalLayerAllocation(int bitrate_kbps,int max_bitrate_kbps,int simulcast_id) const280 std::vector<uint32_t> SimulcastRateAllocator::DefaultTemporalLayerAllocation(
281 int bitrate_kbps,
282 int max_bitrate_kbps,
283 int simulcast_id) const {
284 const size_t num_temporal_layers = NumTemporalStreams(simulcast_id);
285 std::vector<uint32_t> bitrates;
286 for (size_t i = 0; i < num_temporal_layers; ++i) {
287 float layer_bitrate =
288 bitrate_kbps *
289 GetTemporalRateAllocation(
290 num_temporal_layers, i,
291 rate_control_settings_.Vp8BaseHeavyTl3RateAllocation());
292 bitrates.push_back(static_cast<uint32_t>(layer_bitrate + 0.5));
293 }
294
295 // Allocation table is of aggregates, transform to individual rates.
296 uint32_t sum = 0;
297 for (size_t i = 0; i < num_temporal_layers; ++i) {
298 uint32_t layer_bitrate = bitrates[i];
299 RTC_DCHECK_LE(sum, bitrates[i]);
300 bitrates[i] -= sum;
301 sum = layer_bitrate;
302
303 if (sum >= static_cast<uint32_t>(bitrate_kbps)) {
304 // Sum adds up; any subsequent layers will be 0.
305 bitrates.resize(i + 1);
306 break;
307 }
308 }
309
310 return bitrates;
311 }
312
313 std::vector<uint32_t>
ScreenshareTemporalLayerAllocation(int bitrate_kbps,int max_bitrate_kbps,int simulcast_id) const314 SimulcastRateAllocator::ScreenshareTemporalLayerAllocation(
315 int bitrate_kbps,
316 int max_bitrate_kbps,
317 int simulcast_id) const {
318 if (simulcast_id > 0) {
319 return DefaultTemporalLayerAllocation(bitrate_kbps, max_bitrate_kbps,
320 simulcast_id);
321 }
322 std::vector<uint32_t> allocation;
323 allocation.push_back(bitrate_kbps);
324 if (max_bitrate_kbps > bitrate_kbps)
325 allocation.push_back(max_bitrate_kbps - bitrate_kbps);
326 return allocation;
327 }
328
GetCodec() const329 const VideoCodec& webrtc::SimulcastRateAllocator::GetCodec() const {
330 return codec_;
331 }
332
NumTemporalStreams(size_t simulcast_id) const333 int SimulcastRateAllocator::NumTemporalStreams(size_t simulcast_id) const {
334 return std::max<uint8_t>(
335 1,
336 codec_.codecType == kVideoCodecVP8 && codec_.numberOfSimulcastStreams == 0
337 ? codec_.VP8().numberOfTemporalLayers
338 : codec_.simulcastStream[simulcast_id].numberOfTemporalLayers);
339 }
340
341 } // namespace webrtc
342