Åsa Persson | c5a74ff | 2020-09-20 17:50:00 +0200 | [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 |
| 22 | // is a multiple of |requested_alignment|. Each resolution divisible by |
| 23 | // |alignment| will be divisible by |requested_alignment| after the scale factor |
| 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]) |
| 59 | // are written in |config| such that: |
| 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, |
| 69 | VideoEncoderConfig* config) { |
| 70 | const int requested_alignment = encoder_info.requested_resolution_alignment; |
| 71 | if (!encoder_info.apply_alignment_to_all_simulcast_layers) { |
| 72 | return requested_alignment; |
| 73 | } |
| 74 | |
| 75 | if (requested_alignment < 1 || config->number_of_streams <= 1 || |
| 76 | config->simulcast_layers.size() <= 1) { |
| 77 | return requested_alignment; |
| 78 | } |
| 79 | |
| 80 | // Update alignment to also apply to simulcast layers. |
| 81 | const bool has_scale_resolution_down_by = absl::c_any_of( |
| 82 | config->simulcast_layers, [](const webrtc::VideoStream& layer) { |
| 83 | return layer.scale_resolution_down_by >= 1.0; |
| 84 | }); |
| 85 | |
| 86 | if (!has_scale_resolution_down_by) { |
| 87 | // Default resolution downscaling used (scale factors: 1, 2, 4, ...). |
| 88 | return requested_alignment * (1 << (config->simulcast_layers.size() - 1)); |
| 89 | } |
| 90 | |
| 91 | // Get alignment for downscaled layers. |
| 92 | // Adjust |scale_resolution_down_by| to a common multiple to limit the |
| 93 | // alignment value (to avoid largely cropped frames and possibly with an |
| 94 | // aspect ratio far from the original). |
| 95 | const int kMaxAlignment = 16; |
| 96 | |
| 97 | for (auto& layer : config->simulcast_layers) { |
| 98 | layer.scale_resolution_down_by = |
| 99 | std::max(layer.scale_resolution_down_by, 1.0); |
| 100 | layer.scale_resolution_down_by = |
| 101 | std::min(layer.scale_resolution_down_by, 10000.0); |
| 102 | } |
| 103 | |
| 104 | // Decide on common multiple to use. |
| 105 | double min_diff = std::numeric_limits<double>::max(); |
| 106 | int best_alignment = 1; |
| 107 | for (int alignment = requested_alignment; alignment <= kMaxAlignment; |
| 108 | ++alignment) { |
| 109 | double diff = RoundToMultiple(alignment, requested_alignment, config, |
| 110 | /*update_config=*/false); |
| 111 | if (diff < min_diff) { |
| 112 | min_diff = diff; |
| 113 | best_alignment = alignment; |
| 114 | } |
| 115 | } |
| 116 | RoundToMultiple(best_alignment, requested_alignment, config, |
| 117 | /*update_config=*/true); |
| 118 | |
| 119 | return std::max(best_alignment, requested_alignment); |
| 120 | } |
| 121 | } // namespace webrtc |