Åsa Persson | c5a74ff | 2020-09-20 15:50:00 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2020 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 "video/alignment_adjuster.h" |
| 12 | |
| 13 | #include <algorithm> |
| 14 | #include <limits> |
| 15 | |
| 16 | #include "absl/algorithm/container.h" |
| 17 | #include "rtc_base/logging.h" |
| 18 | |
| 19 | namespace webrtc { |
| 20 | namespace { |
| 21 | // Round each scale factor to the closest rational in form alignment/i where i |
Artem Titov | ab30d72 | 2021-07-27 14:22:11 | [diff] [blame] | 22 | // is a multiple of `requested_alignment`. Each resolution divisible by |
| 23 | // `alignment` will be divisible by `requested_alignment` after the scale factor |
Åsa Persson | c5a74ff | 2020-09-20 15:50:00 | [diff] [blame] | 24 | // is applied. |
| 25 | double RoundToMultiple(int alignment, |
| 26 | int requested_alignment, |
| 27 | VideoEncoderConfig* config, |
| 28 | bool update_config) { |
| 29 | double diff = 0.0; |
| 30 | for (auto& layer : config->simulcast_layers) { |
| 31 | double min_dist = std::numeric_limits<double>::max(); |
| 32 | double new_scale = 1.0; |
| 33 | for (int i = requested_alignment; i <= alignment; |
| 34 | i += requested_alignment) { |
| 35 | double dist = std::abs(layer.scale_resolution_down_by - |
| 36 | alignment / static_cast<double>(i)); |
| 37 | if (dist <= min_dist) { |
| 38 | min_dist = dist; |
| 39 | new_scale = alignment / static_cast<double>(i); |
| 40 | } |
| 41 | } |
| 42 | diff += std::abs(layer.scale_resolution_down_by - new_scale); |
| 43 | if (update_config) { |
| 44 | RTC_LOG(LS_INFO) << "scale_resolution_down_by " |
| 45 | << layer.scale_resolution_down_by << " -> " << new_scale; |
| 46 | layer.scale_resolution_down_by = new_scale; |
| 47 | } |
| 48 | } |
| 49 | return diff; |
| 50 | } |
| 51 | } // namespace |
| 52 | |
| 53 | // Input: encoder_info.requested_resolution_alignment (K) |
| 54 | // Input: encoder_info.apply_alignment_to_all_simulcast_layers (B) |
| 55 | // Input: vector config->simulcast_layers.scale_resolution_down_by (S[i]) |
| 56 | // Output: |
| 57 | // If B is false, returns K and does not adjust scaling factors. |
| 58 | // Otherwise, returns adjusted alignment (A), adjusted scaling factors (S'[i]) |
Artem Titov | ab30d72 | 2021-07-27 14:22:11 | [diff] [blame] | 59 | // are written in `config` such that: |
Åsa Persson | c5a74ff | 2020-09-20 15:50:00 | [diff] [blame] | 60 | // |
| 61 | // A / S'[i] are integers divisible by K |
| 62 | // sum abs(S'[i] - S[i]) -> min |
| 63 | // A integer <= 16 |
| 64 | // |
| 65 | // Solution chooses closest S'[i] in a form A / j where j is a multiple of K. |
| 66 | |
| 67 | int AlignmentAdjuster::GetAlignmentAndMaybeAdjustScaleFactors( |
| 68 | const VideoEncoder::EncoderInfo& encoder_info, |
Åsa Persson | a24d35e | 2021-01-19 09:25:10 | [diff] [blame] | 69 | VideoEncoderConfig* config, |
| 70 | absl::optional<size_t> max_layers) { |
Åsa Persson | c5a74ff | 2020-09-20 15:50:00 | [diff] [blame] | 71 | const int requested_alignment = encoder_info.requested_resolution_alignment; |
| 72 | if (!encoder_info.apply_alignment_to_all_simulcast_layers) { |
| 73 | return requested_alignment; |
| 74 | } |
| 75 | |
| 76 | if (requested_alignment < 1 || config->number_of_streams <= 1 || |
| 77 | config->simulcast_layers.size() <= 1) { |
| 78 | return requested_alignment; |
| 79 | } |
| 80 | |
| 81 | // Update alignment to also apply to simulcast layers. |
| 82 | const bool has_scale_resolution_down_by = absl::c_any_of( |
| 83 | config->simulcast_layers, [](const webrtc::VideoStream& layer) { |
| 84 | return layer.scale_resolution_down_by >= 1.0; |
| 85 | }); |
| 86 | |
| 87 | if (!has_scale_resolution_down_by) { |
| 88 | // Default resolution downscaling used (scale factors: 1, 2, 4, ...). |
Åsa Persson | a24d35e | 2021-01-19 09:25:10 | [diff] [blame] | 89 | size_t size = config->simulcast_layers.size(); |
| 90 | if (max_layers && *max_layers > 0 && *max_layers < size) { |
| 91 | size = *max_layers; |
| 92 | } |
| 93 | return requested_alignment * (1 << (size - 1)); |
Åsa Persson | c5a74ff | 2020-09-20 15:50:00 | [diff] [blame] | 94 | } |
| 95 | |
| 96 | // Get alignment for downscaled layers. |
Artem Titov | ab30d72 | 2021-07-27 14:22:11 | [diff] [blame] | 97 | // Adjust `scale_resolution_down_by` to a common multiple to limit the |
Åsa Persson | c5a74ff | 2020-09-20 15:50:00 | [diff] [blame] | 98 | // alignment value (to avoid largely cropped frames and possibly with an |
| 99 | // aspect ratio far from the original). |
| 100 | const int kMaxAlignment = 16; |
| 101 | |
| 102 | for (auto& layer : config->simulcast_layers) { |
| 103 | layer.scale_resolution_down_by = |
| 104 | std::max(layer.scale_resolution_down_by, 1.0); |
| 105 | layer.scale_resolution_down_by = |
| 106 | std::min(layer.scale_resolution_down_by, 10000.0); |
| 107 | } |
| 108 | |
| 109 | // Decide on common multiple to use. |
| 110 | double min_diff = std::numeric_limits<double>::max(); |
| 111 | int best_alignment = 1; |
| 112 | for (int alignment = requested_alignment; alignment <= kMaxAlignment; |
| 113 | ++alignment) { |
| 114 | double diff = RoundToMultiple(alignment, requested_alignment, config, |
| 115 | /*update_config=*/false); |
| 116 | if (diff < min_diff) { |
| 117 | min_diff = diff; |
| 118 | best_alignment = alignment; |
| 119 | } |
| 120 | } |
| 121 | RoundToMultiple(best_alignment, requested_alignment, config, |
| 122 | /*update_config=*/true); |
| 123 | |
| 124 | return std::max(best_alignment, requested_alignment); |
| 125 | } |
| 126 | } // namespace webrtc |