| /* | 
 |  *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "modules/video_coding/svc/svc_rate_allocator.h" | 
 |  | 
 | #include <algorithm> | 
 | #include <cmath> | 
 | #include <cstddef> | 
 | #include <numeric> | 
 | #include <vector> | 
 |  | 
 | #include "absl/container/inlined_vector.h" | 
 | #include "modules/video_coding/svc/create_scalability_structure.h" | 
 | #include "rtc_base/checks.h" | 
 |  | 
 | namespace webrtc { | 
 | namespace { | 
 |  | 
 | constexpr float kSpatialLayeringRateScalingFactor = 0.55f; | 
 | constexpr float kTemporalLayeringRateScalingFactor = 0.55f; | 
 |  | 
 | struct ActiveSpatialLayers { | 
 |   size_t first = 0; | 
 |   size_t num = 0; | 
 | }; | 
 |  | 
 | ActiveSpatialLayers GetActiveSpatialLayers(const VideoCodec& codec, | 
 |                                            size_t num_spatial_layers) { | 
 |   ActiveSpatialLayers active; | 
 |   for (active.first = 0; active.first < num_spatial_layers; ++active.first) { | 
 |     if (codec.spatialLayers[active.first].active) { | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   size_t last_active_layer = active.first; | 
 |   for (; last_active_layer < num_spatial_layers; ++last_active_layer) { | 
 |     if (!codec.spatialLayers[last_active_layer].active) { | 
 |       break; | 
 |     } | 
 |   } | 
 |   active.num = last_active_layer - active.first; | 
 |  | 
 |   return active; | 
 | } | 
 |  | 
 | std::vector<DataRate> AdjustAndVerify( | 
 |     const VideoCodec& codec, | 
 |     size_t first_active_layer, | 
 |     const std::vector<DataRate>& spatial_layer_rates) { | 
 |   std::vector<DataRate> adjusted_spatial_layer_rates; | 
 |   // Keep track of rate that couldn't be applied to the previous layer due to | 
 |   // max bitrate constraint, try to pass it forward to the next one. | 
 |   DataRate excess_rate = DataRate::Zero(); | 
 |   for (size_t sl_idx = 0; sl_idx < spatial_layer_rates.size(); ++sl_idx) { | 
 |     DataRate min_rate = DataRate::KilobitsPerSec( | 
 |         codec.spatialLayers[first_active_layer + sl_idx].minBitrate); | 
 |     DataRate max_rate = DataRate::KilobitsPerSec( | 
 |         codec.spatialLayers[first_active_layer + sl_idx].maxBitrate); | 
 |  | 
 |     DataRate layer_rate = spatial_layer_rates[sl_idx] + excess_rate; | 
 |     if (layer_rate < min_rate) { | 
 |       // Not enough rate to reach min bitrate for desired number of layers, | 
 |       // abort allocation. | 
 |       if (spatial_layer_rates.size() == 1) { | 
 |         return spatial_layer_rates; | 
 |       } | 
 |       return adjusted_spatial_layer_rates; | 
 |     } | 
 |  | 
 |     if (layer_rate <= max_rate) { | 
 |       excess_rate = DataRate::Zero(); | 
 |       adjusted_spatial_layer_rates.push_back(layer_rate); | 
 |     } else { | 
 |       excess_rate = layer_rate - max_rate; | 
 |       adjusted_spatial_layer_rates.push_back(max_rate); | 
 |     } | 
 |   } | 
 |  | 
 |   return adjusted_spatial_layer_rates; | 
 | } | 
 |  | 
 | static std::vector<DataRate> SplitBitrate(size_t num_layers, | 
 |                                           DataRate total_bitrate, | 
 |                                           float rate_scaling_factor) { | 
 |   std::vector<DataRate> bitrates; | 
 |  | 
 |   double denominator = 0.0; | 
 |   for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) { | 
 |     denominator += std::pow(rate_scaling_factor, layer_idx); | 
 |   } | 
 |  | 
 |   double numerator = std::pow(rate_scaling_factor, num_layers - 1); | 
 |   for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) { | 
 |     bitrates.push_back(numerator * total_bitrate / denominator); | 
 |     numerator /= rate_scaling_factor; | 
 |   } | 
 |  | 
 |   const DataRate sum = | 
 |       std::accumulate(bitrates.begin(), bitrates.end(), DataRate::Zero()); | 
 |  | 
 |   // Keep the sum of split bitrates equal to the total bitrate by adding or | 
 |   // subtracting bits, which were lost due to rounding, to the latest layer. | 
 |   if (total_bitrate > sum) { | 
 |     bitrates.back() += total_bitrate - sum; | 
 |   } else if (total_bitrate < sum) { | 
 |     bitrates.back() -= sum - total_bitrate; | 
 |   } | 
 |  | 
 |   return bitrates; | 
 | } | 
 |  | 
 | // Returns the minimum bitrate needed for `num_active_layers` spatial layers to | 
 | // become active using the configuration specified by `codec`. | 
 | DataRate FindLayerTogglingThreshold(const VideoCodec& codec, | 
 |                                     size_t first_active_layer, | 
 |                                     size_t num_active_layers) { | 
 |   if (num_active_layers == 1) { | 
 |     return DataRate::KilobitsPerSec(codec.spatialLayers[0].minBitrate); | 
 |   } | 
 |  | 
 |   if (codec.mode == VideoCodecMode::kRealtimeVideo) { | 
 |     DataRate lower_bound = DataRate::Zero(); | 
 |     DataRate upper_bound = DataRate::Zero(); | 
 |     if (num_active_layers > 1) { | 
 |       for (size_t i = 0; i < num_active_layers - 1; ++i) { | 
 |         lower_bound += DataRate::KilobitsPerSec( | 
 |             codec.spatialLayers[first_active_layer + i].minBitrate); | 
 |         upper_bound += DataRate::KilobitsPerSec( | 
 |             codec.spatialLayers[first_active_layer + i].maxBitrate); | 
 |       } | 
 |     } | 
 |     upper_bound += DataRate::KilobitsPerSec( | 
 |         codec.spatialLayers[first_active_layer + num_active_layers - 1] | 
 |             .minBitrate); | 
 |  | 
 |     // Do a binary search until upper and lower bound is the highest bitrate for | 
 |     // `num_active_layers` - 1 layers and lowest bitrate for `num_active_layers` | 
 |     // layers respectively. | 
 |     while (upper_bound - lower_bound > DataRate::BitsPerSec(1)) { | 
 |       DataRate try_rate = (lower_bound + upper_bound) / 2; | 
 |       if (AdjustAndVerify(codec, first_active_layer, | 
 |                           SplitBitrate(num_active_layers, try_rate, | 
 |                                        kSpatialLayeringRateScalingFactor)) | 
 |               .size() == num_active_layers) { | 
 |         upper_bound = try_rate; | 
 |       } else { | 
 |         lower_bound = try_rate; | 
 |       } | 
 |     } | 
 |     return upper_bound; | 
 |   } else { | 
 |     DataRate toggling_rate = DataRate::Zero(); | 
 |     for (size_t i = 0; i < num_active_layers - 1; ++i) { | 
 |       toggling_rate += DataRate::KilobitsPerSec( | 
 |           codec.spatialLayers[first_active_layer + i].targetBitrate); | 
 |     } | 
 |     toggling_rate += DataRate::KilobitsPerSec( | 
 |         codec.spatialLayers[first_active_layer + num_active_layers - 1] | 
 |             .minBitrate); | 
 |     return toggling_rate; | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | SvcRateAllocator::NumLayers SvcRateAllocator::GetNumLayers( | 
 |     const VideoCodec& codec) { | 
 |   NumLayers layers; | 
 |   if (absl::optional<ScalabilityMode> scalability_mode = | 
 |           codec.GetScalabilityMode(); | 
 |       scalability_mode.has_value()) { | 
 |     if (auto structure = CreateScalabilityStructure(*scalability_mode)) { | 
 |       ScalableVideoController::StreamLayersConfig config = | 
 |           structure->StreamConfig(); | 
 |       layers.spatial = config.num_spatial_layers; | 
 |       layers.temporal = config.num_temporal_layers; | 
 |       return layers; | 
 |     } | 
 |   } | 
 |   if (codec.codecType == kVideoCodecVP9) { | 
 |     layers.spatial = codec.VP9().numberOfSpatialLayers; | 
 |     layers.temporal = codec.VP9().numberOfTemporalLayers; | 
 |     return layers; | 
 |   } | 
 |   layers.spatial = 1; | 
 |   layers.temporal = 1; | 
 |   return layers; | 
 | } | 
 |  | 
 | SvcRateAllocator::SvcRateAllocator(const VideoCodec& codec) | 
 |     : codec_(codec), | 
 |       num_layers_(GetNumLayers(codec)), | 
 |       experiment_settings_(StableTargetRateExperiment::ParseFromFieldTrials()), | 
 |       cumulative_layer_start_bitrates_(GetLayerStartBitrates(codec)), | 
 |       last_active_layer_count_(0) { | 
 |   RTC_DCHECK_GT(num_layers_.spatial, 0); | 
 |   RTC_DCHECK_LE(num_layers_.spatial, kMaxSpatialLayers); | 
 |   RTC_DCHECK_GT(num_layers_.temporal, 0); | 
 |   RTC_DCHECK_LE(num_layers_.temporal, 3); | 
 |   for (size_t layer_idx = 0; layer_idx < num_layers_.spatial; ++layer_idx) { | 
 |     // Verify min <= target <= max. | 
 |     if (codec.spatialLayers[layer_idx].active) { | 
 |       RTC_DCHECK_GT(codec.spatialLayers[layer_idx].maxBitrate, 0); | 
 |       RTC_DCHECK_GE(codec.spatialLayers[layer_idx].maxBitrate, | 
 |                     codec.spatialLayers[layer_idx].minBitrate); | 
 |       RTC_DCHECK_GE(codec.spatialLayers[layer_idx].targetBitrate, | 
 |                     codec.spatialLayers[layer_idx].minBitrate); | 
 |       RTC_DCHECK_GE(codec.spatialLayers[layer_idx].maxBitrate, | 
 |                     codec.spatialLayers[layer_idx].targetBitrate); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | VideoBitrateAllocation SvcRateAllocator::Allocate( | 
 |     VideoBitrateAllocationParameters parameters) { | 
 |   DataRate total_bitrate = parameters.total_bitrate; | 
 |   if (codec_.maxBitrate != 0) { | 
 |     total_bitrate = | 
 |         std::min(total_bitrate, DataRate::KilobitsPerSec(codec_.maxBitrate)); | 
 |   } | 
 |  | 
 |   if (codec_.spatialLayers[0].targetBitrate == 0) { | 
 |     // Delegate rate distribution to encoder wrapper if bitrate thresholds | 
 |     // are not set. | 
 |     VideoBitrateAllocation bitrate_allocation; | 
 |     bitrate_allocation.SetBitrate(0, 0, total_bitrate.bps()); | 
 |     return bitrate_allocation; | 
 |   } | 
 |  | 
 |   const ActiveSpatialLayers active_layers = | 
 |       GetActiveSpatialLayers(codec_, num_layers_.spatial); | 
 |   size_t num_spatial_layers = active_layers.num; | 
 |  | 
 |   if (num_spatial_layers == 0) { | 
 |     return VideoBitrateAllocation();  // All layers are deactivated. | 
 |   } | 
 |  | 
 |   // Figure out how many spatial layers should be active. | 
 |   if (experiment_settings_.IsEnabled() && | 
 |       parameters.stable_bitrate > DataRate::Zero()) { | 
 |     double hysteresis_factor; | 
 |     if (codec_.mode == VideoCodecMode::kScreensharing) { | 
 |       hysteresis_factor = experiment_settings_.GetScreenshareHysteresisFactor(); | 
 |     } else { | 
 |       hysteresis_factor = experiment_settings_.GetVideoHysteresisFactor(); | 
 |     } | 
 |  | 
 |     DataRate stable_rate = std::min(total_bitrate, parameters.stable_bitrate); | 
 |     // First check if bitrate has grown large enough to enable new layers. | 
 |     size_t num_enabled_with_hysteresis = | 
 |         FindNumEnabledLayers(stable_rate / hysteresis_factor); | 
 |     if (num_enabled_with_hysteresis >= last_active_layer_count_) { | 
 |       num_spatial_layers = num_enabled_with_hysteresis; | 
 |     } else { | 
 |       // We could not enable new layers, check if any should be disabled. | 
 |       num_spatial_layers = | 
 |           std::min(last_active_layer_count_, FindNumEnabledLayers(stable_rate)); | 
 |     } | 
 |   } else { | 
 |     num_spatial_layers = FindNumEnabledLayers(total_bitrate); | 
 |   } | 
 |   last_active_layer_count_ = num_spatial_layers; | 
 |  | 
 |   VideoBitrateAllocation allocation; | 
 |   if (codec_.mode == VideoCodecMode::kRealtimeVideo) { | 
 |     allocation = GetAllocationNormalVideo(total_bitrate, active_layers.first, | 
 |                                           num_spatial_layers); | 
 |   } else { | 
 |     allocation = GetAllocationScreenSharing(total_bitrate, active_layers.first, | 
 |                                             num_spatial_layers); | 
 |   } | 
 |   allocation.set_bw_limited(num_spatial_layers < active_layers.num); | 
 |   return allocation; | 
 | } | 
 |  | 
 | VideoBitrateAllocation SvcRateAllocator::GetAllocationNormalVideo( | 
 |     DataRate total_bitrate, | 
 |     size_t first_active_layer, | 
 |     size_t num_spatial_layers) const { | 
 |   std::vector<DataRate> spatial_layer_rates; | 
 |   if (num_spatial_layers == 0) { | 
 |     // Not enough rate for even the base layer. Force allocation at the total | 
 |     // bitrate anyway. | 
 |     num_spatial_layers = 1; | 
 |     spatial_layer_rates.push_back(total_bitrate); | 
 |   } else { | 
 |     spatial_layer_rates = | 
 |         AdjustAndVerify(codec_, first_active_layer, | 
 |                         SplitBitrate(num_spatial_layers, total_bitrate, | 
 |                                      kSpatialLayeringRateScalingFactor)); | 
 |     RTC_DCHECK_EQ(spatial_layer_rates.size(), num_spatial_layers); | 
 |   } | 
 |  | 
 |   VideoBitrateAllocation bitrate_allocation; | 
 |  | 
 |   for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) { | 
 |     std::vector<DataRate> temporal_layer_rates = | 
 |         SplitBitrate(num_layers_.temporal, spatial_layer_rates[sl_idx], | 
 |                      kTemporalLayeringRateScalingFactor); | 
 |  | 
 |     // Distribute rate across temporal layers. Allocate more bits to lower | 
 |     // layers since they are used for prediction of higher layers and their | 
 |     // references are far apart. | 
 |     if (num_layers_.temporal == 1) { | 
 |       bitrate_allocation.SetBitrate(sl_idx + first_active_layer, 0, | 
 |                                     temporal_layer_rates[0].bps()); | 
 |     } else if (num_layers_.temporal == 2) { | 
 |       bitrate_allocation.SetBitrate(sl_idx + first_active_layer, 0, | 
 |                                     temporal_layer_rates[1].bps()); | 
 |       bitrate_allocation.SetBitrate(sl_idx + first_active_layer, 1, | 
 |                                     temporal_layer_rates[0].bps()); | 
 |     } else { | 
 |       RTC_CHECK_EQ(num_layers_.temporal, 3); | 
 |       // In case of three temporal layers the high layer has two frames and the | 
 |       // middle layer has one frame within GOP (in between two consecutive low | 
 |       // layer frames). Thus high layer requires more bits (comparing pure | 
 |       // bitrate of layer, excluding bitrate of base layers) to keep quality on | 
 |       // par with lower layers. | 
 |       bitrate_allocation.SetBitrate(sl_idx + first_active_layer, 0, | 
 |                                     temporal_layer_rates[2].bps()); | 
 |       bitrate_allocation.SetBitrate(sl_idx + first_active_layer, 1, | 
 |                                     temporal_layer_rates[0].bps()); | 
 |       bitrate_allocation.SetBitrate(sl_idx + first_active_layer, 2, | 
 |                                     temporal_layer_rates[1].bps()); | 
 |     } | 
 |   } | 
 |  | 
 |   return bitrate_allocation; | 
 | } | 
 |  | 
 | // Bit-rate is allocated in such a way, that the highest enabled layer will have | 
 | // between min and max bitrate, and all others will have exactly target | 
 | // bit-rate allocated. | 
 | VideoBitrateAllocation SvcRateAllocator::GetAllocationScreenSharing( | 
 |     DataRate total_bitrate, | 
 |     size_t first_active_layer, | 
 |     size_t num_spatial_layers) const { | 
 |   VideoBitrateAllocation bitrate_allocation; | 
 |  | 
 |   if (num_spatial_layers == 0 || | 
 |       total_bitrate < | 
 |           DataRate::KilobitsPerSec( | 
 |               codec_.spatialLayers[first_active_layer].minBitrate)) { | 
 |     // Always enable at least one layer. | 
 |     bitrate_allocation.SetBitrate(first_active_layer, 0, total_bitrate.bps()); | 
 |     return bitrate_allocation; | 
 |   } | 
 |  | 
 |   DataRate allocated_rate = DataRate::Zero(); | 
 |   DataRate top_layer_rate = DataRate::Zero(); | 
 |   size_t sl_idx; | 
 |   for (sl_idx = first_active_layer; | 
 |        sl_idx < first_active_layer + num_spatial_layers; ++sl_idx) { | 
 |     const DataRate min_rate = | 
 |         DataRate::KilobitsPerSec(codec_.spatialLayers[sl_idx].minBitrate); | 
 |     const DataRate target_rate = | 
 |         DataRate::KilobitsPerSec(codec_.spatialLayers[sl_idx].targetBitrate); | 
 |  | 
 |     if (allocated_rate + min_rate > total_bitrate) { | 
 |       // Use stable rate to determine if layer should be enabled. | 
 |       break; | 
 |     } | 
 |  | 
 |     top_layer_rate = std::min(target_rate, total_bitrate - allocated_rate); | 
 |     bitrate_allocation.SetBitrate(sl_idx, 0, top_layer_rate.bps()); | 
 |     allocated_rate += top_layer_rate; | 
 |   } | 
 |  | 
 |   if (sl_idx > 0 && total_bitrate - allocated_rate > DataRate::Zero()) { | 
 |     // Add leftover to the last allocated layer. | 
 |     top_layer_rate = std::min( | 
 |         top_layer_rate + (total_bitrate - allocated_rate), | 
 |         DataRate::KilobitsPerSec(codec_.spatialLayers[sl_idx - 1].maxBitrate)); | 
 |     bitrate_allocation.SetBitrate(sl_idx - 1, 0, top_layer_rate.bps()); | 
 |   } | 
 |  | 
 |   return bitrate_allocation; | 
 | } | 
 |  | 
 | size_t SvcRateAllocator::FindNumEnabledLayers(DataRate target_rate) const { | 
 |   if (cumulative_layer_start_bitrates_.empty()) { | 
 |     return 0; | 
 |   } | 
 |  | 
 |   size_t num_enabled_layers = 0; | 
 |   for (DataRate start_rate : cumulative_layer_start_bitrates_) { | 
 |     // First layer is always enabled. | 
 |     if (num_enabled_layers == 0 || start_rate <= target_rate) { | 
 |       ++num_enabled_layers; | 
 |     } else { | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   return num_enabled_layers; | 
 | } | 
 |  | 
 | DataRate SvcRateAllocator::GetMaxBitrate(const VideoCodec& codec) { | 
 |   const NumLayers num_layers = GetNumLayers(codec); | 
 |   const ActiveSpatialLayers active_layers = | 
 |       GetActiveSpatialLayers(codec, num_layers.spatial); | 
 |  | 
 |   DataRate max_bitrate = DataRate::Zero(); | 
 |   for (size_t sl_idx = 0; sl_idx < active_layers.num; ++sl_idx) { | 
 |     max_bitrate += DataRate::KilobitsPerSec( | 
 |         codec.spatialLayers[active_layers.first + sl_idx].maxBitrate); | 
 |   } | 
 |  | 
 |   if (codec.maxBitrate != 0) { | 
 |     max_bitrate = | 
 |         std::min(max_bitrate, DataRate::KilobitsPerSec(codec.maxBitrate)); | 
 |   } | 
 |  | 
 |   return max_bitrate; | 
 | } | 
 |  | 
 | DataRate SvcRateAllocator::GetPaddingBitrate(const VideoCodec& codec) { | 
 |   auto start_bitrate = GetLayerStartBitrates(codec); | 
 |   if (start_bitrate.empty()) { | 
 |     return DataRate::Zero();  // All layers are deactivated. | 
 |   } | 
 |  | 
 |   return start_bitrate.back(); | 
 | } | 
 |  | 
 | absl::InlinedVector<DataRate, kMaxSpatialLayers> | 
 | SvcRateAllocator::GetLayerStartBitrates(const VideoCodec& codec) { | 
 |   absl::InlinedVector<DataRate, kMaxSpatialLayers> start_bitrates; | 
 |   const NumLayers num_layers = GetNumLayers(codec); | 
 |   const ActiveSpatialLayers active_layers = | 
 |       GetActiveSpatialLayers(codec, num_layers.spatial); | 
 |   DataRate last_rate = DataRate::Zero(); | 
 |   for (size_t i = 1; i <= active_layers.num; ++i) { | 
 |     DataRate layer_toggling_rate = | 
 |         FindLayerTogglingThreshold(codec, active_layers.first, i); | 
 |     start_bitrates.push_back(layer_toggling_rate); | 
 |     RTC_DCHECK_LE(last_rate, layer_toggling_rate); | 
 |     last_rate = layer_toggling_rate; | 
 |   } | 
 |   return start_bitrates; | 
 | } | 
 |  | 
 | }  // namespace webrtc |