1 /*
2  *  Copyright (c) 2018 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 "api/video/video_bitrate_allocation.h"
12 
13 #include <cstdint>
14 
15 #include "rtc_base/checks.h"
16 #include "rtc_base/numerics/safe_conversions.h"
17 #include "rtc_base/strings/string_builder.h"
18 
19 namespace webrtc {
20 
VideoBitrateAllocation()21 VideoBitrateAllocation::VideoBitrateAllocation()
22     : sum_(0), is_bw_limited_(false) {}
23 
SetBitrate(size_t spatial_index,size_t temporal_index,uint32_t bitrate_bps)24 bool VideoBitrateAllocation::SetBitrate(size_t spatial_index,
25                                         size_t temporal_index,
26                                         uint32_t bitrate_bps) {
27   RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
28   RTC_CHECK_LT(temporal_index, kMaxTemporalStreams);
29   int64_t new_bitrate_sum_bps = sum_;
30   absl::optional<uint32_t>& layer_bitrate =
31       bitrates_[spatial_index][temporal_index];
32   if (layer_bitrate) {
33     RTC_DCHECK_LE(*layer_bitrate, sum_);
34     new_bitrate_sum_bps -= *layer_bitrate;
35   }
36   new_bitrate_sum_bps += bitrate_bps;
37   if (new_bitrate_sum_bps > kMaxBitrateBps)
38     return false;
39 
40   layer_bitrate = bitrate_bps;
41   sum_ = rtc::dchecked_cast<uint32_t>(new_bitrate_sum_bps);
42   return true;
43 }
44 
HasBitrate(size_t spatial_index,size_t temporal_index) const45 bool VideoBitrateAllocation::HasBitrate(size_t spatial_index,
46                                         size_t temporal_index) const {
47   RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
48   RTC_CHECK_LT(temporal_index, kMaxTemporalStreams);
49   return bitrates_[spatial_index][temporal_index].has_value();
50 }
51 
GetBitrate(size_t spatial_index,size_t temporal_index) const52 uint32_t VideoBitrateAllocation::GetBitrate(size_t spatial_index,
53                                             size_t temporal_index) const {
54   RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
55   RTC_CHECK_LT(temporal_index, kMaxTemporalStreams);
56   return bitrates_[spatial_index][temporal_index].value_or(0);
57 }
58 
59 // Whether the specific spatial layers has the bitrate set in any of its
60 // temporal layers.
IsSpatialLayerUsed(size_t spatial_index) const61 bool VideoBitrateAllocation::IsSpatialLayerUsed(size_t spatial_index) const {
62   RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
63   for (size_t i = 0; i < kMaxTemporalStreams; ++i) {
64     if (bitrates_[spatial_index][i].has_value())
65       return true;
66   }
67   return false;
68 }
69 
70 // Get the sum of all the temporal layer for a specific spatial layer.
GetSpatialLayerSum(size_t spatial_index) const71 uint32_t VideoBitrateAllocation::GetSpatialLayerSum(
72     size_t spatial_index) const {
73   RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
74   return GetTemporalLayerSum(spatial_index, kMaxTemporalStreams - 1);
75 }
76 
GetTemporalLayerSum(size_t spatial_index,size_t temporal_index) const77 uint32_t VideoBitrateAllocation::GetTemporalLayerSum(
78     size_t spatial_index,
79     size_t temporal_index) const {
80   RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
81   RTC_CHECK_LT(temporal_index, kMaxTemporalStreams);
82   uint32_t sum = 0;
83   for (size_t i = 0; i <= temporal_index; ++i) {
84     sum += bitrates_[spatial_index][i].value_or(0);
85   }
86   return sum;
87 }
88 
GetTemporalLayerAllocation(size_t spatial_index) const89 std::vector<uint32_t> VideoBitrateAllocation::GetTemporalLayerAllocation(
90     size_t spatial_index) const {
91   RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
92   std::vector<uint32_t> temporal_rates;
93 
94   // Find the highest temporal layer with a defined bitrate in order to
95   // determine the size of the temporal layer allocation.
96   for (size_t i = kMaxTemporalStreams; i > 0; --i) {
97     if (bitrates_[spatial_index][i - 1].has_value()) {
98       temporal_rates.resize(i);
99       break;
100     }
101   }
102 
103   for (size_t i = 0; i < temporal_rates.size(); ++i) {
104     temporal_rates[i] = bitrates_[spatial_index][i].value_or(0);
105   }
106 
107   return temporal_rates;
108 }
109 
110 std::vector<absl::optional<VideoBitrateAllocation>>
GetSimulcastAllocations() const111 VideoBitrateAllocation::GetSimulcastAllocations() const {
112   std::vector<absl::optional<VideoBitrateAllocation>> bitrates;
113   for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
114     absl::optional<VideoBitrateAllocation> layer_bitrate;
115     if (IsSpatialLayerUsed(si)) {
116       layer_bitrate = VideoBitrateAllocation();
117       for (int tl = 0; tl < kMaxTemporalStreams; ++tl) {
118         if (HasBitrate(si, tl))
119           layer_bitrate->SetBitrate(0, tl, GetBitrate(si, tl));
120       }
121     }
122     bitrates.push_back(layer_bitrate);
123   }
124   return bitrates;
125 }
126 
operator ==(const VideoBitrateAllocation & other) const127 bool VideoBitrateAllocation::operator==(
128     const VideoBitrateAllocation& other) const {
129   for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
130     for (size_t ti = 0; ti < kMaxTemporalStreams; ++ti) {
131       if (bitrates_[si][ti] != other.bitrates_[si][ti])
132         return false;
133     }
134   }
135   return true;
136 }
137 
ToString() const138 std::string VideoBitrateAllocation::ToString() const {
139   if (sum_ == 0)
140     return "VideoBitrateAllocation [ [] ]";
141 
142   // Max string length in practice is 260, but let's have some overhead and
143   // round up to nearest power of two.
144   char string_buf[512];
145   rtc::SimpleStringBuilder ssb(string_buf);
146 
147   ssb << "VideoBitrateAllocation [";
148   uint32_t spatial_cumulator = 0;
149   for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
150     RTC_DCHECK_LE(spatial_cumulator, sum_);
151     if (spatial_cumulator == sum_)
152       break;
153 
154     const uint32_t layer_sum = GetSpatialLayerSum(si);
155     if (layer_sum == sum_ && si == 0) {
156       ssb << " [";
157     } else {
158       if (si > 0)
159         ssb << ",";
160       ssb << '\n' << "  [";
161     }
162     spatial_cumulator += layer_sum;
163 
164     uint32_t temporal_cumulator = 0;
165     for (size_t ti = 0; ti < kMaxTemporalStreams; ++ti) {
166       RTC_DCHECK_LE(temporal_cumulator, layer_sum);
167       if (temporal_cumulator == layer_sum)
168         break;
169 
170       if (ti > 0)
171         ssb << ", ";
172 
173       uint32_t bitrate = bitrates_[si][ti].value_or(0);
174       ssb << bitrate;
175       temporal_cumulator += bitrate;
176     }
177     ssb << "]";
178   }
179 
180   RTC_DCHECK_EQ(spatial_cumulator, sum_);
181   ssb << " ]";
182   return ssb.str();
183 }
184 
185 }  // namespace webrtc
186