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