• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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