xref: /aosp_15_r20/external/webrtc/modules/video_coding/utility/simulcast_rate_allocator.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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