| /* |
| * Copyright (c) 2020 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 "video/alignment_adjuster.h" |
| |
| #include <algorithm> |
| #include <limits> |
| |
| #include "absl/algorithm/container.h" |
| #include "rtc_base/logging.h" |
| |
| namespace webrtc { |
| namespace { |
| // Round each scale factor to the closest rational in form alignment/i where i |
| // is a multiple of `requested_alignment`. Each resolution divisible by |
| // `alignment` will be divisible by `requested_alignment` after the scale factor |
| // is applied. |
| double RoundToMultiple(int alignment, |
| int requested_alignment, |
| VideoEncoderConfig* config, |
| bool update_config) { |
| double diff = 0.0; |
| for (auto& layer : config->simulcast_layers) { |
| double min_dist = std::numeric_limits<double>::max(); |
| double new_scale = 1.0; |
| for (int i = requested_alignment; i <= alignment; |
| i += requested_alignment) { |
| double dist = std::abs(layer.scale_resolution_down_by - |
| alignment / static_cast<double>(i)); |
| if (dist <= min_dist) { |
| min_dist = dist; |
| new_scale = alignment / static_cast<double>(i); |
| } |
| } |
| diff += std::abs(layer.scale_resolution_down_by - new_scale); |
| if (update_config) { |
| RTC_LOG(LS_INFO) << "scale_resolution_down_by " |
| << layer.scale_resolution_down_by << " -> " << new_scale; |
| layer.scale_resolution_down_by = new_scale; |
| } |
| } |
| return diff; |
| } |
| } // namespace |
| |
| // Input: encoder_info.requested_resolution_alignment (K) |
| // Input: encoder_info.apply_alignment_to_all_simulcast_layers (B) |
| // Input: vector config->simulcast_layers.scale_resolution_down_by (S[i]) |
| // Output: |
| // If B is false, returns K and does not adjust scaling factors. |
| // Otherwise, returns adjusted alignment (A), adjusted scaling factors (S'[i]) |
| // are written in `config` such that: |
| // |
| // A / S'[i] are integers divisible by K |
| // sum abs(S'[i] - S[i]) -> min |
| // A integer <= 16 |
| // |
| // Solution chooses closest S'[i] in a form A / j where j is a multiple of K. |
| |
| int AlignmentAdjuster::GetAlignmentAndMaybeAdjustScaleFactors( |
| const VideoEncoder::EncoderInfo& encoder_info, |
| VideoEncoderConfig* config, |
| std::optional<size_t> max_layers) { |
| const int requested_alignment = encoder_info.requested_resolution_alignment; |
| if (!encoder_info.apply_alignment_to_all_simulcast_layers) { |
| return requested_alignment; |
| } |
| |
| if (requested_alignment < 1 || config->number_of_streams <= 1 || |
| config->simulcast_layers.size() <= 1) { |
| return requested_alignment; |
| } |
| |
| // Update alignment to also apply to simulcast layers. |
| const bool has_scale_resolution_down_by = absl::c_any_of( |
| config->simulcast_layers, [](const webrtc::VideoStream& layer) { |
| return layer.scale_resolution_down_by >= 1.0; |
| }); |
| |
| if (!has_scale_resolution_down_by) { |
| // Default resolution downscaling used (scale factors: 1, 2, 4, ...). |
| size_t size = config->simulcast_layers.size(); |
| if (max_layers && *max_layers > 0 && *max_layers < size) { |
| size = *max_layers; |
| } |
| return requested_alignment * (1 << (size - 1)); |
| } |
| |
| // Get alignment for downscaled layers. |
| // Adjust `scale_resolution_down_by` to a common multiple to limit the |
| // alignment value (to avoid largely cropped frames and possibly with an |
| // aspect ratio far from the original). |
| const int kMaxAlignment = 16; |
| |
| for (auto& layer : config->simulcast_layers) { |
| layer.scale_resolution_down_by = |
| std::max(layer.scale_resolution_down_by, 1.0); |
| layer.scale_resolution_down_by = |
| std::min(layer.scale_resolution_down_by, 10000.0); |
| } |
| |
| // Decide on common multiple to use. |
| double min_diff = std::numeric_limits<double>::max(); |
| int best_alignment = 1; |
| for (int alignment = requested_alignment; alignment <= kMaxAlignment; |
| ++alignment) { |
| double diff = RoundToMultiple(alignment, requested_alignment, config, |
| /*update_config=*/false); |
| if (diff < min_diff) { |
| min_diff = diff; |
| best_alignment = alignment; |
| } |
| } |
| RoundToMultiple(best_alignment, requested_alignment, config, |
| /*update_config=*/true); |
| |
| return std::max(best_alignment, requested_alignment); |
| } |
| } // namespace webrtc |