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.
RoundToMultiple(int alignment,int requested_alignment,VideoEncoderConfig * config,bool update_config)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
GetAlignmentAndMaybeAdjustScaleFactors(const VideoEncoder::EncoderInfo & encoder_info,VideoEncoderConfig * config)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
122