1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker #include "media/base/video_adapter.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
14*d9f75844SAndroid Build Coastguard Worker #include <cmath>
15*d9f75844SAndroid Build Coastguard Worker #include <cstdlib>
16*d9f75844SAndroid Build Coastguard Worker #include <limits>
17*d9f75844SAndroid Build Coastguard Worker #include <utility>
18*d9f75844SAndroid Build Coastguard Worker
19*d9f75844SAndroid Build Coastguard Worker #include "absl/types/optional.h"
20*d9f75844SAndroid Build Coastguard Worker #include "media/base/video_common.h"
21*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
22*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/logging.h"
23*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/strings/string_builder.h"
24*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/time_utils.h"
25*d9f75844SAndroid Build Coastguard Worker #include "system_wrappers/include/field_trial.h"
26*d9f75844SAndroid Build Coastguard Worker
27*d9f75844SAndroid Build Coastguard Worker namespace {
28*d9f75844SAndroid Build Coastguard Worker
29*d9f75844SAndroid Build Coastguard Worker struct Fraction {
30*d9f75844SAndroid Build Coastguard Worker int numerator;
31*d9f75844SAndroid Build Coastguard Worker int denominator;
32*d9f75844SAndroid Build Coastguard Worker
DivideByGcd__anon4cc909e10111::Fraction33*d9f75844SAndroid Build Coastguard Worker void DivideByGcd() {
34*d9f75844SAndroid Build Coastguard Worker int g = cricket::GreatestCommonDivisor(numerator, denominator);
35*d9f75844SAndroid Build Coastguard Worker numerator /= g;
36*d9f75844SAndroid Build Coastguard Worker denominator /= g;
37*d9f75844SAndroid Build Coastguard Worker }
38*d9f75844SAndroid Build Coastguard Worker
39*d9f75844SAndroid Build Coastguard Worker // Determines number of output pixels if both width and height of an input of
40*d9f75844SAndroid Build Coastguard Worker // `input_pixels` pixels is scaled with the fraction numerator / denominator.
scale_pixel_count__anon4cc909e10111::Fraction41*d9f75844SAndroid Build Coastguard Worker int scale_pixel_count(int input_pixels) {
42*d9f75844SAndroid Build Coastguard Worker return (numerator * numerator * input_pixels) / (denominator * denominator);
43*d9f75844SAndroid Build Coastguard Worker }
44*d9f75844SAndroid Build Coastguard Worker };
45*d9f75844SAndroid Build Coastguard Worker
46*d9f75844SAndroid Build Coastguard Worker // Round `value_to_round` to a multiple of `multiple`. Prefer rounding upwards,
47*d9f75844SAndroid Build Coastguard Worker // but never more than `max_value`.
roundUp(int value_to_round,int multiple,int max_value)48*d9f75844SAndroid Build Coastguard Worker int roundUp(int value_to_round, int multiple, int max_value) {
49*d9f75844SAndroid Build Coastguard Worker const int rounded_value =
50*d9f75844SAndroid Build Coastguard Worker (value_to_round + multiple - 1) / multiple * multiple;
51*d9f75844SAndroid Build Coastguard Worker return rounded_value <= max_value ? rounded_value
52*d9f75844SAndroid Build Coastguard Worker : (max_value / multiple * multiple);
53*d9f75844SAndroid Build Coastguard Worker }
54*d9f75844SAndroid Build Coastguard Worker
55*d9f75844SAndroid Build Coastguard Worker // Generates a scale factor that makes `input_pixels` close to `target_pixels`,
56*d9f75844SAndroid Build Coastguard Worker // but no higher than `max_pixels`.
FindScale(int input_width,int input_height,int target_pixels,int max_pixels,bool variable_start_scale_factor)57*d9f75844SAndroid Build Coastguard Worker Fraction FindScale(int input_width,
58*d9f75844SAndroid Build Coastguard Worker int input_height,
59*d9f75844SAndroid Build Coastguard Worker int target_pixels,
60*d9f75844SAndroid Build Coastguard Worker int max_pixels,
61*d9f75844SAndroid Build Coastguard Worker bool variable_start_scale_factor) {
62*d9f75844SAndroid Build Coastguard Worker // This function only makes sense for a positive target.
63*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(target_pixels, 0);
64*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(max_pixels, 0);
65*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GE(max_pixels, target_pixels);
66*d9f75844SAndroid Build Coastguard Worker
67*d9f75844SAndroid Build Coastguard Worker const int input_pixels = input_width * input_height;
68*d9f75844SAndroid Build Coastguard Worker
69*d9f75844SAndroid Build Coastguard Worker // Don't scale up original.
70*d9f75844SAndroid Build Coastguard Worker if (target_pixels >= input_pixels)
71*d9f75844SAndroid Build Coastguard Worker return Fraction{1, 1};
72*d9f75844SAndroid Build Coastguard Worker
73*d9f75844SAndroid Build Coastguard Worker Fraction current_scale = Fraction{1, 1};
74*d9f75844SAndroid Build Coastguard Worker Fraction best_scale = Fraction{1, 1};
75*d9f75844SAndroid Build Coastguard Worker
76*d9f75844SAndroid Build Coastguard Worker if (variable_start_scale_factor) {
77*d9f75844SAndroid Build Coastguard Worker // Start scaling down by 2/3 depending on `input_width` and `input_height`.
78*d9f75844SAndroid Build Coastguard Worker if (input_width % 3 == 0 && input_height % 3 == 0) {
79*d9f75844SAndroid Build Coastguard Worker // 2/3 (then alternates 3/4, 2/3, 3/4,...).
80*d9f75844SAndroid Build Coastguard Worker current_scale = Fraction{6, 6};
81*d9f75844SAndroid Build Coastguard Worker }
82*d9f75844SAndroid Build Coastguard Worker if (input_width % 9 == 0 && input_height % 9 == 0) {
83*d9f75844SAndroid Build Coastguard Worker // 2/3, 2/3 (then alternates 3/4, 2/3, 3/4,...).
84*d9f75844SAndroid Build Coastguard Worker current_scale = Fraction{36, 36};
85*d9f75844SAndroid Build Coastguard Worker }
86*d9f75844SAndroid Build Coastguard Worker }
87*d9f75844SAndroid Build Coastguard Worker
88*d9f75844SAndroid Build Coastguard Worker // The minimum (absolute) difference between the number of output pixels and
89*d9f75844SAndroid Build Coastguard Worker // the target pixel count.
90*d9f75844SAndroid Build Coastguard Worker int min_pixel_diff = std::numeric_limits<int>::max();
91*d9f75844SAndroid Build Coastguard Worker if (input_pixels <= max_pixels) {
92*d9f75844SAndroid Build Coastguard Worker // Start condition for 1/1 case, if it is less than max.
93*d9f75844SAndroid Build Coastguard Worker min_pixel_diff = std::abs(input_pixels - target_pixels);
94*d9f75844SAndroid Build Coastguard Worker }
95*d9f75844SAndroid Build Coastguard Worker
96*d9f75844SAndroid Build Coastguard Worker // Alternately scale down by 3/4 and 2/3. This results in fractions which are
97*d9f75844SAndroid Build Coastguard Worker // effectively scalable. For instance, starting at 1280x720 will result in
98*d9f75844SAndroid Build Coastguard Worker // the series (3/4) => 960x540, (1/2) => 640x360, (3/8) => 480x270,
99*d9f75844SAndroid Build Coastguard Worker // (1/4) => 320x180, (3/16) => 240x125, (1/8) => 160x90.
100*d9f75844SAndroid Build Coastguard Worker while (current_scale.scale_pixel_count(input_pixels) > target_pixels) {
101*d9f75844SAndroid Build Coastguard Worker if (current_scale.numerator % 3 == 0 &&
102*d9f75844SAndroid Build Coastguard Worker current_scale.denominator % 2 == 0) {
103*d9f75844SAndroid Build Coastguard Worker // Multiply by 2/3.
104*d9f75844SAndroid Build Coastguard Worker current_scale.numerator /= 3;
105*d9f75844SAndroid Build Coastguard Worker current_scale.denominator /= 2;
106*d9f75844SAndroid Build Coastguard Worker } else {
107*d9f75844SAndroid Build Coastguard Worker // Multiply by 3/4.
108*d9f75844SAndroid Build Coastguard Worker current_scale.numerator *= 3;
109*d9f75844SAndroid Build Coastguard Worker current_scale.denominator *= 4;
110*d9f75844SAndroid Build Coastguard Worker }
111*d9f75844SAndroid Build Coastguard Worker
112*d9f75844SAndroid Build Coastguard Worker int output_pixels = current_scale.scale_pixel_count(input_pixels);
113*d9f75844SAndroid Build Coastguard Worker if (output_pixels <= max_pixels) {
114*d9f75844SAndroid Build Coastguard Worker int diff = std::abs(target_pixels - output_pixels);
115*d9f75844SAndroid Build Coastguard Worker if (diff < min_pixel_diff) {
116*d9f75844SAndroid Build Coastguard Worker min_pixel_diff = diff;
117*d9f75844SAndroid Build Coastguard Worker best_scale = current_scale;
118*d9f75844SAndroid Build Coastguard Worker }
119*d9f75844SAndroid Build Coastguard Worker }
120*d9f75844SAndroid Build Coastguard Worker }
121*d9f75844SAndroid Build Coastguard Worker best_scale.DivideByGcd();
122*d9f75844SAndroid Build Coastguard Worker
123*d9f75844SAndroid Build Coastguard Worker return best_scale;
124*d9f75844SAndroid Build Coastguard Worker }
125*d9f75844SAndroid Build Coastguard Worker
Swap(const absl::optional<std::pair<int,int>> & in)126*d9f75844SAndroid Build Coastguard Worker absl::optional<std::pair<int, int>> Swap(
127*d9f75844SAndroid Build Coastguard Worker const absl::optional<std::pair<int, int>>& in) {
128*d9f75844SAndroid Build Coastguard Worker if (!in) {
129*d9f75844SAndroid Build Coastguard Worker return absl::nullopt;
130*d9f75844SAndroid Build Coastguard Worker }
131*d9f75844SAndroid Build Coastguard Worker return std::make_pair(in->second, in->first);
132*d9f75844SAndroid Build Coastguard Worker }
133*d9f75844SAndroid Build Coastguard Worker
134*d9f75844SAndroid Build Coastguard Worker } // namespace
135*d9f75844SAndroid Build Coastguard Worker
136*d9f75844SAndroid Build Coastguard Worker namespace cricket {
137*d9f75844SAndroid Build Coastguard Worker
VideoAdapter(int source_resolution_alignment)138*d9f75844SAndroid Build Coastguard Worker VideoAdapter::VideoAdapter(int source_resolution_alignment)
139*d9f75844SAndroid Build Coastguard Worker : frames_in_(0),
140*d9f75844SAndroid Build Coastguard Worker frames_out_(0),
141*d9f75844SAndroid Build Coastguard Worker frames_scaled_(0),
142*d9f75844SAndroid Build Coastguard Worker adaption_changes_(0),
143*d9f75844SAndroid Build Coastguard Worker previous_width_(0),
144*d9f75844SAndroid Build Coastguard Worker previous_height_(0),
145*d9f75844SAndroid Build Coastguard Worker variable_start_scale_factor_(!webrtc::field_trial::IsDisabled(
146*d9f75844SAndroid Build Coastguard Worker "WebRTC-Video-VariableStartScaleFactor")),
147*d9f75844SAndroid Build Coastguard Worker source_resolution_alignment_(source_resolution_alignment),
148*d9f75844SAndroid Build Coastguard Worker resolution_alignment_(source_resolution_alignment),
149*d9f75844SAndroid Build Coastguard Worker resolution_request_target_pixel_count_(std::numeric_limits<int>::max()),
150*d9f75844SAndroid Build Coastguard Worker resolution_request_max_pixel_count_(std::numeric_limits<int>::max()),
151*d9f75844SAndroid Build Coastguard Worker max_framerate_request_(std::numeric_limits<int>::max()) {}
152*d9f75844SAndroid Build Coastguard Worker
VideoAdapter()153*d9f75844SAndroid Build Coastguard Worker VideoAdapter::VideoAdapter() : VideoAdapter(1) {}
154*d9f75844SAndroid Build Coastguard Worker
~VideoAdapter()155*d9f75844SAndroid Build Coastguard Worker VideoAdapter::~VideoAdapter() {}
156*d9f75844SAndroid Build Coastguard Worker
DropFrame(int64_t in_timestamp_ns)157*d9f75844SAndroid Build Coastguard Worker bool VideoAdapter::DropFrame(int64_t in_timestamp_ns) {
158*d9f75844SAndroid Build Coastguard Worker int max_fps = max_framerate_request_;
159*d9f75844SAndroid Build Coastguard Worker if (output_format_request_.max_fps)
160*d9f75844SAndroid Build Coastguard Worker max_fps = std::min(max_fps, *output_format_request_.max_fps);
161*d9f75844SAndroid Build Coastguard Worker
162*d9f75844SAndroid Build Coastguard Worker framerate_controller_.SetMaxFramerate(max_fps);
163*d9f75844SAndroid Build Coastguard Worker return framerate_controller_.ShouldDropFrame(in_timestamp_ns);
164*d9f75844SAndroid Build Coastguard Worker }
165*d9f75844SAndroid Build Coastguard Worker
AdaptFrameResolution(int in_width,int in_height,int64_t in_timestamp_ns,int * cropped_width,int * cropped_height,int * out_width,int * out_height)166*d9f75844SAndroid Build Coastguard Worker bool VideoAdapter::AdaptFrameResolution(int in_width,
167*d9f75844SAndroid Build Coastguard Worker int in_height,
168*d9f75844SAndroid Build Coastguard Worker int64_t in_timestamp_ns,
169*d9f75844SAndroid Build Coastguard Worker int* cropped_width,
170*d9f75844SAndroid Build Coastguard Worker int* cropped_height,
171*d9f75844SAndroid Build Coastguard Worker int* out_width,
172*d9f75844SAndroid Build Coastguard Worker int* out_height) {
173*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&mutex_);
174*d9f75844SAndroid Build Coastguard Worker ++frames_in_;
175*d9f75844SAndroid Build Coastguard Worker
176*d9f75844SAndroid Build Coastguard Worker // The max output pixel count is the minimum of the requests from
177*d9f75844SAndroid Build Coastguard Worker // OnOutputFormatRequest and OnResolutionFramerateRequest.
178*d9f75844SAndroid Build Coastguard Worker int max_pixel_count = resolution_request_max_pixel_count_;
179*d9f75844SAndroid Build Coastguard Worker
180*d9f75844SAndroid Build Coastguard Worker // Select target aspect ratio and max pixel count depending on input frame
181*d9f75844SAndroid Build Coastguard Worker // orientation.
182*d9f75844SAndroid Build Coastguard Worker absl::optional<std::pair<int, int>> target_aspect_ratio;
183*d9f75844SAndroid Build Coastguard Worker if (in_width > in_height) {
184*d9f75844SAndroid Build Coastguard Worker target_aspect_ratio = output_format_request_.target_landscape_aspect_ratio;
185*d9f75844SAndroid Build Coastguard Worker if (output_format_request_.max_landscape_pixel_count)
186*d9f75844SAndroid Build Coastguard Worker max_pixel_count = std::min(
187*d9f75844SAndroid Build Coastguard Worker max_pixel_count, *output_format_request_.max_landscape_pixel_count);
188*d9f75844SAndroid Build Coastguard Worker } else {
189*d9f75844SAndroid Build Coastguard Worker target_aspect_ratio = output_format_request_.target_portrait_aspect_ratio;
190*d9f75844SAndroid Build Coastguard Worker if (output_format_request_.max_portrait_pixel_count)
191*d9f75844SAndroid Build Coastguard Worker max_pixel_count = std::min(
192*d9f75844SAndroid Build Coastguard Worker max_pixel_count, *output_format_request_.max_portrait_pixel_count);
193*d9f75844SAndroid Build Coastguard Worker }
194*d9f75844SAndroid Build Coastguard Worker
195*d9f75844SAndroid Build Coastguard Worker int target_pixel_count =
196*d9f75844SAndroid Build Coastguard Worker std::min(resolution_request_target_pixel_count_, max_pixel_count);
197*d9f75844SAndroid Build Coastguard Worker
198*d9f75844SAndroid Build Coastguard Worker // Drop the input frame if necessary.
199*d9f75844SAndroid Build Coastguard Worker if (max_pixel_count <= 0 || DropFrame(in_timestamp_ns)) {
200*d9f75844SAndroid Build Coastguard Worker // Show VAdapt log every 90 frames dropped. (3 seconds)
201*d9f75844SAndroid Build Coastguard Worker if ((frames_in_ - frames_out_) % 90 == 0) {
202*d9f75844SAndroid Build Coastguard Worker // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed
203*d9f75844SAndroid Build Coastguard Worker // in default calls.
204*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_
205*d9f75844SAndroid Build Coastguard Worker << " / out " << frames_out_ << " / in " << frames_in_
206*d9f75844SAndroid Build Coastguard Worker << " Changes: " << adaption_changes_
207*d9f75844SAndroid Build Coastguard Worker << " Input: " << in_width << "x" << in_height
208*d9f75844SAndroid Build Coastguard Worker << " timestamp: " << in_timestamp_ns
209*d9f75844SAndroid Build Coastguard Worker << " Output fps: " << max_framerate_request_ << "/"
210*d9f75844SAndroid Build Coastguard Worker << output_format_request_.max_fps.value_or(-1)
211*d9f75844SAndroid Build Coastguard Worker << " alignment: " << resolution_alignment_;
212*d9f75844SAndroid Build Coastguard Worker }
213*d9f75844SAndroid Build Coastguard Worker
214*d9f75844SAndroid Build Coastguard Worker // Drop frame.
215*d9f75844SAndroid Build Coastguard Worker return false;
216*d9f75844SAndroid Build Coastguard Worker }
217*d9f75844SAndroid Build Coastguard Worker
218*d9f75844SAndroid Build Coastguard Worker // Calculate how the input should be cropped.
219*d9f75844SAndroid Build Coastguard Worker if (!target_aspect_ratio || target_aspect_ratio->first <= 0 ||
220*d9f75844SAndroid Build Coastguard Worker target_aspect_ratio->second <= 0) {
221*d9f75844SAndroid Build Coastguard Worker *cropped_width = in_width;
222*d9f75844SAndroid Build Coastguard Worker *cropped_height = in_height;
223*d9f75844SAndroid Build Coastguard Worker } else {
224*d9f75844SAndroid Build Coastguard Worker const float requested_aspect =
225*d9f75844SAndroid Build Coastguard Worker target_aspect_ratio->first /
226*d9f75844SAndroid Build Coastguard Worker static_cast<float>(target_aspect_ratio->second);
227*d9f75844SAndroid Build Coastguard Worker *cropped_width =
228*d9f75844SAndroid Build Coastguard Worker std::min(in_width, static_cast<int>(in_height * requested_aspect));
229*d9f75844SAndroid Build Coastguard Worker *cropped_height =
230*d9f75844SAndroid Build Coastguard Worker std::min(in_height, static_cast<int>(in_width / requested_aspect));
231*d9f75844SAndroid Build Coastguard Worker }
232*d9f75844SAndroid Build Coastguard Worker const Fraction scale =
233*d9f75844SAndroid Build Coastguard Worker FindScale(*cropped_width, *cropped_height, target_pixel_count,
234*d9f75844SAndroid Build Coastguard Worker max_pixel_count, variable_start_scale_factor_);
235*d9f75844SAndroid Build Coastguard Worker // Adjust cropping slightly to get correctly aligned output size and a perfect
236*d9f75844SAndroid Build Coastguard Worker // scale factor.
237*d9f75844SAndroid Build Coastguard Worker *cropped_width = roundUp(*cropped_width,
238*d9f75844SAndroid Build Coastguard Worker scale.denominator * resolution_alignment_, in_width);
239*d9f75844SAndroid Build Coastguard Worker *cropped_height = roundUp(
240*d9f75844SAndroid Build Coastguard Worker *cropped_height, scale.denominator * resolution_alignment_, in_height);
241*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_EQ(0, *cropped_width % scale.denominator);
242*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_EQ(0, *cropped_height % scale.denominator);
243*d9f75844SAndroid Build Coastguard Worker
244*d9f75844SAndroid Build Coastguard Worker // Calculate final output size.
245*d9f75844SAndroid Build Coastguard Worker *out_width = *cropped_width / scale.denominator * scale.numerator;
246*d9f75844SAndroid Build Coastguard Worker *out_height = *cropped_height / scale.denominator * scale.numerator;
247*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_EQ(0, *out_width % resolution_alignment_);
248*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_EQ(0, *out_height % resolution_alignment_);
249*d9f75844SAndroid Build Coastguard Worker
250*d9f75844SAndroid Build Coastguard Worker ++frames_out_;
251*d9f75844SAndroid Build Coastguard Worker if (scale.numerator != scale.denominator)
252*d9f75844SAndroid Build Coastguard Worker ++frames_scaled_;
253*d9f75844SAndroid Build Coastguard Worker
254*d9f75844SAndroid Build Coastguard Worker if (previous_width_ &&
255*d9f75844SAndroid Build Coastguard Worker (previous_width_ != *out_width || previous_height_ != *out_height)) {
256*d9f75844SAndroid Build Coastguard Worker ++adaption_changes_;
257*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << "Frame size changed: scaled " << frames_scaled_
258*d9f75844SAndroid Build Coastguard Worker << " / out " << frames_out_ << " / in " << frames_in_
259*d9f75844SAndroid Build Coastguard Worker << " Changes: " << adaption_changes_
260*d9f75844SAndroid Build Coastguard Worker << " Input: " << in_width << "x" << in_height
261*d9f75844SAndroid Build Coastguard Worker << " Scale: " << scale.numerator << "/"
262*d9f75844SAndroid Build Coastguard Worker << scale.denominator << " Output: " << *out_width << "x"
263*d9f75844SAndroid Build Coastguard Worker << *out_height << " fps: " << max_framerate_request_ << "/"
264*d9f75844SAndroid Build Coastguard Worker << output_format_request_.max_fps.value_or(-1)
265*d9f75844SAndroid Build Coastguard Worker << " alignment: " << resolution_alignment_;
266*d9f75844SAndroid Build Coastguard Worker }
267*d9f75844SAndroid Build Coastguard Worker
268*d9f75844SAndroid Build Coastguard Worker previous_width_ = *out_width;
269*d9f75844SAndroid Build Coastguard Worker previous_height_ = *out_height;
270*d9f75844SAndroid Build Coastguard Worker
271*d9f75844SAndroid Build Coastguard Worker return true;
272*d9f75844SAndroid Build Coastguard Worker }
273*d9f75844SAndroid Build Coastguard Worker
OnOutputFormatRequest(const absl::optional<VideoFormat> & format)274*d9f75844SAndroid Build Coastguard Worker void VideoAdapter::OnOutputFormatRequest(
275*d9f75844SAndroid Build Coastguard Worker const absl::optional<VideoFormat>& format) {
276*d9f75844SAndroid Build Coastguard Worker absl::optional<std::pair<int, int>> target_aspect_ratio;
277*d9f75844SAndroid Build Coastguard Worker absl::optional<int> max_pixel_count;
278*d9f75844SAndroid Build Coastguard Worker absl::optional<int> max_fps;
279*d9f75844SAndroid Build Coastguard Worker if (format) {
280*d9f75844SAndroid Build Coastguard Worker target_aspect_ratio = std::make_pair(format->width, format->height);
281*d9f75844SAndroid Build Coastguard Worker max_pixel_count = format->width * format->height;
282*d9f75844SAndroid Build Coastguard Worker if (format->interval > 0)
283*d9f75844SAndroid Build Coastguard Worker max_fps = rtc::kNumNanosecsPerSec / format->interval;
284*d9f75844SAndroid Build Coastguard Worker }
285*d9f75844SAndroid Build Coastguard Worker OnOutputFormatRequest(target_aspect_ratio, max_pixel_count, max_fps);
286*d9f75844SAndroid Build Coastguard Worker }
287*d9f75844SAndroid Build Coastguard Worker
OnOutputFormatRequest(const absl::optional<std::pair<int,int>> & target_aspect_ratio,const absl::optional<int> & max_pixel_count,const absl::optional<int> & max_fps)288*d9f75844SAndroid Build Coastguard Worker void VideoAdapter::OnOutputFormatRequest(
289*d9f75844SAndroid Build Coastguard Worker const absl::optional<std::pair<int, int>>& target_aspect_ratio,
290*d9f75844SAndroid Build Coastguard Worker const absl::optional<int>& max_pixel_count,
291*d9f75844SAndroid Build Coastguard Worker const absl::optional<int>& max_fps) {
292*d9f75844SAndroid Build Coastguard Worker absl::optional<std::pair<int, int>> target_landscape_aspect_ratio;
293*d9f75844SAndroid Build Coastguard Worker absl::optional<std::pair<int, int>> target_portrait_aspect_ratio;
294*d9f75844SAndroid Build Coastguard Worker if (target_aspect_ratio && target_aspect_ratio->first > 0 &&
295*d9f75844SAndroid Build Coastguard Worker target_aspect_ratio->second > 0) {
296*d9f75844SAndroid Build Coastguard Worker // Maintain input orientation.
297*d9f75844SAndroid Build Coastguard Worker const int max_side =
298*d9f75844SAndroid Build Coastguard Worker std::max(target_aspect_ratio->first, target_aspect_ratio->second);
299*d9f75844SAndroid Build Coastguard Worker const int min_side =
300*d9f75844SAndroid Build Coastguard Worker std::min(target_aspect_ratio->first, target_aspect_ratio->second);
301*d9f75844SAndroid Build Coastguard Worker target_landscape_aspect_ratio = std::make_pair(max_side, min_side);
302*d9f75844SAndroid Build Coastguard Worker target_portrait_aspect_ratio = std::make_pair(min_side, max_side);
303*d9f75844SAndroid Build Coastguard Worker }
304*d9f75844SAndroid Build Coastguard Worker OnOutputFormatRequest(target_landscape_aspect_ratio, max_pixel_count,
305*d9f75844SAndroid Build Coastguard Worker target_portrait_aspect_ratio, max_pixel_count, max_fps);
306*d9f75844SAndroid Build Coastguard Worker }
307*d9f75844SAndroid Build Coastguard Worker
OnOutputFormatRequest(const absl::optional<std::pair<int,int>> & target_landscape_aspect_ratio,const absl::optional<int> & max_landscape_pixel_count,const absl::optional<std::pair<int,int>> & target_portrait_aspect_ratio,const absl::optional<int> & max_portrait_pixel_count,const absl::optional<int> & max_fps)308*d9f75844SAndroid Build Coastguard Worker void VideoAdapter::OnOutputFormatRequest(
309*d9f75844SAndroid Build Coastguard Worker const absl::optional<std::pair<int, int>>& target_landscape_aspect_ratio,
310*d9f75844SAndroid Build Coastguard Worker const absl::optional<int>& max_landscape_pixel_count,
311*d9f75844SAndroid Build Coastguard Worker const absl::optional<std::pair<int, int>>& target_portrait_aspect_ratio,
312*d9f75844SAndroid Build Coastguard Worker const absl::optional<int>& max_portrait_pixel_count,
313*d9f75844SAndroid Build Coastguard Worker const absl::optional<int>& max_fps) {
314*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&mutex_);
315*d9f75844SAndroid Build Coastguard Worker
316*d9f75844SAndroid Build Coastguard Worker OutputFormatRequest request = {
317*d9f75844SAndroid Build Coastguard Worker .target_landscape_aspect_ratio = target_landscape_aspect_ratio,
318*d9f75844SAndroid Build Coastguard Worker .max_landscape_pixel_count = max_landscape_pixel_count,
319*d9f75844SAndroid Build Coastguard Worker .target_portrait_aspect_ratio = target_portrait_aspect_ratio,
320*d9f75844SAndroid Build Coastguard Worker .max_portrait_pixel_count = max_portrait_pixel_count,
321*d9f75844SAndroid Build Coastguard Worker .max_fps = max_fps};
322*d9f75844SAndroid Build Coastguard Worker
323*d9f75844SAndroid Build Coastguard Worker if (stashed_output_format_request_) {
324*d9f75844SAndroid Build Coastguard Worker // Save the output format request for later use in case the encoder making
325*d9f75844SAndroid Build Coastguard Worker // this call would become active, because currently all active encoders use
326*d9f75844SAndroid Build Coastguard Worker // requested_resolution instead.
327*d9f75844SAndroid Build Coastguard Worker stashed_output_format_request_ = request;
328*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << "Stashing OnOutputFormatRequest: "
329*d9f75844SAndroid Build Coastguard Worker << stashed_output_format_request_->ToString();
330*d9f75844SAndroid Build Coastguard Worker } else {
331*d9f75844SAndroid Build Coastguard Worker output_format_request_ = request;
332*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << "Setting output_format_request_: "
333*d9f75844SAndroid Build Coastguard Worker << output_format_request_.ToString();
334*d9f75844SAndroid Build Coastguard Worker }
335*d9f75844SAndroid Build Coastguard Worker
336*d9f75844SAndroid Build Coastguard Worker framerate_controller_.Reset();
337*d9f75844SAndroid Build Coastguard Worker }
338*d9f75844SAndroid Build Coastguard Worker
OnSinkWants(const rtc::VideoSinkWants & sink_wants)339*d9f75844SAndroid Build Coastguard Worker void VideoAdapter::OnSinkWants(const rtc::VideoSinkWants& sink_wants) {
340*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&mutex_);
341*d9f75844SAndroid Build Coastguard Worker resolution_request_max_pixel_count_ = sink_wants.max_pixel_count;
342*d9f75844SAndroid Build Coastguard Worker resolution_request_target_pixel_count_ =
343*d9f75844SAndroid Build Coastguard Worker sink_wants.target_pixel_count.value_or(
344*d9f75844SAndroid Build Coastguard Worker resolution_request_max_pixel_count_);
345*d9f75844SAndroid Build Coastguard Worker max_framerate_request_ = sink_wants.max_framerate_fps;
346*d9f75844SAndroid Build Coastguard Worker resolution_alignment_ = cricket::LeastCommonMultiple(
347*d9f75844SAndroid Build Coastguard Worker source_resolution_alignment_, sink_wants.resolution_alignment);
348*d9f75844SAndroid Build Coastguard Worker
349*d9f75844SAndroid Build Coastguard Worker if (!sink_wants.aggregates) {
350*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_WARNING)
351*d9f75844SAndroid Build Coastguard Worker << "These should always be created by VideoBroadcaster!";
352*d9f75844SAndroid Build Coastguard Worker return;
353*d9f75844SAndroid Build Coastguard Worker }
354*d9f75844SAndroid Build Coastguard Worker
355*d9f75844SAndroid Build Coastguard Worker // If requested_resolution is used, and there are no active encoders
356*d9f75844SAndroid Build Coastguard Worker // that are NOT using requested_resolution (aka newapi), then override
357*d9f75844SAndroid Build Coastguard Worker // calls to OnOutputFormatRequest and use values from requested_resolution
358*d9f75844SAndroid Build Coastguard Worker // instead (combined with qualityscaling based on pixel counts above).
359*d9f75844SAndroid Build Coastguard Worker if (webrtc::field_trial::IsDisabled(
360*d9f75844SAndroid Build Coastguard Worker "WebRTC-Video-RequestedResolutionOverrideOutputFormatRequest")) {
361*d9f75844SAndroid Build Coastguard Worker // kill-switch...
362*d9f75844SAndroid Build Coastguard Worker return;
363*d9f75844SAndroid Build Coastguard Worker }
364*d9f75844SAndroid Build Coastguard Worker
365*d9f75844SAndroid Build Coastguard Worker if (!sink_wants.requested_resolution) {
366*d9f75844SAndroid Build Coastguard Worker if (stashed_output_format_request_) {
367*d9f75844SAndroid Build Coastguard Worker // because current active_output_format_request is based on
368*d9f75844SAndroid Build Coastguard Worker // requested_resolution logic, while current encoder(s) doesn't want that,
369*d9f75844SAndroid Build Coastguard Worker // we have to restore the stashed request.
370*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << "Unstashing OnOutputFormatRequest: "
371*d9f75844SAndroid Build Coastguard Worker << stashed_output_format_request_->ToString();
372*d9f75844SAndroid Build Coastguard Worker output_format_request_ = *stashed_output_format_request_;
373*d9f75844SAndroid Build Coastguard Worker stashed_output_format_request_.reset();
374*d9f75844SAndroid Build Coastguard Worker }
375*d9f75844SAndroid Build Coastguard Worker return;
376*d9f75844SAndroid Build Coastguard Worker }
377*d9f75844SAndroid Build Coastguard Worker
378*d9f75844SAndroid Build Coastguard Worker if (sink_wants.aggregates->any_active_without_requested_resolution) {
379*d9f75844SAndroid Build Coastguard Worker return;
380*d9f75844SAndroid Build Coastguard Worker }
381*d9f75844SAndroid Build Coastguard Worker
382*d9f75844SAndroid Build Coastguard Worker if (!stashed_output_format_request_) {
383*d9f75844SAndroid Build Coastguard Worker // The active output format request is about to be rewritten by
384*d9f75844SAndroid Build Coastguard Worker // request_resolution. We need to save it for later use in case the encoder
385*d9f75844SAndroid Build Coastguard Worker // which doesn't use request_resolution logic become active in the future.
386*d9f75844SAndroid Build Coastguard Worker stashed_output_format_request_ = output_format_request_;
387*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << "Stashing OnOutputFormatRequest: "
388*d9f75844SAndroid Build Coastguard Worker << stashed_output_format_request_->ToString();
389*d9f75844SAndroid Build Coastguard Worker }
390*d9f75844SAndroid Build Coastguard Worker
391*d9f75844SAndroid Build Coastguard Worker auto res = *sink_wants.requested_resolution;
392*d9f75844SAndroid Build Coastguard Worker auto pixel_count = res.width * res.height;
393*d9f75844SAndroid Build Coastguard Worker output_format_request_.target_landscape_aspect_ratio =
394*d9f75844SAndroid Build Coastguard Worker std::make_pair(res.width, res.height);
395*d9f75844SAndroid Build Coastguard Worker output_format_request_.max_landscape_pixel_count = pixel_count;
396*d9f75844SAndroid Build Coastguard Worker output_format_request_.target_portrait_aspect_ratio =
397*d9f75844SAndroid Build Coastguard Worker std::make_pair(res.height, res.width);
398*d9f75844SAndroid Build Coastguard Worker output_format_request_.max_portrait_pixel_count = pixel_count;
399*d9f75844SAndroid Build Coastguard Worker output_format_request_.max_fps = max_framerate_request_;
400*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << "Setting output_format_request_ based on sink_wants: "
401*d9f75844SAndroid Build Coastguard Worker << output_format_request_.ToString();
402*d9f75844SAndroid Build Coastguard Worker }
403*d9f75844SAndroid Build Coastguard Worker
GetTargetPixels() const404*d9f75844SAndroid Build Coastguard Worker int VideoAdapter::GetTargetPixels() const {
405*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&mutex_);
406*d9f75844SAndroid Build Coastguard Worker return resolution_request_target_pixel_count_;
407*d9f75844SAndroid Build Coastguard Worker }
408*d9f75844SAndroid Build Coastguard Worker
GetMaxFramerate() const409*d9f75844SAndroid Build Coastguard Worker float VideoAdapter::GetMaxFramerate() const {
410*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&mutex_);
411*d9f75844SAndroid Build Coastguard Worker // Minimum of `output_format_request_.max_fps` and `max_framerate_request_` is
412*d9f75844SAndroid Build Coastguard Worker // used to throttle frame-rate.
413*d9f75844SAndroid Build Coastguard Worker int framerate =
414*d9f75844SAndroid Build Coastguard Worker std::min(max_framerate_request_,
415*d9f75844SAndroid Build Coastguard Worker output_format_request_.max_fps.value_or(max_framerate_request_));
416*d9f75844SAndroid Build Coastguard Worker if (framerate == std::numeric_limits<int>::max()) {
417*d9f75844SAndroid Build Coastguard Worker return std::numeric_limits<float>::infinity();
418*d9f75844SAndroid Build Coastguard Worker } else {
419*d9f75844SAndroid Build Coastguard Worker return max_framerate_request_;
420*d9f75844SAndroid Build Coastguard Worker }
421*d9f75844SAndroid Build Coastguard Worker }
422*d9f75844SAndroid Build Coastguard Worker
ToString() const423*d9f75844SAndroid Build Coastguard Worker std::string VideoAdapter::OutputFormatRequest::ToString() const {
424*d9f75844SAndroid Build Coastguard Worker rtc::StringBuilder oss;
425*d9f75844SAndroid Build Coastguard Worker oss << "[ ";
426*d9f75844SAndroid Build Coastguard Worker if (target_landscape_aspect_ratio == Swap(target_portrait_aspect_ratio) &&
427*d9f75844SAndroid Build Coastguard Worker max_landscape_pixel_count == max_portrait_pixel_count) {
428*d9f75844SAndroid Build Coastguard Worker if (target_landscape_aspect_ratio) {
429*d9f75844SAndroid Build Coastguard Worker oss << target_landscape_aspect_ratio->first << "x"
430*d9f75844SAndroid Build Coastguard Worker << target_landscape_aspect_ratio->second;
431*d9f75844SAndroid Build Coastguard Worker } else {
432*d9f75844SAndroid Build Coastguard Worker oss << "unset-resolution";
433*d9f75844SAndroid Build Coastguard Worker }
434*d9f75844SAndroid Build Coastguard Worker if (max_landscape_pixel_count) {
435*d9f75844SAndroid Build Coastguard Worker oss << " max_pixel_count: " << *max_landscape_pixel_count;
436*d9f75844SAndroid Build Coastguard Worker }
437*d9f75844SAndroid Build Coastguard Worker } else {
438*d9f75844SAndroid Build Coastguard Worker oss << "[ landscape: ";
439*d9f75844SAndroid Build Coastguard Worker if (target_landscape_aspect_ratio) {
440*d9f75844SAndroid Build Coastguard Worker oss << target_landscape_aspect_ratio->first << "x"
441*d9f75844SAndroid Build Coastguard Worker << target_landscape_aspect_ratio->second;
442*d9f75844SAndroid Build Coastguard Worker } else {
443*d9f75844SAndroid Build Coastguard Worker oss << "unset";
444*d9f75844SAndroid Build Coastguard Worker }
445*d9f75844SAndroid Build Coastguard Worker if (max_landscape_pixel_count) {
446*d9f75844SAndroid Build Coastguard Worker oss << " max_pixel_count: " << *max_landscape_pixel_count;
447*d9f75844SAndroid Build Coastguard Worker }
448*d9f75844SAndroid Build Coastguard Worker oss << " ] [ portrait: ";
449*d9f75844SAndroid Build Coastguard Worker if (target_portrait_aspect_ratio) {
450*d9f75844SAndroid Build Coastguard Worker oss << target_portrait_aspect_ratio->first << "x"
451*d9f75844SAndroid Build Coastguard Worker << target_portrait_aspect_ratio->second;
452*d9f75844SAndroid Build Coastguard Worker }
453*d9f75844SAndroid Build Coastguard Worker if (max_portrait_pixel_count) {
454*d9f75844SAndroid Build Coastguard Worker oss << " max_pixel_count: " << *max_portrait_pixel_count;
455*d9f75844SAndroid Build Coastguard Worker }
456*d9f75844SAndroid Build Coastguard Worker oss << " ]";
457*d9f75844SAndroid Build Coastguard Worker }
458*d9f75844SAndroid Build Coastguard Worker oss << " max_fps: ";
459*d9f75844SAndroid Build Coastguard Worker if (max_fps) {
460*d9f75844SAndroid Build Coastguard Worker oss << *max_fps;
461*d9f75844SAndroid Build Coastguard Worker } else {
462*d9f75844SAndroid Build Coastguard Worker oss << "unset";
463*d9f75844SAndroid Build Coastguard Worker }
464*d9f75844SAndroid Build Coastguard Worker oss << " ]";
465*d9f75844SAndroid Build Coastguard Worker return oss.Release();
466*d9f75844SAndroid Build Coastguard Worker }
467*d9f75844SAndroid Build Coastguard Worker
468*d9f75844SAndroid Build Coastguard Worker } // namespace cricket
469