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